diff --git a/docs/AUTH_API_REQUIREMENTS.md b/docs/AUTH_API_REQUIREMENTS.md new file mode 100644 index 0000000..ac7af5e --- /dev/null +++ b/docs/AUTH_API_REQUIREMENTS.md @@ -0,0 +1,17 @@ +# 用户系统与角色权限:前后端接口清单 + +当前后端未注册认证或管理员路由;前端只提供真实请求封装,任何 404、401、403 或 5xx 都会如实抛出,绝不创建本地假身份。 + +| 目标接口 | 方法 | 前端用途 | 后端待实现要求 | +|---|---:|---|---| +| `/api/auth/register` | POST | 注册 | 只创建 `user`;不得接受角色字段 | +| `/api/auth/login` | POST | 登录 | 建立 HttpOnly、Secure、SameSite Cookie 会话 | +| `/api/auth/logout` | POST | 退出 | 作废会话并清除 Cookie | +| `/api/auth/me` | GET | 刷新恢复会话 | 返回用户资料与 `user`/`admin` 角色 | +| `/api/users/me` | PATCH | 修改昵称/邮箱 | 只允许修改当前用户自己的基本资料 | +| `/api/auth/change-password` | POST | 修改密码 | 验证旧密码并进行安全哈希 | +| `/api/admin/users` | GET | 用户管理 | 必须由后端校验 `admin` 角色 | + +## 前端待接入条件 + +现有工程尚未安装 `vue-router`。在用户安装该依赖并确认认证接口可用后,前端将接入路由守卫:未登录跳转登录页、成功后回跳、管理员路由额外检查 `role === 'admin'`。前端只用于体验层控制;知识库、来源、实验、用户管理的最终授权必须由 FastAPI 校验。 diff --git a/frontend/src/services/auth.ts b/frontend/src/services/auth.ts new file mode 100644 index 0000000..9d0cd4a --- /dev/null +++ b/frontend/src/services/auth.ts @@ -0,0 +1,26 @@ +import type { ChangePasswordPayload, CurrentUser, LoginPayload, RegisterPayload, UpdateProfilePayload } from '../types/auth' + +export class AuthApiUnavailableError extends Error { + constructor(public readonly status: number, message: string) { super(message) } +} + +async function authRequest(path: string, init: RequestInit = {}): Promise { + const response = await fetch(path, { + credentials: 'include', + headers: { Accept: 'application/json', 'Content-Type': 'application/json', ...init.headers }, + ...init, + }) + if (!response.ok) { + const body = await response.json().catch(() => null) + throw new AuthApiUnavailableError(response.status, body?.detail || `认证请求失败(${response.status})`) + } + return response.status === 204 ? undefined as T : response.json() as Promise +} + +export const register = (payload: RegisterPayload) => authRequest('/api/auth/register', { method: 'POST', body: JSON.stringify(payload) }) +export const login = (payload: LoginPayload) => authRequest('/api/auth/login', { method: 'POST', body: JSON.stringify(payload) }) +export const logout = () => authRequest('/api/auth/logout', { method: 'POST' }) +export const getCurrentUser = () => authRequest('/api/auth/me') +export const updateMyProfile = (payload: UpdateProfilePayload) => authRequest('/api/users/me', { method: 'PATCH', body: JSON.stringify(payload) }) +export const changePassword = (payload: ChangePasswordPayload) => authRequest('/api/auth/change-password', { method: 'POST', body: JSON.stringify(payload) }) +export const getUsers = () => authRequest('/api/admin/users') diff --git a/frontend/src/types/auth.ts b/frontend/src/types/auth.ts new file mode 100644 index 0000000..9979d31 --- /dev/null +++ b/frontend/src/types/auth.ts @@ -0,0 +1,16 @@ +export type UserRole = 'user' | 'admin' + +export type CurrentUser = { + id: number + username: string + nickname: string | null + email: string | null + role: UserRole + created_at?: string + conversation_count?: number +} + +export type RegisterPayload = { username: string; password: string; email?: string; nickname?: string } +export type LoginPayload = { username: string; password: string } +export type UpdateProfilePayload = { nickname?: string; email?: string } +export type ChangePasswordPayload = { current_password: string; new_password: string } diff --git a/frontend/src/views/AccountSettingsView.vue b/frontend/src/views/AccountSettingsView.vue new file mode 100644 index 0000000..825440c --- /dev/null +++ b/frontend/src/views/AccountSettingsView.vue @@ -0,0 +1,8 @@ + + diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue new file mode 100644 index 0000000..da2fcdf --- /dev/null +++ b/frontend/src/views/LoginView.vue @@ -0,0 +1,7 @@ + + diff --git a/frontend/src/views/ProfileView.vue b/frontend/src/views/ProfileView.vue new file mode 100644 index 0000000..5aca313 --- /dev/null +++ b/frontend/src/views/ProfileView.vue @@ -0,0 +1,8 @@ + + diff --git a/frontend/src/views/RegisterView.vue b/frontend/src/views/RegisterView.vue new file mode 100644 index 0000000..2abc046 --- /dev/null +++ b/frontend/src/views/RegisterView.vue @@ -0,0 +1,7 @@ + + diff --git a/frontend/src/views/UserManagementView.vue b/frontend/src/views/UserManagementView.vue new file mode 100644 index 0000000..96987c9 --- /dev/null +++ b/frontend/src/views/UserManagementView.vue @@ -0,0 +1,8 @@ + +