mirror of
https://github.com/crystelf/crystelf-core.git
synced 2025-12-05 10:31:56 +00:00
feat:oplist获取token实现
This commit is contained in:
parent
cbea0c2b33
commit
fcd50a2569
@ -26,6 +26,7 @@
|
||||
"@nestjs/websockets": "^11.1.6",
|
||||
"axios": "1.11.0",
|
||||
"ioredis": "^5.6.1",
|
||||
"moment": "^2.30.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"simple-git": "^3.28.0",
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -41,6 +41,9 @@ importers:
|
||||
ioredis:
|
||||
specifier: ^5.6.1
|
||||
version: 5.6.1
|
||||
moment:
|
||||
specifier: ^2.30.1
|
||||
version: 2.30.1
|
||||
reflect-metadata:
|
||||
specifier: ^0.2.2
|
||||
version: 0.2.2
|
||||
@ -2780,6 +2783,9 @@ packages:
|
||||
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
|
||||
hasBin: true
|
||||
|
||||
moment@2.30.1:
|
||||
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
|
||||
|
||||
ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
|
||||
@ -6743,6 +6749,8 @@ snapshots:
|
||||
dependencies:
|
||||
minimist: 1.2.8
|
||||
|
||||
moment@2.30.1: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
multer@2.0.2:
|
||||
|
||||
@ -2,15 +2,157 @@ 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';
|
||||
import * as moment from 'moment';
|
||||
|
||||
@Injectable()
|
||||
export class OpenListService {
|
||||
private readonly logger = new Logger(OpenListService.name);
|
||||
private token: string | undefined;
|
||||
private tokenExpireTime: moment.Moment | undefined;
|
||||
|
||||
constructor(
|
||||
@Inject(AppConfigService)
|
||||
private readonly configService: AppConfigService,
|
||||
) {
|
||||
OpenListUtils.init(configService);
|
||||
this.initialize().then();
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务初始化
|
||||
*/
|
||||
private async initialize() {
|
||||
const apiBaseUrl = this.configService.get('OPENLIST_API_BASE_URL');
|
||||
const username = this.configService.get('OPENLIST_API_BASE_USERNAME');
|
||||
const password = this.configService.get('OPENLIST_API_BASE_PASSWORD');
|
||||
|
||||
OpenListUtils.init(this.configService);
|
||||
if (username && password) {
|
||||
this.token = await this.fetchToken(username, password);
|
||||
this.tokenExpireTime = moment().add(48, 'hours');
|
||||
this.logger.log(`OpenList服务初始化成功: ${apiBaseUrl}`);
|
||||
} else {
|
||||
this.logger.error(
|
||||
`OpenList服务初始化失败,请检查是否填写.env处的用户名和密码..`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取OpenList的JWT Token,如果Token已过期,则重新获取
|
||||
* @param username 用户名
|
||||
* @param password 密码
|
||||
* @returns JWT Token
|
||||
*/
|
||||
private async fetchToken(
|
||||
username: string,
|
||||
password: string,
|
||||
): Promise<string> {
|
||||
if (
|
||||
this.token &&
|
||||
this.tokenExpireTime &&
|
||||
moment().isBefore(this.tokenExpireTime)
|
||||
) {
|
||||
return this.token;
|
||||
}
|
||||
try {
|
||||
const newToken = await OpenListUtils.getToken(username, password);
|
||||
this.tokenExpireTime = moment().add(48, 'hours'); //过期时间
|
||||
return newToken;
|
||||
} catch (error) {
|
||||
this.logger.error('获取Token失败:', error);
|
||||
throw new Error('获取Token失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
* @returns 用户信息
|
||||
*/
|
||||
public async getUserInfo(): Promise<UserInfo> {
|
||||
try {
|
||||
const token = await this.fetchToken(
|
||||
<string>this.configService.get('OPENLIST_API_BASE_USERNAME'),
|
||||
<string>this.configService.get('OPENLIST_API_BASE_PASSWORD'),
|
||||
);
|
||||
return await OpenListUtils.getUserInfo(token);
|
||||
} catch (error) {
|
||||
this.logger.error('获取用户信息失败:', error);
|
||||
throw new Error('获取用户信息失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出目录下的所有文件
|
||||
* @param path 目录路径
|
||||
* @returns 目录下的文件列表
|
||||
*/
|
||||
public async listFiles(path: string): Promise<DirectoryList[]> {
|
||||
try {
|
||||
const token = await this.fetchToken(
|
||||
<string>this.configService.get('OPENLIST_API_BASE_USERNAME'),
|
||||
<string>this.configService.get('OPENLIST_API_BASE_PASSWORD'),
|
||||
);
|
||||
return await OpenListUtils.listDirectory(token, path);
|
||||
} catch (error) {
|
||||
this.logger.error('列出目录失败:', error);
|
||||
throw new Error('列出目录失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
* @param filePath 文件路径
|
||||
* @returns 文件信息
|
||||
*/
|
||||
public async getFileInfo(filePath: string): Promise<FileInfo> {
|
||||
try {
|
||||
const token = await this.fetchToken(
|
||||
<string>this.configService.get('OPENLIST_API_BASE_USERNAME'),
|
||||
<string>this.configService.get('OPENLIST_API_BASE_PASSWORD'),
|
||||
);
|
||||
return await OpenListUtils.getFileInfo(token, filePath);
|
||||
} catch (error) {
|
||||
this.logger.error('获取文件信息失败:', error);
|
||||
throw new Error('获取文件信息失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
* @param filePath 文件路径
|
||||
* @param downloadPath 本地下载路径
|
||||
*/
|
||||
public async downloadFile(
|
||||
filePath: string,
|
||||
downloadPath: string,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const token = await this.fetchToken(
|
||||
<string>this.configService.get('OPENLIST_API_BASE_USERNAME'),
|
||||
<string>this.configService.get('OPENLIST_API_BASE_PASSWORD'),
|
||||
);
|
||||
await OpenListUtils.downloadFile(token, filePath, downloadPath);
|
||||
} catch (error) {
|
||||
this.logger.error('下载文件失败:', error);
|
||||
throw new Error('下载文件失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param filePath 上传路径
|
||||
* @param file 文件
|
||||
*/
|
||||
public async uploadFile(filePath: string, file: any): Promise<void> {
|
||||
try {
|
||||
const token = await this.fetchToken(
|
||||
<string>this.configService.get('OPENLIST_API_BASE_USERNAME'),
|
||||
<string>this.configService.get('OPENLIST_API_BASE_PASSWORD'),
|
||||
);
|
||||
await OpenListUtils.uploadFile(token, filePath, file);
|
||||
} catch (error) {
|
||||
this.logger.error('上传文件失败:', error);
|
||||
throw new Error('上传文件失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import axios from 'axios';
|
||||
import { AppConfigService } from '../../config/config.service';
|
||||
import { Inject, Logger } from '@nestjs/common';
|
||||
import * as crypto from 'crypto';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export class OpenListUtils {
|
||||
private static readonly logger = new Logger(OpenListUtils.name);
|
||||
@ -9,6 +10,7 @@ export class OpenListUtils {
|
||||
|
||||
static init(@Inject(AppConfigService) configService: AppConfigService) {
|
||||
this.apiBaseUrl = configService.get('OPENLIST_API_BASE_URL');
|
||||
this.logger.log('OpenListUtils初始化..');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -18,20 +20,25 @@ export class OpenListUtils {
|
||||
* @returns token
|
||||
*/
|
||||
static async getToken(username: string, password: string): Promise<string> {
|
||||
const url = `${this.apiBaseUrl}/auth/token`;
|
||||
const hashedPassword = this.hashPassword(password);
|
||||
const url = `${this.apiBaseUrl}/api/auth/login`;
|
||||
|
||||
try {
|
||||
const response = await axios.post(url, {
|
||||
username,
|
||||
password: hashedPassword,
|
||||
username: username,
|
||||
password: password,
|
||||
});
|
||||
const token = response.data.token;
|
||||
this.logger.log(`获取 Token 成功: ${token}`);
|
||||
this.logger.debug(response);
|
||||
if (response.data.data.token) {
|
||||
const token: string = response.data.data.token;
|
||||
this.logger.log(`获取Token成功: ${token}`);
|
||||
return token;
|
||||
} else {
|
||||
this.logger.error(`获取Token失败: ${response.data.data.message}`);
|
||||
return 'null';
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error('获取 Token 失败', error);
|
||||
throw new Error('获取 Token 失败');
|
||||
this.logger.error('获取Token失败..', error);
|
||||
throw new Error('获取Token失败..');
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,11 +54,11 @@ export class OpenListUtils {
|
||||
const response = await axios.get(url, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
this.logger.log('获取用户信息成功');
|
||||
this.logger.log('获取用户信息成功..');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.logger.error('获取用户信息失败', error);
|
||||
throw new Error('获取用户信息失败');
|
||||
this.logger.error('获取用户信息失败..', error);
|
||||
throw new Error('获取用户信息失败..');
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,11 +76,11 @@ export class OpenListUtils {
|
||||
params: { path },
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
this.logger.log('列出目录成功');
|
||||
this.logger.log('列出目录成功..');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.logger.error('列出目录失败', error);
|
||||
throw new Error('列出目录失败');
|
||||
this.logger.error('列出目录失败..', error);
|
||||
throw new Error('列出目录失败..');
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,11 +98,11 @@ export class OpenListUtils {
|
||||
params: { path: filePath },
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
this.logger.log('获取文件信息成功');
|
||||
this.logger.log('获取文件信息成功..');
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.logger.error('获取文件信息失败', error);
|
||||
throw new Error('获取文件信息失败');
|
||||
this.logger.error('获取文件信息失败..', error);
|
||||
throw new Error('获取文件信息失败..');
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,17 +131,110 @@ export class OpenListUtils {
|
||||
this.logger.log(`文件重命名成功: ${oldPath} => ${newPath}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.logger.error('文件重命名失败', error);
|
||||
throw new Error('文件重命名失败');
|
||||
this.logger.error('文件重命名失败..', error);
|
||||
throw new Error('文件重命名失败..');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为密码生成 sha256 hash
|
||||
* @param password 密码
|
||||
* @returns hashed 密码
|
||||
* 下载文件
|
||||
* @param token 用户 Token
|
||||
* @param filePath 文件路径
|
||||
* @param downloadPath 本地下载路径
|
||||
*/
|
||||
private static hashPassword(password: string): string {
|
||||
return crypto.createHash('sha256').update(password).digest('hex');
|
||||
static async downloadFile(
|
||||
token: string,
|
||||
filePath: string,
|
||||
downloadPath: string,
|
||||
): Promise<void> {
|
||||
const url = `${this.apiBaseUrl}/fs/download`;
|
||||
|
||||
try {
|
||||
const response = await axios.get(url, {
|
||||
params: { path: filePath },
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'stream',
|
||||
});
|
||||
const writer = fs.createWriteStream(downloadPath);
|
||||
response.data.pipe(writer);
|
||||
|
||||
writer.on('finish', () => {
|
||||
this.logger.log(`文件下载成功: ${downloadPath}`);
|
||||
});
|
||||
|
||||
writer.on('error', (error) => {
|
||||
this.logger.error('下载文件失败', error);
|
||||
throw new Error('下载文件失败');
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('下载文件失败..', error);
|
||||
throw new Error('下载文件失败..');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param token 用户 Token
|
||||
* @param filePath 上传文件的路径
|
||||
* @param file 上传文件的内容
|
||||
* @returns 上传结果
|
||||
*/
|
||||
static async uploadFile(
|
||||
token: string,
|
||||
filePath: string,
|
||||
file: any,
|
||||
): Promise<any> {
|
||||
const url = `${this.apiBaseUrl}/fs/upload`;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('path', filePath);
|
||||
|
||||
try {
|
||||
const response = await axios.post(url, formData, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
|
||||
this.logger.log(`文件上传成功: ${filePath}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.logger.error('上传文件失败..', error);
|
||||
throw new Error('上传文件失败..');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取某个目录下的所有文件
|
||||
* @param token 用户 Token
|
||||
* @param directoryPath 目录路径
|
||||
* @returns 文件列表
|
||||
*/
|
||||
static async listFilesInDirectory(
|
||||
token: string,
|
||||
directoryPath: string,
|
||||
): Promise<any[]> {
|
||||
const directoryInfo = await this.listDirectory(token, directoryPath);
|
||||
return directoryInfo.filter(
|
||||
(item: { is_directory: any }) => !item.is_directory,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件更新
|
||||
* @param token 用户 Token
|
||||
* @param filePath 文件路径
|
||||
* @param lastModified 上次改动时间
|
||||
* @returns 文件是否更新
|
||||
*/
|
||||
static async checkFileUpdate(
|
||||
token: string,
|
||||
filePath: string,
|
||||
lastModified: string,
|
||||
): Promise<boolean> {
|
||||
const fileInfo = await this.getFileInfo(token, filePath);
|
||||
return fileInfo.modified_at !== lastModified;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user