@dmqweb/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.
- package/.eslintignore +3 -0
- package/.eslintrc +59 -0
- package/.vscode/settings.json +15 -0
- package/README.md +198 -0
- package/app/controller/base.js +41 -0
- package/app/controller/project.js +98 -0
- package/app/controller/view.js +24 -0
- package/app/extend/logger.js +43 -0
- package/app/middleware/api-params-verify.js +89 -0
- package/app/middleware/api-sign-verify.js +47 -0
- package/app/middleware/error-handler.js +41 -0
- package/app/middleware/project-handler.js +27 -0
- package/app/middleware.js +40 -0
- package/app/pages/asserts/custom.css +12 -0
- package/app/pages/boot.js +59 -0
- package/app/pages/common/curl.js +88 -0
- package/app/pages/common/util.js +3 -0
- package/app/pages/dashboard/complex-view/header-view/complex-view/sub-menu/sub-menu.vue +40 -0
- package/app/pages/dashboard/complex-view/header-view/header-view.vue +141 -0
- package/app/pages/dashboard/complex-view/iframe-view/iframe-view.vue +43 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/search-panel/search-panel.vue +39 -0
- package/app/pages/dashboard/complex-view/schema-view/complex-view/table-panel/table-panel.vue +146 -0
- package/app/pages/dashboard/complex-view/schema-view/components/component-config.js +24 -0
- package/app/pages/dashboard/complex-view/schema-view/components/create-form/create-form.vue +118 -0
- package/app/pages/dashboard/complex-view/schema-view/components/detail-panel/detail-panel.vue +177 -0
- package/app/pages/dashboard/complex-view/schema-view/components/edit-form/edit-form.vue +157 -0
- package/app/pages/dashboard/complex-view/schema-view/hook/schema.js +150 -0
- package/app/pages/dashboard/complex-view/schema-view/schema-view.vue +113 -0
- package/app/pages/dashboard/complex-view/sider-view/complex-view/sub-menu/sub-menu.vue +35 -0
- package/app/pages/dashboard/complex-view/sider-view/sider-view.vue +134 -0
- package/app/pages/dashboard/dashboard.vue +127 -0
- package/app/pages/dashboard/entry.dashboard.js +46 -0
- package/app/pages/store/index.js +5 -0
- package/app/pages/store/menu.js +61 -0
- package/app/pages/store/project.js +13 -0
- package/app/pages/widgets/header-container/asserts/avatar.png +0 -0
- package/app/pages/widgets/header-container/asserts/logo.png +0 -0
- package/app/pages/widgets/header-container/header-container.vue +144 -0
- package/app/pages/widgets/schema-form/complex-view/input/input.vue +165 -0
- package/app/pages/widgets/schema-form/complex-view/input-number/input-number.vue +166 -0
- package/app/pages/widgets/schema-form/complex-view/select/select.vue +144 -0
- package/app/pages/widgets/schema-form/form-item.config.js +24 -0
- package/app/pages/widgets/schema-form/schema-form.vue +144 -0
- package/app/pages/widgets/schema-search-bar/complex-view/date-range/date-range.vue +57 -0
- package/app/pages/widgets/schema-search-bar/complex-view/dynamic-select/dynamic-select.vue +77 -0
- package/app/pages/widgets/schema-search-bar/complex-view/input/input.vue +51 -0
- package/app/pages/widgets/schema-search-bar/complex-view/select/select.vue +58 -0
- package/app/pages/widgets/schema-search-bar/schema-search-bar.vue +138 -0
- package/app/pages/widgets/schema-search-bar/search-item-config.js +27 -0
- package/app/pages/widgets/schema-table/schema-table.vue +254 -0
- package/app/pages/widgets/sider-container/sider-container.vue +32 -0
- package/app/router/business.js +15 -0
- package/app/router/project.js +10 -0
- package/app/router/view.js +11 -0
- package/app/router-schema/business.js +82 -0
- package/app/router-schema/project.js +40 -0
- package/app/service/base.js +13 -0
- package/app/service/project.js +55 -0
- package/app/view/entry.tpl +27 -0
- package/app/webpack/config/blank.js +3 -0
- package/app/webpack/config/webpack.base.js +269 -0
- package/app/webpack/config/webpack.dev.js +61 -0
- package/app/webpack/config/webpack.prod.js +149 -0
- package/app/webpack/dev.js +58 -0
- package/app/webpack/prod.js +21 -0
- package/config/config.default.js +3 -0
- package/elpis-core/env.js +22 -0
- package/elpis-core/index.js +99 -0
- package/elpis-core/loader/config.js +51 -0
- package/elpis-core/loader/controller.js +75 -0
- package/elpis-core/loader/extend.js +54 -0
- package/elpis-core/loader/middleware.js +69 -0
- package/elpis-core/loader/router-schema.js +50 -0
- package/elpis-core/loader/router.js +52 -0
- package/elpis-core/loader/service.js +74 -0
- package/index.js +29 -0
- package/jsconfig.json +16 -0
- package/logs/applocation.log +3 -0
- package/model/index.js +119 -0
- package/package.json +93 -0
- package/test/controller/project.test.js +216 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const merge = require('webpack-merge')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const os = require('os')
|
|
4
|
+
const HappyPack = require('happypack')
|
|
5
|
+
|
|
6
|
+
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
|
7
|
+
const CleanWebpackPlugin = require('clean-webpack-plugin')
|
|
8
|
+
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
|
|
9
|
+
const HtmlWebpackInjectAttributesPlugin = require('html-webpack-inject-attributes-plugin')
|
|
10
|
+
const TerserWebpackPlugin = require('terser-webpack-plugin')
|
|
11
|
+
|
|
12
|
+
// 多线程 build 配置
|
|
13
|
+
const happypackCommonConfig = {
|
|
14
|
+
// 是否开启调试模式,设为false表示不输出调试信息
|
|
15
|
+
debug: false,
|
|
16
|
+
// 创建一个线程池,大小为CPU核心数,用于并行处理任务
|
|
17
|
+
threadPool: HappyPack.ThreadPool({ size: os.cpus().length }),
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
// 基类配置
|
|
22
|
+
const baseConfig = require('./webpack.base.js')
|
|
23
|
+
|
|
24
|
+
// 生产环境配置
|
|
25
|
+
const webpackProdConfig = merge(baseConfig, {
|
|
26
|
+
mode: 'production',
|
|
27
|
+
|
|
28
|
+
output: {
|
|
29
|
+
// 定义输出文件名格式:在 js 目录下生成 [入口名称]_[8位chunkhash].bundle.js
|
|
30
|
+
filename: 'js/[name]_[chunkhash:8].bundle.js',
|
|
31
|
+
// 定义输出文件的绝对路径:当前工作目录下的 ./app/public/dist/prod
|
|
32
|
+
path: path.join(process.cwd(), './app/public/dist/prod/'),
|
|
33
|
+
// 指定浏览器访问资源的公共路径前缀
|
|
34
|
+
publicPath: '/dist/prod/',
|
|
35
|
+
// 设置跨域加载脚本时的 credentials 标志为 anonymous(不发送 cookies 等)
|
|
36
|
+
crossOriginLoading: 'anonymous'
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
module: {
|
|
40
|
+
rules: [{
|
|
41
|
+
test: /\.css$/,
|
|
42
|
+
use: [
|
|
43
|
+
// 1. 提取 loader(负责提取 CSS)
|
|
44
|
+
MiniCssExtractPlugin.loader,
|
|
45
|
+
// 2. CSS loader(负责解析 CSS)
|
|
46
|
+
`${require.resolve('happypack/loader')}?id=css`
|
|
47
|
+
]
|
|
48
|
+
}, {
|
|
49
|
+
test: /\.js$/,
|
|
50
|
+
include: [
|
|
51
|
+
// 处理elpis目录中的文件
|
|
52
|
+
path.resolve(__dirname, '../../pages'),
|
|
53
|
+
// 处理业务目录文件
|
|
54
|
+
path.resolve(process.cwd(), './app/pages'),
|
|
55
|
+
],
|
|
56
|
+
use: [
|
|
57
|
+
`${require.resolve('happypack/loader')}?id=js`,
|
|
58
|
+
]
|
|
59
|
+
}]
|
|
60
|
+
},
|
|
61
|
+
// webpack 不会有大量 hits 信息, 默认为 warning
|
|
62
|
+
performance: {
|
|
63
|
+
hints: false
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
plugins: [
|
|
67
|
+
// 每次build前 , 清空 public/dist 目录
|
|
68
|
+
new CleanWebpackPlugin(['public/dist'], {
|
|
69
|
+
// 设置清理操作的根目录为 ./app 目录
|
|
70
|
+
root: path.resolve(process.cwd(),'./app'),
|
|
71
|
+
// 指定不需要删除的文件或目录(空数组表示全部删除)
|
|
72
|
+
exclude: [],
|
|
73
|
+
// 启用详细日志输出,显示删除过程
|
|
74
|
+
verbose: true,
|
|
75
|
+
// 设置为 false 表示执行实际删除操作(true 为模拟删除)
|
|
76
|
+
dry: false
|
|
77
|
+
}),
|
|
78
|
+
|
|
79
|
+
// 提取 css 公共部分,有效利用缓存 (非公共部分使用 inline )
|
|
80
|
+
new MiniCssExtractPlugin({
|
|
81
|
+
// 异步加载的CSS文件
|
|
82
|
+
chunkFilename: 'css/[name].[contenthash:8].bundle.css',
|
|
83
|
+
}),
|
|
84
|
+
|
|
85
|
+
// 优化并压缩css资源
|
|
86
|
+
new CssMinimizerPlugin(),
|
|
87
|
+
|
|
88
|
+
// 多线程打包 js ,加快打包速度
|
|
89
|
+
new HappyPack({
|
|
90
|
+
// 展开通用配置,包含调试设置和线程池配置
|
|
91
|
+
...happypackCommonConfig,
|
|
92
|
+
|
|
93
|
+
// 指定loader的ID,用于在rules中引用
|
|
94
|
+
id: 'js',
|
|
95
|
+
|
|
96
|
+
// 配置需要使用的loader
|
|
97
|
+
loaders: [`${require.resolve('babel-loader')}?${JSON.stringify({
|
|
98
|
+
// Babel预设配置,用于转换ES6+语法到兼容性更好的ES5
|
|
99
|
+
presets: [require.resolve('@babel/preset-env')],
|
|
100
|
+
// Babel插件配置
|
|
101
|
+
plugins: [
|
|
102
|
+
// 优化Babel生成的代码,减少重复的帮助函数代码
|
|
103
|
+
require.resolve('@babel/plugin-transform-runtime')
|
|
104
|
+
]
|
|
105
|
+
})}`]
|
|
106
|
+
}),
|
|
107
|
+
|
|
108
|
+
// 多线程打包 css
|
|
109
|
+
new HappyPack({
|
|
110
|
+
...happypackCommonConfig,
|
|
111
|
+
id: 'css',
|
|
112
|
+
loaders: [{
|
|
113
|
+
path: require.resolve('css-loader'),
|
|
114
|
+
options: {
|
|
115
|
+
importLoaders: 1
|
|
116
|
+
}
|
|
117
|
+
}]
|
|
118
|
+
}),
|
|
119
|
+
|
|
120
|
+
// 浏览器在请求资源时,不会发送用户的身份凭证
|
|
121
|
+
new HtmlWebpackInjectAttributesPlugin({
|
|
122
|
+
// 确保加载外部资源时不携带身份凭证
|
|
123
|
+
crossorigin: 'anonymous'
|
|
124
|
+
})
|
|
125
|
+
],
|
|
126
|
+
|
|
127
|
+
optimization: {
|
|
128
|
+
// 使用 TerserPlugin 的并发和缓存,提升压缩阶段的性能
|
|
129
|
+
|
|
130
|
+
// 启用代码压缩功能
|
|
131
|
+
minimize: true,
|
|
132
|
+
|
|
133
|
+
// 定义用于执行代码压缩的插件
|
|
134
|
+
minimizer: [
|
|
135
|
+
new TerserWebpackPlugin({
|
|
136
|
+
parallel: true, // 启用缓存来加速构建过程
|
|
137
|
+
cache: true, // 利用多核 CPU 的优势来提升构建速度
|
|
138
|
+
terserOptions: {
|
|
139
|
+
compress: {
|
|
140
|
+
drop_console: true, // 删除所有 console.* 语句
|
|
141
|
+
drop_debugger: true // 删除所有 debugger 语句
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
module.exports = webpackProdConfig
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// 本地开发启动 deserver
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const consoler = require('consoler');
|
|
4
|
+
const webpack = require('webpack');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const devMiddleware = require('webpack-dev-middleware');
|
|
7
|
+
const hotMiddleware = require('webpack-hot-middleware');
|
|
8
|
+
|
|
9
|
+
module.exports = ()=>{
|
|
10
|
+
const {
|
|
11
|
+
webpackDevConfig,
|
|
12
|
+
DEV_SERVER_CONFIG
|
|
13
|
+
} = require('./config/webpack.dev.js');
|
|
14
|
+
|
|
15
|
+
const app = express();
|
|
16
|
+
|
|
17
|
+
const compiler = webpack(webpackDevConfig);
|
|
18
|
+
|
|
19
|
+
// 构建静态文件目录
|
|
20
|
+
app.use(express.static(path.join(__dirname, '../public/dist')))
|
|
21
|
+
|
|
22
|
+
// 引用 devMiddleware 中间件 监控文件改动
|
|
23
|
+
app.use(devMiddleware(compiler, {
|
|
24
|
+
// 落地文件
|
|
25
|
+
writeToDisk: (filePath) => {
|
|
26
|
+
// 只有以.tpl结尾的文件才写入磁盘,其余文件存储在内存中
|
|
27
|
+
return filePath.endsWith('.tpl')
|
|
28
|
+
},
|
|
29
|
+
// 资源路径
|
|
30
|
+
publicPath: webpackDevConfig.output.publicPath,
|
|
31
|
+
|
|
32
|
+
// headers 配置
|
|
33
|
+
headers: {
|
|
34
|
+
'Access-Control-Allow-Origin': '*',
|
|
35
|
+
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
|
36
|
+
'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization'
|
|
37
|
+
},
|
|
38
|
+
stats:{
|
|
39
|
+
colors: true
|
|
40
|
+
}
|
|
41
|
+
}))
|
|
42
|
+
|
|
43
|
+
// 引用 hotMiddleware 中间件 实现热更新通讯
|
|
44
|
+
app.use(hotMiddleware(compiler, {
|
|
45
|
+
path: `${DEV_SERVER_CONFIG.HMR_PATH}`,
|
|
46
|
+
log: () => {
|
|
47
|
+
}
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
consoler.info('请等待webpack初次构建完成提示......')
|
|
51
|
+
|
|
52
|
+
// 启动devserver服务
|
|
53
|
+
const post = DEV_SERVER_CONFIG.PORT;
|
|
54
|
+
app.listen(post, () => {
|
|
55
|
+
consoler.info(`webpack-dev-server is listening on port ${post}`)
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const webpack = require('webpack');
|
|
2
|
+
const webpackProdConfig = require('./config/webpack.prod.js')
|
|
3
|
+
|
|
4
|
+
console.log('\n 开始构建... \n')
|
|
5
|
+
|
|
6
|
+
module.exports = ()=>{
|
|
7
|
+
webpack(webpackProdConfig, (err, stats) => {
|
|
8
|
+
if (err) {
|
|
9
|
+
console.log(err)
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
process.stdout.write(`${stats.toString({
|
|
14
|
+
colors: true, // 在控制台输出色彩信息
|
|
15
|
+
modules: false, // 不显示模块信息
|
|
16
|
+
children: false, // 不显示子模块信息
|
|
17
|
+
chunks: false, // 不显示 chunk 信息
|
|
18
|
+
chunkModules: true // 显示代码块中模块信息
|
|
19
|
+
})}\n`)
|
|
20
|
+
})
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module.exports = (app) => {
|
|
2
|
+
return {
|
|
3
|
+
// 判断是否本地环境
|
|
4
|
+
isLocal() {
|
|
5
|
+
return process.env._ENV === 'local'
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
// 判断是否测试环境
|
|
9
|
+
isBeta() {
|
|
10
|
+
return process.env._ENV === 'beta'
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
// 判断是否生产环境
|
|
14
|
+
isProduction() {
|
|
15
|
+
return process.env._ENV === 'production'
|
|
16
|
+
},
|
|
17
|
+
// 获取当前环境
|
|
18
|
+
get() {
|
|
19
|
+
return process.env._ENV ?? 'local'
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// 引入koa框架
|
|
2
|
+
const Koa = require('koa');
|
|
3
|
+
// 引入路径处理模块
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { sep } = path; // 处理不同操作系统的斜杠
|
|
6
|
+
|
|
7
|
+
// 引入环境配置
|
|
8
|
+
const env = require('./env');
|
|
9
|
+
|
|
10
|
+
// 引入加载器
|
|
11
|
+
const configLoader = require('./loader/config');
|
|
12
|
+
const extendLoader = require('./loader/extend');
|
|
13
|
+
const middlewareLoader = require('./loader/middleware');
|
|
14
|
+
const routerSchemaLoader = require('./loader/router-schema');
|
|
15
|
+
const serviceLoader = require('./loader/service');
|
|
16
|
+
const controllerLoader = require('./loader/controller');
|
|
17
|
+
const routerLoader = require('./loader/router');
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
/**
|
|
21
|
+
* 启动项目
|
|
22
|
+
* @params options 项目配置
|
|
23
|
+
* options{
|
|
24
|
+
* name // 项目名称
|
|
25
|
+
* homePath // 项目首页
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
start (options = {}) {
|
|
29
|
+
// 创建koa实例
|
|
30
|
+
const app = new Koa();
|
|
31
|
+
// 应用配置
|
|
32
|
+
app.options = options;
|
|
33
|
+
|
|
34
|
+
// 设置基础路径为当前工作目录 = 根目录
|
|
35
|
+
app.baseDir = process.cwd();
|
|
36
|
+
|
|
37
|
+
// 业务文件路径的绝对路径 = 根目录 + app目录 的绝对路径
|
|
38
|
+
app.businessPath = path.resolve(app.baseDir,`.${sep}app`);
|
|
39
|
+
|
|
40
|
+
// 初始化环境配置
|
|
41
|
+
app.env = env();
|
|
42
|
+
console.log(`-- [start] env: ${app.env.get()} --`);
|
|
43
|
+
|
|
44
|
+
// 加载配置
|
|
45
|
+
configLoader(app);
|
|
46
|
+
console.log(`-- [start] config done --`);
|
|
47
|
+
|
|
48
|
+
// 加载扩展
|
|
49
|
+
extendLoader(app);
|
|
50
|
+
console.log(`-- [start] extend done --`);
|
|
51
|
+
|
|
52
|
+
// 加载中间件
|
|
53
|
+
middlewareLoader(app);
|
|
54
|
+
console.log(`-- [start] middleware done --`);
|
|
55
|
+
|
|
56
|
+
// 加载路由配置
|
|
57
|
+
routerSchemaLoader(app);
|
|
58
|
+
console.log(`-- [start] routerSchema done --`);
|
|
59
|
+
|
|
60
|
+
// 加载服务
|
|
61
|
+
serviceLoader(app);
|
|
62
|
+
console.log(`-- [start] service done --`);
|
|
63
|
+
|
|
64
|
+
// 加载控制器
|
|
65
|
+
controllerLoader(app);
|
|
66
|
+
console.log(`-- [start] controller done --`);
|
|
67
|
+
|
|
68
|
+
// 注册elpis全局中间件
|
|
69
|
+
const elpisMiddlewarePath = path.resolve(__dirname,`..${sep}app${sep}middleware.js`);
|
|
70
|
+
const elpisMiddleware = require(elpisMiddlewarePath);
|
|
71
|
+
elpisMiddleware(app);
|
|
72
|
+
console.log(`-- [start] load global elpis middleware done --`);
|
|
73
|
+
|
|
74
|
+
// 注册业务全局中间件
|
|
75
|
+
try {
|
|
76
|
+
require(`${app.businessPath}${sep}middleware.js`)(app);
|
|
77
|
+
console.log(`-- [start] load global business middleware done --`);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
console.log(`-- [exception] there is no global business middleware file --`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 加载路由
|
|
83
|
+
routerLoader(app);
|
|
84
|
+
console.log(`-- [start] router done --`);
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
// 启动服务
|
|
88
|
+
try {
|
|
89
|
+
const port = process.env.PORT || 8080;
|
|
90
|
+
const host = process.env.HOST || '0.0.0.0';
|
|
91
|
+
app.listen(port, host);
|
|
92
|
+
console.log(`Server running on port:${port}`);
|
|
93
|
+
} catch(e) {
|
|
94
|
+
console.error(e);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return app;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const { sep } = path
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* config loader
|
|
6
|
+
* @param {object} app koa 实例
|
|
7
|
+
*
|
|
8
|
+
* 通过区别 本地/测试/生产,通过env环境读取不同文件配置 env.config
|
|
9
|
+
* 通过env.config 覆盖 default.config 加载到 app.config 中
|
|
10
|
+
*
|
|
11
|
+
* 目录下对应的 config配置
|
|
12
|
+
* 默认配置 config/config.default.js
|
|
13
|
+
* 本地配置 config/config.local.js
|
|
14
|
+
* 测试配置 config/config.beta.js
|
|
15
|
+
* 生产配置 config/config.prod.js
|
|
16
|
+
*/
|
|
17
|
+
module.exports = (app) => {
|
|
18
|
+
// elpis内部的默认配置文件(内部只有一个默认配置)
|
|
19
|
+
const elpisConfigPath = path.resolve(__dirname, `..${sep}..${sep}config`);
|
|
20
|
+
let defaultConfig = require(path.resolve(elpisConfigPath,`.${sep}config.default.js`));
|
|
21
|
+
// 找到config目录
|
|
22
|
+
const businessConfigPath = path.resolve(app.baseDir, `.${sep}config`);
|
|
23
|
+
// 业务配置、并且集成elpis默认配置
|
|
24
|
+
try {
|
|
25
|
+
defaultConfig = {
|
|
26
|
+
...defaultConfig,
|
|
27
|
+
...require(path.resolve(businessConfigPath, `.${sep}config.default.js`))
|
|
28
|
+
};
|
|
29
|
+
} catch (e) {
|
|
30
|
+
console.log(`[exception] 未配置 default.config.js)`);
|
|
31
|
+
// console.log(`[exception] there is no default.config.js:${e.message})`);
|
|
32
|
+
// console.debug(`[exception] Stack trace: ${e.stack}`);
|
|
33
|
+
}
|
|
34
|
+
// 获取env.config
|
|
35
|
+
let envConfig = {};
|
|
36
|
+
try {
|
|
37
|
+
if (app.env.isLocal()) { // 本地
|
|
38
|
+
envConfig = require(path.resolve(businessConfigPath, `.${sep}config.local.js`));
|
|
39
|
+
} else if (app.env.isBeta()) { // 测试
|
|
40
|
+
envConfig = require(path.resolve(businessConfigPath, `.${sep}config.beta.js`));
|
|
41
|
+
} else if (app.env.isProduction()) { // 生产
|
|
42
|
+
envConfig = require(path.resolve(businessConfigPath, `.${sep}config.prod.js`));
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
const envName = app.env.isLocal() ? 'local' : app.env.isBeta() ? 'beta' : 'prod';
|
|
46
|
+
console.log(`[exception] Failed to load config.${envName}.js: ${e.message}`);
|
|
47
|
+
console.debug(`[exception] Stack trace: ${e.stack}`);
|
|
48
|
+
}
|
|
49
|
+
// 覆盖并加载 config 配置
|
|
50
|
+
app.config = Object.assign({}, defaultConfig, envConfig);
|
|
51
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const glob = require('glob')
|
|
3
|
+
const { sep } = path
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* controller loader
|
|
7
|
+
* @param {object} app Koa 实例
|
|
8
|
+
*
|
|
9
|
+
* 加载所有 controller 可通过 'app.controller.${目录}.${文件名}' 访问'
|
|
10
|
+
*
|
|
11
|
+
* 例如
|
|
12
|
+
* app/controller
|
|
13
|
+
* |
|
|
14
|
+
* | -- custom-module
|
|
15
|
+
* |
|
|
16
|
+
* | -- custom-controller.js
|
|
17
|
+
* => app.controller.customModule.customController
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
module.exports = (app) => {
|
|
21
|
+
const controller = {};
|
|
22
|
+
|
|
23
|
+
// elpis中的controller的加载
|
|
24
|
+
const elpisControllerPath = path.resolve(__dirname, `..${sep}..${sep}app${sep}controller`);
|
|
25
|
+
// 使用glob模式匹配查找所有嵌套目录下的.js文件 (**表示任意层级子目录)
|
|
26
|
+
const elpisControllerFileList = glob.sync(path.resolve(elpisControllerPath, `.${sep}**${sep}**.js`));
|
|
27
|
+
elpisControllerFileList.forEach(handleFile);
|
|
28
|
+
|
|
29
|
+
// 拼接控制器文件所在目录的完整路径 (如: D:\Elpis\app\controller)
|
|
30
|
+
const businessControllerPath = path.resolve(app.businessPath, `.${sep}controller`);
|
|
31
|
+
// 使用glob模式匹配查找所有嵌套目录下的.js文件 (**表示任意层级子目录)
|
|
32
|
+
const businessControllerFileList = glob.sync(path.resolve(businessControllerPath, `.${sep}**${sep}**.js`));
|
|
33
|
+
|
|
34
|
+
// 遍历所有文件目录,把内容加载到app.controller 下
|
|
35
|
+
businessControllerFileList.forEach(handleFile);
|
|
36
|
+
|
|
37
|
+
function handleFile(file) {
|
|
38
|
+
// 提取文件名
|
|
39
|
+
let name = path.resolve(file);
|
|
40
|
+
|
|
41
|
+
// 截取路径 => app/controller/custom-module/custom-controller.js => custom-module/custom-controller
|
|
42
|
+
name = name.substring(name.lastIndexOf(`controller${sep}`) + `controller${sep}`.length, name.lastIndexOf('.'))
|
|
43
|
+
|
|
44
|
+
// 把'-'统一成驼峰式,custom-module/custom-controller => customModule/customController
|
|
45
|
+
name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());
|
|
46
|
+
|
|
47
|
+
// 挂载 controller 到内容 app 对象中
|
|
48
|
+
|
|
49
|
+
// 创建临时引用指向控制器容器,用于构建嵌套结构
|
|
50
|
+
let tempController = controller
|
|
51
|
+
// 将控制器路径按分隔符分割成数组,例如 'user/auth' => ['user'(目录), 'auth'(文件)]
|
|
52
|
+
const names = name.split(sep);
|
|
53
|
+
// 遍历路径数组,构建层次化的对象结构
|
|
54
|
+
for (let i = 0; i < names.length; i++){
|
|
55
|
+
// 判断是否为最后一层
|
|
56
|
+
if (i === names.length - 1){
|
|
57
|
+
// 创建控制器实例对象
|
|
58
|
+
// 1. 加载控制器模块并传入app实例,获取返回的控制器类
|
|
59
|
+
const ControllerModule = require(path.resolve(file))(app);
|
|
60
|
+
// 2. 实例化控制器类,创建具体的控制器对象实例
|
|
61
|
+
tempController[names[i]] = new ControllerModule();
|
|
62
|
+
}else{
|
|
63
|
+
// 处理控制器目录结构
|
|
64
|
+
// 如果当前层级的对象不存在,则创建空对象
|
|
65
|
+
if (!tempController[names[i]]){
|
|
66
|
+
tempController[names[i]] = {}
|
|
67
|
+
}
|
|
68
|
+
// 将临时引用指向下一层级,继续构建更深层的结构
|
|
69
|
+
tempController = tempController[names[i]]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
// 挂载 middlewares 到内容 app 对象中
|
|
74
|
+
app.controller = controller;
|
|
75
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const path = require("path")
|
|
2
|
+
const glob = require("glob")
|
|
3
|
+
const {sep} = path
|
|
4
|
+
/**
|
|
5
|
+
* entend loader
|
|
6
|
+
* @param {object} app Koa 实例
|
|
7
|
+
*
|
|
8
|
+
* 加载所有 extend, 可通过 'app.extend.${文件}访问'
|
|
9
|
+
*
|
|
10
|
+
* 例子:
|
|
11
|
+
* app/extend
|
|
12
|
+
* |
|
|
13
|
+
* | -- custom-extend.js
|
|
14
|
+
* => app.extend.customExtend 访问
|
|
15
|
+
*/
|
|
16
|
+
module.exports = (app) => {
|
|
17
|
+
|
|
18
|
+
// 拼接elpis内部的extend
|
|
19
|
+
const elpisExtendPath = path.resolve(__dirname, `..${sep}..${sep}app${sep}extend`)
|
|
20
|
+
// 使用glob模式匹配查找extend目录下的所有.js文件
|
|
21
|
+
const elpisExtendFileList = glob.sync(path.resolve(elpisExtendPath, `.${sep}**${sep}**.js`))
|
|
22
|
+
elpisExtendFileList.forEach(handleFileList);
|
|
23
|
+
|
|
24
|
+
// 拼接extend目录的完整路径 (如: D:\Elpis\app\extend)
|
|
25
|
+
const businessExtendPath = path.resolve(app.businessPath, `.${sep}extend`)
|
|
26
|
+
// 使用glob模式匹配查找extend目录下的所有.js文件
|
|
27
|
+
const businessExtendFileList = glob.sync(path.resolve(businessExtendPath, `.${sep}**${sep}**.js`))
|
|
28
|
+
|
|
29
|
+
// 遍历所有找到的扩展文件
|
|
30
|
+
businessExtendFileList.forEach(handleFileList);
|
|
31
|
+
function handleFileList(file) {
|
|
32
|
+
// 提取文件的完整路径
|
|
33
|
+
let name = path.resolve(file)
|
|
34
|
+
|
|
35
|
+
// 截取路径 => app/extend/custom-extend.js => custom-extend
|
|
36
|
+
name = name.substring(name.indexOf(`extend${sep}`) + `extend${sep}`.length, name.indexOf('.'))
|
|
37
|
+
|
|
38
|
+
// 把'-'换成驼峰式,custom-extend => customExtend
|
|
39
|
+
name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase())
|
|
40
|
+
|
|
41
|
+
// 检查app中是否已存在同名属性,避免覆盖
|
|
42
|
+
for(const key in app){
|
|
43
|
+
if(key === name){
|
|
44
|
+
console.log(`[extend load error] name: ${name} is already in app `)
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// 加载扩展模块并传入app实例,将返回结果挂载到app上
|
|
49
|
+
// 例如: app.customExtend = require('custom-extend.js')(app)
|
|
50
|
+
app[name] = require(path.resolve(file))(app)
|
|
51
|
+
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const glob = require('glob')
|
|
3
|
+
const { sep } = path
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* middleware loader
|
|
7
|
+
* @param {object} app Koa 实例
|
|
8
|
+
*
|
|
9
|
+
* 加载所有 middleware 可通过 'app.middlewares.${目录}.${文件名}' 访问'
|
|
10
|
+
*
|
|
11
|
+
* 例如
|
|
12
|
+
* app/middleware
|
|
13
|
+
* |
|
|
14
|
+
* | -- custom-moudle
|
|
15
|
+
* |
|
|
16
|
+
* | -- costom-middleware.js
|
|
17
|
+
* => app.middlewares.customMoudle.costomMiddleware
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
module.exports = (app) => {
|
|
21
|
+
const middlewares = {};
|
|
22
|
+
// 读取/elpis/app/middleware/**/**.js下的所有文件
|
|
23
|
+
const elpisMiddlePath = path.resolve(__dirname, `..${sep}..${sep}app${sep}middleware`);
|
|
24
|
+
// 使用glob模式匹配查找所有嵌套目录下的.js文件 (**表示任意层级子目录)
|
|
25
|
+
const elpisFileList = glob.sync(path.resolve(elpisMiddlePath, `.${sep}**${sep}**.js`));
|
|
26
|
+
elpisFileList.forEach(handleFile)
|
|
27
|
+
|
|
28
|
+
const businessMiddleWarePath = path.resolve(app.businessPath, `.${sep}middleware`);
|
|
29
|
+
// 使用glob模式匹配查找所有嵌套目录下的.js文件 (**表示任意层级子目录)
|
|
30
|
+
const businessFileList = glob.sync(path.resolve(businessMiddleWarePath, `.${sep}**${sep}**.js`));
|
|
31
|
+
|
|
32
|
+
// 遍历所有文件目录,把内容加载到app.middlewares下
|
|
33
|
+
businessFileList.forEach(handleFile);
|
|
34
|
+
function handleFile(file) {
|
|
35
|
+
// 提取文件名
|
|
36
|
+
let name = path.resolve(file);
|
|
37
|
+
|
|
38
|
+
// 截取路径 => app/middleware/custom-moudle/costom-middleware.js => custom-moudle/costom-middleware
|
|
39
|
+
name = name.substring(name.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length, name.lastIndexOf('.'))
|
|
40
|
+
|
|
41
|
+
// 把'-'统一成驼峰式,custom-moudle/costom-middleware => customMoudle/costomMiddleware
|
|
42
|
+
name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());
|
|
43
|
+
|
|
44
|
+
// 挂载 middlewares 到内容 app 对象中
|
|
45
|
+
|
|
46
|
+
// 创建临时引用指向中间件容器,用于构建嵌套结构
|
|
47
|
+
let tempMiddleware = middlewares
|
|
48
|
+
// 将中间件路径按分隔符分割成数组,例如 'user/auth' => ['user', 'auth']
|
|
49
|
+
const names = name.split(sep);
|
|
50
|
+
// 遍历路径数组,构建层次化的对象结构
|
|
51
|
+
for (let i = 0; i < names.length; i++){
|
|
52
|
+
// 判断是否为最后一层
|
|
53
|
+
if (i === names.length - 1){
|
|
54
|
+
// 创建中间件对象
|
|
55
|
+
tempMiddleware[names[i]] = require(path.resolve(file))(app);
|
|
56
|
+
}else{
|
|
57
|
+
// 处理中间层目录结构
|
|
58
|
+
// 如果当前层级的对象不存在,则创建空对象
|
|
59
|
+
if (!tempMiddleware[names[i]]){
|
|
60
|
+
tempMiddleware[names[i]] = {}
|
|
61
|
+
}
|
|
62
|
+
// 将临时引用指向下一层级,继续构建更深层的结构
|
|
63
|
+
tempMiddleware = tempMiddleware[names[i]]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// 挂载 middlewares 到内容 app 对象中
|
|
68
|
+
app.middlewares = middlewares;
|
|
69
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const glob = require('glob')
|
|
3
|
+
const { sep } = path
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* router-schema loader
|
|
7
|
+
* @param {object} app koa 实例
|
|
8
|
+
*
|
|
9
|
+
* 通过 'json-schema' & 'ajv' 对 API 规则进行约束, 配合 api-params-verify 中间件使用
|
|
10
|
+
*
|
|
11
|
+
* app/router-schema/**.js
|
|
12
|
+
*
|
|
13
|
+
* 输出:
|
|
14
|
+
* app.rouSchema = {
|
|
15
|
+
* '${api1}': ${jsonSschema},
|
|
16
|
+
* '${api2}': ${jsonSschema},
|
|
17
|
+
* '${api3}': ${jsonSschema},
|
|
18
|
+
* }
|
|
19
|
+
*/
|
|
20
|
+
module.exports = (app) => {
|
|
21
|
+
// 注册所有 router-schema, 使得可以 'app.routerSchema' 这样访问
|
|
22
|
+
// 初始化空对象,用于存储所有路由规则
|
|
23
|
+
let routerSchema = {}
|
|
24
|
+
|
|
25
|
+
// 拼接中间件文件所在目录的完整路径
|
|
26
|
+
const elpisRouterPath = path.resolve(__dirname, `..${sep}..${sep}app${sep}router-schema`);
|
|
27
|
+
// 使用glob模式匹配查找所有嵌套目录下的.js文件 (**表示任意层级子目录)
|
|
28
|
+
const elpisRouterFileList = glob.sync(path.resolve(elpisRouterPath, `.${sep}**${sep}*.js`))
|
|
29
|
+
elpisRouterFileList.forEach(handleFile);
|
|
30
|
+
// 拼接中间件文件所在目录的完整路径
|
|
31
|
+
const businessRouterSchemaPath = path.resolve(app.businessPath, `.${sep}router-schema`)
|
|
32
|
+
// 使用glob模式匹配查找所有嵌套目录下的.js文件 (**表示任意层级子目录)
|
|
33
|
+
const businessFileList = glob.sync(path.resolve(businessRouterSchemaPath, `.${sep}**${sep}*.js`))
|
|
34
|
+
|
|
35
|
+
// 遍历所有找到的路由规则文件
|
|
36
|
+
businessFileList.forEach(handleFile);
|
|
37
|
+
function handleFile(file) {
|
|
38
|
+
if(!file) return;
|
|
39
|
+
// 将当前文件中的路由规则合并到 routerSchema 对象中
|
|
40
|
+
routerSchema = {
|
|
41
|
+
// 展开已有的路由规则(保持之前文件中已加载的规则)
|
|
42
|
+
...routerSchema,
|
|
43
|
+
|
|
44
|
+
// 展开当前文件导出的路由规则(添加新规则)
|
|
45
|
+
...require(path.resolve(file))
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
app.routerSchema = routerSchema
|
|
50
|
+
}
|