Skip to content

Commit d566afb

Browse files
committed
✨ 实现CAT_fileStorage #127
1 parent 6b9d02f commit d566afb

7 files changed

Lines changed: 234 additions & 49 deletions

File tree

example/cat_file_storage.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// ==UserScript==
2+
// @name cat file storage
3+
// @namespace https://bbs.tampermonkey.net.cn/
4+
// @version 0.1.0
5+
// @description 脚本同步储存空间操作
6+
// @author You
7+
// @match https://bbs.tampermonkey.net.cn/
8+
// @grant CAT_fileStorage
9+
// ==/UserScript==
10+
11+
CAT_fileStorage("upload", {
12+
path: "test.txt",
13+
data: new Blob(["Hello World"]),
14+
onload() {
15+
CAT_fileStorage("list", {
16+
onload(list) {
17+
console.log(list);
18+
list.forEach(value => {
19+
if (value.name === "test.txt") {
20+
CAT_fileStorage("download", {
21+
file: value,
22+
async onload(data) {
23+
console.log(await data.text());
24+
CAT_fileStorage("delete", {
25+
path: value.name,
26+
onload() {
27+
console.log('ok');
28+
}
29+
});
30+
}
31+
});
32+
}
33+
});
34+
}
35+
})
36+
}
37+
})

src/app/message/channel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export type ChannelHandler = (data: any) => void;
66

77
export type DisChannelHandler = () => void;
88

9-
export type ChannelCatch = (err: string) => void;
9+
export type ChannelCatch = (err: any) => void;
1010

1111
// 信道,作为长连接的载体,需要先使用channel方法建立信道
1212
export class Channel {

src/app/message/content.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export default class MessageContent
113113
}
114114

