mirror of
https://github.com/crystelf/crystelf-core.git
synced 2025-10-14 13:29:19 +00:00
Compare commits
2 Commits
bdfc7f43d1
...
5c062172f3
Author | SHA1 | Date | |
---|---|---|---|
5c062172f3 | |||
beebcc14f9 |
@ -36,7 +36,7 @@ export class AppConfigService implements OnModuleInit {
|
|||||||
* 检查并补全 .env 文件
|
* 检查并补全 .env 文件
|
||||||
*/
|
*/
|
||||||
private checkAndSyncEnv(): void {
|
private checkAndSyncEnv(): void {
|
||||||
this.logger.log('检查并同步 .env 与 .env.example ...');
|
this.logger.log('检查并同步 .env 与 .envExample ...');
|
||||||
|
|
||||||
if (!fs.existsSync(this.envExamplePath)) {
|
if (!fs.existsSync(this.envExamplePath)) {
|
||||||
this.logger.error(`缺少 ${this.envExamplePath} 文件,无法校验`);
|
this.logger.error(`缺少 ${this.envExamplePath} 文件,无法校验`);
|
||||||
@ -67,7 +67,7 @@ export class AppConfigService implements OnModuleInit {
|
|||||||
});
|
});
|
||||||
this.logger.log('.env 已自动补全缺失项');
|
this.logger.log('.env 已自动补全缺失项');
|
||||||
} else {
|
} else {
|
||||||
this.logger.log('.env 已与 .env.example 保持一致');
|
this.logger.log('.env 已与 .envExample 保持一致');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ export class OpenListService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 列出目录下的所有文件
|
* 列出目录下的所有文件
|
||||||
* @param path 目录路径
|
* @param path 服务器目录路径
|
||||||
* @returns 目录下的文件列表
|
* @returns 目录下的文件列表
|
||||||
*/
|
*/
|
||||||
public async listFiles(path: string): Promise<FsList> {
|
public async listFiles(path: string): Promise<FsList> {
|
||||||
|
@ -5,18 +5,18 @@ export interface FsList {
|
|||||||
code: number;
|
code: number;
|
||||||
message: string;
|
message: string;
|
||||||
data: {
|
data: {
|
||||||
content: [
|
content: Array<{
|
||||||
name: string,
|
name: string;
|
||||||
size: number,
|
size: number;
|
||||||
is_dir: boolean,
|
is_dir: boolean;
|
||||||
modified: string, //修改时间
|
modified: string; // 修改时间
|
||||||
sign: string, //签名
|
sign: string; // 签名
|
||||||
thumb: string, //略缩图
|
thumb: string; // 略缩图
|
||||||
type: number, //类型
|
type: number; // 类型
|
||||||
];
|
}> | null; // 可能为空数组
|
||||||
total: number; //总数
|
total: number; // 总数
|
||||||
readme: string; //说明?
|
readme: string; // 说明?
|
||||||
write: boolean; //是否可写入
|
write: boolean; // 是否可写入
|
||||||
provider: string;
|
provider: string;
|
||||||
header: string;
|
header: string;
|
||||||
};
|
};
|
||||||
|
@ -50,10 +50,20 @@ export class OpenListUtils {
|
|||||||
static async listDirectory(token: string, path: string): Promise<FsList> {
|
static async listDirectory(token: string, path: string): Promise<FsList> {
|
||||||
const url = `${this.apiBaseUrl}/api/fs/list`;
|
const url = `${this.apiBaseUrl}/api/fs/list`;
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(url, {
|
let data = JSON.stringify({
|
||||||
params: { path },
|
path: path,
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
|
||||||
});
|
});
|
||||||
|
//this.logger.debug(path);
|
||||||
|
let config = {
|
||||||
|
method: 'post',
|
||||||
|
url: `${url}`,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `${token}`,
|
||||||
|
},
|
||||||
|
data: data,
|
||||||
|
};
|
||||||
|
let response = await axios(config);
|
||||||
this.logger.log(`列出目录${path}成功..`);
|
this.logger.log(`列出目录${path}成功..`);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -72,7 +82,7 @@ export class OpenListUtils {
|
|||||||
try {
|
try {
|
||||||
const response = await axios.get(url, {
|
const response = await axios.get(url, {
|
||||||
params: { path: filePath },
|
params: { path: filePath },
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `${token}` },
|
||||||
});
|
});
|
||||||
this.logger.log('获取文件信息成功..');
|
this.logger.log('获取文件信息成功..');
|
||||||
return response.data;
|
return response.data;
|
||||||
@ -96,6 +106,7 @@ export class OpenListUtils {
|
|||||||
try {
|
try {
|
||||||
const fileInfo = await this.getFileInfo(token, filePath);
|
const fileInfo = await this.getFileInfo(token, filePath);
|
||||||
const rawUrl = fileInfo.data.raw_url;
|
const rawUrl = fileInfo.data.raw_url;
|
||||||
|
this.logger.debug(`rawUrl: ${rawUrl}`);
|
||||||
if (!rawUrl) {
|
if (!rawUrl) {
|
||||||
this.logger.error('文件没有找到 raw_url 地址..');
|
this.logger.error('文件没有找到 raw_url 地址..');
|
||||||
throw new Error('文件没有找到 raw_url 地址..');
|
throw new Error('文件没有找到 raw_url 地址..');
|
||||||
|
@ -71,7 +71,6 @@ export class PathService {
|
|||||||
this.logger.debug(`成功创建目录: ${dirToCreate}`);
|
this.logger.debug(`成功创建目录: ${dirToCreate}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error(`创建目录失败: ${err}`);
|
this.logger.error(`创建目录失败: ${err}`);
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,16 @@ import { PathModule } from '../../core/path/path.module';
|
|||||||
import { ToolsModule } from '../../core/tools/tools.module';
|
import { ToolsModule } from '../../core/tools/tools.module';
|
||||||
import { RedisModule } from '../../core/redis/redis.module';
|
import { RedisModule } from '../../core/redis/redis.module';
|
||||||
import { OpenListModule } from '../../core/openlist/openlist.module';
|
import { OpenListModule } from '../../core/openlist/openlist.module';
|
||||||
|
import { AppConfigModule } from '../../config/config.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PathModule, OpenListModule, ToolsModule, RedisModule],
|
imports: [
|
||||||
|
PathModule,
|
||||||
|
OpenListModule,
|
||||||
|
ToolsModule,
|
||||||
|
RedisModule,
|
||||||
|
AppConfigModule,
|
||||||
|
],
|
||||||
providers: [MemeService],
|
providers: [MemeService],
|
||||||
controllers: [MemeController],
|
controllers: [MemeController],
|
||||||
})
|
})
|
||||||
|
@ -3,17 +3,20 @@ import * as path from 'path';
|
|||||||
import * as fs from 'fs/promises';
|
import * as fs from 'fs/promises';
|
||||||
import { PathService } from '../../core/path/path.service';
|
import { PathService } from '../../core/path/path.service';
|
||||||
import { OpenListService } from '../../core/openlist/openlist.service';
|
import { OpenListService } from '../../core/openlist/openlist.service';
|
||||||
|
import { AppConfigService } from '../../config/config.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MemeService {
|
export class MemeService {
|
||||||
private readonly logger = new Logger(MemeService.name);
|
private readonly logger = new Logger(MemeService.name);
|
||||||
private readonly updateMs = 15 * 60 * 1000; // 15min
|
private readonly updateMs = 150 * 60 * 1000; // 15min
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(PathService)
|
@Inject(PathService)
|
||||||
private readonly pathService: PathService,
|
private readonly pathService: PathService,
|
||||||
@Inject(OpenListService)
|
@Inject(OpenListService)
|
||||||
private readonly openListService: OpenListService,
|
private readonly openListService: OpenListService,
|
||||||
|
@Inject(AppConfigService)
|
||||||
|
private readonly configService: AppConfigService,
|
||||||
) {
|
) {
|
||||||
this.startAutoUpdate();
|
this.startAutoUpdate();
|
||||||
}
|
}
|
||||||
@ -21,22 +24,30 @@ export class MemeService {
|
|||||||
private startAutoUpdate() {
|
private startAutoUpdate() {
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
const memePath = path.join(this.pathService.get('meme'));
|
const memePath = path.join(this.pathService.get('meme'));
|
||||||
this.logger.log('定时检查表情仓库更新..');
|
const remoteMemePath = this.configService.get('OPENLIST_API_MEME_PATH');
|
||||||
try {
|
if (remoteMemePath) {
|
||||||
const remoteFiles = await this.openListService.listFiles(memePath);
|
this.logger.log('定时检查表情仓库更新..');
|
||||||
if (remoteFiles.code === 200) {
|
try {
|
||||||
const remoteFileList = remoteFiles.data.content;
|
const remoteFiles =
|
||||||
const localFiles = await this.getLocalFileList(memePath);
|
await this.openListService.listFiles(remoteMemePath);
|
||||||
await this.compareAndDownloadFiles(
|
if (remoteFiles.code === 200 && remoteFiles.data.content) {
|
||||||
memePath,
|
let remoteFileList = remoteFiles.data.content;
|
||||||
localFiles,
|
const localFiles = await this.getLocalFileList(memePath);
|
||||||
remoteFileList,
|
//this.logger.debug(localFiles);
|
||||||
);
|
await this.compareAndDownloadFiles(
|
||||||
} else {
|
memePath,
|
||||||
this.logger.error('获取远程表情仓库文件失败..');
|
localFiles,
|
||||||
|
remoteFileList,
|
||||||
|
remoteMemePath,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.logger.error('获取远程表情仓库文件失败..');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('定时检查表情仓库更新失败..', error);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} else {
|
||||||
this.logger.error('定时检查表情仓库更新失败..', error);
|
this.logger.warn('未配置远程表情包地址..');
|
||||||
}
|
}
|
||||||
}, this.updateMs);
|
}, this.updateMs);
|
||||||
}
|
}
|
||||||
@ -47,8 +58,8 @@ export class MemeService {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private async getLocalFileList(dir: string): Promise<string[]> {
|
private async getLocalFileList(dir: string): Promise<string[]> {
|
||||||
const files: string[] = []; //文件
|
const files: string[] = [];
|
||||||
const dirs: string[] = []; //目录
|
const dirs: string[] = [];
|
||||||
try {
|
try {
|
||||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
@ -70,37 +81,57 @@ export class MemeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 比较本地文件和远程文件,并下载缺失的文件
|
* 比较本地文件和远程文件 下载缺失的文件
|
||||||
* @param localPath 本地路径
|
* @param localPath 本地路径
|
||||||
* @param localFiles 本地文件列表
|
* @param localFiles 本地文件列表
|
||||||
* @param remoteFiles 远程文件列表
|
* @param remoteFiles 远程文件列表
|
||||||
|
* @param remoteMemePath 远程基准路径
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private async compareAndDownloadFiles(
|
private async compareAndDownloadFiles(
|
||||||
localPath: string,
|
localPath: string,
|
||||||
localFiles: string[],
|
localFiles: string[],
|
||||||
remoteFiles: any[],
|
remoteFiles: any[],
|
||||||
|
remoteMemePath: string,
|
||||||
) {
|
) {
|
||||||
for (const remoteFile of remoteFiles) {
|
for (const remoteFile of remoteFiles) {
|
||||||
const remoteFilePath = path.join(localPath, remoteFile.name);
|
let relativePath = path.relative(remoteMemePath, remoteFile.path);
|
||||||
|
relativePath = relativePath.replace(/D:\\alist\\crystelf\\meme/g, '');
|
||||||
|
const localFilePath = path.join(
|
||||||
|
localPath,
|
||||||
|
relativePath.replace(/\\/g, '/'),
|
||||||
|
);
|
||||||
if (remoteFile.is_dir) {
|
if (remoteFile.is_dir) {
|
||||||
await fs.mkdir(remoteFilePath, { recursive: true });
|
|
||||||
this.logger.log(`文件夹已创建: ${remoteFile.name}`);
|
|
||||||
await this.compareAndDownloadFiles(
|
|
||||||
remoteFilePath,
|
|
||||||
[],
|
|
||||||
remoteFile.content,
|
|
||||||
);
|
|
||||||
} else if (!localFiles.includes(remoteFilePath)) {
|
|
||||||
this.logger.log(`文件缺失: ${remoteFile.name},开始下载..`);
|
|
||||||
try {
|
try {
|
||||||
await this.openListService.downloadFile(
|
const localDirPath = path.dirname(localFilePath);
|
||||||
remoteFile.raw_url,
|
await fs.mkdir(localDirPath, { recursive: true });
|
||||||
remoteFilePath,
|
this.logger.log(`文件夹已创建: ${localDirPath}`);
|
||||||
|
const subRemoteFiles = await this.openListService.listFiles(
|
||||||
|
remoteFile.path,
|
||||||
);
|
);
|
||||||
this.logger.log(`文件下载成功: ${remoteFile.name}`);
|
if (subRemoteFiles.code === 200 && subRemoteFiles.data.content) {
|
||||||
|
await this.compareAndDownloadFiles(
|
||||||
|
localPath,
|
||||||
|
[],
|
||||||
|
subRemoteFiles.data.content,
|
||||||
|
remoteMemePath,
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`下载文件失败: ${remoteFile.name}`, error);
|
this.logger.error(`创建文件夹失败: ${remoteFile.path}`, error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!localFiles.includes(localFilePath)) {
|
||||||
|
this.logger.log(`文件缺失: ${remoteFile.path}, 开始下载..`);
|
||||||
|
try {
|
||||||
|
await this.openListService.downloadFile(
|
||||||
|
remoteFile.path,
|
||||||
|
localFilePath,
|
||||||
|
);
|
||||||
|
this.logger.log(`文件下载成功: ${remoteFile.path}`);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error(`下载文件失败: ${remoteFile.path}`, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user