Files
lingxi-web/server/src/gateway.js
T

80 lines
2.9 KiB
JavaScript

const base = () => (process.env.TELEGRAM_GATEWAY_URL || 'http://host.docker.internal:3000/api').replace(/\/$/, '')
let gatewayToken = process.env.TELEGRAM_GATEWAY_TOKEN || ''
let refreshPromise
function tokenExpiresSoon(token) {
try {
const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64url').toString('utf8'))
return !payload.exp || payload.exp * 1000 < Date.now() + 5 * 60 * 1000
} catch {
return true
}
}
async function refreshGatewayToken() {
if (refreshPromise) return refreshPromise
refreshPromise = (async () => {
const email = process.env.TELEGRAM_GATEWAY_SERVICE_EMAIL
const password = process.env.TELEGRAM_GATEWAY_SERVICE_PASSWORD
const tenantId = process.env.TELEGRAM_GATEWAY_TENANT_ID
if (!email || !password || !tenantId) {
throw Object.assign(new Error('Telegram 网关服务账号凭据尚未配置'), { status: 503 })
}
const response = await fetch(`${base()}/auth/login`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email, password, tenantId }),
})
const raw = await response.text()
let result
try { result = raw ? JSON.parse(raw) : null } catch { result = raw }
const token = result?.data?.token || result?.token
if (!response.ok || !token) {
throw Object.assign(new Error(result?.message || 'Telegram 网关服务账号续签失败'), { status: response.status || 503 })
}
gatewayToken = token
return token
})().finally(() => { refreshPromise = undefined })
return refreshPromise
}
export async function gatewayRequest(path, options = {}) {
const tenantId = process.env.TELEGRAM_GATEWAY_TENANT_ID
if (!tenantId) throw Object.assign(new Error('Telegram 网关租户尚未配置'), { status: 503 })
if (!gatewayToken || tokenExpiresSoon(gatewayToken)) await refreshGatewayToken()
const send = () => fetch(`${base()}${path}`, {
...options,
headers: {
...(options.body instanceof FormData ? {} : { 'content-type': 'application/json' }),
authorization: `Bearer ${gatewayToken}`,
'x-tenant-id': tenantId,
...(options.headers || {}),
},
})
let response = await send()
if (response.status === 401) {
await refreshGatewayToken()
response = await send()
}
const raw = await response.text()
let result
try { result = raw ? JSON.parse(raw) : null } catch { result = raw }
if (!response.ok) {
throw Object.assign(new Error(result?.message || `Telegram 网关请求失败 (${response.status})`), { status: response.status })
}
return result?.data ?? result
}
export function postGateway(path, body) {
return gatewayRequest(path, { method: 'POST', body: JSON.stringify(body) })
}
export function putGateway(path, body) {
return gatewayRequest(path, { method: 'PUT', body: JSON.stringify(body) })
}
export function uploadGateway(path, formData) {
return gatewayRequest(path, { method: 'POST', body: formData })
}