import type { Kysely } from "kysely"; import { dropTables, entityTable, execDdl, oneOf } from "../src/migration-helpers.ts"; // 底座:扩展、updated_at 触发器函数、加密凭证表 secret、outbox 投递字段(ADR-002 §2.1) // biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型 export async function up(db: Kysely): Promise { await execDdl( db, ` create extension if not exists pgcrypto with schema public; create extension if not exists pg_trgm with schema public; create or replace function umt_touch_updated_at() returns trigger language plpgsql as $$ begin new.updated_at := now(); return new; end $$; ${entityTable( "secret", ` name text not null, purpose text not null ${oneOf("purpose", ["site_cookie", "site_passkey", "site_api_key", "site_password", "feed_url", "downloader_password", "media_server_api_key", "metadata_api_key", "llm_api_key", "other"])}, ciphertext bytea not null, nonce bytea not null, auth_tag bytea, algorithm text not null default 'aes-256-gcm', kdf text not null default 'hkdf-sha256', kdf_params jsonb not null default '{}'::jsonb, key_version integer not null default 1, rotated_at timestamptz, constraint secret_name_key unique (name)`, )} alter table outbox_event add column idempotency_key text, add column status text not null default 'pending' ${oneOf("status", ["pending", "dispatched", "failed", "dead"])}, add column attempts integer not null default 0, add column next_attempt_at timestamptz not null default now(), add column last_error jsonb; create unique index outbox_event_idempotency_key_idx on outbox_event (idempotency_key) where idempotency_key is not null; create index outbox_event_dispatch_idx on outbox_event (next_attempt_at) where status = 'pending'; alter table api_key add column updated_at timestamptz not null default now(); create trigger api_key_touch_updated_at before update on api_key for each row execute function umt_touch_updated_at(); `, ); } // biome-ignore lint/suspicious/noExplicitAny: 同上 export async function down(db: Kysely): Promise { await execDdl( db, ` drop trigger if exists api_key_touch_updated_at on api_key; alter table api_key drop column if exists updated_at; drop index if exists outbox_event_dispatch_idx; drop index if exists outbox_event_idempotency_key_idx; alter table outbox_event drop column if exists idempotency_key, drop column if exists status, drop column if exists attempts, drop column if exists next_attempt_at, drop column if exists last_error; ${dropTables(["secret"])} drop function if exists umt_touch_updated_at(); -- 扩展为库级共享对象,down 不卸载 `, ); }