compose
This commit is contained in:
@@ -1,11 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS idx_document_tags_tag;
|
|
||||||
DROP TABLE IF EXISTS document_tags;
|
|
||||||
DROP INDEX IF EXISTS idx_document_versions_document;
|
|
||||||
DROP TABLE IF EXISTS document_versions;
|
|
||||||
DROP INDEX IF EXISTS idx_documents_deleted_at;
|
|
||||||
DROP INDEX IF EXISTS idx_documents_folder;
|
|
||||||
DROP TABLE IF EXISTS documents;
|
|
||||||
DROP INDEX IF EXISTS idx_folders_parent;
|
|
||||||
DROP TABLE IF EXISTS folders;
|
|
||||||
DROP TABLE IF EXISTS tags;
|
|
||||||
DROP TABLE IF EXISTS users;
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
|
||||||
|
|
||||||
CREATE TABLE users (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
username VARCHAR(100) NOT NULL UNIQUE,
|
|
||||||
password_hash VARCHAR(255) NOT NULL,
|
|
||||||
role VARCHAR(16) NOT NULL,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE folders (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
parent_id UUID REFERENCES folders(id) ON DELETE SET NULL,
|
|
||||||
path_cache VARCHAR(1000),
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
CONSTRAINT folders_parent_name_unique UNIQUE (parent_id, name)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_folders_parent ON folders(parent_id);
|
|
||||||
|
|
||||||
CREATE TABLE documents (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
filename VARCHAR(255) NOT NULL,
|
|
||||||
original_name VARCHAR(255) NOT NULL,
|
|
||||||
content_type VARCHAR(100),
|
|
||||||
folder_id UUID REFERENCES folders(id) ON DELETE SET NULL,
|
|
||||||
current_version INTEGER NOT NULL,
|
|
||||||
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
deleted_at TIMESTAMPTZ,
|
|
||||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_documents_folder ON documents(folder_id);
|
|
||||||
CREATE INDEX idx_documents_deleted_at ON documents(deleted_at);
|
|
||||||
|
|
||||||
CREATE TABLE document_versions (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
|
||||||
version_number INTEGER NOT NULL,
|
|
||||||
s3_key VARCHAR(500) NOT NULL,
|
|
||||||
size_bytes BIGINT NOT NULL,
|
|
||||||
checksum VARCHAR(64) NOT NULL,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
operations_summary JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
||||||
CONSTRAINT document_versions_unique_version UNIQUE (document_id, version_number)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_document_versions_document ON document_versions(document_id);
|
|
||||||
|
|
||||||
CREATE TABLE tags (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
label VARCHAR(100) NOT NULL UNIQUE,
|
|
||||||
color VARCHAR(7),
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE document_tags (
|
|
||||||
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
|
||||||
tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
|
||||||
assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
assigned_by UUID REFERENCES users(id),
|
|
||||||
PRIMARY KEY (document_id, tag_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_document_tags_tag ON document_tags(tag_id);
|
|
||||||
|
|
||||||
INSERT INTO users (id, username, password_hash, role)
|
|
||||||
VALUES (
|
|
||||||
gen_random_uuid(),
|
|
||||||
'admin',
|
|
||||||
'$argon2id$v=19$m=19456,t=2,p=1$UMkfsNut028fmZupy9JoQg$/YFvGQoEZ2hhMiDCyv68ZROF97GcwAxxRwRgwSbpX5U',
|
|
||||||
'admin'
|
|
||||||
);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE IF EXISTS public.tenants;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
CREATE TABLE public.tenants (
|
|
||||||
tenant_id UUID PRIMARY KEY,
|
|
||||||
slug TEXT NOT NULL UNIQUE,
|
|
||||||
storage_root TEXT,
|
|
||||||
quickwit_index TEXT,
|
|
||||||
status TEXT NOT NULL DEFAULT 'active',
|
|
||||||
config JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
DELETE FROM public.tenants
|
|
||||||
WHERE slug = 'admin';
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
INSERT INTO public.tenants (tenant_id, slug, storage_root, quickwit_index, status, config)
|
|
||||||
VALUES (
|
|
||||||
gen_random_uuid(),
|
|
||||||
'admin',
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
'active',
|
|
||||||
'{}'::jsonb
|
|
||||||
)
|
|
||||||
ON CONFLICT (slug) DO NOTHING;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE IF EXISTS public.user_memberships;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
CREATE TABLE public.user_memberships (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
tenant_id UUID NOT NULL REFERENCES public.tenants(tenant_id) ON DELETE CASCADE,
|
|
||||||
role TEXT NOT NULL,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
UNIQUE (user_id, tenant_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX user_memberships_tenant_id_idx ON public.user_memberships (tenant_id);
|
|
||||||
CREATE INDEX user_memberships_user_id_idx ON public.user_memberships (user_id);
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
DELETE FROM public.user_memberships
|
|
||||||
WHERE user_id IN (
|
|
||||||
SELECT id FROM public.users WHERE username = 'admin'
|
|
||||||
)
|
|
||||||
AND tenant_id IN (
|
|
||||||
SELECT tenant_id FROM public.tenants WHERE slug = 'admin'
|
|
||||||
);
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
INSERT INTO public.user_memberships (id, user_id, tenant_id, role)
|
|
||||||
SELECT gen_random_uuid(), u.id, t.tenant_id, 'admin'
|
|
||||||
FROM public.users AS u
|
|
||||||
JOIN public.tenants AS t ON t.slug = 'admin'
|
|
||||||
LEFT JOIN public.user_memberships AS um ON um.user_id = u.id AND um.tenant_id = t.tenant_id
|
|
||||||
WHERE u.username = 'admin' AND um.id IS NULL
|
|
||||||
ON CONFLICT (user_id, tenant_id) DO NOTHING;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE users
|
|
||||||
ADD COLUMN role VARCHAR(16) NOT NULL DEFAULT 'user';
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE users
|
|
||||||
DROP COLUMN role;
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS jobs_tenant_id_idx;
|
|
||||||
ALTER TABLE jobs DROP CONSTRAINT IF EXISTS jobs_tenant_id_fkey;
|
|
||||||
ALTER TABLE jobs DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS refresh_tokens_tenant_id_idx;
|
|
||||||
ALTER TABLE refresh_tokens DROP CONSTRAINT IF EXISTS refresh_tokens_tenant_id_fkey;
|
|
||||||
ALTER TABLE refresh_tokens DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS users_tenant_id_idx;
|
|
||||||
ALTER TABLE users DROP CONSTRAINT IF EXISTS users_tenant_id_fkey;
|
|
||||||
ALTER TABLE users DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS folders_tenant_id_idx;
|
|
||||||
ALTER TABLE folders DROP CONSTRAINT IF EXISTS folders_tenant_id_fkey;
|
|
||||||
ALTER TABLE folders DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS tags_tenant_id_idx;
|
|
||||||
ALTER TABLE tags DROP CONSTRAINT IF EXISTS tags_tenant_id_fkey;
|
|
||||||
ALTER TABLE tags DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS correspondents_tenant_id_idx;
|
|
||||||
ALTER TABLE correspondents DROP CONSTRAINT IF EXISTS correspondents_tenant_id_fkey;
|
|
||||||
ALTER TABLE correspondents DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS document_correspondents_tenant_id_idx;
|
|
||||||
ALTER TABLE document_correspondents DROP CONSTRAINT IF EXISTS document_correspondents_tenant_id_fkey;
|
|
||||||
ALTER TABLE document_correspondents DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS document_tags_tenant_id_idx;
|
|
||||||
ALTER TABLE document_tags DROP CONSTRAINT IF EXISTS document_tags_tenant_id_fkey;
|
|
||||||
ALTER TABLE document_tags DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS document_asset_objects_tenant_id_idx;
|
|
||||||
ALTER TABLE document_asset_objects DROP CONSTRAINT IF EXISTS document_asset_objects_tenant_id_fkey;
|
|
||||||
ALTER TABLE document_asset_objects DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS document_assets_tenant_id_idx;
|
|
||||||
ALTER TABLE document_assets DROP CONSTRAINT IF EXISTS document_assets_tenant_id_fkey;
|
|
||||||
ALTER TABLE document_assets DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS document_versions_tenant_id_idx;
|
|
||||||
ALTER TABLE document_versions DROP CONSTRAINT IF EXISTS document_versions_tenant_id_fkey;
|
|
||||||
ALTER TABLE document_versions DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS documents_tenant_id_idx;
|
|
||||||
ALTER TABLE documents DROP CONSTRAINT IF EXISTS documents_tenant_id_fkey;
|
|
||||||
ALTER TABLE documents DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
-- Add tenant_id to documents
|
|
||||||
ALTER TABLE documents ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE documents
|
|
||||||
SET tenant_id = (SELECT tenant_id FROM public.tenants WHERE slug = 'admin');
|
|
||||||
ALTER TABLE documents ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE documents
|
|
||||||
ADD CONSTRAINT documents_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX documents_tenant_id_idx ON documents (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to document_versions
|
|
||||||
ALTER TABLE document_versions ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE document_versions AS dv
|
|
||||||
SET tenant_id = d.tenant_id
|
|
||||||
FROM documents AS d
|
|
||||||
WHERE dv.document_id = d.id;
|
|
||||||
ALTER TABLE document_versions ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE document_versions
|
|
||||||
ADD CONSTRAINT document_versions_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX document_versions_tenant_id_idx ON document_versions (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to document_assets
|
|
||||||
ALTER TABLE document_assets ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE document_assets AS da
|
|
||||||
SET tenant_id = dv.tenant_id
|
|
||||||
FROM document_versions AS dv
|
|
||||||
WHERE da.document_version_id = dv.id;
|
|
||||||
ALTER TABLE document_assets ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE document_assets
|
|
||||||
ADD CONSTRAINT document_assets_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX document_assets_tenant_id_idx ON document_assets (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to document_asset_objects
|
|
||||||
ALTER TABLE document_asset_objects ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE document_asset_objects AS dao
|
|
||||||
SET tenant_id = da.tenant_id
|
|
||||||
FROM document_assets AS da
|
|
||||||
WHERE dao.asset_id = da.id;
|
|
||||||
ALTER TABLE document_asset_objects ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE document_asset_objects
|
|
||||||
ADD CONSTRAINT document_asset_objects_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX document_asset_objects_tenant_id_idx ON document_asset_objects (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to document_tags
|
|
||||||
ALTER TABLE document_tags ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE document_tags AS dt
|
|
||||||
SET tenant_id = d.tenant_id
|
|
||||||
FROM documents AS d
|
|
||||||
WHERE dt.document_id = d.id;
|
|
||||||
ALTER TABLE document_tags ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE document_tags
|
|
||||||
ADD CONSTRAINT document_tags_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX document_tags_tenant_id_idx ON document_tags (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to document_correspondents
|
|
||||||
ALTER TABLE document_correspondents ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE document_correspondents AS dc
|
|
||||||
SET tenant_id = d.tenant_id
|
|
||||||
FROM documents AS d
|
|
||||||
WHERE dc.document_id = d.id;
|
|
||||||
ALTER TABLE document_correspondents ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE document_correspondents
|
|
||||||
ADD CONSTRAINT document_correspondents_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX document_correspondents_tenant_id_idx ON document_correspondents (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to correspondents
|
|
||||||
ALTER TABLE correspondents ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE correspondents
|
|
||||||
SET tenant_id = (SELECT tenant_id FROM public.tenants WHERE slug = 'admin');
|
|
||||||
ALTER TABLE correspondents ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE correspondents
|
|
||||||
ADD CONSTRAINT correspondents_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX correspondents_tenant_id_idx ON correspondents (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to tags
|
|
||||||
ALTER TABLE tags ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE tags
|
|
||||||
SET tenant_id = (SELECT tenant_id FROM public.tenants WHERE slug = 'admin');
|
|
||||||
ALTER TABLE tags ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE tags
|
|
||||||
ADD CONSTRAINT tags_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX tags_tenant_id_idx ON tags (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to folders
|
|
||||||
ALTER TABLE folders ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE folders
|
|
||||||
SET tenant_id = (SELECT tenant_id FROM public.tenants WHERE slug = 'admin');
|
|
||||||
ALTER TABLE folders ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE folders
|
|
||||||
ADD CONSTRAINT folders_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX folders_tenant_id_idx ON folders (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to users
|
|
||||||
ALTER TABLE users ADD COLUMN tenant_id UUID;
|
|
||||||
-- Add tenant_id to refresh_tokens
|
|
||||||
ALTER TABLE refresh_tokens ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE refresh_tokens AS rt
|
|
||||||
SET tenant_id = (
|
|
||||||
SELECT um.tenant_id
|
|
||||||
FROM user_memberships AS um
|
|
||||||
WHERE um.user_id = rt.user_id
|
|
||||||
LIMIT 1
|
|
||||||
);
|
|
||||||
ALTER TABLE refresh_tokens ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE refresh_tokens
|
|
||||||
ADD CONSTRAINT refresh_tokens_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX refresh_tokens_tenant_id_idx ON refresh_tokens (tenant_id);
|
|
||||||
|
|
||||||
-- Add tenant_id to jobs
|
|
||||||
ALTER TABLE jobs ADD COLUMN tenant_id UUID;
|
|
||||||
UPDATE jobs
|
|
||||||
SET tenant_id = (
|
|
||||||
SELECT um.tenant_id
|
|
||||||
FROM user_memberships AS um
|
|
||||||
WHERE um.user_id = jobs.user_id
|
|
||||||
LIMIT 1
|
|
||||||
);
|
|
||||||
ALTER TABLE jobs ALTER COLUMN tenant_id SET NOT NULL;
|
|
||||||
ALTER TABLE jobs
|
|
||||||
ADD CONSTRAINT jobs_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(tenant_id);
|
|
||||||
CREATE INDEX jobs_tenant_id_idx ON jobs (tenant_id);
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE public.tenants RENAME COLUMN id TO tenant_id;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE public.tenants RENAME COLUMN tenant_id TO id;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS tenants_quickwit_index_unique;
|
|
||||||
DROP INDEX IF EXISTS tenants_storage_root_unique;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
CREATE UNIQUE INDEX tenants_storage_root_unique
|
|
||||||
ON public.tenants (storage_root)
|
|
||||||
WHERE storage_root IS NOT NULL;
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX tenants_quickwit_index_unique
|
|
||||||
ON public.tenants (quickwit_index)
|
|
||||||
WHERE quickwit_index IS NOT NULL;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ALTER TABLE users ADD COLUMN tenant_id UUID;
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ALTER TABLE users DROP CONSTRAINT IF EXISTS users_tenant_id_fkey;
|
|
||||||
DROP INDEX IF EXISTS users_tenant_id_idx;
|
|
||||||
ALTER TABLE users DROP COLUMN IF EXISTS tenant_id;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
DROP TRIGGER IF EXISTS trg_jobs_updated_at ON jobs;
|
|
||||||
DROP FUNCTION IF EXISTS touch_jobs_updated_at;
|
|
||||||
DROP INDEX IF EXISTS idx_jobs_job_type;
|
|
||||||
DROP INDEX IF EXISTS idx_jobs_status_run_after;
|
|
||||||
DROP TABLE IF EXISTS jobs;
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
CREATE TABLE jobs (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
job_type TEXT NOT NULL,
|
|
||||||
payload JSONB NOT NULL,
|
|
||||||
status TEXT NOT NULL DEFAULT 'queued',
|
|
||||||
attempts INTEGER NOT NULL DEFAULT 0,
|
|
||||||
run_after TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
last_error TEXT,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
CONSTRAINT jobs_status_check CHECK (status IN ('queued', 'processing', 'succeeded', 'failed'))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_jobs_status_run_after ON jobs (status, run_after);
|
|
||||||
CREATE INDEX idx_jobs_job_type ON jobs (job_type);
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION touch_jobs_updated_at()
|
|
||||||
RETURNS TRIGGER AS $$
|
|
||||||
BEGIN
|
|
||||||
NEW.updated_at = now();
|
|
||||||
RETURN NEW;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
CREATE TRIGGER trg_jobs_updated_at
|
|
||||||
BEFORE UPDATE ON jobs
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE FUNCTION touch_jobs_updated_at();
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS idx_document_assets_type;
|
|
||||||
DROP INDEX IF EXISTS idx_document_assets_version;
|
|
||||||
DROP TABLE IF EXISTS document_assets;
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
CREATE TABLE document_assets (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
document_version_id UUID NOT NULL REFERENCES document_versions(id) ON DELETE CASCADE,
|
|
||||||
asset_type TEXT NOT NULL,
|
|
||||||
s3_key TEXT NOT NULL,
|
|
||||||
mime_type TEXT NOT NULL,
|
|
||||||
width INTEGER,
|
|
||||||
height INTEGER,
|
|
||||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
CONSTRAINT document_assets_unique UNIQUE (document_version_id, asset_type)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_document_assets_version ON document_assets(document_version_id);
|
|
||||||
CREATE INDEX idx_document_assets_type ON document_assets(asset_type);
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS folders_parent_name_unique_idx;
|
|
||||||
|
|
||||||
ALTER TABLE folders
|
|
||||||
ADD CONSTRAINT folders_parent_name_unique UNIQUE (parent_id, name);
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
ALTER TABLE folders
|
|
||||||
DROP CONSTRAINT IF EXISTS folders_parent_name_unique;
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX folders_parent_name_unique_idx
|
|
||||||
ON folders (COALESCE(parent_id, '00000000-0000-0000-0000-000000000000'::uuid), name);
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE documents
|
|
||||||
DROP COLUMN issued_at;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE documents
|
|
||||||
ADD COLUMN issued_at TIMESTAMPTZ;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE documents
|
|
||||||
DROP COLUMN name;
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
ALTER TABLE documents
|
|
||||||
ADD COLUMN name VARCHAR(255);
|
|
||||||
|
|
||||||
UPDATE documents
|
|
||||||
SET name = CASE
|
|
||||||
WHEN filename ~ '\\.[^./]+$' THEN regexp_replace(filename, '\\.[^./]+$', '')
|
|
||||||
ELSE filename
|
|
||||||
END;
|
|
||||||
|
|
||||||
ALTER TABLE documents
|
|
||||||
ALTER COLUMN name SET NOT NULL;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE documents
|
|
||||||
RENAME COLUMN title TO name;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE documents
|
|
||||||
RENAME COLUMN name TO title;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
DROP TABLE refresh_tokens;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
CREATE TABLE refresh_tokens (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
token_hash TEXT NOT NULL,
|
|
||||||
issued_at TIMESTAMPTZ NOT NULL,
|
|
||||||
expires_at TIMESTAMPTZ NOT NULL,
|
|
||||||
revoked_at TIMESTAMPTZ,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_refresh_tokens_user_id ON refresh_tokens(user_id);
|
|
||||||
CREATE INDEX idx_refresh_tokens_token_hash ON refresh_tokens(token_hash);
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
-- Restore width/height columns and repopulate from metadata where available.
|
|
||||||
|
|
||||||
ALTER TABLE document_assets
|
|
||||||
ADD COLUMN width INTEGER,
|
|
||||||
ADD COLUMN height INTEGER;
|
|
||||||
|
|
||||||
UPDATE document_assets
|
|
||||||
SET width = (metadata->>'width')::INTEGER
|
|
||||||
WHERE metadata ? 'width';
|
|
||||||
|
|
||||||
UPDATE document_assets
|
|
||||||
SET height = (metadata->>'height')::INTEGER
|
|
||||||
WHERE metadata ? 'height';
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
-- Backfill existing width/height values into metadata then drop the columns.
|
|
||||||
|
|
||||||
UPDATE document_assets
|
|
||||||
SET metadata = metadata || jsonb_build_object('width', width)
|
|
||||||
WHERE width IS NOT NULL
|
|
||||||
AND NOT (metadata ? 'width');
|
|
||||||
|
|
||||||
UPDATE document_assets
|
|
||||||
SET metadata = metadata || jsonb_build_object('height', height)
|
|
||||||
WHERE height IS NOT NULL
|
|
||||||
AND NOT (metadata ? 'height');
|
|
||||||
|
|
||||||
ALTER TABLE document_assets
|
|
||||||
DROP COLUMN width,
|
|
||||||
DROP COLUMN height;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
ALTER TABLE documents ADD COLUMN current_version INT4;
|
|
||||||
|
|
||||||
UPDATE documents AS d
|
|
||||||
SET current_version = dv.version_number
|
|
||||||
FROM document_versions AS dv
|
|
||||||
WHERE dv.id = d.current_version_id;
|
|
||||||
|
|
||||||
ALTER TABLE documents
|
|
||||||
ALTER COLUMN current_version SET NOT NULL;
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS idx_documents_current_version_id;
|
|
||||||
|
|
||||||
ALTER TABLE documents
|
|
||||||
DROP CONSTRAINT IF EXISTS documents_current_version_fk;
|
|
||||||
|
|
||||||
ALTER TABLE documents
|
|
||||||
DROP COLUMN current_version_id;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
ALTER TABLE documents ADD COLUMN current_version_id UUID;
|
|
||||||
|
|
||||||
UPDATE documents AS d
|
|
||||||
SET current_version_id = dv.id
|
|
||||||
FROM document_versions AS dv
|
|
||||||
WHERE dv.document_id = d.id
|
|
||||||
AND dv.version_number = d.current_version;
|
|
||||||
|
|
||||||
ALTER TABLE documents
|
|
||||||
ALTER COLUMN current_version_id SET NOT NULL;
|
|
||||||
|
|
||||||
ALTER TABLE documents
|
|
||||||
ADD CONSTRAINT documents_current_version_fk
|
|
||||||
FOREIGN KEY (current_version_id)
|
|
||||||
REFERENCES document_versions(id)
|
|
||||||
DEFERRABLE INITIALLY DEFERRED;
|
|
||||||
|
|
||||||
CREATE INDEX idx_documents_current_version_id
|
|
||||||
ON documents(current_version_id);
|
|
||||||
|
|
||||||
ALTER TABLE documents DROP COLUMN current_version;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS idx_document_correspondents_role;
|
|
||||||
DROP INDEX IF EXISTS idx_document_correspondents_correspondent;
|
|
||||||
DROP INDEX IF EXISTS idx_document_correspondents_document;
|
|
||||||
DROP TABLE IF EXISTS document_correspondents;
|
|
||||||
DROP TABLE IF EXISTS correspondents;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
CREATE TABLE correspondents (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
CONSTRAINT correspondents_name_unique UNIQUE (name)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE document_correspondents (
|
|
||||||
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
|
||||||
correspondent_id UUID NOT NULL REFERENCES correspondents(id) ON DELETE CASCADE,
|
|
||||||
role VARCHAR(32) NOT NULL,
|
|
||||||
assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
||||||
assigned_by UUID REFERENCES users(id),
|
|
||||||
PRIMARY KEY (document_id, correspondent_id, role),
|
|
||||||
CONSTRAINT document_correspondents_role_check CHECK (role IN ('sender', 'receiver', 'other'))
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_document_correspondents_document
|
|
||||||
ON document_correspondents(document_id);
|
|
||||||
|
|
||||||
CREATE INDEX idx_document_correspondents_correspondent
|
|
||||||
ON document_correspondents(correspondent_id);
|
|
||||||
|
|
||||||
CREATE INDEX idx_document_correspondents_role
|
|
||||||
ON document_correspondents(role);
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS idx_documents_folder_filename;
|
|
||||||
DROP INDEX IF EXISTS idx_documents_folder_title;
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
CREATE INDEX idx_documents_folder_title
|
|
||||||
ON documents (
|
|
||||||
COALESCE(folder_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
||||||
title
|
|
||||||
)
|
|
||||||
WHERE deleted_at IS NULL;
|
|
||||||
|
|
||||||
CREATE INDEX idx_documents_folder_filename
|
|
||||||
ON documents (
|
|
||||||
COALESCE(folder_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
||||||
filename
|
|
||||||
)
|
|
||||||
WHERE deleted_at IS NULL;
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS documents_unique_folder_filename;
|
|
||||||
DROP INDEX IF EXISTS idx_documents_folder_title;
|
|
||||||
|
|
||||||
CREATE INDEX idx_documents_folder_title
|
|
||||||
ON documents (
|
|
||||||
COALESCE(folder_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
||||||
title
|
|
||||||
)
|
|
||||||
WHERE deleted_at IS NULL;
|
|
||||||
|
|
||||||
CREATE INDEX idx_documents_folder_filename
|
|
||||||
ON documents (
|
|
||||||
COALESCE(folder_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
||||||
filename
|
|
||||||
)
|
|
||||||
WHERE deleted_at IS NULL;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
DROP INDEX IF EXISTS idx_documents_folder_title;
|
|
||||||
DROP INDEX IF EXISTS idx_documents_folder_filename;
|
|
||||||
DROP INDEX IF EXISTS documents_unique_folder_title;
|
|
||||||
DROP INDEX IF EXISTS documents_unique_folder_filename;
|
|
||||||
|
|
||||||
CREATE INDEX idx_documents_folder_title
|
|
||||||
ON documents (
|
|
||||||
COALESCE(folder_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
||||||
title
|
|
||||||
)
|
|
||||||
WHERE deleted_at IS NULL;
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX documents_unique_folder_filename
|
|
||||||
ON documents (
|
|
||||||
COALESCE(folder_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
||||||
filename
|
|
||||||
)
|
|
||||||
WHERE deleted_at IS NULL;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE document_versions
|
|
||||||
DROP COLUMN metadata;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE document_versions
|
|
||||||
ADD COLUMN metadata JSONB NOT NULL DEFAULT '{}'::jsonb;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE folders
|
|
||||||
ADD COLUMN path_cache VARCHAR(1000);
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE folders
|
|
||||||
DROP COLUMN path_cache;
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
ALTER TABLE document_assets
|
|
||||||
ADD COLUMN s3_key TEXT;
|
|
||||||
|
|
||||||
UPDATE document_assets AS da
|
|
||||||
SET s3_key = dao.s3_key
|
|
||||||
FROM document_asset_objects AS dao
|
|
||||||
WHERE dao.asset_id = da.id
|
|
||||||
AND dao.ordinal = 1
|
|
||||||
AND da.s3_key IS NULL;
|
|
||||||
|
|
||||||
ALTER TABLE document_assets
|
|
||||||
ALTER COLUMN s3_key SET NOT NULL;
|
|
||||||
|
|
||||||
UPDATE document_assets AS da
|
|
||||||
SET metadata = jsonb_set(da.metadata, '{width}', dao.metadata->'width', true)
|
|
||||||
FROM document_asset_objects AS dao
|
|
||||||
WHERE dao.asset_id = da.id
|
|
||||||
AND dao.ordinal = 1
|
|
||||||
AND dao.metadata ? 'width';
|
|
||||||
|
|
||||||
UPDATE document_assets AS da
|
|
||||||
SET metadata = jsonb_set(da.metadata, '{height}', dao.metadata->'height', true)
|
|
||||||
FROM document_asset_objects AS dao
|
|
||||||
WHERE dao.asset_id = da.id
|
|
||||||
AND dao.ordinal = 1
|
|
||||||
AND dao.metadata ? 'height';
|
|
||||||
|
|
||||||
DROP INDEX IF EXISTS idx_document_asset_objects_asset_ordinal;
|
|
||||||
DROP TABLE IF EXISTS document_asset_objects;
|
|
||||||
|
|
||||||
ALTER TABLE document_assets
|
|
||||||
DROP CONSTRAINT IF EXISTS document_assets_cardinality_positive;
|
|
||||||
ALTER TABLE document_assets
|
|
||||||
DROP COLUMN IF EXISTS cardinality;
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
ALTER TABLE document_assets
|
|
||||||
ADD COLUMN cardinality INTEGER,
|
|
||||||
ADD CONSTRAINT document_assets_cardinality_positive CHECK (cardinality IS NULL OR cardinality >= 1);
|
|
||||||
|
|
||||||
CREATE TABLE document_asset_objects (
|
|
||||||
id UUID PRIMARY KEY,
|
|
||||||
asset_id UUID NOT NULL REFERENCES document_assets(id) ON DELETE CASCADE,
|
|
||||||
ordinal INTEGER NOT NULL,
|
|
||||||
s3_key TEXT NOT NULL,
|
|
||||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
||||||
CONSTRAINT document_asset_objects_ordinal_positive CHECK (ordinal >= 1),
|
|
||||||
CONSTRAINT document_asset_objects_asset_ordinal_unique UNIQUE (asset_id, ordinal)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_document_asset_objects_asset_ordinal
|
|
||||||
ON document_asset_objects (asset_id, ordinal);
|
|
||||||
|
|
||||||
INSERT INTO document_asset_objects (id, asset_id, ordinal, s3_key, metadata)
|
|
||||||
SELECT
|
|
||||||
gen_random_uuid(),
|
|
||||||
id,
|
|
||||||
1,
|
|
||||||
s3_key,
|
|
||||||
'{}'::jsonb
|
|
||||||
FROM document_assets;
|
|
||||||
|
|
||||||
UPDATE document_asset_objects AS dao
|
|
||||||
SET metadata = jsonb_set(dao.metadata, '{width}', da.metadata->'width', true)
|
|
||||||
FROM document_assets AS da
|
|
||||||
WHERE dao.asset_id = da.id
|
|
||||||
AND dao.ordinal = 1
|
|
||||||
AND da.metadata ? 'width';
|
|
||||||
|
|
||||||
UPDATE document_asset_objects AS dao
|
|
||||||
SET metadata = jsonb_set(dao.metadata, '{height}', da.metadata->'height', true)
|
|
||||||
FROM document_assets AS da
|
|
||||||
WHERE dao.asset_id = da.id
|
|
||||||
AND dao.ordinal = 1
|
|
||||||
AND da.metadata ? 'height';
|
|
||||||
|
|
||||||
UPDATE document_assets
|
|
||||||
SET metadata = metadata - 'width'
|
|
||||||
WHERE metadata ? 'width';
|
|
||||||
|
|
||||||
UPDATE document_assets
|
|
||||||
SET metadata = metadata - 'height'
|
|
||||||
WHERE metadata ? 'height';
|
|
||||||
|
|
||||||
UPDATE document_assets
|
|
||||||
SET cardinality = 1
|
|
||||||
WHERE cardinality IS NULL;
|
|
||||||
|
|
||||||
ALTER TABLE document_assets
|
|
||||||
DROP COLUMN s3_key;
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
DROP TRIGGER IF EXISTS trg_jobs_updated_at ON jobs;
|
||||||
|
DROP FUNCTION IF EXISTS touch_jobs_updated_at();
|
||||||
|
|
||||||
|
ALTER TABLE documents DROP CONSTRAINT IF EXISTS documents_current_version_fk;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS document_asset_objects;
|
||||||
|
DROP TABLE IF EXISTS document_assets;
|
||||||
|
DROP TABLE IF EXISTS document_versions;
|
||||||
|
DROP TABLE IF EXISTS document_tags;
|
||||||
|
DROP TABLE IF EXISTS document_correspondents;
|
||||||
|
DROP TABLE IF EXISTS correspondents;
|
||||||
|
DROP TABLE IF EXISTS documents;
|
||||||
|
DROP TABLE IF EXISTS folders;
|
||||||
|
DROP TABLE IF EXISTS tags;
|
||||||
|
DROP TABLE IF EXISTS jobs;
|
||||||
|
DROP TABLE IF EXISTS refresh_tokens;
|
||||||
|
DROP TABLE IF EXISTS user_memberships;
|
||||||
|
DROP TABLE IF EXISTS users;
|
||||||
|
DROP TABLE IF EXISTS tenants;
|
||||||
|
|
||||||
|
DROP EXTENSION IF EXISTS "pgcrypto";
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||||
|
|
||||||
|
CREATE TABLE tenants (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
slug TEXT NOT NULL UNIQUE,
|
||||||
|
storage_root TEXT,
|
||||||
|
quickwit_index TEXT,
|
||||||
|
status TEXT NOT NULL DEFAULT 'active',
|
||||||
|
config JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX tenants_storage_root_unique
|
||||||
|
ON tenants (storage_root)
|
||||||
|
WHERE storage_root IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX tenants_quickwit_index_unique
|
||||||
|
ON tenants (quickwit_index)
|
||||||
|
WHERE quickwit_index IS NOT NULL;
|
||||||
|
|
||||||
|
CREATE TABLE users (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
username VARCHAR(100) NOT NULL UNIQUE,
|
||||||
|
password_hash VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE user_memberships (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
role TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
UNIQUE (user_id, tenant_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX user_memberships_tenant_id_idx ON user_memberships (tenant_id);
|
||||||
|
CREATE INDEX user_memberships_user_id_idx ON user_memberships (user_id);
|
||||||
|
|
||||||
|
CREATE TABLE folders (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
parent_id UUID REFERENCES folders(id) ON DELETE SET NULL,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_folders_parent ON folders(parent_id);
|
||||||
|
CREATE INDEX folders_tenant_id_idx ON folders(tenant_id);
|
||||||
|
CREATE UNIQUE INDEX folders_parent_name_unique_idx
|
||||||
|
ON folders (COALESCE(parent_id, '00000000-0000-0000-0000-000000000000'::uuid), name);
|
||||||
|
|
||||||
|
CREATE TABLE documents (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
filename VARCHAR(255) NOT NULL,
|
||||||
|
original_name VARCHAR(255) NOT NULL,
|
||||||
|
content_type VARCHAR(100),
|
||||||
|
folder_id UUID REFERENCES folders(id) ON DELETE SET NULL,
|
||||||
|
uploaded_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
issued_at TIMESTAMPTZ,
|
||||||
|
title VARCHAR(255) NOT NULL,
|
||||||
|
current_version_id UUID NOT NULL,
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_documents_folder ON documents (folder_id);
|
||||||
|
CREATE INDEX idx_documents_deleted_at ON documents (deleted_at);
|
||||||
|
CREATE INDEX documents_tenant_id_idx ON documents (tenant_id);
|
||||||
|
CREATE INDEX idx_documents_current_version_id ON documents (current_version_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_documents_folder_title
|
||||||
|
ON documents (
|
||||||
|
COALESCE(folder_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
||||||
|
title
|
||||||
|
)
|
||||||
|
WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX documents_unique_folder_filename
|
||||||
|
ON documents (
|
||||||
|
COALESCE(folder_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
||||||
|
filename
|
||||||
|
)
|
||||||
|
WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE TABLE document_versions (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
||||||
|
version_number INT NOT NULL,
|
||||||
|
s3_key VARCHAR(500) NOT NULL,
|
||||||
|
size_bytes BIGINT NOT NULL,
|
||||||
|
checksum VARCHAR(64) NOT NULL,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
operations_summary JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||||
|
CONSTRAINT document_versions_unique_version UNIQUE (document_id, version_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_document_versions_document ON document_versions (document_id);
|
||||||
|
CREATE INDEX document_versions_tenant_id_idx ON document_versions (tenant_id);
|
||||||
|
|
||||||
|
ALTER TABLE documents
|
||||||
|
ADD CONSTRAINT documents_current_version_fk
|
||||||
|
FOREIGN KEY (current_version_id)
|
||||||
|
REFERENCES document_versions(id)
|
||||||
|
DEFERRABLE INITIALLY DEFERRED;
|
||||||
|
|
||||||
|
CREATE TABLE tags (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
label VARCHAR(100) NOT NULL UNIQUE,
|
||||||
|
color VARCHAR(7),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX tags_tenant_id_idx ON tags (tenant_id);
|
||||||
|
|
||||||
|
CREATE TABLE document_tags (
|
||||||
|
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
||||||
|
tag_id UUID NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
|
||||||
|
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
assigned_by UUID REFERENCES users(id),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||||
|
PRIMARY KEY (document_id, tag_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_document_tags_tag ON document_tags (tag_id);
|
||||||
|
CREATE INDEX document_tags_tenant_id_idx ON document_tags (tenant_id);
|
||||||
|
|
||||||
|
CREATE TABLE correspondents (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||||
|
CONSTRAINT correspondents_name_unique UNIQUE (name)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX correspondents_tenant_id_idx ON correspondents (tenant_id);
|
||||||
|
|
||||||
|
CREATE TABLE document_correspondents (
|
||||||
|
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
||||||
|
correspondent_id UUID NOT NULL REFERENCES correspondents(id) ON DELETE CASCADE,
|
||||||
|
role VARCHAR(32) NOT NULL,
|
||||||
|
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
assigned_by UUID REFERENCES users(id),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||||
|
PRIMARY KEY (document_id, correspondent_id, role),
|
||||||
|
CONSTRAINT document_correspondents_role_check CHECK (role IN ('sender', 'receiver', 'other'))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_document_correspondents_document ON document_correspondents (document_id);
|
||||||
|
CREATE INDEX idx_document_correspondents_correspondent ON document_correspondents (correspondent_id);
|
||||||
|
CREATE INDEX idx_document_correspondents_role ON document_correspondents (role);
|
||||||
|
CREATE INDEX document_correspondents_tenant_id_idx ON document_correspondents (tenant_id);
|
||||||
|
|
||||||
|
CREATE TABLE document_assets (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
document_version_id UUID NOT NULL REFERENCES document_versions(id) ON DELETE CASCADE,
|
||||||
|
asset_type TEXT NOT NULL,
|
||||||
|
mime_type TEXT NOT NULL,
|
||||||
|
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
cardinality INTEGER,
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||||
|
CONSTRAINT document_assets_unique UNIQUE (document_version_id, asset_type),
|
||||||
|
CONSTRAINT document_assets_cardinality_positive CHECK (cardinality IS NULL OR cardinality >= 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_document_assets_version ON document_assets (document_version_id);
|
||||||
|
CREATE INDEX idx_document_assets_type ON document_assets (asset_type);
|
||||||
|
CREATE INDEX document_assets_tenant_id_idx ON document_assets (tenant_id);
|
||||||
|
|
||||||
|
CREATE TABLE document_asset_objects (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
asset_id UUID NOT NULL REFERENCES document_assets(id) ON DELETE CASCADE,
|
||||||
|
ordinal INTEGER NOT NULL,
|
||||||
|
s3_key TEXT NOT NULL,
|
||||||
|
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||||
|
CONSTRAINT document_asset_objects_ordinal_positive CHECK (ordinal >= 1),
|
||||||
|
CONSTRAINT document_asset_objects_asset_ordinal_unique UNIQUE (asset_id, ordinal)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_document_asset_objects_asset_ordinal
|
||||||
|
ON document_asset_objects (asset_id, ordinal);
|
||||||
|
|
||||||
|
CREATE INDEX document_asset_objects_tenant_id_idx
|
||||||
|
ON document_asset_objects (tenant_id);
|
||||||
|
|
||||||
|
CREATE TABLE jobs (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
job_type TEXT NOT NULL,
|
||||||
|
payload JSONB NOT NULL,
|
||||||
|
status TEXT NOT NULL DEFAULT 'queued',
|
||||||
|
attempts INTEGER NOT NULL DEFAULT 0,
|
||||||
|
run_after TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
last_error TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id),
|
||||||
|
CONSTRAINT jobs_status_check CHECK (status IN ('queued', 'processing', 'succeeded', 'failed'))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_jobs_status_run_after ON jobs (status, run_after);
|
||||||
|
CREATE INDEX idx_jobs_job_type ON jobs (job_type);
|
||||||
|
CREATE INDEX jobs_tenant_id_idx ON jobs (tenant_id);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION touch_jobs_updated_at()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = NOW();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER trg_jobs_updated_at
|
||||||
|
BEFORE UPDATE ON jobs
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION touch_jobs_updated_at();
|
||||||
|
|
||||||
|
CREATE TABLE refresh_tokens (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
token_hash TEXT NOT NULL,
|
||||||
|
issued_at TIMESTAMPTZ NOT NULL,
|
||||||
|
expires_at TIMESTAMPTZ NOT NULL,
|
||||||
|
revoked_at TIMESTAMPTZ,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES tenants(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_refresh_tokens_user_id ON refresh_tokens (user_id);
|
||||||
|
CREATE INDEX idx_refresh_tokens_token_hash ON refresh_tokens (token_hash);
|
||||||
|
CREATE INDEX refresh_tokens_tenant_id_idx ON refresh_tokens (tenant_id);
|
||||||
+33
-14
@@ -10,19 +10,19 @@ services:
|
|||||||
tmpfs:
|
tmpfs:
|
||||||
- /var/lib/postgresql/data
|
- /var/lib/postgresql/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "pg_isready -U papercrate"]
|
test: ["CMD-SHELL", "pg_isready -U papercrate -d papercrate_test"]
|
||||||
interval: 5s
|
interval: 5s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
quickwit:
|
quickwit-test:
|
||||||
image: quickwit/quickwit:0.8.2
|
image: quickwit/quickwit:0.8.2
|
||||||
command: ["run"]
|
command: ["run"]
|
||||||
environment:
|
environment:
|
||||||
QW_ENABLE_API_AUTH: "false"
|
QW_ENABLE_API_AUTH: "false"
|
||||||
QW_DATA_DIR: /quickwit/data
|
QW_DATA_DIR: /quickwit/data
|
||||||
ports:
|
ports:
|
||||||
- "7280:7280"
|
- "7281:7280"
|
||||||
volumes:
|
volumes:
|
||||||
- quickwit_test_data:/quickwit/data
|
- quickwit_test_data:/quickwit/data
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@@ -31,21 +31,40 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
quickwit-create-index:
|
admin-bootstrap:
|
||||||
image: quickwit/quickwit:0.8.2
|
build:
|
||||||
|
context: ./backend
|
||||||
depends_on:
|
depends_on:
|
||||||
quickwit:
|
postgres-test:
|
||||||
|
condition: service_healthy
|
||||||
|
quickwit-test:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
volumes:
|
|
||||||
- quickwit_test_data:/quickwit/data
|
|
||||||
- ./quickwit/documents-index.yaml:/tmp/documents-index.yaml:ro
|
|
||||||
environment:
|
environment:
|
||||||
QW_DATA_DIR: /quickwit/data
|
DATABASE_URL: postgres://papercrate:papercrate_test@postgres-test:5432/papercrate_test
|
||||||
QW_NODE_URI: http://quickwit:7280
|
DATABASE_MAX_POOL_SIZE: 1
|
||||||
entrypoint: ["/bin/sh", "-c"]
|
S3_BUCKET: documents
|
||||||
|
JWT_SECRET: change-me-super-secret
|
||||||
|
QUICKWIT_ENDPOINT: http://quickwit-test:7280
|
||||||
|
DEFAULT_TENANT_SLUG: admin
|
||||||
|
entrypoint: []
|
||||||
command: >
|
command: >
|
||||||
quickwit index list --node-uri http://quickwit:7280 2>/dev/null | grep -q '"documents"' \
|
/bin/sh -c "
|
||||||
|| quickwit index create --index-config /tmp/documents-index.yaml --node-uri http://quickwit:7280
|
echo 'Running database migrations' &&
|
||||||
|
diesel migration run &&
|
||||||
|
echo 'Ensuring tenant admin exists' &&
|
||||||
|
if papercrate-admin list-tenants | grep -q '^admin '; then
|
||||||
|
echo 'tenant admin already exists';
|
||||||
|
else
|
||||||
|
papercrate-admin create-tenant admin;
|
||||||
|
fi &&
|
||||||
|
echo 'Ensuring demo user credentials' &&
|
||||||
|
(papercrate-admin create-user admin adminadmin || papercrate-admin set-password admin adminadmin) &&
|
||||||
|
echo 'Ensuring demo membership' &&
|
||||||
|
papercrate-admin add-user-to-tenant admin admin admin &&
|
||||||
|
echo 'Ensuring Quickwit index for admin tenant' &&
|
||||||
|
papercrate-admin quickwit-create-index admin
|
||||||
|
"
|
||||||
|
user: root
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
+35
-6
@@ -62,15 +62,44 @@ services:
|
|||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
quickwit-create-index:
|
admin-bootstrap:
|
||||||
image: quickwit/quickwit:0.8.2
|
build:
|
||||||
|
context: ./backend
|
||||||
depends_on:
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
quickwit:
|
quickwit:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
volumes:
|
environment:
|
||||||
- quickwit_data:/quickwit/data
|
DATABASE_URL: postgres://papercrate:papercrate_dev@postgres:5432/papercrate
|
||||||
- ./quickwit/documents-index.yaml:/tmp/documents-index.yaml:ro
|
DATABASE_MAX_POOL_SIZE: 2
|
||||||
command: index create --endpoint=http://quickwit:7280 --index-config /tmp/documents-index.yaml
|
AWS_ENDPOINT_URL: http://minio:9000
|
||||||
|
AWS_ACCESS_KEY_ID: minioadmin
|
||||||
|
AWS_SECRET_ACCESS_KEY: minioadmin
|
||||||
|
AWS_REGION: us-east-1
|
||||||
|
S3_BUCKET: documents
|
||||||
|
JWT_SECRET: change-me-super-secret
|
||||||
|
QUICKWIT_ENDPOINT: http://quickwit:7280
|
||||||
|
DEFAULT_TENANT_SLUG: admin
|
||||||
|
entrypoint: []
|
||||||
|
command: >
|
||||||
|
/bin/sh -c "
|
||||||
|
echo 'Running database migrations' &&
|
||||||
|
diesel migration run &&
|
||||||
|
echo 'Ensuring tenant admin exists' &&
|
||||||
|
if papercrate-admin list-tenants | grep -q '^admin '; then
|
||||||
|
echo 'tenant admin already exists';
|
||||||
|
else
|
||||||
|
papercrate-admin create-tenant admin;
|
||||||
|
fi &&
|
||||||
|
echo 'Ensuring demo user credentials' &&
|
||||||
|
(papercrate-admin create-user admin adminadmin || papercrate-admin set-password admin adminadmin) &&
|
||||||
|
echo 'Ensuring demo membership' &&
|
||||||
|
papercrate-admin add-user-to-tenant admin admin admin &&
|
||||||
|
echo 'Ensuring Quickwit index for admin tenant' &&
|
||||||
|
papercrate-admin quickwit-create-index admin
|
||||||
|
"
|
||||||
|
user: root
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
Reference in New Issue
Block a user