feat: WC2026 赛事 seed、生产上线初始化脚本与目录归档

重构 seed 为 WC2026 72 场小组赛与 48 强优胜盘;新增 production 模式仅保留 admin 与赛事示例;提供 prod-init-db 全量重置脚本;管理端 i18n 分包与赛事归档能力。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-12 18:17:00 +08:00
parent 8f14e85ebd
commit e7e938f261
94 changed files with 12332 additions and 976 deletions

View File

@@ -1,4 +1,13 @@
import axios from 'axios';
import { isApiErrorCode } from '@thebet365/shared';
import { useAuthStore } from '../stores/auth';
const ACCOUNT_BLOCKED_CODES = new Set([
'ACCOUNT_SUSPENDED',
'ACCOUNT_DISABLED',
'AGENT_ACCOUNT_SUSPENDED',
'PARENT_AGENT_SUSPENDED',
]);
const api = axios.create({ baseURL: '/api' });
@@ -12,16 +21,28 @@ api.interceptors.request.use((config) => {
return config;
});
function clearSession() {
localStorage.removeItem('token');
localStorage.removeItem('user');
try {
useAuthStore().logout();
} catch {
// Pinia may not be ready during bootstrap
}
}
api.interceptors.response.use(
(res) => res,
(err) => {
if (err.response?.status === 401) {
const url: string = err.config?.url ?? '';
// Don't redirect on login/auth failures — let the caller handle the error
if (!url.includes('/auth/login') && !url.includes('/auth/register')) {
localStorage.removeItem('token');
// 不再强制跳转登录页,让调用方处理 401
}
const status = err.response?.status;
const url: string = err.config?.url ?? '';
const isAuthEndpoint = url.includes('/auth/login') || url.includes('/auth/register');
const code = err.response?.data?.code;
const blockedAccount =
typeof code === 'string' && isApiErrorCode(code) && ACCOUNT_BLOCKED_CODES.has(code);
if (!isAuthEndpoint && (status === 401 || (status === 403 && blockedAccount))) {
clearSession();
}
return Promise.reject(err);
},