fix(web): api-error 展示后端业务消息并忽略 pnpm 本地缓存

- 403 与 5xx 分支优先展示后端本地化业务消息(账号禁用、目录不可用、TLS 配置错误),无消息时回退通用文案
- .gitignore 增加 .pnpm-store/

Signed-off-by: jangrui <admin@jangrui.com>
This commit is contained in:
jangrui 2026-08-01 13:04:36 +08:00
parent 34e01171a6
commit d7e41ec9ff
4 changed files with 31 additions and 2 deletions

1
.gitignore vendored
View file

@ -55,6 +55,7 @@ target/
coverage/
dist/
node_modules/
.pnpm-store/
*.tsbuildinfo
package-lock.json

View file

@ -57,6 +57,30 @@ describe('handleApiError', () => {
expect(errorSpy).toHaveBeenLastCalledWith('Server said no')
})
it('shows the server message for 403 errors when present', async () => {
const { ApiError, handleApiError } = await import('./api-error')
handleApiError(new ApiError('apiError.forbidden', 403, '账号已被禁用,请联系管理员'))
expect(errorSpy).toHaveBeenLastCalledWith('账号已被禁用,请联系管理员')
})
it('shows the server message for 5xx errors when present', async () => {
const { ApiError, handleApiError } = await import('./api-error')
handleApiError(new ApiError('apiError.serverError', 503, '目录服务器暂时不可用,请稍后重试'))
expect(errorSpy).toHaveBeenLastCalledWith('目录服务器暂时不可用,请稍后重试')
})
it('falls back to generic text for 403/5xx without a server message', async () => {
const { ApiError, handleApiError } = await import('./api-error')
handleApiError(new ApiError('apiError.forbidden', 403))
expect(errorSpy).toHaveBeenLastCalledWith(i18n.t('apiError.forbidden'))
})
it('shows network error message when status is 0 (network disconnected)', async () => {
const { ApiError, handleApiError } = await import('./api-error')

View file

@ -68,7 +68,9 @@ export function handleApiError(error: unknown): void {
}
if (status === 403) {
toast.error(i18n.t('apiError.forbidden'))
// The backend may attach a specific business message (e.g. a disabled account or an
// LDAP conflict); prefer it over the generic forbidden text.
toast.error(error.serverMessage || i18n.t('apiError.forbidden'))
return
}
@ -78,7 +80,9 @@ export function handleApiError(error: unknown): void {
}
if (status >= 500) {
toast.error(i18n.t('apiError.serverError'))
// The backend returns localized operational messages for service-level failures such as
// "directory unavailable" or "TLS misconfiguration"; surface them when present.
toast.error(error.serverMessage || i18n.t('apiError.serverError'))
return
}