UNPKG

ts-api-core

Version:

Nodejs api framework core

608 lines (588 loc) 27 kB
"use strict"; 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