huxy-node-server 1.0.0-beta.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 yiru
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,490 @@
1
+ # Huxy Node Server
2
+
3
+ [![Node.js Version](https://img.shields.io/badge/node.js-%3E%3D20.0.0-brightgreen)](https://nodejs.org/)
4
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+ [![Express.js](https://img.shields.io/badge/express.js-5.x-blue)](https://expressjs.com/)
6
+
7
+ 一个精炼、高性能的 Express.js 服务器模板,为现代 Node.js 应用程序设计,提供灵活的功能和最佳实践。
8
+
9
+ ## 🚀 特性
10
+
11
+ ### 核心功能
12
+ - **现代 ES 模块支持**:使用 `"type": "module"` 完全支持 ES Modules
13
+ - **高性能日志**:集成 Pino 日志系统,支持彩色输出和多级别日志
14
+ - **安全防护**:内置 Helmet 安全中间件,提供 CSP、XSS 等多种安全防护
15
+ - **请求限制**:基于 IP 的请求速率限制,防止 DDoS 和暴力攻击
16
+ - **跨域支持**:灵活的 CORS 配置,支持多域名和凭证
17
+ - **压缩支持**:自动 GZIP 压缩响应,减少带宽使用
18
+ - **健康检查**:内置 `/health` 端点,监控服务器状态
19
+ - **优雅关闭**:处理 SIGTERM 和 SIGINT 信号,确保服务器优雅关闭
20
+
21
+ ### 生产环境特性
22
+ - **环境变量支持**:通过 dotenv 管理配置,支持 `.env` 文件
23
+ - **错误处理**:全局错误处理中间件,提供详细错误日志
24
+ - **请求日志**:详细的 HTTP 请求日志,包括响应时间、状态码等
25
+ - **端口检查**:自动检测端口是否被占用,并自动选择可用端口
26
+ - **内存监控**:实时监控服务器内存使用情况
27
+ - **多网络接口支持**:自动检测本地 IP 地址,支持多网卡环境
28
+
29
+ ### 开发者友好
30
+ - **热重载**:开发环境支持 `--watch` 模式,自动重载代码
31
+ - **详细文档**:完整的 API 文档和使用示例
32
+ - **模块化设计**:清晰的代码结构,易于扩展和定制
33
+ - **TypeScript 友好**:代码结构适合 TypeScript 迁移
34
+
35
+ ## 📦 安装
36
+
37
+ ```bash
38
+ # 通过 npm 安装
39
+ npm install huxy-node-server
40
+
41
+ # 或者通过 yarn 安装
42
+ yarn add huxy-node-server
43
+
44
+ # 或者通过 pnpm 安装
45
+ pnpm add huxy-node-server
46
+ ```
47
+
48
+ ## 🚀 快速开始
49
+
50
+ ### 基本使用
51
+
52
+ ```javascript
53
+ import { startServer } from 'huxy-node-server';
54
+
55
+ // 启动服务器
56
+ const { app, config, httpServer } = await startServer({
57
+ port: 3000,
58
+ host: '0.0.0.0',
59
+ basepath: '/api',
60
+ // 其他配置...
61
+ }, (config, app, httpServer) => {
62
+ // 可以在这里添加自定义路由
63
+ app.get('/hello', (req, res) => {
64
+ res.json({ message: 'Hello World!' });
65
+ });
66
+ });
67
+ ```
68
+
69
+ ### 静态文件服务
70
+
71
+ ```javascript
72
+ import { startStatic } from 'huxy-node-server';
73
+
74
+ // 启动静态文件服务器
75
+ const server = await startStatic({
76
+ port: 9000,
77
+ basepath: '/',
78
+ buildPath: './dist', // 静态文件目录
79
+ });
80
+ ```
81
+
82
+ ## 🛠️ 配置选项
83
+
84
+ ### 服务器配置
85
+
86
+ | 选项 | 类型 | 默认值 | 描述 |
87
+ |------|------|--------|------|
88
+ | `port` | number | 3000 | 服务器端口 |
89
+ | `host` | string | '0.0.0.0' | 服务器主机 |
90
+ | `basepath` | string | '/' | 基础路径前缀 |
91
+ | `nodeEnv` | string | 'development' | 运行环境 |
92
+ | `appName` | string | 'HuxyServer' | 应用名称 |
93
+
94
+ ### 安全配置
95
+
96
+ ```javascript
97
+ {
98
+ helmet: {
99
+ contentSecurityPolicy: {
100
+ directives: {
101
+ defaultSrc: ["'self'"],
102
+ styleSrc: ["'self'", "'unsafe-inline'"],
103
+ scriptSrc: ["'self'"],
104
+ imgSrc: ["'self'", "data:", "https:"]
105
+ }
106
+ },
107
+ crossOriginEmbedderPolicy: false
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### CORS 配置
113
+
114
+ ```javascript
115
+ {
116
+ cors: {
117
+ origin: ['http://example.com', 'http://localhost:3000'], // 或 '*'
118
+ credentials: true
119
+ }
120
+ }
121
+ ```
122
+
123
+ ### 请求速率限制
124
+
125
+ ```javascript
126
+ {
127
+ rateLimit: {
128
+ windowMs: 900000, // 15 分钟
129
+ limit: 100, // 每个窗口内最大请求数
130
+ message: {
131
+ error: '请求过于频繁,请稍后再试'
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### 日志配置
138
+
139
+ ```javascript
140
+ {
141
+ logLevel: 30, // 日志级别 (10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal)
142
+ }
143
+ ```
144
+
145
+ ## 🌍 环境变量
146
+
147
+ 可以通过环境变量配置服务器:
148
+
149
+ ```bash
150
+ # .env 文件
151
+ NODE_ENV=production
152
+ PORT=3000
153
+ HOST=0.0.0.0
154
+ BASEPATH=/api
155
+ CORS_ORIGIN=http://example.com,http://localhost:3000
156
+ RATE_LIMIT_WINDOW_MS=900000
157
+ RATE_LIMIT_MAX_REQUESTS=100
158
+ LOG_LEVEL=30
159
+ ```
160
+
161
+ 或者通过命令行参数:
162
+
163
+ ```bash
164
+ node server.js port=3000 host=localhost
165
+ ```
166
+
167
+ ## 📂 目录结构
168
+
169
+ ```
170
+ .
171
+ ├── src/
172
+ │ ├── app.js # Express 应用配置
173
+ │ ├── config.js # 默认配置
174
+ │ ├── server.js # 服务器启动逻辑
175
+ │ ├── routes.js # 默认路由
176
+ │ ├── middleware.js # 中间件集合
177
+ │ ├── logger.js # 日志系统
178
+ │ ├── utils.js # 工具函数
179
+ │ ├── staticServer.js # 静态文件服务器
180
+ │ └── resolvePath.js # 路径解析工具
181
+ ├── example.js # 使用示例
182
+ └── package.json
183
+ ```
184
+
185
+ ## 🔧 高级用法
186
+
187
+ ### 自定义中间件
188
+
189
+ ```javascript
190
+ import { startServer } from 'huxy-node-server';
191
+ import customMiddleware from './customMiddleware';
192
+
193
+ const { app } = await startServer({
194
+ port: 3000,
195
+ }, (config, app) => {
196
+ // 添加自定义中间件
197
+ app.use(customMiddleware);
198
+
199
+ // 添加自定义路由
200
+ app.get('/custom', (req, res) => {
201
+ res.json({ custom: 'route' });
202
+ });
203
+ });
204
+ ```
205
+
206
+ ### 自定义错误处理
207
+
208
+ ```javascript
209
+ import { startServer } from 'huxy-node-server';
210
+
211
+ const { app } = await startServer({
212
+ port: 3000,
213
+ }, (config, app) => {
214
+ // 添加自定义错误处理
215
+ app.use((err, req, res, next) => {
216
+ if (err instanceof CustomError) {
217
+ res.status(400).json({ error: err.message });
218
+ } else {
219
+ next(err);
220
+ }
221
+ });
222
+ });
223
+ ```
224
+
225
+ ### 自定义日志
226
+
227
+ ```javascript
228
+ import { startServer, createLogger } from 'huxy-node-server';
229
+
230
+ const customLogger = createLogger('custom-module', {
231
+ level: 'debug',
232
+ transport: {
233
+ target: 'pino-pretty',
234
+ options: { colorize: true }
235
+ }
236
+ });
237
+
238
+ customLogger.info('自定义日志消息');
239
+ ```
240
+
241
+ ### 与数据库集成
242
+
243
+ ```javascript
244
+ import { startServer } from 'huxy-node-server';
245
+ import mongoose from 'mongoose';
246
+
247
+ const { app } = await startServer({
248
+ port: 3000,
249
+ }, async (config, app) => {
250
+ // 连接到 MongoDB
251
+ await mongoose.connect(config.DATABASE_URL, {
252
+ useNewUrlParser: true,
253
+ useUnifiedTopology: true,
254
+ });
255
+
256
+ // 添加数据库中间件
257
+ app.use((req, res, next) => {
258
+ req.db = mongoose.connection;
259
+ next();
260
+ });
261
+
262
+ // 添加 API 路由
263
+ app.get('/api/users', async (req, res) => {
264
+ const users = await req.db.collection('users').find().toArray();
265
+ res.json({ success: true, data: users });
266
+ });
267
+ });
268
+ ```
269
+
270
+ ## 📊 API 文档
271
+
272
+ ### `startServer(config, callback)`
273
+
274
+ 启动 Express 服务器
275
+
276
+ **参数:**
277
+
278
+ - `config` (Object): 服务器配置对象
279
+ - `callback` (Function): 可选的回调函数,在服务器启动后调用
280
+
281
+ **返回:** Promise<{app, config, httpServer}>
282
+
283
+ ### `startStatic(config, callback)`
284
+
285
+ 启动静态文件服务器
286
+
287
+ **参数:**
288
+
289
+ - `config` (Object): 服务器配置对象
290
+ - `callback` (Function): 可选的回调函数,在服务器启动后调用
291
+
292
+ **返回:** Promise<{app, config, httpServer}>
293
+
294
+ ### `createLogger(name, customConfig)`
295
+
296
+ 创建自定义日志实例
297
+
298
+ **参数:**
299
+
300
+ - `name` (String): 日志实例名称
301
+ - `customConfig` (Object): 自定义配置
302
+
303
+ **返回:** Pino 日志实例
304
+
305
+ ### `logger`
306
+
307
+ 默认日志实例
308
+
309
+ ### 工具函数
310
+
311
+ - `dateTime()`: 获取当前时间字符串
312
+ - `localIPs()`: 获取本地 IP 地址列表
313
+ - `nodeArgs()`: 解析命令行参数
314
+ - `getEnvConfig()`: 获取环境变量配置
315
+ - `checkPort()`: 检查端口是否可用
316
+ - `resolvePath()`: 解析文件路径
317
+
318
+ ## 🛡️ 安全最佳实践
319
+
320
+ ### 1. 环境变量
321
+
322
+ 永远不要在代码中硬编码敏感信息,使用环境变量:
323
+
324
+ ```bash
325
+ # .env 文件
326
+ JWT_SECRET=your_secret_key_here
327
+ DATABASE_URL=your_database_url
328
+ ```
329
+
330
+ ### 2. HTTPS
331
+
332
+ 在生产环境中,始终使用 HTTPS。可以使用反向代理(如 Nginx)或直接配置:
333
+
334
+ ```javascript
335
+ import https from 'https';
336
+ import fs from 'fs';
337
+
338
+ const options = {
339
+ key: fs.readFileSync('server.key'),
340
+ cert: fs.readFileSync('server.cert')
341
+ };
342
+
343
+ https.createServer(options, app).listen(443);
344
+ ```
345
+
346
+ ### 3. 速率限制
347
+
348
+ 根据您的应用需求调整速率限制:
349
+
350
+ ```javascript
351
+ {
352
+ rateLimit: {
353
+ windowMs: 15 * 60 * 1000, // 15 分钟
354
+ limit: 100, // 每个 IP 每个窗口内最大请求数
355
+ message: '太多请求,请稍后再试'
356
+ }
357
+ }
358
+ ```
359
+
360
+ ### 4. CORS
361
+
362
+ 在生产环境中,限制 CORS 来源:
363
+
364
+ ```javascript
365
+ {
366
+ cors: {
367
+ origin: ['https://yourdomain.com', 'https://yourapp.com'],
368
+ credentials: true
369
+ }
370
+ }
371
+ ```
372
+
373
+ ### 5. 安全头
374
+
375
+ 根据需要调整 Helmet 配置:
376
+
377
+ ```javascript
378
+ {
379
+ helmet: {
380
+ contentSecurityPolicy: {
381
+ directives: {
382
+ defaultSrc: ["'self'"],
383
+ scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.example.com"],
384
+ // 其他 CSP 指令...
385
+ }
386
+ }
387
+ }
388
+ }
389
+ ```
390
+
391
+ ## 🚀 部署
392
+
393
+ ### Docker 部署
394
+
395
+ ```dockerfile
396
+ # Dockerfile
397
+ FROM node:20-alpine
398
+
399
+ WORKDIR /app
400
+
401
+ COPY package*.json ./
402
+ RUN npm install --production
403
+
404
+ COPY . .
405
+
406
+ EXPOSE 3000
407
+
408
+ CMD ["node", "src/index.js"]
409
+ ```
410
+
411
+ 构建并运行:
412
+
413
+ ```bash
414
+ docker build -t huxy-server .
415
+ docker run -p 3000:3000 -d huxy-server
416
+ ```
417
+
418
+ ### PM2 部署
419
+
420
+ ```bash
421
+ # 安装 PM2
422
+ npm install -g pm2
423
+
424
+ # 启动服务
425
+ pm2 start src/index.js --name huxy-server
426
+
427
+ # 保存进程列表
428
+ pm2 save
429
+
430
+ # 设置开机启动
431
+ pm2 startup
432
+ ```
433
+
434
+ ### Nginx 反向代理
435
+
436
+ ```nginx
437
+ server {
438
+ listen 80;
439
+ server_name yourdomain.com;
440
+
441
+ location / {
442
+ proxy_pass http://localhost:3000;
443
+ proxy_http_version 1.1;
444
+ proxy_set_header Upgrade $http_upgrade;
445
+ proxy_set_header Connection 'upgrade';
446
+ proxy_set_header Host $host;
447
+ proxy_cache_bypass $http_upgrade;
448
+ }
449
+ }
450
+ ```
451
+
452
+ ## 📝 许可证
453
+
454
+ MIT © [ahyiru](https://github.com/ahyiru)
455
+
456
+ ## 🤝 贡献
457
+
458
+ 欢迎贡献!请遵循以下步骤:
459
+
460
+ 1. Fork 仓库
461
+ 2. 创建您的特性分支 (`git checkout -b feature/AmazingFeature`)
462
+ 3. 提交您的更改 (`git commit -m 'Add some AmazingFeature'`)
463
+ 4. 推送到分支 (`git push origin feature/AmazingFeature`)
464
+ 5. 打开一个 Pull Request
465
+
466
+ ## 📞 支持
467
+
468
+ 如果您有任何问题或建议,请通过以下方式联系:
469
+
470
+ - GitHub Issues: https://github.com/ahyiru/huxy-node-server/issues
471
+ - 电子邮件: ahyiru@example.com
472
+
473
+ ## 📄 详细文档
474
+
475
+ - [文档索引](https://github.com/ahyiru/huxy-node-server/blob/main/docs/INDEX.md)
476
+ - [API 文档](https://github.com/ahyiru/huxy-node-server/blob/main/docs/API.md)
477
+ - [配置指南](https://github.com/ahyiru/huxy-node-server/blob/main/docs/CONFIGURATION.md)
478
+ - [部署指南](https://github.com/ahyiru/huxy-node-server/blob/main/docs/DEPLOYMENT.md)
479
+ - [更新日志](https://github.com/ahyiru/huxy-node-server/blob/main/CHANGELOG.md)
480
+
481
+ ## 📚 相关资源
482
+
483
+ - [Express.js 文档](https://expressjs.com/)
484
+ - [Pino 日志文档](https://getpino.io/)
485
+ - [Helmet 安全文档](https://helmetjs.github.io/)
486
+ - [Node.js 文档](https://nodejs.org/docs/)
487
+
488
+ ---
489
+
490
+ ✨ **Huxy Node Server** - 为现代 Web 应用程序提供强大、可靠的后端解决方案!
package/example.js ADDED
@@ -0,0 +1,34 @@
1
+ import {startServer, startStatic, logger, createLogger, dateTime} from './src/index.js';
2
+
3
+ logger.info(dateTime());
4
+
5
+ const testLogger = createLogger('test');
6
+
7
+ testLogger.info({x: 123}, '测试');
8
+ logger.error({status: 400}, 'HTTP请求错误');
9
+
10
+ // startServer
11
+ const {app, config, httpServer} = await startServer({
12
+ port: 8080,
13
+ host: 'localhost',
14
+ // ...
15
+ }, (config, app, httpServer) => {
16
+ app.get('/config', (req, res) => {
17
+ logger.info('详细配置:', config);
18
+ res.status(200).json({
19
+ result: config,
20
+ });
21
+ });
22
+ });
23
+
24
+ // startStatic
25
+ const huxyServer = await startStatic({
26
+ port: 9000,
27
+ basepath: '/',
28
+ buildPath: './build',
29
+ }, (config, app) => {
30
+ logger.info(config);
31
+ });
32
+
33
+
34
+ // 启动服务可加参数如:node example.js port=8080 或 PORT=8080 node example.js
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "huxy-node-server",
3
+ "version": "1.0.0-beta.0",
4
+ "description": "一个精炼、高性能的 Express.js 服务器模板,为现代 Node.js 应用程序设计,提供灵活的功能和最佳实践。",
5
+ "type": "module",
6
+ "module": "./src/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./src/index.js"
10
+ },
11
+ "./*": {
12
+ "import": "./src/*.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "start": "node ./src/index.js",
17
+ "dev": "NODE_ENV=development node --watch ./src/index.js"
18
+ },
19
+ "keywords": [
20
+ "express",
21
+ "nodejs",
22
+ "esm",
23
+ "server",
24
+ "huxy"
25
+ ],
26
+ "author": "ahyiru",
27
+ "license": "MIT",
28
+ "bugs": {
29
+ "url": "https://github.com/ahyiru/huxy-node-server/issues"
30
+ },
31
+ "homepage": "https://github.com/ahyiru/huxy-node-server#readme",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/ahyiru/huxy-node-server.git"
35
+ },
36
+ "dependencies": {
37
+ "compression": "^1.8.1",
38
+ "cors": "^2.8.5",
39
+ "dotenv": "^17.2.3",
40
+ "express": "^5.2.1",
41
+ "express-rate-limit": "^8.2.1",
42
+ "helmet": "^8.1.0",
43
+ "pino": "^10.1.0",
44
+ "pino-http": "^11.0.0",
45
+ "pino-pretty": "^13.1.3"
46
+ },
47
+ "engines": {
48
+ "node": ">=20.0.0"
49
+ }
50
+ }
package/src/index.js ADDED
@@ -0,0 +1,226 @@
1
+ import T from 'express';
2
+ import V from 'helmet';
3
+ import F from 'cors';
4
+ import {rateLimit as K, ipKeyGenerator as W} from 'express-rate-limit';
5
+ import z from 'compression';
6
+ import X from 'pino-http';
7
+ import {createServer as B} from 'node:http';
8
+ import U from 'pino';
9
+ import w from 'node:os';
10
+ import D from 'node:net';
11
+ var m = (t = new Date()) => t.toLocaleString('zh-CN', {timeZone: 'Asia/Shanghai', hour12: !1}),
12
+ l = t => {
13
+ let o = t ? 'https' : 'http',
14
+ e = w.networkInterfaces(),
15
+ r = [];
16
+ return (Object.keys(e).map(i => r.push(...e[i])), r.filter(i => i.family === 'IPv4').map(i => `${o}://${i.address}`));
17
+ },
18
+ x = t => {
19
+ let o = t ?? process.argv.slice(2) ?? [],
20
+ e = {};
21
+ return (
22
+ o.map(r => {
23
+ let [n, s] = r.split('=');
24
+ e[n] = s;
25
+ }),
26
+ e
27
+ );
28
+ },
29
+ H = {
30
+ NODE_ENV: 'nodeEnv',
31
+ PORT: 'port',
32
+ STATIC_PORT: 'staticPort',
33
+ HOST: 'host',
34
+ BASEPATH: 'basepath',
35
+ CORS_ORIGIN: 'cors.origin',
36
+ RATE_LIMIT_WINDOW_MS: 'rateLimit.windowMs',
37
+ RATE_LIMIT_MAX_REQUESTS: 'rateLimit.limit',
38
+ LOG_LEVEL: 'logLevel',
39
+ API_PREFIX: 'apiPrefix',
40
+ JWT_SECRET: 'secret',
41
+ AUTH_TOKEN: 'authToken',
42
+ },
43
+ M = (t, o, e) => {
44
+ let [r, n] = t.split('.');
45
+ r && n ? (e[r] || (e[r] = {}), (e[r][n] = o)) : (e[r] = o);
46
+ },
47
+ u = (t = {}, o = H) => {
48
+ let {env: e} = process;
49
+ Object.keys(o).map(n => {
50
+ let s = e[n];
51
+ s && M(o[n], s, t);
52
+ });
53
+ let r = {...t, ...x()};
54
+ return ((r.port = r.staticPort || r.port), (r.isDev = r.NODE_ENV === 'development'), r);
55
+ },
56
+ d = (t, o = '127.0.0.1') =>
57
+ new Promise(e => {
58
+ let r = D.createServer();
59
+ (r.once('error', n => {
60
+ (r.close(), e((n.code === 'EADDRINUSE', !1)));
61
+ }),
62
+ r.once('listening', () => {
63
+ (r.close(), e(!0));
64
+ }),
65
+ r.listen(Number(t), o));
66
+ });
67
+ import 'dotenv';
68
+ var k = {
69
+ nodeEnv: 'production',
70
+ isDev: !1,
71
+ port: parseInt(process.env.PORT || '3000', 10),
72
+ host: process.env.HOST || '0.0.0.0',
73
+ basepath: process.env.BASEPATH || '/',
74
+ cors: {origin: process.env.CORS_ORIGIN?.split(',') || '*', credentials: !0},
75
+ rateLimit: {
76
+ windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000', 10),
77
+ limit: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || '100', 10),
78
+ message: {error: '\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5'},
79
+ },
80
+ helmet: {
81
+ contentSecurityPolicy: {directives: {defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", 'data:', 'https:']}},
82
+ crossOriginEmbedderPolicy: !1,
83
+ },
84
+ logLevel: process.env.LOG_LEVEL || 30,
85
+ },
86
+ f = k;
87
+ var c = (t, o) =>
88
+ U({
89
+ name: t,
90
+ level: f.logLevel,
91
+ transport: {target: 'pino-pretty', options: {colorize: !0}, ignore: 'pid,hostname,level,time', translateTime: 'UTC:yyyy-mm-dd HH:MM:ss', customColors: 'err:red,info:blue'},
92
+ ...o,
93
+ }),
94
+ P = () => {
95
+ let t = c('http-request');
96
+ return (o, e, r) => {
97
+ let n = Date.now();
98
+ (e.on('finish', () => {
99
+ let s = Date.now() - n,
100
+ i = {method: o.method, url: o.originalUrl, status: e.statusCode, duration: `${s}ms`, ip: o.ip, userAgent: o.get('User-Agent'), timestamp: m()};
101
+ e.statusCode >= 500 ? t.error(i, 'HTTP\u8BF7\u6C42\u9519\u8BEF') : e.statusCode >= 400 ? t.warn(i, 'HTTP\u5BA2\u6237\u7AEF\u9519\u8BEF') : t.info(i, 'HTTP\u8BF7\u6C42');
102
+ }),
103
+ r());
104
+ };
105
+ },
106
+ a = c('huxy');
107
+ var y = c('error-handler'),
108
+ b = t => (o, e, r) => {
109
+ (y.error({message: 'Not Found', timestamp: m(), url: o.originalUrl, method: o.method, ip: o.ip, userAgent: o.get('User-Agent')}, '\u627E\u4E0D\u5230\u8DEF\u5F84'),
110
+ e.status(404).json({success: !1, timestamp: m(), status: 404, message: `\u8DEF\u7531 ${o.method} ${o.originalUrl} \u4E0D\u5B58\u5728`, url: o.originalUrl}));
111
+ },
112
+ A = t => (o, e, r, n) => {
113
+ let s = o.status || 500,
114
+ i = o.message;
115
+ (y.error({message: i, timestamp: m(), stack: o.stack, url: e.originalUrl, method: e.method, ip: e.ip, userAgent: e.get('User-Agent')}, '\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF'),
116
+ r.status(s).json({success: !1, timestamp: m(), message: t.isDev ? i : '\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF', stack: t.isDev ? o.stack : void 0}));
117
+ };
118
+ var L = t => (o, e, r) => {
119
+ (o.path.match(/\.(js|css|png|jpe?g|ico|webp|svg|mpeg|webm|m4a)$/) ? e.set('Cache-Control', 'public, max-age=31536000, immutable') : e.set('Cache-Control', 'no-cache'), r());
120
+ };
121
+ import {Router as j} from 'express';
122
+ var G = t => {
123
+ let o = j();
124
+ return (
125
+ o.use('/health', (e, r) => {
126
+ r.status(200).json({status: 'OK', timestamp: m(), uptime: process.uptime(), environment: t.nodeEnv, memoryUsage: process.memoryUsage(), pid: process.pid});
127
+ }),
128
+ o.get('/', (e, r) => {
129
+ r.status(200).json({message: 'Node.js \u670D\u52A1\u5668\u8FD0\u884C\u4E2D', timestamp: m(), environment: t.nodeEnv});
130
+ }),
131
+ o
132
+ );
133
+ },
134
+ N = G;
135
+ var Q = (t, o = {}) => (
136
+ t.disable('x-powered-by'),
137
+ t.set('trust proxy', 1),
138
+ t.use(V(o.helmet)),
139
+ t.use(F(o.cors)),
140
+ t.use(K({keyGenerator: e => W(e.ip) || e.headers['x-huxy-auth'] || e.headers['x-api-key'] || e.headers.authorization, ...o.rateLimit})),
141
+ t.use(z()),
142
+ t.use(T.json({limit: '20mb'})),
143
+ t.use(T.urlencoded({extended: !0, limit: '20mb'})),
144
+ t.use(X({logger: a, quietReqLogger: !0, autoLogging: !1})),
145
+ t.use(P()),
146
+ t.use(L(o)),
147
+ t
148
+ ),
149
+ J = t => {
150
+ let o = e => {
151
+ (a.info(`\u6536\u5230 ${e} \u4FE1\u53F7, \u{1F6D1} \u6B63\u5728\u5173\u95ED\u670D\u52A1\u5668...`),
152
+ t.close(() => {
153
+ (a.info('\u{1F44B} \u670D\u52A1\u5668\u5DF2\u5173\u95ED'), process.exit(0));
154
+ }),
155
+ setTimeout(() => {
156
+ (a.error('\u274C \u5F3A\u5236\u5173\u95ED\u670D\u52A1\u5668'), process.exit(1));
157
+ }, 5e3));
158
+ };
159
+ (process.on('SIGTERM', () => o('SIGTERM')),
160
+ process.on('SIGINT', () => o('SIGINT')),
161
+ process.on('uncaughtException', e => {
162
+ (a.error(e, `\u672A\u6355\u83B7\u7684\u5F02\u5E38: ${e.message}`), process.exit(1));
163
+ }),
164
+ process.on('unhandledRejection', (e, r) => {
165
+ (a.error({reason: e, promise: r}, '\u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD'), process.exit(1));
166
+ }));
167
+ },
168
+ Z = async (t, o) => {
169
+ let e = u(t),
170
+ {port: r} = e;
171
+ (await d(r, e.host)) || ((e.port = Number(r) + 1), a.warn(`\u7AEF\u53E3 ${r} \u5DF2\u88AB\u5360\u7528\uFF0C\u73B0\u5728\u4F7F\u7528\u7AEF\u53E3 ${e.port}`));
172
+ let s = T();
173
+ Q(s, e);
174
+ let i = B(s);
175
+ return (o?.(i, s, e), s.use(N(e)), s.use(b(e)), s.use(A(e)), J(i), {app: s, httpServer: i, config: e});
176
+ },
177
+ R = Z;
178
+ var Y = (t, o, e) =>
179
+ R({...f, ...t}, (r, n, s) => {
180
+ let {port: i, host: p, nodeEnv: h, basepath: I, appName: C = 'HuxyServer'} = s;
181
+ r.listen(i, p, () => {
182
+ if (!e) {
183
+ let $ = l()
184
+ .filter(v => v !== `http://${p}`)
185
+ .map(v => `http://${v}:${i}${I}`);
186
+ (a.info(`-----------------------${C}-----------------------`),
187
+ a.info(`\u{1F680} \u670D\u52A1\u8FD0\u884C\u5728\u3010${h}\u3011\u73AF\u5883: http://${p}:${i}${I}`),
188
+ a.info(`-----------------[${m()}]------------------`),
189
+ a.info({ips: $}, '\u672C\u5730\u5730\u5740\uFF1A'));
190
+ }
191
+ o?.(s, n, r);
192
+ });
193
+ }),
194
+ g = Y;
195
+ import oe from 'express';
196
+ import {fileURLToPath as q} from 'node:url';
197
+ import {dirname as ee, resolve as te} from 'node:path';
198
+ var E = (t = import.meta.url) => ee(q(t)),
199
+ S = t => te(E(), t),
200
+ _ = S;
201
+ var re = {port: 9e3, host: 'localhost', basepath: '/', buildPath: './build'},
202
+ se = (t, o) =>
203
+ g({...re, ...t}, (e, r, n) => {
204
+ let {basepath: s, buildPath: i} = e;
205
+ (r.use(s, oe.static(i, {maxAge: '1y', immutable: !0})),
206
+ r.get(`${s}/{*splat}`.replace('//', '/'), (p, h) => {
207
+ h.sendFile(_(i, 'index.html'));
208
+ }),
209
+ o?.(e, r, n));
210
+ }),
211
+ O = se;
212
+ var Ye = {startServer: g, startStatic: O, logger: a, createLogger: c, dateTime: m, localIPs: l, nodeArgs: x, getEnvConfig: u, checkPort: d, getDirName: E, resolvePath: S};
213
+ export {
214
+ d as checkPort,
215
+ c as createLogger,
216
+ m as dateTime,
217
+ Ye as default,
218
+ E as getDirName,
219
+ u as getEnvConfig,
220
+ l as localIPs,
221
+ a as logger,
222
+ x as nodeArgs,
223
+ S as resolvePath,
224
+ g as startServer,
225
+ O as startStatic,
226
+ };