Skip to content

Commit 2eed348

Browse files
committed
✨ 兼容tm的储存导入导出
1 parent 6960b6c commit 2eed348

File tree

9 files changed

+98
-28
lines changed

9 files changed

+98
-28
lines changed

Diff for: pkg/filesystem/filesystem.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface File {
1414
updatetime: number;
1515
}
1616

17-
type ReadType = "string" | "blob" | "base64";
17+
type ReadType = "string" | "blob";
1818
export interface FileReader {
1919
// 读取文件内容
2020
read(type?: ReadType): Promise<any>;

Diff for: src/app/service/script/controller.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default class ScriptController {
2323
}
2424

2525
// 安装或者更新脚本
26-
public upsert(script: Script) {
26+
public upsert(script: Script): Promise<{ id: number }> {
2727
return this.dispatchEvent("upsert", script);
2828
}
2929

Diff for: src/pages/import/App.tsx

+10-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
} from "@App/app/repo/scripts";
2727
import { Subscribe } from "@App/app/repo/subscribe";
2828
import ScriptController from "@App/app/service/script/controller";
29+
import ValueController from "@App/app/service/value/controller";
2930

3031
type ScriptData = ScriptBackupData & {
3132
script?: Script & { oldScript?: Script };
@@ -45,6 +46,7 @@ function App() {
4546
const [installNum, setInstallNum] = useState([0, 0]);
4647
const [loading, setLoading] = useState(true);
4748
const scriptCtrl = IoC.instance(ScriptController) as ScriptController;
49+
const valueCtrl = IoC.instance(ValueController) as ValueController;
4850
const syncCtrl = IoC.instance(SynchronizeController) as SynchronizeController;
4951
const url = new URL(window.location.href);
5052
const uuid = url.searchParams.get("uuid") || "";
@@ -118,14 +120,19 @@ function App() {
118120
onClick={async () => {
119121
setLoading(true);
120122
const result = scripts.map(async (item) => {
121-
let resp = true;
123+
const ok = true;
122124
if (item.install && !item.error) {
123-
resp = await scriptCtrl.upsert(item.script!);
125+
const resp = await scriptCtrl.upsert(item.script!);
126+
// 导入数据
127+
const { data } = item.storage;
128+
Object.keys(data).forEach((key) => {
129+
valueCtrl.setValue(resp.id, key, data[key]);
130+
});
124131
}
125132
setInstallNum((prev) => {
126133
return [prev[0] + 1, prev[1]];
127134
});
128-
return Promise.resolve(resp);
135+
return Promise.resolve(ok);
129136
});
130137
await Promise.all(result);
131138
setLoading(false);

Diff for: src/pkg/backup/backup.test.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -51,28 +51,30 @@ describe("backup", () => {
5151
resources: [
5252
{
5353
meta: { name: "test1", mimetype: "text/plain" },
54-
base64: "aGVsbG8gd29ybGQ=",
54+
base64: "data:text/plain;base64,aGVsbG8gd29ybGQ=",
5555
source: "hello world",
5656
},
5757
],
5858
requires: [
5959
{
6060
meta: { name: "test2", mimetype: "text/plain" },
61-
base64: "aGVsbG8gd29ybGQ=",
61+
base64: "data:text/plain;base64,aGVsbG8gd29ybGQ=",
6262
source: "hello world",
6363
},
6464
],
6565
requiresCss: [
6666
{
67-
meta: { name: "test3", mimetype: "text/plain" },
68-
base64: "aGVsbG8gd29ybGQ=",
67+
meta: { name: "test3", mimetype: "application/javascript" },
68+
base64: "data:application/javascript;base64,aGVsbG8gd29ybGQ=",
6969
source: "hello world",
7070
},
7171
],
7272
storage: {
7373
ts: 1,
7474
data: {
75-
data: 1,
75+
num: 1,
76+
str: "data",
77+
bool: false,
7678
},
7779
},
7880
},
@@ -99,7 +101,13 @@ describe("backup", () => {
99101
],
100102
} as unknown as BackupData;
101103
await new BackupExport(fs).export(data);
104+
expect(data.script[0].storage.data.num).toEqual("n1");
105+
expect(data.script[0].storage.data.str).toEqual("sdata");
106+
expect(data.script[0].storage.data.bool).toEqual("bfalse");
102107
const resp = await new BackupImport(fs).parse();
108+
data.script[0].storage.data.num = 1;
109+
data.script[0].storage.data.str = "data";
110+
data.script[0].storage.data.bool = false;
103111
expect(resp).toEqual(data);
104112
});
105113
});

Diff for: src/pkg/backup/export.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import FileSystem from "@Pkg/filesystem/filesystem";
22
import crypto from "crypto-js";
3-
import ResourceManager from "@App/app/service/resource/manager";
43
import { base64ToBlob } from "../utils/script";
4+
import { toStorageValueStr } from "../utils/utils";
55
import {
66
BackupData,
77
ResourceBackup,
@@ -39,9 +39,13 @@ export default class BackupExport {
3939
).write(JSON.stringify(script.options));
4040
// 写入脚本storage.json
4141
// 不想兼容tm的导出规则了,直接写入storage.json
42+
const storage = { ...script.storage };
43+
Object.keys(storage.data).forEach((key: string) => {
44+
storage.data[key] = toStorageValueStr(storage.data[key]);
45+
});
4246
await (
4347
await this.fs.create(`${name}.storage.json`)
44-
).write(JSON.stringify(script.storage));
48+
).write(JSON.stringify(storage));
4549
// 写入脚本资源文件
4650
await this.writeResource(name, script.resources, "resources");
4751
await this.writeResource(name, script.requires, "requires");
@@ -58,10 +62,7 @@ export default class BackupExport {
5862
const results: Promise<void>[] = resources.map(async (item) => {
5963
// md5是tm的导出规则
6064
const md5 = crypto.MD5(`${type}{val.meta.url}`).toString();
61-
if (
62-
item.meta.mimetype?.startsWith("text/") ||
63-
ResourceManager.textContentTypeMap.has(item.meta.mimetype || "")
64-
) {
65+
if (item.source) {
6566
await (
6667
await this.fs.create(`${name}.user.js-${md5}-${item.meta.name}`)
6768
).write(item.source!);

Diff for: src/pkg/backup/import.ts

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import LoggerCore from "@App/app/logger/core";
22
import Logger from "@App/app/logger/logger";
3-
import ResourceManager from "@App/app/service/resource/manager";
43
import FileSystem, { File } from "@Pkg/filesystem/filesystem";
4+
import { isText } from "../utils/istextorbinary";
5+
import { blobToBase64 } from "../utils/script";
6+
import { parseStorageValue } from "../utils/utils";
57
import {
68
BackupData,
79
ResourceBackup,
@@ -99,6 +101,9 @@ export default class BackupImport {
99101
const data = <ValueStorage>(
100102
JSON.parse(await (await this.fs.open(file)).read())
101103
);
104+
Object.keys(data.data).forEach((dataKey) => {
105+
data.data[dataKey] = parseStorageValue(data.data[dataKey]);
106+
});
102107
map.get(key)!.storage = data;
103108
return Promise.resolve(true);
104109
});
@@ -162,14 +167,21 @@ export default class BackupImport {
162167
return Promise.resolve(false);
163168
}
164169
const resource = map.get(info.key)![info.type][info.index];
165-
resource.base64 = await (await this.fs.open(file)).read("base64");
166-
if (
167-
resource.meta &&
168-
(resource.meta.mimetype?.startsWith("text/") ||
169-
ResourceManager.textContentTypeMap.has(resource.meta.mimetype || ""))
170-
) {
170+
resource.base64 = await blobToBase64(
171+
await (await this.fs.open(file)).read("blob")
172+
);
173+
if (resource.meta) {
171174
// 存在meta
172-
resource.source = await (await this.fs.open(file)).read();
175+
// 替换base64前缀
176+
if (resource.meta.mimetype) {
177+
resource.base64 = resource.base64.replace(
178+
/^data:.*?;base64,/,
179+
`data:${resource.meta.mimetype};base64,`
180+
);
181+
}
182+
if (isText(await (await this.fs.open(file)).read("blob"))) {
183+
resource.source = await (await this.fs.open(file)).read();
184+
}
173185
}
174186
return Promise.resolve(true);
175187
});

Diff for: src/pkg/backup/struct.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
/* eslint-disable camelcase */
2-
import { Metadata } from "@App/app/repo/scripts";
32

43
export type ResourceMeta = {
54
name: string;
@@ -10,12 +9,14 @@ export type ResourceMeta = {
109

1110
export type ResourceBackup = {
1211
meta: ResourceMeta;
12+
// text数据
1313
source?: string;
14+
// 二进制数据
1415
base64: string;
1516
};
1617

1718
export type ValueStorage = {
18-
data: { [key: string]: string };
19+
data: { [key: string]: any };
1920
ts: number;
2021
};
2122

Diff for: src/pkg/utils/script.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,10 @@ export function copySubscribe(sub: Subscribe, old: Subscribe): Subscribe {
151151
return ret;
152152
}
153153

154-
export function blobToBase64(blob: Blob): Promise<string | null> {
154+
export function blobToBase64(blob: Blob): Promise<string> {
155155
return new Promise((resolve) => {
156156
const reader = new FileReader();
157-
reader.onloadend = () => resolve(<string | null>reader.result);
157+
reader.onloadend = () => resolve(<string>reader.result);
158158
reader.readAsDataURL(blob);
159159
});
160160
}

Diff for: src/pkg/utils/utils.ts

+41
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,47 @@ export function valueType(val: any) {
124124
}
125125
}
126126

127+
export function toStorageValueStr(val: any): string {
128+
switch (typeof val) {
129+
case "string":
130+
return `s${val}`;
131+
case "number":
132+
return `n${val.toString()}`;
133+
case "boolean":
134+
return `b${val ? "true" : "false"}`;
135+
default:
136+
try {
137+
return `o${JSON.stringify(val)}`;
138+
} catch (e) {
139+
return "";
140+
}
141+
}
142+
}
143+
144+
export function parseStorageValue(str: string): any {
145+
if (str === "") {
146+
return undefined;
147+
}
148+
const t = str[0];
149+
const s = str.substring(1);
150+
switch (t) {
151+
case "b":
152+
return s === "true";
153+
case "n":
154+
return parseFloat(s);
155+
case "o":
156+
try {
157+
return JSON.parse(s);
158+
} catch (e) {
159+
return str;
160+
}
161+
case "s":
162+
return s;
163+
default:
164+
return str;
165+
}
166+
}
167+
127168
// 尝试重新链接和超时通知
128169
export function tryConnect(
129170
message: MessageInternal,

0 commit comments

Comments
 (0)