koa3-cli 1.0.3 → 1.0.5

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/README.md CHANGED
@@ -38,7 +38,7 @@ npm run dev
38
38
  ## 项目结构
39
39
 
40
40
  ```
41
- koa2-cli/
41
+ koa3-cli/
42
42
  ├── app/ # 应用代码目录
43
43
  │ ├── controller/ # 控制器目录
44
44
  │ │ ├── home.js # 首页控制器
@@ -99,18 +99,6 @@ npm start
99
99
  - API 示例: http://localhost:3000/api/user
100
100
  - 文档: http://localhost:3000/index.html
101
101
 
102
- ### 文档开发
103
-
104
- 启动 VuePress 文档开发服务器:
105
- ```bash
106
- npm run docs:dev
107
- ```
108
-
109
- 构建文档为静态文件:
110
- ```bash
111
- npm run docs:build
112
- ```
113
-
114
102
  ## 环境配置
115
103
 
116
104
  项目支持多环境配置,通过 `NODE_ENV` 环境变量控制:
@@ -196,8 +184,8 @@ router.get('/api/product', productController.list);
196
184
 
197
185
  ## 技术栈
198
186
 
199
- - **Koa2**: Web 框架
200
- - **koa-router**: 路由
187
+ - **Koa3**: Web 框架
188
+ - **@koa/router**: 路由
201
189
  - **koa-bodyparser**: 请求体解析
202
190
  - **koa-static**: 静态资源服务
203
191
  - **koa-views**: 模板引擎支持
@@ -7,7 +7,7 @@ class HomeController {
7
7
  */
8
8
  async index(ctx) {
9
9
  ctx.body = {
10
- message: 'Welcome to Koa2 CLI',
10
+ message: 'Welcome to Koa3 CLI',
11
11
  version: '1.0.0',
12
12
  timestamp: new Date().toISOString()
13
13
  };
@@ -1,89 +1,100 @@
1
- /**
2
- * 用户控制器
3
- */
4
- const userService = require('../service/user');
5
-
6
- class UserController {
7
- /**
8
- * 获取用户列表
9
- */
10
- async list(ctx) {
11
- try {
12
- const users = await userService.getUserList();
13
- ctx.body = users;
14
- } catch (error) {
15
- ctx.throw(500, error.message);
16
- }
17
- }
18
-
19
- /**
20
- * 获取用户详情
21
- */
22
- async detail(ctx) {
23
- try {
24
- const { id } = ctx.params;
25
- const user = await userService.getUserById(id);
26
- if (!user) {
27
- ctx.status = 404;
28
- ctx.body = { message: 'User not found' };
29
- return;
30
- }
31
- ctx.body = user;
32
- } catch (error) {
33
- ctx.throw(500, error.message);
34
- }
35
- }
36
-
37
- /**
38
- * 创建用户
39
- */
40
- async create(ctx) {
41
- try {
42
- const userData = ctx.request.body;
43
- const user = await userService.createUser(userData);
44
- ctx.status = 201;
45
- ctx.body = user;
46
- } catch (error) {
47
- ctx.throw(500, error.message);
48
- }
49
- }
50
-
51
- /**
52
- * 更新用户
53
- */
54
- async update(ctx) {
55
- try {
56
- const { id } = ctx.params;
57
- const userData = ctx.request.body;
58
- const user = await userService.updateUser(id, userData);
59
- if (!user) {
60
- ctx.status = 404;
61
- ctx.body = { message: 'User not found' };
62
- return;
63
- }
64
- ctx.body = user;
65
- } catch (error) {
66
- ctx.throw(500, error.message);
67
- }
68
- }
69
-
70
- /**
71
- * 删除用户
72
- */
73
- async delete(ctx) {
74
- try {
75
- const { id } = ctx.params;
76
- const result = await userService.deleteUser(id);
77
- if (!result) {
78
- ctx.status = 404;
79
- ctx.body = { message: 'User not found' };
80
- return;
81
- }
82
- ctx.status = 204;
83
- } catch (error) {
84
- ctx.throw(500, error.message);
85
- }
86
- }
87
- }
88
-
1
+ const userService = require('../service/user');
2
+
3
+ function logMeta(ctx, extra = {}) {
4
+ return {
5
+ requestId: ctx.state && ctx.state.requestId,
6
+ method: ctx.method,
7
+ url: ctx.originalUrl || ctx.url,
8
+ ...extra
9
+ };
10
+ }
11
+
12
+ class UserController {
13
+ async list(ctx) {
14
+ try {
15
+ const users = await userService.getUserList();
16
+ ctx.logger.info('User list fetched', logMeta(ctx, { count: Array.isArray(users) ? users.length : undefined }));
17
+ ctx.body = users;
18
+ } catch (error) {
19
+ ctx.logger.error('Failed to fetch user list', logMeta(ctx, { message: error.message, stack: error.stack }));
20
+ ctx.throw(500, error.message);
21
+ }
22
+ }
23
+
24
+ async detail(ctx) {
25
+ const { id } = ctx.params;
26
+
27
+ try {
28
+ const user = await userService.getUserById(id);
29
+ if (!user) {
30
+ ctx.logger.warn('User detail not found', logMeta(ctx, { userId: id }));
31
+ ctx.status = 404;
32
+ ctx.body = { message: 'User not found' };
33
+ return;
34
+ }
35
+
36
+ ctx.logger.info('User detail fetched', logMeta(ctx, { userId: id }));
37
+ ctx.body = user;
38
+ } catch (error) {
39
+ ctx.logger.error('Failed to fetch user detail', logMeta(ctx, { userId: id, message: error.message, stack: error.stack }));
40
+ ctx.throw(500, error.message);
41
+ }
42
+ }
43
+
44
+ async create(ctx) {
45
+ const userData = ctx.request.body;
46
+
47
+ try {
48
+ const user = await userService.createUser(userData);
49
+ ctx.logger.info('User created', logMeta(ctx, { userId: user && user.id }));
50
+ ctx.status = 201;
51
+ ctx.body = user;
52
+ } catch (error) {
53
+ ctx.logger.error('Failed to create user', logMeta(ctx, { message: error.message, stack: error.stack }));
54
+ ctx.throw(500, error.message);
55
+ }
56
+ }
57
+
58
+ async update(ctx) {
59
+ const { id } = ctx.params;
60
+ const userData = ctx.request.body;
61
+
62
+ try {
63
+ const user = await userService.updateUser(id, userData);
64
+ if (!user) {
65
+ ctx.logger.warn('User update target not found', logMeta(ctx, { userId: id }));
66
+ ctx.status = 404;
67
+ ctx.body = { message: 'User not found' };
68
+ return;
69
+ }
70
+
71
+ ctx.logger.info('User updated', logMeta(ctx, { userId: id }));
72
+ ctx.body = user;
73
+ } catch (error) {
74
+ ctx.logger.error('Failed to update user', logMeta(ctx, { userId: id, message: error.message, stack: error.stack }));
75
+ ctx.throw(500, error.message);
76
+ }
77
+ }
78
+
79
+ async delete(ctx) {
80
+ const { id } = ctx.params;
81
+
82
+ try {
83
+ const result = await userService.deleteUser(id);
84
+ if (!result) {
85
+ ctx.logger.warn('User delete target not found', logMeta(ctx, { userId: id }));
86
+ ctx.status = 404;
87
+ ctx.body = { message: 'User not found' };
88
+ return;
89
+ }
90
+
91
+ ctx.logger.info('User deleted', logMeta(ctx, { userId: id }));
92
+ ctx.status = 204;
93
+ } catch (error) {
94
+ ctx.logger.error('Failed to delete user', logMeta(ctx, { userId: id, message: error.message, stack: error.stack }));
95
+ ctx.throw(500, error.message);
96
+ }
97
+ }
98
+ }
99
+
89
100
  module.exports = new UserController();
@@ -0,0 +1,141 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const util = require('util');
4
+
5
+ const LEVEL_WEIGHT = {
6
+ debug: 10,
7
+ info: 20,
8
+ warn: 30,
9
+ error: 40
10
+ };
11
+
12
+ function normalizeLevel(level) {
13
+ const resolved = String(level || 'info').toLowerCase();
14
+ return LEVEL_WEIGHT[resolved] ? resolved : 'info';
15
+ }
16
+
17
+ function formatDate(date = new Date()) {
18
+ const pad = (n) => String(n).padStart(2, '0');
19
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
20
+ }
21
+
22
+ function formatTimestamp(date = new Date()) {
23
+ return date.toLocaleString();
24
+ }
25
+
26
+ function safeSerialize(meta) {
27
+ if (meta === undefined) {
28
+ return '';
29
+ }
30
+
31
+ if (meta instanceof Error) {
32
+ return JSON.stringify({
33
+ name: meta.name,
34
+ message: meta.message,
35
+ stack: meta.stack
36
+ });
37
+ }
38
+
39
+ if (typeof meta === 'string') {
40
+ return meta;
41
+ }
42
+
43
+ try {
44
+ return JSON.stringify(meta);
45
+ } catch (error) {
46
+ return util.inspect(meta, { depth: 4, breakLength: 120 });
47
+ }
48
+ }
49
+
50
+ class Logger {
51
+ constructor(options = {}) {
52
+ this.level = normalizeLevel(options.level);
53
+ this.enableConsole = options.enableConsole !== false;
54
+ this.enableFile = options.enableFile !== false;
55
+ this.dir = options.dir || 'logs';
56
+ this.appName = options.appName || 'koa3-cli';
57
+ this.cwd = options.cwd || process.cwd();
58
+ this.logDir = path.isAbsolute(this.dir) ? this.dir : path.join(this.cwd, this.dir);
59
+
60
+ if (this.enableFile) {
61
+ fs.mkdirSync(this.logDir, { recursive: true });
62
+ }
63
+ }
64
+
65
+ shouldLog(level) {
66
+ return LEVEL_WEIGHT[level] >= LEVEL_WEIGHT[this.level];
67
+ }
68
+
69
+ write(level, message, meta) {
70
+ if (!this.shouldLog(level)) {
71
+ return;
72
+ }
73
+
74
+ const timestamp = formatTimestamp();
75
+ const metaText = safeSerialize(meta);
76
+ const line = `[${timestamp}] [${this.appName}] [${level.toUpperCase()}] ${message}${metaText ? ` ${metaText}` : ''}`;
77
+
78
+ if (this.enableConsole) {
79
+ const printer = level === 'error' ? console.error : level === 'warn' ? console.warn : console.log;
80
+ printer(line);
81
+ }
82
+
83
+ if (!this.enableFile) {
84
+ return;
85
+ }
86
+
87
+ const date = formatDate();
88
+ const commonPath = path.join(this.logDir, `${date}.log`);
89
+ fs.appendFile(commonPath, `${line}\n`, () => {});
90
+
91
+ if (level === 'error') {
92
+ const errorPath = path.join(this.logDir, `${date}.error.log`);
93
+ fs.appendFile(errorPath, `${line}\n`, () => {});
94
+ }
95
+ }
96
+
97
+ access(data) {
98
+ const line = {
99
+ timestamp: formatTimestamp(),
100
+ type: 'access',
101
+ app: this.appName,
102
+ ...data
103
+ };
104
+
105
+ if (this.enableConsole && this.shouldLog('info')) {
106
+ console.log(`[${line.timestamp}] [${this.appName}] [ACCESS] ${line.method} ${line.url} ${line.status} ${line.duration}ms ${line.requestId}`);
107
+ }
108
+
109
+ if (!this.enableFile) {
110
+ return;
111
+ }
112
+
113
+ const date = formatDate();
114
+ const accessPath = path.join(this.logDir, `${date}.access.log`);
115
+ fs.appendFile(accessPath, `${JSON.stringify(line)}\n`, () => {});
116
+ }
117
+
118
+ debug(message, meta) {
119
+ this.write('debug', message, meta);
120
+ }
121
+
122
+ info(message, meta) {
123
+ this.write('info', message, meta);
124
+ }
125
+
126
+ warn(message, meta) {
127
+ this.write('warn', message, meta);
128
+ }
129
+
130
+ error(message, meta) {
131
+ this.write('error', message, meta);
132
+ }
133
+ }
134
+
135
+ function createLogger(options) {
136
+ return new Logger(options);
137
+ }
138
+
139
+ module.exports = {
140
+ createLogger
141
+ };
@@ -0,0 +1,27 @@
1
+ function createRequestId() {
2
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
3
+ }
4
+
5
+ module.exports = function requestLogger(logger) {
6
+ return async function requestLoggerMiddleware(ctx, next) {
7
+ const start = Date.now();
8
+ const requestId = ctx.get('x-request-id') || createRequestId();
9
+
10
+ ctx.state.requestId = requestId;
11
+ ctx.set('x-request-id', requestId);
12
+
13
+ try {
14
+ await next();
15
+ } finally {
16
+ const duration = Date.now() - start;
17
+ logger.access({
18
+ requestId,
19
+ method: ctx.method,
20
+ url: ctx.originalUrl || ctx.url,
21
+ status: ctx.status,
22
+ duration,
23
+ ip: ctx.ip
24
+ });
25
+ }
26
+ };
27
+ };
package/app.js CHANGED
@@ -1,112 +1,119 @@
1
- const Koa = require('koa');
2
- const { Router } = require('@koa/router');
3
- const bodyParser = require('koa-bodyparser');
4
- const static = require('koa-static');
5
- const views = require('@ladjs/koa-views');
6
- const path = require('path');
7
- const fs = require('fs');
8
-
9
- // 加载环境变量
10
- require('dotenv').config();
11
-
12
- // 加载配置(支持环境配置覆盖)
13
- const env = process.env.NODE_ENV || 'development';
14
- const defaultConfig = require('./config/config.default');
15
- let envConfig = {};
16
- try {
17
- if (env === 'production') {
18
- envConfig = require('./config/config.prod');
19
- } else if (env === 'local' || env === 'development') {
20
- envConfig = require('./config/config.local');
21
- }
22
- } catch (e) {
23
- // 环境配置文件不存在时忽略
24
- }
25
- const config = Object.assign({}, defaultConfig, envConfig);
26
-
27
- // 加载中间件
28
- const middleware = require('./app/middleware');
29
-
30
- // 加载路由
31
- const router = require('./app/router');
32
-
33
- const app = new Koa();
34
-
35
- // 应用配置
36
- app.keys = config.keys || ['koa2-cli-secret-key'];
37
-
38
- // 静态资源
39
- if (config.static && config.static.enable !== false) {
40
- const staticPath = path.join(__dirname, config.static.dir || 'public');
41
- if (fs.existsSync(staticPath)) {
42
- app.use(static(staticPath, config.static.options || {}));
43
- }
44
- }
45
-
46
- // 模板引擎
47
- if (config.view && config.view.enable !== false) {
48
- const viewPath = path.join(__dirname, config.view.root || 'app/view');
49
- if (fs.existsSync(viewPath)) {
50
- app.use(views(viewPath, config.view.options || {
51
- extension: 'ejs'
52
- }));
53
- }
54
- }
55
-
56
- // 请求体解析
57
- app.use(bodyParser(config.bodyParser || {}));
58
-
59
- // 自定义中间件
60
- if (middleware && typeof middleware === 'function') {
61
- app.use(middleware);
62
- }
63
-
64
- // 错误处理中间件
65
- app.use(async (ctx, next) => {
66
- try {
67
- await next();
68
- } catch (err) {
69
- ctx.status = err.status || 500;
70
- ctx.body = {
71
- success: false,
72
- message: err.message || 'Internal Server Error',
73
- ...(config.env === 'development' && { stack: err.stack })
74
- };
75
- ctx.app.emit('error', err, ctx);
76
- }
77
- });
78
-
79
- // 日志中间件
80
- app.use(async (ctx, next) => {
81
- const start = Date.now();
82
- await next();
83
- const ms = Date.now() - start;
84
- console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
85
- });
86
-
87
- // 注册路由
88
- app.use(router.routes()).use(router.allowedMethods());
89
-
90
- // 404 处理(必须在路由之后)
91
- app.use(async (ctx) => {
92
- if (ctx.status === 404) {
93
- ctx.body = {
94
- success: false,
95
- message: 'Not Found'
96
- };
97
- }
98
- });
99
-
100
- // 错误事件监听
101
- app.on('error', (err, ctx) => {
102
- console.error('Server error:', err);
103
- });
104
-
105
- // 启动服务器
106
- const port = config.port || 3000;
107
- app.listen(port, () => {
108
- console.log(`Server is running on http://localhost:${port}`);
109
- console.log(`Environment: ${config.env}`);
110
- });
111
-
1
+ const Koa = require('koa');
2
+ const bodyParser = require('koa-bodyparser');
3
+ const serveStatic = require('koa-static');
4
+ const views = require('@ladjs/koa-views');
5
+ const path = require('path');
6
+ const fs = require('fs');
7
+ const { createLogger } = require('./app/lib/logger');
8
+ const createRequestLogger = require('./app/middleware/requestLogger');
9
+
10
+ require('dotenv').config();
11
+
12
+ const env = process.env.NODE_ENV || 'development';
13
+ const defaultConfig = require('./config/config.default');
14
+ let envConfig = {};
15
+ try {
16
+ if (env === 'production') {
17
+ envConfig = require('./config/config.prod');
18
+ } else if (env === 'local' || env === 'development') {
19
+ envConfig = require('./config/config.local');
20
+ }
21
+ } catch (e) {
22
+ // Ignore missing env config override.
23
+ }
24
+ const config = Object.assign({}, defaultConfig, envConfig);
25
+
26
+ const middleware = require('./app/middleware');
27
+ const router = require('./app/router');
28
+
29
+ const app = new Koa();
30
+ const logger = createLogger({
31
+ ...(config.logger || {}),
32
+ appName: config.name || 'koa3-cli',
33
+ cwd: __dirname
34
+ });
35
+
36
+ app.keys = config.keys || ['koa3-cli-secret-key'];
37
+ app.context.logger = logger;
38
+
39
+ if (config.static && config.static.enable !== false) {
40
+ const staticPath = path.join(__dirname, config.static.dir || 'public');
41
+ if (fs.existsSync(staticPath)) {
42
+ app.use(serveStatic(staticPath, config.static.options || {}));
43
+ }
44
+ }
45
+
46
+ if (config.view && config.view.enable !== false) {
47
+ const viewPath = path.join(__dirname, config.view.root || 'app/view');
48
+ if (fs.existsSync(viewPath)) {
49
+ app.use(views(viewPath, config.view.options || {
50
+ extension: 'ejs'
51
+ }));
52
+ }
53
+ }
54
+
55
+ app.use(bodyParser(config.bodyParser || {}));
56
+ app.use(createRequestLogger(logger));
57
+
58
+ if (middleware && typeof middleware === 'function') {
59
+ app.use(middleware);
60
+ }
61
+
62
+ app.use(async (ctx, next) => {
63
+ try {
64
+ await next();
65
+ } catch (err) {
66
+ ctx.status = err.status || 500;
67
+ ctx.body = {
68
+ success: false,
69
+ message: err.message || 'Internal Server Error',
70
+ ...(config.env === 'development' && { stack: err.stack })
71
+ };
72
+
73
+ logger.error('Request failed', {
74
+ requestId: ctx.state && ctx.state.requestId,
75
+ method: ctx.method,
76
+ url: ctx.originalUrl || ctx.url,
77
+ status: ctx.status,
78
+ message: err.message,
79
+ stack: err.stack
80
+ });
81
+
82
+ ctx.app.emit('error', err, ctx);
83
+ }
84
+ });
85
+
86
+ app.use(router.routes()).use(router.allowedMethods());
87
+
88
+ app.use(async (ctx) => {
89
+ if (ctx.status === 404) {
90
+ ctx.body = {
91
+ success: false,
92
+ message: 'Not Found'
93
+ };
94
+ }
95
+ });
96
+
97
+ app.on('error', (err, ctx) => {
98
+ logger.error('Server error event', {
99
+ requestId: ctx && ctx.state && ctx.state.requestId,
100
+ message: err.message,
101
+ stack: err.stack
102
+ });
103
+ });
104
+
105
+ process.on('unhandledRejection', (reason) => {
106
+ logger.error('Unhandled promise rejection', reason);
107
+ });
108
+
109
+ process.on('uncaughtException', (error) => {
110
+ logger.error('Uncaught exception', error);
111
+ });
112
+
113
+ const port = config.port || 3000;
114
+ app.listen(port, () => {
115
+ logger.info(`Server is running on http://localhost:${port}`);
116
+ logger.info(`Environment: ${config.env}`);
117
+ });
118
+
112
119
  module.exports = app;
