/** * 系统日志「动作」中文化:路由模板 + 方法 → 人话描述。 * 精确匹配优先,未命中回退资源段兜底;新增路由不强求登记,兜底可读。 */ const exact: Record = { 'POST /api/v1/auth/login': '登录面板', 'POST /api/v1/auth/logout': '退出登录', 'GET /api/v1/auth/oauth/:provider/callback': '外部身份登录 / 绑定', 'PUT /api/v1/auth/credentials': '修改登录凭据', 'PUT /api/v1/auth/password-login': '切换密码登录开关', 'POST /api/v1/auth/revoke-sessions': '撤销全部会话', 'POST /api/v1/auth/totp/setup': '发起两步验证绑定', 'POST /api/v1/auth/totp/activate': '启用两步验证', 'POST /api/v1/auth/totp/disable': '停用两步验证', 'DELETE /api/v1/auth/identities/:id': '解绑外部身份', 'PUT /api/v1/settings/telegram': '保存 Telegram 通知配置', 'POST /api/v1/settings/telegram/test': '发送通知测试消息', 'PUT /api/v1/settings/notify-events': '保存通知管理开关', 'PUT /api/v1/settings/task': '保存任务设置', 'PUT /api/v1/settings/security': '保存安全设置', 'PUT /api/v1/settings/oauth': '保存登录方式配置', 'POST /api/v1/oci-configs': '导入租户 API Key', 'PUT /api/v1/oci-configs/:id': '修改租户配置', 'DELETE /api/v1/oci-configs/:id': '删除租户配置', 'POST /api/v1/oci-configs/:id/verify': '租户测活', 'POST /api/v1/oci-configs/:id/region-subscriptions': '订阅区域', 'POST /api/v1/oci-configs/:id/instances': '创建实例', 'PUT /api/v1/oci-configs/:id/instances/:instanceId': '修改实例配置', 'DELETE /api/v1/oci-configs/:id/instances/:instanceId': '终止实例', 'POST /api/v1/oci-configs/:id/instances/:instanceId/action': '实例电源操作', 'POST /api/v1/oci-configs/:id/instances/:instanceId/change-public-ip': '更换公网 IP', 'POST /api/v1/oci-configs/:id/instances/:instanceId/ipv6-addresses': '添加 IPv6 地址', 'DELETE /api/v1/oci-configs/:id/instances/:instanceId/ipv6-addresses': '删除 IPv6 地址', 'POST /api/v1/oci-configs/:id/instances/:instanceId/replace-boot-volume': '更换引导卷', 'POST /api/v1/oci-configs/:id/users': '创建租户用户', 'PUT /api/v1/oci-configs/:id/users/:userId': '修改租户用户', 'DELETE /api/v1/oci-configs/:id/users/:userId': '删除租户用户', 'POST /api/v1/oci-configs/:id/users/:userId/reset-password': '重置用户密码', 'DELETE /api/v1/oci-configs/:id/users/:userId/mfa-devices': '移除用户 MFA', 'DELETE /api/v1/oci-configs/:id/users/:userId/api-keys': '清除用户 API Key', 'POST /api/v1/oci-configs/:id/log-webhook': '生成回传回调地址', 'DELETE /api/v1/oci-configs/:id/log-webhook': '撤销回传回调地址', 'POST /api/v1/oci-configs/:id/log-relay': '一键创建回传链路', 'DELETE /api/v1/oci-configs/:id/log-relay': '销毁回传链路', 'POST /api/v1/tasks': '创建后台任务', 'PUT /api/v1/tasks/:id': '修改后台任务', 'DELETE /api/v1/tasks/:id': '删除后台任务', 'POST /api/v1/tasks/:id/run': '手动执行任务', 'POST /api/v1/webhooks/oci-logs/:secret': '回传消息投递', 'POST /api/v1/ai-keys': '创建网关密钥', 'PUT /api/v1/ai-keys/:id': '修改网关密钥', 'DELETE /api/v1/ai-keys/:id': '删除网关密钥', 'PUT /api/v1/ai-keys/:id/content-log': '设置密钥内容留痕', 'POST /api/v1/ai-channels': '创建网关渠道', 'PUT /api/v1/ai-channels/:id': '修改网关渠道', 'DELETE /api/v1/ai-channels/:id': '删除网关渠道', 'POST /api/v1/ai-channels/:id/probe': '探测网关渠道', 'POST /api/v1/ai-channels/:id/sync-models': '同步渠道模型', 'POST /api/v1/ai-blacklist': '拉黑模型', 'DELETE /api/v1/ai-blacklist/:id': '移出模型黑名单', 'PUT /api/v1/ai-settings': '保存网关设置', 'PUT /api/v1/settings/notify-channels/:type': '保存通知渠道配置', 'POST /api/v1/settings/notify-channels/:type/test': '发送通知测试消息', 'PUT /api/v1/settings/notify-templates/:kind': '保存通知模板', 'POST /api/v1/settings/notify-templates/:kind/test': '发送模板测试消息', 'POST /api/v1/proxies/import': '批量导入代理', 'POST /api/v1/proxies/:id/probe': '探测代理', 'PUT /api/v1/proxies/:id/tenants': '设置代理关联租户', } /** 资源段兜底词典:精确表未命中时以「动词 + 资源」组合 */ const nouns: Record = { vcns: 'VCN', subnets: '子网', 'security-lists': '安全列表', 'boot-volumes': '引导卷', 'boot-volume-attachments': '引导卷挂载', 'volume-attachments': '块卷挂载', 'console-sessions': '控制台会话', 'console-connections': '控制台连接', 'notification-recipients': '通知收件人', 'password-policies': '密码策略', 'identity-settings': '身份设置', 'identity-providers': '身份提供方', 'sign-on-exemptions': 'MFA 豁免', proxies: '代理', } const verbs: Record = { POST: '创建', PUT: '修改', DELETE: '删除' } /** 取路径中最后一个可翻译的资源段(跳过 :param 与动作尾段) */ function lastNoun(path: string): string { const segs = path.split('/').filter((s) => s && !s.startsWith(':')) for (let i = segs.length - 1; i >= 0; i--) { const zh = nouns[segs[i]] if (zh) return zh } return '' } export function actionLabel(method: string, path: string): string { const hit = exact[`${method} ${path}`] if (hit) return hit const noun = lastNoun(path) if (noun) return `${verbs[method] ?? method}${noun}` return '' }