mirror of
https://github.com/jiangrui1994/CloudSaver.git
synced 2026-01-11 23:58:46 +08:00
Refactor the backend
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { AxiosHeaders, AxiosInstance } from "axios"; // 导入 AxiosHeaders
|
||||
import { createAxiosInstance } from "../utils/axiosInstance";
|
||||
import { ShareInfoResponse } from "../types/cloud115";
|
||||
import { ShareInfoResponse, FolderListResponse, SaveFileParams } from "../types/cloud";
|
||||
import { injectable } from "inversify";
|
||||
import { Request } from "express";
|
||||
import UserSetting from "../models/UserSetting";
|
||||
import { ICloudService } from "../types/services";
|
||||
import { ICloudStorageService } from "@/types/services";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
interface Cloud115ListItem {
|
||||
@@ -19,13 +19,8 @@ interface Cloud115FolderItem {
|
||||
ns: number;
|
||||
}
|
||||
|
||||
interface Cloud115PathItem {
|
||||
cid: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class Cloud115Service implements ICloudService {
|
||||
export class Cloud115Service implements ICloudStorageService {
|
||||
private api: AxiosInstance;
|
||||
private cookie: string = "";
|
||||
|
||||
@@ -80,19 +75,21 @@ export class Cloud115Service implements ICloudService {
|
||||
});
|
||||
if (response.data?.state && response.data.data?.list?.length > 0) {
|
||||
return {
|
||||
data: response.data.data.list.map((item: Cloud115ListItem) => ({
|
||||
fileId: item.cid,
|
||||
fileName: item.n,
|
||||
fileSize: item.s,
|
||||
})),
|
||||
data: {
|
||||
list: response.data.data.list.map((item: Cloud115ListItem) => ({
|
||||
fileId: item.cid,
|
||||
fileName: item.n,
|
||||
fileSize: item.s,
|
||||
})),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
logger.error("未找到文件信息:", response.data);
|
||||
throw new Error("未找到文件信息");
|
||||
}
|
||||
throw new Error("未找到文件信息");
|
||||
}
|
||||
|
||||
async getFolderList(
|
||||
parentCid = "0"
|
||||
): Promise<{ data: { cid: string; name: string; path: Cloud115PathItem[] }[] }> {
|
||||
async getFolderList(parentCid = "0"): Promise<FolderListResponse> {
|
||||
const response = await this.api.get("/files", {
|
||||
params: {
|
||||
aid: 1,
|
||||
@@ -128,17 +125,12 @@ export class Cloud115Service implements ICloudService {
|
||||
}
|
||||
}
|
||||
|
||||
async saveSharedFile(params: {
|
||||
cid: string;
|
||||
shareCode: string;
|
||||
receiveCode: string;
|
||||
fileId: string;
|
||||
}): Promise<{ message: string; data: unknown }> {
|
||||
async saveSharedFile(params: SaveFileParams): Promise<{ message: string; data: unknown }> {
|
||||
const param = new URLSearchParams({
|
||||
cid: params.cid,
|
||||
share_code: params.shareCode,
|
||||
receive_code: params.receiveCode,
|
||||
file_id: params.fileId,
|
||||
cid: params.folderId || "",
|
||||
share_code: params.shareCode || "",
|
||||
receive_code: params.receiveCode || "",
|
||||
file_id: params.fids?.[0] || "",
|
||||
});
|
||||
const response = await this.api.post("/share/receive", param.toString());
|
||||
logger.info("保存文件:", response.data);
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import { Sequelize, QueryTypes } from "sequelize";
|
||||
import GlobalSetting from "../models/GlobalSetting";
|
||||
import { Searcher } from "./Searcher";
|
||||
import sequelize from "../config/database";
|
||||
|
||||
// 全局设置默认值
|
||||
const DEFAULT_GLOBAL_SETTINGS = {
|
||||
httpProxyHost: "127.0.0.1",
|
||||
httpProxyPort: 7890,
|
||||
isProxyEnabled: false,
|
||||
CommonUserCode: 9527,
|
||||
AdminUserCode: 230713,
|
||||
};
|
||||
|
||||
export class DatabaseService {
|
||||
private sequelize: Sequelize;
|
||||
|
||||
constructor() {
|
||||
this.sequelize = new Sequelize({
|
||||
dialect: "sqlite",
|
||||
storage: "./data/database.sqlite",
|
||||
});
|
||||
this.sequelize = sequelize;
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
@@ -16,11 +25,26 @@ export class DatabaseService {
|
||||
await this.cleanupBackupTables();
|
||||
await this.sequelize.sync({ alter: true });
|
||||
await this.sequelize.query("PRAGMA foreign_keys = ON");
|
||||
await this.initializeGlobalSettings();
|
||||
} catch (error) {
|
||||
throw new Error(`数据库初始化失败: ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async initializeGlobalSettings(): Promise<void> {
|
||||
try {
|
||||
const settings = await GlobalSetting.findOne();
|
||||
if (!settings) {
|
||||
await GlobalSetting.create(DEFAULT_GLOBAL_SETTINGS);
|
||||
console.log("✅ Global settings initialized with default values.");
|
||||
}
|
||||
await Searcher.updateAxiosInstance();
|
||||
} catch (error) {
|
||||
console.error("❌ Failed to initialize global settings:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async cleanupBackupTables(): Promise<void> {
|
||||
const backupTables = await this.sequelize.query<{ name: string }>(
|
||||
"SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%\\_backup%' ESCAPE '\\'",
|
||||
|
||||
@@ -9,34 +9,60 @@ export class ImageService {
|
||||
private axiosInstance: AxiosInstance | null = null;
|
||||
|
||||
constructor() {
|
||||
this.initializeAxiosInstance();
|
||||
// 移除构造函数中的初始化,改为懒加载
|
||||
}
|
||||
|
||||
private async initializeAxiosInstance(): Promise<void> {
|
||||
const settings = await GlobalSetting.findOne();
|
||||
const globalSetting = settings?.dataValues || ({} as GlobalSettingAttributes);
|
||||
private async ensureAxiosInstance(): Promise<AxiosInstance> {
|
||||
if (!this.axiosInstance) {
|
||||
const settings = await GlobalSetting.findOne();
|
||||
const globalSetting = settings?.dataValues || ({} as GlobalSettingAttributes);
|
||||
|
||||
this.axiosInstance = axios.create({
|
||||
timeout: 3000,
|
||||
httpsAgent: globalSetting.isProxyEnabled
|
||||
? tunnel.httpsOverHttp({
|
||||
proxy: {
|
||||
host: globalSetting.httpProxyHost,
|
||||
port: globalSetting.httpProxyPort,
|
||||
headers: {
|
||||
"Proxy-Authorization": "",
|
||||
this.axiosInstance = axios.create({
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
Accept: "image/*, */*",
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||
},
|
||||
withCredentials: false,
|
||||
maxRedirects: 5,
|
||||
httpsAgent: globalSetting.isProxyEnabled
|
||||
? tunnel.httpsOverHttp({
|
||||
proxy: {
|
||||
host: globalSetting.httpProxyHost,
|
||||
port: globalSetting.httpProxyPort,
|
||||
headers: {
|
||||
"Proxy-Authorization": "",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
: undefined,
|
||||
withCredentials: true,
|
||||
});
|
||||
})
|
||||
: undefined,
|
||||
});
|
||||
|
||||
this.axiosInstance.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
}
|
||||
return this.axiosInstance;
|
||||
}
|
||||
|
||||
async updateAxiosInstance(): Promise<void> {
|
||||
this.axiosInstance = null;
|
||||
await this.ensureAxiosInstance();
|
||||
}
|
||||
|
||||
async getImages(url: string): Promise<any> {
|
||||
if (!this.axiosInstance) {
|
||||
throw new Error("Axios instance not initialized");
|
||||
}
|
||||
return await this.axiosInstance.get(url, { responseType: "stream" });
|
||||
const axiosInstance = await this.ensureAxiosInstance();
|
||||
|
||||
return await axiosInstance.get(url, {
|
||||
responseType: "stream",
|
||||
validateStatus: (status) => status >= 200 && status < 300,
|
||||
headers: {
|
||||
Referer: new URL(url).origin,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,13 @@ import { createAxiosInstance } from "../utils/axiosInstance";
|
||||
import { injectable } from "inversify";
|
||||
import { Request } from "express";
|
||||
import UserSetting from "../models/UserSetting";
|
||||
import {
|
||||
ShareInfoResponse,
|
||||
FolderListResponse,
|
||||
QuarkFolderItem,
|
||||
SaveFileParams,
|
||||
} from "../types/cloud";
|
||||
import { ICloudStorageService } from "@/types/services";
|
||||
|
||||
interface QuarkShareInfo {
|
||||
stoken?: string;
|
||||
@@ -17,14 +24,8 @@ interface QuarkShareInfo {
|
||||
}[];
|
||||
}
|
||||
|
||||
interface QuarkFolderItem {
|
||||
fid: string;
|
||||
file_name: string;
|
||||
file_type: number;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class QuarkService {
|
||||
export class QuarkService implements ICloudStorageService {
|
||||
private api: AxiosInstance;
|
||||
private cookie: string = "";
|
||||
|
||||
@@ -64,7 +65,7 @@ export class QuarkService {
|
||||
}
|
||||
}
|
||||
|
||||
async getShareInfo(pwdId: string, passcode = ""): Promise<{ data: QuarkShareInfo }> {
|
||||
async getShareInfo(pwdId: string, passcode = ""): Promise<ShareInfoResponse> {
|
||||
const response = await this.api.post(
|
||||
`/1/clouddrive/share/sharepage/token?pr=ucpro&fr=pc&uc_param_str=&__dt=994&__t=${Date.now()}`,
|
||||
{
|
||||
@@ -84,7 +85,7 @@ export class QuarkService {
|
||||
throw new Error("获取夸克分享信息失败");
|
||||
}
|
||||
|
||||
async getShareList(pwdId: string, stoken: string): Promise<QuarkShareInfo> {
|
||||
async getShareList(pwdId: string, stoken: string): Promise<ShareInfoResponse["data"]> {
|
||||
const response = await this.api.get("/1/clouddrive/share/sharepage/detail", {
|
||||
params: {
|
||||
pr: "ucpro",
|
||||
@@ -125,9 +126,7 @@ export class QuarkService {
|
||||
}
|
||||
}
|
||||
|
||||
async getFolderList(
|
||||
parentCid = "0"
|
||||
): Promise<{ data: { cid: string; name: string; path: [] }[] }> {
|
||||
async getFolderList(parentCid = "0"): Promise<FolderListResponse> {
|
||||
const response = await this.api.get("/1/clouddrive/file/sort", {
|
||||
params: {
|
||||
pr: "ucpro",
|
||||
@@ -161,19 +160,20 @@ export class QuarkService {
|
||||
}
|
||||
}
|
||||
|
||||
async saveSharedFile(params: {
|
||||
fid_list: string[];
|
||||
fid_token_list: string[];
|
||||
to_pdir_fid: string;
|
||||
pwd_id: string;
|
||||
stoken: string;
|
||||
pdir_fid: string;
|
||||
scene: string;
|
||||
}): Promise<{ message: string; data: unknown }> {
|
||||
async saveSharedFile(params: SaveFileParams): Promise<{ message: string; data: unknown }> {
|
||||
const quarkParams = {
|
||||
fid_list: params.fids,
|
||||
fid_token_list: params.fidTokens,
|
||||
to_pdir_fid: params.folderId,
|
||||
pwd_id: params.shareCode,
|
||||
stoken: params.receiveCode,
|
||||
pdir_fid: "0",
|
||||
scene: "link",
|
||||
};
|
||||
try {
|
||||
const response = await this.api.post(
|
||||
`/1/clouddrive/share/sharepage/save?pr=ucpro&fr=pc&uc_param_str=&__dt=208097&__t=${Date.now()}`,
|
||||
params
|
||||
quarkParams
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -31,9 +31,12 @@ export class Searcher {
|
||||
Searcher.instance = this;
|
||||
}
|
||||
|
||||
private async initAxiosInstance() {
|
||||
const settings = await GlobalSetting.findOne();
|
||||
const globalSetting = settings?.dataValues || ({} as GlobalSettingAttributes);
|
||||
private async initAxiosInstance(isUpdate: boolean = false) {
|
||||
let globalSetting = {} as GlobalSettingAttributes;
|
||||
if (isUpdate) {
|
||||
const settings = await GlobalSetting.findOne();
|
||||
globalSetting = settings?.dataValues || ({} as GlobalSettingAttributes);
|
||||
}
|
||||
this.api = createAxiosInstance(
|
||||
config.telegram.baseUrl,
|
||||
AxiosHeaders.from({
|
||||
@@ -59,9 +62,7 @@ export class Searcher {
|
||||
}
|
||||
|
||||
public static async updateAxiosInstance(): Promise<void> {
|
||||
if (Searcher.instance) {
|
||||
await Searcher.instance.initAxiosInstance();
|
||||
}
|
||||
await Searcher.instance.initAxiosInstance(true);
|
||||
}
|
||||
|
||||
private extractCloudLinks(text: string): { links: string[]; cloudType: string } {
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { injectable } from "inversify";
|
||||
import { injectable, inject } from "inversify";
|
||||
import { TYPES } from "../core/types";
|
||||
import UserSetting from "../models/UserSetting";
|
||||
import GlobalSetting from "../models/GlobalSetting";
|
||||
import { Searcher } from "./Searcher";
|
||||
import { ImageService } from "./ImageService";
|
||||
|
||||
@injectable()
|
||||
export class SettingService {
|
||||
constructor(@inject(TYPES.ImageService) private imageService: ImageService) {}
|
||||
|
||||
async getSettings(userId: string | undefined, role: number | undefined) {
|
||||
if (!userId) {
|
||||
throw new Error("用户ID无效");
|
||||
@@ -39,8 +43,17 @@ export class SettingService {
|
||||
if (role === 1 && globalSetting) {
|
||||
await GlobalSetting.update(globalSetting, { where: {} });
|
||||
}
|
||||
|
||||
await Searcher.updateAxiosInstance();
|
||||
await this.updateSettings();
|
||||
return { message: "保存成功" };
|
||||
}
|
||||
|
||||
async updateSettings(/* 参数 */): Promise<void> {
|
||||
// ... 其他代码 ...
|
||||
|
||||
// 修改这一行,使用注入的实例方法而不是静态方法
|
||||
await this.imageService.updateAxiosInstance();
|
||||
await Searcher.updateAxiosInstance();
|
||||
|
||||
// ... 其他代码 ...
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user