diff --git a/backend/src/controllers/BaseCloudController.ts b/backend/src/controllers/BaseCloudController.ts new file mode 100644 index 0000000..21d7ad6 --- /dev/null +++ b/backend/src/controllers/BaseCloudController.ts @@ -0,0 +1,32 @@ +import { Request, Response } from "express"; +import { BaseController } from "./BaseController"; +import { ICloudStorageService } from "@/types/services"; + +export abstract class BaseCloudController extends BaseController { + constructor(protected cloudService: ICloudStorageService) { + super(); + } + + async getShareInfo(req: Request, res: Response): Promise { + await this.handleRequest(req, res, async () => { + const { shareCode, receiveCode } = req.query; + await this.cloudService.setCookie(req); + return await this.cloudService.getShareInfo(shareCode as string, receiveCode as string); + }); + } + + async getFolderList(req: Request, res: Response): Promise { + await this.handleRequest(req, res, async () => { + const { parentCid } = req.query; + await this.cloudService.setCookie(req); + return await this.cloudService.getFolderList(parentCid as string); + }); + } + + async saveFile(req: Request, res: Response): Promise { + await this.handleRequest(req, res, async () => { + await this.cloudService.setCookie(req); + return await this.cloudService.saveSharedFile(req.body); + }); + } +} diff --git a/backend/src/controllers/BaseController.ts b/backend/src/controllers/BaseController.ts index b40cbdf..c17d954 100644 --- a/backend/src/controllers/BaseController.ts +++ b/backend/src/controllers/BaseController.ts @@ -9,11 +9,13 @@ export abstract class BaseController { protected async handleRequest( req: Request, res: Response, - action: () => Promise> + action: () => Promise | void> ): Promise { try { const result = await action(); - res.json(ApiResponse.success(result.data, result.message)); + if (result) { + res.json(ApiResponse.success(result.data, result.message)); + } } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : "未知错误"; res.status(200).json(ApiResponse.error(errorMessage)); diff --git a/backend/src/controllers/cloud115.ts b/backend/src/controllers/cloud115.ts index 7ff5c14..0b39e99 100644 --- a/backend/src/controllers/cloud115.ts +++ b/backend/src/controllers/cloud115.ts @@ -1,41 +1,11 @@ -import { Request, Response } from "express"; import { Cloud115Service } from "../services/Cloud115Service"; -import { BaseController } from "./BaseController"; import { injectable, inject } from "inversify"; import { TYPES } from "../core/types"; +import { BaseCloudController } from "./BaseCloudController"; @injectable() -export class Cloud115Controller extends BaseController { - constructor(@inject(TYPES.Cloud115Service) private cloud115Service: Cloud115Service) { - super(); - } - - async getShareInfo(req: Request, res: Response): Promise { - await this.handleRequest(req, res, async () => { - const { shareCode, receiveCode } = req.query; - await this.cloud115Service.setCookie(req); - return await this.cloud115Service.getShareInfo(shareCode as string, receiveCode as string); - }); - } - - async getFolderList(req: Request, res: Response): Promise { - await this.handleRequest(req, res, async () => { - const { parentCid } = req.query; - await this.cloud115Service.setCookie(req); - return await this.cloud115Service.getFolderList(parentCid as string); - }); - } - - async saveFile(req: Request, res: Response): Promise { - await this.handleRequest(req, res, async () => { - const { shareCode, receiveCode, fileId, folderId } = req.body; - await this.cloud115Service.setCookie(req); - return await this.cloud115Service.saveSharedFile({ - shareCode, - receiveCode, - fileId, - cid: folderId, - }); - }); +export class Cloud115Controller extends BaseCloudController { + constructor(@inject(TYPES.Cloud115Service) cloud115Service: Cloud115Service) { + super(cloud115Service); } } diff --git a/backend/src/controllers/quark.ts b/backend/src/controllers/quark.ts index ec57fa0..4ef6ddb 100644 --- a/backend/src/controllers/quark.ts +++ b/backend/src/controllers/quark.ts @@ -2,34 +2,11 @@ import { Request, Response } from "express"; import { injectable, inject } from "inversify"; import { TYPES } from "../core/types"; import { QuarkService } from "../services/QuarkService"; -import { BaseController } from "./BaseController"; +import { BaseCloudController } from "./BaseCloudController"; @injectable() -export class QuarkController extends BaseController { - constructor(@inject(TYPES.QuarkService) private quarkService: QuarkService) { - super(); - } - - async getShareInfo(req: Request, res: Response): Promise { - await this.handleRequest(req, res, async () => { - const { shareCode, receiveCode } = req.query; - await this.quarkService.setCookie(req); - return await this.quarkService.getShareInfo(shareCode as string, receiveCode as string); - }); - } - - async getFolderList(req: Request, res: Response): Promise { - await this.handleRequest(req, res, async () => { - const { parentCid } = req.query; - await this.quarkService.setCookie(req); - return await this.quarkService.getFolderList(parentCid as string); - }); - } - - async saveFile(req: Request, res: Response): Promise { - await this.handleRequest(req, res, async () => { - await this.quarkService.setCookie(req); - return await this.quarkService.saveSharedFile(req.body); - }); +export class QuarkController extends BaseCloudController { + constructor(@inject(TYPES.QuarkService) quarkService: QuarkService) { + super(quarkService); } } diff --git a/backend/src/controllers/teleImages.ts b/backend/src/controllers/teleImages.ts index f838631..450503a 100644 --- a/backend/src/controllers/teleImages.ts +++ b/backend/src/controllers/teleImages.ts @@ -12,8 +12,19 @@ export class ImageController extends BaseController { async getImages(req: Request, res: Response): Promise { await this.handleRequest(req, res, async () => { - const url = req.query.url as string; - return await this.imageService.getImages(url); + const url = decodeURIComponent((req.query.url as string) || ""); + const response = await this.imageService.getImages(url); + + // 设置正确的响应头 + res.setHeader("Content-Type", response.headers["content-type"]); + res.setHeader("Cache-Control", "no-cache"); + + // 确保清除任何可能导致304响应的头信息 + res.removeHeader("etag"); + res.removeHeader("last-modified"); + + // 直接传输图片数据 + response.data.pipe(res); }); } } diff --git a/backend/src/interfaces/ICloudService.ts b/backend/src/interfaces/ICloudService.ts deleted file mode 100644 index 996c104..0000000 --- a/backend/src/interfaces/ICloudService.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { - ShareInfoResponse, - FolderListResponse, - SaveFileParams, - SaveFileResponse, -} from "../types/cloud"; - -export interface ICloudService { - getShareInfo(shareCode: string, receiveCode?: string): Promise; - getFolderList(parentCid?: string): Promise; - saveSharedFile(params: SaveFileParams): Promise; -} diff --git a/backend/src/middleware/auth.ts b/backend/src/middleware/auth.ts index 0511ef7..684707a 100644 --- a/backend/src/middleware/auth.ts +++ b/backend/src/middleware/auth.ts @@ -16,7 +16,6 @@ export const authMiddleware = async ( res: Response, next: NextFunction ): Promise => { - console.log(req.path); if (req.path === "/user/login" || req.path === "/user/register" || req.path === "/tele-images/") { return next(); } diff --git a/backend/src/middleware/requestLogger.ts b/backend/src/middleware/requestLogger.ts index 4803b6e..4e850b3 100644 --- a/backend/src/middleware/requestLogger.ts +++ b/backend/src/middleware/requestLogger.ts @@ -1,10 +1,15 @@ import { Request, Response, NextFunction } from "express"; import { logger } from "../utils/logger"; +const excludePaths = ["/tele-images/"]; + export const requestLogger = () => { return (req: Request, res: Response, next: NextFunction) => { const start = Date.now(); res.on("finish", () => { + if (excludePaths.includes(req.path)) { + return; + } const duration = Date.now() - start; logger.info({ method: req.method, diff --git a/backend/src/routes/setting.ts b/backend/src/routes/setting.ts deleted file mode 100644 index 474c116..0000000 --- a/backend/src/routes/setting.ts +++ /dev/null @@ -1,9 +0,0 @@ -import express from "express"; -import { settingController } from "../controllers/setting"; - -const router = express.Router(); - -router.get("/get", settingController.get); -router.post("/save", settingController.save); - -export default router; diff --git a/backend/src/routes/user.ts b/backend/src/routes/user.ts deleted file mode 100644 index f0dbc76..0000000 --- a/backend/src/routes/user.ts +++ /dev/null @@ -1,10 +0,0 @@ -// backend/src/routes/user.ts -import express from "express"; -import { userController } from "../controllers/user"; - -const router = express.Router(); - -router.post("/register", userController.register); -router.post("/login", userController.login); - -export default router; diff --git a/backend/src/services/Cloud115Service.ts b/backend/src/services/Cloud115Service.ts index 53a06c8..d4245e4 100644 --- a/backend/src/services/Cloud115Service.ts +++ b/backend/src/services/Cloud115Service.ts @@ -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 { 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); diff --git a/backend/src/services/DatabaseService.ts b/backend/src/services/DatabaseService.ts index dbe01ac..ec447b9 100644 --- a/backend/src/services/DatabaseService.ts +++ b/backend/src/services/DatabaseService.ts @@ -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 { @@ -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 { + 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 { const backupTables = await this.sequelize.query<{ name: string }>( "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%\\_backup%' ESCAPE '\\'", diff --git a/backend/src/services/ImageService.ts b/backend/src/services/ImageService.ts index 8c454e0..1f59ff5 100644 --- a/backend/src/services/ImageService.ts +++ b/backend/src/services/ImageService.ts @@ -9,34 +9,60 @@ export class ImageService { private axiosInstance: AxiosInstance | null = null; constructor() { - this.initializeAxiosInstance(); + // 移除构造函数中的初始化,改为懒加载 } - private async initializeAxiosInstance(): Promise { - const settings = await GlobalSetting.findOne(); - const globalSetting = settings?.dataValues || ({} as GlobalSettingAttributes); + private async ensureAxiosInstance(): Promise { + 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 { + this.axiosInstance = null; + await this.ensureAxiosInstance(); } async getImages(url: string): Promise { - 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, + }, + }); } } diff --git a/backend/src/services/QuarkService.ts b/backend/src/services/QuarkService.ts index cfce52c..d5b3f8a 100644 --- a/backend/src/services/QuarkService.ts +++ b/backend/src/services/QuarkService.ts @@ -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 { 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 { + async getShareList(pwdId: string, stoken: string): Promise { 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 { 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 { diff --git a/backend/src/services/Searcher.ts b/backend/src/services/Searcher.ts index e99aa67..005e0b8 100644 --- a/backend/src/services/Searcher.ts +++ b/backend/src/services/Searcher.ts @@ -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 { - if (Searcher.instance) { - await Searcher.instance.initAxiosInstance(); - } + await Searcher.instance.initAxiosInstance(true); } private extractCloudLinks(text: string): { links: string[]; cloudType: string } { diff --git a/backend/src/services/SettingService.ts b/backend/src/services/SettingService.ts index a30e1e8..98e75b4 100644 --- a/backend/src/services/SettingService.ts +++ b/backend/src/services/SettingService.ts @@ -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 { + // ... 其他代码 ... + + // 修改这一行,使用注入的实例方法而不是静态方法 + await this.imageService.updateAxiosInstance(); + await Searcher.updateAxiosInstance(); + + // ... 其他代码 ... + } } diff --git a/backend/src/types/cloud.ts b/backend/src/types/cloud.ts index 84776e6..4b428f1 100644 --- a/backend/src/types/cloud.ts +++ b/backend/src/types/cloud.ts @@ -1,11 +1,23 @@ export interface ShareInfoResponse { data: { - fileId: string; - fileName: string; - fileSize: number; - }[]; + list: ShareInfoItem[]; + fileSize?: number; + pwdId?: string; + stoken?: string; + }; } +export interface GetShareInfoParams { + shareCode: string; + receiveCode?: string; +} + +export interface ShareInfoItem { + fileId: string; + fileName: string; + fileSize?: number; + fileIdToken?: string; +} export interface FolderListResponse { data: { cid: string; @@ -15,13 +27,57 @@ export interface FolderListResponse { } export interface SaveFileParams { - shareCode: string; - receiveCode?: string; - fileId: string; - cid?: string; + shareCode: string; // 分享code + receiveCode?: string; // 分享文件的密码 + folderId?: string; // 文件夹id + fids?: string[]; // 存储文件id + fidTokens?: string[]; // 存储文件token } export interface SaveFileResponse { message: string; data: unknown; } + +export interface ShareFileInfo { + shareCode: string; + receiveCode?: string; + fileId: string; + cid?: string; + fid_list?: string[]; + fid_token_list?: string[]; + to_pdir_fid?: string; + pwd_id?: string; + stoken?: string; + pdir_fid?: string; + scene?: string; + [key: string]: any; +} + +export interface QuarkShareFileInfo { + fid_list: string[]; + fid_token_list: string[]; + to_pdir_fid: string; + pwd_id: string; + stoken: string; + pdir_fid: string; + scene: string; +} + +export interface QuarkShareInfo { + stoken?: string; + pwdId?: string; + fileSize?: number; + list: { + fid: string; + file_name: string; + file_type: number; + share_fid_token: string; + }[]; +} + +export interface QuarkFolderItem { + fid: string; + file_name: string; + file_type: number; +} diff --git a/backend/src/types/services.ts b/backend/src/types/services.ts index bcb6fcc..f07fba4 100644 --- a/backend/src/types/services.ts +++ b/backend/src/types/services.ts @@ -1,9 +1,9 @@ import { Request } from "express"; -import { ShareInfoResponse } from "./cloud115"; +import { ShareInfoResponse, FolderListResponse, SaveFileParams } from "./cloud"; -export interface ICloudService { +export interface ICloudStorageService { setCookie(req: Request): Promise; getShareInfo(shareCode: string, receiveCode?: string): Promise; - getFolderList(parentCid?: string): Promise; - saveSharedFile(params: any): Promise; + getFolderList(parentCid?: string): Promise; + saveSharedFile(params: SaveFileParams): Promise; } diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 326a461..609c95a 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -43,27 +43,6 @@ declare module 'vue' { RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SearchBar: typeof import('./src/components/SearchBar.vue')['default'] - VanBackTop: typeof import('vant/es')['BackTop'] - VanButton: typeof import('vant/es')['Button'] - VanCell: typeof import('vant/es')['Cell'] - VanCellGroup: typeof import('vant/es')['CellGroup'] - VanCheckbox: typeof import('vant/es')['Checkbox'] - VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup'] - VanEmpty: typeof import('vant/es')['Empty'] - VanField: typeof import('vant/es')['Field'] - VanForm: typeof import('vant/es')['Form'] - VanIcon: typeof import('vant/es')['Icon'] - VanImage: typeof import('vant/es')['Image'] - VanLoading: typeof import('vant/es')['Loading'] - VanOverlay: typeof import('vant/es')['Overlay'] - VanPopup: typeof import('vant/es')['Popup'] - VanSearch: typeof import('vant/es')['Search'] - VanSwitch: typeof import('vant/es')['Switch'] - VanTab: typeof import('vant/es')['Tab'] - VanTabbar: typeof import('vant/es')['Tabbar'] - VanTabbarItem: typeof import('vant/es')['TabbarItem'] - VanTabs: typeof import('vant/es')['Tabs'] - VanTag: typeof import('vant/es')['Tag'] } export interface ComponentCustomProperties { vLoading: typeof import('element-plus/es')['ElLoadingDirective'] diff --git a/frontend/src/api/cloud115.ts b/frontend/src/api/cloud115.ts index 73c016f..f8d3168 100644 --- a/frontend/src/api/cloud115.ts +++ b/frontend/src/api/cloud115.ts @@ -1,10 +1,10 @@ import request from "@/utils/request"; -import type { ShareInfoResponse, Folder, Save115FileParams } from "@/types"; +import type { ShareInfoResponse, Folder, SaveFileParams, GetShareInfoParams } from "@/types"; export const cloud115Api = { - async getShareInfo(shareCode: string, receiveCode = "") { + async getShareInfo(params: GetShareInfoParams) { const { data } = await request.get("/api/cloud115/share-info", { - params: { shareCode, receiveCode }, + params, }); return data as ShareInfoResponse; }, @@ -16,7 +16,7 @@ export const cloud115Api = { return res; }, - async saveFile(params: Save115FileParams) { + async saveFile(params: SaveFileParams) { const res = await request.post("/api/cloud115/save", params); return res; }, diff --git a/frontend/src/api/quark.ts b/frontend/src/api/quark.ts index 2e7bb52..ebd3a7a 100644 --- a/frontend/src/api/quark.ts +++ b/frontend/src/api/quark.ts @@ -1,10 +1,10 @@ import request from "@/utils/request"; -import type { ShareInfoResponse, Folder, SaveQuarkFileParams } from "@/types"; +import type { ShareInfoResponse, Folder, SaveFileParams, GetShareInfoParams } from "@/types"; export const quarkApi = { - async getShareInfo(shareCode: string, receiveCode = "") { + async getShareInfo(params: GetShareInfoParams) { const { data } = await request.get("/api/quark/share-info", { - params: { shareCode, receiveCode }, + params, }); return data as ShareInfoResponse; }, @@ -16,7 +16,7 @@ export const quarkApi = { return data; }, - async saveFile(params: SaveQuarkFileParams) { + async saveFile(params: SaveFileParams) { return await request.post("/api/quark/save", params); }, }; diff --git a/frontend/src/stores/resource.ts b/frontend/src/stores/resource.ts index c7b0ee9..24338b1 100644 --- a/frontend/src/stores/resource.ts +++ b/frontend/src/stores/resource.ts @@ -5,10 +5,11 @@ import { quarkApi } from "@/api/quark"; import type { Resource, ShareInfoResponse, - Save115FileParams, - SaveQuarkFileParams, ShareInfo, ResourceItem, + GetShareInfoParams, + SaveFileParams, + ShareFileInfoAndFolder, } from "@/types"; import { ElMessage } from "element-plus"; @@ -24,46 +25,40 @@ const lastResource = ( ) as StorageListObject; // 定义云盘驱动配置类型 -interface CloudDriveConfig< - T extends Record, - P extends Save115FileParams | SaveQuarkFileParams, -> { +interface CloudDriveConfig { name: string; type: string; regex: RegExp; api: { - getShareInfo: (parsedCode: T) => Promise; - saveFile: (params: P) => Promise<{ code: number; message?: string }>; + getShareInfo: (params: GetShareInfoParams) => Promise; + saveFile: (params: SaveFileParams) => Promise<{ code: number; message?: string }>; }; - parseShareCode: (match: RegExpMatchArray) => T; - getSaveParams: (shareInfo: ShareInfoResponse, folderId: string) => P; + parseShareCode: (match: RegExpMatchArray) => GetShareInfoParams; + getSaveParams: (shareInfoAndFolder: ShareFileInfoAndFolder) => SaveFileParams; } // 云盘类型配置 -export const CLOUD_DRIVES: [ - CloudDriveConfig<{ shareCode: string; receiveCode: string }, Save115FileParams>, - CloudDriveConfig<{ shareCode: string }, SaveQuarkFileParams>, -] = [ +export const CLOUD_DRIVES: CloudDriveConfig[] = [ { name: "115网盘", type: "pan115", regex: /(?:115|anxia|115cdn)\.com\/s\/([^?]+)(?:\?password=([^&#]+))?/, api: { - getShareInfo: (parsedCode: { shareCode: string; receiveCode: string }) => - cloud115Api.getShareInfo(parsedCode.shareCode, parsedCode.receiveCode), - saveFile: async (params: Save115FileParams) => { - return await cloud115Api.saveFile(params as Save115FileParams); + getShareInfo: (params: GetShareInfoParams) => cloud115Api.getShareInfo(params), + saveFile: async (params: SaveFileParams) => { + return await cloud115Api.saveFile(params); }, }, parseShareCode: (match: RegExpMatchArray) => ({ shareCode: match[1], receiveCode: match[2] || "", }), - getSaveParams: (shareInfo: ShareInfoResponse, folderId: string) => ({ - shareCode: shareInfo.shareCode || "", - receiveCode: shareInfo.receiveCode || "", - fileId: shareInfo.list[0].fileId, - folderId, + getSaveParams: (shareInfoAndFolder: ShareFileInfoAndFolder) => ({ + shareCode: shareInfoAndFolder.shareCode || "", + receiveCode: shareInfoAndFolder.receiveCode || "", + fileId: shareInfoAndFolder.shareInfo.list[0].fileId, + folderId: shareInfoAndFolder.folderId, + fids: shareInfoAndFolder.shareInfo.list.map((item: { fileId?: string }) => item.fileId || ""), }), }, { @@ -71,23 +66,20 @@ export const CLOUD_DRIVES: [ type: "quark", regex: /pan\.quark\.cn\/s\/([a-zA-Z0-9]+)/, api: { - getShareInfo: (parsedCode: { shareCode: string }) => - quarkApi.getShareInfo(parsedCode.shareCode), - saveFile: async (params: SaveQuarkFileParams) => { - return await quarkApi.saveFile(params as SaveQuarkFileParams); + getShareInfo: (params) => quarkApi.getShareInfo(params), + saveFile: async (params: SaveFileParams) => { + return await quarkApi.saveFile(params); }, }, parseShareCode: (match: RegExpMatchArray) => ({ shareCode: match[1] }), - getSaveParams: (shareInfo: ShareInfoResponse, folderId: string) => ({ - fid_list: shareInfo.list.map((item: { fileId?: string }) => item.fileId || ""), - fid_token_list: shareInfo.list.map( + getSaveParams: (shareInfoAndFolder: ShareFileInfoAndFolder) => ({ + fids: shareInfoAndFolder.shareInfo.list.map((item: { fileId?: string }) => item.fileId || ""), + fidTokens: shareInfoAndFolder.shareInfo.list.map( (item: { fileIdToken?: string }) => item.fileIdToken || "" ), - to_pdir_fid: folderId, - pwd_id: shareInfo.shareCode || "", - stoken: shareInfo.stoken || "", - pdir_fid: "0", - scene: "link", + folderId: shareInfoAndFolder.folderId, + shareCode: shareInfoAndFolder.shareInfo.pwdId || "", + receiveCode: shareInfoAndFolder.shareInfo.stoken || "", }), }, ]; @@ -189,39 +181,32 @@ export const useResourceStore = defineStore("resource", { async saveResourceToDrive( resource: ResourceItem, folderId: string, - drive: - | CloudDriveConfig<{ shareCode: string; receiveCode: string }, Save115FileParams> - | CloudDriveConfig<{ shareCode: string }, SaveQuarkFileParams> + drive: CloudDriveConfig ): Promise { const link = resource.cloudLinks.find((link) => drive.regex.test(link)); if (!link) return; const match = link.match(drive.regex); if (!match) throw new Error("链接解析失败"); + const parsedCode = drive.parseShareCode(match); const shareInfo = { ...this.shareInfo, list: this.resourceSelect.filter((x) => x.isChecked), }; + console.log(shareInfo); - if (this.is115Drive(drive)) { - const params = drive.getSaveParams(shareInfo, folderId); - const result = await drive.api.saveFile(params); + const params = drive.getSaveParams({ + shareInfo, + ...parsedCode, + folderId, + }); + const result = await drive.api.saveFile(params); - if (result.code === 0) { - ElMessage.success(`${drive.name} 转存成功`); - } else { - ElMessage.error(result.message); - } + if (result.code === 0) { + ElMessage.success(`${drive.name} 转存成功`); } else { - const params = drive.getSaveParams(shareInfo, folderId); - const result = await drive.api.saveFile(params); - - if (result.code === 0) { - ElMessage.success(`${drive.name} 转存成功`); - } else { - ElMessage.error(result.message); - } + ElMessage.error(result.message); } }, @@ -237,18 +222,7 @@ export const useResourceStore = defineStore("resource", { if (!match) throw new Error("链接解析失败"); const parsedCode = matchedDrive.parseShareCode(match); - let shareInfo = this.is115Drive(matchedDrive) - ? await matchedDrive.api.getShareInfo( - parsedCode as { shareCode: string; receiveCode: string } - ) - : await matchedDrive.api.getShareInfo(parsedCode as { shareCode: string }); - - if (Array.isArray(shareInfo)) { - shareInfo = { - list: shareInfo, - }; - } - + const shareInfo = await matchedDrive.api.getShareInfo(parsedCode); if (shareInfo?.list?.length) { this.resources = [ { @@ -296,30 +270,14 @@ export const useResourceStore = defineStore("resource", { if (!match) throw new Error("链接解析失败"); const parsedCode = drive.parseShareCode(match); - let shareInfo = {} as ShareInfoResponse; this.setLoadTree(true); - if (this.is115Drive(drive)) { - shareInfo = await drive.api.getShareInfo( - parsedCode as { shareCode: string; receiveCode: string } - ); - } else { - shareInfo = this.is115Drive(drive) - ? await drive.api.getShareInfo(parsedCode as { shareCode: string; receiveCode: string }) - : await drive.api.getShareInfo(parsedCode as { shareCode: string }); - } + let shareInfo = await drive.api.getShareInfo(parsedCode); this.setLoadTree(false); if (shareInfo) { - if (Array.isArray(shareInfo)) { - shareInfo = { - list: shareInfo, - ...parsedCode, - }; - } else { - shareInfo = { - ...shareInfo, - ...parsedCode, - }; - } + shareInfo = { + ...shareInfo, + ...parsedCode, + }; this.shareInfo = shareInfo; this.setSelectedResource(this.shareInfo.list.map((x) => ({ ...x, isChecked: true }))); return true; @@ -334,13 +292,5 @@ export const useResourceStore = defineStore("resource", { console.error(message, error); ElMessage.error(error instanceof Error ? error.message : message); }, - - is115Drive( - drive: - | CloudDriveConfig<{ shareCode: string; receiveCode: string }, Save115FileParams> - | CloudDriveConfig<{ shareCode: string }, SaveQuarkFileParams> - ): drive is CloudDriveConfig<{ shareCode: string; receiveCode: string }, Save115FileParams> { - return drive.type === "pan115"; - }, }, }); diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index cb1fb1d..f1d1e56 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -33,13 +33,25 @@ export interface ShareInfo { isChecked?: boolean; } +export interface ShareInfoItem { + fileId: string; + fileName: string; + fileSize?: number; + fileIdToken?: string; +} + export interface ShareInfoResponse { - list: ShareInfo[]; + list: ShareInfoItem[]; + fileSize?: number; pwdId?: string; stoken?: string; - shareCode?: string; +} + +export interface ShareFileInfoAndFolder { + shareInfo: ShareInfoResponse; + folderId: string; + shareCode: string; receiveCode?: string; - fileSize?: number; } export interface Folder { @@ -49,10 +61,16 @@ export interface Folder { } export interface SaveFileParams { + shareCode: string; // 分享code + receiveCode?: string; // 分享文件的密码 + folderId: string; // 文件夹id + fids: string[]; // 存储文件id + fidTokens?: string[]; // 存储文件token +} + +export interface GetShareInfoParams { shareCode: string; - receiveCode: string; - fileId: string; - folderId: string; + receiveCode?: string; } export interface ApiResponse {