feat: Phase 1-3 全部完成 — 沙盒增强、学情分析、学习路径

This commit is contained in:
yuzhiran-dev
2026-05-18 09:48:51 +08:00
commit 11bb86854c
277 changed files with 37755 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api/v1';
export function getToken(): string | null {
if (typeof window === 'undefined') return null;
return localStorage.getItem('token');
}
export function getAdminToken(): string | null {
if (typeof window === 'undefined') return null;
return localStorage.getItem('adminToken');
}
export function getRefreshToken(): string | null {
if (typeof window === 'undefined') return null;
return localStorage.getItem('refreshToken');
}
export function setTokens(accessToken: string, refreshToken?: string) {
localStorage.setItem('token', accessToken);
if (refreshToken) localStorage.setItem('refreshToken', refreshToken);
}
export function clearTokens() {
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
}
export function clearAdminToken() {
localStorage.removeItem('adminToken');
}
export function isLoggedIn(): boolean {
return !!getToken();
}
export function isAdminLoggedIn(): boolean {
return !!getAdminToken();
}
export async function adminApiFetch(url: string, opts: RequestInit = {}): Promise<Response> {
const token = getAdminToken();
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(opts.headers as Record<string, string> || {}),
};
if (token) headers['Authorization'] = `Bearer ${token}`;
return fetch(`${API_BASE}${url}`, { ...opts, headers });
}
export async function refreshToken(): Promise<string | null> {
const token = getToken();
const refreshTk = getRefreshToken();
if (!token && !refreshTk) return null;
try {
const res = await fetch(`${API_BASE}/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ accessToken: token }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.message);
setTokens(data.accessToken, data.refreshToken);
return data.accessToken;
} catch {
clearTokens();
return null;
}
}
export async function apiFetch(url: string, opts: RequestInit = {}): Promise<Response> {
const token = getToken();
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(opts.headers as Record<string, string> || {}),
};
if (token) headers['Authorization'] = `Bearer ${token}`;
let res = await fetch(`${API_BASE}${url}`, { ...opts, headers });
if (res.status === 401 && token) {
const newToken = await refreshToken();
if (newToken) {
headers['Authorization'] = `Bearer ${newToken}`;
res = await fetch(`${API_BASE}${url}`, { ...opts, headers });
}
}
return res;
}