Files
one-pipe-system/scripts/tests/gatewayBrowserFixtures.mjs
luo 049f8257fb
All checks were successful
构建并部署前端到测试环境 / deploy (push) Successful in 56s
feat: 新增网关限流
2026-10-24 16:22:28 +08:00

246 lines
8.0 KiB
JavaScript

// 本地浏览器验收专用;所有 /api 请求在这里终止,不会代理到真实后端。
const time = '2026-10-24T10:00:00+08:00'
const option = (value, label) => ({ value, label })
const options = {
error_categories: [
option('rate_limited_upstream', '上游频率限制'),
option('business', '业务错误')
],
exempt_sources: [option('manual', '人工')],
gateway_operations: [
option('query_flow', '查流量'),
option('query_status', '查状态'),
option('query_realname', '查实名'),
option('stop', '停机'),
option('start', '复机')
],
match_types: [option('contains', '包含'), option('prefix', '前缀'), option('regex', '正则')],
rate_limit_modes: [option('group', '限流组')],
stop_resume_actions: [option('stop', '停机'), option('start', '复机')],
stop_resume_results: [
option('success', '成功'),
option('failed', '失败'),
option('unknown', '未知'),
option('already_target', '已是目标状态'),
option('skipped', '已跳过')
]
}
const rules = [
{
id: 1,
category: 'rate_limited_upstream',
category_name: '上游频率限制',
keyword: '请求过于频繁',
match_type: 'contains',
operations: ['stop'],
priority: 10,
maybe_accepted: false,
status: 1,
remark: '本地模拟规则',
created_at: time,
updated_at: time
}
]
const groups = [
{
id: 1,
carrier_id: null,
carrier_name: '',
group_code: 'default',
group_name: '全局默认组',
operations: ['query_flow', 'stop', 'start'],
max_qps: 20,
reserved_qps: 5,
high_priority_operations: ['stop', 'start'],
status: 1,
status_name: '启用',
created_at: time,
updated_at: time
}
]
const records = [
{
id: 1,
card_id: 42,
carrier_id: 8,
iccid: 'MOCK-ICCID-42',
action: 'stop',
action_name: '停机',
result: 'success',
error_category: '',
error_category_name: '',
provider_message: '操作成功',
provider_code: '0',
attempt: 1,
command_id: 'MOCK-CMD',
source: 'manual',
scene: 'manual_stop',
reason: '本地验收',
duration_ms: 42,
created_at: time
}
]
const page = (items, url) => ({
items,
total: items.length,
page: Number(url.searchParams.get('page') || 1),
size: Number(url.searchParams.get('page_size') || 20)
})
export const mockApiPlugin = {
name: 'gateway-local-mock-api',
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
const url = new URL(req.url, 'http://127.0.0.1')
const path = url.pathname
if (!path.startsWith('/api/')) return next()
let body = ''
for await (const chunk of req) body += chunk
const input = body ? JSON.parse(body) : {}
let data = page([], url)
let code = 0
let msg = 'local mock only'
if (path === '/api/auth/me' || path === '/api/auth/login') {
const viewer = req.headers.authorization?.includes('local-gateway-viewer')
const pagePermissions = [
'card_stop_resume_record:page',
'gateway_error_classification:page',
'gateway_rate_limit_group:page',
'gateway_rate_limit_group:detail_page',
'gateway_call_stats:page'
]
const menuEntries = [
['停复机记录', 'stop-resume-records', pagePermissions[0]],
['上游错误分类', 'error-classifications', pagePermissions[1]],
['网关限流组', 'rate-limit-groups', pagePermissions[2]],
['网关限流组详情', 'rate-limit-groups/detail/:id', pagePermissions[3]],
['网关调用统计', 'gateway-stats', pagePermissions[4]]
]
data = {
access_token: viewer ? 'local-gateway-viewer' : 'local-gateway-mock-only',
refresh_token: '',
expires_in: 3600,
user: {
id: viewer ? 2 : 1,
user_type: viewer ? 2 : 1,
username: viewer ? '本地只读账号' : '本地模拟管理员',
roles: [viewer ? 'R_ADMIN' : 'R_SUPER']
},
permissions: viewer ? pagePermissions : [],
buttons: viewer
? [
'gateway_error_classification:test',
'gateway_rate_limit_group:detail',
'gateway_call_stats:trend'
]
: [],
menus: viewer
? [
{
url: '/polling-management',
name: '轮询管理',
children: menuEntries.map(([name, segment, perm_code]) => ({
name,
url: `/polling-management/${segment}`,
perm_code
}))
}
]
: []
}
} else if (path === '/api/admin/polling-options') data = options
else if (path === '/api/admin/carriers')
data = page(
[
{
id: 8,
carrier_name: '模拟运营商账户',
carrier_type: 'CMCC',
status: 1,
created_at: time,
updated_at: time
}
].filter((carrier) =>
carrier.carrier_name.includes(url.searchParams.get('carrier_name') || '')
),
url
)
else if (
path === '/api/admin/card-stop-resume-records' ||
/\/iot-cards\/\d+\/stop-resume-records$/.test(path)
)
data = page(records, url)
else if (path === '/api/admin/gateway-error-classifications/test') {
data = {
matched: input.message?.includes(input.draft?.keyword || '请求过于频繁') || false,
rule_id: input.draft ? 0 : 1,
category: input.draft?.category || rules[0].category,
category_name: '上游频率限制',
keyword: input.draft?.keyword || rules[0].keyword,
maybe_accepted: false
}
} else if (
/^\/api\/admin\/(gateway-error-classifications|polling-rate-limit-groups)(\/\d+)?(\/status)?$/.test(
path
)
) {
const list = path.includes('gateway-error-classifications') ? rules : groups
const id = Number(path.split('/')[4])
const row = list.find((item) => item.id === id)
if (req.method === 'GET') data = id ? row : page(list, url)
else if (req.method === 'POST') {
data = {
...input,
id: Math.max(...list.map((item) => item.id), 0) + 1,
created_at: time,
updated_at: time
}
list.push(data)
} else if (req.method === 'PUT' && row) {
Object.assign(row, input)
if ('group_name' in row) row.status_name = row.status === 1 ? '启用' : '停用'
data = row
} else if (req.method === 'DELETE' && row) {
list.splice(list.indexOf(row), 1)
data = {}
}
} else if (path === '/api/admin/polling-stats/carriers') {
data = {
minutes: Number(url.searchParams.get('minutes') || 15),
mode: '本地模拟模式',
carriers: [
{
carrier_id: 8,
carrier_name: '模拟运营商',
example_calls: 100,
example_upstream_failures: 2
}
]
}
} else if (/\/polling-stats\/carriers\/\d+\/trend$/.test(path)) {
data = {
carrier_id: Number(path.split('/')[5]),
points: [{ example_time: time, example_calls: 10 }]
}
} else if (path.startsWith('/api/admin/assets/resolve/')) {
data = {
asset_type: 'card',
asset_id: 42,
identifier: 'MOCK-ICCID-42',
iccid: 'MOCK-ICCID-42',
status: 1,
network_status: 1,
real_name_status: 1,
carrier_name: '模拟运营商',
cards: []
}
} else if (req.method !== 'GET') {
code = 400
msg = '此本地验收接口未定义,已拦截写请求'
}
res.setHeader('Content-Type', 'application/json; charset=utf-8')
res.end(JSON.stringify({ code, msg, data }))
})
}
}