- 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]>
54 lines
2.4 KiB
TypeScript
54 lines
2.4 KiB
TypeScript
import type { Kysely } from "kysely";
|
|
import { dropTables, entityTable, execDdl, oneOf } from "../src/migration-helpers.ts";
|
|
|
|
// 运维域:reconcile_run(五类校验器的可恢复运行记录)、server_mapping(Emby 兼容库存映射,REQ §7.4、§8.1)
|
|
// 外部库存"未知 ≠ 缺失";server_mapping 只是辅助证据,不构成文件纳管授权。
|
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
|
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
await execDdl(
|
|
db,
|
|
`
|
|
${entityTable(
|
|
"reconcile_run",
|
|
`
|
|
kind text not null ${oneOf("kind", ["identity", "metadata", "asset", "acquisition", "server"])},
|
|
mode text not null default 'normal' ${oneOf("mode", ["normal", "read_only"])},
|
|
status text not null default 'queued' ${oneOf("status", ["queued", "running", "succeeded", "partial", "failed", "cancelled"])},
|
|
scope jsonb not null default '{}'::jsonb,
|
|
resume_cursor jsonb not null default '{}'::jsonb,
|
|
stats jsonb not null default '{}'::jsonb,
|
|
task_id uuid references task (id) on delete set null,
|
|
error jsonb,
|
|
started_at timestamptz,
|
|
finished_at timestamptz`,
|
|
)}
|
|
create index reconcile_run_kind_idx on reconcile_run (kind, created_at desc);
|
|
|
|
${entityTable(
|
|
"server_mapping",
|
|
`
|
|
media_server_instance_id uuid not null references media_server_instance (id) on delete cascade,
|
|
server_item_id text not null,
|
|
item_type text not null ${oneOf("item_type", ["movie", "series", "season", "episode", "other"])},
|
|
media_entity_id uuid references media_entity (id) on delete set null,
|
|
content_unit_id uuid references content_unit (id) on delete set null,
|
|
media_asset_id uuid references media_asset (id) on delete set null,
|
|
server_path text,
|
|
path_mapping_id uuid references path_mapping (id) on delete set null,
|
|
match_status text not null default 'unmatched' ${oneOf("match_status", ["matched", "ambiguous", "unmatched", "stale"])},
|
|
version_info jsonb not null default '{}'::jsonb,
|
|
evidence jsonb not null default '{}'::jsonb,
|
|
observed_at timestamptz not null default now(),
|
|
constraint server_mapping_item_key unique (media_server_instance_id, server_item_id)`,
|
|
)}
|
|
create index server_mapping_entity_idx on server_mapping (media_entity_id) where media_entity_id is not null;
|
|
`,
|
|
);
|
|
}
|
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: 同上
|
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
await execDdl(db, dropTables(["server_mapping", "reconcile_run"]));
|
|
}
|