Files
ai-kefu-admin/admin/src/api/mask.ts
T
浅念 3973abea8b feat: API层与认证流(client拦截/守卫/登录改密实装/域API骨架/vitest)
- 自建轻量 axios client(src/api/client.ts):Bearer 注入、401 清会话跳登录
  (login 请求除外)、409 精确匹配"必须先修改初始密码"跳强制改密(区别于并发
  冲突 409)、429/网络错误统一 ElMessage、ApiError 标准化、showErrorMessage
  可opt-out——模板 utils/http 的 {code,msg,data} 信封与后端裸 HTTP 语义不符故弃用
- Pinia auth store(src/store/modules/auth.ts):token/role/tenant_slug/
  must_change 落地并持久化(all-kefu-auth key),login/logout/changePassword/
  会话过期/强制改密跳转(动态 import 断循环)
- 路由守卫:token 事实源判定、must_change 全站门(除改密页)、UserInfo 由登录
  快照派生(后端无用户信息端点);user store 残留自愈
- 登录页实装(去除角色调试开关)、强制改密页实装;用户菜单/锁屏对接真实 logout
- 域 API 骨架:tenants/config/conversations/stats/settings(类型+端点常量)
- mask 哨兵助手:__SET__ ↔ 已设置 表单语义
- vitest 25 例(client 11/store 8/mask 6);登录页拖拽验证 reset 空引用修复

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-13 16:28:11 +08:00

45 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 掩码哨兵辅助(Phase 3 Task 3)
*
* 契约对齐后端 app/platform/routes/config_routes.py:
* - 密钥字段 GET 响应:库中非空 → `__SET__`(MASKED 哨兵);空/缺省 → 原样
* - PUT 提交:密钥字段传 `__SET__` = 保持库值(后端还原,哨兵字面量绝不落库)
*
* 本模块提供哨兵 ↔ 表单语义的纯函数转换,配置中心(Task 7)与
* 平台设置(Task 9)的凭据表单消费:
* - secretPlaceholder:GET 掩码值 → 输入框占位文案("已设置,输入新值覆盖")
* - secretToSubmit:用户输入 + 原掩码状态 → 提交值(未改动回哨兵保持库值)
*/
/** 后端密钥掩码哨兵(config_routes.MASKED) */
export const MASKED_SENTINEL = '__SET__'
/** 表单侧"已设置"展示语义 */
export const MASKED_DISPLAY = '已设置'
/** 值是否为掩码哨兵 */
export function isMaskedSentinel(value: unknown): value is typeof MASKED_SENTINEL {
return value === MASKED_SENTINEL
}
/**
* GET 掩码值 → 表单占位文案
*
* @param maskedValue 后端下发的密钥字段值(哨兵=已设置;空串/null/undefined=未设置)
*/
export function secretPlaceholder(maskedValue: string | null | undefined): string {
return isMaskedSentinel(maskedValue) ? '已设置,输入新值覆盖' : '未设置,输入新值'
}
/**
* 表单输入 → 提交值
*
* @param input 用户输入(空串=未改动/清空)
* @param originalMasked GET 时的掩码值(哨兵=库中已有值;否则=未设置)
* @returns 提交值:输入非空 → 新值;输入空且已设置 → 哨兵(保持库值);输入空且未设置 → 空串
*/
export function secretToSubmit(input: string, originalMasked: string | null | undefined): string {
if (input !== '') return input
return isMaskedSentinel(originalMasked) ? MASKED_SENTINEL : ''
}