162 lines
5.1 KiB
SQL
162 lines
5.1 KiB
SQL
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
|
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
version text PRIMARY KEY,
|
|
applied_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS 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(),
|
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
organization_id uuid NOT NULL REFERENCES organizations(id),
|
|
username text NOT NULL,
|
|
password_hash text NOT NULL,
|
|
role text NOT NULL DEFAULT 'admin' CHECK (role = 'admin'),
|
|
is_active boolean NOT NULL DEFAULT true,
|
|
failed_login_count integer NOT NULL DEFAULT 0,
|
|
locked_until timestamptz,
|
|
last_login_at timestamptz,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
UNIQUE (organization_id, username)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
token_hash bytea NOT NULL UNIQUE,
|
|
csrf_token_hash bytea NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
last_seen_at timestamptz NOT NULL DEFAULT now(),
|
|
expires_at timestamptz NOT NULL,
|
|
idle_expires_at timestamptz NOT NULL,
|
|
revoked_at timestamptz,
|
|
ip_address inet,
|
|
user_agent text
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS sessions_active_idx
|
|
ON sessions (token_hash, expires_at, idle_expires_at)
|
|
WHERE revoked_at IS NULL;
|
|
|
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
organization_id uuid NOT NULL REFERENCES organizations(id),
|
|
task_id text NOT NULL,
|
|
original_text_ciphertext text,
|
|
operation_ciphertext text,
|
|
parse_response_ciphertext text,
|
|
execution_result_ciphertext text,
|
|
operation jsonb,
|
|
parse_response jsonb,
|
|
summary jsonb,
|
|
status text NOT NULL,
|
|
stage text NOT NULL,
|
|
message text,
|
|
error text,
|
|
handoff_status text,
|
|
execution_result jsonb,
|
|
created_by uuid REFERENCES users(id),
|
|
confirmed_by uuid REFERENCES users(id),
|
|
confirmed_at timestamptz,
|
|
idempotency_key text,
|
|
lease_owner text,
|
|
lease_expires_at timestamptz,
|
|
last_event_id bigint,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
UNIQUE (organization_id, task_id),
|
|
UNIQUE (organization_id, idempotency_key)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS tasks_queue_idx
|
|
ON tasks (organization_id, status, lease_expires_at, created_at);
|
|
|
|
CREATE TABLE IF NOT EXISTS task_events (
|
|
id bigserial PRIMARY KEY,
|
|
organization_id uuid NOT NULL REFERENCES organizations(id),
|
|
task_id uuid NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
status text NOT NULL,
|
|
stage text NOT NULL,
|
|
message text,
|
|
payload jsonb,
|
|
actor_user_id uuid REFERENCES users(id),
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS task_events_task_idx ON task_events (task_id, id);
|
|
|
|
CREATE TABLE IF NOT EXISTS task_attempts (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
organization_id uuid NOT NULL REFERENCES organizations(id),
|
|
task_id uuid NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
attempt_no integer NOT NULL,
|
|
phase text NOT NULL,
|
|
status text NOT NULL,
|
|
request_hash text,
|
|
response_hash text,
|
|
error_code text,
|
|
details jsonb,
|
|
started_at timestamptz NOT NULL DEFAULT now(),
|
|
finished_at timestamptz,
|
|
UNIQUE (task_id, attempt_no, phase)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS idempotency_keys (
|
|
id bigserial PRIMARY KEY,
|
|
organization_id uuid NOT NULL REFERENCES organizations(id),
|
|
scope text NOT NULL,
|
|
idempotency_key text NOT NULL,
|
|
request_hash text NOT NULL,
|
|
task_id uuid REFERENCES tasks(id) ON DELETE CASCADE,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
expires_at timestamptz,
|
|
UNIQUE (organization_id, scope, idempotency_key)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS audit_events (
|
|
id bigserial PRIMARY KEY,
|
|
organization_id uuid NOT NULL REFERENCES organizations(id),
|
|
actor_user_id uuid REFERENCES users(id),
|
|
event_type text NOT NULL,
|
|
entity_type text NOT NULL,
|
|
entity_id text,
|
|
request_id text,
|
|
metadata jsonb,
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS audit_events_created_idx ON audit_events (organization_id, created_at DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS browser_connections (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
organization_id uuid NOT NULL REFERENCES organizations(id),
|
|
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
connection_id text NOT NULL,
|
|
extension_version text,
|
|
status text NOT NULL DEFAULT 'connected',
|
|
last_seen_at timestamptz NOT NULL DEFAULT now(),
|
|
metadata jsonb,
|
|
UNIQUE (organization_id, connection_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS outbox_events (
|
|
id bigserial PRIMARY KEY,
|
|
organization_id uuid NOT NULL REFERENCES organizations(id),
|
|
topic text NOT NULL,
|
|
aggregate_type text NOT NULL,
|
|
aggregate_id text NOT NULL,
|
|
payload jsonb NOT NULL,
|
|
created_at timestamptz NOT NULL DEFAULT now(),
|
|
published_at timestamptz
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS outbox_pending_idx ON outbox_events (organization_id, created_at) WHERE published_at IS NULL;
|