'use strict'; /** * 邮件服务器「账号接口」客户端(对接服务端 v2.2.0 的账号体系)。 * * 走的是服务器上**只放账号类接口**的公网 HTTPS 入口(默认 https://mail.example.com:9443): * 注册、邮箱验证码、找回密码、登录换 token、改密码、会话与资料。 * 邮件读写(读信/发件/队列)**不经过这里**,仍然走 IMAP 993 / SMTP 587 —— * 那个入口对邮件与管理接口一律返回 404(可在服务器上用 /api/messages 验证)。 * * 零依赖:用 Node 内置的全局 fetch(Node 18+)。 */ const DEFAULT_API_BASE = 'https://mail.example.com:9443'; /** 服务器给的错误信息本身就是中文说明,直接透传给 UI;只补上网络层失败的可读提示。 */ async function call(base, path, options = {}) { const { method = 'GET', body, token, timeoutMs = 20000 } = options; const root = String(base || DEFAULT_API_BASE).replace(/\/+$/, ''); const url = root + path; const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { const headers = { 'Content-Type': 'application/json; charset=utf-8' }; if (token) headers.Authorization = `Bearer ${token}`; const res = await fetch(url, { method, headers, body: body === undefined ? undefined : JSON.stringify(body), signal: controller.signal, }); const text = await res.text(); let data = null; try { data = text ? JSON.parse(text) : null; } catch { data = { error: text }; } if (!res.ok) { const err = new Error((data && data.error) || `账号接口返回 HTTP ${res.status}`); err.status = res.status; err.data = data; throw err; } return data; } catch (err) { if (err && err.status) throw err; if (err && err.name === 'AbortError') throw new Error(`账号接口超时:${url}`); throw new Error(`连不上账号接口 ${url}:${err && err.message}`); } finally { clearTimeout(timer); } } function createAccountApi(base) { const apiBase = String(base || DEFAULT_API_BASE).replace(/\/+$/, ''); return { apiBase, policy: () => call(apiBase, '/api/auth/policy'), register: (body) => call(apiBase, '/api/register', { method: 'POST', body }), verifyRegistration: (body) => call(apiBase, '/api/register/verify', { method: 'POST', body }), resendCode: (body) => call(apiBase, '/api/register/resend', { method: 'POST', body }), forgot: (email) => call(apiBase, '/api/auth/forgot', { method: 'POST', body: { email } }), reset: (body) => call(apiBase, '/api/auth/reset', { method: 'POST', body }), login: (email, password) => call(apiBase, '/api/login', { method: 'POST', body: { email, password } }), logout: (token) => call(apiBase, '/api/logout', { method: 'POST', body: {}, token }), changePassword: (token, body) => call(apiBase, '/api/account/password', { method: 'POST', body, token }), profile: (token) => call(apiBase, '/api/me', { token }), updateProfile: (token, displayName) => call(apiBase, '/api/account/profile', { method: 'PATCH', body: { displayName }, token }), sessions: (token) => call(apiBase, '/api/account/sessions', { token }), revokeSessions: (token, body) => call(apiBase, '/api/account/sessions/revoke', { method: 'POST', body, token }), audit: (token, limit = 30) => call(apiBase, `/api/account/audit?limit=${limit}`, { token }), }; } module.exports = { DEFAULT_API_BASE, createAccountApi };