import type { Kysely } from "kysely"; import { dropTables, entityTable, execDdl, oneOf } from "../src/migration-helpers.ts"; // 实例域:媒体库根、下载器实例、媒体服务器实例、多实例路径映射(REQ §7.1、§8.4、§12.5) const HEALTH = ["unknown", "healthy", "degraded", "offline"] as const; // biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型 export async function up(db: Kysely): Promise { await execDdl( db, ` ${entityTable( "library_root", ` name text not null, media_kind text not null default 'mixed' ${oneOf("media_kind", ["movie", "series", "variety", "mixed", "other"])}, local_path text not null, management_mode text not null default 'register_only' ${oneOf("management_mode", ["register_only", "managed"])}, enabled boolean not null default true, sentinel_file text, health_status text not null default 'unknown' ${oneOf("health_status", HEALTH)}, last_health_at timestamptz, health_detail jsonb not null default '{}'::jsonb, notes text, constraint library_root_name_key unique (name), constraint library_root_local_path_key unique (local_path)`, )} ${entityTable( "downloader_instance", ` name text not null, kind text not null default 'qbittorrent' ${oneOf("kind", ["qbittorrent"])}, base_url text not null, username text, secret_ref uuid references secret (id) on delete restrict, managed_tag text not null default 'umt', cache_volume_key text, enabled boolean not null default true, health_status text not null default 'unknown' ${oneOf("health_status", HEALTH)}, last_seen_at timestamptz, version_info jsonb not null default '{}'::jsonb, settings jsonb not null default '{}'::jsonb, constraint downloader_instance_name_key unique (name)`, )} ${entityTable( "media_server_instance", ` name text not null, kind text not null default 'emby' ${oneOf("kind", ["emby", "jellyfin"])}, base_url text not null, user_id text, secret_ref uuid references secret (id) on delete restrict, enabled boolean not null default true, health_status text not null default 'unknown' ${oneOf("health_status", HEALTH)}, last_seen_at timestamptz, version_info jsonb not null default '{}'::jsonb, settings jsonb not null default '{}'::jsonb, constraint media_server_instance_name_key unique (name)`, )} ${entityTable( "path_mapping", ` view_kind text not null ${oneOf("view_kind", ["downloader", "media_server"])}, downloader_instance_id uuid references downloader_instance (id) on delete cascade, media_server_instance_id uuid references media_server_instance (id) on delete cascade, library_root_id uuid references library_root (id) on delete set null, remote_prefix text not null, local_prefix text not null, priority integer not null default 0, enabled boolean not null default true, verify_status text not null default 'unverified' ${oneOf("verify_status", ["unverified", "ok", "failed"])}, verified_at timestamptz, verify_detail jsonb not null default '{}'::jsonb, constraint path_mapping_view_owner_check check ( (view_kind = 'downloader' and downloader_instance_id is not null and media_server_instance_id is null) or (view_kind = 'media_server' and media_server_instance_id is not null and downloader_instance_id is null) )`, )} create unique index path_mapping_downloader_prefix_idx on path_mapping (downloader_instance_id, remote_prefix) where downloader_instance_id is not null; create unique index path_mapping_media_server_prefix_idx on path_mapping (media_server_instance_id, remote_prefix) where media_server_instance_id is not null; `, ); } // biome-ignore lint/suspicious/noExplicitAny: 同上 export async function down(db: Kysely): Promise { await execDdl(db, dropTables(["path_mapping", "media_server_instance", "downloader_instance", "library_root"])); }