feat(ov): emergency disable channel independent of sync
P2 Task 5(海外紧急停用入口): - ov 新增 POST /v1/internal/emergency/disable|enable(InternalSyncGuard HMAC,albumToken/albumId 恰好其一、都缺省 400;副本不存在 404 ALBUM_NOT_FOUND):直写 albums_ov.emergencyDisabled(唯一 ov 本地可写 位),独立于同步版本链——更高版本 album.updated 重放不解除(e2e 断言 entityVersion 推进而标志保持),恢复仅经显式 enable;幂等语义 (alreadyDisabled/wasDisabled 入审计行) - cn 侧不感知:无对应 cn 状态位、无反向同步(契约描述文档化) - 审计:pino child(audit=emergency 字段常驻每行)结构化日志——偏差 登记:计划原文「写 ov 侧日志文件」落地为字段标记而非独立文件通道 (免新增文件 transport 依赖,P5 运维按字段路由聚合);注入令牌 EMERGENCY_AUDIT_LOGGER,测试覆写为内存目的地断言字段 - isAlbumVisibleOv 共享判定(status=active && publicEnabled && !emergencyDisabled——镜像 cn 公开语义叠加本地停用位):Task 6 公开 API / Task 7 WS subscribe 的 404 ALBUM_NOT_PUBLIC 语义单一实现, 判定矩阵单测锁定 - 契约 [internal] 增 emergency disable/enable 操作与 schema(gen 已跑、 redocly 通过);e2e:token/id 定位、幂等、enable 恢复、未签名 401、 均缺省 400、未知相册 404、同步事件不解除 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+157
@@ -688,6 +688,54 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/internal/emergency/disable": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/**
|
||||
* 海外紧急停用相册(ov 域)
|
||||
* @description ov 侧独立于同步链路的紧急停用入口:直接置 albums_ov.emergency_disabled
|
||||
* =true(ov 本地写,不经同步版本链——后续任何同步事件不得解除,恢复仅经
|
||||
* /internal/emergency/enable 显式操作)。停用相册在 ov 公开 API/WS 侧按
|
||||
* 404 ALBUM_NOT_PUBLIC 语义处理;cn 侧不感知(同步照常)。albumToken 与
|
||||
* albumId 二选一(都缺省 400)。审计:ov 侧结构化日志以 audit=emergency
|
||||
* 字段标记(P2 决策:不做独立文件通道,P5 运维按字段路由聚合)。
|
||||
* 鉴权见 [internal] tag 描述。
|
||||
*/
|
||||
post: operations["emergencyDisable"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/internal/emergency/enable": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
/**
|
||||
* 解除海外紧急停用(ov 域,显式唯一恢复通道)
|
||||
* @description 唯一能清除 albums_ov.emergency_disabled 的入口(显式操作,任何同步
|
||||
* 事件都不会代为解除)。albumToken 与 albumId 二选一(都缺省 400)。
|
||||
* 审计同 disable(audit=emergency 字段)。鉴权见 [internal] tag 描述。
|
||||
*/
|
||||
post: operations["emergencyEnable"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
}
|
||||
export type webhooks = Record<string, never>;
|
||||
export interface components {
|
||||
@@ -1454,6 +1502,29 @@ export interface components {
|
||||
/** @description 相册摘要清单;空库为空数组。 */
|
||||
albums: components["schemas"]["AlbumDigest"][];
|
||||
};
|
||||
/** @description 紧急停用请求(albumToken 与 albumId 恰好提供其一)。 */
|
||||
EmergencyDisableRequest: {
|
||||
/** @description 公开令牌定位相册(与 albumId 二选一)。 */
|
||||
albumToken?: string;
|
||||
/** @description 相册 ID 定位(与 albumToken 二选一)。 */
|
||||
albumId?: string;
|
||||
/** @description 停用原因(审计留痕)。 */
|
||||
reason: string;
|
||||
};
|
||||
/** @description 解除停用请求(albumToken 与 albumId 恰好提供其一)。 */
|
||||
EmergencyEnableRequest: {
|
||||
/** @description 公开令牌定位相册(与 albumId 二选一)。 */
|
||||
albumToken?: string;
|
||||
/** @description 相册 ID 定位(与 albumToken 二选一)。 */
|
||||
albumId?: string;
|
||||
};
|
||||
/** @description 紧急停用通道操作结果。 */
|
||||
EmergencyStatus: {
|
||||
/** @description 操作后的停用状态(disable 恒 true / enable 恒 false)。 */
|
||||
disabled: boolean;
|
||||
/** @description 相册 ID。 */
|
||||
albumId: string;
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description 未认证:令牌缺失、无效或已过期(AUTH_TOKEN_EXPIRED)。 */
|
||||
@@ -3008,4 +3079,90 @@ export interface operations {
|
||||
500: components["responses"]["InternalError"];
|
||||
};
|
||||
};
|
||||
emergencyDisable: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["EmergencyDisableRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description 已停用(幂等:重复停用返回同一结果)。 */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["EmergencyStatus"];
|
||||
};
|
||||
};
|
||||
/** @description 请求体校验失败(VALIDATION_FAILED;含 albumToken/albumId 均缺省)。 */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["Error"];
|
||||
};
|
||||
};
|
||||
/** @description 相册副本不存在(ALBUM_NOT_FOUND——ov 尚未同步该相册)。 */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["Error"];
|
||||
};
|
||||
};
|
||||
500: components["responses"]["InternalError"];
|
||||
};
|
||||
};
|
||||
emergencyEnable: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["EmergencyEnableRequest"];
|
||||
};
|
||||
};
|
||||
responses: {
|
||||
/** @description 已恢复(幂等:重复启用返回同一结果)。 */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["EmergencyStatus"];
|
||||
};
|
||||
};
|
||||
/** @description 请求体校验失败(VALIDATION_FAILED;含 albumToken/albumId 均缺省)。 */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["Error"];
|
||||
};
|
||||
};
|
||||
/** @description 相册副本不存在(ALBUM_NOT_FOUND)。 */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["Error"];
|
||||
};
|
||||
};
|
||||
500: components["responses"]["InternalError"];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user