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>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 掩码哨兵辅助(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 : ''
|
||||
}
|
||||
Reference in New Issue
Block a user