import type { Sql } from 'postgres'; /** * Auto-create all Shade tables if they don't exist. * * Called on PostgresStorage / PostgresPrekeyStore construction. * Uses raw SQL (not Drizzle migrations) for zero-config deployment. * * All tables prefixed with `shade_` to avoid collisions when sharing * a PostgreSQL instance with another project. */ export async function ensureClientTables(sql: Sql): Promise { await sql` CREATE TABLE IF NOT EXISTS shade_identity ( id INTEGER PRIMARY KEY CHECK (id = 1), signing_public_key TEXT NOT NULL, signing_private_key TEXT NOT NULL, dh_public_key TEXT NOT NULL, dh_private_key TEXT NOT NULL ) `; await sql` CREATE TABLE IF NOT EXISTS shade_config ( key TEXT PRIMARY KEY, value TEXT NOT NULL ) `; await sql` CREATE TABLE IF NOT EXISTS shade_signed_prekeys ( key_id INTEGER PRIMARY KEY, data_json TEXT NOT NULL ) `; await sql` CREATE TABLE IF NOT EXISTS shade_one_time_prekeys ( key_id INTEGER PRIMARY KEY, data_json TEXT NOT NULL ) `; await sql` CREATE TABLE IF NOT EXISTS shade_sessions ( address TEXT PRIMARY KEY, state_json TEXT NOT NULL ) `; await sql` CREATE TABLE IF NOT EXISTS shade_trusted_identities ( address TEXT PRIMARY KEY, identity_key TEXT NOT NULL ) `; await sql` CREATE TABLE IF NOT EXISTS shade_retired_identities ( id SERIAL PRIMARY KEY, data_json TEXT NOT NULL, retired_at BIGINT NOT NULL ) `; await sql` CREATE INDEX IF NOT EXISTS shade_retired_at_idx ON shade_retired_identities(retired_at) `; await sql` CREATE TABLE IF NOT EXISTS shade_stream_state ( stream_id TEXT PRIMARY KEY, direction TEXT NOT NULL CHECK (direction IN ('send','receive')), peer_address TEXT NOT NULL, status TEXT NOT NULL CHECK (status IN ('active','paused','finished','aborted')), metadata_json TEXT NOT NULL, partition_json TEXT NOT NULL, lane_state_json TEXT NOT NULL, io_descriptor_json TEXT NOT NULL, secret_enc BYTEA NOT NULL, secret_nonce BYTEA NOT NULL, overall_hash_state TEXT, created_at BIGINT NOT NULL, updated_at BIGINT NOT NULL ) `; await sql` CREATE INDEX IF NOT EXISTS shade_stream_state_peer_idx ON shade_stream_state(peer_address) `; await sql` CREATE INDEX IF NOT EXISTS shade_stream_state_updated_idx ON shade_stream_state(updated_at) `; await sql` CREATE INDEX IF NOT EXISTS shade_stream_state_status_idx ON shade_stream_state(status, direction) `; } export async function ensurePrekeyServerTables(sql: Sql): Promise { await sql` CREATE TABLE IF NOT EXISTS shade_server_identities ( address TEXT PRIMARY KEY, identity_signing_key TEXT NOT NULL, identity_dh_key TEXT NOT NULL, last_activity_at BIGINT NOT NULL DEFAULT 0 ) `; // Migrate existing deployments (no-op if column exists) await sql` ALTER TABLE shade_server_identities ADD COLUMN IF NOT EXISTS last_activity_at BIGINT NOT NULL DEFAULT 0 `; await sql` CREATE INDEX IF NOT EXISTS shade_server_identities_activity_idx ON shade_server_identities(last_activity_at) `; await sql` CREATE TABLE IF NOT EXISTS shade_server_signed_prekeys ( address TEXT PRIMARY KEY, key_id INTEGER NOT NULL, public_key TEXT NOT NULL, signature TEXT NOT NULL ) `; await sql` CREATE TABLE IF NOT EXISTS shade_server_one_time_prekeys ( id SERIAL PRIMARY KEY, address TEXT NOT NULL, key_id INTEGER NOT NULL, public_key TEXT NOT NULL ) `; await sql` CREATE INDEX IF NOT EXISTS shade_server_otp_address_idx ON shade_server_one_time_prekeys(address) `; }