create-skweb 3.2.0 → 3.3.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/.rollup.cache/Users/stayknight/projects/skweb-mono/tools/create-skweb/dist/index.js +15 -51
- package/.rollup.cache/Users/stayknight/projects/skweb-mono/tools/create-skweb/tsconfig.tsbuildinfo +1 -1
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +12 -0
- package/dist/cli.js +15 -51
- package/dist/index.js +15 -51
- package/package.json +1 -1
- package/src/index.ts +21 -50
- package/templates/cjs/{.env.example.auth-jwks → .env.example} +7 -0
- package/templates/cjs/ecosystem.config.js +2 -2
- package/templates/cjs/package.json +4 -4
- package/templates/cjs/src/controllers/user.js +2 -2
- package/templates/cjs/src/cron-app.js +40 -0
- package/templates/cjs/src/cron-framework.js +44 -0
- package/templates/cjs/src/{swagger.auth-jwks.js → swagger.js} +9 -0
- package/templates/cjs/src/utils/{config.auth-jwks.js → config.js} +18 -0
- package/templates/cjs/src/{app.auth-jwks.js → web-app.js} +34 -21
- package/templates/cjs/src/web-framework.js +23 -0
- package/templates/{ts/.env.example.auth-jwks → mjs/.env.example} +7 -0
- package/templates/mjs/ecosystem.config.js +2 -2
- package/templates/mjs/package.json +4 -4
- package/templates/mjs/src/controllers/user.js +2 -2
- package/templates/mjs/src/cron-app.js +45 -0
- package/templates/mjs/src/cron-framework.js +44 -0
- package/templates/mjs/src/{swagger.auth-jwks.js → swagger.js} +9 -0
- package/templates/mjs/src/utils/{config.auth-jwks.js → config.js} +14 -0
- package/templates/mjs/src/{app.auth-jwks.js → web-app.js} +29 -23
- package/templates/mjs/src/web-framework.js +23 -0
- package/templates/{mjs/.env.example.auth-jwks → ts/.env.example} +7 -0
- package/templates/ts/ecosystem.config.js +2 -2
- package/templates/ts/package.json +4 -4
- package/templates/ts/src/controllers/user.ts +2 -2
- package/templates/ts/src/cron-app.ts +45 -0
- package/templates/ts/src/cron-framework.ts +44 -0
- package/templates/ts/src/{swagger.auth-jwks.ts → swagger.ts} +9 -0
- package/templates/ts/src/utils/{config.auth-jwks.ts → config.ts} +14 -0
- package/templates/ts/src/{app.auth-jwks.ts → web-app.ts} +28 -21
- package/templates/ts/src/web-framework.ts +23 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/templates/cjs/.env.example.auth-static +0 -54
- package/templates/cjs/src/app.auth-static.js +0 -129
- package/templates/cjs/src/cronWorker.js +0 -80
- package/templates/cjs/src/index.js +0 -7
- package/templates/cjs/src/services/database.js +0 -15
- package/templates/cjs/src/swagger.auth-static.js +0 -35
- package/templates/cjs/src/utils/config.auth-static.js +0 -65
- package/templates/mjs/.env.example.auth-static +0 -54
- package/templates/mjs/src/app.auth-static.js +0 -131
- package/templates/mjs/src/cronWorker.js +0 -86
- package/templates/mjs/src/index.js +0 -8
- package/templates/mjs/src/services/database.js +0 -16
- package/templates/mjs/src/swagger.auth-static.js +0 -35
- package/templates/mjs/src/utils/config.auth-static.js +0 -63
- package/templates/ts/.env.example.auth-static +0 -54
- package/templates/ts/src/app.auth-static.ts +0 -131
- package/templates/ts/src/cronWorker.ts +0 -85
- package/templates/ts/src/index.ts +0 -8
- package/templates/ts/src/services/database.ts +0 -16
- package/templates/ts/src/swagger.auth-static.ts +0 -36
- package/templates/ts/src/utils/config.auth-static.ts +0 -68
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# create-skweb
|
|
2
2
|
|
|
3
|
+
## 3.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 修复生成项目问题:.env.example 文件生成、startCronWorker 改为 bootstrap、去除 ts-nocheck 标记、缩短输出文本
|
|
8
|
+
|
|
9
|
+
## 3.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 重构模板结构:入口文件重命名为 web-app/cron-app,新增 web-framework/cron-framework 单例文件,移除冗余的 index 和 services/database
|
|
14
|
+
|
|
3
15
|
## 3.2.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/cli.js
CHANGED
|
@@ -32,29 +32,22 @@ async function createProject(options) {
|
|
|
32
32
|
await fs.mkdirs(path.join(targetDir, fileName));
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const authMatch = fileName.match(/^(.*?)\.auth-(static|jwks)(\.[^.]+)?$/);
|
|
39
|
-
if (authMatch) {
|
|
40
|
-
const [, base, fileAuth, ext] = authMatch;
|
|
41
|
-
if (fileAuth !== auth) {
|
|
42
|
-
// 不匹配用户选择的 auth,跳过
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
outputName = ext ? `${base}${ext}` : base;
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
// 没有 auth- 后缀的 .env.example:跳过(避免与 auth- 版本冲突)
|
|
49
|
-
if (fileName === '.env.example' || fileName.endsWith('/.env.example')) {
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
35
|
+
// 跳过 auth- 后缀的文件(已合并到统一模板)
|
|
36
|
+
if (/\.auth-(static|jwks)(\.[^.]+)?$/.test(fileName)) {
|
|
37
|
+
continue;
|
|
52
38
|
}
|
|
39
|
+
const outputName = fileName;
|
|
53
40
|
let content = await fs.readFile(srcPath, 'utf-8');
|
|
41
|
+
// 处理条件块:{{#if_eq auth "static"}}...{{else}}...{{/if_eq}}
|
|
42
|
+
content = content.replace(/\{\{#if_eq auth "(static|jwks)"\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if_eq\}\}/g, (_match, value, mainContent, elseContent) => {
|
|
43
|
+
return value === auth ? mainContent : (elseContent || '');
|
|
44
|
+
});
|
|
54
45
|
content = content
|
|
55
46
|
.replace(/\{\{name\}\}/g, name)
|
|
56
47
|
.replace(/\{\{description\}\}/g, description)
|
|
57
48
|
.replace(/\{\{auth\}\}/g, auth);
|
|
49
|
+
// 移除 ts-nocheck 标记
|
|
50
|
+
content = content.replace(/^\/\/ @ts-nocheck\s*\n/gm, '');
|
|
58
51
|
const destPath = path.join(targetDir, outputName);
|
|
59
52
|
await fs.writeFile(destPath, content);
|
|
60
53
|
}
|
|
@@ -88,44 +81,15 @@ function printBanner() {
|
|
|
88
81
|
function printSuccess(name, dest) {
|
|
89
82
|
const projectPath = path.join(path.resolve(dest), name);
|
|
90
83
|
console.log(`
|
|
91
|
-
|
|
92
|
-
║ 项目创建成功! ║
|
|
93
|
-
╚════════════════════════════════════════════════════════════════════╝
|
|
94
|
-
|
|
95
|
-
项目名称: ${name}
|
|
96
|
-
项目路径: ${projectPath}
|
|
84
|
+
项目创建成功!
|
|
97
85
|
|
|
98
|
-
|
|
86
|
+
名称: ${name}
|
|
87
|
+
路径: ${projectPath}
|
|
99
88
|
|
|
89
|
+
接下来执行:
|
|
100
90
|
cd ${name}
|
|
101
|
-
|
|
102
|
-
# 复制环境变量模板并按需修改
|
|
103
91
|
cp .env.example .env
|
|
104
|
-
|
|
105
|
-
# 开发模式运行
|
|
106
|
-
npm run start:dev
|
|
107
|
-
|
|
108
|
-
# 生产模式运行 (需要先构建)
|
|
109
|
-
npm run build
|
|
110
|
-
npm run start
|
|
111
|
-
|
|
112
|
-
# 查看日志
|
|
113
|
-
npm run logs
|
|
114
|
-
|
|
115
|
-
# 停止服务
|
|
116
|
-
npm run stop
|
|
117
|
-
|
|
118
|
-
# 重启服务
|
|
119
|
-
npm run restart
|
|
120
|
-
|
|
121
|
-
项目特性:
|
|
122
|
-
✅ TypeScript/MJS/CJS 支持
|
|
123
|
-
✅ @dotenvx/dotenvx 环境变量管理
|
|
124
|
-
✅ PM2 进程管理
|
|
125
|
-
✅ SKWeb Express 集成
|
|
126
|
-
✅ Keycloak/JWKS 鉴权 (可选)
|
|
127
|
-
✅ ESLint 代码检查
|
|
128
|
-
✅ Mocha 测试框架
|
|
92
|
+
npm run web:dev
|
|
129
93
|
`);
|
|
130
94
|
}
|
|
131
95
|
|
package/dist/index.js
CHANGED
|
@@ -27,29 +27,22 @@ async function createProject(options) {
|
|
|
27
27
|
await fs.mkdirs(path.join(targetDir, fileName));
|
|
28
28
|
continue;
|
|
29
29
|
}
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const authMatch = fileName.match(/^(.*?)\.auth-(static|jwks)(\.[^.]+)?$/);
|
|
34
|
-
if (authMatch) {
|
|
35
|
-
const [, base, fileAuth, ext] = authMatch;
|
|
36
|
-
if (fileAuth !== auth) {
|
|
37
|
-
// 不匹配用户选择的 auth,跳过
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
outputName = ext ? `${base}${ext}` : base;
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
// 没有 auth- 后缀的 .env.example:跳过(避免与 auth- 版本冲突)
|
|
44
|
-
if (fileName === '.env.example' || fileName.endsWith('/.env.example')) {
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
30
|
+
// 跳过 auth- 后缀的文件(已合并到统一模板)
|
|
31
|
+
if (/\.auth-(static|jwks)(\.[^.]+)?$/.test(fileName)) {
|
|
32
|
+
continue;
|
|
47
33
|
}
|
|
34
|
+
const outputName = fileName;
|
|
48
35
|
let content = await fs.readFile(srcPath, 'utf-8');
|
|
36
|
+
// 处理条件块:{{#if_eq auth "static"}}...{{else}}...{{/if_eq}}
|
|
37
|
+
content = content.replace(/\{\{#if_eq auth "(static|jwks)"\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if_eq\}\}/g, (_match, value, mainContent, elseContent) => {
|
|
38
|
+
return value === auth ? mainContent : (elseContent || '');
|
|
39
|
+
});
|
|
49
40
|
content = content
|
|
50
41
|
.replace(/\{\{name\}\}/g, name)
|
|
51
42
|
.replace(/\{\{description\}\}/g, description)
|
|
52
43
|
.replace(/\{\{auth\}\}/g, auth);
|
|
44
|
+
// 移除 ts-nocheck 标记
|
|
45
|
+
content = content.replace(/^\/\/ @ts-nocheck\s*\n/gm, '');
|
|
53
46
|
const destPath = path.join(targetDir, outputName);
|
|
54
47
|
await fs.writeFile(destPath, content);
|
|
55
48
|
}
|
|
@@ -83,44 +76,15 @@ function printBanner() {
|
|
|
83
76
|
function printSuccess(name, dest) {
|
|
84
77
|
const projectPath = path.join(path.resolve(dest), name);
|
|
85
78
|
console.log(`
|
|
86
|
-
|
|
87
|
-
║ 项目创建成功! ║
|
|
88
|
-
╚════════════════════════════════════════════════════════════════════╝
|
|
89
|
-
|
|
90
|
-
项目名称: ${name}
|
|
91
|
-
项目路径: ${projectPath}
|
|
79
|
+
项目创建成功!
|
|
92
80
|
|
|
93
|
-
|
|
81
|
+
名称: ${name}
|
|
82
|
+
路径: ${projectPath}
|
|
94
83
|
|
|
84
|
+
接下来执行:
|
|
95
85
|
cd ${name}
|
|
96
|
-
|
|
97
|
-
# 复制环境变量模板并按需修改
|
|
98
86
|
cp .env.example .env
|
|
99
|
-
|
|
100
|
-
# 开发模式运行
|
|
101
|
-
npm run start:dev
|
|
102
|
-
|
|
103
|
-
# 生产模式运行 (需要先构建)
|
|
104
|
-
npm run build
|
|
105
|
-
npm run start
|
|
106
|
-
|
|
107
|
-
# 查看日志
|
|
108
|
-
npm run logs
|
|
109
|
-
|
|
110
|
-
# 停止服务
|
|
111
|
-
npm run stop
|
|
112
|
-
|
|
113
|
-
# 重启服务
|
|
114
|
-
npm run restart
|
|
115
|
-
|
|
116
|
-
项目特性:
|
|
117
|
-
✅ TypeScript/MJS/CJS 支持
|
|
118
|
-
✅ @dotenvx/dotenvx 环境变量管理
|
|
119
|
-
✅ PM2 进程管理
|
|
120
|
-
✅ SKWeb Express 集成
|
|
121
|
-
✅ Keycloak/JWKS 鉴权 (可选)
|
|
122
|
-
✅ ESLint 代码检查
|
|
123
|
-
✅ Mocha 测试框架
|
|
87
|
+
npm run web:dev
|
|
124
88
|
`);
|
|
125
89
|
}
|
|
126
90
|
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -42,31 +42,31 @@ export async function createProject(options: CreateOptions): Promise<void> {
|
|
|
42
42
|
continue
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const authMatch = fileName.match(/^(.*?)\.auth-(static|jwks)(\.[^.]+)?$/)
|
|
49
|
-
if (authMatch) {
|
|
50
|
-
const [, base, fileAuth, ext] = authMatch
|
|
51
|
-
if (fileAuth !== auth) {
|
|
52
|
-
// 不匹配用户选择的 auth,跳过
|
|
53
|
-
continue
|
|
54
|
-
}
|
|
55
|
-
outputName = ext ? `${base}${ext}` : base
|
|
56
|
-
} else {
|
|
57
|
-
// 没有 auth- 后缀的 .env.example:跳过(避免与 auth- 版本冲突)
|
|
58
|
-
if (fileName === '.env.example' || fileName.endsWith('/.env.example')) {
|
|
59
|
-
continue
|
|
60
|
-
}
|
|
45
|
+
// 跳过 auth- 后缀的文件(已合并到统一模板)
|
|
46
|
+
if (/\.auth-(static|jwks)(\.[^.]+)?$/.test(fileName)) {
|
|
47
|
+
continue
|
|
61
48
|
}
|
|
62
49
|
|
|
50
|
+
const outputName = fileName
|
|
51
|
+
|
|
63
52
|
let content = await fs.readFile(srcPath, 'utf-8')
|
|
64
53
|
|
|
54
|
+
// 处理条件块:{{#if_eq auth "static"}}...{{else}}...{{/if_eq}}
|
|
55
|
+
content = content.replace(
|
|
56
|
+
/\{\{#if_eq auth "(static|jwks)"\}\}([\s\S]*?)(?:\{\{else\}\}([\s\S]*?))?\{\{\/if_eq\}\}/g,
|
|
57
|
+
(_match: string, value: string, mainContent: string, elseContent: string) => {
|
|
58
|
+
return value === auth ? mainContent : (elseContent || '')
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
|
|
65
62
|
content = content
|
|
66
63
|
.replace(/\{\{name\}\}/g, name)
|
|
67
64
|
.replace(/\{\{description\}\}/g, description)
|
|
68
65
|
.replace(/\{\{auth\}\}/g, auth)
|
|
69
66
|
|
|
67
|
+
// 移除 ts-nocheck 标记
|
|
68
|
+
content = content.replace(/^\/\/ @ts-nocheck\s*\n/gm, '')
|
|
69
|
+
|
|
70
70
|
const destPath = path.join(targetDir, outputName)
|
|
71
71
|
await fs.writeFile(destPath, content)
|
|
72
72
|
}
|
|
@@ -108,43 +108,14 @@ export function printBanner(): void {
|
|
|
108
108
|
export function printSuccess(name: string, dest: string): void {
|
|
109
109
|
const projectPath = path.join(path.resolve(dest), name)
|
|
110
110
|
console.log(`
|
|
111
|
-
|
|
112
|
-
║ 项目创建成功! ║
|
|
113
|
-
╚════════════════════════════════════════════════════════════════════╝
|
|
111
|
+
项目创建成功!
|
|
114
112
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
接下来可以执行:
|
|
113
|
+
名称: ${name}
|
|
114
|
+
路径: ${projectPath}
|
|
119
115
|
|
|
116
|
+
接下来执行:
|
|
120
117
|
cd ${name}
|
|
121
|
-
|
|
122
|
-
# 复制环境变量模板并按需修改
|
|
123
118
|
cp .env.example .env
|
|
124
|
-
|
|
125
|
-
# 开发模式运行
|
|
126
|
-
npm run start:dev
|
|
127
|
-
|
|
128
|
-
# 生产模式运行 (需要先构建)
|
|
129
|
-
npm run build
|
|
130
|
-
npm run start
|
|
131
|
-
|
|
132
|
-
# 查看日志
|
|
133
|
-
npm run logs
|
|
134
|
-
|
|
135
|
-
# 停止服务
|
|
136
|
-
npm run stop
|
|
137
|
-
|
|
138
|
-
# 重启服务
|
|
139
|
-
npm run restart
|
|
140
|
-
|
|
141
|
-
项目特性:
|
|
142
|
-
✅ TypeScript/MJS/CJS 支持
|
|
143
|
-
✅ @dotenvx/dotenvx 环境变量管理
|
|
144
|
-
✅ PM2 进程管理
|
|
145
|
-
✅ SKWeb Express 集成
|
|
146
|
-
✅ Keycloak/JWKS 鉴权 (可选)
|
|
147
|
-
✅ ESLint 代码检查
|
|
148
|
-
✅ Mocha 测试框架
|
|
119
|
+
npm run web:dev
|
|
149
120
|
`)
|
|
150
121
|
}
|
|
@@ -38,6 +38,12 @@ REDIS_DB=0
|
|
|
38
38
|
# (Production is always disabled to avoid exposing internal API details.)
|
|
39
39
|
SWAGGER_ENABLED=true
|
|
40
40
|
|
|
41
|
+
{{#if_eq auth "static"}}
|
|
42
|
+
# ---------- Auth: static (HS256 shared secret) ----------
|
|
43
|
+
# [REQUIRED in production] Use a strong random string, e.g. `openssl rand -hex 32`
|
|
44
|
+
JWT_SECRET=please-change-this-in-production
|
|
45
|
+
JWT_EXPIRES_IN=7d
|
|
46
|
+
{{else}}
|
|
41
47
|
# ---------- Auth: jwks (OIDC compatible) ----------
|
|
42
48
|
# Works with any OIDC-compliant IdP. Common issuer formats:
|
|
43
49
|
# Keycloak: https://kc.example.com/realms/myrealm
|
|
@@ -50,6 +56,7 @@ SWAGGER_ENABLED=true
|
|
|
50
56
|
OIDC_ISSUER=
|
|
51
57
|
OIDC_AUDIENCE=
|
|
52
58
|
OIDC_JWKS_URI=
|
|
59
|
+
{{/if_eq}}
|
|
53
60
|
|
|
54
61
|
# ---------- Logging ----------
|
|
55
62
|
# error | warn | info | http | verbose | debug | silly
|
|
@@ -2,7 +2,7 @@ module.exports = {
|
|
|
2
2
|
apps: [
|
|
3
3
|
{
|
|
4
4
|
name: '{{name}}-web',
|
|
5
|
-
script: 'src/
|
|
5
|
+
script: 'src/web-app.js',
|
|
6
6
|
instances: 'max',
|
|
7
7
|
exec_mode: 'cluster',
|
|
8
8
|
env: {
|
|
@@ -16,7 +16,7 @@ module.exports = {
|
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
name: '{{name}}-cron',
|
|
19
|
-
script: 'src/
|
|
19
|
+
script: 'src/cron-app.js',
|
|
20
20
|
instances: 1,
|
|
21
21
|
exec_mode: 'fork',
|
|
22
22
|
autorestart: true,
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"restart": "pm2 restart all",
|
|
10
10
|
"delete": "pm2 delete all",
|
|
11
11
|
"logs": "pm2 logs",
|
|
12
|
-
"web:dev": "npx dotenvx run -- node --watch src/
|
|
13
|
-
"cron:dev": "npx dotenvx run -- node --watch src/
|
|
12
|
+
"web:dev": "npx dotenvx run -- node --watch src/web-app.js",
|
|
13
|
+
"cron:dev": "npx dotenvx run -- node --watch src/cron-app.js",
|
|
14
14
|
"seed": "npx dotenvx run -- node src/commands/seed.js",
|
|
15
15
|
"migrate": "npx dotenvx run -- node src/commands/migrate.js",
|
|
16
16
|
"build": "cp -r src dist",
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@dotenvx/dotenvx": "^1.75.1",
|
|
24
24
|
"http-errors": "^2.0.1",
|
|
25
|
-
"skweb-express": "^2.0.
|
|
25
|
+
"skweb-express": "^2.0.4",
|
|
26
26
|
"skweb-express-jwt": "^3.0.0",
|
|
27
27
|
"skweb-express-logger": "^2.0.0",
|
|
28
28
|
"skweb-sequelize": "^2.0.0",
|
|
29
29
|
"skweb-redis": "^2.0.0",
|
|
30
30
|
"skweb-cron": "^2.0.0",
|
|
31
|
-
"skweb-framework": "^2.1.
|
|
31
|
+
"skweb-framework": "^2.1.1",
|
|
32
32
|
"skweb-plugin": "^2.1.1",
|
|
33
33
|
"skweb-util": "^2.0.0",
|
|
34
34
|
"winston": "^3.19.0",
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require('dotenv/config')
|
|
2
|
+
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const { framework } = require('./cron-framework.js')
|
|
5
|
+
const { logger } = require('./utils/logger.js')
|
|
6
|
+
const { checkRequiredEnv } = require('./utils/config.js')
|
|
7
|
+
|
|
8
|
+
async function bootstrap() {
|
|
9
|
+
logger.info('[cron-worker] Starting cron worker process...')
|
|
10
|
+
|
|
11
|
+
if (!checkRequiredEnv()) {
|
|
12
|
+
logger.error('[cron-worker] Configuration validation failed, exiting...')
|
|
13
|
+
process.exit(1)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
await framework.setup({
|
|
17
|
+
modelsDir: path.join(__dirname, './models'),
|
|
18
|
+
cronActionsDir: path.join(__dirname, './cronActions')
|
|
19
|
+
})
|
|
20
|
+
await framework.start()
|
|
21
|
+
logger.info('[cron-worker] Cron worker started')
|
|
22
|
+
|
|
23
|
+
process.on('uncaughtException', (err) => {
|
|
24
|
+
logger.error(`[cron-worker] uncaughtException: ${err.stack || err.message}`)
|
|
25
|
+
process.exit(1)
|
|
26
|
+
})
|
|
27
|
+
process.on('unhandledRejection', (reason) => {
|
|
28
|
+
logger.error(`[cron-worker] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (require.main === module) {
|
|
34
|
+
bootstrap().catch((err) => {
|
|
35
|
+
logger.error('[cron-worker] Fatal error:', err)
|
|
36
|
+
process.exit(1)
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { bootstrap }
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
const { CronFramework } = require('skweb-framework')
|
|
3
|
+
const { logger } = require('./utils/logger.js')
|
|
4
|
+
|
|
5
|
+
const framework = new CronFramework({
|
|
6
|
+
db: {
|
|
7
|
+
host: process.env.DB_HOST || '127.0.0.1',
|
|
8
|
+
port: Number(process.env.DB_PORT) || 3306,
|
|
9
|
+
username: process.env.DB_USER || 'root',
|
|
10
|
+
password: process.env.DB_PASS || '',
|
|
11
|
+
database: process.env.DB_NAME || 'test-project',
|
|
12
|
+
dialect: process.env.DB_DIALECT || 'mysql',
|
|
13
|
+
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
14
|
+
underscored: true,
|
|
15
|
+
logging: process.env.DEBUG_DB === 'true'
|
|
16
|
+
},
|
|
17
|
+
redis: {
|
|
18
|
+
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
19
|
+
port: Number(process.env.REDIS_PORT) || 6379,
|
|
20
|
+
password: process.env.REDIS_PASSWORD || undefined
|
|
21
|
+
},
|
|
22
|
+
channel: 'cron:task:cmd',
|
|
23
|
+
logger,
|
|
24
|
+
onSaveTask: async (taskData) => {
|
|
25
|
+
const SysCronTask = framework.db?.sequelize?.models.SysCronTask
|
|
26
|
+
if (SysCronTask) {
|
|
27
|
+
await SysCronTask.update(
|
|
28
|
+
{ cache: taskData.cache, data: taskData.data },
|
|
29
|
+
{ where: { id: taskData.taskId } }
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
loadTasks: async () => {
|
|
34
|
+
const SysCronTask = framework.db?.sequelize?.models.SysCronTask
|
|
35
|
+
if (!SysCronTask) return []
|
|
36
|
+
return (await SysCronTask.findAll()).map((t) => ({
|
|
37
|
+
id: String(t.id),
|
|
38
|
+
cronTime: t.cronTime,
|
|
39
|
+
method: t.method
|
|
40
|
+
}))
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
module.exports = { framework }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
'use strict'
|
|
2
3
|
|
|
3
4
|
const path = require('path')
|
|
@@ -18,6 +19,13 @@ function buildOpenApiSpec() {
|
|
|
18
19
|
],
|
|
19
20
|
components: {
|
|
20
21
|
securitySchemes: {
|
|
22
|
+
{{#if_eq auth "static"}}
|
|
23
|
+
bearerAuth: {
|
|
24
|
+
type: 'http',
|
|
25
|
+
scheme: 'bearer',
|
|
26
|
+
bearerFormat: 'JWT'
|
|
27
|
+
}
|
|
28
|
+
{{else}}
|
|
21
29
|
oidcAuth: {
|
|
22
30
|
type: 'openIdConnect',
|
|
23
31
|
openIdConnectUrl: process.env.OIDC_JWKS_URI
|
|
@@ -26,6 +34,7 @@ function buildOpenApiSpec() {
|
|
|
26
34
|
? `${process.env.OIDC_ISSUER.replace(/\/$/, '')}/.well-known/openid-configuration`
|
|
27
35
|
: undefined)
|
|
28
36
|
}
|
|
37
|
+
{{/if_eq}}
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
40
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
const { logger } = require('./logger.js')
|
|
2
3
|
|
|
3
4
|
function validateConfig() {
|
|
@@ -38,6 +39,18 @@ function validateConfig() {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
{{#if_eq auth "static"}}
|
|
43
|
+
function checkJwtSecret() {
|
|
44
|
+
if (process.env.JWT_SECRET === 'please-change-this-in-production' || !process.env.JWT_SECRET) {
|
|
45
|
+
if (process.env.NODE_ENV === 'production') {
|
|
46
|
+
logger.error('[config] JWT_SECRET must be set in production!')
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
logger.warn('[config] WARNING: JWT_SECRET is using default value, please change in production!')
|
|
50
|
+
}
|
|
51
|
+
return true
|
|
52
|
+
}
|
|
53
|
+
{{else}}
|
|
41
54
|
function checkOIDCConfig() {
|
|
42
55
|
const issuer = process.env.OIDC_ISSUER
|
|
43
56
|
const jwksUri = process.env.OIDC_JWKS_URI
|
|
@@ -50,6 +63,7 @@ function checkOIDCConfig() {
|
|
|
50
63
|
}
|
|
51
64
|
return true
|
|
52
65
|
}
|
|
66
|
+
{{/if_eq}}
|
|
53
67
|
|
|
54
68
|
function checkRequiredEnv() {
|
|
55
69
|
const result = validateConfig()
|
|
@@ -64,4 +78,8 @@ function checkRequiredEnv() {
|
|
|
64
78
|
return true
|
|
65
79
|
}
|
|
66
80
|
|
|
81
|
+
{{#if_eq auth "static"}}
|
|
82
|
+
module.exports = { validateConfig, checkJwtSecret, checkRequiredEnv }
|
|
83
|
+
{{else}}
|
|
67
84
|
module.exports = { validateConfig, checkOIDCConfig, checkRequiredEnv }
|
|
85
|
+
{{/if_eq}}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
require('dotenv/config')
|
|
2
3
|
|
|
3
4
|
const path = require('path')
|
|
@@ -5,11 +6,25 @@ const express = require('skweb-express').default
|
|
|
5
6
|
const expressLogger = require('skweb-express-logger').default
|
|
6
7
|
const expressErrorHandler = require('skweb-express-logger').errorHandler
|
|
7
8
|
const swaggerUi = require('swagger-ui-express')
|
|
8
|
-
const {
|
|
9
|
+
const { framework } = require('./web-framework.js')
|
|
9
10
|
const { logger } = require('./utils/logger.js')
|
|
10
|
-
const { checkRequiredEnv, checkOIDCConfig } = require('./utils/config.js')
|
|
11
|
+
const { checkRequiredEnv{{#if_eq auth "static"}}, checkJwtSecret{{/if_eq}}{{#if_eq auth "jwks"}}, checkOIDCConfig{{/if_eq}} } = require('./utils/config.js')
|
|
11
12
|
const { buildOpenApiSpec } = require('./swagger.js')
|
|
12
13
|
|
|
14
|
+
{{#if_eq auth "static"}}
|
|
15
|
+
/**
|
|
16
|
+
* JWT 配置(static 模式,HS256 共享密钥)。
|
|
17
|
+
*/
|
|
18
|
+
function buildJwtOptions() {
|
|
19
|
+
return {
|
|
20
|
+
provider: 'static',
|
|
21
|
+
secret: process.env.JWT_SECRET || 'please-change-this-in-production',
|
|
22
|
+
algorithms: ['HS256'],
|
|
23
|
+
sign: { expiresIn: process.env.JWT_EXPIRES_IN || '7d' },
|
|
24
|
+
requestProperty: 'resolvedToken'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
{{else}}
|
|
13
28
|
/**
|
|
14
29
|
* JWT 配置(jwks 模式,通过 OIDC 兼容的 JWKS 端点验证 RS256 签名)。
|
|
15
30
|
*/
|
|
@@ -28,7 +43,12 @@ function buildJwtOptions() {
|
|
|
28
43
|
requestProperty: 'resolvedToken'
|
|
29
44
|
}
|
|
30
45
|
}
|
|
46
|
+
{{/if_eq}}
|
|
31
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Swagger UI 仅在 NODE_ENV 为 development / test,且 SWAGGER_ENABLED !== 'false' 时启用。
|
|
50
|
+
* 生产环境(production / unset)默认禁用,避免暴露内部 API 细节。
|
|
51
|
+
*/
|
|
32
52
|
function shouldEnableSwagger() {
|
|
33
53
|
const env = (process.env.NODE_ENV || '').toLowerCase()
|
|
34
54
|
if (env !== 'development' && env !== 'test') return false
|
|
@@ -43,29 +63,18 @@ async function bootstrap() {
|
|
|
43
63
|
logger.error('[app] Configuration validation failed, exiting...')
|
|
44
64
|
process.exit(1)
|
|
45
65
|
}
|
|
66
|
+
{{#if_eq auth "static"}}
|
|
67
|
+
if (!checkJwtSecret()) {
|
|
68
|
+
logger.error('[app] JWT configuration validation failed, exiting...')
|
|
69
|
+
process.exit(1)
|
|
70
|
+
}
|
|
71
|
+
{{else}}
|
|
46
72
|
if (!checkOIDCConfig()) {
|
|
47
73
|
logger.error('[app] OIDC configuration validation failed, exiting...')
|
|
48
74
|
process.exit(1)
|
|
49
75
|
}
|
|
76
|
+
{{/if_eq}}
|
|
50
77
|
|
|
51
|
-
const framework = new WebFramework({
|
|
52
|
-
db: {
|
|
53
|
-
host: process.env.DB_HOST || '127.0.0.1',
|
|
54
|
-
port: Number(process.env.DB_PORT) || 3306,
|
|
55
|
-
username: process.env.DB_USER || 'root',
|
|
56
|
-
password: process.env.DB_PASS || '',
|
|
57
|
-
database: process.env.DB_NAME || 'test-project',
|
|
58
|
-
dialect: process.env.DB_DIALECT || 'mysql',
|
|
59
|
-
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
60
|
-
underscored: true,
|
|
61
|
-
logging: process.env.DEBUG_DB === 'true' ? (msg) => logger.debug(msg) : false
|
|
62
|
-
},
|
|
63
|
-
redis: {
|
|
64
|
-
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
65
|
-
port: Number(process.env.REDIS_PORT) || 6379,
|
|
66
|
-
password: process.env.REDIS_PASSWORD || undefined
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
78
|
const app = framework.createApp()
|
|
70
79
|
|
|
71
80
|
await framework.setup({
|
|
@@ -86,7 +95,11 @@ async function bootstrap() {
|
|
|
86
95
|
app.setJWT('default', jwtOptions)
|
|
87
96
|
app.setJwtHintHeader('JwtHint')
|
|
88
97
|
|
|
89
|
-
|
|
98
|
+
{{#if_eq auth "static"}}
|
|
99
|
+
logger.info('[app] JWT auth provider: static (HS256)')
|
|
100
|
+
{{else}}
|
|
101
|
+
logger.info(`[app] JWT auth provider: jwks (issuer: ${jwtOptions.jwks?.issuer || jwtOptions.jwks?.jwksUri})`)
|
|
102
|
+
{{/if_eq}}
|
|
90
103
|
|
|
91
104
|
app.use(expressLogger())
|
|
92
105
|
app.use(express.static(path.join(__dirname, '..', 'public')))
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
const { WebFramework } = require('skweb-framework')
|
|
3
|
+
|
|
4
|
+
const framework = new WebFramework({
|
|
5
|
+
db: {
|
|
6
|
+
host: process.env.DB_HOST || '127.0.0.1',
|
|
7
|
+
port: Number(process.env.DB_PORT) || 3306,
|
|
8
|
+
username: process.env.DB_USER || 'root',
|
|
9
|
+
password: process.env.DB_PASS || '',
|
|
10
|
+
database: process.env.DB_NAME || 'test-project',
|
|
11
|
+
dialect: process.env.DB_DIALECT || 'mysql',
|
|
12
|
+
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
13
|
+
underscored: true,
|
|
14
|
+
logging: process.env.DEBUG_DB === 'true'
|
|
15
|
+
},
|
|
16
|
+
redis: {
|
|
17
|
+
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
18
|
+
port: Number(process.env.REDIS_PORT) || 6379,
|
|
19
|
+
password: process.env.REDIS_PASSWORD || undefined
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
module.exports = { framework }
|
|
@@ -38,6 +38,12 @@ REDIS_DB=0
|
|
|
38
38
|
# (Production is always disabled to avoid exposing internal API details.)
|
|
39
39
|
SWAGGER_ENABLED=true
|
|
40
40
|
|
|
41
|
+
{{#if_eq auth "static"}}
|
|
42
|
+
# ---------- Auth: static (HS256 shared secret) ----------
|
|
43
|
+
# [REQUIRED in production] Use a strong random string, e.g. `openssl rand -hex 32`
|
|
44
|
+
JWT_SECRET=please-change-this-in-production
|
|
45
|
+
JWT_EXPIRES_IN=7d
|
|
46
|
+
{{else}}
|
|
41
47
|
# ---------- Auth: jwks (OIDC compatible) ----------
|
|
42
48
|
# Works with any OIDC-compliant IdP. Common issuer formats:
|
|
43
49
|
# Keycloak: https://kc.example.com/realms/myrealm
|
|
@@ -50,6 +56,7 @@ SWAGGER_ENABLED=true
|
|
|
50
56
|
OIDC_ISSUER=
|
|
51
57
|
OIDC_AUDIENCE=
|
|
52
58
|
OIDC_JWKS_URI=
|
|
59
|
+
{{/if_eq}}
|
|
53
60
|
|
|
54
61
|
# ---------- Logging ----------
|
|
55
62
|
# error | warn | info | http | verbose | debug | silly
|