feat(front): 网关台账前端——台账/路由/目标三页+重载注册表
- 与 notice 同形态:hash 路由 + TablePageAlovaNew 分页 + alova 信封分支 - API 层手写(文档端点仅容器网内可达,api:gen 无通路),键名对齐后端 operationId - 审计修复:outDir 回 dist 根(CI zip 根须含 html)、main.ts 显式引入 @/api 挂载全局 Apis、 保存失败重拉回滚、路由页补只读 timeout/bodyLimit 列、详情弹窗 backendCode 恒空说明
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 网关治理管理面接口定义(手写)。
|
||||
* <p>
|
||||
* 说明:open-gateway 文档端点 {@code /v3/api-docs} 属网关自身路径(RoutingFilter 已白名单旁路),
|
||||
* 但出口仅容器网内可达,本地/CI 无法执行 alova wormhole 的 api:gen,故本文件手写。
|
||||
* 键名 = 后端 {@code @Operation(operationId=...)}(见 InternalAdminController / RouteRefreshController),
|
||||
* 保持与 UserBase/Notice 生成物同形:{@code [method, path]}。
|
||||
*/
|
||||
export default {
|
||||
'GatewayAdmin.page': ['GET', '/api/internal/admin/page'],
|
||||
'GatewayAdmin.routes': ['GET', '/api/internal/admin/routes'],
|
||||
'GatewayAdmin.updateRoute': ['PUT', '/api/internal/admin/routes/{id}'],
|
||||
'GatewayAdmin.targets': ['GET', '/api/internal/admin/targets'],
|
||||
'GatewayAdmin.updateTarget': ['PUT', '/api/internal/admin/targets/{id}'],
|
||||
'GatewayAdmin.logs': ['GET', '/api/internal/admin/logs'],
|
||||
'GatewayAdmin.refresh': ['POST', '/api/internal/routes/refresh'],
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
/// <reference types='./globals.d.ts' />
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* 接口调用代理工厂(与 UserBase/Notice 同形,来源为 alova 生成器的运行时部分)。
|
||||
* 依据 apiDefinitions 的 [method, url] 构造 alova Method;{path} 占位由 pathParams 替换。
|
||||
*/
|
||||
import { Method } from 'alova';
|
||||
import apiDefinitions from './apiDefinitions';
|
||||
|
||||
const cache = Object.create(null);
|
||||
const createFunctionalProxy = (
|
||||
array: (string | symbol)[],
|
||||
alovaInstance: Alova<any, any, any, any, any>,
|
||||
configMap: any
|
||||
) => {
|
||||
const apiPathKey = array.join('.') as keyof typeof apiDefinitions;
|
||||
if (cache[apiPathKey]) {
|
||||
return cache[apiPathKey];
|
||||
}
|
||||
// create a new proxy instance
|
||||
const proxy = new Proxy(function () {}, {
|
||||
get(_, property) {
|
||||
// record the target property, so that it can get the completed accessing paths
|
||||
const newArray = [...array, property];
|
||||
// always return a new proxy to continue recording accessing paths.
|
||||
return createFunctionalProxy(newArray, alovaInstance, configMap);
|
||||
},
|
||||
apply(_, __, [config]) {
|
||||
const apiItem = apiDefinitions[apiPathKey];
|
||||
if (!apiItem) {
|
||||
throw new Error(`the api path of \`${apiPathKey}\` is not found`);
|
||||
}
|
||||
const mergedConfig = {
|
||||
...configMap[apiPathKey],
|
||||
...config
|
||||
};
|
||||
const [method, url] = apiItem;
|
||||
const pathParams = mergedConfig.pathParams;
|
||||
const urlReplaced = url!.replace(/\{([^}]+)\}/g, (_, key) => {
|
||||
const pathParam = pathParams[key];
|
||||
return pathParam;
|
||||
});
|
||||
delete mergedConfig.pathParams;
|
||||
let data = mergedConfig.data;
|
||||
if (Object.prototype.toString.call(data) === '[object Object]' && typeof FormData !== 'undefined') {
|
||||
let hasBlobData = false;
|
||||
const formData = new FormData();
|
||||
for (const key in data) {
|
||||
if (Array.isArray(data[key])) {
|
||||
for (const ele of data[key]) {
|
||||
formData.append(key, ele);
|
||||
if (ele instanceof Blob) {
|
||||
hasBlobData = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
formData.append(key, data[key]);
|
||||
if (data[key] instanceof Blob) {
|
||||
hasBlobData = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
data = hasBlobData ? formData : data;
|
||||
}
|
||||
return new Method(method!.toUpperCase() as MethodType, alovaInstance, urlReplaced, mergedConfig, data);
|
||||
}
|
||||
});
|
||||
cache[apiPathKey] = proxy;
|
||||
return proxy;
|
||||
};
|
||||
|
||||
export const createApis = (alovaInstance: Alova<any, any, any, any, any>, configMap: any) => {
|
||||
const Apis = new Proxy({} as Apis, {
|
||||
get(_, property) {
|
||||
return createFunctionalProxy([property], alovaInstance, configMap);
|
||||
}
|
||||
});
|
||||
return Apis;
|
||||
};
|
||||
|
||||
export const mountApis = (Apis: Apis) => {
|
||||
// define global variable `Apis`
|
||||
(globalThis as any).Apis = Apis;
|
||||
};
|
||||
|
||||
export const withConfigType = <Config extends MethodsConfigMap>(config: Config) => config;
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
/* 手写精简版(替代 alova wormhole 生成物):只声明本工程 Apis 代理机制所需的最小类型。
|
||||
网关无 api:gen 通路(文档端点仅容器网内可达),故不生成全量接口类型。
|
||||
所有类型必须挂在 declare global 下——createApis.ts 是裸引用(不 import)。 */
|
||||
declare global {
|
||||
type MethodType = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
|
||||
|
||||
/** 调用配置(params/pathParams/data/cacheFor 等,宽松即可) */
|
||||
interface Alova2MethodConfig {
|
||||
params?: Record<string, any>;
|
||||
pathParams?: Record<string, any>;
|
||||
data?: any;
|
||||
headers?: Record<string, any>;
|
||||
cacheFor?: any;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/** alova 实例占位(仅作为 createApis 形参,不参与类型推导) */
|
||||
type Alova<T1 = any, T2 = any, T3 = any, T4 = any, T5 = any> = any;
|
||||
|
||||
/** Api 方法返回形态(实际为 alova Method 实例) */
|
||||
type ApiMethod = any;
|
||||
|
||||
/** 按命名空间分组的接口代理(如 Apis.GatewayAdmin.logs(...)) */
|
||||
interface Apis {
|
||||
[namespace: string]: {
|
||||
[method: string]: (config?: Alova2MethodConfig) => ApiMethod;
|
||||
};
|
||||
}
|
||||
|
||||
interface MethodsConfigMap {
|
||||
[key: string]: Alova2MethodConfig;
|
||||
}
|
||||
|
||||
const Apis: Apis;
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createApis, withConfigType, mountApis } from "./createApis";
|
||||
|
||||
import { alovaInstance } from "@/utils/alova/alova-iview";
|
||||
|
||||
export const $$userConfigMap = withConfigType({});
|
||||
|
||||
const Apis = createApis(alovaInstance, $$userConfigMap);
|
||||
|
||||
mountApis(Apis);
|
||||
|
||||
export default Apis;
|
||||
Reference in New Issue
Block a user