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.
Files changed (60) hide show
  1. package/.rollup.cache/Users/stayknight/projects/skweb-mono/tools/create-skweb/dist/index.js +15 -51
  2. package/.rollup.cache/Users/stayknight/projects/skweb-mono/tools/create-skweb/tsconfig.tsbuildinfo +1 -1
  3. package/.turbo/turbo-build.log +4 -4
  4. package/CHANGELOG.md +12 -0
  5. package/dist/cli.js +15 -51
  6. package/dist/index.js +15 -51
  7. package/package.json +1 -1
  8. package/src/index.ts +21 -50
  9. package/templates/cjs/{.env.example.auth-jwks → .env.example} +7 -0
  10. package/templates/cjs/ecosystem.config.js +2 -2
  11. package/templates/cjs/package.json +4 -4
  12. package/templates/cjs/src/controllers/user.js +2 -2
  13. package/templates/cjs/src/cron-app.js +40 -0
  14. package/templates/cjs/src/cron-framework.js +44 -0
  15. package/templates/cjs/src/{swagger.auth-jwks.js → swagger.js} +9 -0
  16. package/templates/cjs/src/utils/{config.auth-jwks.js → config.js} +18 -0
  17. package/templates/cjs/src/{app.auth-jwks.js → web-app.js} +34 -21
  18. package/templates/cjs/src/web-framework.js +23 -0
  19. package/templates/{ts/.env.example.auth-jwks → mjs/.env.example} +7 -0
  20. package/templates/mjs/ecosystem.config.js +2 -2
  21. package/templates/mjs/package.json +4 -4
  22. package/templates/mjs/src/controllers/user.js +2 -2
  23. package/templates/mjs/src/cron-app.js +45 -0
  24. package/templates/mjs/src/cron-framework.js +44 -0
  25. package/templates/mjs/src/{swagger.auth-jwks.js → swagger.js} +9 -0
  26. package/templates/mjs/src/utils/{config.auth-jwks.js → config.js} +14 -0
  27. package/templates/mjs/src/{app.auth-jwks.js → web-app.js} +29 -23
  28. package/templates/mjs/src/web-framework.js +23 -0
  29. package/templates/{mjs/.env.example.auth-jwks → ts/.env.example} +7 -0
  30. package/templates/ts/ecosystem.config.js +2 -2
  31. package/templates/ts/package.json +4 -4
  32. package/templates/ts/src/controllers/user.ts +2 -2
  33. package/templates/ts/src/cron-app.ts +45 -0
  34. package/templates/ts/src/cron-framework.ts +44 -0
  35. package/templates/ts/src/{swagger.auth-jwks.ts → swagger.ts} +9 -0
  36. package/templates/ts/src/utils/{config.auth-jwks.ts → config.ts} +14 -0
  37. package/templates/ts/src/{app.auth-jwks.ts → web-app.ts} +28 -21
  38. package/templates/ts/src/web-framework.ts +23 -0
  39. package/tsconfig.tsbuildinfo +1 -1
  40. package/templates/cjs/.env.example.auth-static +0 -54
  41. package/templates/cjs/src/app.auth-static.js +0 -129
  42. package/templates/cjs/src/cronWorker.js +0 -80
  43. package/templates/cjs/src/index.js +0 -7
  44. package/templates/cjs/src/services/database.js +0 -15
  45. package/templates/cjs/src/swagger.auth-static.js +0 -35
  46. package/templates/cjs/src/utils/config.auth-static.js +0 -65
  47. package/templates/mjs/.env.example.auth-static +0 -54
  48. package/templates/mjs/src/app.auth-static.js +0 -131
  49. package/templates/mjs/src/cronWorker.js +0 -86
  50. package/templates/mjs/src/index.js +0 -8
  51. package/templates/mjs/src/services/database.js +0 -16
  52. package/templates/mjs/src/swagger.auth-static.js +0 -35
  53. package/templates/mjs/src/utils/config.auth-static.js +0 -63
  54. package/templates/ts/.env.example.auth-static +0 -54
  55. package/templates/ts/src/app.auth-static.ts +0 -131
  56. package/templates/ts/src/cronWorker.ts +0 -85
  57. package/templates/ts/src/index.ts +0 -8
  58. package/templates/ts/src/services/database.ts +0 -16
  59. package/templates/ts/src/swagger.auth-static.ts +0 -36
  60. 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
- // 处理 auth-后缀:例如 src/app.auth-static.js -> 生成 src/app.js
36
- // .env.example.auth-static -> 生成 .env.example
37
- let outputName = fileName;
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
- // 处理 auth-后缀:例如 src/app.auth-static.js -> 生成 src/app.js
31
- // .env.example.auth-static -> 生成 .env.example
32
- let outputName = fileName;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-skweb",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "Create a new SKWeb project with TypeScript, CJS, or MJS support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
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
- // 处理 auth-后缀:例如 src/app.auth-static.js -> 生成 src/app.js
46
- // .env.example.auth-static -> 生成 .env.example
47
- let outputName = fileName
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
- 项目名称: ${name}
116
- 项目路径: ${projectPath}
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/index.js',
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/cronWorker.js',
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/index.js",
13
- "cron:dev": "npx dotenvx run -- node --watch src/cronWorker.js",
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.3",
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.0",
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",
@@ -1,7 +1,7 @@
1
1
  const createHttpError = require('http-errors')
2
- const { db } = require('../services/database.js')
2
+ const { framework } = require('../web-framework.js')
3
3
 
4
- const { SysUser } = db.sequelize.models
4
+ const { SysUser } = framework.models
5
5
 
6
6
  /**
7
7
  * @swagger
@@ -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 { WebFramework } = require('skweb-framework')
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
- logger.info(`[app] JWT auth provider: jwks (issuer: ${jwtOptions.jwks.issuer || jwtOptions.jwks.jwksUri})`)
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