mirror of
https://github.com/crystelf/crystelf-core.git
synced 2025-07-04 14:49:19 +00:00
commit
66c82dcb01
62
.idea/codeStyles/Project.xml
generated
Normal file
62
.idea/codeStyles/Project.xml
generated
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<HTMLCodeStyleSettings>
|
||||||
|
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||||
|
</HTMLCodeStyleSettings>
|
||||||
|
<JSCodeStyleSettings version="0">
|
||||||
|
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||||
|
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||||
|
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||||
|
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||||
|
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||||
|
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||||
|
</JSCodeStyleSettings>
|
||||||
|
<TypeScriptCodeStyleSettings version="0">
|
||||||
|
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||||
|
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||||
|
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||||
|
<option name="PREFER_EXPLICIT_TYPES_VARS_FIELDS" value="true" />
|
||||||
|
<option name="PREFER_EXPLICIT_TYPES_FUNCTION_RETURNS" value="true" />
|
||||||
|
<option name="PREFER_EXPLICIT_TYPES_FUNCTION_EXPRESSION_RETURNS" value="true" />
|
||||||
|
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||||
|
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||||
|
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||||
|
</TypeScriptCodeStyleSettings>
|
||||||
|
<VueCodeStyleSettings>
|
||||||
|
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||||
|
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||||
|
</VueCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="HTML">
|
||||||
|
<option name="SOFT_MARGINS" value="100" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="JavaScript">
|
||||||
|
<option name="SOFT_MARGINS" value="100" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="TypeScript">
|
||||||
|
<option name="SOFT_MARGINS" value="100" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="Vue">
|
||||||
|
<option name="SOFT_MARGINS" value="100" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
8
.idea/prettier.xml
generated
Normal file
8
.idea/prettier.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PrettierConfiguration">
|
||||||
|
<option name="myConfigurationMode" value="MANUAL" />
|
||||||
|
<option name="myRunOnSave" value="true" />
|
||||||
|
<option name="myRunOnReformat" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
14
.idea/webResources.xml
generated
Normal file
14
.idea/webResources.xml
generated
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="WebResourcesPaths">
|
||||||
|
<contentEntries>
|
||||||
|
<entry url="file://$PROJECT_DIR$">
|
||||||
|
<entryData>
|
||||||
|
<resourceRoots>
|
||||||
|
<path value="file://$PROJECT_DIR$/src" />
|
||||||
|
</resourceRoots>
|
||||||
|
</entryData>
|
||||||
|
</entry>
|
||||||
|
</contentEntries>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -9,7 +9,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "4",
|
"chalk": "4",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"express": "^4.18.0"
|
"express": "^4.18.0",
|
||||||
|
"mkdirp": "^3.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^4.17.0",
|
"@types/express": "^4.17.0",
|
||||||
|
49
src/app.ts
49
src/app.ts
@ -1,23 +1,50 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import logger from './utils/logger';
|
import logger from './utils/core/logger';
|
||||||
import fc from './utils/file';
|
import paths from './utils/core/path';
|
||||||
import paths from './utils/path';
|
|
||||||
import sampleController from './modules/sample/sample.controller';
|
import sampleController from './modules/sample/sample.controller';
|
||||||
import imageController from './modules/image/image.controller';
|
import imageController from './modules/image/image.controller';
|
||||||
|
import Config from './utils/core/config';
|
||||||
|
import fc from './utils/core/file';
|
||||||
|
|
||||||
const apps = {
|
const apps = {
|
||||||
createApp() {
|
createApp() {
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
logger.info('晶灵核心初始化..');
|
||||||
|
|
||||||
|
Config.check(['PORT', 'DEBUG']);
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use('/public', express.static(paths.get('public')));
|
logger.debug('成功加载express.json()中间件');
|
||||||
logger.debug(`路由/public挂载成功..`);
|
|
||||||
app.use('/api/sample', sampleController.getRouter());
|
const publicPath = paths.get('public');
|
||||||
logger.debug(`路由/api/sample挂载成功..`);
|
app.use('/public', express.static(publicPath));
|
||||||
app.use('/images', imageController.getRouter());
|
logger.debug(`静态资源路由挂载:/public → ${publicPath}`);
|
||||||
logger.debug(`路由/images挂载成功..`);
|
|
||||||
fc.createDir(paths.get('log'));
|
const modules = [
|
||||||
logger.info('晶灵核心初始化成功!');
|
{ path: '/api/sample', name: '测试模块', controller: sampleController },
|
||||||
|
{ path: '/images', name: '图像模块', controller: imageController },
|
||||||
|
];
|
||||||
|
|
||||||
|
modules.forEach((module) => {
|
||||||
|
app.use(module.path, module.controller.getRouter());
|
||||||
|
logger.debug(`模块路由挂载: ${module.path.padEnd(12)} → ${module.name}`);
|
||||||
|
|
||||||
|
if (Config.get('DEBUG', false)) {
|
||||||
|
module.controller.getRouter().stack.forEach((layer) => {
|
||||||
|
if (layer.route) {
|
||||||
|
const methods = Object.keys(layer.route)
|
||||||
|
.map((m) => m.toUpperCase())
|
||||||
|
.join(',');
|
||||||
|
logger.debug(` ↳ ${methods.padEnd(6)} ${module.path}${layer.route.path}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const logPath = paths.get('log');
|
||||||
|
fc.createDir(logPath);
|
||||||
|
logger.debug(`日志目录初始化: ${logPath}`);
|
||||||
|
logger.info('晶灵核心初始化完毕!');
|
||||||
return app;
|
return app;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
1
src/config/keep
Normal file
1
src/config/keep
Normal file
@ -0,0 +1 @@
|
|||||||
|
keep
|
@ -1,5 +1,5 @@
|
|||||||
import apps from './app';
|
import apps from './app';
|
||||||
import logger from './utils/logger';
|
import logger from './utils/core/logger';
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
1
src/middlewares/keep
Normal file
1
src/middlewares/keep
Normal file
@ -0,0 +1 @@
|
|||||||
|
keep
|
@ -1,6 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import ImageService from './image.service';
|
import ImageService from './image.service';
|
||||||
import logger from '../../utils/logger';
|
import logger from '../../utils/core/logger';
|
||||||
|
|
||||||
class ImageController {
|
class ImageController {
|
||||||
private readonly router: express.Router;
|
private readonly router: express.Router;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import paths from '../../utils/path';
|
import paths from '../../utils/core/path';
|
||||||
import logger from '../../utils/logger';
|
import logger from '../../utils/core/logger';
|
||||||
|
|
||||||
class ImageService {
|
class ImageService {
|
||||||
private readonly imageDir: string;
|
private readonly imageDir: string;
|
||||||
|
1
src/modules/monitor/kep
Normal file
1
src/modules/monitor/kep
Normal file
@ -0,0 +1 @@
|
|||||||
|
keep
|
@ -1,5 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import sampleService from './sample.service';
|
import sampleService from './sample.service';
|
||||||
|
import response from '../../utils/core/response';
|
||||||
|
|
||||||
class SampleController {
|
class SampleController {
|
||||||
private readonly router: express.Router;
|
private readonly router: express.Router;
|
||||||
@ -21,37 +22,24 @@ class SampleController {
|
|||||||
private getHello = (req: express.Request, res: express.Response): void => {
|
private getHello = (req: express.Request, res: express.Response): void => {
|
||||||
try {
|
try {
|
||||||
const result = sampleService.getHello();
|
const result = sampleService.getHello();
|
||||||
this.sendSuccess(res, result);
|
response.success(res, result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.sendError(res, error);
|
response.error(res, '请求失败了..', 500, error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private postGreet = (req: express.Request, res: express.Response): void => {
|
private postGreet = (req: express.Request, res: express.Response): void => {
|
||||||
try {
|
try {
|
||||||
const { name } = req.body;
|
const { name } = req.body;
|
||||||
|
if (!name) {
|
||||||
|
return response.error(res, '姓名不能为空!', 400);
|
||||||
|
}
|
||||||
const result = sampleService.generateGreeting(name);
|
const result = sampleService.generateGreeting(name);
|
||||||
this.sendSuccess(res, result);
|
response.success(res, result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.sendError(res, error);
|
response.error(res, '请求失败了..', 500, error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private sendSuccess(res: express.Response, data: any, statusCode = 200): void {
|
|
||||||
res.status(statusCode).json({
|
|
||||||
success: true,
|
|
||||||
data,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private sendError(res: express.Response, error: any, statusCode = 500): void {
|
|
||||||
res.status(statusCode).json({
|
|
||||||
success: false,
|
|
||||||
message: error.message,
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new SampleController();
|
export default new SampleController();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import logger from '../../utils/logger';
|
import logger from '../../utils/core/logger';
|
||||||
|
|
||||||
class SampleService {
|
class SampleService {
|
||||||
getHello() {
|
getHello() {
|
||||||
@ -6,7 +6,7 @@ class SampleService {
|
|||||||
return { message: 'Hello World!' };
|
return { message: 'Hello World!' };
|
||||||
}
|
}
|
||||||
|
|
||||||
generateGreeting(name: string) {
|
generateGreeting(name: string): object {
|
||||||
logger.debug(`有个小可爱正在请求generateGreeting方法..`);
|
logger.debug(`有个小可爱正在请求generateGreeting方法..`);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
logger.warn('Name is required');
|
logger.warn('Name is required');
|
||||||
|
1
src/modules/trss/keep
Normal file
1
src/modules/trss/keep
Normal file
@ -0,0 +1 @@
|
|||||||
|
keep
|
1
src/types/keep
Normal file
1
src/types/keep
Normal file
@ -0,0 +1 @@
|
|||||||
|
keep
|
@ -1,23 +0,0 @@
|
|||||||
import dotenv from 'dotenv';
|
|
||||||
|
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
let config = {
|
|
||||||
get: (key: string, defaultValue: any) => {
|
|
||||||
const value = process.env[key];
|
|
||||||
if (value === undefined) {
|
|
||||||
if (defaultValue !== undefined) return defaultValue;
|
|
||||||
throw new Error(`环境变量${key}未定义..`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof defaultValue === 'number') return Number(value);
|
|
||||||
if (typeof defaultValue === 'boolean') return value === 'true';
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
|
|
||||||
set: (key: string, value: any) => {
|
|
||||||
process.env[key] = value;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
1
src/utils/constants/keep
Normal file
1
src/utils/constants/keep
Normal file
@ -0,0 +1 @@
|
|||||||
|
keep
|
72
src/utils/core/config.ts
Normal file
72
src/utils/core/config.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import dotenv from 'dotenv';
|
||||||
|
import logger from './logger';
|
||||||
|
|
||||||
|
class ConfigManger {
|
||||||
|
private static instance: ConfigManger;
|
||||||
|
private env: NodeJS.ProcessEnv;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
dotenv.config();
|
||||||
|
this.env = process.env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单例实例
|
||||||
|
*/
|
||||||
|
public static getInstance(): ConfigManger {
|
||||||
|
if (!ConfigManger.instance) {
|
||||||
|
ConfigManger.instance = new ConfigManger();
|
||||||
|
}
|
||||||
|
return ConfigManger.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取环境变量(带类型推断)
|
||||||
|
* @param key 环境变量键名
|
||||||
|
* @param defaultValue 默认值(决定返回类型)
|
||||||
|
*/
|
||||||
|
public get<T = string>(key: string, defaultValue?: T): T {
|
||||||
|
const value = this.env[key];
|
||||||
|
if (value === undefined) {
|
||||||
|
if (defaultValue !== undefined) return defaultValue;
|
||||||
|
logger.error(`环境变量${key}未定义!`);
|
||||||
|
return undefined as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (typeof defaultValue) {
|
||||||
|
case 'number':
|
||||||
|
return Number(value) as T;
|
||||||
|
case 'boolean':
|
||||||
|
return (value === 'true') as T;
|
||||||
|
default:
|
||||||
|
return value as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置环境变量(运行时有效)
|
||||||
|
* @param key 键名
|
||||||
|
* @param value 值
|
||||||
|
*/
|
||||||
|
public set(key: string, value: string | number | boolean): void {
|
||||||
|
this.env[key] = String(value);
|
||||||
|
logger.debug(`成功更改环境变量${key}为${value}!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查环境变量是否已加载
|
||||||
|
*/
|
||||||
|
public check(keys: string[]): void {
|
||||||
|
keys.forEach((key) => {
|
||||||
|
if (!(key in this.env)) {
|
||||||
|
logger.error(`必须环境变量缺失:${key}`);
|
||||||
|
} else {
|
||||||
|
logger.debug(`检测到环境变量${key}!`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Config = ConfigManger.getInstance();
|
||||||
|
|
||||||
|
export default Config;
|
74
src/utils/core/date.ts
Normal file
74
src/utils/core/date.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
class date {
|
||||||
|
/**
|
||||||
|
* 获取当前日期 (格式: YYYYMMDD)
|
||||||
|
*/
|
||||||
|
public static getCurrentDate(): string {
|
||||||
|
const now = new Date();
|
||||||
|
return [
|
||||||
|
now.getFullYear(),
|
||||||
|
(now.getMonth() + 1).toString().padStart(2, '0'),
|
||||||
|
now.getDate().toString().padStart(2, '0'),
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前时间 (格式: HH:mm:ss)
|
||||||
|
*/
|
||||||
|
public static getCurrentTime(): string {
|
||||||
|
return new Date().toLocaleTimeString('en-US', {
|
||||||
|
hour12: false,
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取格式化日期时间
|
||||||
|
* @param formatStr 格式字符串 (YYYY-年, MM-月, DD-日, HH-时, mm-分, ss-秒)
|
||||||
|
* @example format('YYYY-MM-DD HH:mm:ss') => '2023-10-15 14:30:45'
|
||||||
|
*/
|
||||||
|
public static format(formatStr: string = 'YYYY-MM-DD HH:mm:ss'): string {
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
const replacements: Record<string, string> = {
|
||||||
|
YYYY: now.getFullYear().toString(),
|
||||||
|
MM: (now.getMonth() + 1).toString().padStart(2, '0'),
|
||||||
|
DD: now.getDate().toString().padStart(2, '0'),
|
||||||
|
HH: now.getHours().toString().padStart(2, '0'),
|
||||||
|
mm: now.getMinutes().toString().padStart(2, '0'),
|
||||||
|
ss: now.getSeconds().toString().padStart(2, '0'),
|
||||||
|
};
|
||||||
|
|
||||||
|
return formatStr.replace(/YYYY|MM|DD|HH|mm|ss/g, (match) => replacements[match]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算日期差值
|
||||||
|
* @param start 开始日期
|
||||||
|
* @param end 结束日期 (默认当前时间)
|
||||||
|
* @param unit 返回单位 ('days' | 'hours' | 'minutes' | 'seconds')
|
||||||
|
*/
|
||||||
|
public static diff(
|
||||||
|
start: Date,
|
||||||
|
end: Date = new Date(),
|
||||||
|
unit: 'days' | 'hours' | 'minutes' | 'seconds' = 'days'
|
||||||
|
): number {
|
||||||
|
const msDiff = end.getTime() - start.getTime();
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case 'seconds':
|
||||||
|
return Math.floor(msDiff / 1000);
|
||||||
|
case 'minutes':
|
||||||
|
return Math.floor(msDiff / (1000 * 60));
|
||||||
|
case 'hours':
|
||||||
|
return Math.floor(msDiff / (1000 * 60 * 60));
|
||||||
|
case 'days':
|
||||||
|
return Math.floor(msDiff / (1000 * 60 * 60 * 24));
|
||||||
|
default:
|
||||||
|
return msDiff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default date;
|
@ -1,13 +1,14 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import paths from './path';
|
|
||||||
import date from './date';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import paths from './path';
|
||||||
|
import date from './date';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
|
|
||||||
let fc = {
|
class fc {
|
||||||
createDir: (targetPath: string = '', includeFile: boolean = false) => {
|
public static createDir(targetPath: string = '', includeFile: boolean = false): void {
|
||||||
const root = paths.get('root');
|
const root = paths.get('root');
|
||||||
|
|
||||||
if (path.isAbsolute(targetPath)) {
|
if (path.isAbsolute(targetPath)) {
|
||||||
if (includeFile) {
|
if (includeFile) {
|
||||||
const parentDir = path.dirname(targetPath);
|
const parentDir = path.dirname(targetPath);
|
||||||
@ -17,6 +18,7 @@ let fc = {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(targetPath)) {
|
if (!fs.existsSync(targetPath)) {
|
||||||
fs.mkdirSync(targetPath, { recursive: true });
|
fs.mkdirSync(targetPath, { recursive: true });
|
||||||
logger.debug(`成功创建绝对目录: ${targetPath}`);
|
logger.debug(`成功创建绝对目录: ${targetPath}`);
|
||||||
@ -27,20 +29,21 @@ let fc = {
|
|||||||
const fullPath = includeFile
|
const fullPath = includeFile
|
||||||
? path.join(root, path.dirname(targetPath))
|
? path.join(root, path.dirname(targetPath))
|
||||||
: path.join(root, targetPath);
|
: path.join(root, targetPath);
|
||||||
|
|
||||||
if (!fs.existsSync(fullPath)) {
|
if (!fs.existsSync(fullPath)) {
|
||||||
fs.mkdirSync(fullPath, { recursive: true });
|
fs.mkdirSync(fullPath, { recursive: true });
|
||||||
logger.debug(`成功创建相对目录: ${fullPath}`);
|
logger.debug(`成功创建相对目录: ${fullPath}`);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
logToFile: (level: string, message: string) => {
|
public static logToFile(level: string, message: string): void {
|
||||||
const logFile = path.join(paths.get('log'), `${date.getCurrentDate()}.log`);
|
const logFile = path.join(paths.get('log'), `${date.getCurrentDate()}.log`);
|
||||||
const logMessage = `[${date.getCurrentTime()}] [${level}] ${message}\n`;
|
const logMessage = `[${date.getCurrentTime()}] [${level}] ${message}\n`;
|
||||||
|
|
||||||
fs.appendFile(logFile, logMessage, (err) => {
|
fs.appendFile(logFile, logMessage, (err) => {
|
||||||
if (err) console.error(chalk.red('[LOGGER] 写入日志失败:'), err);
|
if (err) console.error(chalk.red('[LOGGER] 写入日志失败:'), err);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default fc;
|
export default fc;
|
68
src/utils/core/logger.ts
Normal file
68
src/utils/core/logger.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import chalk from 'chalk';
|
||||||
|
import Config from './config';
|
||||||
|
import fc from './file';
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
private static instance: Logger;
|
||||||
|
private readonly isDebug: boolean;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
this.isDebug = Config.get('DEBUG', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance(): Logger {
|
||||||
|
if (!Logger.instance) {
|
||||||
|
Logger.instance = new Logger();
|
||||||
|
}
|
||||||
|
return Logger.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public debug(...args: any[]): void {
|
||||||
|
if (this.isDebug) {
|
||||||
|
const message = this.formatMessage('DEBUG', args);
|
||||||
|
console.log(chalk.cyan(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public info(...args: any[]): void {
|
||||||
|
const message = this.formatMessage('INFO', args);
|
||||||
|
console.log(chalk.green(message));
|
||||||
|
this.logToFile('INFO', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public warn(...args: any[]): void {
|
||||||
|
const message = this.formatMessage('WARN', args);
|
||||||
|
console.log(chalk.yellow(message));
|
||||||
|
this.logToFile('WARN', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public error(...args: any[]): void {
|
||||||
|
const message = this.formatMessage('ERROR', args);
|
||||||
|
console.error(chalk.red(message));
|
||||||
|
this.logToFile('ERROR', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fatal(args: any[], exitCode: number = 1): never {
|
||||||
|
const message = this.formatMessage('FATAL', args);
|
||||||
|
console.error(chalk.red.bold(message));
|
||||||
|
this.logToFile('FATAL', message);
|
||||||
|
process.exit(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatMessage(level: string, args: any[]): string {
|
||||||
|
return `[${level}] ${args
|
||||||
|
.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : arg))
|
||||||
|
.join(' ')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private logToFile(level: string, message: string): void {
|
||||||
|
try {
|
||||||
|
fc.logToFile(level, `${new Date().toISOString()} ${message}`);
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(chalk.red(`[LOGGER] 写入日志失败: ${err.message}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const logger = Logger.getInstance();
|
||||||
|
export default logger;
|
62
src/utils/core/path.ts
Normal file
62
src/utils/core/path.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
class PathManager {
|
||||||
|
private static instance: PathManager;
|
||||||
|
private readonly baseDir: string;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
this.baseDir = path.join(__dirname, '../..');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取单例实例
|
||||||
|
*/
|
||||||
|
public static getInstance(): PathManager {
|
||||||
|
if (!PathManager.instance) {
|
||||||
|
PathManager.instance = new PathManager();
|
||||||
|
}
|
||||||
|
return PathManager.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取预定义路径
|
||||||
|
* @param type 路径类型
|
||||||
|
*/
|
||||||
|
public get(type?: PathType): string {
|
||||||
|
const mappings: Record<PathType, string> = {
|
||||||
|
root: this.baseDir,
|
||||||
|
public: path.join(this.baseDir, 'public'),
|
||||||
|
images: path.join(this.baseDir, 'public/images'),
|
||||||
|
log: path.join(this.baseDir, 'logs'),
|
||||||
|
config: path.join(this.baseDir, 'config'),
|
||||||
|
temp: path.join(this.baseDir, 'temp'),
|
||||||
|
};
|
||||||
|
|
||||||
|
return type ? mappings[type] : this.baseDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析相对路径(基于项目根目录)
|
||||||
|
* @param segments 路径片段
|
||||||
|
*/
|
||||||
|
public resolve(...segments: string[]): string {
|
||||||
|
return path.join(this.baseDir, ...segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查路径是否存在(同步)
|
||||||
|
*/
|
||||||
|
public existsSync(targetPath: string): boolean {
|
||||||
|
try {
|
||||||
|
return fs.existsSync(targetPath);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathType = 'root' | 'public' | 'images' | 'log' | 'config' | 'temp';
|
||||||
|
|
||||||
|
const paths = PathManager.getInstance();
|
||||||
|
export default paths;
|
61
src/utils/core/response.ts
Normal file
61
src/utils/core/response.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { Response } from 'express';
|
||||||
|
import logger from './logger';
|
||||||
|
|
||||||
|
class response {
|
||||||
|
/**
|
||||||
|
* 成功响应
|
||||||
|
* @param res Express响应对象
|
||||||
|
* @param data 返回数据
|
||||||
|
* @param statusCode HTTP状态码,默认200
|
||||||
|
*/
|
||||||
|
static success(res: Response, data: any, statusCode = 200) {
|
||||||
|
res.status(statusCode).json({
|
||||||
|
success: true,
|
||||||
|
data,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误响应
|
||||||
|
* @param res Express响应对象
|
||||||
|
* @param message 错误信息
|
||||||
|
* @param statusCode HTTP状态码,默认500
|
||||||
|
* @param error 原始错误对象(开发环境显示)
|
||||||
|
*/
|
||||||
|
static error(res: Response, message: string, statusCode = 500, error?: any) {
|
||||||
|
const response: Record<string, any> = {
|
||||||
|
success: false,
|
||||||
|
message,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.debug(error instanceof Error ? error.stack : error);
|
||||||
|
|
||||||
|
res.status(statusCode).json(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页数据响应
|
||||||
|
* @param res Express响应对象
|
||||||
|
* @param data 数据数组
|
||||||
|
* @param total 总条数
|
||||||
|
* @param page 当前页码
|
||||||
|
* @param pageSize 每页条数
|
||||||
|
*/
|
||||||
|
static pagination(res: Response, data: any[], total: number, page: number, pageSize: number) {
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
data,
|
||||||
|
pagination: {
|
||||||
|
total,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
totalPages: Math.ceil(total / pageSize),
|
||||||
|
},
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default response;
|
@ -1,11 +0,0 @@
|
|||||||
let date = {
|
|
||||||
getCurrentDate: () => {
|
|
||||||
const now = new Date();
|
|
||||||
return `${now.getFullYear()}${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}`;
|
|
||||||
},
|
|
||||||
getCurrentTime: () => {
|
|
||||||
return new Date().toLocaleTimeString('en-US', { hour12: false });
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default date;
|
|
1
src/utils/helpers/keep
Normal file
1
src/utils/helpers/keep
Normal file
@ -0,0 +1 @@
|
|||||||
|
keep
|
0
src/utils/index.ts
Normal file
0
src/utils/index.ts
Normal file
1
src/utils/lib/keep
Normal file
1
src/utils/lib/keep
Normal file
@ -0,0 +1 @@
|
|||||||
|
keep
|
@ -1,30 +0,0 @@
|
|||||||
import chalk from 'chalk';
|
|
||||||
import config from './config';
|
|
||||||
import fc from './file';
|
|
||||||
|
|
||||||
const logger = {
|
|
||||||
debug: (...args: any[]) => {
|
|
||||||
if (config.get('DEBUG', false)) {
|
|
||||||
//const message = args.join(' ');
|
|
||||||
console.log(chalk.cyan('[DEBUG]'), ...args);
|
|
||||||
//fc.logToFile('DEBUG', message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
info: (...args: any[]) => {
|
|
||||||
const message = args.join(' ');
|
|
||||||
console.log(chalk.green('[INFO]'), ...args);
|
|
||||||
fc.logToFile('INFO', message);
|
|
||||||
},
|
|
||||||
warn: (...args: any[]) => {
|
|
||||||
const message = args.join(' ');
|
|
||||||
console.log(chalk.yellow('[WARN]'), ...args);
|
|
||||||
fc.logToFile('WARN', message);
|
|
||||||
},
|
|
||||||
error: (...args: any[]) => {
|
|
||||||
const message = args.join(' ');
|
|
||||||
console.log(chalk.red('[ERROR]'), ...args);
|
|
||||||
fc.logToFile('ERROR', message);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default logger;
|
|
@ -1,24 +0,0 @@
|
|||||||
import path from 'path';
|
|
||||||
|
|
||||||
const paths = {
|
|
||||||
get: (value?: string) => {
|
|
||||||
switch (value) {
|
|
||||||
case 'root':
|
|
||||||
return path.join(__dirname, '..');
|
|
||||||
case 'public':
|
|
||||||
return path.join(__dirname, '../public');
|
|
||||||
case 'images':
|
|
||||||
return path.join(__dirname, '../../public/images/');
|
|
||||||
case 'log':
|
|
||||||
return path.join(__dirname, '../../logs');
|
|
||||||
default:
|
|
||||||
return path.join(__dirname, '..');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
resolve: (segments: string) => {
|
|
||||||
return path.join(__dirname, segments);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default paths;
|
|
Loading…
x
Reference in New Issue
Block a user