246 lines
8.0 KiB
JavaScript
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 }))
|
|
})
|
|
}
|
|
}
|