@x-9lab/xlab 1.0.0-alpha.1
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/LICENSE +21 -0
- package/README.md +182 -0
- package/dist/@config/config.dev.js +13 -0
- package/dist/@config/config.js +25 -0
- package/dist/@config/config.sand.js +10 -0
- package/dist/bin/watch.js +145 -0
- package/dist/bin/xlab +49 -0
- package/dist/business/utils/cookie/1.1.0/clean/index.js +35 -0
- package/dist/business/utils/cookie/1.1.0/clean/js/@tpls/js.tpl +32 -0
- package/dist/business/utils/cookie/1.1.0/clean/js/index.js +30 -0
- package/dist/cluster.js +120 -0
- package/dist/components/assets/index.js +31 -0
- package/dist/components/cache/index.js +9 -0
- package/dist/components/cache/lru.js +75 -0
- package/dist/components/cluster/index.js +40 -0
- package/dist/components/combo/index.js +170 -0
- package/dist/components/common.js +324 -0
- package/dist/components/cron.js +117 -0
- package/dist/components/header/index.js +74 -0
- package/dist/components/header/time.js +37 -0
- package/dist/components/html-processor/index.js +95 -0
- package/dist/components/injection/index.js +83 -0
- package/dist/components/js-processor/index.js +65 -0
- package/dist/components/log.js +112 -0
- package/dist/components/md5/index.js +19 -0
- package/dist/components/mime/index.js +18 -0
- package/dist/components/platform/index.js +103 -0
- package/dist/components/proxy/index.js +62 -0
- package/dist/components/querystring/index.js +60 -0
- package/dist/components/redirect/index.js +79 -0
- package/dist/components/return-code.js +117 -0
- package/dist/components/uuid/index.js +33 -0
- package/dist/components/version/index.js +96 -0
- package/dist/config/getEnv.js +61 -0
- package/dist/config/index.js +137 -0
- package/dist/config/process-custom-config-files.js +67 -0
- package/dist/config/process-def-config-file.js +29 -0
- package/dist/custom.js +44 -0
- package/dist/global.js +67 -0
- package/dist/middleware/@bin/html-filter.js +94 -0
- package/dist/middleware/bad-request.js +36 -0
- package/dist/middleware/combo.js +13 -0
- package/dist/middleware/compress.js +36 -0
- package/dist/middleware/cors.js +66 -0
- package/dist/middleware/fresh-filter.js +63 -0
- package/dist/middleware/handle-pre-dir.js +25 -0
- package/dist/middleware/request-filter.js +144 -0
- package/dist/middleware/service-mark.js +30 -0
- package/dist/middlewares.js +40 -0
- package/dist/package.json +61 -0
- package/dist/router.js +101 -0
- package/dist/server.js +132 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 x-9lab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
X-9lab 通用服务端
|
|
2
|
+
=======
|
|
3
|
+
|
|
4
|
+
## 开发环境配置
|
|
5
|
+
1. 安装 [node](https://nodejs.org/) ,需要 10.0.0 以上版本
|
|
6
|
+
|
|
7
|
+
## 使用
|
|
8
|
+
- 将 `@x-9lab/xlab` 加入到依赖中
|
|
9
|
+
- `package.json` 中调用 `xlab`
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"scripts": {
|
|
13
|
+
"start-dev": "xlab"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
- 除了静态资源,服务端业务需要放在项目根目录下的 `@server` 目录中
|
|
18
|
+
- 支持业务自定义以下内容 (无特殊说明的都是存放在 @server 目录中)
|
|
19
|
+
- 配置项,存放于 `@config` 目录
|
|
20
|
+
- 支持不同环境配置文件
|
|
21
|
+
- 无中间词缀的 config 文件为生产环境配置文件
|
|
22
|
+
- 中间词缀为 `dev` 的 config 文件为开发环境配置文件
|
|
23
|
+
- 中间词缀为`sand` 的 config 文件为沙箱环境配置文件
|
|
24
|
+
- 中间词缀为`lo` 的 config 文件为本地环境配置文件,该文件不应被提交到仓库
|
|
25
|
+
- 配置文件按照中间词缀,合并优先级为 `lo > dev = sand > 无`
|
|
26
|
+
- 开发环境与沙箱环境同时只会根据当前环境取其中一个,因此优先级一样
|
|
27
|
+
- 服务接口,存放于 `business` 目录
|
|
28
|
+
- 接口地址
|
|
29
|
+
- 默认服务接口均以 `api` 开始
|
|
30
|
+
- 按照目录结构生成 api
|
|
31
|
+
- 文件名是 `index` 的会被从 api 上先强行去掉
|
|
32
|
+
- 默认都是 `get` 请求
|
|
33
|
+
- 支持以下形式定义接口
|
|
34
|
+
- 模块返回的是数组,则使用文件名做为方法名,并在 api 上去掉文件名
|
|
35
|
+
- 非数组则文件当作正常的 api 地址
|
|
36
|
+
- 模块返回为函数的则直接绑定为接口处理函数
|
|
37
|
+
- 返回是对象则取对应的字段
|
|
38
|
+
- `method` 接口类型,如 `get` 或 `post`
|
|
39
|
+
- `middleware` 接口中间件
|
|
40
|
+
- `handler` 接口处理函数
|
|
41
|
+
- 自定义逻辑,存放于 `custom` 目录,模块需要导出一个函数作为执行入口
|
|
42
|
+
- 中间件,存放于 `middleware` 目录
|
|
43
|
+
- 定时任务,存放于 `cron` 目录
|
|
44
|
+
- 静态资源,默认存放在项目根目录下的 `public` 目录中
|
|
45
|
+
### 全局对象与函数
|
|
46
|
+
|
|
47
|
+
#### 全局对象
|
|
48
|
+
- `log` 全局日志对象
|
|
49
|
+
- `masterLog` 只在 master 上输出的日志对象
|
|
50
|
+
|
|
51
|
+
#### 全局函数
|
|
52
|
+
- `getApp` 获取应用实例对象
|
|
53
|
+
```ts
|
|
54
|
+
/**
|
|
55
|
+
* 获取应用实例对象
|
|
56
|
+
*/
|
|
57
|
+
function getApp(): Koa;
|
|
58
|
+
```
|
|
59
|
+
- `requireMod` 获取内置组件
|
|
60
|
+
```ts
|
|
61
|
+
/**
|
|
62
|
+
* 获取内置组件
|
|
63
|
+
* @param name 模块名
|
|
64
|
+
* @return 模块对象
|
|
65
|
+
*/
|
|
66
|
+
function requireMod<T extends InternalComponents[K], K extends keyof InternalComponents>(name: K): T;
|
|
67
|
+
```
|
|
68
|
+
- `getSysConfig` 获取系统配置
|
|
69
|
+
- 获取全部配置
|
|
70
|
+
```js
|
|
71
|
+
const config = getSysConfig();
|
|
72
|
+
```
|
|
73
|
+
- 获取指定配置
|
|
74
|
+
```js
|
|
75
|
+
const allowCache = getSysConfig("allowCache");
|
|
76
|
+
```
|
|
77
|
+
- `setSysConfig` 更新系统配置
|
|
78
|
+
```ts
|
|
79
|
+
/**
|
|
80
|
+
* 更新系统配置
|
|
81
|
+
* @param conf 配置项
|
|
82
|
+
*/
|
|
83
|
+
function setSysConfig(conf: XLab.IConfig): void;
|
|
84
|
+
```
|
|
85
|
+
- `requireModel` 全局获取 model 的方法
|
|
86
|
+
- `requireService` 全局获取 service 的方法
|
|
87
|
+
|
|
88
|
+
#### Namespace `XLab`
|
|
89
|
+
`XLab` 提供了一些标准化的定义及系统配置
|
|
90
|
+
- `IStdRes` 标准返回数据
|
|
91
|
+
- `IConfig` 系统配置对象
|
|
92
|
+
- `ICodeItem` 错误信息对象
|
|
93
|
+
- `ICodeDetail` 错误定义
|
|
94
|
+
|
|
95
|
+
### 配置项
|
|
96
|
+
|名称|类型|默认值|说明|
|
|
97
|
+
|-|-|-|-|
|
|
98
|
+
|name|`string`| package.json 中的 name 字段 |服务(应用)名称|
|
|
99
|
+
|version|`string`|package.json 中的 version 字段|版本|
|
|
100
|
+
|env|`string`|development|环境标识|
|
|
101
|
+
|host|`string`| |业务绑定的域名|
|
|
102
|
+
|304|`boolean`| true |是否开启 304 协商缓存|
|
|
103
|
+
|workers|`number`|0|Worker 数量|
|
|
104
|
+
|biServer|`string`||业务服务器地址|
|
|
105
|
+
|protocol|`string`|http|业务服务器协议|
|
|
106
|
+
|debug|`boolean`|false|是否开启 debug 模式|
|
|
107
|
+
|staticMaxage|`number`|1800000|静态文件缓存时间|
|
|
108
|
+
|staticHtmlFileMaxage|`number`|0|静态 html 文件缓存时间|
|
|
109
|
+
|enableComboCache|`boolean`|true|是否开启 Combo 缓存|
|
|
110
|
+
|enableCron|`boolean`|false|是否开启定时任务|
|
|
111
|
+
|middleware|`Array<string | (string | Record<string, any>)[]>`|[]|开启的中间件列表 |
|
|
112
|
+
|custom|`string[]`||自定模块配置|
|
|
113
|
+
|strictSSL|`boolean`||是否启用严格 ssl|
|
|
114
|
+
|apis|`Record<string, string>`|{}|页端注入的 api 设置|
|
|
115
|
+
|hasLo|`boolean`||本地是否存在本地开发配置文件|
|
|
116
|
+
|passExtApis|`boolean`||不处理业务 api|
|
|
117
|
+
|allowCache|`boolean`||是否允许页端缓存|
|
|
118
|
+
|root|`string`|public|静态文件根目录|
|
|
119
|
+
|port|`number`|5000|监听端口|
|
|
120
|
+
|isMaster|`boolean`||是否是主进程|
|
|
121
|
+
|clearLocalStorage|`boolean`||是否每次都强制清除 LocalStorage|
|
|
122
|
+
|pathReplaceRegExp|`string`||处理代理过来多余的地址层级路径替换判断正则|
|
|
123
|
+
|routeMobile|`string`||移动端入口文件地址(旧版逻辑)|
|
|
124
|
+
|launchRouter|`Record<string, string>`||不同端入口地址设置|
|
|
125
|
+
|cron|`{def?: number;}`|{"def": 60}|定时任务设置|
|
|
126
|
+
|injection|`string[]`|[]|注入参数列表|
|
|
127
|
+
|indexPageCacheTime|`number`||首页缓存时间|
|
|
128
|
+
|staticResourceCacheTime|`number`||静态资源缓存时间|
|
|
129
|
+
|
|
130
|
+
## 文件结构
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
├── README.md
|
|
134
|
+
├── business
|
|
135
|
+
│ └── @services
|
|
136
|
+
│ └── @models
|
|
137
|
+
│ │ └── forumUser.js
|
|
138
|
+
│ └── ...
|
|
139
|
+
├── components
|
|
140
|
+
│ ├── cache
|
|
141
|
+
│ ├── common.js
|
|
142
|
+
│ └── ...
|
|
143
|
+
├── @config
|
|
144
|
+
│ ├── config.dev.ts
|
|
145
|
+
│ └── config.ts
|
|
146
|
+
├── middleware
|
|
147
|
+
├── cron
|
|
148
|
+
├── public
|
|
149
|
+
├── test
|
|
150
|
+
├── route
|
|
151
|
+
├── private
|
|
152
|
+
│ └── log
|
|
153
|
+
├── package.json
|
|
154
|
+
├── config.js
|
|
155
|
+
├── router.js
|
|
156
|
+
└── server.js
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 说明
|
|
160
|
+
- ``business目录``:前端业务
|
|
161
|
+
- ``@services目录``:业务services
|
|
162
|
+
- ``@models目录``:存放model
|
|
163
|
+
- ``components目录``: 存放当前项目组件
|
|
164
|
+
- ``@config``:存放环境配置,不同环境在名称与文件后缀中间使用不同代号区分
|
|
165
|
+
- ``middleware目录``:存放请求特殊处理模块,一般作为中间件
|
|
166
|
+
- ``public目录``:存放前端资源文件
|
|
167
|
+
- ``cron目录``:存放定时任务文件
|
|
168
|
+
- ``test目录``:存放单元测试
|
|
169
|
+
- ``route目录``:存放不同平台的路由配置
|
|
170
|
+
- ``private目录``:存放业务 log ,或其他服务端相关的内容
|
|
171
|
+
- ``package.json``:nodejs后端所需要的依赖描述文件,即npm的 [package.json](https://www.npmjs.org/doc/files/package.json.html) 文件
|
|
172
|
+
- ``router.js``:路由模块
|
|
173
|
+
- ``server.js``:服务器主模块
|
|
174
|
+
- ``config.js``:配置处理模块
|
|
175
|
+
|
|
176
|
+
## TODO
|
|
177
|
+
|
|
178
|
+
- [ ] swc 支持 `--out-file-extension` 后转为直接输出 `.mjs` 文件
|
|
179
|
+
1. `packgae.json` 增加 `"type": "module"` 设置
|
|
180
|
+
1. `.swcrc` 中 `module.type` 改为 `es6`
|
|
181
|
+
- [x] 配置文件由 `json` 改为 `ts`
|
|
182
|
+
- [x] 支持各内置模块的完整类型推导
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: ()=>_default
|
|
8
|
+
});
|
|
9
|
+
const CONFIG = {
|
|
10
|
+
"304": false,
|
|
11
|
+
"workers": 0
|
|
12
|
+
};
|
|
13
|
+
const _default = CONFIG;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: ()=>_default
|
|
8
|
+
});
|
|
9
|
+
const CONFIG = {
|
|
10
|
+
"host": "",
|
|
11
|
+
"304": true,
|
|
12
|
+
"workers": 0,
|
|
13
|
+
"biServer": "",
|
|
14
|
+
"protocol": "http",
|
|
15
|
+
"debug": false,
|
|
16
|
+
"staticMaxage": 1800000,
|
|
17
|
+
"staticHtmlFileMaxage": 0,
|
|
18
|
+
"enableComboCache": true,
|
|
19
|
+
"enableCron": false,
|
|
20
|
+
"middleware": [],
|
|
21
|
+
"strictSSL": false,
|
|
22
|
+
"launchRouter": {},
|
|
23
|
+
"root": "public"
|
|
24
|
+
};
|
|
25
|
+
const _default = CONFIG;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const { date } = require("@x-drive/utils");
|
|
3
|
+
const crossSpawn = require("cross-spawn");
|
|
4
|
+
colors = require("colors/safe");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
/**输出内容名称 */ const X_LAB_STR = colors.bold(colors.cyan("🛸 XLab"));
|
|
8
|
+
/**
|
|
9
|
+
* 子进程对象
|
|
10
|
+
* @type child_process.ChildProcess
|
|
11
|
+
*/ var childProcess = null;
|
|
12
|
+
/**
|
|
13
|
+
* 获取 chokidar
|
|
14
|
+
* @return {FSWatcher}
|
|
15
|
+
*/ function getChokidar() {
|
|
16
|
+
try {
|
|
17
|
+
return require("chokidar");
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**构建 Spawn 启动参数 */ function buildSpawnOptions() {
|
|
23
|
+
const argvs = process.argv.slice(2);
|
|
24
|
+
const options = [
|
|
25
|
+
path.resolve(__dirname, "..", "server.js")
|
|
26
|
+
].concat(argvs);
|
|
27
|
+
return options;
|
|
28
|
+
}
|
|
29
|
+
/**停止 */ function stop() {
|
|
30
|
+
if (childProcess) {
|
|
31
|
+
childProcess.kill("SIGTERM");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**启动 */ function start() {
|
|
35
|
+
childProcess = crossSpawn("node", buildSpawnOptions(), {
|
|
36
|
+
"stdio": "inherit"
|
|
37
|
+
}).on("error", (err)=>{
|
|
38
|
+
console.error(err);
|
|
39
|
+
}).on("close", (code)=>{
|
|
40
|
+
if (Number(code) !== 0) {
|
|
41
|
+
const err = new Error(`子进程退出, Code: ${code}`);
|
|
42
|
+
err.code = code;
|
|
43
|
+
// TODO: 自动拉起来?
|
|
44
|
+
console.error(err);
|
|
45
|
+
}
|
|
46
|
+
}).once("exit", ()=>{
|
|
47
|
+
childProcess.removeAllListeners();
|
|
48
|
+
childProcess = null;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**日志 */ function log(...rest) {
|
|
52
|
+
console.log.apply(console, [
|
|
53
|
+
X_LAB_STR,
|
|
54
|
+
colors.green(`[${date(new Date(), "Y-m-d H:i:s.M")}]`),
|
|
55
|
+
...rest
|
|
56
|
+
]);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 初始化状态
|
|
60
|
+
* @type {boolean}
|
|
61
|
+
*/ var inited = false;
|
|
62
|
+
/**
|
|
63
|
+
* @typedef {object} WatchConfig
|
|
64
|
+
* @property {string[]=} paths 需要监控的目录
|
|
65
|
+
* @property {boolean=} enable 监控是否已启用
|
|
66
|
+
*/ /**
|
|
67
|
+
* 启动文件 watch
|
|
68
|
+
* @param {FSWatcher} chokidar
|
|
69
|
+
* @param {WatchConfig} config 监控配置
|
|
70
|
+
* @param {number} wait 间隔时间
|
|
71
|
+
*/ function watch(chokidar, config, wait = 250) {
|
|
72
|
+
let holding = false;
|
|
73
|
+
let initing = true;
|
|
74
|
+
chokidar.watch(config.paths).on("all", ()=>{
|
|
75
|
+
if (inited) {
|
|
76
|
+
if (!holding) {
|
|
77
|
+
holding = true;
|
|
78
|
+
setTimeout(async ()=>{
|
|
79
|
+
holding = false;
|
|
80
|
+
log("Restarting app...\n");
|
|
81
|
+
if (childProcess) {
|
|
82
|
+
childProcess.on("exit", start);
|
|
83
|
+
stop();
|
|
84
|
+
} else {
|
|
85
|
+
start();
|
|
86
|
+
}
|
|
87
|
+
}, wait);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
if (initing) {
|
|
91
|
+
initing = false;
|
|
92
|
+
log("Watch 模式启动\n");
|
|
93
|
+
setTimeout(()=>{
|
|
94
|
+
inited = true;
|
|
95
|
+
}, wait);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 设置 watch 模式
|
|
102
|
+
* @param {WatchConfig} config
|
|
103
|
+
* @param {number} wait
|
|
104
|
+
*/ function setup(config, wait = 250) {
|
|
105
|
+
const chokidar = getChokidar();
|
|
106
|
+
if (config && config.enable) {
|
|
107
|
+
if (chokidar) {
|
|
108
|
+
if (!config.paths) {
|
|
109
|
+
// 默认业务自定义服务端业务文件夹
|
|
110
|
+
const customDir = path.resolve(process.cwd(), "@server");
|
|
111
|
+
config.paths = [
|
|
112
|
+
customDir
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
var stats = 0;
|
|
116
|
+
try {
|
|
117
|
+
for(let i = 0; i < config.paths.length; i++){
|
|
118
|
+
fs.statSync(config.paths[i]);
|
|
119
|
+
stats += 1;
|
|
120
|
+
}
|
|
121
|
+
} catch (e) {
|
|
122
|
+
log(e);
|
|
123
|
+
}
|
|
124
|
+
if (stats > 0) {
|
|
125
|
+
// 传递主进程 SIGTERM
|
|
126
|
+
process.on("SIGTERM", ()=>{
|
|
127
|
+
if (childProcess && childProcess.connected) {
|
|
128
|
+
childProcess.kill("SIGTERM");
|
|
129
|
+
}
|
|
130
|
+
process.exit(0);
|
|
131
|
+
});
|
|
132
|
+
watch(chokidar, config, wait);
|
|
133
|
+
start();
|
|
134
|
+
} else {
|
|
135
|
+
log("业务自定义目录不存在, watch 模式启动失败");
|
|
136
|
+
}
|
|
137
|
+
stats = null;
|
|
138
|
+
} else {
|
|
139
|
+
log("请将 chokidar 安装到开发依赖中以启用 watch 模式");
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
log("配置不存在或 watch.enable 为 false, watch 模式启动失败\n", config);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
module.exports = setup;
|
package/dist/bin/xlab
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const argv = require("minimist")(process.argv.slice(2));
|
|
4
|
+
const { merge } = require("@x-drive/utils");
|
|
5
|
+
const Liftoff = require("liftoff");
|
|
6
|
+
|
|
7
|
+
const cli = new Liftoff({
|
|
8
|
+
"name": "x-lab"
|
|
9
|
+
, "processTitle": "X Lab"
|
|
10
|
+
, "moduleName": "xlab"
|
|
11
|
+
, "configName": "xlab-config"
|
|
12
|
+
, "extensions": {
|
|
13
|
+
".js": null
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const hasWatch = argv.w || argv.watch;
|
|
18
|
+
|
|
19
|
+
cli.launch(
|
|
20
|
+
{
|
|
21
|
+
"cwd": argv.r || argv.root
|
|
22
|
+
, "configPath": argv.f || argv.file
|
|
23
|
+
}
|
|
24
|
+
, function (env) {
|
|
25
|
+
let config = {
|
|
26
|
+
"watch": {
|
|
27
|
+
"enable": false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (env.configPath) {
|
|
31
|
+
try {
|
|
32
|
+
let customConfig = require(env.configPath);
|
|
33
|
+
if (customConfig) {
|
|
34
|
+
merge(config, customConfig);
|
|
35
|
+
}
|
|
36
|
+
} catch (e) { }
|
|
37
|
+
}
|
|
38
|
+
if (hasWatch) {
|
|
39
|
+
config.watch.enable = true;
|
|
40
|
+
}
|
|
41
|
+
if (config.watch && config.watch.enable) {
|
|
42
|
+
const watch = require("./watch");
|
|
43
|
+
watch(config.watch);
|
|
44
|
+
} else {
|
|
45
|
+
const server = require("../server");
|
|
46
|
+
server.boot();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: ()=>_default
|
|
8
|
+
});
|
|
9
|
+
function onClean(ctx) {
|
|
10
|
+
var cookies = ctx.query.c && ctx.query.c.split(",") || null;
|
|
11
|
+
if (!cookies) {
|
|
12
|
+
cookies = [];
|
|
13
|
+
(ctx.headers.cookie || "").split("; ").forEach((item)=>{
|
|
14
|
+
cookies.push(item.split("=")[0]);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
let len = 0;
|
|
18
|
+
if (cookies && cookies.length) {
|
|
19
|
+
let cookiePath = "/";
|
|
20
|
+
let expires = new Date(-1);
|
|
21
|
+
let httpOnly = false;
|
|
22
|
+
cookies.forEach((cookie)=>{
|
|
23
|
+
if (cookie) {
|
|
24
|
+
ctx.cookies.set(cookie, "", {
|
|
25
|
+
"path": cookiePath,
|
|
26
|
+
expires,
|
|
27
|
+
httpOnly
|
|
28
|
+
});
|
|
29
|
+
len += 1;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
ctx.body = `Clean ${len}`;
|
|
34
|
+
}
|
|
35
|
+
const _default = onClean;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="HandheldFriendly" content="true">
|
|
6
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
7
|
+
<meta name="format-detection" content="telephone=no" />
|
|
8
|
+
<script type="text/javascript">
|
|
9
|
+
(function(){
|
|
10
|
+
var el = document.createElement("p");
|
|
11
|
+
var cookies = {cookies};
|
|
12
|
+
var expires = (new Date(-1)).toUTCString();
|
|
13
|
+
var len = 0;
|
|
14
|
+
if (cookies && cookies.length) {
|
|
15
|
+
cookies.forEach(item => {
|
|
16
|
+
if (item) {
|
|
17
|
+
document.cookie = item + "=''; path=/;expires=" + expires;
|
|
18
|
+
len += 1;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
el.innerHTML = "Clean " + len;
|
|
23
|
+
setTimeout(function(){
|
|
24
|
+
document.body.append(el);
|
|
25
|
+
el = null;
|
|
26
|
+
}, 100)
|
|
27
|
+
})()
|
|
28
|
+
</script>
|
|
29
|
+
</head>
|
|
30
|
+
<body>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: ()=>_default
|
|
8
|
+
});
|
|
9
|
+
const _path = /*#__PURE__*/ _interopRequireDefault(require("path"));
|
|
10
|
+
const _fs = /*#__PURE__*/ _interopRequireDefault(require("fs"));
|
|
11
|
+
function _interopRequireDefault(obj) {
|
|
12
|
+
return obj && obj.__esModule ? obj : {
|
|
13
|
+
default: obj
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const JS_TPL = _fs.default.readFileSync(_path.default.resolve(__dirname, "./@tpls/js.tpl"), "utf8");
|
|
17
|
+
function onJsClean(ctx) {
|
|
18
|
+
var cookies = ctx.query.c;
|
|
19
|
+
cookies = cookies && cookies.split(",") || null;
|
|
20
|
+
if (!cookies) {
|
|
21
|
+
cookies = [];
|
|
22
|
+
(ctx.headers.cookie || "").split("; ").forEach((item)=>{
|
|
23
|
+
if (item) {
|
|
24
|
+
cookies.push(item.split("=")[0]);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
ctx.body = JS_TPL.replace("{cookies}", JSON.stringify(cookies));
|
|
29
|
+
}
|
|
30
|
+
const _default = onJsClean;
|
package/dist/cluster.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: ()=>_default
|
|
8
|
+
});
|
|
9
|
+
const _cluster = /*#__PURE__*/ _interopRequireWildcard(require("./components/cluster"));
|
|
10
|
+
const _config = require("./config");
|
|
11
|
+
const _cluster1 = /*#__PURE__*/ _interopRequireDefault(require("cluster"));
|
|
12
|
+
const _server = /*#__PURE__*/ _interopRequireDefault(require("./server"));
|
|
13
|
+
const _os = /*#__PURE__*/ _interopRequireDefault(require("os"));
|
|
14
|
+
function _interopRequireDefault(obj) {
|
|
15
|
+
return obj && obj.__esModule ? obj : {
|
|
16
|
+
default: obj
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
20
|
+
if (typeof WeakMap !== "function") return null;
|
|
21
|
+
var cacheBabelInterop = new WeakMap();
|
|
22
|
+
var cacheNodeInterop = new WeakMap();
|
|
23
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
24
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
25
|
+
})(nodeInterop);
|
|
26
|
+
}
|
|
27
|
+
function _interopRequireWildcard(obj, nodeInterop) {
|
|
28
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
29
|
+
return obj;
|
|
30
|
+
}
|
|
31
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
32
|
+
return {
|
|
33
|
+
default: obj
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
37
|
+
if (cache && cache.has(obj)) {
|
|
38
|
+
return cache.get(obj);
|
|
39
|
+
}
|
|
40
|
+
var newObj = {};
|
|
41
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
42
|
+
for(var key in obj){
|
|
43
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
44
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
45
|
+
if (desc && (desc.get || desc.set)) {
|
|
46
|
+
Object.defineProperty(newObj, key, desc);
|
|
47
|
+
} else {
|
|
48
|
+
newObj[key] = obj[key];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
newObj.default = obj;
|
|
53
|
+
if (cache) {
|
|
54
|
+
cache.set(obj, newObj);
|
|
55
|
+
}
|
|
56
|
+
return newObj;
|
|
57
|
+
}
|
|
58
|
+
const logger = log.getLogger("cluster");
|
|
59
|
+
const CPUnum = _os.default.cpus().length;
|
|
60
|
+
const config = (0, _config.get)();
|
|
61
|
+
/**
|
|
62
|
+
* 消息处理函数
|
|
63
|
+
* @param msg 消息配置对象
|
|
64
|
+
*/ function masterMessageHandler(msg) {
|
|
65
|
+
if (msg && msg.cmd) {
|
|
66
|
+
let handler = _cluster.get(msg.cmd);
|
|
67
|
+
if (handler) {
|
|
68
|
+
let args = msg.args || [];
|
|
69
|
+
args.push(msg.returnData);
|
|
70
|
+
let re = handler.apply(handler, args);
|
|
71
|
+
if (msg.wid) {
|
|
72
|
+
let worker = _cluster1.default.workers[msg.wid];
|
|
73
|
+
if (worker) {
|
|
74
|
+
worker.send({
|
|
75
|
+
"cmd": msg.cmd,
|
|
76
|
+
"data": re,
|
|
77
|
+
"returnData": msg.returnData
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
worker = null;
|
|
81
|
+
}
|
|
82
|
+
args = null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**尝试以 cluster 模式启动服务 */ function start() {
|
|
87
|
+
var server;
|
|
88
|
+
var serverListenConf = {
|
|
89
|
+
"port": config.port
|
|
90
|
+
};
|
|
91
|
+
if (config.ip) {
|
|
92
|
+
serverListenConf.host = config.ip;
|
|
93
|
+
}
|
|
94
|
+
if (config.isMaster) {
|
|
95
|
+
if (config.workers) {
|
|
96
|
+
// 小于使用数则取一半
|
|
97
|
+
let num = CPUnum < config.workers ? Math.ceil(CPUnum / 2) : config.workers;
|
|
98
|
+
for(var i = 0; i < num; i++){
|
|
99
|
+
_cluster1.default.fork();
|
|
100
|
+
}
|
|
101
|
+
// if(!config.isProd){
|
|
102
|
+
_cluster1.default.on("listening", function(worker, address) {
|
|
103
|
+
masterLog("cluster", "info", "\tWorker ", worker.process.pid, " online, Address: ", address.address, ":", address.port);
|
|
104
|
+
});
|
|
105
|
+
_cluster1.default.on("message", masterMessageHandler);
|
|
106
|
+
// }
|
|
107
|
+
_cluster1.default.on("exit", function(worker) {
|
|
108
|
+
logger.warn("Worker " + worker.process.pid + " is dead!");
|
|
109
|
+
_cluster1.default.fork();
|
|
110
|
+
});
|
|
111
|
+
} else {
|
|
112
|
+
server = _server.default.listen(serverListenConf);
|
|
113
|
+
}
|
|
114
|
+
masterLog("cluster", "info", "[%s] online,listening on %s port %d", config.env, config.ip || "localhost", config.port);
|
|
115
|
+
} else {
|
|
116
|
+
server = _server.default.listen(serverListenConf);
|
|
117
|
+
}
|
|
118
|
+
return server;
|
|
119
|
+
}
|
|
120
|
+
const _default = start;
|