新增玩家手动充值全流程(收款方式配置、充值下单/审核、钱包上分), 支持邀请码注册、邀请历史与专属返水率;完善后台代理/玩家管理与响应式操作栏, 并补充前台注册、充值页及多语言错误码。 Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
2.4 KiB
TypeScript
83 lines
2.4 KiB
TypeScript
import {
|
|
reconcileStaffSessionFromToken,
|
|
useAuthStore,
|
|
type StaffUser,
|
|
type StaffUserType,
|
|
} from '../stores/auth';
|
|
|
|
let hydratePromise: Promise<boolean> | null = null;
|
|
|
|
function isStaffUserType(value: unknown): value is StaffUserType {
|
|
return value === 'ADMIN' || value === 'AGENT';
|
|
}
|
|
|
|
export function resetStaffSessionHydration() {
|
|
hydratePromise = null;
|
|
}
|
|
|
|
function hasCompleteStaffUser(u: StaffUser | null | undefined): u is StaffUser {
|
|
return !!(u?.id && u.username && u.userType);
|
|
}
|
|
|
|
/** Sync manage_user from JWT + /manage/auth/me (fixes stale localStorage userType). */
|
|
export async function hydrateStaffSession(): Promise<boolean> {
|
|
const auth = useAuthStore();
|
|
if (!auth.token.value) return false;
|
|
if (hydratePromise) return hydratePromise;
|
|
|
|
hydratePromise = (async () => {
|
|
reconcileStaffSessionFromToken();
|
|
|
|
if (!hasCompleteStaffUser(auth.user.value)) {
|
|
auth.clearStaffSession();
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
const { default: api } = await import('../api');
|
|
const { data } = await api.get('/manage/auth/me');
|
|
const raw = data.data as Partial<StaffUser>;
|
|
if (!raw?.id || !raw.username || !isStaffUserType(raw.userType)) {
|
|
return true;
|
|
}
|
|
auth.setSession(auth.token.value, {
|
|
id: raw.id,
|
|
username: raw.username,
|
|
userType: raw.userType,
|
|
locale: raw.locale,
|
|
role: raw.role,
|
|
agentLevel: typeof raw.agentLevel === 'number' ? raw.agentLevel : null,
|
|
maxAgentLevel: typeof raw.maxAgentLevel === 'number' ? raw.maxAgentLevel : null,
|
|
canManageSubAgents: raw.canManageSubAgents === true,
|
|
inviteCode: raw.inviteCode ?? null,
|
|
});
|
|
return true;
|
|
} catch (e: unknown) {
|
|
const status = (e as { response?: { status?: number } })?.response?.status;
|
|
if (status === 401) {
|
|
auth.clearStaffSession();
|
|
return false;
|
|
}
|
|
return hasCompleteStaffUser(auth.user.value);
|
|
} finally {
|
|
hydratePromise = null;
|
|
}
|
|
})();
|
|
|
|
return hydratePromise;
|
|
}
|
|
|
|
/** Run before any authenticated route — JWT reconcile + optional /me refresh. */
|
|
export async function ensureStaffSession(): Promise<boolean> {
|
|
reconcileStaffSessionFromToken();
|
|
const auth = useAuthStore();
|
|
if (!auth.token.value) return false;
|
|
if (!hasCompleteStaffUser(auth.user.value)) {
|
|
reconcileStaffSessionFromToken();
|
|
}
|
|
if (!hasCompleteStaffUser(auth.user.value)) {
|
|
return false;
|
|
}
|
|
return hydrateStaffSession();
|
|
}
|