mirror of
https://github.com/crystelf/crystelf-core.git
synced 2025-07-03 22:29:19 +00:00
初步实现图床和api功能
This commit is contained in:
parent
06f82c886a
commit
ad5c9140ec
14
.gitignore
vendored
14
.gitignore
vendored
@ -1 +1,13 @@
|
||||
/node_modules/
|
||||
/tmp
|
||||
/out-tsc
|
||||
|
||||
/node_modules
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
.vscode/*
|
||||
/dist/
|
||||
/logs/
|
||||
|
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
12
.idea/crystelf-core.iml
generated
Normal file
12
.idea/crystelf-core.iml
generated
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/crystelf-core.iml" filepath="$PROJECT_DIR$/.idea/crystelf-core.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
23
package.json
23
package.json
@ -1,5 +1,22 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"express": "^5.1.0"
|
||||
}
|
||||
"name": "crystelf-core",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "ts-node-dev src/main.ts",
|
||||
"start": "node dist/main.js",
|
||||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "4",
|
||||
"dotenv": "^16.0.0",
|
||||
"express": "^4.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.0",
|
||||
"@types/mkdirp": "^2.0.0",
|
||||
"@types/node": "^18.0.0",
|
||||
"prettier": "^3.5.3",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
923
pnpm-lock.yaml
generated
923
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
25
src/app.ts
Normal file
25
src/app.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import express from 'express';
|
||||
import logger from './utils/logger';
|
||||
import fc from './utils/file';
|
||||
import paths from './utils/path';
|
||||
import sampleController from './modules/sample/sample.controller';
|
||||
import imageController from './modules/image/image.controller';
|
||||
|
||||
const apps = {
|
||||
createApp() {
|
||||
const app = express();
|
||||
|
||||
app.use(express.json());
|
||||
app.use('/public', express.static(paths.get('public')));
|
||||
logger.debug(`路由/public挂载成功..`);
|
||||
app.use('/api/sample', sampleController.getRouter());
|
||||
logger.debug(`路由/api/sample挂载成功..`);
|
||||
app.use('/images', imageController.getRouter());
|
||||
logger.debug(`路由/images挂载成功..`);
|
||||
fc.createDir(paths.get('log'));
|
||||
logger.info('晶灵核心初始化成功!');
|
||||
return app;
|
||||
},
|
||||
};
|
||||
|
||||
export default apps;
|
10
src/main.ts
Normal file
10
src/main.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import apps from './app';
|
||||
import logger from './utils/logger';
|
||||
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
const app = apps.createApp();
|
||||
|
||||
app.listen(PORT, () => {
|
||||
logger.info(`Crystelf-core listening on ${PORT}`);
|
||||
});
|
51
src/modules/image/image.controller.ts
Normal file
51
src/modules/image/image.controller.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import express from 'express';
|
||||
import ImageService from './image.service';
|
||||
import logger from '../../utils/logger';
|
||||
|
||||
class ImageController {
|
||||
private readonly router: express.Router;
|
||||
private readonly imageService: ImageService;
|
||||
|
||||
constructor() {
|
||||
this.router = express.Router();
|
||||
this.imageService = new ImageService();
|
||||
this.initializeRoutes();
|
||||
}
|
||||
|
||||
public getRouter(): express.Router {
|
||||
return this.router;
|
||||
}
|
||||
|
||||
private initializeRoutes(): void {
|
||||
this.router.get('/:filename', this.handleGetImage);
|
||||
}
|
||||
|
||||
private handleGetImage = (req: express.Request, res: express.Response): void => {
|
||||
try {
|
||||
const filename = req.params.filename;
|
||||
logger.debug(`有个小可爱正在请求${filename}噢..`);
|
||||
|
||||
const filePath = this.imageService.getImage(filename);
|
||||
if (!filePath) {
|
||||
this.sendError(res, 404, '文件不存在啦!');
|
||||
return;
|
||||
}
|
||||
|
||||
res.sendFile(filePath);
|
||||
logger.info(`成功投递文件: ${filePath}`);
|
||||
} catch (error) {
|
||||
this.sendError(res, 500, '晶灵服务处理图像请求时出错..');
|
||||
logger.error('晶灵图像请求处理失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
private sendError(res: express.Response, statusCode: number, message: string): void {
|
||||
res.status(statusCode).json({
|
||||
success: false,
|
||||
error: message,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new ImageController();
|
30
src/modules/image/image.service.ts
Normal file
30
src/modules/image/image.service.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import paths from '../../utils/path';
|
||||
import logger from '../../utils/logger';
|
||||
|
||||
class ImageService {
|
||||
private readonly imageDir: string;
|
||||
|
||||
constructor() {
|
||||
this.imageDir = paths.get('images');
|
||||
logger.info(`晶灵云图数据中心初始化..数据存储在: ${this.imageDir}`);
|
||||
}
|
||||
|
||||
public getImage(filename: string): string | null {
|
||||
const filePath = path.join(this.imageDir, filename);
|
||||
logger.debug(`尝试访问图像路径: ${filePath}`);
|
||||
|
||||
if (!this.isValidFilename(filename)) {
|
||||
throw new Error('无效的文件名格式..');
|
||||
}
|
||||
|
||||
return fs.existsSync(filePath) ? filePath : null;
|
||||
}
|
||||
|
||||
private isValidFilename(filename: string): boolean {
|
||||
return /^[a-zA-Z0-9_\-\.]+$/.test(filename);
|
||||
}
|
||||
}
|
||||
|
||||
export default ImageService;
|
57
src/modules/sample/sample.controller.ts
Normal file
57
src/modules/sample/sample.controller.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import express from 'express';
|
||||
import sampleService from './sample.service';
|
||||
|
||||
class SampleController {
|
||||
private readonly router: express.Router;
|
||||
|
||||
constructor() {
|
||||
this.router = express.Router();
|
||||
this.initializeRoutes();
|
||||
}
|
||||
|
||||
public getRouter(): express.Router {
|
||||
return this.router;
|
||||
}
|
||||
|
||||
private initializeRoutes(): void {
|
||||
this.router.get('/hello', this.getHello);
|
||||
this.router.post('/greet', this.postGreet);
|
||||
}
|
||||
|
||||
private getHello = (req: express.Request, res: express.Response): void => {
|
||||
try {
|
||||
const result = sampleService.getHello();
|
||||
this.sendSuccess(res, result);
|
||||
} catch (error) {
|
||||
this.sendError(res, error);
|
||||
}
|
||||
};
|
||||
|
||||
private postGreet = (req: express.Request, res: express.Response): void => {
|
||||
try {
|
||||
const { name } = req.body;
|
||||
const result = sampleService.generateGreeting(name);
|
||||
this.sendSuccess(res, result);
|
||||
} catch (error) {
|
||||
this.sendError(res, 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();
|
19
src/modules/sample/sample.service.ts
Normal file
19
src/modules/sample/sample.service.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import logger from '../../utils/logger';
|
||||
|
||||
class SampleService {
|
||||
getHello() {
|
||||
logger.debug(`有个小可爱正在请求GetHello方法..`);
|
||||
return { message: 'Hello World!' };
|
||||
}
|
||||
|
||||
generateGreeting(name: string) {
|
||||
logger.debug(`有个小可爱正在请求generateGreeting方法..`);
|
||||
if (!name) {
|
||||
logger.warn('Name is required');
|
||||
throw new Error('Name is required');
|
||||
}
|
||||
return { message: `Hello, ${name}!` };
|
||||
}
|
||||
}
|
||||
|
||||
export default new SampleService();
|
23
src/utils/config.ts
Normal file
23
src/utils/config.ts
Normal file
@ -0,0 +1,23 @@
|
||||
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;
|
11
src/utils/date.ts
Normal file
11
src/utils/date.ts
Normal file
@ -0,0 +1,11 @@
|
||||
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;
|
46
src/utils/file.ts
Normal file
46
src/utils/file.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import path from 'path';
|
||||
import paths from './path';
|
||||
import date from './date';
|
||||
import fs from 'fs';
|
||||
import chalk from 'chalk';
|
||||
import logger from './logger';
|
||||
|
||||
let fc = {
|
||||
createDir: (targetPath: string = '', includeFile: boolean = false) => {
|
||||
const root = paths.get('root');
|
||||
if (path.isAbsolute(targetPath)) {
|
||||
if (includeFile) {
|
||||
const parentDir = path.dirname(targetPath);
|
||||
if (!fs.existsSync(parentDir)) {
|
||||
fs.mkdirSync(parentDir, { recursive: true });
|
||||
logger.debug(`成功创建绝对目录: ${parentDir}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!fs.existsSync(targetPath)) {
|
||||
fs.mkdirSync(targetPath, { recursive: true });
|
||||
logger.debug(`成功创建绝对目录: ${targetPath}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const fullPath = includeFile
|
||||
? path.join(root, path.dirname(targetPath))
|
||||
: path.join(root, targetPath);
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
fs.mkdirSync(fullPath, { recursive: true });
|
||||
logger.debug(`成功创建相对目录: ${fullPath}`);
|
||||
}
|
||||
},
|
||||
|
||||
logToFile: (level: string, message: string) => {
|
||||
const logFile = path.join(paths.get('log'), `${date.getCurrentDate()}.log`);
|
||||
const logMessage = `[${date.getCurrentTime()}] [${level}] ${message}\n`;
|
||||
|
||||
fs.appendFile(logFile, logMessage, (err) => {
|
||||
if (err) console.error(chalk.red('[LOGGER] 写入日志失败:'), err);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default fc;
|
30
src/utils/logger.ts
Normal file
30
src/utils/logger.ts
Normal file
@ -0,0 +1,30 @@
|
||||
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;
|
24
src/utils/path.ts
Normal file
24
src/utils/path.ts
Normal file
@ -0,0 +1,24 @@
|
||||
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;
|
0
src/utils/tool.ts
Normal file
0
src/utils/tool.ts
Normal file
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user