144 lines
5.8 KiB
SQL
144 lines
5.8 KiB
SQL
-- Plan purchases use a separate payment-event table because the recharge
|
|
-- payment_events.order_id foreign key intentionally points at recharge_orders.
|
|
ALTER TABLE IF EXISTS plan_purchases ADD COLUMN IF NOT EXISTS provider TEXT NOT NULL DEFAULT 'adapter';
|
|
ALTER TABLE IF EXISTS plan_purchases ADD COLUMN IF NOT EXISTS plan_snapshot JSONB;
|
|
ALTER TABLE IF EXISTS plan_purchases ADD COLUMN IF NOT EXISTS credit_bucket_id UUID;
|
|
ALTER TABLE IF EXISTS plan_purchases ADD COLUMN IF NOT EXISTS entitlement_id UUID;
|
|
ALTER TABLE IF EXISTS recharge_orders ADD COLUMN IF NOT EXISTS credit_bucket_id UUID;
|
|
ALTER TABLE IF EXISTS user_plan_entitlements ADD COLUMN IF NOT EXISTS snapshot JSONB;
|
|
CREATE TABLE IF NOT EXISTS plan_payment_events (
|
|
id UUID PRIMARY KEY,
|
|
provider TEXT NOT NULL,
|
|
external_event_id TEXT NOT NULL,
|
|
purchase_id UUID NOT NULL REFERENCES plan_purchases(id),
|
|
payload_digest TEXT NOT NULL,
|
|
status TEXT NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
applied_at TIMESTAMPTZ,
|
|
UNIQUE(provider, external_event_id)
|
|
);
|
|
|
|
-- Runtime records that are part of the server's durable state but are not
|
|
-- user-facing business aggregates. They keep authentication challenges,
|
|
-- provider configuration and staging metadata relationally addressable while
|
|
-- preserving encrypted payloads and ownership boundaries.
|
|
ALTER TABLE IF EXISTS verification_codes ADD COLUMN IF NOT EXISTS record_key TEXT;
|
|
ALTER TABLE IF EXISTS verification_codes ADD COLUMN IF NOT EXISTS record_kind TEXT NOT NULL DEFAULT 'verification';
|
|
ALTER TABLE IF EXISTS verification_codes ADD COLUMN IF NOT EXISTS ip TEXT;
|
|
ALTER TABLE IF EXISTS verification_codes ADD COLUMN IF NOT EXISTS device_id TEXT;
|
|
ALTER TABLE IF EXISTS verification_codes ADD COLUMN IF NOT EXISTS max_attempts INTEGER NOT NULL DEFAULT 5;
|
|
CREATE INDEX IF NOT EXISTS verification_codes_record_key_idx ON verification_codes(record_kind, record_key);
|
|
|
|
ALTER TABLE IF EXISTS users ADD COLUMN IF NOT EXISTS mfa_secret_encrypted TEXT;
|
|
ALTER TABLE IF EXISTS users ADD COLUMN IF NOT EXISTS mfa_recovery_code_hashes JSONB;
|
|
ALTER TABLE IF EXISTS users ADD COLUMN IF NOT EXISTS mfa_last_totp_counter BIGINT;
|
|
ALTER TABLE IF EXISTS users ADD COLUMN IF NOT EXISTS mfa_required BOOLEAN NOT NULL DEFAULT false;
|
|
ALTER TABLE IF EXISTS users ADD COLUMN IF NOT EXISTS mfa_enabled BOOLEAN NOT NULL DEFAULT false;
|
|
ALTER TABLE IF EXISTS users ADD COLUMN IF NOT EXISTS roles JSONB NOT NULL DEFAULT '[]'::jsonb;
|
|
ALTER TABLE IF EXISTS users ADD COLUMN IF NOT EXISTS failed_login_count INTEGER NOT NULL DEFAULT 0;
|
|
ALTER TABLE IF EXISTS users ADD COLUMN IF NOT EXISTS locked_until BIGINT;
|
|
|
|
ALTER TABLE IF EXISTS sessions ADD COLUMN IF NOT EXISTS replaced_by TEXT;
|
|
ALTER TABLE IF EXISTS sessions ADD COLUMN IF NOT EXISTS roles JSONB;
|
|
ALTER TABLE IF EXISTS sessions ADD COLUMN IF NOT EXISTS scopes JSONB;
|
|
|
|
CREATE TABLE IF NOT EXISTS captcha_challenges (
|
|
id TEXT PRIMARY KEY,
|
|
action TEXT NOT NULL,
|
|
answer_hash TEXT NOT NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
attempts INTEGER NOT NULL DEFAULT 0,
|
|
max_attempts INTEGER NOT NULL DEFAULT 5,
|
|
consumed_at TIMESTAMPTZ,
|
|
ip TEXT NOT NULL,
|
|
device_id TEXT
|
|
);
|
|
CREATE TABLE IF NOT EXISTS mfa_challenges (
|
|
id TEXT PRIMARY KEY,
|
|
subject_id TEXT NOT NULL,
|
|
scope TEXT NOT NULL,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
attempts INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
CREATE TABLE IF NOT EXISTS session_families (
|
|
family_id TEXT PRIMARY KEY,
|
|
subject_id TEXT NOT NULL,
|
|
scope TEXT NOT NULL,
|
|
revoked_at TIMESTAMPTZ
|
|
);
|
|
CREATE TABLE IF NOT EXISTS admin_accounts (
|
|
id TEXT PRIMARY KEY,
|
|
email TEXT NOT NULL UNIQUE,
|
|
password_hash TEXT NOT NULL,
|
|
role TEXT NOT NULL,
|
|
mfa_secret_encrypted TEXT,
|
|
mfa_required BOOLEAN NOT NULL DEFAULT true,
|
|
recovery_code_hashes JSONB NOT NULL DEFAULT '[]'::jsonb,
|
|
mfa_last_totp_counter BIGINT
|
|
);
|
|
CREATE TABLE IF NOT EXISTS runtime_records (
|
|
record_type TEXT NOT NULL,
|
|
record_key TEXT NOT NULL,
|
|
payload JSONB NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (record_type, record_key)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS message_providers (
|
|
id TEXT PRIMARY KEY,
|
|
snapshot JSONB NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
CREATE TABLE IF NOT EXISTS message_templates (
|
|
id TEXT PRIMARY KEY,
|
|
snapshot JSONB NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
CREATE TABLE IF NOT EXISTS payment_providers (
|
|
id TEXT PRIMARY KEY,
|
|
snapshot JSONB NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
CREATE TABLE IF NOT EXISTS webdav_files (
|
|
user_id TEXT NOT NULL,
|
|
path TEXT NOT NULL,
|
|
mime_type TEXT NOT NULL,
|
|
data TEXT NOT NULL DEFAULT '',
|
|
checksum TEXT NOT NULL,
|
|
etag TEXT NOT NULL,
|
|
version INTEGER NOT NULL DEFAULT 1,
|
|
updated_at TIMESTAMPTZ NOT NULL,
|
|
deleted_at TIMESTAMPTZ,
|
|
PRIMARY KEY (user_id, path)
|
|
);
|
|
CREATE TABLE IF NOT EXISTS webdav_sync_jobs (
|
|
id TEXT PRIMARY KEY,
|
|
user_id TEXT NOT NULL,
|
|
operation TEXT NOT NULL,
|
|
path TEXT NOT NULL,
|
|
data TEXT,
|
|
mime_type TEXT,
|
|
if_match TEXT,
|
|
attempts INTEGER NOT NULL DEFAULT 0,
|
|
status TEXT NOT NULL,
|
|
next_attempt_at TIMESTAMPTZ NOT NULL,
|
|
lease_expires_at TIMESTAMPTZ,
|
|
last_error TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL
|
|
);
|
|
CREATE TABLE IF NOT EXISTS stored_objects (
|
|
id TEXT PRIMARY KEY,
|
|
owner_id TEXT NOT NULL,
|
|
mime_type TEXT NOT NULL,
|
|
data TEXT,
|
|
staging_key TEXT,
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
task_id TEXT,
|
|
revoked BOOLEAN NOT NULL DEFAULT false
|
|
);
|
|
CREATE INDEX IF NOT EXISTS webdav_files_user_idx ON webdav_files(user_id, updated_at DESC);
|
|
CREATE INDEX IF NOT EXISTS webdav_sync_jobs_user_idx ON webdav_sync_jobs(user_id, status, next_attempt_at);
|
|
CREATE INDEX IF NOT EXISTS stored_objects_owner_idx ON stored_objects(owner_id, expires_at);
|
|
|
|
ALTER TABLE IF EXISTS webdav_configs ADD COLUMN IF NOT EXISTS manifest_retention_expires_at TIMESTAMPTZ;
|
|
ALTER TABLE IF EXISTS webdav_configs ADD COLUMN IF NOT EXISTS manifest_extension_days INTEGER NOT NULL DEFAULT 0;
|