@isxiaoyuan/elpis 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc +52 -0
  3. package/.vscode/settings.json +8 -0
  4. package/REMADE.md +228 -0
  5. package/app/controller/base.js +43 -0
  6. package/app/controller/project.js +106 -0
  7. package/app/controller/view.js +21 -0
  8. package/app/extend/logger.js +36 -0
  9. package/app/middleware/api-params-verify.js +73 -0
  10. package/app/middleware/api-sign-verify.js +43 -0
  11. package/app/middleware/error-handler.js +33 -0
  12. package/app/middleware/project-handle.js +26 -0
  13. package/app/middleware.js +41 -0
  14. package/app/pages/asserts/custom.css +11 -0
  15. package/app/pages/boot.js +43 -0
  16. package/app/pages/common/curl.js +88 -0
  17. package/app/pages/common/utils.js +2 -0
  18. package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +19 -0
  19. package/app/pages/dashboard/complex-view/header-view/header-view.vue +138 -0
  20. package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +51 -0
  21. package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +41 -0
  22. package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +128 -0
  23. package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +20 -0
  24. package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +95 -0
  25. package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +98 -0
  26. package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +123 -0
  27. package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +129 -0
  28. package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +98 -0
  29. package/app/pages/dashboard/complex-view/silder-view/silder-view.vue +127 -0
  30. package/app/pages/dashboard/dashboard.vue +96 -0
  31. package/app/pages/dashboard/entry.dashboard.js +48 -0
  32. package/app/pages/store/index.js +5 -0
  33. package/app/pages/store/menu.js +70 -0
  34. package/app/pages/store/project.js +17 -0
  35. package/app/pages/widgets/header-container/asserts/avatar.png +0 -0
  36. package/app/pages/widgets/header-container/asserts/logo.png +0 -0
  37. package/app/pages/widgets/header-container/header-container.vue +102 -0
  38. package/app/pages/widgets/schema-form/complex-view/input/input.vue +137 -0
  39. package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +136 -0
  40. package/app/pages/widgets/schema-form/complex-view/select/select.vue +122 -0
  41. package/app/pages/widgets/schema-form/form-item-config.js +20 -0
  42. package/app/pages/widgets/schema-form/schema-form.vue +136 -0
  43. package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +55 -0
  44. package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +69 -0
  45. package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +45 -0
  46. package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +53 -0
  47. package/app/pages/widgets/schema-search-bar/schema-item-config.js +24 -0
  48. package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +115 -0
  49. package/app/pages/widgets/schema-table/schema-table.vue +255 -0
  50. package/app/pages/widgets/sider-container/complex-view/sub-menu/sub-menu.vue +18 -0
  51. package/app/pages/widgets/sider-container/sider-container.vue +29 -0
  52. package/app/public/dist/entry.dashboard.tpl +27 -0
  53. package/app/public/dist/entry.page1.tpl +25 -0
  54. package/app/public/dist/entry.page2.tpl +25 -0
  55. package/app/public/dist/entry.project-list.tpl +27 -0
  56. package/app/public/static/logo.png +0 -0
  57. package/app/public/static/normalize.css +239 -0
  58. package/app/router/project.js +12 -0
  59. package/app/router/view.js +7 -0
  60. package/app/router-schema/project.js +30 -0
  61. package/app/service/base.js +13 -0
  62. package/app/service/project.js +58 -0
  63. package/app/view/entry.tpl +27 -0
  64. package/app/webpack/config/webpack.base.js +305 -0
  65. package/app/webpack/config/webpack.dev.js +62 -0
  66. package/app/webpack/config/webpack.prod.js +120 -0
  67. package/app/webpack/dev.js +62 -0
  68. package/app/webpack/libs/blank.js +1 -0
  69. package/app/webpack/prod.js +22 -0
  70. package/config/config.beta.js +3 -0
  71. package/config/config.default.js +3 -0
  72. package/config/config.prod.js +3 -0
  73. package/elpis-core/env.js +20 -0
  74. package/elpis-core/index.js +97 -0
  75. package/elpis-core/loader/config.js +66 -0
  76. package/elpis-core/loader/controller.js +86 -0
  77. package/elpis-core/loader/extend.js +66 -0
  78. package/elpis-core/loader/middleware.js +74 -0
  79. package/elpis-core/loader/router-schema.js +56 -0
  80. package/elpis-core/loader/router.js +49 -0
  81. package/elpis-core/loader/service.js +82 -0
  82. package/index.js +42 -0
  83. package/model/index.js +109 -0
  84. package/package.json +92 -0
  85. package/test/controller/project.test.js +188 -0
