mirror of
https://github.com/crystelf/crystelf-core.git
synced 2025-07-04 06:39:18 +00:00
模块路由加载优化,系统自动重启&路由方法
This commit is contained in:
parent
832bf3704c
commit
683df58d12
@ -1,7 +1,7 @@
|
|||||||
## 构建:
|
## 构建:
|
||||||
- pnpm i
|
- pnpm i
|
||||||
- pnpm build
|
- pnpm build
|
||||||
- pnpm start
|
- bash start.sh
|
||||||
|
|
||||||
## 使用:
|
## 使用:
|
||||||
- public/files/image/图片 支持多级目录
|
- public/files/image/图片 支持多级目录
|
||||||
|
70
src/app.ts
70
src/app.ts
@ -1,20 +1,19 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
import compression from 'compression';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
import logger from './utils/core/logger';
|
import logger from './utils/core/logger';
|
||||||
import paths from './utils/core/path';
|
import paths from './utils/core/path';
|
||||||
import sampleController from './modules/sample/sample.controller';
|
|
||||||
import imageController from './modules/image/file.controller';
|
|
||||||
import config from './utils/core/config';
|
import config from './utils/core/config';
|
||||||
import './services/ws/wsServer';
|
import './services/ws/wsServer';
|
||||||
import compression from 'compression';
|
import System from './utils/core/system';
|
||||||
import testController from './modules/test/test.controller';
|
|
||||||
import BotController from './modules/bot/bot.controller';
|
|
||||||
|
|
||||||
const apps = {
|
const apps = {
|
||||||
async createApp() {
|
async createApp() {
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
paths.init();
|
paths.init();
|
||||||
logger.info('晶灵核心初始化..');
|
logger.info('晶灵核心初始化..');
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
logger.debug('成功加载 express.json() 中间件');
|
logger.debug('成功加载 express.json() 中间件');
|
||||||
@ -23,28 +22,61 @@ const apps = {
|
|||||||
app.use('/public', express.static(publicPath));
|
app.use('/public', express.static(publicPath));
|
||||||
logger.debug(`静态资源路由挂载: /public => ${publicPath}`);
|
logger.debug(`静态资源路由挂载: /public => ${publicPath}`);
|
||||||
|
|
||||||
const modules = [
|
const modulesDir = path.resolve(__dirname, './modules');
|
||||||
{ path: '/api/sample', name: '测试模块', controller: sampleController },
|
const controllerPattern = /\.controller\.[jt]s$/;
|
||||||
{ path: '/public', name: '文件模块', controller: imageController },
|
|
||||||
{ path: '/api/test', name: '测试', controller: testController },
|
|
||||||
{ path: '/api/bot', name: '寄气人模块', controller: BotController },
|
|
||||||
];
|
|
||||||
|
|
||||||
modules.forEach((module) => {
|
if (!fs.existsSync(modulesDir)) {
|
||||||
app.use(module.path, module.controller.getRouter());
|
logger.warn(`未找到模块目录: ${modulesDir}`);
|
||||||
logger.debug(`模块路由挂载: ${module.path.padEnd(12)} => ${module.name}`);
|
} else {
|
||||||
|
const moduleFolders = fs.readdirSync(modulesDir).filter((folder) => {
|
||||||
|
const fullPath = path.join(modulesDir, folder);
|
||||||
|
return fs.statSync(fullPath).isDirectory();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const folder of moduleFolders) {
|
||||||
|
const folderPath = path.join(modulesDir, folder);
|
||||||
|
const files = fs.readdirSync(folderPath).filter((f) => controllerPattern.test(f));
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const filePath = path.join(folderPath, file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
//logger.debug(`尝试加载模块: ${moduleUrl}`);
|
||||||
|
const controllerModule = require(filePath);
|
||||||
|
const controller = controllerModule.default;
|
||||||
|
|
||||||
|
if (controller?.getRouter) {
|
||||||
|
const routePath = `/api/${folder}`;
|
||||||
|
app.use(routePath, controller.getRouter());
|
||||||
|
logger.debug(`模块路由挂载: ${routePath.padEnd(12)} => ${file}`);
|
||||||
|
|
||||||
if (config.get('DEBUG', false)) {
|
if (config.get('DEBUG', false)) {
|
||||||
module.controller.getRouter().stack.forEach((layer) => {
|
controller.getRouter().stack.forEach((layer: any) => {
|
||||||
if (layer.route) {
|
if (layer.route) {
|
||||||
const methods = Object.keys(layer.route)
|
const methods = Object.keys(layer.route.methods || {})
|
||||||
.map((m) => m.toUpperCase())
|
.map((m) => m.toUpperCase())
|
||||||
.join(',');
|
.join(',');
|
||||||
logger.debug(` ↳ ${methods.padEnd(6)} ${module.path}${layer.route.path}`);
|
logger.debug(` ↳ ${methods.padEnd(6)} ${routePath}${layer.route.path}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
logger.warn(`模块 ${file} 没有导出 getRouter 方法,跳过..`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`模块 ${file} 加载失败:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const duration = System.checkRestartTime();
|
||||||
|
//logger.info(duration);
|
||||||
|
if (duration) {
|
||||||
|
logger.warn(`重启完成!耗时 ${duration} 秒..`);
|
||||||
|
fs.writeFileSync('/temp/restart_time', duration.toString());
|
||||||
|
}
|
||||||
|
|
||||||
logger.info('晶灵核心初始化完毕!');
|
logger.info('晶灵核心初始化完毕!');
|
||||||
return app;
|
return app;
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import response from '../../utils/core/response';
|
import response from '../../utils/core/response';
|
||||||
import BotService from './bot.service';
|
import BotService from './bot.service';
|
||||||
import Config from '../../utils/core/config';
|
import tools from '../../utils/modules/tools';
|
||||||
|
|
||||||
class BotController {
|
class BotController {
|
||||||
private readonly router: express.Router;
|
private readonly router: express.Router;
|
||||||
@ -19,19 +19,19 @@ class BotController {
|
|||||||
this.router.post(`/getBotId`, this.postBotsId);
|
this.router.post(`/getBotId`, this.postBotsId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前连接到核心的全部botId数组
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
private postBotsId = async (req: express.Request, res: express.Response): Promise<void> => {
|
private postBotsId = async (req: express.Request, res: express.Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const token = req.body.token;
|
const token = req.body.token;
|
||||||
if (token.toString() === Config.get('TOKEN').toString()) {
|
if (tools.checkToken(token.toString())) {
|
||||||
const result = await BotService.getBotId();
|
const result = await BotService.getBotId();
|
||||||
await response.success(res, result);
|
await response.success(res, result);
|
||||||
} else {
|
} else {
|
||||||
await response.error(
|
await tools.tokenCheckFailed(res, token);
|
||||||
res,
|
|
||||||
'token验证失败..',
|
|
||||||
404,
|
|
||||||
`有个小可爱使用了错误的token:${JSON.stringify(token)}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
await response.error(res, `请求失败..`, 500, err);
|
await response.error(res, `请求失败..`, 500, err);
|
||||||
|
@ -5,6 +5,9 @@ import path from 'path';
|
|||||||
import redisService from '../../services/redis/redis';
|
import redisService from '../../services/redis/redis';
|
||||||
|
|
||||||
class BotService {
|
class BotService {
|
||||||
|
/**
|
||||||
|
* 获取botId数组
|
||||||
|
*/
|
||||||
public async getBotId() {
|
public async getBotId() {
|
||||||
logger.debug('GetBotId..');
|
logger.debug('GetBotId..');
|
||||||
const userPath = paths.get('userData');
|
const userPath = paths.get('userData');
|
||||||
|
62
src/modules/system/system.controller.ts
Normal file
62
src/modules/system/system.controller.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import tools from '../../utils/modules/tools';
|
||||||
|
import response from '../../utils/core/response';
|
||||||
|
import SystemService from './system.service';
|
||||||
|
|
||||||
|
class SystemController {
|
||||||
|
private readonly router: express.Router;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.router = express.Router();
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRouter(): express.Router {
|
||||||
|
return this.router;
|
||||||
|
}
|
||||||
|
|
||||||
|
private init(): void {
|
||||||
|
this.router.post('/restart', this.systemRestart);
|
||||||
|
this.router.post('/getRestartTime', this.getRestartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统重启路由
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
private systemRestart = async (req: express.Request, res: express.Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const token = req.body.token;
|
||||||
|
if (tools.checkToken(token.toString())) {
|
||||||
|
await SystemService.systemRestart();
|
||||||
|
await response.success(res, '核心正在重启..');
|
||||||
|
} else {
|
||||||
|
await tools.tokenCheckFailed(res, token);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
await response.error(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取重启所需时间
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
*/
|
||||||
|
private getRestartTime = async (req: express.Request, res: express.Response): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const token = req.body.token;
|
||||||
|
if (tools.checkToken(token.toString())) {
|
||||||
|
const time = await SystemService.getRestartTime();
|
||||||
|
await response.success(res, time);
|
||||||
|
} else {
|
||||||
|
await tools.tokenCheckFailed(res, token);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
await response.error(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SystemController();
|
17
src/modules/system/system.service.ts
Normal file
17
src/modules/system/system.service.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import System from '../../utils/core/system';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
import logger from '../../utils/core/logger';
|
||||||
|
|
||||||
|
class SystemService {
|
||||||
|
public async systemRestart() {
|
||||||
|
logger.debug(`有个小可爱正在请求重启核心..`);
|
||||||
|
await System.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getRestartTime() {
|
||||||
|
logger.debug(`有个小可爱想知道核心重启花了多久..`);
|
||||||
|
return await fs.readFile('/temp/restart_time', 'utf8');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SystemService();
|
@ -48,7 +48,9 @@ async function testGetAPI() {
|
|||||||
|
|
||||||
async function testPostAPI() {
|
async function testPostAPI() {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post('http://localhost:4000/api/bot/getBotId', { token: 54188 });
|
const response = await axios.post('http://localhost:4000/api/system/getRestartTime', {
|
||||||
|
token: 54188,
|
||||||
|
});
|
||||||
console.log('[HTTP][POST] Response:', response.data);
|
console.log('[HTTP][POST] Response:', response.data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[HTTP][POST] Error:', err);
|
console.error('[HTTP][POST] Error:', err);
|
||||||
|
@ -36,6 +36,7 @@ class PathManager {
|
|||||||
files: path.join(this.baseDir, 'public/files'),
|
files: path.join(this.baseDir, 'public/files'),
|
||||||
media: path.join(this.baseDir, 'public/files/media'),
|
media: path.join(this.baseDir, 'public/files/media'),
|
||||||
package: path.join(this.baseDir, 'package.json'),
|
package: path.join(this.baseDir, 'package.json'),
|
||||||
|
modules: path.join(this.baseDir, 'src/modules'),
|
||||||
};
|
};
|
||||||
|
|
||||||
return type ? mappings[type] : this.baseDir;
|
return type ? mappings[type] : this.baseDir;
|
||||||
@ -63,6 +64,7 @@ class PathManager {
|
|||||||
this.get('config'),
|
this.get('config'),
|
||||||
this.get('userData'),
|
this.get('userData'),
|
||||||
this.get('media'),
|
this.get('media'),
|
||||||
|
this.get('temp'),
|
||||||
];
|
];
|
||||||
|
|
||||||
pathsToInit.forEach((dirPath) => {
|
pathsToInit.forEach((dirPath) => {
|
||||||
@ -83,7 +85,8 @@ type PathType =
|
|||||||
| 'userData'
|
| 'userData'
|
||||||
| 'files'
|
| 'files'
|
||||||
| 'package'
|
| 'package'
|
||||||
| 'media';
|
| 'media'
|
||||||
|
| 'modules';
|
||||||
|
|
||||||
const paths = PathManager.getInstance();
|
const paths = PathManager.getInstance();
|
||||||
export default paths;
|
export default paths;
|
||||||
|
@ -23,7 +23,12 @@ class response {
|
|||||||
* @param statusCode HTTP状态码,默认500
|
* @param statusCode HTTP状态码,默认500
|
||||||
* @param error 原始错误对象(开发环境显示)
|
* @param error 原始错误对象(开发环境显示)
|
||||||
*/
|
*/
|
||||||
public static async error(res: Response, message: string, statusCode = 500, error?: any) {
|
public static async error(
|
||||||
|
res: Response,
|
||||||
|
message: string = '请求失败..',
|
||||||
|
statusCode = 500,
|
||||||
|
error?: any
|
||||||
|
) {
|
||||||
const response: Record<string, any> = {
|
const response: Record<string, any> = {
|
||||||
success: false,
|
success: false,
|
||||||
message,
|
message,
|
||||||
|
40
src/utils/core/system.ts
Normal file
40
src/utils/core/system.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import paths from './path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import logger from './logger';
|
||||||
|
|
||||||
|
const restartFile = path.join(paths.get('temp'), 'restart.timestamp');
|
||||||
|
class System {
|
||||||
|
/**
|
||||||
|
* 重启前保存时间戳
|
||||||
|
*/
|
||||||
|
private static markRestartTime() {
|
||||||
|
const now = Date.now();
|
||||||
|
fs.writeFileSync(restartFile, now.toString(), 'utf-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查重启时间戳
|
||||||
|
*/
|
||||||
|
public static checkRestartTime() {
|
||||||
|
if (fs.existsSync(restartFile)) {
|
||||||
|
const prev = Number(fs.readFileSync(restartFile, 'utf-8'));
|
||||||
|
const duration = ((Date.now() - prev) / 1000 - 5).toFixed(2);
|
||||||
|
fs.unlinkSync(restartFile);
|
||||||
|
return Number(duration);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重启服务
|
||||||
|
*/
|
||||||
|
public static async restart() {
|
||||||
|
this.markRestartTime();
|
||||||
|
logger.warn('服务即将重启..');
|
||||||
|
await new Promise((r) => setTimeout(r, 300));
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default System;
|
29
src/utils/modules/tools.ts
Normal file
29
src/utils/modules/tools.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import response from '../core/response';
|
||||||
|
import Config from '../core/config';
|
||||||
|
|
||||||
|
let tools = {
|
||||||
|
/**
|
||||||
|
* token验证错误处理逻辑
|
||||||
|
* @param res
|
||||||
|
* @param token
|
||||||
|
*/
|
||||||
|
async tokenCheckFailed(res: express.Response, token: string): Promise<void> {
|
||||||
|
await response.error(
|
||||||
|
res,
|
||||||
|
'token验证失败..',
|
||||||
|
404,
|
||||||
|
`有个小可爱使用了错误的token:${JSON.stringify(token)}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查token是否正确
|
||||||
|
* @param token
|
||||||
|
*/
|
||||||
|
checkToken(token: string): boolean {
|
||||||
|
return token.toString() === Config.get('TOKEN').toString();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default tools;
|
Loading…
x
Reference in New Issue
Block a user