112 lines
3.7 KiB
JavaScript
112 lines
3.7 KiB
JavaScript
const TOKEN_KEY = 'mengpost_token'
|
||
const EVT = 'mengpost-token'
|
||
|
||
/** 生产环境前后端分离时设 VITE_API_URL, 如 http://127.0.0.1:8787 */
|
||
const base = (import.meta.env.VITE_API_URL || '').replace(/\/$/, '')
|
||
|
||
export function apiUrl(path) {
|
||
if (!path.startsWith('/')) path = '/' + path
|
||
return base ? base + path : path
|
||
}
|
||
|
||
export const tokenStore = {
|
||
get: () => localStorage.getItem(TOKEN_KEY) || '',
|
||
set: (t) => {
|
||
localStorage.setItem(TOKEN_KEY, t)
|
||
window.dispatchEvent(new Event(EVT))
|
||
},
|
||
clear: () => {
|
||
localStorage.removeItem(TOKEN_KEY)
|
||
window.dispatchEvent(new Event(EVT))
|
||
},
|
||
subscribe: (fn) => {
|
||
window.addEventListener(EVT, fn)
|
||
return () => window.removeEventListener(EVT, fn)
|
||
},
|
||
}
|
||
|
||
function parseJsonSafe(txt) {
|
||
if (!txt || !txt.trim()) return null
|
||
try {
|
||
return JSON.parse(txt)
|
||
} catch {
|
||
return { error: txt.length > 120 ? txt.slice(0, 120) + '…' : txt }
|
||
}
|
||
}
|
||
|
||
async function request(method, path, body) {
|
||
const headers = { 'Content-Type': 'application/json' }
|
||
const t = tokenStore.get()
|
||
if (t) headers['X-Auth-Token'] = t
|
||
let res
|
||
try {
|
||
res = await fetch(apiUrl(path), {
|
||
method,
|
||
headers,
|
||
body: body ? JSON.stringify(body) : undefined,
|
||
})
|
||
} catch (e) {
|
||
throw new Error(
|
||
e.message?.includes('Failed to fetch')
|
||
? '无法连接后端:请确认已启动 mengpost-backend (默认 8787),且本页通过 Vite 开发服务器打开或已配置 VITE_API_URL'
|
||
: e.message,
|
||
)
|
||
}
|
||
const txt = await res.text()
|
||
const data = parseJsonSafe(txt)
|
||
if (!res.ok) {
|
||
const msg = (data && data.error) || res.statusText || '请求失败'
|
||
throw new Error(msg)
|
||
}
|
||
return data
|
||
}
|
||
|
||
/** 列表接口在空 body / SW 缓存异常时可能得到 null,统一成数组 */
|
||
function asArray(v) {
|
||
return Array.isArray(v) ? v : []
|
||
}
|
||
|
||
/** 校验 token 时不带 X-Auth-Token,避免旧 token 干扰 */
|
||
export async function verifyToken(token) {
|
||
let res
|
||
try {
|
||
res = await fetch(apiUrl('/api/auth/verify'), {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ token }),
|
||
})
|
||
} catch (e) {
|
||
throw new Error(
|
||
e.message?.includes('Failed to fetch')
|
||
? '无法连接后端:请先运行 dev.bat / dev.sh 启动后端,或设置 VITE_API_URL'
|
||
: e.message,
|
||
)
|
||
}
|
||
const txt = await res.text()
|
||
const data = parseJsonSafe(txt)
|
||
if (!res.ok) {
|
||
if (data && data.ok === false) return { ok: false }
|
||
throw new Error((data && data.error) || res.statusText || '验证失败')
|
||
}
|
||
return data
|
||
}
|
||
|
||
export const api = {
|
||
listAccounts: () => request('GET', '/api/accounts').then(asArray),
|
||
createAccount: (a) => request('POST', '/api/accounts', a),
|
||
updateAccount: (id, a) => request('PUT', `/api/accounts/${id}`, a),
|
||
deleteAccount: (id) => request('DELETE', `/api/accounts/${id}`),
|
||
listMailboxes: (id) => request('GET', `/api/accounts/${id}/mailboxes`).then(asArray),
|
||
listMessages: (id, mailbox = 'INBOX', limit = 30) =>
|
||
request('GET', `/api/accounts/${id}/messages?mailbox=${encodeURIComponent(mailbox)}&limit=${limit}`).then(
|
||
asArray,
|
||
),
|
||
getMessage: (id, uid, mailbox = 'INBOX') =>
|
||
request('GET', `/api/accounts/${id}/messages/${uid}?mailbox=${encodeURIComponent(mailbox)}`),
|
||
send: (id, body) => request('POST', `/api/accounts/${id}/send`, body),
|
||
listSent: (id, limit = 50) =>
|
||
request('GET', `/api/accounts/${id}/sent?limit=${limit}`).then(asArray),
|
||
microsoftOAuthStart: () => request('GET', '/api/oauth/microsoft/start'),
|
||
microsoftOAuthFinish: (state) => request('POST', '/api/oauth/microsoft/finish', { state }),
|
||
}
|