mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-06-04 12:33:01 +00:00
initiall commit
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
-- +goose Up
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS core;
|
||||
|
||||
CREATE TYPE core.org_role AS ENUM ('owner', 'admin', 'member');
|
||||
CREATE TYPE core.project_role AS ENUM ('admin', 'developer', 'viewer');
|
||||
CREATE TYPE core.bucket_visibility AS ENUM ('private', 'public');
|
||||
|
||||
CREATE TABLE core.users (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
auth_subject TEXT NOT NULL UNIQUE,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
email_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE core.organizations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE core.organization_members (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organization_id UUID NOT NULL REFERENCES core.organizations(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES core.users(id) ON DELETE CASCADE,
|
||||
role core.org_role NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (organization_id, user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE core.projects (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organization_id UUID NOT NULL REFERENCES core.organizations(id) ON DELETE CASCADE,
|
||||
slug TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (organization_id, slug)
|
||||
);
|
||||
|
||||
CREATE TABLE core.project_members (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES core.projects(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES core.users(id) ON DELETE CASCADE,
|
||||
role core.project_role NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (project_id, user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE core.api_keys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES core.projects(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
prefix TEXT NOT NULL UNIQUE,
|
||||
secret_hash BYTEA NOT NULL,
|
||||
created_by_user_id UUID REFERENCES core.users(id) ON DELETE SET NULL,
|
||||
last_used_at TIMESTAMPTZ,
|
||||
revoked_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE core.buckets (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES core.projects(id) ON DELETE CASCADE,
|
||||
slug TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
visibility core.bucket_visibility NOT NULL DEFAULT 'private',
|
||||
created_by_user_id UUID REFERENCES core.users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (project_id, slug)
|
||||
);
|
||||
|
||||
CREATE TABLE core.bucket_objects (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
bucket_id UUID NOT NULL REFERENCES core.buckets(id) ON DELETE CASCADE,
|
||||
object_key TEXT NOT NULL,
|
||||
content_type TEXT NOT NULL,
|
||||
size_bytes BIGINT NOT NULL,
|
||||
checksum_sha256 TEXT NOT NULL,
|
||||
storage_path TEXT NOT NULL UNIQUE,
|
||||
uploaded_by_user_id UUID REFERENCES core.users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (bucket_id, object_key)
|
||||
);
|
||||
|
||||
CREATE TABLE core.project_invitations (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organization_id UUID NOT NULL REFERENCES core.organizations(id) ON DELETE CASCADE,
|
||||
project_id UUID REFERENCES core.projects(id) ON DELETE CASCADE,
|
||||
email TEXT NOT NULL,
|
||||
org_role core.org_role NOT NULL DEFAULT 'member',
|
||||
project_role core.project_role,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMPTZ NOT NULL,
|
||||
accepted_at TIMESTAMPTZ,
|
||||
invited_by_user_id UUID REFERENCES core.users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE core.audit_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organization_id UUID REFERENCES core.organizations(id) ON DELETE CASCADE,
|
||||
project_id UUID REFERENCES core.projects(id) ON DELETE CASCADE,
|
||||
actor_user_id UUID REFERENCES core.users(id) ON DELETE SET NULL,
|
||||
actor_api_key_id UUID REFERENCES core.api_keys(id) ON DELETE SET NULL,
|
||||
action TEXT NOT NULL,
|
||||
resource_type TEXT NOT NULL,
|
||||
resource_id TEXT NOT NULL,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
request_id TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_users_auth_subject ON core.users(auth_subject);
|
||||
CREATE INDEX idx_org_members_user_id ON core.organization_members(user_id);
|
||||
CREATE INDEX idx_projects_org_id ON core.projects(organization_id);
|
||||
CREATE INDEX idx_project_members_user_id ON core.project_members(user_id);
|
||||
CREATE INDEX idx_api_keys_project_id ON core.api_keys(project_id);
|
||||
CREATE INDEX idx_api_keys_prefix ON core.api_keys(prefix);
|
||||
CREATE INDEX idx_buckets_project_id ON core.buckets(project_id);
|
||||
CREATE INDEX idx_bucket_objects_bucket_id ON core.bucket_objects(bucket_id);
|
||||
CREATE INDEX idx_bucket_objects_bucket_key ON core.bucket_objects(bucket_id, object_key);
|
||||
CREATE INDEX idx_project_invitations_email ON core.project_invitations(email);
|
||||
CREATE INDEX idx_audit_logs_project_id ON core.audit_logs(project_id, created_at DESC);
|
||||
CREATE INDEX idx_audit_logs_org_id ON core.audit_logs(organization_id, created_at DESC);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS core.audit_logs;
|
||||
DROP TABLE IF EXISTS core.project_invitations;
|
||||
DROP TABLE IF EXISTS core.bucket_objects;
|
||||
DROP TABLE IF EXISTS core.buckets;
|
||||
DROP TABLE IF EXISTS core.api_keys;
|
||||
DROP TABLE IF EXISTS core.project_members;
|
||||
DROP TABLE IF EXISTS core.projects;
|
||||
DROP TABLE IF EXISTS core.organization_members;
|
||||
DROP TABLE IF EXISTS core.organizations;
|
||||
DROP TABLE IF EXISTS core.users;
|
||||
DROP TYPE IF EXISTS core.bucket_visibility;
|
||||
DROP TYPE IF EXISTS core.project_role;
|
||||
DROP TYPE IF EXISTS core.org_role;
|
||||
DROP SCHEMA IF EXISTS core;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-- +goose Up
|
||||
CREATE TABLE core.collections (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
project_id UUID NOT NULL REFERENCES core.projects(id) ON DELETE CASCADE,
|
||||
slug TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
schema JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_by_user_id UUID REFERENCES core.users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (project_id, slug)
|
||||
);
|
||||
|
||||
CREATE TABLE core.documents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
collection_id UUID NOT NULL REFERENCES core.collections(id) ON DELETE CASCADE,
|
||||
data JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
created_by_user_id UUID REFERENCES core.users(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_collections_project_id ON core.collections(project_id);
|
||||
CREATE INDEX idx_documents_collection_id ON core.documents(collection_id);
|
||||
CREATE INDEX idx_documents_data ON core.documents USING gin (data);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE IF EXISTS core.documents;
|
||||
DROP TABLE IF EXISTS core.collections;
|
||||
@@ -0,0 +1,40 @@
|
||||
-- name: CreateAPIKey :one
|
||||
INSERT INTO core.api_keys (
|
||||
project_id,
|
||||
name,
|
||||
prefix,
|
||||
secret_hash,
|
||||
created_by_user_id
|
||||
) VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING *;
|
||||
|
||||
-- name: ListAPIKeysForProject :many
|
||||
SELECT * FROM core.api_keys
|
||||
WHERE project_id = $1
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: GetAPIKeyByIDForProject :one
|
||||
SELECT * FROM core.api_keys
|
||||
WHERE project_id = $1
|
||||
AND id = $2;
|
||||
|
||||
-- name: GetAPIKeyByPrefix :one
|
||||
SELECT
|
||||
ak.*,
|
||||
p.organization_id
|
||||
FROM core.api_keys ak
|
||||
JOIN core.projects p ON p.id = ak.project_id
|
||||
WHERE ak.prefix = $1;
|
||||
|
||||
-- name: RevokeAPIKey :one
|
||||
UPDATE core.api_keys
|
||||
SET revoked_at = NOW()
|
||||
WHERE project_id = $1
|
||||
AND id = $2
|
||||
AND revoked_at IS NULL
|
||||
RETURNING *;
|
||||
|
||||
-- name: TouchAPIKey :exec
|
||||
UPDATE core.api_keys
|
||||
SET last_used_at = NOW()
|
||||
WHERE id = $1;
|
||||
@@ -0,0 +1,59 @@
|
||||
-- name: CreateAuditLog :one
|
||||
INSERT INTO core.audit_logs (
|
||||
organization_id,
|
||||
project_id,
|
||||
actor_user_id,
|
||||
actor_api_key_id,
|
||||
action,
|
||||
resource_type,
|
||||
resource_id,
|
||||
metadata,
|
||||
request_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
$9
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: ListAuditLogsForProject :many
|
||||
SELECT * FROM core.audit_logs
|
||||
WHERE project_id = $1
|
||||
AND (
|
||||
NULLIF(TRIM($2), '') IS NULL
|
||||
OR action ILIKE '%' || TRIM($2) || '%'
|
||||
OR resource_type ILIKE '%' || TRIM($2) || '%'
|
||||
OR resource_id ILIKE '%' || TRIM($2) || '%'
|
||||
OR request_id ILIKE '%' || TRIM($2) || '%'
|
||||
OR metadata::text ILIKE '%' || TRIM($2) || '%'
|
||||
)
|
||||
AND (
|
||||
NULLIF(TRIM($3), '') IS NULL
|
||||
OR action ILIKE TRIM($3) || '%'
|
||||
)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $4
|
||||
OFFSET $5;
|
||||
|
||||
-- name: CountAuditLogsForProject :one
|
||||
SELECT COUNT(*)::BIGINT
|
||||
FROM core.audit_logs
|
||||
WHERE project_id = $1
|
||||
AND (
|
||||
NULLIF(TRIM($2), '') IS NULL
|
||||
OR action ILIKE '%' || TRIM($2) || '%'
|
||||
OR resource_type ILIKE '%' || TRIM($2) || '%'
|
||||
OR resource_id ILIKE '%' || TRIM($2) || '%'
|
||||
OR request_id ILIKE '%' || TRIM($2) || '%'
|
||||
OR metadata::text ILIKE '%' || TRIM($2) || '%'
|
||||
)
|
||||
AND (
|
||||
NULLIF(TRIM($3), '') IS NULL
|
||||
OR action ILIKE TRIM($3) || '%'
|
||||
);
|
||||
@@ -0,0 +1,29 @@
|
||||
-- name: BootstrapOrganization :one
|
||||
WITH new_org AS (
|
||||
INSERT INTO core.organizations (slug, name)
|
||||
VALUES ($1, $2)
|
||||
RETURNING *
|
||||
),
|
||||
new_org_member AS (
|
||||
INSERT INTO core.organization_members (organization_id, user_id, role)
|
||||
SELECT id, $3, 'owner'::core.org_role FROM new_org
|
||||
),
|
||||
new_project AS (
|
||||
INSERT INTO core.projects (organization_id, slug, name, description)
|
||||
SELECT id, $4, $5, $6 FROM new_org
|
||||
RETURNING *
|
||||
),
|
||||
new_project_member AS (
|
||||
INSERT INTO core.project_members (project_id, user_id, role)
|
||||
SELECT id, $3, 'admin'::core.project_role FROM new_project
|
||||
)
|
||||
SELECT
|
||||
new_org.id AS organization_id,
|
||||
new_org.slug AS organization_slug,
|
||||
new_org.name AS organization_name,
|
||||
new_project.id AS project_id,
|
||||
new_project.slug AS project_slug,
|
||||
new_project.name AS project_name
|
||||
FROM new_org
|
||||
JOIN new_project ON TRUE;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
-- name: CreateBucket :one
|
||||
INSERT INTO core.buckets (
|
||||
project_id,
|
||||
slug,
|
||||
name,
|
||||
visibility,
|
||||
created_by_user_id
|
||||
) VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING *;
|
||||
|
||||
-- name: ListBucketsForProject :many
|
||||
SELECT * FROM core.buckets
|
||||
WHERE project_id = $1
|
||||
AND (
|
||||
btrim($2) = ''
|
||||
OR slug ILIKE '%' || btrim($2) || '%'
|
||||
OR name ILIKE '%' || btrim($2) || '%'
|
||||
)
|
||||
ORDER BY created_at ASC;
|
||||
|
||||
-- name: GetBucketByID :one
|
||||
SELECT
|
||||
b.*,
|
||||
p.organization_id
|
||||
FROM core.buckets b
|
||||
JOIN core.projects p ON p.id = b.project_id
|
||||
WHERE b.id = $1;
|
||||
|
||||
-- name: UpdateBucketByID :one
|
||||
UPDATE core.buckets
|
||||
SET slug = $2,
|
||||
name = $3,
|
||||
visibility = $4
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteBucketByID :one
|
||||
DELETE FROM core.buckets
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
@@ -0,0 +1,66 @@
|
||||
-- name: ListCollections :many
|
||||
SELECT * FROM core.collections
|
||||
WHERE project_id = $1
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: GetCollectionBySlug :one
|
||||
SELECT * FROM core.collections
|
||||
WHERE project_id = $1 AND slug = $2;
|
||||
|
||||
-- name: GetCollectionByID :one
|
||||
SELECT * FROM core.collections
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: CreateCollection :one
|
||||
INSERT INTO core.collections (
|
||||
project_id, slug, name, description, schema, created_by_user_id
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6
|
||||
) RETURNING *;
|
||||
|
||||
-- name: UpdateCollection :one
|
||||
UPDATE core.collections
|
||||
SET
|
||||
name = $3,
|
||||
description = $4,
|
||||
schema = $5,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1 AND project_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteCollection :exec
|
||||
DELETE FROM core.collections
|
||||
WHERE id = $1 AND project_id = $2;
|
||||
|
||||
-- name: ListDocuments :many
|
||||
SELECT * FROM core.documents
|
||||
WHERE collection_id = $1
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $2 OFFSET $3;
|
||||
|
||||
-- name: CountDocuments :one
|
||||
SELECT COUNT(*) FROM core.documents
|
||||
WHERE collection_id = $1;
|
||||
|
||||
-- name: GetDocumentByID :one
|
||||
SELECT * FROM core.documents
|
||||
WHERE id = $1 AND collection_id = $2;
|
||||
|
||||
-- name: CreateDocument :one
|
||||
INSERT INTO core.documents (
|
||||
collection_id, data, created_by_user_id
|
||||
) VALUES (
|
||||
$1, $2, $3
|
||||
) RETURNING *;
|
||||
|
||||
-- name: UpdateDocument :one
|
||||
UPDATE core.documents
|
||||
SET
|
||||
data = $3,
|
||||
updated_at = NOW()
|
||||
WHERE id = $1 AND collection_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteDocument :exec
|
||||
DELETE FROM core.documents
|
||||
WHERE id = $1 AND collection_id = $2;
|
||||
@@ -0,0 +1,45 @@
|
||||
-- name: CreateBucketObject :one
|
||||
INSERT INTO core.bucket_objects (
|
||||
bucket_id,
|
||||
object_key,
|
||||
content_type,
|
||||
size_bytes,
|
||||
checksum_sha256,
|
||||
storage_path,
|
||||
uploaded_by_user_id
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING *;
|
||||
|
||||
-- name: ListBucketObjects :many
|
||||
SELECT * FROM core.bucket_objects
|
||||
WHERE bucket_id = $1
|
||||
AND (btrim($2) = '' OR object_key ILIKE '%' || btrim($2) || '%')
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $3
|
||||
OFFSET $4;
|
||||
|
||||
-- name: CountBucketObjects :one
|
||||
SELECT COUNT(*)::BIGINT
|
||||
FROM core.bucket_objects
|
||||
WHERE bucket_id = $1
|
||||
AND (btrim($2) = '' OR object_key ILIKE '%' || btrim($2) || '%');
|
||||
|
||||
-- name: GetBucketObjectByKey :one
|
||||
SELECT * FROM core.bucket_objects
|
||||
WHERE bucket_id = $1
|
||||
AND object_key = $2;
|
||||
|
||||
-- name: MoveBucketObject :one
|
||||
UPDATE core.bucket_objects
|
||||
SET bucket_id = $3,
|
||||
object_key = $4,
|
||||
storage_path = $5
|
||||
WHERE bucket_id = $1
|
||||
AND object_key = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: DeleteBucketObjectByKey :one
|
||||
DELETE FROM core.bucket_objects
|
||||
WHERE bucket_id = $1
|
||||
AND object_key = $2
|
||||
RETURNING *;
|
||||
@@ -0,0 +1,149 @@
|
||||
-- name: CreateOrganization :one
|
||||
INSERT INTO core.organizations (slug, name)
|
||||
VALUES ($1, $2)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateOrganizationByID :one
|
||||
UPDATE core.organizations
|
||||
SET slug = $2,
|
||||
name = $3
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: ListOrganizationsForUser :many
|
||||
SELECT
|
||||
o.*,
|
||||
om.role AS membership_role
|
||||
FROM core.organizations o
|
||||
JOIN core.organization_members om ON om.organization_id = o.id
|
||||
WHERE om.user_id = $1
|
||||
ORDER BY o.created_at ASC;
|
||||
|
||||
-- name: GetOrganizationMembership :one
|
||||
SELECT
|
||||
om.*,
|
||||
o.name AS organization_name,
|
||||
o.slug AS organization_slug
|
||||
FROM core.organization_members om
|
||||
JOIN core.organizations o ON o.id = om.organization_id
|
||||
WHERE om.organization_id = $1
|
||||
AND om.user_id = $2;
|
||||
|
||||
-- name: ListOrganizationMembers :many
|
||||
SELECT
|
||||
om.organization_id,
|
||||
om.user_id,
|
||||
om.role,
|
||||
om.created_at,
|
||||
u.email,
|
||||
u.name,
|
||||
u.email_verified
|
||||
FROM core.organization_members om
|
||||
JOIN core.users u ON u.id = om.user_id
|
||||
WHERE om.organization_id = $1
|
||||
ORDER BY om.created_at ASC;
|
||||
|
||||
-- name: UpdateOrganizationMemberRole :one
|
||||
UPDATE core.organization_members
|
||||
SET role = $3
|
||||
WHERE organization_id = $1
|
||||
AND user_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: RemoveOrganizationMember :one
|
||||
DELETE FROM core.organization_members
|
||||
WHERE organization_id = $1
|
||||
AND user_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: RemoveProjectMembershipsForOrganizationUser :exec
|
||||
DELETE FROM core.project_members pm
|
||||
USING core.projects p
|
||||
WHERE pm.project_id = p.id
|
||||
AND p.organization_id = $1
|
||||
AND pm.user_id = $2;
|
||||
|
||||
-- name: CountOrganizationOwners :one
|
||||
SELECT COUNT(*)::BIGINT FROM core.organization_members
|
||||
WHERE organization_id = $1
|
||||
AND role = 'owner';
|
||||
|
||||
-- name: CreateInvitation :one
|
||||
INSERT INTO core.project_invitations (
|
||||
organization_id,
|
||||
project_id,
|
||||
email,
|
||||
org_role,
|
||||
project_role,
|
||||
token_hash,
|
||||
expires_at,
|
||||
invited_by_user_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
LOWER($3),
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetInvitationByTokenHash :one
|
||||
SELECT * FROM core.project_invitations
|
||||
WHERE token_hash = $1;
|
||||
|
||||
-- name: GetInvitationByIDForOrganization :one
|
||||
SELECT * FROM core.project_invitations
|
||||
WHERE organization_id = $1
|
||||
AND id = $2;
|
||||
|
||||
-- name: ListInvitationsForOrganization :many
|
||||
SELECT
|
||||
i.id,
|
||||
i.organization_id,
|
||||
i.project_id,
|
||||
p.name AS project_name,
|
||||
i.email,
|
||||
i.org_role,
|
||||
i.project_role,
|
||||
i.expires_at,
|
||||
i.accepted_at,
|
||||
i.invited_by_user_id,
|
||||
i.created_at
|
||||
FROM core.project_invitations i
|
||||
LEFT JOIN core.projects p ON p.id = i.project_id
|
||||
WHERE i.organization_id = $1
|
||||
ORDER BY i.created_at DESC;
|
||||
|
||||
-- name: DeletePendingInvitationByIDForOrganization :one
|
||||
DELETE FROM core.project_invitations
|
||||
WHERE organization_id = $1
|
||||
AND id = $2
|
||||
AND accepted_at IS NULL
|
||||
RETURNING *;
|
||||
|
||||
-- name: MarkInvitationAccepted :one
|
||||
UPDATE core.project_invitations
|
||||
SET accepted_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: AddOrganizationMember :one
|
||||
INSERT INTO core.organization_members (organization_id, user_id, role)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (organization_id, user_id) DO UPDATE
|
||||
SET role = EXCLUDED.role
|
||||
RETURNING *;
|
||||
|
||||
-- name: ListBucketsForOrganization :many
|
||||
SELECT b.id
|
||||
FROM core.buckets b
|
||||
JOIN core.projects p ON p.id = b.project_id
|
||||
WHERE p.organization_id = $1;
|
||||
|
||||
-- name: DeleteOrganizationByID :one
|
||||
DELETE FROM core.organizations
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
@@ -0,0 +1,146 @@
|
||||
-- name: ListProjectsForOrganization :many
|
||||
SELECT
|
||||
p.*,
|
||||
pm.role AS membership_role
|
||||
FROM core.projects p
|
||||
LEFT JOIN core.project_members pm
|
||||
ON pm.project_id = p.id
|
||||
AND pm.user_id = $2
|
||||
WHERE p.organization_id = $1
|
||||
AND (
|
||||
btrim($3) = ''
|
||||
OR p.slug ILIKE '%' || btrim($3) || '%'
|
||||
OR p.name ILIKE '%' || btrim($3) || '%'
|
||||
OR COALESCE(p.description, '') ILIKE '%' || btrim($3) || '%'
|
||||
)
|
||||
ORDER BY p.created_at ASC;
|
||||
|
||||
-- name: CreateProject :one
|
||||
INSERT INTO core.projects (
|
||||
organization_id,
|
||||
slug,
|
||||
name,
|
||||
description
|
||||
) VALUES ($1, $2, $3, $4)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateProjectByID :one
|
||||
UPDATE core.projects
|
||||
SET slug = $2,
|
||||
name = $3,
|
||||
description = $4
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
|
||||
-- name: AddProjectMember :one
|
||||
INSERT INTO core.project_members (project_id, user_id, role)
|
||||
VALUES ($1, $2, $3)
|
||||
ON CONFLICT (project_id, user_id) DO UPDATE
|
||||
SET role = EXCLUDED.role
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetProjectMembership :one
|
||||
SELECT
|
||||
pm.*,
|
||||
p.organization_id
|
||||
FROM core.project_members pm
|
||||
JOIN core.projects p ON p.id = pm.project_id
|
||||
WHERE pm.project_id = $1
|
||||
AND pm.user_id = $2;
|
||||
|
||||
-- name: ListProjectMembers :many
|
||||
SELECT
|
||||
pm.project_id,
|
||||
pm.user_id,
|
||||
pm.role,
|
||||
pm.created_at,
|
||||
u.email,
|
||||
u.name,
|
||||
u.email_verified
|
||||
FROM core.project_members pm
|
||||
JOIN core.users u ON u.id = pm.user_id
|
||||
WHERE pm.project_id = $1
|
||||
ORDER BY pm.created_at ASC;
|
||||
|
||||
-- name: UpdateProjectMemberRole :one
|
||||
UPDATE core.project_members
|
||||
SET role = $3
|
||||
WHERE project_id = $1
|
||||
AND user_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: RemoveProjectMember :one
|
||||
DELETE FROM core.project_members
|
||||
WHERE project_id = $1
|
||||
AND user_id = $2
|
||||
RETURNING *;
|
||||
|
||||
-- name: CountProjectAdmins :one
|
||||
SELECT COUNT(*)::BIGINT FROM core.project_members
|
||||
WHERE project_id = $1
|
||||
AND role = 'admin';
|
||||
|
||||
-- name: GetProjectByID :one
|
||||
SELECT * FROM core.projects
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: GetProjectOverview :one
|
||||
SELECT
|
||||
p.id AS project_id,
|
||||
p.organization_id,
|
||||
p.slug AS project_slug,
|
||||
p.name AS project_name,
|
||||
(
|
||||
SELECT COUNT(*)::BIGINT
|
||||
FROM core.project_members pm
|
||||
WHERE pm.project_id = p.id
|
||||
) AS member_count,
|
||||
(
|
||||
SELECT COUNT(*)::BIGINT
|
||||
FROM core.api_keys ak
|
||||
WHERE ak.project_id = p.id
|
||||
AND ak.revoked_at IS NULL
|
||||
) AS active_api_key_count,
|
||||
(
|
||||
SELECT COUNT(*)::BIGINT
|
||||
FROM core.buckets b
|
||||
WHERE b.project_id = p.id
|
||||
) AS bucket_count,
|
||||
(
|
||||
SELECT COUNT(*)::BIGINT
|
||||
FROM core.bucket_objects bo
|
||||
JOIN core.buckets b ON b.id = bo.bucket_id
|
||||
WHERE b.project_id = p.id
|
||||
) AS object_count,
|
||||
(
|
||||
SELECT COALESCE(SUM(bo.size_bytes), 0)::BIGINT
|
||||
FROM core.bucket_objects bo
|
||||
JOIN core.buckets b ON b.id = bo.bucket_id
|
||||
WHERE b.project_id = p.id
|
||||
) AS object_bytes_total,
|
||||
(
|
||||
SELECT COUNT(*)::BIGINT
|
||||
FROM core.project_invitations pi
|
||||
WHERE pi.organization_id = p.organization_id
|
||||
AND (pi.project_id IS NULL OR pi.project_id = p.id)
|
||||
AND pi.accepted_at IS NULL
|
||||
AND pi.expires_at > NOW()
|
||||
) AS pending_invitation_count,
|
||||
(
|
||||
SELECT COUNT(*)::BIGINT
|
||||
FROM core.audit_logs al
|
||||
WHERE al.project_id = p.id
|
||||
AND al.created_at >= NOW() - INTERVAL '24 hours'
|
||||
) AS audit_events_24h,
|
||||
(
|
||||
SELECT MAX(al.created_at)::TIMESTAMPTZ
|
||||
FROM core.audit_logs al
|
||||
WHERE al.project_id = p.id
|
||||
) AS last_audit_at
|
||||
FROM core.projects p
|
||||
WHERE p.id = $1;
|
||||
|
||||
-- name: DeleteProjectByID :one
|
||||
DELETE FROM core.projects
|
||||
WHERE id = $1
|
||||
RETURNING *;
|
||||
@@ -0,0 +1,35 @@
|
||||
-- name: UpsertUser :one
|
||||
INSERT INTO core.users (
|
||||
auth_subject,
|
||||
email,
|
||||
name,
|
||||
email_verified,
|
||||
updated_at,
|
||||
last_seen_at
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
NOW(),
|
||||
NOW()
|
||||
)
|
||||
ON CONFLICT (auth_subject) DO UPDATE
|
||||
SET
|
||||
email = EXCLUDED.email,
|
||||
name = EXCLUDED.name,
|
||||
email_verified = EXCLUDED.email_verified,
|
||||
updated_at = NOW(),
|
||||
last_seen_at = NOW()
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetUserByAuthSubject :one
|
||||
SELECT * FROM core.users
|
||||
WHERE auth_subject = $1;
|
||||
|
||||
-- name: GetUserByID :one
|
||||
SELECT * FROM core.users
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: CountOrganizations :one
|
||||
SELECT COUNT(*)::BIGINT FROM core.organizations;
|
||||
Reference in New Issue
Block a user