90 lines
5.6 KiB
JavaScript
90 lines
5.6 KiB
JavaScript
import http from 'node:http'
|
|
|
|
const port = Number(process.env.MOCK_CORE_PORT || 18080)
|
|
const token = process.env.MOCK_ACCESS_TOKEN || 'MOCK-ACCESS'
|
|
const refresh = process.env.MOCK_REFRESH_TOKEN || 'MOCK-REFRESH'
|
|
const captchaProvider = String(process.env.MOCK_CAPTCHA_PROVIDER || '').toLowerCase()
|
|
const users = new Map([
|
|
['admin@example.com', { id: 1, role: 'admin', email: 'admin@example.com', username: 'admin' }],
|
|
['user@example.com', { id: 2, role: 'user', email: 'user@example.com', username: 'user' }]
|
|
])
|
|
|
|
function json(res, status, body) {
|
|
res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' })
|
|
res.end(JSON.stringify(body))
|
|
}
|
|
|
|
async function body(req) {
|
|
const chunks = []
|
|
for await (const chunk of req) chunks.push(chunk)
|
|
return chunks.length ? JSON.parse(Buffer.concat(chunks).toString('utf8')) : {}
|
|
}
|
|
|
|
function authorized(req) {
|
|
return req.headers.authorization === `Bearer ${token}` || req.headers.authorization === `Bearer NEW-MOCK-ACCESS`
|
|
}
|
|
|
|
function subscriptions(page = 1, pageSize = 50) {
|
|
const all = Array.from({ length: 57 }, (_, index) => ({
|
|
id: index + 1,
|
|
user_id: 1,
|
|
plan_id: 10 + (index % 2),
|
|
plan_name: index % 2 ? '专业版' : '基础版',
|
|
status: 'active',
|
|
starts_at: '2026-08-01T00:00:00Z',
|
|
expires_at: '2026-09-01T00:00:00Z',
|
|
cycle_usage_usd: index / 100,
|
|
cycle_quota_usd: 100,
|
|
user: { id: 1, username: 'admin', email: 'admin@example.com', balance: 123.45 }
|
|
}))
|
|
const start = (page - 1) * pageSize
|
|
return { items: all.slice(start, start + pageSize), total: all.length, page, page_size: pageSize, pages: Math.ceil(all.length / pageSize) }
|
|
}
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
const url = new URL(req.url, `http://${req.headers.host}`)
|
|
const path = url.pathname
|
|
if (req.method === 'POST' && path === '/api/v1/auth/login') {
|
|
const input = await body(req)
|
|
const user = users.get(input.email)
|
|
if (!user || input.password !== 'password') return json(res, 401, { code: 401, message: 'invalid credentials' })
|
|
if (captchaProvider === 'turnstile' && input.turnstile_token !== 'MOCK-TURNSTILE-TOKEN') return json(res, 400, { code: 400, message: 'captcha required' })
|
|
if (captchaProvider === 'tencent' && (!input.tencent_captcha_ticket || !input.tencent_captcha_randstr)) return json(res, 400, { code: 400, message: 'captcha required' })
|
|
if (captchaProvider === 'aliyun' && !input.turnstile_token) return json(res, 400, { code: 400, message: 'captcha required' })
|
|
return json(res, 200, { code: 0, message: 'success', data: { access_token: token, refresh_token: refresh, user } })
|
|
}
|
|
if (req.method === 'POST' && path === '/api/v1/auth/refresh') {
|
|
const input = await body(req)
|
|
if (input.refresh_token !== refresh && input.refresh_token !== 'NEW-MOCK-REFRESH') return json(res, 401, { code: 401, message: 'expired' })
|
|
return json(res, 200, { code: 0, message: 'success', data: { access_token: 'NEW-MOCK-ACCESS', refresh_token: 'NEW-MOCK-REFRESH' } })
|
|
}
|
|
if (req.method === 'POST' && path === '/api/v1/auth/logout') return json(res, 200, { code: 0, message: 'success', data: {} })
|
|
if (req.method === 'GET' && path === '/api/v1/settings/public') return json(res, 200, { code: 0, message: 'success', data: {
|
|
turnstile_enabled: captchaProvider === 'turnstile', turnstile_site_key: captchaProvider === 'turnstile' ? 'mock-site-key' : '',
|
|
geetest_captcha_enabled: false,
|
|
tencent_captcha_enabled: captchaProvider === 'tencent', tencent_captcha_app_id: captchaProvider === 'tencent' ? '123456' : '', tencent_captcha_region: 'cn',
|
|
aliyun_captcha_enabled: captchaProvider === 'aliyun', aliyun_captcha_scene_id: captchaProvider === 'aliyun' ? 'mock-scene' : '', aliyun_captcha_prefix: captchaProvider === 'aliyun' ? 'mock-prefix' : '', aliyun_captcha_region: 'cn'
|
|
} })
|
|
if (!authorized(req)) return json(res, 401, { code: 401, message: 'unauthorized' })
|
|
if (req.method === 'GET' && path === '/api/v1/auth/me') return json(res, 200, { code: 0, message: 'success', data: users.get('admin@example.com') })
|
|
if (req.method === 'GET' && path === '/api/v1/admin/payment/plans') {
|
|
return json(res, 200, { code: 0, message: 'success', data: [{ id: 10, name: '基础版', price: 9.9, currency: 'USD', validity_days: 30, validity_unit: 'day', for_sale: true, included_groups: [] }, { id: 11, name: '专业版', price: 19.9, currency: 'USD', validity_days: 30, validity_unit: 'day', for_sale: true, included_groups: [] }] })
|
|
}
|
|
if (req.method === 'GET' && path === '/api/v1/admin/subscriptions') {
|
|
const page = Number(url.searchParams.get('page') || 1)
|
|
const pageSize = Number(url.searchParams.get('page_size') || 50)
|
|
return json(res, 200, { code: 0, message: 'success', data: subscriptions(page, pageSize) })
|
|
}
|
|
const subscriptionMatch = path.match(/^\/api\/v1\/admin\/subscriptions\/(\d+)$/)
|
|
if (req.method === 'GET' && subscriptionMatch) return json(res, 200, { code: 0, message: 'success', data: subscriptions(1, 1).items[0] })
|
|
const userSubscriptionsMatch = path.match(/^\/api\/v1\/admin\/users\/(\d+)\/subscriptions$/)
|
|
if (req.method === 'GET' && userSubscriptionsMatch) return json(res, 200, { code: 0, message: 'success', data: subscriptions(1, 2).items })
|
|
const userMatch = path.match(/^\/api\/v1\/admin\/users\/(\d+)$/)
|
|
if (req.method === 'GET' && userMatch) return json(res, 200, { code: 0, message: 'success', data: { id: Number(userMatch[1]), username: 'admin', email: 'admin@example.com', balance: 123.45, frozen_balance: 0 } })
|
|
return json(res, 404, { code: 404, message: 'not found' })
|
|
})
|
|
|
|
server.listen(port, '127.0.0.1', () => {
|
|
process.stdout.write(`mock core listening on 127.0.0.1:${port}\n`)
|
|
})
|