@jayfong/x-server 2.12.14 → 2.12.16
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/lib/_cjs/cli/api_generator.js +20 -54
- package/lib/_cjs/cli/build_util.js +16 -45
- package/lib/_cjs/cli/cli.js +17 -29
- package/lib/_cjs/cli/deploy_util.js +0 -12
- package/lib/_cjs/cli/dev_util.js +18 -0
- package/lib/_cjs/cli/env_util.js +16 -49
- package/lib/_cjs/cli/{register.js → esbuild_register.js} +0 -1
- package/lib/_cjs/cli/template_util.js +26 -29
- package/lib/_cjs/core/define_bus.js +9 -6
- package/lib/_cjs/core/define_cron.js +0 -2
- package/lib/_cjs/core/define_handler.js +10 -12
- package/lib/_cjs/core/define_hook.js +0 -2
- package/lib/_cjs/core/define_server.js +0 -2
- package/lib/_cjs/core/define_task.js +7 -13
- package/lib/_cjs/core/get_handler_url.js +1 -2
- package/lib/_cjs/core/handler.js +8 -29
- package/lib/_cjs/core/http_error.js +0 -3
- package/lib/_cjs/core/http_header.js +8 -12
- package/lib/_cjs/core/server.js +8 -41
- package/lib/_cjs/core/types.js +0 -7
- package/lib/_cjs/index.js +0 -72
- package/lib/_cjs/plugins/cors.js +0 -7
- package/lib/_cjs/plugins/file_parser.js +2 -9
- package/lib/_cjs/plugins/form_body_parser.js +0 -6
- package/lib/_cjs/plugins/ws_parser.js +0 -6
- package/lib/_cjs/plugins/xml_parser.js +0 -12
- package/lib/_cjs/services/cache.js +18 -58
- package/lib/_cjs/services/captcha.js +0 -14
- package/lib/_cjs/services/dingtalk.js +0 -14
- package/lib/_cjs/services/dispose.js +0 -9
- package/lib/_cjs/services/emoji.js +2 -5
- package/lib/_cjs/services/jwt.js +0 -21
- package/lib/_cjs/services/log.js +0 -17
- package/lib/_cjs/services/mail.js +0 -9
- package/lib/_cjs/services/pay.js +9 -40
- package/lib/_cjs/services/rate_limit.js +0 -12
- package/lib/_cjs/services/redis.js +0 -4
- package/lib/_cjs/services/request.js +0 -5
- package/lib/_cjs/services/sensitive_words.js +13 -19
- package/lib/_cjs/x.js +0 -11
- package/lib/cli/api_generator.js +20 -42
- package/lib/cli/build_util.js +16 -19
- package/lib/cli/cli.js +17 -14
- package/lib/cli/deploy_util.js +0 -4
- package/lib/cli/dev_util.d.ts +8 -0
- package/lib/cli/dev_util.js +12 -0
- package/lib/cli/env_util.js +16 -37
- package/lib/cli/template_util.d.ts +1 -0
- package/lib/cli/template_util.js +26 -14
- package/lib/core/define_bus.js +7 -5
- package/lib/core/define_handler.js +10 -9
- package/lib/core/define_task.js +7 -6
- package/lib/core/get_handler_url.js +2 -1
- package/lib/core/handler.js +9 -22
- package/lib/core/http_header.js +6 -9
- package/lib/core/server.js +8 -31
- package/lib/core/types.js +0 -7
- package/lib/index.js +4 -3
- package/lib/plugins/cors.js +0 -3
- package/lib/plugins/file_parser.js +2 -5
- package/lib/plugins/form_body_parser.js +0 -3
- package/lib/plugins/ws_parser.js +0 -3
- package/lib/plugins/xml_parser.js +0 -10
- package/lib/services/cache.js +18 -52
- package/lib/services/captcha.js +0 -7
- package/lib/services/dingtalk.js +0 -7
- package/lib/services/dispose.js +0 -5
- package/lib/services/emoji.js +2 -3
- package/lib/services/jwt.js +0 -13
- package/lib/services/log.js +0 -8
- package/lib/services/mail.js +0 -3
- package/lib/services/pay.js +9 -30
- package/lib/services/rate_limit.js +0 -8
- package/lib/services/redis.js +0 -1
- package/lib/services/request.js +0 -1
- package/lib/services/sensitive_words.js +13 -15
- package/package.json +3 -3
- /package/lib/cli/{register.d.ts → esbuild_register.d.ts} +0 -0
- /package/lib/cli/{register.js → esbuild_register.js} +0 -0
package/lib/cli/build_util.js
CHANGED
|
@@ -8,7 +8,6 @@ import { dedent, uniq } from 'vtils';
|
|
|
8
8
|
export class BuildUtil {
|
|
9
9
|
static async build(options) {
|
|
10
10
|
var _options$minify, _options$inlineEnvs;
|
|
11
|
-
|
|
12
11
|
const mainFile = path.join(options.cwd, 'src/main.ts');
|
|
13
12
|
const pkgFile = path.join(options.cwd, 'package.json');
|
|
14
13
|
const pkgContent = await fs.readJson(pkgFile);
|
|
@@ -20,8 +19,9 @@ export class BuildUtil {
|
|
|
20
19
|
const distNoInstallFile = path.join(distDir, 'no_install.lock');
|
|
21
20
|
const distBundleFile = path.join(tempDir, `app.${pkgContent.version}.tgz`);
|
|
22
21
|
await fs.ensureDir(distDir);
|
|
23
|
-
await fs.ensureDir(tempDir);
|
|
22
|
+
await fs.ensureDir(tempDir);
|
|
24
23
|
|
|
24
|
+
// 处理 external
|
|
25
25
|
const external = uniq(options.external || []);
|
|
26
26
|
const externalWithVersions = (await Promise.all(external.map(async item => {
|
|
27
27
|
try {
|
|
@@ -39,17 +39,17 @@ export class BuildUtil {
|
|
|
39
39
|
version: ''
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
-
}))).filter(item => !!item.version);
|
|
42
|
+
}))).filter(item => !!item.version);
|
|
43
43
|
|
|
44
|
+
// fix: hexoid 构建问题
|
|
44
45
|
try {
|
|
45
46
|
const hexoidPackageJsonFile = require.resolve('hexoid/package.json');
|
|
46
|
-
|
|
47
47
|
const pkg = await fs.readJson(hexoidPackageJsonFile);
|
|
48
48
|
delete pkg.module;
|
|
49
49
|
await fs.writeJson(hexoidPackageJsonFile, pkg);
|
|
50
|
-
} catch {}
|
|
51
|
-
|
|
50
|
+
} catch {}
|
|
52
51
|
|
|
52
|
+
// 构建
|
|
53
53
|
await fs.emptyDir(distDir);
|
|
54
54
|
await esbuild.build({
|
|
55
55
|
entryPoints: [mainFile],
|
|
@@ -75,7 +75,6 @@ export class BuildUtil {
|
|
|
75
75
|
},
|
|
76
76
|
plugins: [{
|
|
77
77
|
name: 'extract-assets',
|
|
78
|
-
|
|
79
78
|
setup(build) {
|
|
80
79
|
build.onLoad({
|
|
81
80
|
filter: /\/svg-captcha\/lib\/option-manager\.js$/
|
|
@@ -114,30 +113,29 @@ export class BuildUtil {
|
|
|
114
113
|
};
|
|
115
114
|
});
|
|
116
115
|
}
|
|
117
|
-
|
|
118
116
|
}]
|
|
119
|
-
});
|
|
117
|
+
});
|
|
120
118
|
|
|
119
|
+
// 兼容 prisma
|
|
121
120
|
const prismaSchemaFile = path.join(options.cwd, 'src/db/schema.prisma');
|
|
122
|
-
|
|
123
121
|
if (await fs.pathExists(prismaSchemaFile)) {
|
|
124
122
|
// 复制 schema.prisma
|
|
125
123
|
const distPrismaSchemaFile = path.join(distDir, path.basename(prismaSchemaFile));
|
|
126
|
-
await fs.copyFile(prismaSchemaFile, distPrismaSchemaFile);
|
|
124
|
+
await fs.copyFile(prismaSchemaFile, distPrismaSchemaFile);
|
|
127
125
|
|
|
126
|
+
// 复制查询引擎
|
|
128
127
|
const libqueryEngineFiles = await globby('libquery_engine-*', {
|
|
129
128
|
cwd: path.join(options.cwd, 'node_modules/.prisma/client'),
|
|
130
129
|
ignore: ['libquery_engine-{darwin,windows}*'],
|
|
131
130
|
onlyFiles: true,
|
|
132
131
|
absolute: true
|
|
133
132
|
});
|
|
134
|
-
|
|
135
133
|
for (const libqueryEngineFile of libqueryEngineFiles) {
|
|
136
134
|
await fs.copyFile(libqueryEngineFile, path.join(distDir, path.basename(libqueryEngineFile)));
|
|
137
135
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
136
|
+
}
|
|
140
137
|
|
|
138
|
+
// 写入 pkg
|
|
141
139
|
const deps = externalWithVersions.reduce((res, item) => {
|
|
142
140
|
res[item.name] = item.version;
|
|
143
141
|
return res;
|
|
@@ -147,8 +145,9 @@ export class BuildUtil {
|
|
|
147
145
|
version: pkgContent.version,
|
|
148
146
|
dependencies: deps
|
|
149
147
|
};
|
|
150
|
-
await fs.writeJSON(distPkgFile, distPkgContent);
|
|
148
|
+
await fs.writeJSON(distPkgFile, distPkgContent);
|
|
151
149
|
|
|
150
|
+
// 写入 pm2 配置
|
|
152
151
|
await fs.writeFile(distPm2File, dedent`
|
|
153
152
|
module.exports = {
|
|
154
153
|
apps: [
|
|
@@ -160,7 +159,6 @@ export class BuildUtil {
|
|
|
160
159
|
],
|
|
161
160
|
}
|
|
162
161
|
`);
|
|
163
|
-
|
|
164
162
|
if (externalWithVersions.length) {
|
|
165
163
|
if (options.noInstall) {
|
|
166
164
|
await fs.writeFile(distNoInstallFile, '');
|
|
@@ -177,10 +175,9 @@ export class BuildUtil {
|
|
|
177
175
|
}
|
|
178
176
|
});
|
|
179
177
|
}
|
|
180
|
-
}
|
|
181
|
-
|
|
178
|
+
}
|
|
182
179
|
|
|
180
|
+
// 打包为 tgz
|
|
183
181
|
await compressing.tgz.compressDir(distDir, distBundleFile);
|
|
184
182
|
}
|
|
185
|
-
|
|
186
183
|
}
|
package/lib/cli/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { ApiGenerator } from "./api_generator";
|
|
|
6
6
|
import { BuildUtil } from "./build_util";
|
|
7
7
|
import { castArray, debounce } from 'vtils';
|
|
8
8
|
import { DeployUtil } from "./deploy_util";
|
|
9
|
+
import { DevUtil } from "./dev_util";
|
|
9
10
|
import { EnvUtil } from "./env_util";
|
|
10
11
|
import { generateManyIndex } from 'vscode-generate-index-standalone';
|
|
11
12
|
import { TemplateUtil } from "./template_util";
|
|
@@ -17,17 +18,19 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
17
18
|
}).positional('npc', {
|
|
18
19
|
describe: 'npc 地址及密钥,格式:地址@密钥',
|
|
19
20
|
type: 'string'
|
|
21
|
+
}).positional('tsx', {
|
|
22
|
+
describe: '使用 tsx 运行',
|
|
23
|
+
type: 'boolean'
|
|
20
24
|
}), async argv => {
|
|
21
25
|
process.env.NODE_ENV = 'development';
|
|
22
26
|
let lastRun;
|
|
23
|
-
|
|
24
27
|
const run = () => {
|
|
25
|
-
lastRun =
|
|
28
|
+
lastRun = DevUtil.runFile({
|
|
29
|
+
file: 'src/main.ts',
|
|
26
30
|
cwd: process.cwd(),
|
|
27
|
-
|
|
31
|
+
runner: argv.tsx ? 'tsx' : 'esbuild-register'
|
|
28
32
|
});
|
|
29
33
|
};
|
|
30
|
-
|
|
31
34
|
if (argv.npc) {
|
|
32
35
|
const [server, vkey] = argv.npc.split('@');
|
|
33
36
|
execa('docker', ['run', '--rm', 'ffdfgdfg/npc:v0.26.9', `-server=${server}`, `-vkey=${vkey}`, '-type=tcp'], {
|
|
@@ -35,10 +38,10 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
35
38
|
stdio: 'inherit'
|
|
36
39
|
});
|
|
37
40
|
}
|
|
38
|
-
|
|
39
41
|
await TemplateUtil.init(process.cwd());
|
|
40
42
|
const watcher = chokidar.watch(['src', '.env', '.env.*'], {
|
|
41
|
-
cwd: process.cwd()
|
|
43
|
+
cwd: process.cwd(),
|
|
44
|
+
ignored: '**/*.test.*'
|
|
42
45
|
});
|
|
43
46
|
watcher.on('all', debounce(async () => {
|
|
44
47
|
await EnvUtil.importFile({
|
|
@@ -58,7 +61,6 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
58
61
|
console.log(`✔️ 索引文件已更新: ${filePath}`);
|
|
59
62
|
}
|
|
60
63
|
});
|
|
61
|
-
|
|
62
64
|
if (lastRun) {
|
|
63
65
|
console.log('==== 重启服务中 ====');
|
|
64
66
|
lastRun.kill();
|
|
@@ -124,15 +126,14 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
124
126
|
await EnvUtil.importFile({
|
|
125
127
|
cwd: process.cwd(),
|
|
126
128
|
file: '.env'
|
|
127
|
-
});
|
|
129
|
+
});
|
|
130
|
+
// 之所以能成功是因为 Prisma 用的 dotenv 默认不会覆盖已经设置的环境变量
|
|
128
131
|
// https://github.com/motdotla/dotenv#override
|
|
129
|
-
|
|
130
132
|
process.env.DATABASE_URL = process.env.DATABASE_ACTION_URL || process.env.DATABASE_URL;
|
|
131
133
|
await execa('tnpx', ['prisma', ...argv._.slice(1), '--schema', 'src/db/schema.prisma'], {
|
|
132
134
|
cwd: process.cwd(),
|
|
133
135
|
stdio: 'inherit'
|
|
134
136
|
});
|
|
135
|
-
|
|
136
137
|
if (!argv.production) {
|
|
137
138
|
await TemplateUtil.initModels(process.cwd());
|
|
138
139
|
}
|
|
@@ -144,6 +145,9 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
144
145
|
describe: '是否生产模式',
|
|
145
146
|
type: 'boolean',
|
|
146
147
|
default: false
|
|
148
|
+
}).positional('tsx', {
|
|
149
|
+
describe: '使用 tsx 运行',
|
|
150
|
+
type: 'boolean'
|
|
147
151
|
}), async argv => {
|
|
148
152
|
process.env.NODE_ENV = argv.production ? 'production' : 'development';
|
|
149
153
|
await EnvUtil.importFile({
|
|
@@ -151,16 +155,16 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
151
155
|
file: '.env'
|
|
152
156
|
});
|
|
153
157
|
process.env.DATABASE_URL = process.env.DATABASE_ACTION_URL || process.env.DATABASE_URL;
|
|
154
|
-
|
|
155
158
|
if (!argv.script) {
|
|
156
159
|
await execa('tnpx', ['select-run'], {
|
|
157
160
|
cwd: process.cwd(),
|
|
158
161
|
stdio: 'inherit'
|
|
159
162
|
});
|
|
160
163
|
} else {
|
|
161
|
-
await
|
|
164
|
+
await DevUtil.runFile({
|
|
165
|
+
file: argv.script,
|
|
162
166
|
cwd: process.cwd(),
|
|
163
|
-
|
|
167
|
+
runner: argv.tsx ? 'tsx' : 'esbuild-register'
|
|
164
168
|
});
|
|
165
169
|
}
|
|
166
170
|
}).command('deploy [type]', '部署', _ => _.positional('type', {
|
|
@@ -172,7 +176,6 @@ yargs.command('dev', '开始开发', _ => _.positional('index', {
|
|
|
172
176
|
cwd: process.cwd(),
|
|
173
177
|
file: '.env.deploy'
|
|
174
178
|
});
|
|
175
|
-
|
|
176
179
|
if (argv.type === 'env') {
|
|
177
180
|
const appEnv = await EnvUtil.parseFileAsMap({
|
|
178
181
|
cwd: process.cwd(),
|
package/lib/cli/deploy_util.js
CHANGED
|
@@ -6,11 +6,9 @@ export class DeployUtil {
|
|
|
6
6
|
static async deploy(options) {
|
|
7
7
|
const pkgContent = await fs.readJson(path.join(options.cwd, 'package.json'));
|
|
8
8
|
const appFile = path.join(options.cwd, `temp/app.${pkgContent.version}.tgz`);
|
|
9
|
-
|
|
10
9
|
if (!(await fs.pathExists(appFile))) {
|
|
11
10
|
throw new Error('请先构建');
|
|
12
11
|
}
|
|
13
|
-
|
|
14
12
|
const ssh = new NodeSSH();
|
|
15
13
|
await ssh.connect({
|
|
16
14
|
host: options.host,
|
|
@@ -44,7 +42,6 @@ export class DeployUtil {
|
|
|
44
42
|
});
|
|
45
43
|
ssh.dispose();
|
|
46
44
|
}
|
|
47
|
-
|
|
48
45
|
static async deployEnv(options) {
|
|
49
46
|
const ssh = new NodeSSH();
|
|
50
47
|
await ssh.connect({
|
|
@@ -68,5 +65,4 @@ export class DeployUtil {
|
|
|
68
65
|
});
|
|
69
66
|
ssh.dispose();
|
|
70
67
|
}
|
|
71
|
-
|
|
72
68
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import execa from 'execa';
|
|
2
|
+
export class DevUtil {
|
|
3
|
+
static runFile(options) {
|
|
4
|
+
return options.runner === 'tsx' ? execa('tsx', [options.file], {
|
|
5
|
+
cwd: options.cwd,
|
|
6
|
+
stdio: 'inherit'
|
|
7
|
+
}) : execa('node', ['-r', require.resolve("./esbuild_register"), options.file], {
|
|
8
|
+
cwd: options.cwd,
|
|
9
|
+
stdio: 'inherit'
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
package/lib/cli/env_util.js
CHANGED
|
@@ -9,27 +9,21 @@ export class EnvUtil {
|
|
|
9
9
|
const [devValue, prodValue] = value.split(/\s+\|\|\s+/);
|
|
10
10
|
return devOrProd(() => EnvUtil.normalizeValue(devValue), () => EnvUtil.normalizeValue(prodValue));
|
|
11
11
|
}
|
|
12
|
-
|
|
13
12
|
return value === 'true' ? true : value === 'false' ? false : value.startsWith('[') && value.endsWith(']') || value.startsWith('{') && value.endsWith('}') ? JSON.parse(value) : isNumeric(value) ? Number(value) : value;
|
|
14
13
|
} else if (value && typeof value === 'object') {
|
|
15
14
|
if (value.dev != null && value.prod != null) {
|
|
16
15
|
return devOrProd(() => value.dev, () => value.prod);
|
|
17
16
|
}
|
|
18
|
-
|
|
19
17
|
return value;
|
|
20
18
|
}
|
|
21
|
-
|
|
22
19
|
return value;
|
|
23
20
|
}
|
|
24
|
-
|
|
25
21
|
static parseContent(src, isYaml = false) {
|
|
26
22
|
const envs = [];
|
|
27
|
-
|
|
28
23
|
if (isYaml) {
|
|
29
24
|
const envObj = yamlParse(src);
|
|
30
25
|
Object.keys(envObj).forEach(key => {
|
|
31
26
|
var _src$match;
|
|
32
|
-
|
|
33
27
|
envs.push({
|
|
34
28
|
key: key,
|
|
35
29
|
value: this.normalizeValue(envObj[key]),
|
|
@@ -38,56 +32,54 @@ export class EnvUtil {
|
|
|
38
32
|
});
|
|
39
33
|
} else {
|
|
40
34
|
// https://github.com/andreialecu/dotenv/blob/feat-multiline/lib/main.js
|
|
41
|
-
const multilineLineBreaks = true;
|
|
35
|
+
const multilineLineBreaks = true;
|
|
42
36
|
|
|
37
|
+
// convert Buffers before splitting into lines and processing
|
|
43
38
|
const lines = src.split(EnvUtil.NEWLINES_MATCH);
|
|
44
39
|
let lastComment = '';
|
|
45
|
-
|
|
46
40
|
for (let idx = 0; idx < lines.length; idx++) {
|
|
47
|
-
let line = lines[idx];
|
|
48
|
-
|
|
49
|
-
const keyValueArr = line.match(EnvUtil.RE_INI_KEY_VAL); // matched?
|
|
41
|
+
let line = lines[idx];
|
|
50
42
|
|
|
43
|
+
// matching "KEY' and 'VAL' in 'KEY=VAL'
|
|
44
|
+
const keyValueArr = line.match(EnvUtil.RE_INI_KEY_VAL);
|
|
45
|
+
// matched?
|
|
51
46
|
if (keyValueArr != null) {
|
|
52
|
-
const key = keyValueArr[1];
|
|
53
|
-
|
|
47
|
+
const key = keyValueArr[1];
|
|
48
|
+
// default undefined or missing values to empty string
|
|
54
49
|
let val = keyValueArr[2] || '';
|
|
55
50
|
let end = val.length - 1;
|
|
56
51
|
const isDoubleQuoted = val[0] === '"' && val[end] === '"';
|
|
57
52
|
const isSingleQuoted = val[0] === "'" && val[end] === "'";
|
|
58
53
|
const isMultilineDoubleQuoted = val[0] === '"' && (val.length === 1 || val[end] !== '"');
|
|
59
|
-
const isMultilineSingleQuoted = val[0] === "'" && (val.length === 1 || val[end] !== "'");
|
|
54
|
+
const isMultilineSingleQuoted = val[0] === "'" && (val.length === 1 || val[end] !== "'");
|
|
60
55
|
|
|
56
|
+
// if parsing line breaks and the value starts with a quote
|
|
61
57
|
if (multilineLineBreaks && (isMultilineDoubleQuoted || isMultilineSingleQuoted)) {
|
|
62
58
|
const quoteChar = isMultilineDoubleQuoted ? '"' : "'";
|
|
63
59
|
val = val.substring(1);
|
|
64
|
-
|
|
65
60
|
while (idx++ < lines.length - 1) {
|
|
66
61
|
line = lines[idx];
|
|
67
62
|
end = line.length - 1;
|
|
68
|
-
|
|
69
63
|
if (line[end] === quoteChar) {
|
|
70
64
|
val += EnvUtil.NEWLINE + line.substring(0, end);
|
|
71
65
|
break;
|
|
72
66
|
}
|
|
73
|
-
|
|
74
67
|
val += EnvUtil.NEWLINE + line;
|
|
75
68
|
}
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
val = dedent(val);
|
|
70
|
+
// if single or double quoted, remove quotes
|
|
78
71
|
} else if (isSingleQuoted || isDoubleQuoted) {
|
|
79
|
-
val = val.substring(1, end);
|
|
72
|
+
val = val.substring(1, end);
|
|
80
73
|
|
|
74
|
+
// if double quoted, expand newlines
|
|
81
75
|
if (isDoubleQuoted) {
|
|
82
76
|
val = val.replace(EnvUtil.RE_NEWLINES, EnvUtil.NEWLINE);
|
|
83
77
|
}
|
|
84
|
-
|
|
85
78
|
val = dedent(val);
|
|
86
79
|
} else {
|
|
87
80
|
// remove surrounding whitespace
|
|
88
81
|
val = dedent(val).trim();
|
|
89
82
|
}
|
|
90
|
-
|
|
91
83
|
envs.push({
|
|
92
84
|
key: key,
|
|
93
85
|
value: EnvUtil.normalizeValue(val),
|
|
@@ -96,58 +88,47 @@ export class EnvUtil {
|
|
|
96
88
|
lastComment = '';
|
|
97
89
|
} else {
|
|
98
90
|
const commentArr = line.match(EnvUtil.COMMENT_MATCH);
|
|
99
|
-
|
|
100
91
|
if (commentArr) {
|
|
101
92
|
lastComment = commentArr[1];
|
|
102
93
|
}
|
|
103
94
|
}
|
|
104
95
|
}
|
|
105
96
|
}
|
|
106
|
-
|
|
107
97
|
return envs;
|
|
108
98
|
}
|
|
109
|
-
|
|
110
99
|
static async parseFile(options) {
|
|
111
100
|
const envFile = path.join(options.cwd, options.file);
|
|
112
101
|
const envYmlFile = path.join(options.cwd, `${options.file}.yml`);
|
|
113
102
|
const envYamlFile = path.join(options.cwd, `${options.file}.yaml`);
|
|
114
103
|
const file = (await fs.pathExists(envYmlFile)) ? envYmlFile : (await fs.pathExists(envYamlFile)) ? envYamlFile : (await fs.pathExists(envFile)) ? envFile : '';
|
|
115
|
-
|
|
116
104
|
if (file) {
|
|
117
105
|
const envContent = await fs.readFile(file, 'utf-8');
|
|
118
106
|
const envs = EnvUtil.parseContent(envContent, /\.ya?ml$/i.test(file));
|
|
119
107
|
return envs;
|
|
120
108
|
}
|
|
121
|
-
|
|
122
109
|
return [];
|
|
123
110
|
}
|
|
124
|
-
|
|
125
111
|
static async parseFileAsMap(options) {
|
|
126
112
|
const envs = await EnvUtil.parseFile(options);
|
|
127
113
|
const envsObj = {};
|
|
128
|
-
|
|
129
114
|
for (const env of envs) {
|
|
130
115
|
envsObj[env.key] = env.value;
|
|
131
116
|
}
|
|
132
|
-
|
|
133
117
|
return envsObj;
|
|
134
118
|
}
|
|
135
|
-
|
|
136
119
|
static async importFile(options) {
|
|
137
120
|
const envs = await EnvUtil.parseFile(options);
|
|
138
121
|
const envsObj = {};
|
|
139
|
-
|
|
140
122
|
for (const env of envs) {
|
|
141
123
|
envsObj[env.key] = env.value;
|
|
142
124
|
process.env[env.key] = env.value;
|
|
143
125
|
}
|
|
144
|
-
|
|
145
|
-
|
|
126
|
+
process.env.X_SERVER_ENVS = JSON.stringify({
|
|
127
|
+
// @ts-ignore
|
|
146
128
|
...JSON.parse(process.env.X_SERVER_ENVS || '{}'),
|
|
147
129
|
...envsObj
|
|
148
130
|
});
|
|
149
131
|
}
|
|
150
|
-
|
|
151
132
|
static makeTypes(envs) {
|
|
152
133
|
return dedent`
|
|
153
134
|
declare namespace NodeJS {
|
|
@@ -168,14 +149,12 @@ export class EnvUtil {
|
|
|
168
149
|
}
|
|
169
150
|
`;
|
|
170
151
|
}
|
|
171
|
-
|
|
172
152
|
static async outputTypes(options) {
|
|
173
153
|
const envs = await EnvUtil.parseFile(options);
|
|
174
154
|
const outFile = path.join(options.cwd, options.outFile);
|
|
175
155
|
const types = EnvUtil.makeTypes(envs);
|
|
176
156
|
await fs.outputFile(outFile, types);
|
|
177
157
|
}
|
|
178
|
-
|
|
179
158
|
}
|
|
180
159
|
EnvUtil.NEWLINE = '\n';
|
|
181
160
|
EnvUtil.RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*(.*)?\s*$/;
|
package/lib/cli/template_util.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
|
+
import globby from 'globby';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { camelCase, dedent, snakeCase, upperFirst } from 'vtils';
|
|
4
5
|
import { generateManyIndex } from 'vscode-generate-index-standalone';
|
|
@@ -9,32 +10,39 @@ export class TemplateUtil {
|
|
|
9
10
|
static async init(cwd) {
|
|
10
11
|
await Promise.all([this.initHelperPackage(cwd), this.initTasks(cwd)]);
|
|
11
12
|
}
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* 初始化辅助包。
|
|
14
16
|
*/
|
|
15
|
-
|
|
16
|
-
|
|
17
17
|
static async initHelperPackage(cwd) {
|
|
18
|
+
const prismaClientFile = TemplateUtil.getPrismaClientTypeFile();
|
|
18
19
|
const fromDir = path.join(__dirname, 'templates');
|
|
19
20
|
const toDir = path.join(cwd, 'node_modules/.x');
|
|
20
|
-
await
|
|
21
|
-
|
|
21
|
+
const fromFiles = await globby('*', {
|
|
22
|
+
cwd: fromDir,
|
|
23
|
+
onlyFiles: true,
|
|
24
|
+
absolute: true
|
|
22
25
|
});
|
|
26
|
+
await fs.ensureDir(toDir);
|
|
27
|
+
await Promise.all(fromFiles.map(async fromFile => {
|
|
28
|
+
const toFile = fromFile.replace(fromDir, toDir);
|
|
29
|
+
let fromFileContent = await fs.readFile(fromFile, 'utf-8');
|
|
30
|
+
fromFileContent = fromFileContent.replaceAll('../.prisma/client/index.d.ts', path.relative(toDir, prismaClientFile)).replaceAll('../../src', path.relative(toDir, `${cwd}/src`));
|
|
31
|
+
await fs.writeFile(toFile, fromFileContent);
|
|
32
|
+
}));
|
|
23
33
|
await generateManyIndex({
|
|
24
34
|
cwd: cwd,
|
|
25
35
|
patterns: ['node_modules/.x/*.ts'],
|
|
26
36
|
replaceFile: true
|
|
27
37
|
});
|
|
28
38
|
}
|
|
39
|
+
|
|
29
40
|
/**
|
|
30
41
|
* 初始化任务。
|
|
31
42
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
43
|
static async initTasks(cwd) {
|
|
35
44
|
const tasksDir = path.join(cwd, 'src/tasks');
|
|
36
45
|
const tasksIndexFile = path.join(tasksDir, 'index.ts');
|
|
37
|
-
|
|
38
46
|
if (!(await fs.pathExists(tasksIndexFile))) {
|
|
39
47
|
await fs.outputFile(tasksIndexFile, dedent`
|
|
40
48
|
// @index(['./**/*.ts', '!**/*.test.ts', '!**/_*'], f => \`export * from '\${f.path}'\`)
|
|
@@ -42,23 +50,21 @@ export class TemplateUtil {
|
|
|
42
50
|
`);
|
|
43
51
|
}
|
|
44
52
|
}
|
|
53
|
+
|
|
45
54
|
/**
|
|
46
55
|
* 初始化模型。
|
|
47
56
|
*/
|
|
48
|
-
|
|
49
|
-
|
|
50
57
|
static async initModels(cwd) {
|
|
51
58
|
const modelsDir = path.join(cwd, 'src/models');
|
|
52
59
|
const indexFile = path.join(modelsDir, 'index.ts');
|
|
53
60
|
const indexFile2 = path.join(cwd, 'node_modules/.x/models.ts');
|
|
54
|
-
const prismaClientFile =
|
|
61
|
+
const prismaClientFile = TemplateUtil.getPrismaClientTypeFile();
|
|
55
62
|
const prismaClientFileContent = await fs.readFile(prismaClientFile, 'utf8');
|
|
56
63
|
const modelNames = [...prismaClientFileContent.match(/(?<=const ModelName:).+?(?=\})/s)[0].matchAll(/(\S+?):/g)].map(match => camelCase(match[1]));
|
|
57
64
|
await Promise.all(modelNames.map(async modelName => {
|
|
58
65
|
const model_name = snakeCase(modelName);
|
|
59
66
|
const ModelName = upperFirst(modelName);
|
|
60
67
|
const modelFile = path.join(modelsDir, `${model_name}.ts`);
|
|
61
|
-
|
|
62
68
|
if (!(await fs.pathExists(modelFile))) {
|
|
63
69
|
await fs.outputFile(modelFile, dedent`
|
|
64
70
|
import { ${ModelName}BaseModel } from '@jayfong/x-server'
|
|
@@ -69,19 +75,25 @@ export class TemplateUtil {
|
|
|
69
75
|
`);
|
|
70
76
|
}
|
|
71
77
|
}));
|
|
72
|
-
|
|
73
78
|
if (!(await fs.pathExists(indexFile))) {
|
|
74
79
|
await fs.outputFile(indexFile, dedent`
|
|
75
80
|
// @index(['./**/*.ts', '!**/*.test.ts', '!**/_*'], f => \`export * from '\${f.path}'\`)
|
|
76
81
|
// @endindex
|
|
77
82
|
`);
|
|
78
83
|
}
|
|
79
|
-
|
|
80
84
|
await generateManyIndex({
|
|
81
85
|
cwd: cwd,
|
|
82
86
|
patterns: [indexFile, indexFile2],
|
|
83
87
|
replaceFile: true
|
|
84
88
|
});
|
|
85
89
|
}
|
|
86
|
-
|
|
90
|
+
static getPrismaClientTypeFile() {
|
|
91
|
+
// const prismaClientFile = path.join(
|
|
92
|
+
// cwd,
|
|
93
|
+
// 'node_modules/.prisma/client/index.d.ts',
|
|
94
|
+
// )
|
|
95
|
+
// 用 require.resolve 以兼容 pnpm
|
|
96
|
+
const prismaClientFile = require.resolve('@prisma/client/package.json').replace('@prisma/client/package.json', '.prisma/client/index.d.ts');
|
|
97
|
+
return prismaClientFile;
|
|
98
|
+
}
|
|
87
99
|
}
|
package/lib/core/define_bus.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
|
|
3
2
|
class EventBus extends EventEmitter {
|
|
4
3
|
onReturnOff(event, listener) {
|
|
5
4
|
this.on(event, listener);
|
|
6
5
|
return () => this.off(event, listener);
|
|
7
6
|
}
|
|
8
|
-
|
|
9
7
|
}
|
|
10
|
-
|
|
11
8
|
export function defineBus() {
|
|
12
9
|
const eventEmitter = new EventBus();
|
|
13
10
|
return eventEmitter;
|
|
14
|
-
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// strict-event-emitter-types
|
|
15
14
|
// https://github.com/bterlson/strict-event-emitter-types/blob/9ed45fc68a69f0b7f85ce4e5fe11ff53cc6a009e/src/index.ts
|
|
15
|
+
|
|
16
16
|
// the overridden signatures need to be assignment compatible, but
|
|
17
17
|
// due to how tuple types work[0] it's not possible to be assignment
|
|
18
18
|
// compatible anymore. This hack fixes it with a unique symbol that
|
|
@@ -20,4 +20,6 @@ export function defineBus() {
|
|
|
20
20
|
//
|
|
21
21
|
// Unfortunately, this has the result of giving a poor error message when
|
|
22
22
|
// you mix up types.
|
|
23
|
-
// 0: https://github.com/Microsoft/TypeScript/issues/26013)
|
|
23
|
+
// 0: https://github.com/Microsoft/TypeScript/issues/26013)
|
|
24
|
+
// TODO: Stash under a symbol key once TS compiler bug is fixed
|
|
25
|
+
// EventEmitter method overrides
|
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
import { Handler } from "./handler";
|
|
2
2
|
export function defineHandler(options) {
|
|
3
|
-
const handler = new Handler({
|
|
3
|
+
const handler = new Handler({
|
|
4
|
+
...options,
|
|
4
5
|
requestMethod: 'POST'
|
|
5
6
|
});
|
|
6
7
|
return handler;
|
|
7
8
|
}
|
|
8
9
|
defineHandler.POST = defineHandler;
|
|
9
|
-
|
|
10
10
|
defineHandler.GET = options => {
|
|
11
|
-
const handler = new Handler({
|
|
11
|
+
const handler = new Handler({
|
|
12
|
+
...options,
|
|
12
13
|
requestMethod: 'GET'
|
|
13
14
|
});
|
|
14
15
|
return handler;
|
|
15
16
|
};
|
|
16
|
-
|
|
17
17
|
defineHandler.FILE = options => {
|
|
18
|
-
const handler = new Handler({
|
|
18
|
+
const handler = new Handler({
|
|
19
|
+
...options,
|
|
19
20
|
requestMethod: 'FILE'
|
|
20
21
|
});
|
|
21
22
|
return handler;
|
|
22
23
|
};
|
|
23
|
-
|
|
24
24
|
defineHandler.WS = options => {
|
|
25
|
-
const handler = new Handler({
|
|
25
|
+
const handler = new Handler({
|
|
26
|
+
...options,
|
|
26
27
|
requestMethod: 'WS'
|
|
27
28
|
});
|
|
28
29
|
return handler;
|
|
29
30
|
};
|
|
30
|
-
|
|
31
31
|
defineHandler.XML = options => {
|
|
32
|
-
const handler = new Handler({
|
|
32
|
+
const handler = new Handler({
|
|
33
|
+
...options,
|
|
33
34
|
requestMethod: 'XML'
|
|
34
35
|
});
|
|
35
36
|
return handler;
|
package/lib/core/define_task.js
CHANGED
|
@@ -4,7 +4,8 @@ import { ms } from 'vtils';
|
|
|
4
4
|
import { x } from "../x";
|
|
5
5
|
export function defineTask(options) {
|
|
6
6
|
const queue = new Queue(options.name, {
|
|
7
|
-
redis: {
|
|
7
|
+
redis: {
|
|
8
|
+
...x.redis.options,
|
|
8
9
|
// https://github.com/OptimalBits/bull/issues/2186
|
|
9
10
|
maxRetriesPerRequest: null,
|
|
10
11
|
enableReadyCheck: false
|
|
@@ -28,7 +29,6 @@ export function defineSliceTask(options) {
|
|
|
28
29
|
handle: async data => {
|
|
29
30
|
const res = data.count ? await x.redis.multi([['lrange', data.redisKey, `-${data.count}`, '-1'], ['ltrim', data.redisKey, '0', `-${data.count + 1}`]]).exec() : await x.redis.multi([['lrange', data.redisKey, '0', '-1'], ['del', data.redisKey]]).exec();
|
|
30
31
|
const list = res[0][1].reverse().map(item => JSON.parse(item));
|
|
31
|
-
|
|
32
32
|
if (list.length) {
|
|
33
33
|
return options.handle(list, data.key);
|
|
34
34
|
}
|
|
@@ -44,9 +44,10 @@ export function defineSliceTask(options) {
|
|
|
44
44
|
assert(duration != null || threshold != null, '参数 threshold 和 duration 必须至少设置 1 个');
|
|
45
45
|
const redisKey = !key ? redisKeyPrefix : `${redisKeyPrefix}_${key}`;
|
|
46
46
|
const res = await x.redis.multi([['llen', redisKey], ['lpush', redisKey, JSON.stringify(data)]]).exec();
|
|
47
|
-
const count = parseInt(res[0][1], 10) + 1;
|
|
48
|
-
// 1分钟内的合并推送
|
|
47
|
+
const count = parseInt(res[0][1], 10) + 1;
|
|
49
48
|
|
|
49
|
+
// 仅时段
|
|
50
|
+
// 1分钟内的合并推送
|
|
50
51
|
if (duration != null) {
|
|
51
52
|
if (count === 1) {
|
|
52
53
|
await task.add({
|
|
@@ -56,7 +57,8 @@ export function defineSliceTask(options) {
|
|
|
56
57
|
delay: duration
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
|
-
}
|
|
60
|
+
}
|
|
61
|
+
// 仅阈值
|
|
60
62
|
// 满10条推送
|
|
61
63
|
else if (threshold != null) {
|
|
62
64
|
if (thresholdTimeout) {
|
|
@@ -70,7 +72,6 @@ export function defineSliceTask(options) {
|
|
|
70
72
|
delay: thresholdTimeout
|
|
71
73
|
});
|
|
72
74
|
}
|
|
73
|
-
|
|
74
75
|
if (count === threshold) {
|
|
75
76
|
await task.add({
|
|
76
77
|
key: key,
|