feat(teacher-portal): 实现登录页侧边栏路由组与真实JWT集成
- lib/auth.ts: localStorage token 存储 + login/logout - app/login: 登录表单页 - components/AppShell: 视口驱动侧边栏 + 路由保护 - app/(app)/layout: 路由组布局套 AppShell - app/(app)/dashboard: 聚合统计卡片页 - app/(app)/classes: 迁移 CRUD 改用真实 JWT - app/page.tsx: 根路径重定向 - known-issues.md: 记录 P2 经验
This commit is contained in:
77
apps/teacher-portal/src/lib/auth.ts
Normal file
77
apps/teacher-portal/src/lib/auth.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
// 认证工具:token 存储 + 路由保护
|
||||
|
||||
const TOKEN_KEY = "edu_access_token";
|
||||
const USER_KEY = "edu_user_info";
|
||||
|
||||
export interface UserInfo {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
roles: string[];
|
||||
permissions: string[];
|
||||
dataScope: string;
|
||||
}
|
||||
|
||||
export function getToken(): string | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
return localStorage.getItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
export function setToken(token: string): void {
|
||||
if (typeof window === "undefined") return;
|
||||
localStorage.setItem(TOKEN_KEY, token);
|
||||
}
|
||||
|
||||
export function clearToken(): void {
|
||||
if (typeof window === "undefined") return;
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
localStorage.removeItem(USER_KEY);
|
||||
}
|
||||
|
||||
export function getUser(): UserInfo | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
const raw = localStorage.getItem(USER_KEY);
|
||||
if (!raw) return null;
|
||||
try {
|
||||
return JSON.parse(raw) as UserInfo;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function setUser(user: UserInfo): void {
|
||||
if (typeof window === "undefined") return;
|
||||
localStorage.setItem(USER_KEY, JSON.stringify(user));
|
||||
}
|
||||
|
||||
export function isAuthenticated(): boolean {
|
||||
return getToken() !== null;
|
||||
}
|
||||
|
||||
// 调用 Gateway 登录接口
|
||||
export async function login(
|
||||
email: string,
|
||||
password: string,
|
||||
): Promise<{ user: UserInfo; token: string }> {
|
||||
const res = await fetch("/api/v1/iam/login", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!json.success) {
|
||||
throw new Error(json.error?.message || "登录失败");
|
||||
}
|
||||
const user = json.data.user as UserInfo;
|
||||
const token = json.data.tokens.accessToken as string;
|
||||
setToken(token);
|
||||
setUser(user);
|
||||
return { user, token };
|
||||
}
|
||||
|
||||
export function logout(): void {
|
||||
clearToken();
|
||||
if (typeof window !== "undefined") {
|
||||
window.location.href = "/login";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user