mirror of
https://github.com/crystelf/crystelf-core.git
synced 2025-10-14 05:19:19 +00:00
99 lines
3.2 KiB
TypeScript
99 lines
3.2 KiB
TypeScript
import { Inject, Injectable, Logger } from '@nestjs/common';
|
|
import * as path from 'path';
|
|
import { existsSync } from 'fs';
|
|
import { PathService } from '../../core/path/path.service';
|
|
import { OpenListService } from '../../core/openlist/openlist.service';
|
|
import { AppConfigService } from '../../config/config.service';
|
|
import { FilesService } from '../../core/files/files.service';
|
|
|
|
@Injectable()
|
|
export class CdnService {
|
|
private readonly logger = new Logger(CdnService.name);
|
|
private filePath: string;
|
|
private readonly updateMs = 1 * 60 * 100; // 15min
|
|
@Inject(PathService)
|
|
private readonly paths: PathService;
|
|
constructor(
|
|
@Inject(PathService)
|
|
private readonly pathService: PathService,
|
|
@Inject(OpenListService)
|
|
private readonly openListService: OpenListService,
|
|
@Inject(AppConfigService)
|
|
private readonly configService: AppConfigService,
|
|
@Inject(FilesService)
|
|
private readonly filesService: FilesService,
|
|
) {
|
|
this.startAutoUpdate();
|
|
this.logger.log(`晶灵云图数据中心初始化.. 数据存储在: ${this.filePath}`);
|
|
}
|
|
|
|
private startAutoUpdate() {
|
|
setInterval(async () => {
|
|
const cdnPath = path.join(this.pathService.get('private'));
|
|
const remoteCdnPath = this.configService.get('OPENLIST_API_CDN_PATH');
|
|
if (remoteCdnPath) {
|
|
this.logger.log('定时检查晶灵cdn更新..');
|
|
try {
|
|
const remoteFiles =
|
|
await this.openListService.listFiles(remoteCdnPath);
|
|
if (remoteFiles.code === 200 && remoteFiles.data.content) {
|
|
let remoteFileList = remoteFiles.data.content;
|
|
const localFiles =
|
|
await this.filesService.getLocalFileList(cdnPath);
|
|
await this.filesService.compareAndDownloadFiles(
|
|
cdnPath,
|
|
localFiles,
|
|
remoteFileList,
|
|
remoteCdnPath,
|
|
'\crystelf\cdn',
|
|
);
|
|
} else {
|
|
this.logger.error(`晶灵cdn检查更新失败: ${remoteFiles.code}`);
|
|
}
|
|
} catch (error) {
|
|
this.logger.error(`晶灵cdn检查更新失败: ${error}`);
|
|
}
|
|
} else {
|
|
this.logger.warn('未配置远程表情包地址..');
|
|
}
|
|
}, this.updateMs);
|
|
}
|
|
|
|
/**
|
|
* 获取文件
|
|
* @param relativePath 文件相对路径
|
|
*/
|
|
public async getFile(relativePath: string): Promise<string | null> {
|
|
if (!this.filePath) this.filePath = this.paths.get('public');
|
|
if (
|
|
!this.isValidPath(relativePath) &&
|
|
!this.isValidFilename(path.basename(relativePath))
|
|
) {
|
|
throw new Error('非法路径请求');
|
|
}
|
|
const filePath = path.join(this.filePath, relativePath);
|
|
this.logger.debug(`尝试访问文件路径: ${filePath}`);
|
|
return existsSync(filePath) ? filePath : null;
|
|
}
|
|
|
|
/**
|
|
* 检查路径合法性
|
|
*/
|
|
private isValidPath(relativePath: string): boolean {
|
|
try {
|
|
const normalized = path.normalize(relativePath);
|
|
let flag = true;
|
|
if (normalized.startsWith('../') && path.isAbsolute(normalized))
|
|
flag = false;
|
|
return flag;
|
|
} catch (err) {
|
|
this.logger.error(err);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private isValidFilename(filename: string): boolean {
|
|
return /^[a-zA-Z0-9_\-.]+$/.test(filename);
|
|
}
|
|
}
|