From 185ba6e5cccaa97fd60b3c102f022cbc0bdd0780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=B8=80=E4=B9=8B?= Date: Fri, 18 Apr 2025 18:01:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=91=E5=90=8C=E6=AD=A5=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/service/service_worker/client.ts | 29 +++++++-- src/app/service/service_worker/index.ts | 11 ++++ src/app/service/service_worker/synchronize.ts | 53 ++++++++++++++-- src/locales/zh-CN/translation.json | 3 +- src/pages/options/routes/ScriptList.tsx | 2 +- src/pages/options/routes/Setting.tsx | 11 ++-- src/pages/options/routes/Tools.tsx | 61 +++++++++---------- src/pkg/config/config.ts | 7 +++ 8 files changed, 127 insertions(+), 50 deletions(-) diff --git a/src/app/service/service_worker/client.ts b/src/app/service/service_worker/client.ts index 53d7d78..157a190 100644 --- a/src/app/service/service_worker/client.ts +++ b/src/app/service/service_worker/client.ts @@ -5,6 +5,10 @@ import { Resource } from "@App/app/repo/resource"; import { MessageSend } from "@Packages/message/server"; import { ScriptMenu, ScriptMenuItem } from "./popup"; import PermissionVerify, { ConfirmParam, UserConfirm } from "./permission_verify"; +import { FileSystemType } from "@Packages/filesystem/factory"; +import { v4 as uuidv4 } from "uuid"; +import Cache from "@App/app/cache"; +import CacheKey from "@App/app/cache_key"; export class ServiceWorkerClient extends Client { constructor(msg: MessageSend) { @@ -156,11 +160,28 @@ export class SynchronizeClient extends Client { super(msg, "serviceWorker/synchronize"); } - backup(uuids?: string[]) { - return this.do("backup", uuids); + export(uuids?: string[]) { + return this.do("export", uuids); } - openImportWindow(filename: string, url: string) { - return this.do("openImportWindow", { filename, url }); + backupToCloud(type: FileSystemType, params: any) { + return this.do("backupToCloud", { type, params }); + } + + async openImportWindow(filename: string, file: File | Blob) { + // 打开导入窗口,用cache实现数据交互 + const url = URL.createObjectURL(file); + setTimeout(() => { + URL.revokeObjectURL(url); + }, 60 * 1000); + const uuid = uuidv4(); + await Cache.getInstance().set(CacheKey.importFile(uuid), { + filename: filename, + url: url, + }); + // 打开导入窗口,用cache实现数据交互 + chrome.tabs.create({ + url: `/src/import.html?uuid=${uuid}`, + }); } } diff --git a/src/app/service/service_worker/index.ts b/src/app/service/service_worker/index.ts index e16adca..c946b63 100644 --- a/src/app/service/service_worker/index.ts +++ b/src/app/service/service_worker/index.ts @@ -50,5 +50,16 @@ export default class ServiceWorkerManager { break; } }); + + // 监听配置变化 + this.mq.subscribe("systemConfigChange", (msg) => { + console.log("systemConfigChange", msg); + switch (msg.key) { + case "cloud_sync": { + synchronize.startCloudSync(msg.value); + break; + } + } + }); } } diff --git a/src/app/service/service_worker/synchronize.ts b/src/app/service/service_worker/synchronize.ts index 0582ee0..8fba6d8 100644 --- a/src/app/service/service_worker/synchronize.ts +++ b/src/app/service/service_worker/synchronize.ts @@ -12,6 +12,9 @@ import { ValueService } from "./value"; import { ResourceService } from "./resource"; import dayjs from "dayjs"; import { createObjectURL } from "../offscreen/client"; +import FileSystemFactory, { FileSystemType } from "@Packages/filesystem/factory"; +import { systemConfig } from "@App/pages/store/global"; +import { CloudSyncConfig } from "@App/pkg/config/config"; export class SynchronizeService { logger: Logger; @@ -169,8 +172,8 @@ export class SynchronizeService { }; } - // 请求生成备份文件 - async requestBackup(uuids?: string[]) { + // 请求导出文件 + async requestExport(uuids?: string[]) { const zip = new JSZip(); const fs = new ZipFileSystem(zip); await this.backup(fs, uuids); @@ -192,11 +195,49 @@ export class SynchronizeService { return Promise.resolve(); } - // 请求导入备份文件 - async openImportWindow(url: string) {} + // 备份到云端 + async backupToCloud({ type, params }: { type: FileSystemType; params: any }) { + // 首先生成zip文件 + const zip = new JSZip(); + const fs = new ZipFileSystem(zip); + await this.backup(fs); + this.logger.info("backup to cloud"); + // 然后创建云端文件系统 + let cloudFs = await FileSystemFactory.create(type, params); + try { + await cloudFs.createDir("ScriptCat"); + cloudFs = await cloudFs.openDir("ScriptCat"); + // 云端文件系统写入文件 + const file = await cloudFs.create(`scriptcat-backup-${dayjs().format("YYYY-MM-DDTHH-mm-ss")}.zip`); + await file.write( + await zip.generateAsync({ + type: "blob", + compression: "DEFLATE", + compressionOptions: { + level: 9, + }, + comment: "Created by Scriptcat", + }) + ); + } catch (e) { + this.logger.error("backup to cloud error", Logger.E(e)); + return Promise.reject(e); + } + return Promise.resolve(); + } + + cloudSync() {} + + startCloudSync(value: CloudSyncConfig) { + if (value.enable) { + + this.cloudSync(); + } + } init() { - this.group.on("backup", this.requestBackup.bind(this)); - this.group.on("import", this.openImportWindow.bind(this)); + this.group.on("export", this.requestExport.bind(this)); + this.group.on("backupToCloud", this.backupToCloud.bind(this)); + // this.group.on("import", this.openImportWindow.bind(this)); } } diff --git a/src/locales/zh-CN/translation.json b/src/locales/zh-CN/translation.json index fa5d238..efb594b 100644 --- a/src/locales/zh-CN/translation.json +++ b/src/locales/zh-CN/translation.json @@ -365,5 +365,6 @@ "menu_expand_num_after": "个时,自动隐藏", "script_name_cannot_be_set_to_empty": "脚本name不可以设置为空", "eslint_config_format_error": "eslint配置格式错误", - "export_success": "导出成功" + "export_success": "导出成功", + "get_backup_dir_url_failed": "获取备份目录地址失败" } \ No newline at end of file diff --git a/src/pages/options/routes/ScriptList.tsx b/src/pages/options/routes/ScriptList.tsx index b8ffafa..aaec85a 100644 --- a/src/pages/options/routes/ScriptList.tsx +++ b/src/pages/options/routes/ScriptList.tsx @@ -723,7 +723,7 @@ function ScriptList() { id: "export", content: t("exporting"), }); - new SynchronizeClient(message).backup(uuids).then(() => { + new SynchronizeClient(message).export(uuids).then(() => { Message.success({ id: "export", content: t("export_success"), diff --git a/src/pages/options/routes/Setting.tsx b/src/pages/options/routes/Setting.tsx index 82e67b3..d9e432a 100644 --- a/src/pages/options/routes/Setting.tsx +++ b/src/pages/options/routes/Setting.tsx @@ -170,14 +170,15 @@ function Setting() { return; } } - const params = { ...systemConfig.backup.params }; + const cloudSync = await systemConfig.getCloudSync(); + const params = { ...cloudSync.params }; params[fileSystemType] = fileSystemParams; - systemConfig.cloudSync = { - enable: enableCloudSync, - syncDelete, + systemConfig.setCloudSync({ + enable: enableCloudSync || false, + syncDelete: syncDelete || false, filesystem: fileSystemType, params, - }; + }); Message.success(t("save_success")!); }} > diff --git a/src/pages/options/routes/Tools.tsx b/src/pages/options/routes/Tools.tsx index e961e7b..f562957 100644 --- a/src/pages/options/routes/Tools.tsx +++ b/src/pages/options/routes/Tools.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { Button, Card, Checkbox, Drawer, Empty, Input, List, Message, Modal, Space } from "@arco-design/web-react"; import Title from "@arco-design/web-react/es/Typography/title"; import { formatUnixTime } from "@App/pkg/utils/utils"; @@ -6,12 +6,11 @@ import FileSystemParams from "@App/pages/components/FileSystemParams"; import { IconQuestionCircleFill } from "@arco-design/web-react/icon"; import { RefInputType } from "@arco-design/web-react/es/Input/interface"; import { useTranslation } from "react-i18next"; -import { FileSystemType } from "@Packages/filesystem/factory"; +import FileSystemFactory, { FileSystemType } from "@Packages/filesystem/factory"; +import { File, FileReader } from "@Packages/filesystem/filesystem"; import { message, systemConfig } from "@App/pages/store/global"; import { SynchronizeClient } from "@App/app/service/service_worker/client"; -import Cache from "@App/app/cache"; -import CacheKey from "@App/app/cache_key"; -import { v4 as uuidv4 } from "uuid"; +import { set } from "node_modules/yaml/dist/schema/yaml-1.1/set"; const synchronizeClient = new SynchronizeClient(message); @@ -22,16 +21,26 @@ function Tools() { const [fileSystemParams, setFilesystemParam] = useState<{ [key: string]: any; }>({}); + const [vscodeUrl, setVscodeUrl] = useState(""); + const [vscodeReconnect, setVscodeReconnect] = useState(false); const [backupFileList, setBackupFileList] = useState([]); const vscodeRef = useRef(null); const { t } = useTranslation(); useEffect(() => { // 获取配置 - systemConfig.getBackup().then((backup) => { + const loadConfig = async () => { + const [backup, vscodeUrl] = await Promise.all([ + systemConfig.getBackup(), + systemConfig.getVscodeUrl(), + systemConfig.getVscodeReconnect(), + ]); setFilesystemType(backup.filesystem); setFilesystemParam(backup.params[backup.filesystem] || {}); - }); + setVscodeUrl(vscodeUrl); + setVscodeReconnect(systemConfig.vscodeReconnect); + }; + loadConfig(); }, []); return ( @@ -55,7 +64,7 @@ function Tools() { loading={loading.local} onClick={async () => { setLoading((prev) => ({ ...prev, local: true })); - await synchronizeClient.backup(); + await synchronizeClient.export(); setLoading((prev) => ({ ...prev, local: false })); }} > @@ -74,21 +83,9 @@ function Tools() { if (!file) { return; } - const url = URL.createObjectURL(file); try { - const uuid = uuidv4(); - Cache.getInstance() - .set(CacheKey.importFile(uuid), { - filename: file.name, - url: url, - }) - .then(() => { - Message.success(t("select_import_script")!); - // 打开导入窗口 - chrome.tabs.create({ - url: `/src/import.html?uuid=${uuid}`, - }); - }); + await synchronizeClient.openImportWindow(file.name, file); + Message.success(t("select_import_script")!); } catch (e) { Message.error(`${t("import_error")}: ${e}`); } @@ -123,7 +120,7 @@ function Tools() { }); setLoading((prev) => ({ ...prev, cloud: true })); Message.info(t("preparing_backup")!); - syncCtrl + synchronizeClient .backupToCloud(fileSystemType, fileSystemParams) .then(() => { Message.success(t("backup_success")!); @@ -221,12 +218,8 @@ function Tools() { Message.error(`${t("pull_failed")}: ${e}`); return; } - const url = URL.createObjectURL(data); - setTimeout(() => { - URL.revokeObjectURL(url); - }, 60 * 100000); - syncCtrl - .openImportWindow(item.name, url) + synchronizeClient + .openImportWindow(item.name, data) .then(() => { Message.success(t("select_import_script")!); }) @@ -299,22 +292,24 @@ function Tools() { {t("vscode_url")} { - systemConfig.vscodeUrl = value; + setVscodeUrl(value); }} /> { - systemConfig.vscodeReconnect = checked; + setVscodeReconnect(checked); }} - defaultChecked={systemConfig.vscodeReconnect} > {t("auto_connect_vscode_service")}