Files
UHD_Media_Tool/packages/db/migrations/0011_ops.ts
T
rootandClaude Fable 5.1 cc8b3a7df4 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]>
2026-09-27 18:11:37 +08:00

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"]));
}