115115
// 特殊处理relatedTarget
116-
if (typeof detail.data.relatedTarget === "object") {
116+
if (detail.data && typeof detail.data.relatedTarget === "object") {
117117
// 先将relatedTarget转换成id发送过去
118118
const target = detail.data.relatedTarget;
119119
delete detail.data.relatedTarget;

src/pages/components/layout/MainLayout.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,7 @@ const MainLayout: React.FC<{
7979
setImportVisible(false);
8080
}}
8181
>
82-
<Input
83-
ref={importRef}
84-
defaultValue="https://scriptcat.org/scripts/code/336/%F0%9F%90%A4%E3%80%90%E8%B6%85%E6%98%9F%E7%BD%91%E8%AF%BE%E5%B0%8F%E5%8A%A9%E6%89%8B%E3%80%91%E3%80%90%E6%94%AF%E6%8C%81%E5%9B%BE%E7%89%87%E9%A2%98%E3%80%91%E8%A7%86%E9%A2%91-%E7%AB%A0%E8%8A%82%E6%B5%8B%E8%AF%95%7C%E8%87%AA%E5%8A%A8%E6%8C%82%E6%9C%BA%7C%E5%8F%AF%E5%A4%9A%E5%BC%80%E4%B8%8D%E5%8D%A0%E7%BD%91%E9%80%9F%7C%E9%98%B2%E6%B8%85%E8%BF%9B%E5%BA%A6%E3%80%90%E7%94%A8%E8%BF%87%E9%83%BD%E8%AF%B4%E5%A5%BD%E3%80%91.user.js"
85-
/>
82+
<Input ref={importRef} defaultValue="" />
8683
</Modal>
8784
<div className="flex row items-center">
8885
<img

src/runtime/background/gm_api.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { isFirefox } from "@App/pkg/utils/utils";
1313
import Hook from "@App/app/service/hook";
1414
import IoC from "@App/app/ioc";
1515
import { SystemConfig } from "@App/pkg/config/config";
16+
import FileSystemFactory from "@Pkg/filesystem/factory";
17+
import FileSystem from "@Pkg/filesystem/filesystem";
1618
import PermissionVerify, {
1719
ConfirmParam,
1820
IPermissionVerify,
@@ -755,4 +757,108 @@ export default class GMApi {
755757
active: true,
756758
});
757759
}
760+
761+
@PermissionVerify.API({
762+
confirm: (request: Request) => {
763+
return Promise.resolve({
764+
permission: "file_storage",
765+
permissionValue: "*",
766+
title: "脚本正在试图操作脚本同步储存空间",
767+
metadata: {
768+
脚本名称: request.script.name,
769+
},
770+
describe: `请您确认是否允许脚本进行此操作,允许后将允许脚本操作你的脚本同步储存空间,会在储存空间下创建一个app/${request.script.uuid}的目录供给脚本使用`,
771+
wildcard: true,
772+
permissionContent: "脚本",
773+
} as ConfirmParam);
774+
},
775+
alias: ["GM.xmlHttpRequest"],
776+
})
777+
// eslint-disable-next-line consistent-return
778+
async CAT_fileStorage(request: Request, channel: Channel) {
779+
const config = this.systemConfig.cloudSync;
780+
if (!config.enable) {
781+
return channel.throw({ code: 1, error: "is disable" });
782+
}
783+
const [action, details] = request.params;
784+
let fs: FileSystem;
785+
const baseDir = `ScriptCat/app/${request.script.uuid}`;
786+
try {
787+
fs = await FileSystemFactory.create(
788+
config.filesystem,
789+
config.params[config.filesystem]
790+
);
791+
await FileSystemFactory.mkdirAll(fs, baseDir);
792+
} catch (e: any) {
793+
return channel.throw({ code: 2, error: e.message });
794+
}
795+
switch (action) {
796+
case "list":
797+
fs.list(`${baseDir}/${details.path}`)
798+
.then((list) => {
799+
list.forEach((file) => {
800+
(<any>file).absPath = file.path;
801+
file.path = file.path.substring(baseDir.length + 1);
802+
});
803+
channel.send({ action: "onload", data: list });
804+
channel.disChannel();
805+
})
806+
.catch((e) => {
807+
channel.throw({ code: 3, error: e.message });
808+
});
809+
break;
810+
case "upload":
811+
// eslint-disable-next-line no-case-declarations
812+
const w = await fs.create(`${baseDir}/${details.path}`);
813+
w.write(await (await fetch(<string>details.data)).blob())
814+
.then(() => {
815+
channel.send({ action: "onload", data: true });
816+
channel.disChannel();
817+
})
818+
.catch((e) => {
819+
channel.throw({ code: 4, error: e.message });
820+
});
821+
break;
822+
case "download":
823+
// eslint-disable-next-line no-case-declarations, no-undef
824+
const info = <CATType.FileStorageFileInfo>details.file;
825+
fs = await fs.openDir(`${baseDir}${info.path}`);
826+
// eslint-disable-next-line no-case-declarations
827+
const r = await fs.open({
828+
fsid: (<any>info).fsid,
829+
name: info.name,
830+
path: `${baseDir}/${info.path}`,
831+
size: info.size,
832+
digest: info.digest,
833+
createtime: info.createtime,
834+
updatetime: info.updatetime,
835+
});
836+
r.read("blob")
837+
.then((blob) => {
838+
const url = URL.createObjectURL(blob);
839+
setTimeout(() => {
840+
URL.revokeObjectURL(url);
841+
}, 6000);
842+
channel.send({ action: "onload", data: url });
843+
channel.disChannel();
844+
})
845+
.catch((e) => {
846+
channel.throw({ code: 5, error: e.message });
847+
});
848+
break;
849+
case "delete":
850+
fs.delete(`${baseDir}/${details.path}`)
851+
.then(() => {
852+
channel.send({ action: "onload", data: true });
853+
channel.disChannel();
854+
})
855+
.catch((e) => {
856+
channel.throw({ code: 6, error: e.message });
857+
});
858+
break;
859+
default:
860+
channel.disChannel();
861+
break;
862+
}
863+
}
758864
}

src/runtime/content/gm_api.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,4 +720,42 @@ export default class GMApi {
720720
);
721721
return el;
722722
}
723+
724+
@GMContext.API({
725+
depend: ["CAT_fetchBlob", "CAT_createBlobUrl"],
726+
})
727+
async CAT_fileStorage(
728+
action: "list" | "download" | "upload" | "delete",
729+
details: any
730+
) {
731+
const sendDetails: { [key: string]: string } = {
732+
path: details.path || "",
733+
filename: details.filename,
734+
file: details.file,
735+
};
736+
if (action === "upload") {
737+
const url = await this.CAT_createBlobUrl(details.data);
738+
sendDetails.data = url;
739+
}
740+
const channel = this.connect(
741+
"CAT_fileStorage",
742+
[action, sendDetails],
743+
async (resp: any) => {
744+
if (action === "download") {
745+
// 读取blob
746+
const blob = await this.CAT_fetchBlob(resp.data);
747+
details.onload && details.onload(blob);
748+
} else {
749+
details.onload && details.onload(resp.data);
750+
}
751+
}
752+
);
753+
channel.setCatch((err) => {
754+
if (typeof err.code === "undefined") {
755+
details.onerror && details.onerror({ code: -1, message: err.message });
756+
return;
757+
}
758+
details.onerror && details.onerror(err);
759+
});
760+
}
723761
}

0 commit comments

Comments
 (0)