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
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
require('dotenv/config')
|
|
2
|
-
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const express = require('skweb-express').default
|
|
5
|
-
const expressLogger = require('skweb-express-logger').default
|
|
6
|
-
const expressErrorHandler = require('skweb-express-logger').errorHandler
|
|
7
|
-
const swaggerUi = require('swagger-ui-express')
|
|
8
|
-
const { WebFramework } = require('skweb-framework')
|
|
9
|
-
const { logger } = require('./utils/logger.js')
|
|
10
|
-
const { checkRequiredEnv, checkJwtSecret } = require('./utils/config.js')
|
|
11
|
-
const { buildOpenApiSpec } = require('./swagger.js')
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* JWT 配置(static 模式,HS256 共享密钥)。
|
|
15
|
-
*/
|
|
16
|
-
function buildJwtOptions() {
|
|
17
|
-
return {
|
|
18
|
-
provider: 'static',
|
|
19
|
-
secret: process.env.JWT_SECRET || 'please-change-this-in-production',
|
|
20
|
-
algorithms: ['HS256'],
|
|
21
|
-
sign: { expiresIn: process.env.JWT_EXPIRES_IN || '7d' },
|
|
22
|
-
requestProperty: 'resolvedToken'
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Swagger UI 仅在 NODE_ENV 为 development / test,且 SWAGGER_ENABLED !== 'false' 时启用。
|
|
28
|
-
* 生产环境(production / unset)默认禁用,避免暴露内部 API 细节。
|
|
29
|
-
*/
|
|
30
|
-
function shouldEnableSwagger() {
|
|
31
|
-
const env = (process.env.NODE_ENV || '').toLowerCase()
|
|
32
|
-
if (env !== 'development' && env !== 'test') return false
|
|
33
|
-
if ((process.env.SWAGGER_ENABLED || '').toLowerCase() === 'false') return false
|
|
34
|
-
return true
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function bootstrap() {
|
|
38
|
-
logger.info('[app] Initializing web process...')
|
|
39
|
-
|
|
40
|
-
if (!checkRequiredEnv()) {
|
|
41
|
-
logger.error('[app] Configuration validation failed, exiting...')
|
|
42
|
-
process.exit(1)
|
|
43
|
-
}
|
|
44
|
-
if (!checkJwtSecret()) {
|
|
45
|
-
logger.error('[app] JWT configuration validation failed, exiting...')
|
|
46
|
-
process.exit(1)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const framework = new WebFramework({
|
|
50
|
-
db: {
|
|
51
|
-
host: process.env.DB_HOST || '127.0.0.1',
|
|
52
|
-
port: Number(process.env.DB_PORT) || 3306,
|
|
53
|
-
username: process.env.DB_USER || 'root',
|
|
54
|
-
password: process.env.DB_PASS || '',
|
|
55
|
-
database: process.env.DB_NAME || 'test-project',
|
|
56
|
-
dialect: process.env.DB_DIALECT || 'mysql',
|
|
57
|
-
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
58
|
-
underscored: true,
|
|
59
|
-
logging: process.env.DEBUG_DB === 'true' ? (msg) => logger.debug(msg) : false
|
|
60
|
-
},
|
|
61
|
-
redis: {
|
|
62
|
-
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
63
|
-
port: Number(process.env.REDIS_PORT) || 6379,
|
|
64
|
-
password: process.env.REDIS_PASSWORD || undefined
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
const app = framework.createApp()
|
|
68
|
-
|
|
69
|
-
await framework.setup({
|
|
70
|
-
modelsDir: path.join(__dirname, './models'),
|
|
71
|
-
controllersDir: path.join(__dirname, './controllers')
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
app.disable('x-powered-by')
|
|
75
|
-
app.disable('etag')
|
|
76
|
-
|
|
77
|
-
app.set('trust proxy', true)
|
|
78
|
-
|
|
79
|
-
app.set('wrap response', true)
|
|
80
|
-
app.setOkCode(0)
|
|
81
|
-
app.setOkMessage('success')
|
|
82
|
-
|
|
83
|
-
const jwtOptions = buildJwtOptions()
|
|
84
|
-
app.setJWT('default', jwtOptions)
|
|
85
|
-
app.setJwtHintHeader('JwtHint')
|
|
86
|
-
|
|
87
|
-
logger.info('[app] JWT auth provider: static (HS256)')
|
|
88
|
-
|
|
89
|
-
app.use(expressLogger())
|
|
90
|
-
app.use(express.static(path.join(__dirname, '..', 'public')))
|
|
91
|
-
|
|
92
|
-
if (shouldEnableSwagger()) {
|
|
93
|
-
const openapiSpec = buildOpenApiSpec()
|
|
94
|
-
app.get('/api-docs.json', (_req, res) => res.json(openapiSpec))
|
|
95
|
-
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openapiSpec, {
|
|
96
|
-
customSiteTitle: `${process.env.APP_NAME || 'SKWeb'} API Docs`,
|
|
97
|
-
swaggerOptions: { persistAuthorization: true }
|
|
98
|
-
}))
|
|
99
|
-
logger.info(`[app] Swagger UI mounted at http://localhost:${process.env.PORT || 3000}/api-docs (env: ${process.env.NODE_ENV})`)
|
|
100
|
-
} else {
|
|
101
|
-
logger.info(`[app] Swagger UI disabled (env: ${process.env.NODE_ENV || 'unset'})`)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
await framework.loadControllers()
|
|
105
|
-
|
|
106
|
-
app.use(expressErrorHandler())
|
|
107
|
-
|
|
108
|
-
framework.start(Number(process.env.PORT) || 3000)
|
|
109
|
-
|
|
110
|
-
logger.info('[app] Initialization complete (web process)')
|
|
111
|
-
|
|
112
|
-
process.on('uncaughtException', (err) => {
|
|
113
|
-
logger.error(`[app] uncaughtException: ${err.stack || err.message}`)
|
|
114
|
-
process.exit(1)
|
|
115
|
-
})
|
|
116
|
-
process.on('unhandledRejection', (reason) => {
|
|
117
|
-
logger.error(`[app] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
118
|
-
process.exit(1)
|
|
119
|
-
})
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (require.main === module) {
|
|
123
|
-
bootstrap().catch((err) => {
|
|
124
|
-
logger.error('[app] Fatal error:', err)
|
|
125
|
-
process.exit(1)
|
|
126
|
-
})
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
module.exports = { bootstrap }
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
require('dotenv/config')
|
|
2
|
-
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const { CronFramework } = require('skweb-framework')
|
|
5
|
-
const { logger } = require('./utils/logger.js')
|
|
6
|
-
const { checkRequiredEnv } = require('./utils/config.js')
|
|
7
|
-
|
|
8
|
-
async function startCronWorker() {
|
|
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
|
-
const framework = new CronFramework({
|
|
17
|
-
db: {
|
|
18
|
-
host: process.env.DB_HOST || '127.0.0.1',
|
|
19
|
-
port: Number(process.env.DB_PORT) || 3306,
|
|
20
|
-
username: process.env.DB_USER || 'root',
|
|
21
|
-
password: process.env.DB_PASS || '',
|
|
22
|
-
database: process.env.DB_NAME || 'test-project',
|
|
23
|
-
dialect: process.env.DB_DIALECT || 'mysql',
|
|
24
|
-
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
25
|
-
underscored: true,
|
|
26
|
-
logging: process.env.DEBUG_DB === 'true' ? (msg) => logger.debug(msg) : false
|
|
27
|
-
},
|
|
28
|
-
redis: {
|
|
29
|
-
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
30
|
-
port: Number(process.env.REDIS_PORT) || 6379,
|
|
31
|
-
password: process.env.REDIS_PASSWORD || undefined
|
|
32
|
-
},
|
|
33
|
-
channel: 'cron:task:cmd',
|
|
34
|
-
logger,
|
|
35
|
-
onSaveTask: async (taskData) => {
|
|
36
|
-
const SysCronTask = framework.db?.sequelize?.models.SysCronTask
|
|
37
|
-
if (SysCronTask) {
|
|
38
|
-
await SysCronTask.update(
|
|
39
|
-
{ cache: taskData.cache, data: taskData.data },
|
|
40
|
-
{ where: { id: taskData.taskId } }
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
loadTasks: async () => {
|
|
45
|
-
const SysCronTask = framework.db?.sequelize?.models.SysCronTask
|
|
46
|
-
if (!SysCronTask) return []
|
|
47
|
-
return (await SysCronTask.findAll()).map((t) => ({
|
|
48
|
-
id: String(t.id),
|
|
49
|
-
cronTime: t.cronTime,
|
|
50
|
-
method: t.method
|
|
51
|
-
}))
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
await framework.setup({
|
|
56
|
-
modelsDir: path.join(__dirname, './models'),
|
|
57
|
-
cronActionsDir: path.join(__dirname, './cronActions')
|
|
58
|
-
})
|
|
59
|
-
await framework.start()
|
|
60
|
-
logger.info('[cron-worker] Cron worker started')
|
|
61
|
-
|
|
62
|
-
// 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
|
|
63
|
-
process.on('uncaughtException', (err) => {
|
|
64
|
-
logger.error(`[cron-worker] uncaughtException: ${err.stack || err.message}`)
|
|
65
|
-
process.exit(1)
|
|
66
|
-
})
|
|
67
|
-
process.on('unhandledRejection', (reason) => {
|
|
68
|
-
logger.error(`[cron-worker] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
69
|
-
process.exit(1)
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (require.main === module) {
|
|
74
|
-
startCronWorker().catch((err) => {
|
|
75
|
-
logger.error('[cron-worker] Fatal error:', err)
|
|
76
|
-
process.exit(1)
|
|
77
|
-
})
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
module.exports = { startCronWorker }
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
const { Database } = require('skweb-sequelize')
|
|
2
|
-
|
|
3
|
-
const db = new Database({
|
|
4
|
-
host: process.env.DB_HOST || '127.0.0.1',
|
|
5
|
-
port: Number(process.env.DB_PORT) || 3306,
|
|
6
|
-
username: process.env.DB_USER || 'root',
|
|
7
|
-
password: process.env.DB_PASS || '',
|
|
8
|
-
database: process.env.DB_NAME || '{{name}}',
|
|
9
|
-
dialect: process.env.DB_DIALECT || 'mysql',
|
|
10
|
-
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
11
|
-
underscored: true,
|
|
12
|
-
logging: process.env.DEBUG_DB === 'true'
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
module.exports = { db }
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const swaggerJSDoc = require('swagger-jsdoc')
|
|
5
|
-
|
|
6
|
-
function buildOpenApiSpec() {
|
|
7
|
-
return swaggerJSDoc({
|
|
8
|
-
definition: {
|
|
9
|
-
openapi: '3.0.3',
|
|
10
|
-
info: {
|
|
11
|
-
title: process.env.APP_NAME || 'SKWeb API',
|
|
12
|
-
version: process.env.API_VERSION || '1.0.0',
|
|
13
|
-
description: process.env.APP_DESCRIPTION || 'SKWeb project API documentation',
|
|
14
|
-
contact: { name: 'API Support' }
|
|
15
|
-
},
|
|
16
|
-
servers: [
|
|
17
|
-
{ url: `http://localhost:${process.env.PORT || 3000}`, description: 'Local development' }
|
|
18
|
-
],
|
|
19
|
-
components: {
|
|
20
|
-
securitySchemes: {
|
|
21
|
-
bearerAuth: {
|
|
22
|
-
type: 'http',
|
|
23
|
-
scheme: 'bearer',
|
|
24
|
-
bearerFormat: 'JWT'
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
apis: [
|
|
30
|
-
path.join(__dirname, 'controllers', '*.js')
|
|
31
|
-
]
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
module.exports = { buildOpenApiSpec }
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
const { logger } = require('./logger.js')
|
|
2
|
-
|
|
3
|
-
function validateConfig() {
|
|
4
|
-
const errors = []
|
|
5
|
-
|
|
6
|
-
if (!process.env.DB_HOST) {
|
|
7
|
-
logger.warn('[config] DB_HOST not set, using default: 127.0.0.1')
|
|
8
|
-
}
|
|
9
|
-
if (!process.env.DB_NAME) {
|
|
10
|
-
logger.warn('[config] DB_NAME not set, using default: test-project')
|
|
11
|
-
}
|
|
12
|
-
if (!process.env.DB_USER) {
|
|
13
|
-
logger.warn('[config] DB_USER not set, using default: root')
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (!process.env.REDIS_HOST) {
|
|
17
|
-
logger.warn('[config] REDIS_HOST not set, using default: 127.0.0.1')
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const port = Number(process.env.PORT)
|
|
21
|
-
if (port && (port < 1 || port > 65535)) {
|
|
22
|
-
errors.push('PORT must be between 1 and 65535')
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const dbPort = Number(process.env.DB_PORT)
|
|
26
|
-
if (dbPort && (dbPort < 1 || dbPort > 65535)) {
|
|
27
|
-
errors.push('DB_PORT must be between 1 and 65535')
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const redisPort = Number(process.env.REDIS_PORT)
|
|
31
|
-
if (redisPort && (redisPort < 1 || redisPort > 65535)) {
|
|
32
|
-
errors.push('REDIS_PORT must be between 1 and 65535')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
isValid: errors.length === 0,
|
|
37
|
-
errors
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function checkJwtSecret() {
|
|
42
|
-
if (process.env.JWT_SECRET === 'please-change-this-in-production' || !process.env.JWT_SECRET) {
|
|
43
|
-
if (process.env.NODE_ENV === 'production') {
|
|
44
|
-
logger.error('[config] JWT_SECRET must be set in production!')
|
|
45
|
-
return false
|
|
46
|
-
}
|
|
47
|
-
logger.warn('[config] WARNING: JWT_SECRET is using default value, please change in production!')
|
|
48
|
-
}
|
|
49
|
-
return true
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function checkRequiredEnv() {
|
|
53
|
-
const result = validateConfig()
|
|
54
|
-
|
|
55
|
-
if (result.errors.length > 0) {
|
|
56
|
-
logger.error('[config] Configuration validation failed:')
|
|
57
|
-
result.errors.forEach((err) => logger.error(` - ${err}`))
|
|
58
|
-
return false
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
logger.info('[config] Configuration validation passed')
|
|
62
|
-
return true
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = { validateConfig, checkJwtSecret, checkRequiredEnv }
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# ============================================
|
|
2
|
-
# SKWeb Environment Variables Template
|
|
3
|
-
# ============================================
|
|
4
|
-
# Copy this file to .env and fill in your values:
|
|
5
|
-
# cp .env.example .env
|
|
6
|
-
#
|
|
7
|
-
# Variables with default values can be omitted.
|
|
8
|
-
# Variables marked [REQUIRED] must be set in production.
|
|
9
|
-
|
|
10
|
-
# ---------- Node Environment ----------
|
|
11
|
-
# Development | Production | Test
|
|
12
|
-
NODE_ENV=development
|
|
13
|
-
|
|
14
|
-
# ---------- Server ----------
|
|
15
|
-
PORT=3000
|
|
16
|
-
HOST=0.0.0.0
|
|
17
|
-
|
|
18
|
-
# ---------- Database (MySQL / MariaDB) ----------
|
|
19
|
-
DB_HOST=127.0.0.1
|
|
20
|
-
DB_PORT=3306
|
|
21
|
-
DB_USER=root
|
|
22
|
-
DB_PASS=
|
|
23
|
-
DB_NAME={{name}}
|
|
24
|
-
DB_DIALECT=mysql
|
|
25
|
-
DB_CHARSET=utf8mb4
|
|
26
|
-
DEBUG_DB=false
|
|
27
|
-
|
|
28
|
-
# ---------- Redis ----------
|
|
29
|
-
REDIS_HOST=127.0.0.1
|
|
30
|
-
REDIS_PORT=6379
|
|
31
|
-
REDIS_PASSWORD=
|
|
32
|
-
REDIS_DB=0
|
|
33
|
-
|
|
34
|
-
# ---------- Swagger / OpenAPI ----------
|
|
35
|
-
# Mounts Swagger UI at /api-docs and exposes raw spec at /api-docs.json.
|
|
36
|
-
# Only enabled when NODE_ENV is 'development' or 'test'.
|
|
37
|
-
# Set SWAGGER_ENABLED=false to force-disable in dev/test as well.
|
|
38
|
-
# (Production is always disabled to avoid exposing internal API details.)
|
|
39
|
-
SWAGGER_ENABLED=true
|
|
40
|
-
|
|
41
|
-
# ---------- Auth: static (HS256 shared secret) ----------
|
|
42
|
-
# [REQUIRED in production] Use a strong random string, e.g. `openssl rand -hex 32`
|
|
43
|
-
JWT_SECRET=please-change-this-in-production
|
|
44
|
-
JWT_EXPIRES_IN=7d
|
|
45
|
-
|
|
46
|
-
# ---------- Logging ----------
|
|
47
|
-
# error | warn | info | http | verbose | debug | silly
|
|
48
|
-
LOG_LEVEL=info
|
|
49
|
-
LOG_DIR=./logs
|
|
50
|
-
LOG_CONSOLE=true
|
|
51
|
-
|
|
52
|
-
# ---------- CORS ----------
|
|
53
|
-
# Comma-separated origins, or * for all
|
|
54
|
-
CORS_ORIGIN=*
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import 'dotenv/config'
|
|
2
|
-
|
|
3
|
-
import path from 'path'
|
|
4
|
-
import { fileURLToPath } from 'url'
|
|
5
|
-
import express from 'skweb-express'
|
|
6
|
-
import expressLogger, { errorHandler as expressErrorHandler } from 'skweb-express-logger'
|
|
7
|
-
import swaggerUi from 'swagger-ui-express'
|
|
8
|
-
import logger from './utils/logger.js'
|
|
9
|
-
import { checkRequiredEnv, checkJwtSecret } from './utils/config.js'
|
|
10
|
-
import { buildOpenApiSpec } from './swagger.js'
|
|
11
|
-
|
|
12
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
13
|
-
const __dirname = path.dirname(__filename)
|
|
14
|
-
|
|
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
|
-
|
|
28
|
-
function shouldEnableSwagger() {
|
|
29
|
-
const env = (process.env.NODE_ENV || '').toLowerCase()
|
|
30
|
-
if (env !== 'development' && env !== 'test') return false
|
|
31
|
-
if ((process.env.SWAGGER_ENABLED || '').toLowerCase() === 'false') return false
|
|
32
|
-
return true
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function bootstrap() {
|
|
36
|
-
logger.info('[app] Initializing web process...')
|
|
37
|
-
|
|
38
|
-
if (!checkRequiredEnv()) {
|
|
39
|
-
logger.error('[app] Configuration validation failed, exiting...')
|
|
40
|
-
process.exit(1)
|
|
41
|
-
}
|
|
42
|
-
if (!checkJwtSecret()) {
|
|
43
|
-
logger.error('[app] JWT configuration validation failed, exiting...')
|
|
44
|
-
process.exit(1)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const { WebFramework } = await import('skweb-framework')
|
|
48
|
-
|
|
49
|
-
const framework = new WebFramework({
|
|
50
|
-
db: {
|
|
51
|
-
host: process.env.DB_HOST || '127.0.0.1',
|
|
52
|
-
port: Number(process.env.DB_PORT) || 3306,
|
|
53
|
-
username: process.env.DB_USER || 'root',
|
|
54
|
-
password: process.env.DB_PASS || '',
|
|
55
|
-
database: process.env.DB_NAME || 'test-project',
|
|
56
|
-
dialect: process.env.DB_DIALECT || 'mysql',
|
|
57
|
-
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
58
|
-
underscored: true,
|
|
59
|
-
logging: process.env.DEBUG_DB === 'true' ? (msg) => logger.debug(msg) : false
|
|
60
|
-
},
|
|
61
|
-
redis: {
|
|
62
|
-
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
63
|
-
port: Number(process.env.REDIS_PORT) || 6379,
|
|
64
|
-
password: process.env.REDIS_PASSWORD || undefined
|
|
65
|
-
}
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
const app = framework.createApp()
|
|
69
|
-
|
|
70
|
-
await framework.setup({
|
|
71
|
-
modelsDir: path.join(__dirname, './models'),
|
|
72
|
-
controllersDir: path.join(__dirname, './controllers')
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
app.disable('x-powered-by')
|
|
76
|
-
app.disable('etag')
|
|
77
|
-
|
|
78
|
-
app.set('trust proxy', true)
|
|
79
|
-
|
|
80
|
-
app.set('wrap response', true)
|
|
81
|
-
app.setOkCode(0)
|
|
82
|
-
app.setOkMessage('success')
|
|
83
|
-
|
|
84
|
-
const jwtOptions = buildJwtOptions()
|
|
85
|
-
app.setJWT('default', jwtOptions)
|
|
86
|
-
app.setJwtHintHeader('JwtHint')
|
|
87
|
-
|
|
88
|
-
logger.info('[app] JWT auth provider: static (HS256)')
|
|
89
|
-
|
|
90
|
-
app.use(expressLogger())
|
|
91
|
-
app.use(express.static(path.join(__dirname, '..', 'public')))
|
|
92
|
-
|
|
93
|
-
if (shouldEnableSwagger()) {
|
|
94
|
-
const openapiSpec = buildOpenApiSpec()
|
|
95
|
-
app.get('/api-docs.json', (_req, res) => res.json(openapiSpec))
|
|
96
|
-
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openapiSpec, {
|
|
97
|
-
customSiteTitle: `${process.env.APP_NAME || 'SKWeb'} API Docs`,
|
|
98
|
-
swaggerOptions: { persistAuthorization: true }
|
|
99
|
-
}))
|
|
100
|
-
logger.info(`[app] Swagger UI mounted at http://localhost:${process.env.PORT || 3000}/api-docs (env: ${process.env.NODE_ENV})`)
|
|
101
|
-
} else {
|
|
102
|
-
logger.info(`[app] Swagger UI disabled (env: ${process.env.NODE_ENV || 'unset'})`)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
await framework.loadControllers()
|
|
106
|
-
|
|
107
|
-
app.use(expressErrorHandler())
|
|
108
|
-
|
|
109
|
-
framework.start(Number(process.env.PORT) || 3000)
|
|
110
|
-
|
|
111
|
-
logger.info('[app] Initialization complete (web process)')
|
|
112
|
-
|
|
113
|
-
process.on('uncaughtException', (err) => {
|
|
114
|
-
logger.error(`[app] uncaughtException: ${err.stack || err.message}`)
|
|
115
|
-
process.exit(1)
|
|
116
|
-
})
|
|
117
|
-
process.on('unhandledRejection', (reason) => {
|
|
118
|
-
logger.error(`[app] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
119
|
-
process.exit(1)
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const isMainModule = import.meta.url === `file://${process.argv[1]}`
|
|
124
|
-
if (isMainModule) {
|
|
125
|
-
bootstrap().catch((err) => {
|
|
126
|
-
logger.error('[app] Fatal error:', err)
|
|
127
|
-
process.exit(1)
|
|
128
|
-
})
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export { bootstrap }
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import 'dotenv/config'
|
|
2
|
-
|
|
3
|
-
import path from 'path'
|
|
4
|
-
import { fileURLToPath } from 'url'
|
|
5
|
-
import logger from './utils/logger.js'
|
|
6
|
-
import { checkRequiredEnv } from './utils/config.js'
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
-
const __dirname = path.dirname(__filename)
|
|
10
|
-
|
|
11
|
-
async function startCronWorker() {
|
|
12
|
-
logger.info('[cron-worker] Starting cron worker process...')
|
|
13
|
-
|
|
14
|
-
if (!checkRequiredEnv()) {
|
|
15
|
-
logger.error('[cron-worker] Configuration validation failed, exiting...')
|
|
16
|
-
process.exit(1)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const { CronFramework } = await import('skweb-framework')
|
|
20
|
-
|
|
21
|
-
const framework = new CronFramework({
|
|
22
|
-
db: {
|
|
23
|
-
host: process.env.DB_HOST || '127.0.0.1',
|
|
24
|
-
port: Number(process.env.DB_PORT) || 3306,
|
|
25
|
-
username: process.env.DB_USER || 'root',
|
|
26
|
-
password: process.env.DB_PASS || '',
|
|
27
|
-
database: process.env.DB_NAME || 'test-project',
|
|
28
|
-
dialect: process.env.DB_DIALECT || 'mysql',
|
|
29
|
-
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
30
|
-
underscored: true,
|
|
31
|
-
logging: process.env.DEBUG_DB === 'true' ? (msg) => logger.debug(msg) : false
|
|
32
|
-
},
|
|
33
|
-
redis: {
|
|
34
|
-
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
35
|
-
port: Number(process.env.REDIS_PORT) || 6379,
|
|
36
|
-
password: process.env.REDIS_PASSWORD || undefined
|
|
37
|
-
},
|
|
38
|
-
channel: 'cron:task:cmd',
|
|
39
|
-
logger,
|
|
40
|
-
onSaveTask: async (taskData) => {
|
|
41
|
-
const SysCronTask = framework.db?.sequelize?.models.SysCronTask
|
|
42
|
-
if (SysCronTask) {
|
|
43
|
-
await SysCronTask.update(
|
|
44
|
-
{ cache: taskData.cache, data: taskData.data },
|
|
45
|
-
{ where: { id: taskData.taskId } }
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
loadTasks: async () => {
|
|
50
|
-
const SysCronTask = framework.db?.sequelize?.models.SysCronTask
|
|
51
|
-
if (!SysCronTask) return []
|
|
52
|
-
return (await SysCronTask.findAll()).map((t) => ({
|
|
53
|
-
id: String(t.id),
|
|
54
|
-
cronTime: t.cronTime,
|
|
55
|
-
method: t.method
|
|
56
|
-
}))
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
await framework.setup({
|
|
61
|
-
modelsDir: path.join(__dirname, './models'),
|
|
62
|
-
cronActionsDir: path.join(__dirname, './cronActions')
|
|
63
|
-
})
|
|
64
|
-
await framework.start()
|
|
65
|
-
logger.info('[cron-worker] Cron worker started')
|
|
66
|
-
|
|
67
|
-
// 未捕获异常:打印堆栈后直接退出。OS / 进程管理器负责回收资源。
|
|
68
|
-
process.on('uncaughtException', (err) => {
|
|
69
|
-
logger.error(`[cron-worker] uncaughtException: ${err.stack || err.message}`)
|
|
70
|
-
process.exit(1)
|
|
71
|
-
})
|
|
72
|
-
process.on('unhandledRejection', (reason) => {
|
|
73
|
-
logger.error(`[cron-worker] unhandledRejection: ${reason instanceof Error ? reason.stack : String(reason)}`)
|
|
74
|
-
process.exit(1)
|
|
75
|
-
})
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const isMainModule = import.meta.url === `file://${process.argv[1]}`
|
|
79
|
-
if (isMainModule) {
|
|
80
|
-
startCronWorker().catch((err) => {
|
|
81
|
-
logger.error('[cron-worker] Fatal error:', err)
|
|
82
|
-
process.exit(1)
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export { startCronWorker }
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Database } from 'skweb-sequelize'
|
|
2
|
-
|
|
3
|
-
const db = new Database({
|
|
4
|
-
host: process.env.DB_HOST || '127.0.0.1',
|
|
5
|
-
port: Number(process.env.DB_PORT) || 3306,
|
|
6
|
-
username: process.env.DB_USER || 'root',
|
|
7
|
-
password: process.env.DB_PASS || '',
|
|
8
|
-
database: process.env.DB_NAME || '{{name}}',
|
|
9
|
-
dialect: process.env.DB_DIALECT || 'mysql',
|
|
10
|
-
charset: process.env.DB_CHARSET || 'utf8mb4',
|
|
11
|
-
underscored: true,
|
|
12
|
-
logging: process.env.DEBUG_DB === 'true'
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
export { db }
|
|
16
|
-
export default db
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import { fileURLToPath } from 'url'
|
|
3
|
-
import swaggerJSDoc from 'swagger-jsdoc'
|
|
4
|
-
|
|
5
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
6
|
-
const __dirname = path.dirname(__filename)
|
|
7
|
-
|
|
8
|
-
export function buildOpenApiSpec() {
|
|
9
|
-
return swaggerJSDoc({
|
|
10
|
-
definition: {
|
|
11
|
-
openapi: '3.0.3',
|
|
12
|
-
info: {
|
|
13
|
-
title: process.env.APP_NAME || 'SKWeb API',
|
|
14
|
-
version: process.env.API_VERSION || '1.0.0',
|
|
15
|
-
description: process.env.APP_DESCRIPTION || 'SKWeb project API documentation',
|
|
16
|
-
contact: { name: 'API Support' }
|
|
17
|
-
},
|
|
18
|
-
servers: [
|
|
19
|
-
{ url: `http://localhost:${process.env.PORT || 3000}`, description: 'Local development' }
|
|
20
|
-
],
|
|
21
|
-
components: {
|
|
22
|
-
securitySchemes: {
|
|
23
|
-
bearerAuth: {
|
|
24
|
-
type: 'http',
|
|
25
|
-
scheme: 'bearer',
|
|
26
|
-
bearerFormat: 'JWT'
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
apis: [
|
|
32
|
-
path.join(__dirname, 'controllers', '*.js')
|
|
33
|
-
]
|
|
34
|
-
})
|
|
35
|
-
}
|