diff --git a/website/src/services/auth.js b/website/src/services/auth.js index 90421eb..14009bb 100644 --- a/website/src/services/auth.js +++ b/website/src/services/auth.js @@ -1,79 +1,75 @@ /** - * 认证服务(auth service) + * 认证服务(auth service)——统一账号 * ------------------------------------------------------------- - * 用户端:手机号 + 短信验证码登录 / 注册(手机号即账号,未注册自动注册)。 - * POST /api/auth/send-code 发送验证码(演示环境响应返回 debugCode) - * POST /api/auth/phone-login 手机号 + 验证码 → 自动注册或登录 → token + * 用户端:手机号 + 短信验证码登录(手机号即账号,未注册自动注册)。 + * POST /auth/send-code 发送验证码(stub 环境返回 stub_code/debugCode) + * POST /auth/phone-login 手机号 + 验证码 → 自动注册或登录 → 平台 JWT + * POST /auth/update-profile 更新资料 → 平台 profile * 平台端(/pine 内部管理):账号密码登录(内置 pine/123456)。 - * POST /api/auth/login - * 迁移真实后端:只改 API_BASE 与保持 /api/auth/* 契约即可。 + * POST /auth/login 密码登录 → 平台 JWT + 身份列表 + * 统一账号:所有登录写全局 users + 平台 JWT(与小程序一致,见 分端口同步约束.md)。 */ const API_BASE = 'https://opc.pinesound.cn'; // 云超服 FastAPI 后端 const TOKEN_KEY = 'pine_token'; const USER_KEY = 'pine_user'; +/** 平台 profile → 本地 user 契约字段(各页面读取 name/status/topics/source/phoneBound 等) */ +function pickUser(d) { + return { + username: d.username, + name: d.name || d.nickname || d.username || '用户', + avatar: d.avatar || '', + phone: d.phone || '', + phoneBound: !!d.phoneBound, + status: d.status || '', + topics: Array.isArray(d.topics) ? d.topics : [], + source: d.source || '' + }; +} + export function saveSession(data) { if (!data || !data.token) return; localStorage.setItem(TOKEN_KEY, data.token); - localStorage.setItem(USER_KEY, JSON.stringify({ username: data.username, name: data.name || data.username })); + localStorage.setItem(USER_KEY, JSON.stringify(pickUser(data))); +} + +async function req(path, { method = 'POST', body, auth = false } = {}) { + const headers = { 'Content-Type': 'application/json', 'X-Client': 'web' }; + if (auth) { const t = getToken(); if (t) headers.Authorization = `Bearer ${t}`; } + let res; + try { + res = await fetch(`${API_BASE}${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined }); + } catch { + throw new Error('网络连接失败,请检查网络后重试'); + } + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data.detail || data.error || '请求失败,请重试'); + return data; // 平台契约:token/username/name/...(/auth/*) } /* ---------------- 用户端:手机号 + 验证码 ---------------- */ export async function sendCode(phone) { - let res; - try { - res = await fetch(`${API_BASE}/api/auth/send-code`, { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'X-Client': 'web' }, - body: JSON.stringify({ phone }) - }); - } catch { - throw new Error('网络连接失败,请检查网络后重试'); - } - const data = await res.json().catch(() => ({})); - if (!res.ok || !data.ok) throw new Error(data.error || '验证码发送失败,请重试'); - return data; // { ok, sent, debugCode } + const data = await req('/auth/send-code', { body: { phone } }); + return { ok: true, sent: true, debugCode: data.stub_code ?? data.debugCode }; } export async function phoneLogin(phone, code) { - let res; - try { - res = await fetch(`${API_BASE}/api/auth/phone-login`, { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'X-Client': 'web' }, - body: JSON.stringify({ phone, code }) - }); - } catch { - throw new Error('网络连接失败,请检查网络后重试'); - } - const data = await res.json().catch(() => ({})); - if (!res.ok || !data.token) throw new Error(data.error || '登录失败,请重试'); - saveSession(data); + const data = await req('/auth/phone-login', { body: { phone, code } }); + if (data.token) saveSession(data); return data; } /* ---------------- 平台端:账号密码登录(内部管理) ---------------- */ export async function login(username, password) { - let res; - try { - res = await fetch(`${API_BASE}/api/auth/login`, { - method: 'POST', - headers: { 'Content-Type': 'application/json', 'X-Client': 'web' }, - body: JSON.stringify({ username, password }) - }); - } catch { - throw new Error('网络连接失败,请检查网络后重试'); - } - const data = await res.json().catch(() => ({})); - if (!res.ok || !data.token) throw new Error(data.error || '登录失败,请重试'); - saveSession(data); + const data = await req('/auth/login', { body: { username, password } }); + if (data.token) saveSession(data); return data; } export function logout() { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); - try { fetch(`${API_BASE}/api/auth/logout`, { method: 'POST' }).catch(() => {}); } catch { /* noop */ } + // 平台用 /auth/revoke-token;这里本地退出即可(与小程序一致) } export function getToken() { @@ -89,15 +85,9 @@ export function saveUser(user) { /** 更新当前用户资料(昵称/头像 + 报名资料 status/topics/source),需登录(token) */ export async function updateProfile(data) { - const token = getToken(); - const res = await fetch(`${API_BASE}/api/auth/update-profile`, { - method: 'POST', - headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) }, - body: JSON.stringify(data) - }); - const r = await res.json().catch(() => ({})); - if (res.ok && r.ok && r.user) saveUser(r.user); - return r; + const r = await req('/auth/update-profile', { body: data, auth: true }); + if (r.username) saveUser(pickUser(r)); + return { ok: true, user: pickUser(r) }; } export function isAuthed() {