feat(db,contracts): full v0.1 data model migrations and shared zod contracts (W1-1)
- migrations 0002-0012 split by domain (foundation/instances/content/identity/ sources/assets/policy/execution/agent/ops/organization): 41 tables, uuid pk, created_at/updated_at + touch trigger, text+CHECK status sets, pgcrypto/pg_trgm - key constraints: source_release(site_id, site_release_id), observation(feed_id, guid), external_identity accepted partial unique, download_task(instance_id, torrent_hash), one active acquisition_plan per slot, one active path_binding per entity/season, asset_location root+path uniqueness, organization_record change_seq/change_xid cursor - secret table (ciphertext/nonce/kdf) + secret_ref columns; outbox dispatch fields (ADR-002) - src/types.ts generated from the migrated schema (scripts/gen-types.ts), drift-checked in tests; int8 -> number, date -> string parsers; migrator accepts migrationTableSchema - contracts: Row/Create/Update schemas per domain, public organization-record API (changeSeq cursor), GlobalSettings, CapabilityDescriptor + 14 capability IO schemas, typed SSE events, Api endpoint table - tests: migration up/down round trip on real PG in an isolated schema, constraint checks, contracts<->db column/enum sync, positive/negative parse for every exported schema Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
7ba712ea55
commit
cc8b3a7df4
42 files changed
+7026
-25
No files matched your search
@@ -0,0 +1,72 @@
|
||||
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<any>): Promise<void> {
|
||||
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<any>): Promise<void> {
|
||||
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 不卸载
|
||||
`,
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user