package/.eslintignore ADDED
@@ -0,0 +1,3 @@
1
+ node_modules/
2
+ public/
3
+ docs/
package/.eslintrc ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "extends": ["plugin:vue/base", "plugin:vue/recommended"],
3
+ "plugins": ["vue"],
4
+ "env": {
5
+ "browser": true,
6
+ "node": true,
7
+ },
8
+ "parser": "vue-eslint-parser",
9
+ "parserOptions": {
10
+ "parser": "babel-eslint",
11
+ "ecmaVersion": 2017,
12
+ "sourceType": "module",
13
+ },
14
+ "rules": {
15
+ "no-unused-vars": [2, { "args": "none" }],
16
+ "strict": "off",
17
+ "valid-jsdoc": "off",
18
+ "jsdoc/require-param-description": "off",
19
+ "jsdoc/require-param-type": "off",
20
+ "jsdoc/check-param-names": "off",
21
+ "jsdoc/require-param": "off",
22
+ "jsdoc/check-tag-names": "off",
23
+ "linebreak-style": "off",
24
+ "array-bracket-spacing": "off",
25
+ "prefer-promise-reject-errors": "off",
26
+ "comma-dangle": "off",
27
+ "newline-per-chained-call": "off",
28
+ "no-loop-func": "off",
29
+ "no-empty": "off",
30
+ "no-else-return": "off",
31
+ "no-unneeded-ternary": "off",
32
+ "no-eval": "off",
33
+ "prefer-destructuring": "off",
34
+ "no-param-reassign": "off",
35
+ "max-len": "off",
36
+ "no-restricted-syntax": "off",
37
+ "no-plusplus": "off",
38
+ "no-useless-escape": "off",
39
+ "no-nested-ternary": "off",
40
+ "radix": "off",
41
+ "arrow-body-style": "off",
42
+ "arrow-parens": "off",
43
+ "vue/multi-word-component-names": "off",
44
+ "vue/valid-v-for": "off",
45
+ "vue/no-multiple-template-root": "off",
46
+ },
47
+ "globals": {
48
+ "$": true,
49
+ "axios": true,
50
+ "Vue": true,
51
+ },
52
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "files.associations": {
3
+ "*.tpl": "html"
4
+ },
5
+ "[html]": {
6
+ "editor.defaultFormatter": "vscode.html-language-features"
7
+ }
8
+ }
package/REMADE.md ADDED
@@ -0,0 +1,228 @@
1
+ # elpis
2
+
3
+ ## 一个企业级应用框架,通过全栈实现。
4
+
5
+ ### model配置
6
+
7
+ ```javascript
8
+ {
9
+ mode: "dashboard", //模板类型,不同模板类型对应不同的模板数据结构
10
+ name: "", // 名称
11
+ desc: "", // 描述
12
+ icon: "", // 图标
13
+ homePage: "", // 看板首页路由
14
+ // 头部菜单
15
+ menu: [
16
+ {
17
+ key: "", //菜单唯一描述
18
+ name: "首页", //菜单名称
19
+ menuType: "", // 枚举值, group / module
20
+
21
+ //当 menuType === group 时,可填
22
+ subMenu: [
23
+ {
24
+ // 可递归 menuItem
25
+ },
26
+ ],
27
+
28
+ //当 menuType === module 时,可填
29
+ moduleType: "", // 枚举值,sider / iframe / custom / schema
30
+
31
+ //当 moduleType === sider 时,可填
32
+ siderConfig: {
33
+ menu: [
34
+ {
35
+ // 可递归 menuItem ( 除 moduleType === sider)
36
+ },
37
+ ],
38
+ },
39
+ //当 moduleType === iframe 时,可填
40
+ iframeConfig: {
41
+ path: "", // 嵌入的 iframe 地址
42
+ },
43
+ //当 moduleType === custom 时,可填
44
+ customConfig: {
45
+ path: "", // 自定义路由路径
46
+ },
47
+ //当 moduleType === schema 时,可填
48
+ schemaConfig: {
49
+ api: "", // 数据源API (遵循 RESTFUL 规范)
50
+ schema: {
51
+ // 板块数据结构
52
+ type: "object",
53
+ properties: {
54
+ key: {
55
+ ...schema,
56
+ type: "", // 字段类型
57
+ label: "", // 字段中文名
58
+ // 字段在table 中的相关配置
59
+ tableOption: {
60
+ ...elTableColumnConfig, // 标准 el-table-cloumn 配置
61
+ toFixed: 0, // 数字类型字段保留小数位数
62
+ visible: true, // 是否在 table 中显示(不配置就展示)
63
+ },
64
+ // 字段在 search-bar 中的相关配置
65
+ searchOption: {
66
+ ...eleComponentConfig, // 标准 el-component-cloumn 配置
67
+ comType: "", // 配置控件类型
68
+ default: "", // 默认值
69
+
70
+ // 当 comType === select 时,可填
71
+ enumList: [
72
+ // {
73
+ // value: "", // 选项值
74
+ // label: "", // 选项中文名
75
+ // },
76
+ ],
77
+
78
+ // 当 comType === dynamic-select 时,可填
79
+ api: "", // 动态获取选项列表的 API
80
+ },
81
+ // create-form 表单相关配置
82
+ // 字段在不同动态 component 中的相关配置 前置对应componentConfig 中的键值
83
+ // 如: componentConfig.createForm 这里对应 createFormOption
84
+ createFormOption: {
85
+ ...elComponentConfig, // 标准 el-component-cloumn 配置
86
+ comType: "", // 配置控件类型
87
+ visible: true, // 是否在 create-form 中显示(不配置就展示)
88
+ disabled: false, // 是否禁用
89
+ default: "", // 默认值
90
+
91
+ // 当 comType === select 时,可填
92
+ enumList: [
93
+ // {
94
+ // value: "", // 选项值
95
+ // label: "", // 选项中文名
96
+ // },
97
+ ],
98
+ },
99
+ // edit-form 表单相关配置
100
+ editFormOption: {
101
+ ...elComponentConfig, // 标准 el-component-cloumn 配置
102
+ comType: "", // 配置控件类型
103
+ visible: true, // 是否在 edit-form 中显示(不配置就展示)
104
+ disabled: false, // 是否禁用
105
+ default: "", // 默认值
106
+
107
+ // 当 comType === select 时,可填
108
+ enumList: [],
109
+ },
110
+ detailPanelOption: {
111
+ ...elComponentConfig, // 标准 el-component-cloumn 配置
112
+ // visible: true,
113
+ },
114
+ },
115
+ },
116
+ required: [], // 必须字段
117
+ },
118
+ // tabel 相关配置
119
+ tableConfig: {
120
+ headerButtons: [
121
+ {
122
+ label: "", // 按钮中文名
123
+ evenKey: "", // 按钮事件名
124
+ // 按钮具体配置
125
+ eventOption: {
126
+ // 当 eventKey === 'showComponent'
127
+ comName: "", // 组件名称
128
+ },
129
+ ...elButtonConfig, // 标准 el-button 配置
130
+ },
131
+ ],
132
+ rowButtons: [
133
+ {
134
+ label: "", // 按钮中文名
135
+ evenKey: "", // 按钮事件名
136
+ eventOption: {
137
+ // 当 eventKey === 'showComponent'
138
+ comName: "", // 组件名称
139
+
140
+ // 当eventKey === remove
141
+ params: {
142
+ // paramKey 参数的键值
143
+ // rowValueKey 参数值(当格式为 schema:xxx 的时候,到 table 中找响应的字段)
144
+ paramKey: rowValueKey,
145
+ },
146
+ }, // 按钮具体配置
147
+ ...elButtonConfig, // 标准 el-button 配置
148
+ },
149
+ ],
150
+ },
151
+ searchConfig: {}, // search-bar 相关配置
152
+ // 模块组件 动态组件
153
+ componentsConfig: {
154
+ // create-form 表单相关配置
155
+ createForm: {
156
+ title: "", // 表单标题
157
+ saveBtnText: "", // 保存按钮文案
158
+ },
159
+ // edit-form 表单相关配置
160
+ editForm: {
161
+ mainKey: "", // 表单主键, 用于唯一标识要修改的数据对象
162
+ title: "", // 表单标题
163
+ saveBtnText: "", // 保存按钮文案
164
+ },
165
+ // detail-panel 相关配置
166
+ detailPanel: {
167
+ mainKey: "", // 详情面板主键, 用于唯一标识要展示的数据对象
168
+ title: "", // 详情面板标题
169
+ },
170
+ // ...支持用户动态扩展其他组件配置
171
+ },
172
+ },
173
+ },...
174
+ ],
175
+ };
176
+
177
+ ```
178
+
179
+ ### 服务端启动
180
+
181
+ ```javascript
182
+ const { serverStart } = require("@isyuan/elpis");
183
+
184
+ // 启动 elpis 服务
185
+
186
+ const app = serverStart();
187
+ ```
188
+
189
+ ### 自定义服务端
190
+
191
+ - router-schema
192
+ - router
193
+ - controller
194
+ - service
195
+ - extend
196
+ - config
197
+
198
+ ### 前端构建
199
+
200
+ ```javascript
201
+ const { frontendBuild } = require("@isyuan/elpis");
202
+
203
+ // 编译构建前端工程
204
+ frontendBuild(process.env._ENV);
205
+ ```
206
+
207
+ ### 自定义页面扩展
208
+
209
+ - 在 `app/pages` 目录下写入口 entry.xxx.js
210
+
211
+ ### dashboard / custom-view 自定义页面扩展
212
+
213
+ - 在 `app/pages/dashboard/xxx` 下写页面
214
+
215
+ ### dashboard / schema-view / components 动态组件扩展
216
+
217
+ 1. 在 `app/pages/dashboard/complex-view/schema-view/components` 下写组件
218
+ 2. 配置到 `app/pages/dashboard/complex-view/schema-view/components/component-config.js`
219
+
220
+ ### schema-form 控件扩展
221
+
222
+ 1. 在 `app/pages/widgets/schema-form/complex-view` 下写控件
223
+ 2. 配置到 `app/pages/widgets/schema-form/complex-view/component-config.js`
224
+
225
+ ### schema-search-bar 控件扩展
226
+
227
+ 1. 在 `app/pages/widgets/schema-search-bar/complex-view` 下写控件
228
+ 2. 配置到 `app/pages/widgets/schema-search-bar/complex-view/component-config.js`
@@ -0,0 +1,43 @@
1
+ module.exports = (app) =>
2
+ class BaseController {
3
+ /**
4
+ * controller 基类
5
+ * 统一收拢 controller 中的公共方法
6
+ * @param {object} app koa 实例
7
+ */
8
+ constructor() {
9
+ this.app = app;
10
+ this.config = app.config;
11
+ // this.service = app.service;
12
+ }
13
+
14
+ /**
15
+ * 成功响应
16
+ * @param {object} ctx 上下文
17
+ * @param {object} data 数据
18
+ * @param {object} metadata 附加数据
19
+ */
20
+ success(ctx, data = {}, metadata = {}) {
21
+ ctx.status = 200;
22
+ ctx.body = {
23
+ success: true,
24
+ data,
25
+ metadata,
26
+ };
27
+ }
28
+
29
+ /**
30
+ * 失败响应
31
+ * @param {object} ctx 上下文
32
+ * @param {string} message 错误信息
33
+ * @param {number} code 错误码
34
+ */
35
+ fail(ctx, message, code) {
36
+ ctx.status = 400;
37
+ ctx.body = {
38
+ success: false,
39
+ message,
40
+ code,
41
+ };
42
+ }
43
+ };
@@ -0,0 +1,106 @@
1
+ module.exports = (app) => {
2
+ const BaseController = require("./base")(app);
3
+ return class ProjectController extends BaseController {
4
+ /**
5
+ * 根据 proj_Key 获取项目配置
6
+ * @param {object} ctx 上下文
7
+ */
8
+
9
+ get(ctx) {
10
+ const { proj_key: projKey } = ctx.request.query;
11
+ const { project: projectService } = app.service;
12
+ const projConfig = projectService.get(projKey);
13
+
14
+ if (!projConfig) {
15
+ this.fail(ctx, "获取项目异常", 50000);
16
+ return;
17
+ }
18
+ this.success(ctx, projConfig);
19
+ }
20
+
21
+ /**
22
+ * 获取当前 projKey 对应模型下的项目列表(如果无 projKey, 全量获取)
23
+ * @param {object} ctx 上下文
24
+ */
25
+ getList(ctx) {
26
+ const { proj_key: projKey } = ctx.request.query;
27
+ const { project: projectService } = app.service;
28
+ const projectList = projectService.getList({ projKey });
29
+
30
+ //构造关键数据 list
31
+ const dtoProjectList = projectList.map((item) => {
32
+ const { modelKey, key, name, desc, homePage } = item;
33
+ return {
34
+ modelKey,
35
+ key,
36
+ name,
37
+ desc,
38
+ homePage,
39
+ };
40
+ });
41
+
42
+ // for (let i = 0; i < projectList.length; ++i) {
43
+ // const item = projectList[i];
44
+ // const { key, name, desc, homePage } = item;
45
+ // dtoProjectList.push({
46
+ // key,
47
+ // name,
48
+ // desc,
49
+ // homePage,
50
+ // });
51
+ // }
52
+ // const dtoProjectList = projectList.reduce((preList, item) => {
53
+ // const { key, name, desc, homePage } = item;
54
+ // preList.push({
55
+ // key,
56
+ // name,
57
+ // desc,
58
+ // homePage,
59
+ // });
60
+ // return preList;
61
+ // }, []);
62
+ this.success(ctx, dtoProjectList);
63
+ }
64
+ /**
65
+ * 获取所有项目与项目的结构化数据
66
+ * @param {object} ctx 上下文
67
+ */
68
+ async getModelList(ctx) {
69
+ const { project: projectService } = app.service;
70
+ const modelList = await projectService.getModelList();
71
+
72
+ // 构造返回结果,返回关键数据
73
+ const dtoModelList = modelList.reduce((preList, item) => {
74
+ const { model, project } = item;
75
+ // 构造 model 关键数据
76
+ const { key, name, desc } = model;
77
+ const dtoModel = { key, name, desc };
78
+ // 构造 project 关键数据
79
+ const dtoProject = Object.keys(project).reduce((preObj, projKey) => {
80
+ const { key, name, desc, homePage } = project[projKey];
81
+ preObj[projKey] = {
82
+ key,
83
+ name,
84
+ desc,
85
+ homePage,
86
+ };
87
+ return preObj;
88
+ }, {});
89
+ // const dtoProject = {};
90
+ // for (const projKey in project) {
91
+ // const { key, name, desc, homePage } = project[projKey];
92
+ // dtoProject[projKey] = {
93
+ // key,
94
+ // name,
95
+ // desc,
96
+ // homePage,
97
+ // };
98
+ // }
99
+ preList.push({ model: dtoModel, project: dtoProject });
100
+ return preList;
101
+ }, []);
102
+
103
+ this.success(ctx, dtoModelList);
104
+ }
105
+ };
106
+ };
@@ -0,0 +1,21 @@
1
+ module.exports = (app) => {
2
+ return class ViewController {
3
+ /**
4
+ * 渲染页面
5
+ * @param {object} ctx 上下文
6
+ */
7
+ async renderPage(ctx) {
8
+ const { query } = ctx.request;
9
+ const { params } = ctx;
10
+ app.logger.info(`[ViewController] query: ${JSON.stringify(query)}`);
11
+ app.logger.info(`[ViewController] chapters: ${JSON.stringify(params)}`);
12
+
13
+ await ctx.render(`dist/entry.${params.page}`, {
14
+ projKey: query?.proj_key,
15
+ name: app.options?.name,
16
+ env: app.env.get(),
17
+ options: JSON.stringify(app.options),
18
+ });
19
+ }
20
+ };
21
+ };
@@ -0,0 +1,36 @@
1
+ const log4js = require("log4js");
2
+
3
+ /**
4
+ *
5
+ * 日志工具
6
+ * 外部调用 app.logger.info.logger.error
7
+ */
8
+
9
+ module.exports = (app) => {
10
+ let logger;
11
+ if (app.env.isLocal()) {
12
+ //打印在控制台即可
13
+ logger = console;
14
+ } else {
15
+ // 把日志输出落地到磁盘 (日志落盘)
16
+ log4js.configure({
17
+ // 第一步:定义日志输出器(Appenders)—— 日志要输出到哪里
18
+ appenders: {
19
+ console: { type: "console" },
20
+ // 日志文件切分
21
+ dateFile: {
22
+ type: "dateFile", // 输出器类型:按日期自动切分文件
23
+ filename: "./logs/application.log",
24
+ pattern: ".yyyy-MM-dd",
25
+ alwaysIncludePattern: true, // 输出器类型:按日期自动切分文件
26
+ },
27
+ // file: { type: "file", filename: "app.log" },
28
+ },
29
+ categories: {
30
+ default: { appenders: ["console", "dateFile"], level: "trace" }, // 日志级别:trace(最低级别,会输出所有日志)
31
+ },
32
+ });
33
+ logger = log4js.getLogger();
34
+ }
35
+ return logger;
36
+ };
@@ -0,0 +1,73 @@
1
+ const Ajv = require("ajv");
2
+ const ajv = new Ajv();
3
+ /**
4
+ * 验证 api 参数
5
+ * @param {object} app koa 实例
6
+ */
7
+ module.exports = (app) => {
8
+ return async (ctx, next) => {
9
+ const $schema = "http://json-schema.org/draft-07/schema#";
10
+ // 只对api请求做校验
11
+ if (ctx.path.indexOf("/api/") < 0) {
12
+ return await next();
13
+ }
14
+
15
+ //获取请求参数
16
+ const { query, body, headers } = ctx.request;
17
+ const { params, path, method } = ctx;
18
+
19
+ app.logger.info(`[${method} ${path}] body: ${JSON.stringify(body)}`);
20
+ app.logger.info(`[${method} ${path}] query: ${JSON.stringify(query)}`);
21
+ app.logger.info(`[${method} ${path}] params: ${JSON.stringify(params)}`);
22
+ app.logger.info(`[${method} ${path}] headers: ${JSON.stringify(headers)}`);
23
+
24
+ const scheam = app.routerSchema[path]?.[method.toLowerCase()];
25
+ if (!scheam) {
26
+ return await next();
27
+ }
28
+
29
+ let valid = true;
30
+
31
+ // ajv 校验器
32
+ let validate;
33
+
34
+ // 校验headers
35
+ if (valid && headers && scheam.headers) {
36
+ scheam.headers.$schema = $schema;
37
+ validate = ajv.compile(scheam.headers);
38
+ valid = validate(headers);
39
+ }
40
+
41
+ // 校验body
42
+ if (valid && body && scheam.body) {
43
+ scheam.body.$schema = $schema;
44
+ validate = ajv.compile(scheam.body);
45
+ valid = validate(body);
46
+ }
47
+
48
+ // 校验query
49
+ if (valid && query && scheam.query) {
50
+ scheam.query.$schema = $schema;
51
+ validate = ajv.compile(scheam.query);
52
+ valid = validate(query);
53
+ }
54
+
55
+ // 校验params
56
+ if (valid && params && scheam.params) {
57
+ scheam.params.$schema = $schema;
58
+ validate = ajv.compile(scheam.params);
59
+ valid = validate(params);
60
+ }
61
+
62
+ if (!valid) {
63
+ ctx.status = 200;
64
+ ctx.body = {
65
+ code: 442,
66
+ success: false,
67
+ message: `request validate fail: ${ajv.errorsText(validate.errors)}`,
68
+ };
69
+ return;
70
+ }
71
+ await next();
72
+ };
73
+ };
@@ -0,0 +1,43 @@
1
+ const md5 = require("md5");
2
+
3
+ /**
4
+ * 验证 api 签名合法性
5
+ * @param {object} app koa 实例
6
+ */
7
+
8
+ module.exports = (app) => {
9
+ return async (ctx, next) => {
10
+ // 只对api请求做签名校验
11
+ if (ctx.path.indexOf("/api") < 0) {
12
+ return await next();
13
+ }
14
+
15
+ const { path, method } = ctx;
16
+ const { headers } = ctx.request;
17
+ const { s_sign: sSign, s_t: st } = headers;
18
+
19
+ const signKey = "isyuankkkkkll123kasd2444";
20
+ const signature = md5(`${signKey}_${st}`);
21
+
22
+ app.logger.info(`[${method} ${path}] signature: ${signature}`);
23
+
24
+ //时效600s内 并且用户传来的签名要等于signature 并且有时效 有签名
25
+ //sSign 是用户传的签名 st 是用户传的时间戳
26
+ // signKey 是服务器端的密钥 signature 是服务器端计算的签名 要具有对称性
27
+ if (
28
+ !sSign ||
29
+ !st ||
30
+ sSign.toLowerCase() !== signature ||
31
+ Date.now() - st > 600000
32
+ ) {
33
+ ctx.status = 200;
34
+ ctx.body = {
35
+ success: false,
36
+ code: 445,
37
+ message: "signature no correct or api timeout!!",
38
+ };
39
+ return;
40
+ }
41
+ return await next();
42
+ };
43
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 运行时异常错误处理,兜底所有异常
3
+ * @param {object} app koa 实例
4
+ */
5
+
6
+ module.exports = (app) => {
7
+ return async (ctx, next) => {
8
+ try {
9
+ await next();
10
+ } catch (err) {
11
+ const { status, message, detail } = err;
12
+ app.logger.info(JSON.stringify(err));
13
+ app.logger.error("[-- exception --]", err);
14
+ app.logger.error("[-- exception --]", status, message, detail);
15
+
16
+ if (message && message.indexOf("template not found") > -1) {
17
+ // 页面重定向 301 永久重定向(资源被永久移动到新位置 缓存在会一直访问不到) 302 临时重定向(资源临时移动到新位置)
18
+ ctx.status = 302;
19
+ ctx.redirect(`${app.options?.homePage}`);
20
+ return;
21
+ }
22
+
23
+ const resBody = {
24
+ success: false,
25
+ code: 50000,
26
+ message: "网络异常 请稍后重试",
27
+ };
28
+
29
+ ctx.status = 200;
30
+ ctx.body = resBody;
31
+ }
32
+ };
33
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * 项目相关处理中间件
3
+ * @param {object} app 应用实例
4
+ */
5
+
6
+ module.exports = (app) => {
7
+ return async (ctx, next) => {
8
+ // 只对 业务 api 进行proj_key 处理
9
+ if (ctx.path.indexOf("/api/proj/") < 0) {
10
+ return await next();
11
+ }
12
+ const { proj_key: projKey } = ctx.request.headers;
13
+ if (!projKey) {
14
+ ctx.status = 2000;
15
+ ctx.body = {
16
+ success: false,
17
+ code: 446,
18
+ message: "proj_Key is not found",
19
+ };
20
+ return;
21
+ }
22
+
23
+ ctx.projKey = projKey;
24
+ await next();
25
+ };
26
+ };