云同步配置
This commit is contained in:
parent
07c4518cba
commit
185ba6e5cc
@ -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}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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": "获取备份目录地址失败"
|
||||
}
|
@ -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"),
|
||||
|
@ -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")!);
|
||||
}}
|
||||
>
|
||||
|
@ -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<string>("");
|
||||
const [vscodeReconnect, setVscodeReconnect] = useState<boolean>(false);
|
||||
const [backupFileList, setBackupFileList] = useState<File[]>([]);
|
||||
const vscodeRef = useRef<RefInputType>(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(() => {
|
||||
await synchronizeClient.openImportWindow(file.name, file);
|
||||
Message.success(t("select_import_script")!);
|
||||
// 打开导入窗口
|
||||
chrome.tabs.create({
|
||||
url: `/src/import.html?uuid=${uuid}`,
|
||||
});
|
||||
});
|
||||
} 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() {
|
||||
<Title heading={6}>{t("vscode_url")}</Title>
|
||||
<Input
|
||||
ref={vscodeRef}
|
||||
defaultValue={systemConfig.vscodeUrl}
|
||||
value={vscodeUrl}
|
||||
onChange={(value) => {
|
||||
systemConfig.vscodeUrl = value;
|
||||
setVscodeUrl(value);
|
||||
}}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={vscodeReconnect}
|
||||
onChange={(checked) => {
|
||||
systemConfig.vscodeReconnect = checked;
|
||||
setVscodeReconnect(checked);
|
||||
}}
|
||||
defaultChecked={systemConfig.vscodeReconnect}
|
||||
>
|
||||
{t("auto_connect_vscode_service")}
|
||||
</Checkbox>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
systemConfig.setVscodeUrl(vscodeUrl);
|
||||
systemConfig.setVscodeReconnect(vscodeReconnect);
|
||||
const ctrl = IoC.instance(SystemController) as SystemController;
|
||||
ctrl
|
||||
.connectVSCode()
|
||||
|
@ -33,6 +33,13 @@ export class SystemConfig {
|
||||
});
|
||||
}
|
||||
|
||||
addListener(key: string, callback: (value: any) => void) {
|
||||
this.mq.subscribe(key, (msg) => {
|
||||
const { value } = msg;
|
||||
callback(value);
|
||||
});
|
||||
}
|
||||
|
||||
async getAll(): Promise<{ [key: string]: any }> {
|
||||
const ret: { [key: string]: any } = {};
|
||||
const list = await this.storage.keys();
|
||||
|
Loading…
x
Reference in New Issue
Block a user