@@ -1,82 +1,83 @@
1
- /**
2
- * 默认配置文件
3
- * 所有环境都会加载此配置
4
- */
5
- module.exports = {
6
- // 应用名称
7
- name: 'koa2-cli',
8
-
9
- // 运行环境: development, production, test
10
- env: process.env.NODE_ENV || 'development',
11
-
12
- // 服务端口
13
- port: process.env.PORT || 3000,
14
-
15
- // 密钥,用于加密cookie等
16
- keys: process.env.KEYS ? process.env.KEYS.split(',') : ['koa2-cli-secret-key'],
17
-
18
- // 静态资源配置
19
- static: {
20
- enable: true,
21
- dir: 'public',
22
- options: {
23
- maxAge: 365 * 24 * 60 * 60 * 1000, // 1年
24
- gzip: true
25
- }
26
- },
27
-
28
- // VuePress 文档配置
29
- docs: {
30
- enable: true,
31
- buildDir: 'public/docs'
32
- },
33
-
34
- // 视图配置
35
- view: {
36
- enable: true,
37
- root: 'app/view',
38
- options: {
39
- extension: 'ejs',
40
- map: {
41
- html: 'ejs'
42
- }
43
- }
44
- },
45
-
46
- // bodyParser配置
47
- bodyParser: {
48
- enableTypes: ['json', 'form', 'text'],
49
- jsonLimit: '10mb',
50
- formLimit: '10mb'
51
- },
52
-
53
- // 数据库配置(示例)
54
- database: {
55
- client: 'mysql',
56
- connection: {
57
- host: process.env.DB_HOST || 'localhost',
58
- port: process.env.DB_PORT || 3306,
59
- user: process.env.DB_USER || 'root',
60
- password: process.env.DB_PASSWORD || '',
61
- database: process.env.DB_NAME || 'test'
62
- },
63
- pool: {
64
- min: 2,
65
- max: 10
66
- }
67
- },
68
-
69
- // Redis配置(示例)
70
- redis: {
71
- host: process.env.REDIS_HOST || 'localhost',
72
- port: process.env.REDIS_PORT || 6379,
73
- password: process.env.REDIS_PASSWORD || '',
74
- db: process.env.REDIS_DB || 0
75
- },
76
-
77
- // 日志配置
78
- logger: {
79
- level: process.env.LOG_LEVEL || 'info',
80
- dir: 'logs'
81
- }
1
+ /**
2
+ * Default config loaded in all environments.
3
+ */
4
+ module.exports = {
5
+ // Application name
6
+ name: 'koa3-cli',
7
+
8
+ // Runtime env: development, production, test
9
+ env: process.env.NODE_ENV || 'development',
10
+
11
+ // Server port
12
+ port: process.env.PORT || 3000,
13
+
14
+ // Cookie signing keys
15
+ keys: process.env.KEYS ? process.env.KEYS.split(',') : ['koa3-cli-secret-key'],
16
+
17
+ // Static assets
18
+ static: {
19
+ enable: true,
20
+ dir: 'public',
21
+ options: {
22
+ maxAge: 365 * 24 * 60 * 60 * 1000,
23
+ gzip: true
24
+ }
25
+ },
26
+
27
+ // Docs build config
28
+ docs: {
29
+ enable: true,
30
+ buildDir: 'public/docs'
31
+ },
32
+
33
+ // View engine
34
+ view: {
35
+ enable: true,
36
+ root: 'app/view',
37
+ options: {
38
+ extension: 'ejs',
39
+ map: {
40
+ html: 'ejs'
41
+ }
42
+ }
43
+ },
44
+
45
+ // bodyParser
46
+ bodyParser: {
47
+ enableTypes: ['json', 'form', 'text'],
48
+ jsonLimit: '10mb',
49
+ formLimit: '10mb'
50
+ },
51
+
52
+ // Database (example)
53
+ database: {
54
+ client: 'mysql',
55
+ connection: {
56
+ host: process.env.DB_HOST || 'localhost',
57
+ port: process.env.DB_PORT || 3306,
58
+ user: process.env.DB_USER || 'root',
59
+ password: process.env.DB_PASSWORD || '',
60
+ database: process.env.DB_NAME || 'test'
61
+ },
62
+ pool: {
63
+ min: 2,
64
+ max: 10
65
+ }
66
+ },
67
+
68
+ // Redis (example)
69
+ redis: {
70
+ host: process.env.REDIS_HOST || 'localhost',
71
+ port: process.env.REDIS_PORT || 6379,
72
+ password: process.env.REDIS_PASSWORD || '',
73
+ db: process.env.REDIS_DB || 0
74
+ },
75
+
76
+ // Logger
77
+ logger: {
78
+ level: process.env.LOG_LEVEL || 'info',
79
+ dir: process.env.LOG_DIR || 'logs',
80
+ enableConsole: process.env.LOG_ENABLE_CONSOLE !== 'false',
81
+ enableFile: process.env.LOG_ENABLE_FILE !== 'false'
82
+ }
82
83
  };
package/env.example CHANGED
@@ -25,3 +25,7 @@ REDIS_DB=0
25
25
 
26
26
  # 日志级别: debug, info, warn, error
27
27
  LOG_LEVEL=info
28
+
29
+ LOG_DIR=logs
30
+ LOG_ENABLE_CONSOLE=true
31
+ LOG_ENABLE_FILE=true
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "koa3-cli",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Koa3脚手架",
5
5
  "main": "app.js",
6
6
  "bin": {
7
- "koa3-cli": "./bin/cli.js"
7
+ "koa3-cli": "bin/cli.js"
8
8
  },
9
9
  "preferGlobal": true,
10
10
  "scripts": {
@@ -25,17 +25,17 @@
25
25
  "homepage": "https://atwzc.cn/",
26
26
  "license": "MIT",
27
27
  "dependencies": {
28
- "@koa/router": "^15.2.0",
28
+ "@koa/router": "^15.3.1",
29
29
  "@ladjs/koa-views": "^9.0.0",
30
- "dotenv": "^17.2.3",
31
- "ejs": "^3.1.10",
32
- "koa": "^3.1.1",
30
+ "dotenv": "^17.3.1",
31
+ "ejs": "^4.0.1",
32
+ "koa": "^3.1.2",
33
33
  "koa-bodyparser": "^4.4.1",
34
34
  "koa-cors": "^0.0.16",
35
35
  "koa-static": "^5.0.0"
36
36
  },
37
37
  "devDependencies": {
38
- "nodemon": "^3.1.11"
38
+ "nodemon": "^3.1.14"
39
39
  },
40
40
  "volta": {
41
41
  "node": "20.18.1"