From cbea0c2b33751f1a31be55cbf95e66a5c8099007 Mon Sep 17 00:00:00 2001 From: Jerryplusy Date: Mon, 15 Sep 2025 16:58:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:oplist=E5=B7=A5=E5=85=B7=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .envExample | 3 + src/app.module.ts | 2 + src/core/openlist/openlist.module.ts | 10 ++ src/core/openlist/openlist.service.ts | 16 +++ src/core/openlist/openlist.types.ts | 25 +++++ src/core/openlist/openlist.utils.ts | 140 ++++++++++++++++++++++++++ 6 files changed, 196 insertions(+) create mode 100644 src/core/openlist/openlist.module.ts create mode 100644 src/core/openlist/openlist.service.ts create mode 100644 src/core/openlist/openlist.types.ts create mode 100644 src/core/openlist/openlist.utils.ts diff --git a/.envExample b/.envExample index 7ab03af..53ef1d3 100644 --- a/.envExample +++ b/.envExample @@ -2,3 +2,6 @@ RD_PORT=6379 RD_ADD=127.0.0.1 WS_SECRET=114514 TOKEN=54188 +OPENLIST_API_BASE_URL=http://127.0.0.1:5244 +OPENLIST_API_BASE_USERNAME=USER +OPENLIST_API_BASE_PASSWORD=123456 \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index 26d70da..d7c7cd2 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -13,6 +13,7 @@ import { BotModule } from './modules/bot/bot.module'; import { CdnModule } from './modules/cdn/cdn.module'; import { WordsModule } from './modules/words/words.module'; import { MemeModule } from './modules/meme/meme.module'; +import { OpenListModule } from './core/openlist/openlist.module'; @Module({ imports: [ @@ -30,6 +31,7 @@ import { MemeModule } from './modules/meme/meme.module'; CdnModule, WordsModule, MemeModule, + OpenListModule, ], }) export class AppModule {} diff --git a/src/core/openlist/openlist.module.ts b/src/core/openlist/openlist.module.ts new file mode 100644 index 0000000..c57a5dc --- /dev/null +++ b/src/core/openlist/openlist.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { OpenListService } from './openlist.service'; +import { AppConfigModule } from '../../config/config.module'; + +@Module({ + imports: [AppConfigModule], + providers: [OpenListService], + exports: [OpenListService], +}) +export class OpenListModule {} diff --git a/src/core/openlist/openlist.service.ts b/src/core/openlist/openlist.service.ts new file mode 100644 index 0000000..61da63e --- /dev/null +++ b/src/core/openlist/openlist.service.ts @@ -0,0 +1,16 @@ +import { Inject, Injectable, Logger } from '@nestjs/common'; +import { AppConfigService } from '../../config/config.service'; +import { DirectoryList, FileInfo, UserInfo } from './openlist.types'; +import { OpenListUtils } from './openlist.utils'; + +@Injectable() +export class OpenListService { + private readonly logger = new Logger(OpenListService.name); + + constructor( + @Inject(AppConfigService) + private readonly configService: AppConfigService, + ) { + OpenListUtils.init(configService); + } +} diff --git a/src/core/openlist/openlist.types.ts b/src/core/openlist/openlist.types.ts new file mode 100644 index 0000000..6b695c4 --- /dev/null +++ b/src/core/openlist/openlist.types.ts @@ -0,0 +1,25 @@ +export interface UserInfo { + id: number; + username: string; + email: string; + role: string; + created_at: string; + updated_at: string; +} + +export interface DirectoryList { + path: string; + name: string; + is_directory: boolean; + size: number; + modified_at: string; +} + +export interface FileInfo { + path: string; + size: number; + mime_type: string; + created_at: string; + modified_at: string; + is_directory: boolean; +} diff --git a/src/core/openlist/openlist.utils.ts b/src/core/openlist/openlist.utils.ts new file mode 100644 index 0000000..a429a48 --- /dev/null +++ b/src/core/openlist/openlist.utils.ts @@ -0,0 +1,140 @@ +import axios from 'axios'; +import { AppConfigService } from '../../config/config.service'; +import { Inject, Logger } from '@nestjs/common'; +import * as crypto from 'crypto'; + +export class OpenListUtils { + private static readonly logger = new Logger(OpenListUtils.name); + private static apiBaseUrl: string | undefined; + + static init(@Inject(AppConfigService) configService: AppConfigService) { + this.apiBaseUrl = configService.get('OPENLIST_API_BASE_URL'); + } + + /** + * 获取 JWT Token + * @param username 用户名 + * @param password 密码 + * @returns token + */ + static async getToken(username: string, password: string): Promise { + const url = `${this.apiBaseUrl}/auth/token`; + const hashedPassword = this.hashPassword(password); + + try { + const response = await axios.post(url, { + username, + password: hashedPassword, + }); + const token = response.data.token; + this.logger.log(`获取 Token 成功: ${token}`); + return token; + } catch (error) { + this.logger.error('获取 Token 失败', error); + throw new Error('获取 Token 失败'); + } + } + + /** + * 获取当前用户信息 + * @param token 用户 Token + * @returns 用户信息 + */ + static async getUserInfo(token: string): Promise { + const url = `${this.apiBaseUrl}/auth/userinfo`; + + try { + const response = await axios.get(url, { + headers: { Authorization: `Bearer ${token}` }, + }); + this.logger.log('获取用户信息成功'); + return response.data; + } catch (error) { + this.logger.error('获取用户信息失败', error); + throw new Error('获取用户信息失败'); + } + } + + /** + * 获取目录列表 + * @param token 用户 Token + * @param path 目录路径 + * @returns 文件目录列表 + */ + static async listDirectory(token: string, path: string): Promise { + const url = `${this.apiBaseUrl}/fs/list`; + + try { + const response = await axios.get(url, { + params: { path }, + headers: { Authorization: `Bearer ${token}` }, + }); + this.logger.log('列出目录成功'); + return response.data; + } catch (error) { + this.logger.error('列出目录失败', error); + throw new Error('列出目录失败'); + } + } + + /** + * 获取某个文件的详细信息 + * @param token 用户 Token + * @param filePath 文件路径 + * @returns 文件信息 + */ + static async getFileInfo(token: string, filePath: string): Promise { + const url = `${this.apiBaseUrl}/fs/info`; + + try { + const response = await axios.get(url, { + params: { path: filePath }, + headers: { Authorization: `Bearer ${token}` }, + }); + this.logger.log('获取文件信息成功'); + return response.data; + } catch (error) { + this.logger.error('获取文件信息失败', error); + throw new Error('获取文件信息失败'); + } + } + + /** + * 文件重命名 + * @param token 用户 Token + * @param oldPath 旧路径 + * @param newPath 新路径 + * @returns 重命名结果 + */ + static async renameFile( + token: string, + oldPath: string, + newPath: string, + ): Promise { + const url = `${this.apiBaseUrl}/fs/rename`; + + try { + const response = await axios.post( + url, + { oldPath, newPath }, + { + headers: { Authorization: `Bearer ${token}` }, + }, + ); + this.logger.log(`文件重命名成功: ${oldPath} => ${newPath}`); + return response.data; + } catch (error) { + this.logger.error('文件重命名失败', error); + throw new Error('文件重命名失败'); + } + } + + /** + * 为密码生成 sha256 hash + * @param password 密码 + * @returns hashed 密码 + */ + private static hashPassword(password: string): string { + return crypto.createHash('sha256').update(password).digest('hex'); + } +}