455 lines
12 KiB
TypeScript
455 lines
12 KiB
TypeScript
import type {
|
|
InterfaceChangeLogItem,
|
|
InterfaceDocInfo,
|
|
InterfaceExportResponse,
|
|
InterfaceFormValues,
|
|
InterfaceGatewayFormValues,
|
|
InterfaceGatewayPolicy,
|
|
InterfaceIntegrationInfo,
|
|
InterfaceItem,
|
|
InterfaceListParams,
|
|
InterfaceMockInfo,
|
|
InterfaceRoutePreview,
|
|
} from '@/pages/InterfaceManagement/data';
|
|
import type { Request, Response } from 'express';
|
|
import type { ParamsDictionary } from 'express-serve-static-core';
|
|
|
|
type InterfaceListQuery = Omit<InterfaceListParams, 'current' | 'pageSize'> & {
|
|
current?: string;
|
|
pageSize?: string;
|
|
};
|
|
|
|
const interfaceList: InterfaceItem[] = [
|
|
{
|
|
id: 'api-1001',
|
|
name: '用户列表查询',
|
|
method: 'GET',
|
|
path: '/api/users',
|
|
owner: '李娜',
|
|
status: 'active',
|
|
version: 'v1',
|
|
description: '分页获取用户列表',
|
|
createdAt: '2024-10-01 09:30:00',
|
|
updatedAt: '2024-10-10 18:20:00',
|
|
},
|
|
{
|
|
id: 'api-1002',
|
|
name: '新增订单',
|
|
method: 'POST',
|
|
path: '/api/orders',
|
|
owner: '王彬',
|
|
status: 'active',
|
|
version: 'v2',
|
|
description: '创建新的订单记录',
|
|
createdAt: '2024-09-18 13:10:00',
|
|
updatedAt: '2024-10-05 11:05:00',
|
|
},
|
|
{
|
|
id: 'api-1003',
|
|
name: '库存调整',
|
|
method: 'PUT',
|
|
path: '/api/inventory/adjust',
|
|
owner: '赵敏',
|
|
status: 'deprecated',
|
|
version: 'v1',
|
|
description: '旧库存调整接口,计划下线',
|
|
createdAt: '2024-08-02 08:45:00',
|
|
updatedAt: '2024-09-21 16:40:00',
|
|
},
|
|
{
|
|
id: 'api-1004',
|
|
name: '删除文章',
|
|
method: 'DELETE',
|
|
path: '/api/articles/:id',
|
|
owner: '陈曦',
|
|
status: 'disabled',
|
|
version: 'v1',
|
|
description: '文章删除功能已暂时关闭',
|
|
createdAt: '2024-07-12 10:20:00',
|
|
updatedAt: '2024-08-20 19:10:00',
|
|
},
|
|
];
|
|
|
|
const interfaceDocMap: Record<string, InterfaceDocInfo> = {
|
|
'api-1001': {
|
|
docUrl: 'https://docs.example.com/interfaces/api-1001',
|
|
exampleText:
|
|
'curl -X GET https://api.example.com/api/users?page=1&pageSize=10',
|
|
updatedAt: '2024-10-10 18:20:00',
|
|
owner: '李娜',
|
|
},
|
|
'api-1002': {
|
|
docUrl: 'https://docs.example.com/interfaces/api-1002',
|
|
exampleText: 'curl -X POST https://api.example.com/api/orders -d "{}"',
|
|
updatedAt: '2024-10-05 11:05:00',
|
|
owner: '王彬',
|
|
},
|
|
};
|
|
|
|
const gatewayPolicyMap: Record<string, InterfaceGatewayPolicy> = {
|
|
'api-1001': {
|
|
routeId: 'route-api-1001',
|
|
rateLimit: '2000 次/分钟',
|
|
authType: 'OAuth2 + 签名校验',
|
|
timeoutMs: 3000,
|
|
},
|
|
'api-1002': {
|
|
routeId: 'route-api-1002',
|
|
rateLimit: '1200 次/分钟',
|
|
authType: 'API Key',
|
|
timeoutMs: 5000,
|
|
},
|
|
};
|
|
|
|
const mockInfoMap: Record<string, InterfaceMockInfo> = {
|
|
'api-1001': {
|
|
requestSample:
|
|
'curl -X GET https://api.example.com/api/users?page=1&pageSize=10',
|
|
responseSample: '{"success":true,"data":[{"id":"1"}],"total":1}',
|
|
latency: '120ms',
|
|
mockEnabled: true,
|
|
mockUrl: 'https://mock.example.com/interfaces/api-1001',
|
|
},
|
|
'api-1002': {
|
|
requestSample: 'curl -X POST https://api.example.com/api/orders -d "{}"',
|
|
responseSample: '{"success":true,"data":{"id":"100"}}',
|
|
latency: '210ms',
|
|
mockEnabled: false,
|
|
mockUrl: 'https://mock.example.com/interfaces/api-1002',
|
|
},
|
|
};
|
|
|
|
const integrationInfoMap: Record<string, InterfaceIntegrationInfo> = {
|
|
'api-1001': {
|
|
partner: '物流平台 A',
|
|
channel: 'API Key + IP 白名单',
|
|
status: '已接入',
|
|
consoleUrl: 'https://console.example.com/integrations/api-1001',
|
|
},
|
|
'api-1002': {
|
|
partner: '支付平台 B',
|
|
channel: 'JWT + 双向 TLS',
|
|
status: '对接中',
|
|
consoleUrl: 'https://console.example.com/integrations/api-1002',
|
|
},
|
|
};
|
|
|
|
const changeLogMap: Record<string, InterfaceChangeLogItem[]> = {
|
|
'api-1001': [
|
|
{
|
|
id: 'log-1',
|
|
time: '2024-10-10 18:20',
|
|
description: '调整响应字段,新增可选字段 note',
|
|
operator: '李娜',
|
|
},
|
|
{
|
|
id: 'log-2',
|
|
time: '2024-09-18 13:10',
|
|
description: '接口首次发布',
|
|
operator: '王彬',
|
|
},
|
|
],
|
|
'api-1002': [
|
|
{
|
|
id: 'log-3',
|
|
time: '2024-10-05 11:05',
|
|
description: '补充校验规则与异常码',
|
|
operator: '王彬',
|
|
},
|
|
],
|
|
};
|
|
|
|
const parseNumber = (value: string | undefined, fallback: number) => {
|
|
const parsed = Number(value);
|
|
return Number.isNaN(parsed) ? fallback : parsed;
|
|
};
|
|
|
|
const getInterfaceItem = (id: string) =>
|
|
interfaceList.find((item) => item.id === id);
|
|
|
|
const ensureGatewayPolicy = (id: string): InterfaceGatewayPolicy => {
|
|
if (!gatewayPolicyMap[id]) {
|
|
gatewayPolicyMap[id] = {
|
|
routeId: `route-${id}`,
|
|
rateLimit: '2000 次/分钟',
|
|
authType: 'OAuth2 + 签名校验',
|
|
timeoutMs: 3000,
|
|
};
|
|
}
|
|
return gatewayPolicyMap[id];
|
|
};
|
|
|
|
const ensureMockInfo = (id: string): InterfaceMockInfo => {
|
|
if (!mockInfoMap[id]) {
|
|
const item = getInterfaceItem(id);
|
|
mockInfoMap[id] = {
|
|
requestSample: `curl -X ${item?.method ?? 'GET'} https://api.example.com${
|
|
item?.path ?? '/api/example'
|
|
}`,
|
|
responseSample: '{"success":true}',
|
|
latency: '120ms',
|
|
mockEnabled: false,
|
|
mockUrl: `https://mock.example.com/interfaces/${id}`,
|
|
};
|
|
}
|
|
return mockInfoMap[id];
|
|
};
|
|
|
|
export default {
|
|
'GET /api/interfaces': (
|
|
req: Request<ParamsDictionary, unknown, unknown, InterfaceListQuery>,
|
|
res: Response,
|
|
) => {
|
|
const { current, pageSize, name, method, path, owner, status, version } =
|
|
req.query;
|
|
const currentPage = parseNumber(current, 1);
|
|
const size = parseNumber(pageSize, 10);
|
|
|
|
let dataSource = [...interfaceList];
|
|
|
|
if (name) {
|
|
dataSource = dataSource.filter((item) => item.name.includes(name));
|
|
}
|
|
|
|
if (method) {
|
|
dataSource = dataSource.filter((item) => item.method === method);
|
|
}
|
|
|
|
if (path) {
|
|
dataSource = dataSource.filter((item) => item.path.includes(path));
|
|
}
|
|
|
|
if (owner) {
|
|
dataSource = dataSource.filter((item) => item.owner.includes(owner));
|
|
}
|
|
|
|
if (status) {
|
|
dataSource = dataSource.filter((item) => item.status === status);
|
|
}
|
|
|
|
if (version) {
|
|
dataSource = dataSource.filter((item) => item.version.includes(version));
|
|
}
|
|
|
|
const start = (currentPage - 1) * size;
|
|
const end = start + size;
|
|
|
|
res.json({
|
|
success: true,
|
|
data: dataSource.slice(start, end),
|
|
total: dataSource.length,
|
|
});
|
|
},
|
|
|
|
'POST /api/interfaces': (
|
|
req: Request<ParamsDictionary, InterfaceItem, InterfaceFormValues>,
|
|
res: Response,
|
|
) => {
|
|
const now = new Date().toISOString();
|
|
const newItem: InterfaceItem = {
|
|
id: `api-${Date.now()}`,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
...req.body,
|
|
};
|
|
interfaceList.unshift(newItem);
|
|
res.json(newItem);
|
|
},
|
|
|
|
'PUT /api/interfaces/:id': (
|
|
req: Request<{ id: string }, InterfaceItem, InterfaceFormValues>,
|
|
res: Response,
|
|
) => {
|
|
const { id } = req.params;
|
|
const index = interfaceList.findIndex((item) => item.id === id);
|
|
if (index >= 0) {
|
|
interfaceList[index] = {
|
|
...interfaceList[index],
|
|
...req.body,
|
|
updatedAt: new Date().toISOString(),
|
|
};
|
|
res.json(interfaceList[index]);
|
|
return;
|
|
}
|
|
|
|
res.status(404).json({
|
|
success: false,
|
|
data: interfaceList,
|
|
total: interfaceList.length,
|
|
});
|
|
},
|
|
|
|
'DELETE /api/interfaces/:id': (
|
|
req: Request<{ id: string }>,
|
|
res: Response,
|
|
) => {
|
|
const { id } = req.params;
|
|
const index = interfaceList.findIndex((item) => item.id === id);
|
|
if (index >= 0) {
|
|
interfaceList.splice(index, 1);
|
|
res.json({ success: true });
|
|
return;
|
|
}
|
|
res.status(404).json({ success: false });
|
|
},
|
|
|
|
'GET /api/interfaces/:id/doc': (
|
|
req: Request<{ id: string }>,
|
|
res: Response<InterfaceDocInfo>,
|
|
) => {
|
|
const { id } = req.params;
|
|
const item = getInterfaceItem(id);
|
|
if (!item) {
|
|
res.status(404).end();
|
|
return;
|
|
}
|
|
const docInfo: InterfaceDocInfo = interfaceDocMap[id] ?? {
|
|
docUrl: `https://docs.example.com/interfaces/${id}`,
|
|
exampleText: `curl -X ${item.method} https://api.example.com${item.path}`,
|
|
updatedAt: item.updatedAt,
|
|
owner: item.owner,
|
|
};
|
|
interfaceDocMap[id] = docInfo;
|
|
res.json(docInfo);
|
|
},
|
|
|
|
'GET /api/interfaces/:id/gateway': (
|
|
req: Request<{ id: string }>,
|
|
res: Response<InterfaceGatewayPolicy>,
|
|
) => {
|
|
const { id } = req.params;
|
|
res.json(ensureGatewayPolicy(id));
|
|
},
|
|
|
|
'PUT /api/interfaces/:id/gateway': (
|
|
req: Request<
|
|
{ id: string },
|
|
InterfaceGatewayPolicy,
|
|
InterfaceGatewayFormValues
|
|
>,
|
|
res: Response<InterfaceGatewayPolicy>,
|
|
) => {
|
|
const { id } = req.params;
|
|
const currentPolicy = ensureGatewayPolicy(id);
|
|
const nextPolicy: InterfaceGatewayPolicy = {
|
|
...currentPolicy,
|
|
rateLimit: req.body.rateLimit,
|
|
authType: req.body.authType,
|
|
timeoutMs: req.body.timeoutMs,
|
|
};
|
|
gatewayPolicyMap[id] = nextPolicy;
|
|
res.json(nextPolicy);
|
|
},
|
|
|
|
'GET /api/interfaces/:id/gateway/preview': (
|
|
req: Request<{ id: string }>,
|
|
res: Response<InterfaceRoutePreview>,
|
|
) => {
|
|
const { id } = req.params;
|
|
const item = getInterfaceItem(id);
|
|
if (!item) {
|
|
res.status(404).end();
|
|
return;
|
|
}
|
|
const policy = ensureGatewayPolicy(id);
|
|
const preview: InterfaceRoutePreview = {
|
|
routeId: policy.routeId,
|
|
upstream: 'https://gateway.example.com',
|
|
path: item.path,
|
|
method: item.method,
|
|
timeoutMs: policy.timeoutMs,
|
|
rateLimit: policy.rateLimit,
|
|
authType: policy.authType,
|
|
};
|
|
res.json(preview);
|
|
},
|
|
|
|
'GET /api/interfaces/:id/mock': (
|
|
req: Request<{ id: string }>,
|
|
res: Response<InterfaceMockInfo>,
|
|
) => {
|
|
const { id } = req.params;
|
|
res.json(ensureMockInfo(id));
|
|
},
|
|
|
|
'POST /api/interfaces/:id/debug': (
|
|
req: Request<{ id: string }>,
|
|
res: Response<{ success: boolean; message: string }>,
|
|
) => {
|
|
const { id } = req.params;
|
|
const mockInfo = ensureMockInfo(id);
|
|
mockInfo.mockEnabled = true;
|
|
mockInfo.latency = '98ms';
|
|
res.json({ success: true, message: 'debug-started' });
|
|
},
|
|
|
|
'GET /api/interfaces/:id/integrations': (
|
|
req: Request<{ id: string }>,
|
|
res: Response<InterfaceIntegrationInfo>,
|
|
) => {
|
|
const { id } = req.params;
|
|
const info =
|
|
integrationInfoMap[id] ??
|
|
({
|
|
partner: '待配置',
|
|
channel: '未配置',
|
|
status: '未接入',
|
|
consoleUrl: 'https://console.example.com/integrations',
|
|
} as InterfaceIntegrationInfo);
|
|
integrationInfoMap[id] = info;
|
|
res.json(info);
|
|
},
|
|
|
|
'POST /api/interfaces/:id/integrations': (
|
|
req: Request<
|
|
{ id: string },
|
|
InterfaceIntegrationInfo,
|
|
Pick<InterfaceIntegrationInfo, 'partner' | 'channel'>
|
|
>,
|
|
res: Response<InterfaceIntegrationInfo>,
|
|
) => {
|
|
const { id } = req.params;
|
|
const info: InterfaceIntegrationInfo = {
|
|
partner: req.body.partner,
|
|
channel: req.body.channel,
|
|
status: '已接入',
|
|
consoleUrl: `https://console.example.com/integrations/${id}`,
|
|
};
|
|
integrationInfoMap[id] = info;
|
|
res.json(info);
|
|
},
|
|
|
|
'POST /api/interfaces/:id/integrations/key': (
|
|
req: Request<{ id: string }>,
|
|
res: Response<InterfaceIntegrationInfo>,
|
|
) => {
|
|
const { id } = req.params;
|
|
const info = integrationInfoMap[id] ?? {
|
|
partner: '待配置',
|
|
channel: '未配置',
|
|
status: '未接入',
|
|
consoleUrl: `https://console.example.com/integrations/${id}`,
|
|
};
|
|
res.json(info);
|
|
},
|
|
|
|
'GET /api/interfaces/:id/changelog': (
|
|
req: Request<{ id: string }>,
|
|
res: Response<InterfaceChangeLogItem[]>,
|
|
) => {
|
|
const { id } = req.params;
|
|
res.json(changeLogMap[id] ?? []);
|
|
},
|
|
|
|
'GET /api/interfaces/:id/changelog/export': (
|
|
req: Request<{ id: string }>,
|
|
res: Response<InterfaceExportResponse>,
|
|
) => {
|
|
const { id } = req.params;
|
|
res.json({
|
|
downloadUrl: `https://download.example.com/interfaces/${id}/changelog.csv`,
|
|
fileName: `interface-${id}-changelog.csv`,
|
|
});
|
|
},
|
|
};
|