@huangzongxin/elpis 0.0.0-stage → 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 +58 -0
  3. package/.husky/pre-commit +4 -0
  4. package/.vscode/settings.json +5 -0
  5. package/README.md +183 -3
  6. package/app/controller/base.js +44 -0
  7. package/app/controller/project.js +62 -0
  8. package/app/controller/view.js +17 -0
  9. package/app/extend/logger.js +38 -0
  10. package/app/middleware/api-params-verify.js +71 -0
  11. package/app/middleware/api-sign-verify.js +35 -0
  12. package/app/middleware/error-handler.js +33 -0
  13. package/app/middleware/project-handler.js +26 -0
  14. package/app/middleware.js +41 -0
  15. package/app/pages/asserts/custom.css +13 -0
  16. package/app/pages/boot.js +46 -0
  17. package/app/pages/common/curl.js +82 -0
  18. package/app/pages/common/utils.js +2 -0
  19. package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +16 -0
  20. package/app/pages/dashboard/complex-view/header-view/header-view.vue +103 -0
  21. package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +41 -0
  22. package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +39 -0
  23. package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +113 -0
  24. package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +23 -0
  25. package/app/pages/dashboard/complex-view/schema-view/components/create-form/creat-form.vue +73 -0
  26. package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +103 -0
  27. package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +112 -0
  28. package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +123 -0
  29. package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +77 -0
  30. package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +16 -0
  31. package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +107 -0
  32. package/app/pages/dashboard/dashboard.vue +86 -0
  33. package/app/pages/dashboard/entry.dashboard.js +42 -0
  34. package/app/pages/store/index.js +7 -0
  35. package/app/pages/store/menu.js +53 -0
  36. package/app/pages/store/project.js +15 -0
  37. package/app/pages/widgets/header-container/asserts/avatar.png +0 -0
  38. package/app/pages/widgets/header-container/asserts/logo.png +0 -0
  39. package/app/pages/widgets/header-container/header-container.vue +108 -0
  40. package/app/pages/widgets/schema-form/complex-view/input/input.vue +110 -0
  41. package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +126 -0
  42. package/app/pages/widgets/schema-form/complex-view/select/select.vue +103 -0
  43. package/app/pages/widgets/schema-form/form-item-config.js +23 -0
  44. package/app/pages/widgets/schema-form/schema-form.vue +115 -0
  45. package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +38 -0
  46. package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +50 -0
  47. package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +35 -0
  48. package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +37 -0
  49. package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +116 -0
  50. package/app/pages/widgets/schema-search-bar/search-item-config.js +26 -0
  51. package/app/pages/widgets/schema-table/schema-table.vue +193 -0
  52. package/app/pages/widgets/sider-container/sider-container.vue +32 -0
  53. package/app/public/static/logo.png +0 -0
  54. package/app/public/static/normalize.css +239 -0
  55. package/app/router/project.js +15 -0
  56. package/app/router/view.js +7 -0
  57. package/app/router-schema/project.js +31 -0
  58. package/app/service/base.js +14 -0
  59. package/app/service/project.js +43 -0
  60. package/app/view/entry.tpl +27 -0
  61. package/app/webpack/config/webpack.base.js +270 -0
  62. package/app/webpack/config/webpack.dev.js +57 -0
  63. package/app/webpack/config/webpack.prod.js +82 -0
  64. package/app/webpack/dev.js +55 -0
  65. package/app/webpack/libs/blank.js +1 -0
  66. package/app/webpack/prod.js +22 -0
  67. package/config/config.default.js +4 -0
  68. package/docs/ELPIS-DSL.md +406 -0
  69. package/docs/dashboard-model.js +140 -0
  70. package/docs/webpack/345/267/245/347/250/213/345/214/226/345/256/236/350/267/265.md +313 -0
  71. package/elpis-core/env.js +17 -0
  72. package/elpis-core/index.js +101 -0
  73. package/elpis-core/loader/config.js +64 -0
  74. package/elpis-core/loader/controller.js +75 -0
  75. package/elpis-core/loader/extend.js +54 -0
  76. package/elpis-core/loader/middleware.js +76 -0
  77. package/elpis-core/loader/router-schema.js +57 -0
  78. package/elpis-core/loader/router.js +53 -0
  79. package/elpis-core/loader/service.js +72 -0
  80. package/index.html +0 -0
  81. package/index.js +39 -0
  82. package/model/index.js +107 -0
  83. package/package.json +91 -6
  84. package/study.md +9 -0
  85. package/test/controller/project.test.js +207 -0
