koa3-cli 1.0.4 → 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/app/controller/user.js +99 -88
- package/app/lib/logger.js +141 -0
- package/app/middleware/requestLogger.js +27 -0
- package/app.js +118 -110
- package/config/config.default.js +82 -81
- package/env.example +4 -0
- package/package.json +6 -6
package/app/controller/user.js
CHANGED
|
@@ -1,89 +1,100 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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,111 +1,119 @@
|
|
|
1
|
-
const Koa = require('koa');
|
|
2
|
-
const bodyParser = require('koa-bodyparser');
|
|
3
|
-
const
|
|
4
|
-
const views = require('@ladjs/koa-views');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
//
|
|
23
|
-
}
|
|
24
|
-
const config = Object.assign({}, defaultConfig, envConfig);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
56
|
-
app.use(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
+
|
|
111
119
|
module.exports = app;
|
package/config/config.default.js
CHANGED
|
@@ -1,82 +1,83 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koa3-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Koa3脚手架",
|
|
5
5
|
"main": "app.js",
|
|
6
6
|
"bin": {
|
|
@@ -25,17 +25,17 @@
|
|
|
25
25
|
"homepage": "https://atwzc.cn/",
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@koa/router": "^15.
|
|
28
|
+
"@koa/router": "^15.3.1",
|
|
29
29
|
"@ladjs/koa-views": "^9.0.0",
|
|
30
|
-
"dotenv": "^17.
|
|
31
|
-
"ejs": "^
|
|
32
|
-
"koa": "^3.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.
|
|
38
|
+
"nodemon": "^3.1.14"
|
|
39
39
|
},
|
|
40
40
|
"volta": {
|
|
41
41
|
"node": "20.18.1"
|