Files
sub2api-add/plugins/subscription-admin/test/mock-core.mjs
T
Qiufeng ada4ab3c21
Business Plugins CI / check (plugin-admin) (push) Successful in 1m42s
Business Plugins CI / check (subscription-admin) (push) Successful in 1m30s
feat: complete unified plugin admin v1.1.0
2026-08-30 12:10:04 +08:00

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`)
})