云同步配置
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 { MessageSend } from "@Packages/message/server";
|
||||||
import { ScriptMenu, ScriptMenuItem } from "./popup";
|
import { ScriptMenu, ScriptMenuItem } from "./popup";
|
||||||
import PermissionVerify, { ConfirmParam, UserConfirm } from "./permission_verify";
|
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 {
|
export class ServiceWorkerClient extends Client {
|
||||||
constructor(msg: MessageSend) {
|
constructor(msg: MessageSend) {
|
||||||
@ -156,11 +160,28 @@ export class SynchronizeClient extends Client {
|
|||||||
super(msg, "serviceWorker/synchronize");
|
super(msg, "serviceWorker/synchronize");
|
||||||
}
|
}
|
||||||
|
|
||||||
backup(uuids?: string[]) {
|
export(uuids?: string[]) {
|
||||||
return this.do("backup", uuids);
|
return this.do("export", uuids);
|
||||||
}
|
}
|
||||||
|
|
||||||
openImportWindow(filename: string, url: string) {
|
backupToCloud(type: FileSystemType, params: any) {
|
||||||
return this.do("openImportWindow", { filename, url });
|
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;
|
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 { ResourceService } from "./resource";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { createObjectURL } from "../offscreen/client";
|
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 {
|
export class SynchronizeService {
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
@ -169,8 +172,8 @@ export class SynchronizeService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求生成备份文件
|
// 请求导出文件
|
||||||
async requestBackup(uuids?: string[]) {
|
async requestExport(uuids?: string[]) {
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
const fs = new ZipFileSystem(zip);
|
const fs = new ZipFileSystem(zip);
|
||||||
await this.backup(fs, uuids);
|
await this.backup(fs, uuids);
|
||||||
@ -192,11 +195,49 @@ export class SynchronizeService {
|
|||||||
return Promise.resolve();
|
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() {
|
init() {
|
||||||
this.group.on("backup", this.requestBackup.bind(this));
|
this.group.on("export", this.requestExport.bind(this));
|
||||||
this.group.on("import", this.openImportWindow.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": "个时,自动隐藏",
|
"menu_expand_num_after": "个时,自动隐藏",
|
||||||
"script_name_cannot_be_set_to_empty": "脚本name不可以设置为空",
|
"script_name_cannot_be_set_to_empty": "脚本name不可以设置为空",
|
||||||
"eslint_config_format_error": "eslint配置格式错误",
|
"eslint_config_format_error": "eslint配置格式错误",
|
||||||
"export_success": "导出成功"
|
"export_success": "导出成功",
|
||||||
|
"get_backup_dir_url_failed": "获取备份目录地址失败"
|
||||||
}
|
}
|
@ -723,7 +723,7 @@ function ScriptList() {
|
|||||||
id: "export",
|
id: "export",
|
||||||
content: t("exporting"),
|
content: t("exporting"),
|
||||||
});
|
});
|
||||||
new SynchronizeClient(message).backup(uuids).then(() => {
|
new SynchronizeClient(message).export(uuids).then(() => {
|
||||||
Message.success({
|
Message.success({
|
||||||
id: "export",
|
id: "export",
|
||||||
content: t("export_success"),
|
content: t("export_success"),
|
||||||
|
@ -170,14 +170,15 @@ function Setting() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const params = { ...systemConfig.backup.params };
|
const cloudSync = await systemConfig.getCloudSync();
|
||||||
|
const params = { ...cloudSync.params };
|
||||||
params[fileSystemType] = fileSystemParams;
|
params[fileSystemType] = fileSystemParams;
|
||||||
systemConfig.cloudSync = {
|
systemConfig.setCloudSync({
|
||||||
enable: enableCloudSync,
|
enable: enableCloudSync || false,
|
||||||
syncDelete,
|
syncDelete: syncDelete || false,
|
||||||
filesystem: fileSystemType,
|
filesystem: fileSystemType,
|
||||||
params,
|
params,
|
||||||
};
|
});
|
||||||
Message.success(t("save_success")!);
|
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 { 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 Title from "@arco-design/web-react/es/Typography/title";
|
||||||
import { formatUnixTime } from "@App/pkg/utils/utils";
|
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 { IconQuestionCircleFill } from "@arco-design/web-react/icon";
|
||||||
import { RefInputType } from "@arco-design/web-react/es/Input/interface";
|
import { RefInputType } from "@arco-design/web-react/es/Input/interface";
|
||||||
import { useTranslation } from "react-i18next";
|
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 { message, systemConfig } from "@App/pages/store/global";
|
||||||
import { SynchronizeClient } from "@App/app/service/service_worker/client";
|
import { SynchronizeClient } from "@App/app/service/service_worker/client";
|
||||||
import Cache from "@App/app/cache";
|
import { set } from "node_modules/yaml/dist/schema/yaml-1.1/set";
|
||||||
import CacheKey from "@App/app/cache_key";
|
|
||||||
import { v4 as uuidv4 } from "uuid";
|
|
||||||
|
|
||||||
const synchronizeClient = new SynchronizeClient(message);
|
const synchronizeClient = new SynchronizeClient(message);
|
||||||
|
|
||||||
@ -22,16 +21,26 @@ function Tools() {
|
|||||||
const [fileSystemParams, setFilesystemParam] = useState<{
|
const [fileSystemParams, setFilesystemParam] = useState<{
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}>({});
|
}>({});
|
||||||
|
const [vscodeUrl, setVscodeUrl] = useState<string>("");
|
||||||
|
const [vscodeReconnect, setVscodeReconnect] = useState<boolean>(false);
|
||||||
const [backupFileList, setBackupFileList] = useState<File[]>([]);
|
const [backupFileList, setBackupFileList] = useState<File[]>([]);
|
||||||
const vscodeRef = useRef<RefInputType>(null);
|
const vscodeRef = useRef<RefInputType>(null);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 获取配置
|
// 获取配置
|
||||||
systemConfig.getBackup().then((backup) => {
|
const loadConfig = async () => {
|
||||||
|
const [backup, vscodeUrl] = await Promise.all([
|
||||||
|
systemConfig.getBackup(),
|
||||||
|
systemConfig.getVscodeUrl(),
|
||||||
|
systemConfig.getVscodeReconnect(),
|
||||||
|
]);
|
||||||
setFilesystemType(backup.filesystem);
|
setFilesystemType(backup.filesystem);
|
||||||
setFilesystemParam(backup.params[backup.filesystem] || {});
|
setFilesystemParam(backup.params[backup.filesystem] || {});
|
||||||
});
|
setVscodeUrl(vscodeUrl);
|
||||||
|
setVscodeReconnect(systemConfig.vscodeReconnect);
|
||||||
|
};
|
||||||
|
loadConfig();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -55,7 +64,7 @@ function Tools() {
|
|||||||
loading={loading.local}
|
loading={loading.local}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setLoading((prev) => ({ ...prev, local: true }));
|
setLoading((prev) => ({ ...prev, local: true }));
|
||||||
await synchronizeClient.backup();
|
await synchronizeClient.export();
|
||||||
setLoading((prev) => ({ ...prev, local: false }));
|
setLoading((prev) => ({ ...prev, local: false }));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -74,21 +83,9 @@ function Tools() {
|
|||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const url = URL.createObjectURL(file);
|
|
||||||
try {
|
try {
|
||||||
const uuid = uuidv4();
|
await synchronizeClient.openImportWindow(file.name, file);
|
||||||
Cache.getInstance()
|
Message.success(t("select_import_script")!);
|
||||||
.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}`,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Message.error(`${t("import_error")}: ${e}`);
|
Message.error(`${t("import_error")}: ${e}`);
|
||||||
}
|
}
|
||||||
@ -123,7 +120,7 @@ function Tools() {
|
|||||||
});
|
});
|
||||||
setLoading((prev) => ({ ...prev, cloud: true }));
|
setLoading((prev) => ({ ...prev, cloud: true }));
|
||||||
Message.info(t("preparing_backup")!);
|
Message.info(t("preparing_backup")!);
|
||||||
syncCtrl
|
synchronizeClient
|
||||||
.backupToCloud(fileSystemType, fileSystemParams)
|
.backupToCloud(fileSystemType, fileSystemParams)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
Message.success(t("backup_success")!);
|
Message.success(t("backup_success")!);
|
||||||
@ -221,12 +218,8 @@ function Tools() {
|
|||||||
Message.error(`${t("pull_failed")}: ${e}`);
|
Message.error(`${t("pull_failed")}: ${e}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const url = URL.createObjectURL(data);
|
synchronizeClient
|
||||||
setTimeout(() => {
|
.openImportWindow(item.name, data)
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
}, 60 * 100000);
|
|
||||||
syncCtrl
|
|
||||||
.openImportWindow(item.name, url)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
Message.success(t("select_import_script")!);
|
Message.success(t("select_import_script")!);
|
||||||
})
|
})
|
||||||
@ -299,22 +292,24 @@ function Tools() {
|
|||||||
<Title heading={6}>{t("vscode_url")}</Title>
|
<Title heading={6}>{t("vscode_url")}</Title>
|
||||||
<Input
|
<Input
|
||||||
ref={vscodeRef}
|
ref={vscodeRef}
|
||||||
defaultValue={systemConfig.vscodeUrl}
|
value={vscodeUrl}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
systemConfig.vscodeUrl = value;
|
setVscodeUrl(value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
checked={vscodeReconnect}
|
||||||
onChange={(checked) => {
|
onChange={(checked) => {
|
||||||
systemConfig.vscodeReconnect = checked;
|
setVscodeReconnect(checked);
|
||||||
}}
|
}}
|
||||||
defaultChecked={systemConfig.vscodeReconnect}
|
|
||||||
>
|
>
|
||||||
{t("auto_connect_vscode_service")}
|
{t("auto_connect_vscode_service")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
systemConfig.setVscodeUrl(vscodeUrl);
|
||||||
|
systemConfig.setVscodeReconnect(vscodeReconnect);
|
||||||
const ctrl = IoC.instance(SystemController) as SystemController;
|
const ctrl = IoC.instance(SystemController) as SystemController;
|
||||||
ctrl
|
ctrl
|
||||||
.connectVSCode()
|
.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 }> {
|
async getAll(): Promise<{ [key: string]: any }> {
|
||||||
const ret: { [key: string]: any } = {};
|
const ret: { [key: string]: any } = {};
|
||||||
const list = await this.storage.keys();
|
const list = await this.storage.keys();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user