ts-api-core
Version:
Nodejs api framework core
608 lines (588 loc) • 27 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var RouteDocController_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RouteDocController = void 0;
const app_1 = require("../base/app");
const express_decorator_1 = require("../mvc/express.decorator");
const reqest_data_1 = require("../base/reqest.data");
const base_controller_1 = require("./base.controller");
const ioc_decorator_1 = require("../mvc/ioc.decorator");
/**
* 接口文档服务
*/
let RouteDocController = RouteDocController_1 = class RouteDocController extends base_controller_1.BaseController {
getRoute(path) {
if (!RouteDocController_1.routeMap) {
const routeMap = new Map();
RouteDocController_1.routeMap = routeMap;
const routes = this.getRoutes();
const server = this.context.server;
routes.forEach((item) => {
const baseUrl = this.getBaseUrl(item, server);
routeMap.set(baseUrl + item.path, item);
});
}
return path ? RouteDocController_1.routeMap.get(path) : undefined;
}
getBaseUrl(route, server) {
const v = server !== null && server !== void 0 ? server : this.context.server;
return v ? v.registerRoutes.getBaseUrl(route) : '';
}
get pageTitle() {
var _a, _b;
const name = (_b = (_a = this.context.server) === null || _a === void 0 ? void 0 : _a.options) === null || _b === void 0 ? void 0 : _b.name;
return `${app_1.App.serviceName}${name ? ' - ' + name : ''} 接口文档`;
}
config(resp, path) {
return __awaiter(this, void 0, void 0, function* () {
const data = this.getRoute(path);
if (data) {
return data;
}
else {
resp.status(404).send(undefined);
}
});
}
docCssStyle(resp) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
let result = '';
if (this.docStyleHandler !== undefined) {
console.log('xxx');
result = this.docStyleHandler();
}
else {
const defaultConfig = {
themeColor: '#7ba7ab',
tocBgColor: 'rgb(250, 250, 250)',
tocWidth: '350px',
inputWidth: '220px',
customCss: '',
};
const cfg = this.docStyleConfig ? Object.assign(defaultConfig, this.docStyleConfig()) : defaultConfig;
result = `.toc { position: fixed; top: 0px; bottom: 0px; left: 0px; width: ${cfg.tocWidth}; height: 100%; overflow-y: auto; overflow-x: hidden; background: ${cfg.tocBgColor}; padding-top: 8px; }
.toc a { font-weight: normal; line-height: 17px; font-size: 14px; padding: 5px 15px !important; background: none; color: rgb(54, 65, 73); display: block;}
.toc .sel { color: #fff; background: ${cfg.themeColor} !important; border-radius: 5px; margin-right: 12px; }
.toc .bg { position: fixed; top: 0px; height: 106px; width: 331px; background: ${cfg.tocBgColor}; }
.toc .ul { padding: 0px 0px 4px 20px !important; margin: 0px !important; display: block; font-weight: 600; line-height: 30px; }
.toc .search { position: fixed; top: 36px; margin: 16px; width: ${cfg.tocWidth} }
.toc h1 { position: fixed; top: 8px;}
input { width: ${cfg.inputWidth}; height: 36px; padding-left: 13px; border: 2px solid ${cfg.themeColor}; border-radius: 5px 0 0 5px; background: #fff; color: #111; outline: none;}
button { height: 40px; width: 36px; cursor: pointer; position: absolute; border: none; border-radius: 0 5px 5px 0; background: ${cfg.themeColor}; outline: none; }
button:before { content: "搜"; color: #f9f0da; font-size: 16px; }
.md-body {margin-left: 24px; margin-right: 16px; margin-top: 4px; margin-bottom: 32px; padding-left: ${cfg.tocWidth} !important;}
.ul-title {font-size: 18px; line-height: 1.7; font-weight: 600;}
.toc-range {padding-top: 110px; padding-bottom: 24px;}
.ml16 {margin-left: 16px;}
.ms0 {margin:0 0px 0 0;}
.ms1 {margin:0 18px 0 0;}
.ms2 {margin:0 18px 0 0; padding: 0 18px 0 0; border-right: solid #ccc 1px;}
.ms3 {margin:0 18px 0 0; padding: 0 36px 0 0; border-right: solid #ccc 1px;}
.ms4 {margin:0 18px 0 0; padding: 0 54px 0 0; border-right: solid #ccc 1px;}
.ms5 {margin:0 18px 0 0; padding: 0 72px 0 0; border-right: solid #ccc 1px;}
.ms6 {margin:0 18px 0 0; padding: 0 90px 0 0; border-right: solid #ccc 1px;}
${(_a = cfg.customCss) !== null && _a !== void 0 ? _a : ''}
`;
}
resp.set({
"Content-Type": "text/css; charset=utf-8",
"cache-control": "max-age=3600"
});
return result;
});
}
doc(resp, path) {
return __awaiter(this, void 0, void 0, function* () {
const data = this.getRoute(path);
let result = '\n';
if (data) {
result += yield this.getMarkdownResult(data, true);
resp.set({
"Content-Type": "text/plain; charset=utf-8",
Pragma: "no-cache",
Expires: 0
});
return result + "\n";
}
else {
resp.status(404).send(undefined);
}
});
}
docList(resp, path) {
return __awaiter(this, void 0, void 0, function* () {
const routes = this.getGroupRoutes();
let result = `
# ${this.pageTitle}
## 接口数量:共 \`${this.getRoutes().length}\` 个
## 接口列表
`;
result += this.getMarkdownApiGroupResult(routes, true);
resp.set({
"Content-Type": "text/plain; charset=utf-8",
Pragma: "no-cache",
Expires: 0
});
return result + "\n";
});
}
docHtml(resp, path) {
return __awaiter(this, void 0, void 0, function* () {
const data = this.getRoute(path);
let result = '\n';
if (data) {
result += yield this.getMarkdownResult(data, false);
resp.set({
"Content-Type": "text/html; charset=utf-8",
Pragma: "no-cache",
Expires: 0
});
return this.getHtmlData(data.name + " - 接口", result) + "\n";
}
else {
resp.status(404).send(undefined);
}
});
}
/** 目录信息 */
handleToc() {
return __awaiter(this, void 0, void 0, function* () {
const items = this.getGroupRoutes();
const vs = [];
const result = { items: vs, count: 0 };
const server = this.context.server;
const _this = this;
const getListResult = function (group) {
let j = 0;
const rv = [];
group.routes.forEach((data) => {
j += 1;
rv.push({ name: data.name, path: _this.getBaseUrl(data, server) + data.path, method: data.methed });
});
return [rv, j];
};
items.forEach((value) => {
const values = getListResult(value);
result.items.push({ name: value.name, count: values[1], items: values[0] });
});
return result;
});
}
/** 目录信息 */
getApiData(resp, path) {
return __awaiter(this, void 0, void 0, function* () {
let result = '\n';
if (path) {
const data = this.getRoute(path);
if (data) {
result += yield this.getMarkdownResult(data, false);
resp.set({
"Content-Type": "text/html; charset=utf-8",
Pragma: "no-cache",
Expires: 0
});
return result + "\n";
}
else {
resp.status(404).send(undefined);
}
}
else {
const routes = this.getGroupRoutes();
result = `## 接口总数量:共 \`${this.getRoutes().length}\` 个\n\n## 接口列表`;
result += this.getMarkdownApiGroupResult(routes, false, undefined);
resp.set({
"Content-Type": "text/html; charset=utf-8",
Pragma: "no-cache",
Expires: 0
});
}
return result;
});
}
docListHtml(resp) {
return __awaiter(this, void 0, void 0, function* () {
let result = '';
resp.set({
"Content-Type": "text/html; charset=utf-8",
Pragma: "no-cache",
Expires: 0
});
result = this.getHtmlData(this.pageTitle, result, true, undefined);
return result;
});
}
getHtmlData(title, data, canSearch = false, toc) {
return `<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>${title}</title>
<link media="all" rel="stylesheet" href="https://qzonestyle.gtimg.cn/qz-proj/wy-pc-v3/static/css/page-md-preview.css" />
<link media="all" rel="stylesheet" href="/doc/style.css" />
<style>html{overflow:auto;}</style>
</head>
<body>
<script type="text/javascript" src="https://unpkg.zhimg.com/jquery@3.5.0/dist/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/marked/4.0.2/marked.min.js"></script>
<div>
<div id="article" class="markdown-body md-body">
<div class="artentry">
${data}
</div>
</div>
<div id="toc" class="toc"><div class="bg"></div>
<h1 class="ul ul-title">${this.pageTitle}</h1>
${canSearch ? '<div class="search"><input type="text" placeholder="搜索接口" id="key"><button type="button" onclick="javascript:search()"></div>' : ''}
<div class="_toc toc-range">${toc !== null && toc !== void 0 ? toc : ''}</div>
</div></div>
<script type="text/javascript">
let toc;
function path2id(path) {
return path.replaceAll("/","_");
}
function show(id, path) {
let e = $("._toc .sel");
if (e && e.length > 0) e[0].className = "";
e = $("#"+id);
if (e && e.length > 0) e[0].className = "sel";
var mdTxt = $.ajax({url: "apidata.md?path="+(path ?? ''), async: false}).responseText;
marked.options = {breaks: true, smartLists: true}
var htmlData = marked.parse(mdTxt);
$(".artentry").html(htmlData);
$(document).scrollTop(0);
}
function getToc(key) {
if (!toc) {
var tocTxt = $.ajax({url: "toc.json", async: false}).responseText;
toc = JSON.parse(tocTxt);
}
var txt = "";
var sk = key ? key.toLowerCase() : '';
toc.items.forEach((e) => {
let vv = "";
let r = 0;
e.items.forEach((v) => {
if (sk && ! v.path.toLowerCase().includes(sk) && ! v.name.toLowerCase().includes(sk))
return;
r++;
let id = path2id(v.path);
vv += '<div class="ul"><a href="javascript:;" id="' + id + '" onclick="javascript:show(id, \\'' + v.path + '\\');">' + v.name + '</a></div>\\n';
});
if (sk && ! e.name.toLowerCase().includes(sk) && r <= 0)
return;
txt += '<div class="ul">' + e.name + ' (' + r + ' 个 )</div>\\n' + vv;
});
$("._toc").html(txt);
}
function search() {
let key = $("#key")[0].value;
getToc(key);
}
function getQueryString(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
var r = window.location.search.substr(1).match(reg);
if (r != null) { return unescape(r[2]);}
return null;
}
$("#key").bind('keydown', function (event) {
if (event.keyCode == 13) { search(); }
});
getToc("");
var p = getQueryString("api");
if (p) {
show(path2id(p), p);
} else { show(); }
</script>
</body>
</html>
`;
}
getMarkdownApiGroupResult(items, md, searchKey) {
let result = '';
const sk = searchKey ? searchKey.toLowerCase() : undefined;
items.forEach((value) => {
const values = this.getMarkdownApiListResult(value.routes, md, sk);
if (sk && !value.name.toLowerCase().includes(sk) && values[1] <= 0) {
return;
}
result += `
### ${value.name} ( ${values[1]} 个 )
${values[0]}
`;
});
return result;
}
/** 获取 api 列表 markdown 文档内容 */
getMarkdownApiListResult(routes, md, sk) {
let j = 0;
let result = `
| 操作 | 请求方式 | 接口 | 描述 |
| :-- | :-- | :-- | -- |
`;
const server = this.context.server;
routes.forEach((data) => {
const path = this.getBaseUrl(data, server) + data.path;
if (sk && !path.toLowerCase().includes(sk) && !data.name.toLowerCase().includes(sk)) {
return;
}
j += 1;
result = result
+ `| [文档](${md ? "/doc/api.md?path=" : "?"
+ (sk ? "key=" + sk + "&" : '') + "api="}${path})<span class="ml16"/>[配置](/doc/api.config?path=${path})`
+ `| \`${Array.isArray(data.methed) ? data.methed.join(", ").toUpperCase() : data.methed.toUpperCase()}\``
+ `| ${path} `
+ `| ${data.name} `
+ `|\n`;
});
return [result, j];
}
/** 获取 markdown 文档内容 */
getMarkdownResult(data, md) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const path = this.getBaseUrl(data) + data.path;
const remakrs = (_a = (data.remarks && data.remarks instanceof Promise ? yield data.remarks : data.remarks)) !== null && _a !== void 0 ? _a : '暂无备注信息。';
let result = `
# ${data.name}
[文档列表](/doc/${md ? "api/list.md" : "index.html"}) <span class="ml16"/>[配置数据](/doc/api.config?md=${md ? 1 : 0}&path=${path})
## 基本信息
**接口类型**:${data.type}
**接口路径**:\`${Array.isArray(data.methed) ? data.methed.join(", ").toUpperCase() : data.methed.toUpperCase()}\` <font color=#0000ff>${path}</font>
## 备注信息
${remakrs}
## 请求参数
### Headers
| <nobr>参数名称</nobr> | <nobr>参数值</nobr> | <nobr>必须</nobr> | <nobr>示例</nobr> | 备注 |
| :-- | :-- | :-- | -- | :-- |
${this.getHeadersList(data.headers)}
### Query
| 参数名称 | <nobr>必须</nobr> | <nobr>默认值</nobr> | 示例 | 备注 |
| :-- | -- | :-- | :-- | :-- |
${this.getQueryParamList(data)}
`;
if ((Array.isArray(data.methed) && data.methed.includes(reqest_data_1.RequestMethod.POST))
|| data.methed === reqest_data_1.RequestMethod.POST) {
result += `
### Body
| 名称 | 类型 | <nobr>必须</nobr> | <nobr>默认值</nobr> | <nobr>示例</nobr> | 备注 |
| :-- | :-- | -- | :-- | :-- | :-- |
${this.getBodyParamList(data.config, data.body)}
`;
}
result += '## 返回数据\n';
result += data.resp ? `
| 名称 | 类型 | <nobr>是否必须</nobr> | <nobr>默认值</nobr> | 备注 | 示例 |
| :---- | :--- | -- | :-- | :-- | :-- |
${this.getResponseList(data.resp)}
` : (data.result ? JSON.stringify(data.result) : '');
return result;
});
}
getQueryParamList(data) {
var _a, _b;
const items = data.query;
let result = '';
if (items && ((_a = data.config) === null || _a === void 0 ? void 0 : _a.exclude) !== true && ((_b = data.config) === null || _b === void 0 ? void 0 : _b.exclude) !== 1) {
items.forEach((item) => {
var _a, _b, _c;
if (typeof item === 'string') {
result += `| ${item} | 否 | | |\n`;
}
else {
result += `| ${item.name} | ${item.must === true || item.must === 1 ? '是' : '否'} | ${(_a = item.default) !== null && _a !== void 0 ? _a : ''} | ${(_b = item.demo) !== null && _b !== void 0 ? _b : ''} | ${(_c = item.remarks) !== null && _c !== void 0 ? _c : ''} |\n`;
}
});
}
return result;
}
getBodyParamList(config, data, level = 0) {
const items = data ? (Array.isArray(data) ? data : [data]) : undefined;
let result = '';
if (items && (config === null || config === void 0 ? void 0 : config.exclude) !== true && (config === null || config === void 0 ? void 0 : config.exclude) !== 1) {
const indent = level <= 0 ? '' : `<span class="ms${level}"></span>`;
items.forEach((item) => {
var _a, _b;
if (typeof item === 'string') {
result += `| ${indent}${item} | ${reqest_data_1.RequestParamType.StringType} | 非必须 | | | |\n`;
}
else {
result = result
+ `| ${indent}${item.name} `
+ `| ${item.type ? item.type : (item.children ? reqest_data_1.RequestParamType.ObjectType : reqest_data_1.RequestParamType.StringType)}${item.array === 2 ? '[]' : (item.array === true || item.array === 1 ? '[]' : '')}`
+ `| ${item.must === true || item.must === 1 ? '必须' : '非必须'} `
+ `| ${item.default === '' || item.default === undefined ? '' : (typeof item.default === 'string' ? item.default : JSON.stringify(item.default))} `
+ `| ${(_a = item.demo) !== null && _a !== void 0 ? _a : ''}`
+ `| ${(_b = item.remarks) !== null && _b !== void 0 ? _b : ''} `
+ `|\n`;
if (item.children && (item.type === undefined || item.type === reqest_data_1.RequestParamType.ObjectType)) {
result += this.getBodyParamList(config, item.children, level + 1);
}
}
});
}
return result;
}
getHeadersList(data) {
if (data) {
let result = '';
Object.keys(data).forEach((key) => {
var _a, _b;
const v = data[key];
if (typeof v === 'string') {
result += `| ${key} | ${v} | 是 | - | - |\n`;
}
else {
result += `| ${key} | ${v.value} | ${v.must === true ? '是' : '否'} | ${(_a = v.demo) !== null && _a !== void 0 ? _a : '-'} | ${(_b = v.remark) !== null && _b !== void 0 ? _b : '-'} |\n`;
}
});
return result;
}
else {
return '| Content-Type | application/json | 是 | - | - |';
}
}
getResponseList(data, level = 0) {
const fields = data === null || data === void 0 ? void 0 : data.fields;
let result = '';
let lv = level;
if (level === 0 && (data === null || data === void 0 ? void 0 : data.raw) !== true) {
result += this.getResponseListByFields(RouteDocController_1.defaultResultFrame, lv);
result += this.getResponseListByFields([{ name: "data", type: fields ? reqest_data_1.RequestParamType.ObjectType : data === null || data === void 0 ? void 0 : data.type, array: data === null || data === void 0 ? void 0 : data.array, must: false, remarks: data === null || data === void 0 ? void 0 : data.remarks }], lv);
if (fields) {
lv += 1;
}
}
if (fields) {
const items = Array.isArray(fields) ? fields : [fields];
result += this.getResponseListByFields(items, lv);
}
return result;
}
getResponseListByFields(items, level) {
let result = '';
const indent = level <= 0 ? '' : `<span class="ms${level}"></span>`;
items.forEach((item) => {
var _a, _b;
if (typeof item === 'string') {
result += `| ${indent}${item} | ${reqest_data_1.RequestParamType.StringType} | 非必须 | | | |\n`;
}
else {
const existChildren = item.children && (item.type === undefined || item.type === reqest_data_1.RequestParamType.ObjectType);
const bold = existChildren ? '**' : '';
result = result
+ `| ${indent}${bold}${item.name}${bold} `
+ `| ${item.type ? item.type : (item.children ? reqest_data_1.RequestParamType.ObjectType : reqest_data_1.RequestParamType.StringType)}${item.array === true || item.array === 1 ? '[]' : ''} `
+ `| ${item.must === false ? '非必须' : '必须'} `
+ `| ${item.default === '' || item.default === undefined ? '' : (typeof item.default === 'string' ? item.default : JSON.stringify(item.default))} `
+ `| ${(_a = item.remarks) !== null && _a !== void 0 ? _a : ''} `
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ `| ${(_b = item.demo) !== null && _b !== void 0 ? _b : ''} `
+ `|\n`;
if (existChildren) {
result += this.getResponseList(item.children, level + 1);
}
}
});
return result;
}
};
RouteDocController.defaultResultFrame = [
{ name: "code", type: reqest_data_1.RequestParamType.IntType, remarks: '响应代码,0 表示成功' },
{ name: "msg", type: reqest_data_1.RequestParamType.StringType, remarks: '响应消息,异常时为错误信息', must: false },
{ name: "reqId", type: reqest_data_1.RequestParamType.StringType, remarks: '请求ID' }
];
__decorate([
(0, express_decorator_1.Get)("/api.config"),
__param(0, express_decorator_1.HttpResponse),
__param(1, (0, express_decorator_1.Query)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], RouteDocController.prototype, "config", null);
__decorate([
(0, ioc_decorator_1.inject)('', { must: false }),
__metadata("design:type", Function)
], RouteDocController.prototype, "docStyleConfig", void 0);
__decorate([
(0, ioc_decorator_1.inject)('', { must: false }),
__metadata("design:type", Function)
], RouteDocController.prototype, "docStyleHandler", void 0);
__decorate([
(0, express_decorator_1.Get)("/style.css"),
__param(0, express_decorator_1.HttpResponse),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], RouteDocController.prototype, "docCssStyle", null);
__decorate([
(0, express_decorator_1.Get)("/api.md"),
__param(0, express_decorator_1.HttpResponse),
__param(1, (0, express_decorator_1.Query)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], RouteDocController.prototype, "doc", null);
__decorate([
(0, express_decorator_1.Get)("/api/list.md"),
__param(0, express_decorator_1.HttpResponse),
__param(1, (0, express_decorator_1.Query)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], RouteDocController.prototype, "docList", null);
__decorate([
(0, express_decorator_1.Get)("/api.html"),
__param(0, express_decorator_1.HttpResponse),
__param(1, (0, express_decorator_1.Query)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], RouteDocController.prototype, "docHtml", null);
__decorate([
(0, express_decorator_1.Get)("/toc.json"),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], RouteDocController.prototype, "handleToc", null);
__decorate([
(0, express_decorator_1.Get)("/apidata.md"),
__param(0, express_decorator_1.HttpResponse),
__param(1, (0, express_decorator_1.Query)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String]),
__metadata("design:returntype", Promise)
], RouteDocController.prototype, "getApiData", null);
__decorate([
(0, express_decorator_1.Get)("/index.html"),
__param(0, express_decorator_1.HttpResponse),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], RouteDocController.prototype, "docListHtml", null);
RouteDocController = RouteDocController_1 = __decorate([
(0, express_decorator_1.Controller)("/doc", undefined, false)
], RouteDocController);
exports.RouteDocController = RouteDocController;
//# sourceMappingURL=route.doc.controller.js.map