refactor: 拆分大文件(contracts 端点、整理服务、浏览器纯函数、整理弹窗来源)
- contracts/src/api.ts 2733 行 → api/*.ts 13 个领域文件 + shared.ts;api.ts 按原顺序合并 Api。 新旧对比:354 个端点内容与顺序一致,分组键与对外导出一致 - acquisition-organize/services/organize.ts 2645 行 → organize-types / -plan / -exec / -finalize / -records, organize.ts 保留为汇总入口,外部引用不变 - browser/service.ts 类外的纯函数(视图映射、原因文案、坐标换算、图片尺寸、出口代理)移到 session-view.ts, service.ts 照旧转出 - organize-wizard:来源解析 useResolvedSource 移到 source.ts,FormSelect 移到 form-select.tsx 只挪位置不改逻辑;typecheck、lint、test:all、web build、server 编译与编译产物自检通过。 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
c9e8120fc6
commit
8bec4bad5d
26 files changed
+5906
-5696
No files matched your search
@@ -0,0 +1,322 @@
|
||||
import { z } from "zod";
|
||||
import * as classification from "../classification.ts";
|
||||
import { cursorPage, IdParam } from "../common.ts";
|
||||
import * as files from "../files.ts";
|
||||
import * as organization from "../organization.ts";
|
||||
import * as organizeSources from "../organize-sources.ts";
|
||||
import { ep } from "./shared.ts";
|
||||
|
||||
export const organizeApi = {
|
||||
// ── 整理(公开合同) ──
|
||||
organization: {
|
||||
list: ep({
|
||||
method: "get",
|
||||
path: "/organization-records",
|
||||
scope: "organize.read",
|
||||
summary: "整理记录列表",
|
||||
query: organization.OrganizationListQuery,
|
||||
response: cursorPage(organization.PublicOrganizationRecord),
|
||||
}),
|
||||
get: ep({
|
||||
method: "get",
|
||||
path: "/organization-records/{id}",
|
||||
scope: "organize.read",
|
||||
summary: "整理记录详情(含逐文件结果)",
|
||||
params: IdParam,
|
||||
response: organization.PublicOrganizationRecord,
|
||||
}),
|
||||
changes: ep({
|
||||
method: "get",
|
||||
path: "/organization-records/changes",
|
||||
scope: "organize.read",
|
||||
summary: "按 changeSeq 增量读取(含既有记录状态更新)",
|
||||
query: organization.OrganizationChangesQuery,
|
||||
response: organization.OrganizationChangesResponse,
|
||||
}),
|
||||
stats: ep({
|
||||
method: "get",
|
||||
path: "/organization-records/stats",
|
||||
scope: "organize.read",
|
||||
summary: "整理记录按状态计数(筛选同列表)",
|
||||
query: organization.OrganizationStatsQuery,
|
||||
response: organization.OrganizationStats,
|
||||
}),
|
||||
remove: ep({
|
||||
method: "post",
|
||||
path: "/organization-records/delete",
|
||||
scope: "organize.execute",
|
||||
summary: "删除整理记录,可选把源文件 / 媒体库文件移入回收站(dryRun 只核对)",
|
||||
body: organization.OrganizationDeleteRequest,
|
||||
response: organization.OrganizationDeleteResult,
|
||||
}),
|
||||
create: ep({
|
||||
method: "post",
|
||||
path: "/organization-records",
|
||||
scope: "organize.execute",
|
||||
summary: "手动发起整理",
|
||||
body: organization.OrganizeRequest,
|
||||
response: organization.OrganizeAccepted,
|
||||
}),
|
||||
},
|
||||
|
||||
// ── 文件管理(docs/runbook/files.md;只浏览已登记根,写操作两段式 dryRun → planId) ──
|
||||
classification: {
|
||||
get: ep({
|
||||
method: "get",
|
||||
path: "/classification",
|
||||
scope: "organize.read",
|
||||
summary: "分类配置",
|
||||
response: classification.ClassificationConfigView,
|
||||
}),
|
||||
put: ep({
|
||||
method: "put",
|
||||
path: "/classification",
|
||||
scope: "admin",
|
||||
summary: "保存分类配置",
|
||||
body: classification.ClassificationPut,
|
||||
response: classification.ClassificationConfigView,
|
||||
}),
|
||||
preview: ep({
|
||||
method: "post",
|
||||
path: "/classification/preview",
|
||||
scope: "organize.read",
|
||||
summary: "测试分类规则",
|
||||
body: classification.ClassificationPreviewInput,
|
||||
response: classification.ClassificationPreviewResult,
|
||||
}),
|
||||
},
|
||||
organizeSources: {
|
||||
list: ep({
|
||||
method: "get",
|
||||
path: "/organize-sources",
|
||||
scope: "organize.read",
|
||||
summary: "整理来源",
|
||||
response: organizeSources.OrganizeSourceList,
|
||||
}),
|
||||
save: ep({
|
||||
method: "put",
|
||||
path: "/organize-sources/{id}",
|
||||
scope: "admin",
|
||||
summary: "保存整理来源",
|
||||
params: IdParam,
|
||||
body: organizeSources.OrganizeSourceInput,
|
||||
response: organizeSources.OrganizeSourceView,
|
||||
}),
|
||||
remove: ep({
|
||||
method: "delete",
|
||||
path: "/organize-sources/{id}",
|
||||
scope: "admin",
|
||||
summary: "删除整理来源",
|
||||
params: IdParam,
|
||||
response: z.object({ deleted: z.boolean() }),
|
||||
}),
|
||||
stock: ep({
|
||||
method: "post",
|
||||
path: "/organize-sources/{id}/stock",
|
||||
scope: "organize.execute",
|
||||
summary: "扫描存量文件",
|
||||
params: IdParam,
|
||||
body: organizeSources.OrganizeStockInput,
|
||||
response: organizeSources.OrganizeStockResult,
|
||||
}),
|
||||
},
|
||||
files: {
|
||||
roots: ep({
|
||||
method: "get",
|
||||
path: "/files/roots",
|
||||
scope: "organize.read",
|
||||
summary: "可浏览的根(库根 + 下载根),含实时健康与空间",
|
||||
response: files.FileRootList,
|
||||
}),
|
||||
list: ep({
|
||||
method: "get",
|
||||
path: "/files",
|
||||
scope: "organize.read",
|
||||
summary: "列目录(分页/排序/过滤/截断,行内业务状态);根离线 503",
|
||||
query: files.FilesListQuery,
|
||||
response: files.FilesListResponse,
|
||||
}),
|
||||
stat: ep({
|
||||
method: "get",
|
||||
path: "/files/stat",
|
||||
scope: "organize.read",
|
||||
summary: "单项详情:realpath/inode/nlink/sidecars/路径绑定",
|
||||
query: files.FileStatQuery,
|
||||
response: files.FileStat,
|
||||
}),
|
||||
mkdir: ep({
|
||||
method: "post",
|
||||
path: "/files/mkdir",
|
||||
scope: "organize.execute",
|
||||
summary: "新建目录(非递归,已存在 409,仅 writable 根)",
|
||||
body: files.FilesMkdirRequest,
|
||||
response: files.FilesMkdirResult,
|
||||
}),
|
||||
rename: ep({
|
||||
method: "post",
|
||||
path: "/files/rename",
|
||||
scope: "organize.execute",
|
||||
summary: "同目录改名(不覆盖;同步 asset_location 与 path_binding 修订;做种中 409)",
|
||||
body: files.FilesRenameRequest,
|
||||
response: files.FilesRenameResult,
|
||||
}),
|
||||
move: ep({
|
||||
method: "post",
|
||||
path: "/files/move",
|
||||
scope: "organize.execute",
|
||||
summary: "移动/复制到目录:dryRun 返回 planId,执行回传 planId(源变更 409 plan_stale)",
|
||||
body: files.FilesMoveRequest,
|
||||
response: files.FilesTransferResult,
|
||||
}),
|
||||
copy: ep({
|
||||
method: "post",
|
||||
path: "/files/copy",
|
||||
scope: "organize.execute",
|
||||
summary: "复制到目录(同 /files/move mode=copy)",
|
||||
body: files.FilesCopyRequest,
|
||||
response: files.FilesTransferResult,
|
||||
}),
|
||||
organize: ep({
|
||||
method: "post",
|
||||
path: "/files/organize",
|
||||
scope: "organize.execute",
|
||||
summary: "按作品分组整理入库(每组走 planImport,与 /organization-records 同实现)",
|
||||
body: files.FilesOrganizeRequest,
|
||||
response: files.FilesOrganizeResult,
|
||||
}),
|
||||
delete: ep({
|
||||
method: "post",
|
||||
path: "/files/delete",
|
||||
scope: "organize.execute",
|
||||
summary: "删除(默认进回收站;引用检查:做种/资产/绑定;permanent 仅 admin 会话)",
|
||||
body: files.FilesDeleteRequest,
|
||||
response: files.FilesDeleteResult,
|
||||
}),
|
||||
trash: ep({
|
||||
method: "get",
|
||||
path: "/files/trash",
|
||||
scope: "organize.read",
|
||||
summary: "回收站列表",
|
||||
query: files.FilesTrashQuery,
|
||||
response: files.FilesTrashList,
|
||||
}),
|
||||
trashRestore: ep({
|
||||
method: "post",
|
||||
path: "/files/trash/restore",
|
||||
scope: "organize.execute",
|
||||
summary: "从回收站还原(原位被占的项失败 conflict)",
|
||||
body: files.FilesTrashRestoreRequest,
|
||||
response: files.FilesTrashRestoreResult,
|
||||
}),
|
||||
trashPurge: ep({
|
||||
method: "post",
|
||||
path: "/files/trash/purge",
|
||||
scope: "admin",
|
||||
auth: "session",
|
||||
summary: "清空回收站(缺省只清过期项;仅管理员控制台会话,API Key 与 DEV key 一律 403)",
|
||||
body: files.FilesTrashPurgeRequest,
|
||||
response: files.FilesTrashPurgeResult,
|
||||
}),
|
||||
identify: ep({
|
||||
method: "post",
|
||||
path: "/files/identify",
|
||||
scope: "organize.read",
|
||||
summary: "识别(只读):解析 + 本地/TMDB 召回前 5 候选,不落库",
|
||||
body: files.FilesIdentifyRequest,
|
||||
response: files.FilesIdentifyResult,
|
||||
}),
|
||||
scrape: ep({
|
||||
method: "post",
|
||||
path: "/files/scrape",
|
||||
scope: "organize.execute",
|
||||
summary:
|
||||
"刮削:对目录或视频文件就地写 NFO 与图片(按路径识别作品,不要求已登记;认不准时返回候选,选定后再提交)",
|
||||
body: files.FilesScrapeRequest,
|
||||
response: files.FilesScrapeResult,
|
||||
}),
|
||||
scrapeRecords: ep({
|
||||
method: "get",
|
||||
path: "/files/scrape-records",
|
||||
scope: "organize.read",
|
||||
summary: "按 id 查刮削记录(目录、识别结果、逐文件结果)",
|
||||
query: files.FilesScrapeRecordsQuery,
|
||||
response: files.FilesScrapeRecordsResult,
|
||||
}),
|
||||
probe: ep({
|
||||
method: "post",
|
||||
path: "/files/probe",
|
||||
scope: "organize.execute",
|
||||
summary: "ffprobe 摘要(≤20 项;已登记资产写回 media_info,所以要 organize.execute)",
|
||||
body: files.FilesProbeRequest,
|
||||
response: files.FilesProbeResult,
|
||||
}),
|
||||
download: ep({
|
||||
method: "get",
|
||||
path: "/files/download",
|
||||
scope: "admin",
|
||||
auth: "session",
|
||||
summary: "下载小文件(nfo/字幕/图片/文本/.torrent,≤20 MiB;流式;仅管理员控制台会话;审计)",
|
||||
query: files.FilesDownloadQuery,
|
||||
response: files.FilesDownloadResponse,
|
||||
}),
|
||||
preview: ep({
|
||||
method: "get",
|
||||
path: "/files/preview",
|
||||
scope: "admin",
|
||||
auth: "session",
|
||||
summary:
|
||||
"结构化预览(文本/NFO 前 1 MiB 带编码探测、字幕前 200 条、.torrent 文件列表、图片尺寸;仅管理员控制台会话)",
|
||||
query: files.FilesPreviewQuery,
|
||||
response: files.FilePreview,
|
||||
}),
|
||||
records: ep({
|
||||
method: "get",
|
||||
path: "/files/records",
|
||||
scope: "organize.read",
|
||||
summary: "按整理记录 id 或 planId 回查整理记录(整理页预填)",
|
||||
query: files.FilesRecordsQuery,
|
||||
response: files.FilesRecordsResult,
|
||||
}),
|
||||
// W9-H 文件管理 v2 后端缺口
|
||||
entityFromCandidate: ep({
|
||||
method: "post",
|
||||
path: "/files/entity-from-candidate",
|
||||
scope: "organize.execute",
|
||||
summary: "按 TMDB 候选就地建本地作品 + 外部映射(accepted/manual)+ 季集单元;幂等",
|
||||
body: files.FilesEntityFromCandidateRequest,
|
||||
response: files.FilesEntityFromCandidateResult,
|
||||
}),
|
||||
scrapeBatch: ep({
|
||||
method: "post",
|
||||
path: "/files/scrape-batch",
|
||||
scope: "organize.execute",
|
||||
summary: "批量刷新元数据:按作品去重入队 refreshMetadata(锁定字段不变)",
|
||||
body: files.FilesScrapeBatchRequest,
|
||||
response: files.FilesScrapeBatchResult,
|
||||
}),
|
||||
tree: ep({
|
||||
method: "get",
|
||||
path: "/files/tree",
|
||||
scope: "organize.read",
|
||||
summary: "目录摘要:上一级、子目录(是否有下级)、视频/已登记计数(树与上一级用)",
|
||||
query: files.FilesTreeQuery,
|
||||
response: files.FilesTreeResult,
|
||||
}),
|
||||
recordSources: ep({
|
||||
method: "get",
|
||||
path: "/files/record-sources",
|
||||
scope: "organize.read",
|
||||
summary: "整理记录的源路径换算成登记目录 FileRef(重新整理用)",
|
||||
query: files.FilesRecordSourcesQuery,
|
||||
response: files.FilesRecordSourcesResult,
|
||||
}),
|
||||
suggestName: ep({
|
||||
method: "post",
|
||||
path: "/files/suggest-name",
|
||||
scope: "organize.read",
|
||||
summary: "按命名模板(作品 > 库根 > 全局)生成推荐名;识别不可信时 name=null + 原因",
|
||||
body: files.FilesSuggestNameRequest,
|
||||
response: files.FilesSuggestNameResult,
|
||||
}),
|
||||
},
|
||||
} as const;
|
||||
Reference in new issue
Block a user