Files
UHD_Media_Tool/packages/contracts/src/api/shared.ts
T
rootandClaude Opus 5.5 8bec4bad5d 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]>
2026-10-02 17:40:43 +08:00

153 lines
5.1 KiB
TypeScript

// 端点定义共用的 helper 与跨分组 schema(由 api.ts 汇总导出公开部分)
import { z } from "zod";
import * as agent from "../agent.ts";
import { type ApiScope, CursorQuery, cursorPage, Id, IdParam } from "../common.ts";
import * as content from "../content.ts";
import * as identity from "../identity.ts";
import * as instances from "../instances.ts";
// 所有 REST 端点的路径常量与请求/响应 schema 汇总。
// path 相对 API_PREFIX,参数用 OpenAPI 风格 `{id}`(server 的 createRoute 直接使用);
// web 用 buildPath() 填参数。响应体统一套 apiResponse() 信封,这里的 response 是 data 部分。
export const API_PREFIX = "/api/v1";
export type HttpMethod = "get" | "post" | "patch" | "put" | "delete";
/**
* 鉴权方式(不写即按 scope 校验,cookie 会话与 API Key 同样适用):
* - public:免鉴权(必须与服务端 http/auth.ts 的 PUBLIC_AUTH_PATHS 一致,测试断言);
* - session:只接受控制台管理员 cookie 会话,API Key(含 admin scope)一律 403。
*/
export type EndpointAuth = "public" | "session";
export interface EndpointDef {
method: HttpMethod;
path: string;
scope: ApiScope;
auth?: EndpointAuth;
summary: string;
params?: z.ZodObject;
query?: z.ZodType;
body?: z.ZodType;
response: z.ZodType;
}
export const ep = <E extends EndpointDef>(e: E): E => e;
/** 列表查询:游标分页 + 资源自有过滤 */
export const listQuery = <S extends z.ZodRawShape>(filters: S) => CursorQuery.extend(filters);
/** 删除/无内容动作的响应 */
export const OkResult = z.object({ ok: z.literal(true) });
export interface CrudSchemas {
row: z.ZodType;
create?: z.ZodType;
update?: z.ZodType;
filters?: z.ZodRawShape;
}
/** 标准资源端点:list / get / create / update / remove(按给出的 schema 决定是否生成) */
export function crud<const B extends string>(
base: B,
name: string,
s: CrudSchemas,
scopes: { read: ApiScope; write: ApiScope },
) {
const item = `${base}/{id}` as const;
return {
list: ep({
method: "get",
path: base,
scope: scopes.read,
summary: `${name}列表`,
query: listQuery(s.filters ?? {}),
response: cursorPage(s.row),
}),
get: ep({
method: "get",
path: item,
scope: scopes.read,
summary: `${name}详情`,
params: IdParam,
response: s.row,
}),
...(s.create
? {
create: ep({
method: "post",
path: base,
scope: scopes.write,
summary: `新建${name}`,
body: s.create,
response: s.row,
}),
}
: {}),
...(s.update
? {
update: ep({
method: "patch",
path: item,
scope: scopes.write,
summary: `修改${name}`,
params: IdParam,
body: s.update,
response: s.row,
}),
}
: {}),
};
}
export const R = { read: "read", write: "admin" } as const;
export const del = (path: string, summary: string) =>
ep({ method: "delete", path, scope: "admin", summary, params: IdParam, response: OkResult });
/** 实例连通性测试 */
export const instanceTest = (path: string, summary: string) =>
ep({ method: "post", path, scope: "admin", summary, params: IdParam, response: instances.InstanceTestResult });
/** 入队后台任务的受理结果(taskId = 引擎 run id) */
export const TaskAccepted = z.object({ taskId: Id });
export type TaskAccepted = z.infer<typeof TaskAccepted>;
/** 人工指定来源资源身份的结果(待识别池 assign / source-releases identity) */
export const ReleaseIdentityResult = z.object({ mediaEntityId: Id, contentUnitIds: z.array(Id) });
export type ReleaseIdentityResult = z.infer<typeof ReleaseIdentityResult>;
/** 身份修订历史 */
export const IdentityRevisionList = z.object({ items: z.array(identity.IdentityRevision) });
export type IdentityRevisionList = z.infer<typeof IdentityRevisionList>;
/** 字段覆盖/取消覆盖的结果 */
export const FieldOverrideResult = z.object({
mediaEntityId: Id,
changedFields: z.array(z.string()),
lockedFields: z.array(z.string()),
});
export const FieldParam = z.object({ id: Id, field: content.EntityMetadataField });
export const UnitFieldParam = z.object({ id: Id, field: content.UnitMetadataField });
export const UnitFieldOverrideResult = z.object({
contentUnitId: Id,
changedFields: z.array(z.string()),
lockedFields: z.array(z.string()),
});
/** 引擎 run id 不是 uuid(openworkflow 自有 id) */
export const EngineRunIdParam = z.object({ id: z.string().min(1).max(200) });
/** 待处理事项聚合分组 */
export const DecisionSummaryGroup = z.object({
kind: agent.DecisionKind,
mediaEntityId: Id.nullable(),
mediaEntityTitle: z.string().nullable(),
count: z.number().int(),
maxSeverity: z.enum(["info", "warning", "critical"]),
latestAt: z.string(),
});
export type DecisionSummaryGroup = z.infer<typeof DecisionSummaryGroup>;
export const DecisionSummary = z.object({ total: z.number().int(), groups: z.array(DecisionSummaryGroup) });
export type DecisionSummary = z.infer<typeof DecisionSummary>;