package/.eslintignore ADDED
@@ -0,0 +1,3 @@
1
+ node_modules/
2
+ public/
3
+ docs/
package/.eslintrc ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "ignorePatterns": ["docs/**/*.js"],
3
+ "extends": [
4
+ "plugin:vue/base",
5
+ "plugin:vue/recommended"
6
+ ],
7
+ "plugins": ["vue"],
8
+ "env": {
9
+ "browser": true,
10
+ "node": true
11
+ },
12
+ "parserOptions": {
13
+ "ecmaVersion": 2020,
14
+ "sourceType": "module"
15
+ },
16
+ "rules": {
17
+ "no-unused-vars": [2, {"args": "none"}],
18
+ "strict": "off",
19
+ "valid-jsdoc": "off",
20
+ "jsdoc/require-param-description": "off",
21
+ "jsdoc/require-param-type": "off",
22
+ "jsdoc/check-param-names": "off",
23
+ "jsdoc/require-param": "off",
24
+ "jsdoc/check-tag-names": "off",
25
+ "linebreak-style": "off",
26
+ "array-bracket-spacing": "off",
27
+ "prefer-promise-reject-errors": "off",
28
+ "comma-dangle": "off",
29
+ "newline-per-chained-call": "off",
30
+ "no-loop-func": "off",
31
+ "no-empty": "off",
32
+ "no-else-return": "off",
33
+ "no-unneeded-ternary": "off",
34
+ "no-eval": "off",
35
+ "prefer-destructuring": "off",
36
+ "no-param-reassign": "off",
37
+ "max-len": "off",
38
+ "no-restricted-syntax": "off",
39
+ "no-plusplus": "off",
40
+ "no-useless-escape": "off",
41
+ "no-nested-ternary": "off",
42
+ "radix": "off",
43
+ "arrow-body-style": "off",
44
+ "arrow-parens": "off",
45
+ "vue/multi-word-component-names": "off",
46
+ "vue/valid-v-for": "off",
47
+ "vue/no-multiple-template-root": "off",
48
+ "vue/max-attributes-per-line": ["warn", {
49
+ "singleline": 3,
50
+ "multiline": 1
51
+ }]
52
+ },
53
+ "globals": {
54
+ "$": true,
55
+ "axios": true,
56
+ "Vue": true
57
+ }
58
+ }
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ npm run lint
@@ -0,0 +1,5 @@
1
+ {
2
+ "files.associations": {
3
+ "*.tpl": "html"
4
+ }
5
+ }
package/README.md CHANGED
@@ -1,3 +1,183 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ model配置
2
+ {
3
+ mode: 'dashboard', // 模版类型,不同模版类型对应不一样的模版数据结构
4
+ name: '', // 名称
5
+ desc: '', // 描述
6
+ icon: '', // icon
7
+ homePage: '', // 首页(项目配置)
8
+ // 头部菜单
9
+ menu: [{
10
+ key: '', // 菜单唯一描述,
11
+ name: '', // 菜单名称
12
+ menuType: '', // 枚举值:group / module
13
+
14
+ // 当 menuType == group 时,可填
15
+ subMenu: [{
16
+ // 可递归 menuItem
17
+ }, ...],
18
+
19
+ // 当 menuType == module 时,可填
20
+ moduleType: '', // 枚举值:sider/iframe/custom/schema
21
+
22
+ // 当 moduleType == sider 时
23
+ siderConfig: {
24
+ menu: [{
25
+ // 可递归 menuItem(除 moduleType === sider)
26
+ }, ...]
27
+ },
28
+
29
+ // 当 moduleType == iframe 时
30
+ iframeConfig: {
31
+ path: '', // iframe 路径
32
+ },
33
+
34
+ // 当 moduleType == custom 时
35
+ customConfig: {
36
+ path: '', // 自定义路由路径
37
+ },
38
+
39
+ // 当 moduleType == schema 时
40
+ schemaConfig: {
41
+ api: '', // 数据源API(遵循 RESTFUL 规范)
42
+ schema: { // 板块数据结构
43
+ type: 'object',
44
+ properties: {
45
+ key: {
46
+ ...schema, // 标准 schema 配置
47
+ type: '', // 字段类型
48
+ label: '', // 字段的中文名
49
+ // 字段在 table 中的相关配置
50
+ tableOption: {
51
+ ...elTableColumnConfig, // 标准 el-table-column 配置
52
+ toFixed: 0, // 保留小数点后几位
53
+ visible: true, // 默认为 true(false 时,表示不在表单中显示)
54
+ },
55
+ // 字段在 search-bar 中的相关配置
56
+ searchOption: {
57
+ ...eleComponentConfig, // 标准 el-component-column 配置
58
+ comType: '', // 配置组件类型 input/select/.....
59
+ default: '', // 默认值
60
+
61
+ // comType === 'select'
62
+ enumList: [], // 下拉框可选项
63
+
64
+ // comType === 'dynamicSelect'
65
+ api: ''
66
+ },
67
+ // 字段在不同动态 component 中的相关配置,前缀对应 componentComfig 中的键值
68
+ // 如:componentConfig.createForm,这里对应 createFormOption
69
+ // 字段在 createForm 中相关配置
70
+ createFormOption: {
71
+ ...eleComponentConfig, // 标准 el-component 配置
72
+ comType: '', // 控件类型 input/selct/input-number
73
+ visible: true, // 是否展示 (true/false),默认为 true
74
+ disabled: false, // 是否禁用 (true/false),默认为 false
75
+ default: '', // 默认值
76
+
77
+ // comType === 'select' 时生效
78
+ enumList: [] // 枚举列表
79
+ },
80
+ // 字段在 editForm 表单中的相关配置
81
+ editFormOption: {
82
+ ...eleComponentConfig, // 标准 el-component 配置
83
+ comType: '', // 控件类型 input/selct/input-number
84
+ visible: true, // 是否展示 (true/false),默认为 true
85
+ disabled: false, // 是否禁用 (true/false),默认为 false
86
+ default: '', // 默认值
87
+
88
+ // comType === 'select' 时生效
89
+ enumList: [] // 枚举列表
90
+ },
91
+ detailPanelOption: {
92
+ ...eleComponentConfig // 标准 el-component 配置
93
+ }
94
+ },
95
+ ...
96
+ },
97
+ required: [], // 标记哪些字段是必填项
98
+ },
99
+ // table 相关配置
100
+ tableConfig: {
101
+ headerButtons: [{
102
+ label: '', // 按钮中文名
103
+ eventKey: '', // 按钮事件名
104
+ // 按钮事件具体配置
105
+ eventOption: {
106
+ // 当 eventKey === 'showComponent'
107
+ comName: '' // 组件名称
108
+ },
109
+ ...elButtonConfig // 标准 el-button 配置
110
+ }, ...],
111
+ rowButtons: [{
112
+ label: '', // 按钮中文名
113
+ eventKey: '', // 按钮事件名
114
+ eventOption: {
115
+ // 当 eventKey === 'showComponent'
116
+ comName: '' // 组件名称
117
+
118
+ // 当 eventKey === 'remove'
119
+ params: {
120
+ // paramKey = 参数的键值
121
+ // rowValueKey = 参数值, 格式为 schema::tableKey,到 table 中找相应的字段
122
+ paramKey: rowValueKey
123
+ }
124
+ }, // 按钮事件具体配置
125
+ ...elButtonConfig // 标准 el-button 配置
126
+ }, ...]
127
+ },
128
+ // search-bar 相关配置
129
+ searchConfig: {},
130
+ // 动态组件 相关配置
131
+ componentConfig: {
132
+ // create-form 表单相关配置
133
+ createForm: {
134
+ title: '', // 表单标题
135
+ saveBtnText: '', // 保存按钮文案
136
+ },
137
+ // edit-form 表单相关配置
138
+ editForm: {
139
+ mainKey: '', // 表单主键,用于唯一标识要修改的数据对象
140
+ title: '', // 表单标题
141
+ saveBtnText: '', // 保存按钮文案
142
+ },
143
+ // detail-panel 相关配置
144
+ detailPanel: {
145
+ mainKey: '', // 表单主键,用于唯一标识要修改的数据对象
146
+ title: '', // 表单标题
147
+ }
148
+ // ...支持用户动态扩展
149
+ }
150
+ }
151
+
152
+ }, ...]
153
+ }
154
+ 服务端启动
155
+ const {
156
+ serverStart
157
+ } = require('@fsiaonma/elpis');
158
+
159
+ // 启动 elpis 服务
160
+ const app = serverStart({});
161
+ 前端构建
162
+ const { frontendBuild } = require('@fsiaonma/elpis');
163
+
164
+ // 编译构建前端工程(签名不变)
165
+ // local → Vite createServer :9002
166
+ // production → Vite build → app/public/dist/prod
167
+ frontendBuild(process.env.\_ENV);
168
+ 自定义页面扩展
169
+ 在 app/pages/ 目录下写入口 entry.xxx.js
170
+ dashboard / custom-view 自定义页面扩展
171
+ 在 app/pages/dashboard/xxxx 下写页面
172
+ dashboard / shcema-view / components 动态组件扩展
173
+ 在 app/pages/dashboard/complex-view/schema-view/components 下写组件
174
+ 配置到 app/pages/dashboard/complex-view/schema-view/components/component-config.js
175
+ schema-form 控件扩展
176
+ 在 app/widgets/schema-form/complex-view 下写控件
177
+ 配置到 app/widgets/schema-form/form-item-config.js
178
+ schema-search-bar 控件扩展
179
+ 在 app/widgets/schema-search-bar/complex-view 下写控件
180
+ 配置到 app/widgets/schema-search-bar/search-item-config.js
181
+
182
+
183
+
@@ -0,0 +1,44 @@
1
+ module.exports = (app) => class BaseController {
2
+ /**
3
+ * 基础控制器
4
+ * conttroller 基类
5
+ * 统一收拢 controller 相关的公共方法
6
+ */
7
+ constructor() {
8
+ this.app = app;
9
+ this.config = app.config;
10
+ }
11
+
12
+ /**
13
+ * API 处理成功响应统一返回结构
14
+ * @param {*} ctx koa 上下文
15
+ * @param {*} data 响应数据
16
+ * @param {*} metadata 元数据
17
+ * @returns
18
+ */
19
+ success(ctx,data={}, metadata = {}) {
20
+ ctx.status = 200;
21
+ ctx.body = {
22
+ success:true,
23
+ data,
24
+ metadata
25
+ }
26
+ }
27
+
28
+
29
+ /**
30
+ * API 处理失败响应统一返回结构
31
+ * @param {*} ctx koa 上下文
32
+ * @param {*} message 错误消息
33
+ * @param {*} code 错误码
34
+ * @returns
35
+ */
36
+ fail(ctx,message, code) {
37
+ ctx.body = {
38
+ success:false,
39
+ message,
40
+ code,
41
+ }
42
+ }
43
+
44
+ }
@@ -0,0 +1,62 @@
1
+ module.exports = (app) => {
2
+ const BaseController = require("./base")(app);
3
+
4
+ return class ProjectController extends BaseController {
5
+ /*
6
+ 根据proj_key 获取项目配置
7
+ */
8
+ get(ctx) {
9
+ const { proj_key: projKey } = ctx.request.query;
10
+ const { project: projectService } = app.service;
11
+ const projConfig = projectService.get(projKey);
12
+ if (!projConfig) {
13
+ this.fail(ctx, "获取项目异常", 50000);
14
+ return;
15
+ }
16
+ this.success(ctx, projConfig);
17
+ }
18
+ /*
19
+ 获取当前projectKey 对应模型下的项目列表,(如果无projectKey,全量获取)
20
+ */
21
+ getList(ctx) {
22
+ const { proj_key: projKey } = ctx.request.query;
23
+ const { project: projectService } = app.service;
24
+ const projectList = projectService.getList({ projKey });
25
+
26
+ const dtoProjectList = projectList.map((item) => {
27
+ const { modelKey, key, name, desc, homePage } = item;
28
+ return { modelKey, key, name, desc, homePage };
29
+ });
30
+
31
+ this.success(ctx, dtoProjectList);
32
+ }
33
+
34
+ /*
35
+ 获取所有模型与项目的结构化数据
36
+ */
37
+ async getModelList(ctx) {
38
+ const { project: projectService } = app.service;
39
+ const modleList = await projectService.getModelList();
40
+ //构造返回结果,只返回关键数据
41
+ const dtoModelList = modleList.reduce((preList, item) => {
42
+ const { model, project } = item;
43
+ //构造model关键数据
44
+ const { key, name, desc } = model;
45
+ const dtoModel = { key, name, desc };
46
+ //构造project 关键数据
47
+ const dtoProject = Object.keys(project).reduce((preObj, projKey) => {
48
+ const { key, name, desc, homePage } = project[projKey];
49
+ preObj[projKey] = { key, name, desc, homePage };
50
+ return preObj;
51
+ }, {});
52
+
53
+ preList.push({
54
+ model: dtoModel,
55
+ project: dtoProject,
56
+ });
57
+ return preList;
58
+ }, []);
59
+ this.success(ctx, dtoModelList);
60
+ }
61
+ };
62
+ };
@@ -0,0 +1,17 @@
1
+ module.exports = (app) => {
2
+ // 渲染模板
3
+ return class ViewController {
4
+ async renderPage(ctx) {
5
+ const { query, body } = ctx.request;
6
+ app.logger.info(`[ViewController] query: ${JSON.stringify(query)}`);
7
+ app.logger.info(`[ViewController] body: ${JSON.stringify(body)}`);
8
+
9
+ await ctx.render(`dist/entry.${ctx.params.page}`, {
10
+ projKey: ctx.query?.proj_key,
11
+ name: app.options.name,
12
+ env: app.env.get(),
13
+ options: JSON.stringify(app.options),
14
+ });
15
+ }
16
+ };
17
+ };
@@ -0,0 +1,38 @@
1
+ const log4js = require('log4js');
2
+ /**
3
+ * 日志记录器
4
+ * 外部调用 app.logger.info app.logger.error
5
+ * @returns 日志记录器实例
6
+ */
7
+ module.exports = (app) => {
8
+ let logger ;
9
+ if(app.env.isLocal()){
10
+ // 打印在控制台即可
11
+ logger = console;
12
+ }else{
13
+ // 生产环境,八日至输出并落地到磁盘(日志落盘)
14
+ log4js.configure({
15
+ appenders: {
16
+ console: {
17
+ type: 'console'
18
+ },
19
+ // 日志文件切分
20
+ dateFile: {
21
+ type: 'dateFile',
22
+ filename: './logs/application.log',
23
+ pattern: '.yyyy-MM-dd'
24
+ }
25
+ },
26
+ categories: {
27
+ default: {
28
+ appenders: ['dateFile','console'],
29
+ level: 'trace'
30
+ }
31
+ }
32
+ });
33
+
34
+ logger = log4js.getLogger();
35
+ }
36
+
37
+ return logger;
38
+ }
@@ -0,0 +1,71 @@
1
+ const Ajv = require("ajv");
2
+ const ajv = new Ajv();
3
+
4
+ /**
5
+ * API参数验证中间件,合法性校验
6
+ */
7
+ module.exports = (app) => {
8
+ //定义 json-schema 校验规则
9
+ const $schema = "http://json-schema.org/draft-07/schema#";
10
+ return async (ctx, next) => {
11
+ // 只对api请求进行参数校验
12
+ if (ctx.path.indexOf("/api/") < 0) {
13
+ return await next();
14
+ }
15
+
16
+ // 验证请求参数
17
+ const { query, body, headers } = ctx.request;
18
+ const { params, path, method } = ctx;
19
+
20
+ app.logger.info(`[${method}${path}] query: ${JSON.stringify(query)}`);
21
+ app.logger.info(`[${method}${path}] body: ${JSON.stringify(body)}`);
22
+ app.logger.info(`[${method}${path}] headers: ${JSON.stringify(headers)}`);
23
+ app.logger.info(`[${method}${path}] params: ${JSON.stringify(params)}`);
24
+
25
+ const schema = app.routerSchema[path]?.[method.toLowerCase()];
26
+ if (!schema) {
27
+ return await next();
28
+ }
29
+
30
+ let valid = true;
31
+
32
+ // ajv 校验请求参数 校验器
33
+ let validate;
34
+
35
+ // 校验 headers
36
+ if (valid && headers && schema.headers) {
37
+ schema.headers.$schema = $schema;
38
+ validate = ajv.compile(schema.headers);
39
+ valid = validate(headers);
40
+ }
41
+ // 校验 body
42
+ if (valid && body && schema.body) {
43
+ schema.body.$schema = $schema;
44
+ validate = ajv.compile(schema.body);
45
+ valid = validate(body);
46
+ }
47
+ // 校验 query
48
+ if (valid && query && schema.query) {
49
+ schema.query.$schema = $schema;
50
+ validate = ajv.compile(schema.query);
51
+ valid = validate(query);
52
+ }
53
+ // 校验 params
54
+ if (valid && params && schema.params) {
55
+ schema.params.$schema = $schema;
56
+ validate = ajv.compile(schema.params);
57
+ valid = validate(params);
58
+ }
59
+ if (!valid) {
60
+ ctx.status = 200;
61
+ ctx.body = {
62
+ success: false,
63
+ message: `request validate fail: ${ajv.errorsText(validate.errors)}`,
64
+ code: 442,
65
+ };
66
+ return;
67
+ }
68
+
69
+ await next();
70
+ };
71
+ };
@@ -0,0 +1,35 @@
1
+ const md5 = require("md5");
2
+ /**
3
+ * API签名验证中间件,合法性校验
4
+ */
5
+
6
+ module.exports = (app) => {
7
+ return async (ctx, next) => {
8
+ // 只对api请求进行校验
9
+ if (ctx.path.indexOf("/api") < 0) {
10
+ return await next();
11
+ }
12
+ const { path, method } = ctx;
13
+ const { headers } = ctx.request;
14
+ const { s_sign: sSgin, s_t: st } = headers;
15
+
16
+ const signKey = "ahjflkjcklvmlakdujsfwiujz123s"; //随便写的
17
+ const signature = md5(`${signKey}_${st}`);
18
+ app.logger.info(`[${method}${path}] signature: ${signature}`);
19
+ if (
20
+ !sSgin ||
21
+ !st ||
22
+ signature !== sSgin.toLowerCase() ||
23
+ Date.now() - st > 1000 * 600
24
+ ) {
25
+ ctx.status = 200;
26
+ ctx.body = {
27
+ success: false,
28
+ message: "signature not correct or api request timeout !!",
29
+ code: 445,
30
+ };
31
+ return;
32
+ }
33
+ await next();
34
+ };
35
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 运行时 错误处理中间件 ,兜底所有异常
3
+ * @param {*} app
4
+ * @returns
5
+ */
6
+ module.exports = (app) => {
7
+ return async (ctx,next) => {
8
+ try {
9
+ await next();
10
+ } catch (err) {
11
+ // 异常处理
12
+ const {status,message,detail} =err
13
+ app.logger.info(JSON.stringify(err))
14
+ app.logger.error('[-- exception --]:',err)
15
+ app.logger.error('[-- exception --]:',status , message , detail)
16
+
17
+ if(message && message.indexOf('template not found') > -1){
18
+ // 进行页面重定向 301:永久重定向 302:临时重定向
19
+ ctx.status = 302;
20
+ ctx.redirect(`${app.options?.homePage}`);
21
+ return;
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
+ * projectHandler 项目处理中间件
3
+ */
4
+ module.exports = (app) => {
5
+ return async (ctx, next) => {
6
+ // 只对业务API 进行proj_key校验
7
+ if (ctx.path.indexOf("/api/proj/") < 0) {
8
+ return await next();
9
+ }
10
+ // 项目处理中间件
11
+ const { proj_key: projKey } = ctx.request.headers;
12
+
13
+ if (!projKey) {
14
+ ctx.status = 200;
15
+ ctx.body = {
16
+ success: false,
17
+ code: 446,
18
+ msg: "proj_key not found",
19
+ };
20
+ return;
21
+ }
22
+ ctx.projKey = projKey;
23
+
24
+ await next();
25
+ };
26
+ };
@@ -0,0 +1,41 @@
1
+ const path = require("path");
2
+ const koaNunjucks = require("koa-nunjucks-2");
3
+
4
+ module.exports = (app) => {
5
+ // 配置静态根目录
6
+ const koaStatic = require("koa-static");
7
+ app.use(koaStatic(path.resolve(process.cwd(), "./app/public")));
8
+
9
+ // 加载模板引擎
10
+ app.use(
11
+ koaNunjucks({
12
+ ext: "tpl",
13
+ path: path.resolve(process.cwd(), "./app/public"),
14
+ nunjucksConfig: {
15
+ noCache: true,
16
+ trimBlocks: true,
17
+ },
18
+ }),
19
+ );
20
+
21
+ // 引入ctx.body 解析中间件
22
+ const BodyParser = require("koa-bodyparser");
23
+ app.use(
24
+ BodyParser({
25
+ formList: "1000mb",
26
+ enableTypes: ["json", "form-data", "text"],
27
+ }),
28
+ );
29
+
30
+ // 引入错误处理中间件
31
+ app.use(app.middlewares.errorHandler);
32
+
33
+ // 引入API签名验证中间件
34
+ app.use(app.middlewares.apiSignVerify);
35
+
36
+ // 引入API参数验证中间件
37
+ app.use(app.middlewares.apiParamsVerify);
38
+
39
+ // 引入项目处理中间件
40
+ app.use(app.middlewares.projectHandler);
41
+ };
@@ -0,0 +1,13 @@
1
+ html,body {
2
+ margin: 0;
3
+ padding: 0;
4
+ height: 100%;
5
+ }
6
+
7
+ #root {
8
+ height: 100%;
9
+ }
10
+
11
+ input:-webkit-autofill {
12
+ -webkit-box-shadow: 0 0 0 1000px #121212 inset !important;
13
+ }