refactor(web): update service clients

- services/agentAssets.js: update agent assets service client
- services/api.js: update API service client
- services/employees.js: update employees service client
This commit is contained in:
caoxiaozhu
2026-05-14 02:58:35 +00:00
parent 3c28cab288
commit feacf2765d
3 changed files with 124 additions and 47 deletions

View File

@@ -1,4 +1,4 @@
import { apiRequest } from './api.js' import { apiRequest, pickSafeHeaderValue } from './api.js'
const AUTH_USER_STORAGE_KEY = 'x-financial-auth-user' const AUTH_USER_STORAGE_KEY = 'x-financial-auth-user'
@@ -14,7 +14,11 @@ function readActorName() {
try { try {
const payload = JSON.parse(raw) const payload = JSON.parse(raw)
return String(payload?.name || payload?.username || 'system').trim() || 'system' return (
pickSafeHeaderValue(payload?.name, payload?.username) ||
pickSafeHeaderValue(payload?.username, 'system') ||
'system'
)
} catch { } catch {
return 'system' return 'system'
} }

View File

@@ -1,6 +1,36 @@
const API_BASE_STORAGE_KEY = 'x-financial-api-base-url' const API_BASE_STORAGE_KEY = 'x-financial-api-base-url'
const AUTH_USER_STORAGE_KEY = 'x-financial-auth-user' const AUTH_USER_STORAGE_KEY = 'x-financial-auth-user'
function isHeaderValueSafe(value) {
const normalized = String(value || '').trim()
if (!normalized) {
return false
}
for (let index = 0; index < normalized.length; index += 1) {
const codePoint = normalized.charCodeAt(index)
if (codePoint > 255 || codePoint === 10 || codePoint === 13 || codePoint === 0) {
return false
}
}
return true
}
export function pickSafeHeaderValue(value, fallback = '') {
const normalized = String(value || '').trim()
if (isHeaderValueSafe(normalized)) {
return normalized
}
const fallbackValue = String(fallback || '').trim()
if (isHeaderValueSafe(fallbackValue)) {
return fallbackValue
}
return ''
}
function readCurrentUserHeaders() { function readCurrentUserHeaders() {
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
return {} return {}
@@ -17,17 +47,27 @@ function readCurrentUserHeaders() {
const name = String(payload?.name || username).trim() const name = String(payload?.name || username).trim()
const roleCodes = Array.isArray(payload?.roleCodes) ? payload.roleCodes.filter(Boolean) : [] const roleCodes = Array.isArray(payload?.roleCodes) ? payload.roleCodes.filter(Boolean) : []
const isAdmin = Boolean(payload?.isAdmin) const isAdmin = Boolean(payload?.isAdmin)
const safeUsername = pickSafeHeaderValue(username)
const safeName = pickSafeHeaderValue(name)
if (!username && !name) { if (!safeUsername && !safeName) {
return {} return {}
} }
return { const headers = {
'x-auth-username': username,
'x-auth-name': name,
'x-auth-role-codes': roleCodes.join(','), 'x-auth-role-codes': roleCodes.join(','),
'x-auth-is-admin': String(isAdmin) 'x-auth-is-admin': String(isAdmin)
} }
if (safeUsername) {
headers['x-auth-username'] = safeUsername
}
if (safeName) {
headers['x-auth-name'] = safeName
}
return headers
} catch { } catch {
return {} return {}
} }
@@ -158,6 +198,29 @@ function resolveErrorMessage(payload, fallback = '接口请求失败,请稍后
return fallback return fallback
} }
function sanitizeHeaders(headers) {
const nextHeaders = {}
Object.entries(headers || {}).forEach(([key, value]) => {
if (typeof value === 'undefined' || value === null) {
return
}
const normalizedValue = String(value).trim()
if (!normalizedValue) {
return
}
if (!isHeaderValueSafe(normalizedValue)) {
return
}
nextHeaders[key] = normalizedValue
})
return nextHeaders
}
export async function apiRequest(path, options = {}) { export async function apiRequest(path, options = {}) {
const { const {
contentType = 'application/json', contentType = 'application/json',
@@ -166,10 +229,10 @@ export async function apiRequest(path, options = {}) {
...fetchOptions ...fetchOptions
} = options } = options
const headers = { const headers = sanitizeHeaders({
...readCurrentUserHeaders(), ...readCurrentUserHeaders(),
...(customHeaders || {}) ...(customHeaders || {})
} })
if (contentType !== null && typeof headers['Content-Type'] === 'undefined') { if (contentType !== null && typeof headers['Content-Type'] === 'undefined') {
headers['Content-Type'] = contentType headers['Content-Type'] = contentType
@@ -182,7 +245,11 @@ export async function apiRequest(path, options = {}) {
...fetchOptions, ...fetchOptions,
headers headers
}) })
} catch { } catch (error) {
if (String(error?.message || '').includes('ByteString')) {
throw new Error('当前登录用户信息包含浏览器不支持的请求头字符,请重新登录后重试。')
}
throw new Error('无法连接 FastAPI 后端服务,请确认后端已启动且浏览器可访问后端端口。') throw new Error('无法连接 FastAPI 后端服务,请确认后端已启动且浏览器可访问后端端口。')
} }

View File

@@ -35,3 +35,9 @@ export function disableEmployee(employeeId) {
method: 'POST' method: 'POST'
}) })
} }
export function enableEmployee(employeeId) {
return apiRequest(`/employees/${employeeId}/enable`, {
method: 'POST'
})
}