clickgo 3.0.0-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +75 -0
- package/dist/app/demo/config.json +106 -0
- package/dist/app/demo/form/control/block/block.css +1 -0
- package/dist/app/demo/form/control/block/block.scss +17 -0
- package/dist/app/demo/form/control/block/block.xml +7 -0
- package/dist/app/demo/form/control/button/button.css +1 -0
- package/dist/app/demo/form/control/button/button.js +27 -0
- package/dist/app/demo/form/control/button/button.scss +18 -0
- package/dist/app/demo/form/control/button/button.xml +126 -0
- package/dist/app/demo/form/control/check/check.js +12 -0
- package/dist/app/demo/form/control/check/check.xml +13 -0
- package/dist/app/demo/form/control/dialog/dialog.js +8 -0
- package/dist/app/demo/form/control/dialog/dialog.xml +26 -0
- package/dist/app/demo/form/control/file/file.js +23 -0
- package/dist/app/demo/form/control/file/file.xml +25 -0
- package/dist/app/demo/form/control/form/form.css +1 -0
- package/dist/app/demo/form/control/form/form.js +38 -0
- package/dist/app/demo/form/control/form/form.scss +9 -0
- package/dist/app/demo/form/control/form/form.xml +28 -0
- package/dist/app/demo/form/control/greatview/greatview.css +1 -0
- package/dist/app/demo/form/control/greatview/greatview.js +92 -0
- package/dist/app/demo/form/control/greatview/greatview.scss +22 -0
- package/dist/app/demo/form/control/greatview/greatview.xml +89 -0
- package/dist/app/demo/form/control/img/img.xml +16 -0
- package/dist/app/demo/form/control/label/label.xml +11 -0
- package/dist/app/demo/form/control/list/list.css +1 -0
- package/dist/app/demo/form/control/list/list.js +194 -0
- package/dist/app/demo/form/control/list/list.scss +7 -0
- package/dist/app/demo/form/control/list/list.xml +91 -0
- package/dist/app/demo/form/control/loading/loading.xml +8 -0
- package/dist/app/demo/form/control/marquee/marquee.js +30 -0
- package/dist/app/demo/form/control/marquee/marquee.xml +36 -0
- package/dist/app/demo/form/control/menu/menu.js +8 -0
- package/dist/app/demo/form/control/menu/menu.xml +122 -0
- package/dist/app/demo/form/control/monaco/monaco.js +113 -0
- package/dist/app/demo/form/control/monaco/monaco.xml +27 -0
- package/dist/app/demo/form/control/overflow/overflow.css +1 -0
- package/dist/app/demo/form/control/overflow/overflow.js +70 -0
- package/dist/app/demo/form/control/overflow/overflow.scss +18 -0
- package/dist/app/demo/form/control/overflow/overflow.xml +98 -0
- package/dist/app/demo/form/control/property/property.js +129 -0
- package/dist/app/demo/form/control/property/property.xml +6 -0
- package/dist/app/demo/form/control/radio/radio.js +7 -0
- package/dist/app/demo/form/control/radio/radio.xml +12 -0
- package/dist/app/demo/form/control/scroll/scroll.js +14 -0
- package/dist/app/demo/form/control/scroll/scroll.xml +35 -0
- package/dist/app/demo/form/control/select/select.js +91 -0
- package/dist/app/demo/form/control/select/select.xml +74 -0
- package/dist/app/demo/form/control/tab/tab.js +75 -0
- package/dist/app/demo/form/control/tab/tab.xml +22 -0
- package/dist/app/demo/form/control/text/text.js +53 -0
- package/dist/app/demo/form/control/text/text.xml +37 -0
- package/dist/app/demo/form/control/view/view.css +1 -0
- package/dist/app/demo/form/control/view/view.js +73 -0
- package/dist/app/demo/form/control/view/view.scss +18 -0
- package/dist/app/demo/form/control/view/view.xml +94 -0
- package/dist/app/demo/form/event/form/form.css +1 -0
- package/dist/app/demo/form/event/form/form.js +129 -0
- package/dist/app/demo/form/event/form/form.scss +24 -0
- package/dist/app/demo/form/event/form/form.xml +16 -0
- package/dist/app/demo/form/event/screen/screen.js +51 -0
- package/dist/app/demo/form/event/screen/screen.xml +9 -0
- package/dist/app/demo/form/event/task/task.js +78 -0
- package/dist/app/demo/form/event/task/task.xml +20 -0
- package/dist/app/demo/form/main.css +1 -0
- package/dist/app/demo/form/main.js +25 -0
- package/dist/app/demo/form/main.scss +9 -0
- package/dist/app/demo/form/main.xml +49 -0
- package/dist/app/demo/form/method/core/core.js +25 -0
- package/dist/app/demo/form/method/core/core.xml +7 -0
- package/dist/app/demo/form/method/dom/dom.css +1 -0
- package/dist/app/demo/form/method/dom/dom.js +163 -0
- package/dist/app/demo/form/method/dom/dom.scss +10 -0
- package/dist/app/demo/form/method/dom/dom.xml +55 -0
- package/dist/app/demo/form/method/form/form.css +1 -0
- package/dist/app/demo/form/method/form/form.js +217 -0
- package/dist/app/demo/form/method/form/form.scss +3 -0
- package/dist/app/demo/form/method/form/form.xml +56 -0
- package/dist/app/demo/form/method/form/test.xml +5 -0
- package/dist/app/demo/form/method/fs/fs.js +88 -0
- package/dist/app/demo/form/method/fs/fs.xml +8 -0
- package/dist/app/demo/form/method/fs/text.js +15 -0
- package/dist/app/demo/form/method/fs/text.xml +3 -0
- package/dist/app/demo/form/method/task/locale1.json +3 -0
- package/dist/app/demo/form/method/task/locale2.json +3 -0
- package/dist/app/demo/form/method/task/task.js +153 -0
- package/dist/app/demo/form/method/task/task.xml +57 -0
- package/dist/app/demo/form/method/theme/theme.js +74 -0
- package/dist/app/demo/form/method/theme/theme.xml +9 -0
- package/dist/app/demo/form/method/tool/tool.js +64 -0
- package/dist/app/demo/form/method/tool/tool.xml +26 -0
- package/dist/app/demo/form/method/zip/zip.js +99 -0
- package/dist/app/demo/form/method/zip/zip.xml +12 -0
- package/dist/app/demo/global.css +3 -0
- package/dist/app/demo/res/icon.svg +1 -0
- package/dist/app/demo/res/img.jpg +0 -0
- package/dist/app/demo/res/r-1.svg +1 -0
- package/dist/app/demo/res/r-2.svg +1 -0
- package/dist/app/demo/res/sql.svg +1 -0
- package/dist/app/demo/res/txt.svg +1 -0
- package/dist/app/demo/res/zip.svg +1 -0
- package/dist/app/task/config.json +29 -0
- package/dist/app/task/form/bar/bar.js +299 -0
- package/dist/app/task/form/bar/bar.xml +47 -0
- package/dist/app/task/form/desktop/desktop.xml +1 -0
- package/dist/app/task/locale/en.json +11 -0
- package/dist/app/task/locale/ja.json +11 -0
- package/dist/app/task/locale/sc.json +11 -0
- package/dist/app/task/locale/tc.json +11 -0
- package/dist/clickgo.js +41 -0
- package/dist/clickgo.ts +51 -0
- package/dist/control/common.cgc +0 -0
- package/dist/control/form.cgc +0 -0
- package/dist/control/monaco.cgc +0 -0
- package/dist/control/property.cgc +0 -0
- package/dist/control/task.cgc +0 -0
- package/dist/global.css +1 -0
- package/dist/icon.png +0 -0
- package/dist/index.js +88 -0
- package/dist/index.ts +92 -0
- package/dist/lib/control.js +365 -0
- package/dist/lib/control.ts +428 -0
- package/dist/lib/core.js +668 -0
- package/dist/lib/core.ts +732 -0
- package/dist/lib/dom.js +1471 -0
- package/dist/lib/dom.ts +1785 -0
- package/dist/lib/form.js +2101 -0
- package/dist/lib/form.ts +2496 -0
- package/dist/lib/fs.js +849 -0
- package/dist/lib/fs.ts +995 -0
- package/dist/lib/native.js +138 -0
- package/dist/lib/native.ts +219 -0
- package/dist/lib/task.js +686 -0
- package/dist/lib/task.ts +842 -0
- package/dist/lib/theme.js +159 -0
- package/dist/lib/theme.ts +196 -0
- package/dist/lib/tool.js +501 -0
- package/dist/lib/tool.ts +620 -0
- package/dist/lib/zip.js +352 -0
- package/dist/lib/zip.ts +434 -0
- package/dist/theme/familiar.cgt +0 -0
- package/package.json +27 -0
- package/types/dev.d.ts +30 -0
- package/types/index.d.ts +673 -0
package/dist/lib/fs.ts
ADDED
|
@@ -0,0 +1,995 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /clickgo/ ClickGo 系统文件目录,永远只读
|
|
3
|
+
* /storage/ 本地模式可用,用于读取本地用户文件
|
|
4
|
+
* /mounted/ 挂载目录,可挂载第三方,如 /mounted/clouddrive/
|
|
5
|
+
* /package/ 包内文件存在时可用,用于读取包内文件,永远只读
|
|
6
|
+
* /current/ app 运行起来的 task 的当前路径链接,这是个软连接,实际映射到实际的运行目录
|
|
7
|
+
*/
|
|
8
|
+
import * as types from '../../types';
|
|
9
|
+
import * as tool from './tool';
|
|
10
|
+
|
|
11
|
+
const clickgoFiles = ['/app/', '/app/demo/', '/app/demo/config.json', '/app/demo/form/', '/app/demo/form/control/', '/app/demo/form/control/block/', '/app/demo/form/control/block/block.css', '/app/demo/form/control/block/block.xml', '/app/demo/form/control/button/', '/app/demo/form/control/button/button.css', '/app/demo/form/control/button/button.js', '/app/demo/form/control/button/button.xml', '/app/demo/form/control/check/', '/app/demo/form/control/check/check.js', '/app/demo/form/control/check/check.xml', '/app/demo/form/control/dialog/', '/app/demo/form/control/dialog/dialog.js', '/app/demo/form/control/dialog/dialog.xml', '/app/demo/form/control/file/', '/app/demo/form/control/file/file.js', '/app/demo/form/control/file/file.xml', '/app/demo/form/control/form/', '/app/demo/form/control/form/form.css', '/app/demo/form/control/form/form.js', '/app/demo/form/control/form/form.xml', '/app/demo/form/control/greatview/', '/app/demo/form/control/greatview/greatview.css', '/app/demo/form/control/greatview/greatview.js', '/app/demo/form/control/greatview/greatview.xml', '/app/demo/form/control/img/', '/app/demo/form/control/img/img.xml', '/app/demo/form/control/label/', '/app/demo/form/control/label/label.xml', '/app/demo/form/control/list/', '/app/demo/form/control/list/list.css', '/app/demo/form/control/list/list.js', '/app/demo/form/control/list/list.xml', '/app/demo/form/control/loading/', '/app/demo/form/control/loading/loading.xml', '/app/demo/form/control/marquee/', '/app/demo/form/control/marquee/marquee.js', '/app/demo/form/control/marquee/marquee.xml', '/app/demo/form/control/menu/', '/app/demo/form/control/menu/menu.js', '/app/demo/form/control/menu/menu.xml', '/app/demo/form/control/monaco/', '/app/demo/form/control/monaco/monaco.js', '/app/demo/form/control/monaco/monaco.xml', '/app/demo/form/control/overflow/', '/app/demo/form/control/overflow/overflow.css', '/app/demo/form/control/overflow/overflow.js', '/app/demo/form/control/overflow/overflow.xml', '/app/demo/form/control/property/', '/app/demo/form/control/property/property.js', '/app/demo/form/control/property/property.xml', '/app/demo/form/control/radio/', '/app/demo/form/control/radio/radio.js', '/app/demo/form/control/radio/radio.xml', '/app/demo/form/control/scroll/', '/app/demo/form/control/scroll/scroll.js', '/app/demo/form/control/scroll/scroll.xml', '/app/demo/form/control/select/', '/app/demo/form/control/select/select.js', '/app/demo/form/control/select/select.xml', '/app/demo/form/control/tab/', '/app/demo/form/control/tab/tab.js', '/app/demo/form/control/tab/tab.xml', '/app/demo/form/control/text/', '/app/demo/form/control/text/text.js', '/app/demo/form/control/text/text.xml', '/app/demo/form/control/view/', '/app/demo/form/control/view/view.css', '/app/demo/form/control/view/view.js', '/app/demo/form/control/view/view.xml', '/app/demo/form/event/', '/app/demo/form/event/form/', '/app/demo/form/event/form/form.css', '/app/demo/form/event/form/form.js', '/app/demo/form/event/form/form.xml', '/app/demo/form/event/screen/', '/app/demo/form/event/screen/screen.js', '/app/demo/form/event/screen/screen.xml', '/app/demo/form/event/task/', '/app/demo/form/event/task/task.js', '/app/demo/form/event/task/task.xml', '/app/demo/form/main.css', '/app/demo/form/main.js', '/app/demo/form/main.xml', '/app/demo/form/method/', '/app/demo/form/method/core/', '/app/demo/form/method/core/core.js', '/app/demo/form/method/core/core.xml', '/app/demo/form/method/dom/', '/app/demo/form/method/dom/dom.css', '/app/demo/form/method/dom/dom.js', '/app/demo/form/method/dom/dom.xml', '/app/demo/form/method/form/', '/app/demo/form/method/form/form.css', '/app/demo/form/method/form/form.js', '/app/demo/form/method/form/form.xml', '/app/demo/form/method/form/test.xml', '/app/demo/form/method/fs/', '/app/demo/form/method/fs/fs.js', '/app/demo/form/method/fs/fs.xml', '/app/demo/form/method/fs/text.js', '/app/demo/form/method/fs/text.xml', '/app/demo/form/method/task/', '/app/demo/form/method/task/locale1.json', '/app/demo/form/method/task/locale2.json', '/app/demo/form/method/task/task.js', '/app/demo/form/method/task/task.xml', '/app/demo/form/method/theme/', '/app/demo/form/method/theme/theme.js', '/app/demo/form/method/theme/theme.xml', '/app/demo/form/method/tool/', '/app/demo/form/method/tool/tool.js', '/app/demo/form/method/tool/tool.xml', '/app/demo/form/method/zip/', '/app/demo/form/method/zip/zip.js', '/app/demo/form/method/zip/zip.xml', '/app/demo/global.css', '/app/demo/res/', '/app/demo/res/icon.svg', '/app/demo/res/img.jpg', '/app/demo/res/r-1.svg', '/app/demo/res/r-2.svg', '/app/demo/res/sql.svg', '/app/demo/res/txt.svg', '/app/demo/res/zip.svg', '/app/task/', '/app/task/config.json', '/app/task/form/', '/app/task/form/bar/', '/app/task/form/bar/bar.js', '/app/task/form/bar/bar.xml', '/app/task/form/desktop/', '/app/task/form/desktop/desktop.xml', '/app/task/locale/', '/app/task/locale/en.json', '/app/task/locale/ja.json', '/app/task/locale/sc.json', '/app/task/locale/tc.json', '/clickgo.js', '/clickgo.ts', '/control/', '/control/common.cgc', '/control/form.cgc', '/control/monaco.cgc', '/control/property.cgc', '/control/task.cgc', '/global.css', '/icon.png', '/index.js', '/index.ts', '/lib/', '/lib/control.js', '/lib/control.ts', '/lib/core.js', '/lib/core.ts', '/lib/dom.js', '/lib/dom.ts', '/lib/form.js', '/lib/form.ts', '/lib/fs.js', '/lib/fs.ts', '/lib/native.js', '/lib/native.ts', '/lib/task.js', '/lib/task.ts', '/lib/theme.js', '/lib/theme.ts', '/lib/tool.js', '/lib/tool.ts', '/lib/zip.js', '/lib/zip.ts', '/theme/', '/theme/familiar.cgt'];
|
|
12
|
+
|
|
13
|
+
export async function getContent(path: string, options?: {
|
|
14
|
+
'start'?: number;
|
|
15
|
+
'end'?: number;
|
|
16
|
+
'files'?: Record<string, Blob | string>;
|
|
17
|
+
'current'?: string;
|
|
18
|
+
'progress'?: (loaded: number, total: number) => void | Promise<void>;
|
|
19
|
+
}): Promise<string | Blob | null>;
|
|
20
|
+
export async function getContent(path: string, options: BufferEncoding | {
|
|
21
|
+
'encoding': BufferEncoding;
|
|
22
|
+
'start'?: number;
|
|
23
|
+
'end'?: number;
|
|
24
|
+
'files'?: Record<string, Blob | string>;
|
|
25
|
+
'current'?: string;
|
|
26
|
+
'progress'?: (loaded: number, total: number) => void | Promise<void>;
|
|
27
|
+
}): Promise<string | null>;
|
|
28
|
+
/**
|
|
29
|
+
* --- 读取完整文件或一段 ---
|
|
30
|
+
* @param path 文件路径
|
|
31
|
+
* @param options 编码或选项
|
|
32
|
+
*/
|
|
33
|
+
export async function getContent(path: string, options?: BufferEncoding | {
|
|
34
|
+
'encoding'?: BufferEncoding;
|
|
35
|
+
'start'?: number;
|
|
36
|
+
'end'?: number;
|
|
37
|
+
'files'?: Record<string, Blob | string>;
|
|
38
|
+
'current'?: string;
|
|
39
|
+
'progress'?: (loaded: number, total: number) => void | Promise<void>;
|
|
40
|
+
}): Promise<Blob | string | null> {
|
|
41
|
+
path = tool.urlResolve('/', path);
|
|
42
|
+
const fpath = path.slice(8);
|
|
43
|
+
if (typeof options === 'string') {
|
|
44
|
+
options = {
|
|
45
|
+
'encoding': options
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
else if (!options) {
|
|
49
|
+
options = {};
|
|
50
|
+
}
|
|
51
|
+
const encoding = options.encoding;
|
|
52
|
+
const start = options.start;
|
|
53
|
+
const end = options.end;
|
|
54
|
+
if (path.startsWith('/clickgo/') || path.startsWith('http:') || path.startsWith('https:')) {
|
|
55
|
+
let ourl: string = '';
|
|
56
|
+
if (path.startsWith('/clickgo/')) {
|
|
57
|
+
if (!clickgoFiles.includes(fpath)) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
ourl = tool.urlResolve(__dirname, './').slice(0, -1) + fpath;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
ourl = path;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const rand = Math.random().toString();
|
|
67
|
+
let blob: Blob | null = null;
|
|
68
|
+
const headers: Record<string, string> = {};
|
|
69
|
+
if (start || end) {
|
|
70
|
+
headers['range'] = `bytes=${start === undefined ? '0' : start}-${end === undefined ? '' : end}`;
|
|
71
|
+
}
|
|
72
|
+
if (options.progress) {
|
|
73
|
+
blob = await tool.request(ourl + '?' + rand, {
|
|
74
|
+
'headers': headers,
|
|
75
|
+
'progress': options.progress,
|
|
76
|
+
'responseType': 'blob'
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
blob = await (await fetch(ourl + '?' + rand, {
|
|
81
|
+
'headers': headers
|
|
82
|
+
})).blob();
|
|
83
|
+
}
|
|
84
|
+
if (!blob) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (!encoding) {
|
|
88
|
+
return blob;
|
|
89
|
+
}
|
|
90
|
+
return await new Promise(function(resolve) {
|
|
91
|
+
const fr = new FileReader();
|
|
92
|
+
fr.addEventListener('load', function() {
|
|
93
|
+
resolve(fr.result as string | null);
|
|
94
|
+
});
|
|
95
|
+
fr.readAsText(blob!, encoding);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if (path.startsWith('/storage/')) {
|
|
103
|
+
// --- TODO ---
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
else if (path.startsWith('/mounted/')) {
|
|
107
|
+
// --- TODO ---
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
else if (path.startsWith('/package/')) {
|
|
111
|
+
if (!options.files) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const file = options.files[fpath];
|
|
115
|
+
if (!file) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
if (typeof file === 'string') {
|
|
119
|
+
// --- 是文本直接返回 ---
|
|
120
|
+
return file;
|
|
121
|
+
}
|
|
122
|
+
if (!options.encoding) {
|
|
123
|
+
// --- 没有编码则返回 blob ---
|
|
124
|
+
return file;
|
|
125
|
+
}
|
|
126
|
+
const encoding = options.encoding;
|
|
127
|
+
return new Promise(function(resolve) {
|
|
128
|
+
const fr = new FileReader();
|
|
129
|
+
fr.addEventListener('load', function() {
|
|
130
|
+
resolve(fr.result as string | null);
|
|
131
|
+
});
|
|
132
|
+
fr.readAsText(file, encoding);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else if (path.startsWith('/current/')) {
|
|
136
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
options.current = options.current.slice(0, -1);
|
|
140
|
+
return getContent(options.current + fpath, options);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* --- 写入文件内容 ---
|
|
149
|
+
* @param path 文件路径
|
|
150
|
+
* @param data 要写入的内容
|
|
151
|
+
* @param options 选项
|
|
152
|
+
*/
|
|
153
|
+
export async function putContent(path: string, data: string | Buffer, options: {
|
|
154
|
+
'encoding'?: BufferEncoding | null;
|
|
155
|
+
'mode'?: string | number;
|
|
156
|
+
'flag'?: string | number;
|
|
157
|
+
'current'?: string;
|
|
158
|
+
} = {}): Promise<boolean> {
|
|
159
|
+
path = tool.urlResolve('/', path);
|
|
160
|
+
const fpath = path.slice(8);
|
|
161
|
+
if (path.startsWith('/clickgo/')) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
else if (path.startsWith('/storage/')) {
|
|
165
|
+
// --- TODO ---
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
else if (path.startsWith('/mounted/')) {
|
|
169
|
+
// --- TODO ---
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
else if (path.startsWith('/package/')) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
else if (path.startsWith('/current/')) {
|
|
176
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
options.current = options.current.slice(0, -1);
|
|
180
|
+
return putContent(options.current + fpath, data, options);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* --- 读取链接的 target ---
|
|
189
|
+
* @param path 要读取的路径
|
|
190
|
+
* @param encoding 编码
|
|
191
|
+
*/
|
|
192
|
+
export async function readLink(path: string, options?: BufferEncoding | {
|
|
193
|
+
'encoding'?: BufferEncoding;
|
|
194
|
+
'current'?: string;
|
|
195
|
+
}): Promise<string | null> {
|
|
196
|
+
path = tool.urlResolve('/', path);
|
|
197
|
+
const fpath = path.slice(8);
|
|
198
|
+
if (typeof options === 'string') {
|
|
199
|
+
options = {
|
|
200
|
+
'encoding': options
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
else if (!options) {
|
|
204
|
+
options = {};
|
|
205
|
+
}
|
|
206
|
+
if (path.startsWith('/clickgo/')) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
else if (path.startsWith('/storage/')) {
|
|
210
|
+
// --- TODO ---
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
else if (path.startsWith('/mounted/')) {
|
|
214
|
+
// --- TODO ---
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
else if (path.startsWith('/package/')) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
else if (path.startsWith('/current/')) {
|
|
221
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
return options.current;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* --- 把源文件创建一个 link ---
|
|
233
|
+
* @param filePath 源文件
|
|
234
|
+
* @param linkPath 连接路径
|
|
235
|
+
* @param type 仅 Windows,类型,默认 file
|
|
236
|
+
*/
|
|
237
|
+
export async function symlink(filePath: string, linkPath: string, options: {
|
|
238
|
+
'type'?: 'dir' | 'file' | 'junction';
|
|
239
|
+
'current'?: string;
|
|
240
|
+
} = {}): Promise<boolean> {
|
|
241
|
+
filePath = tool.urlResolve('/', filePath);
|
|
242
|
+
linkPath = tool.urlResolve('/', linkPath);
|
|
243
|
+
if (filePath.startsWith('/clickgo/')) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
else if (filePath.startsWith('/storage/')) {
|
|
247
|
+
// --- TODO ---
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
else if (filePath.startsWith('/mounted/')) {
|
|
251
|
+
// --- TODO ---
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
else if (filePath.startsWith('/package/')) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
else if (filePath.startsWith('/current/')) {
|
|
258
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
options.current = options.current.slice(0, -1);
|
|
262
|
+
if (linkPath.startsWith('/current/')) {
|
|
263
|
+
linkPath = options.current + linkPath.slice(8);
|
|
264
|
+
}
|
|
265
|
+
return symlink(options.current + filePath.slice(8), linkPath, options);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* --- 删除一个文件 ---
|
|
274
|
+
* @param path 要删除的文件路径
|
|
275
|
+
*/
|
|
276
|
+
export async function unlink(path: string, options: {
|
|
277
|
+
'current'?: string;
|
|
278
|
+
} = {}): Promise<boolean> {
|
|
279
|
+
path = tool.urlResolve('/', path);
|
|
280
|
+
const fpath = path.slice(8);
|
|
281
|
+
if (path.startsWith('/clickgo/')) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
else if (path.startsWith('/storage/')) {
|
|
285
|
+
// --- TODO ---
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
else if (path.startsWith('/mounted/')) {
|
|
289
|
+
// --- TODO ---
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
else if (path.startsWith('/package/')) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
else if (path.startsWith('/current/')) {
|
|
296
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
options.current = options.current.slice(0, -1);
|
|
300
|
+
return unlink(options.current + fpath, options);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function getClickGoStats(path: string): Promise<types.IStats | null> {
|
|
308
|
+
if (path.endsWith('/')) {
|
|
309
|
+
// --- 文件夹 ---
|
|
310
|
+
const date = new Date();
|
|
311
|
+
const ms = date.getTime();
|
|
312
|
+
return {
|
|
313
|
+
isFile: function() {
|
|
314
|
+
return false;
|
|
315
|
+
},
|
|
316
|
+
isDirectory: function() {
|
|
317
|
+
return true;
|
|
318
|
+
},
|
|
319
|
+
isSymbolicLink: function() {
|
|
320
|
+
return false;
|
|
321
|
+
},
|
|
322
|
+
'size': 0,
|
|
323
|
+
'blksize': 0,
|
|
324
|
+
'atimeMs': ms,
|
|
325
|
+
'mtimeMs': ms,
|
|
326
|
+
'ctimeMs': ms,
|
|
327
|
+
'birthtimeMs': ms,
|
|
328
|
+
'atime': date,
|
|
329
|
+
'mtime': date,
|
|
330
|
+
'ctime': date,
|
|
331
|
+
'birthtime': date
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
// --- 文件 ---
|
|
336
|
+
try {
|
|
337
|
+
const res = await fetch(tool.urlResolve(__dirname, './').slice(0, -1) + path + '?' + Math.random().toString(), {
|
|
338
|
+
'headers': {
|
|
339
|
+
'range': `bytes=0-1`
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
const hdate = res.headers.get('date');
|
|
343
|
+
const hmdate = res.headers.get('last-modified');
|
|
344
|
+
const hlength = res.headers.get('content-length');
|
|
345
|
+
let date = new Date();
|
|
346
|
+
let mdate = date;
|
|
347
|
+
if (hdate) {
|
|
348
|
+
date = new Date(hdate);
|
|
349
|
+
}
|
|
350
|
+
if (hmdate) {
|
|
351
|
+
mdate = new Date(hmdate);
|
|
352
|
+
}
|
|
353
|
+
const ms = date.getTime();
|
|
354
|
+
const mms = mdate.getTime();
|
|
355
|
+
return {
|
|
356
|
+
isFile: function() {
|
|
357
|
+
return true;
|
|
358
|
+
},
|
|
359
|
+
isDirectory: function() {
|
|
360
|
+
return false;
|
|
361
|
+
},
|
|
362
|
+
isSymbolicLink: function() {
|
|
363
|
+
return false;
|
|
364
|
+
},
|
|
365
|
+
'size': hlength ? parseInt(hlength) : 0,
|
|
366
|
+
'blksize': hlength ? parseInt(hlength) : 0,
|
|
367
|
+
'atimeMs': ms,
|
|
368
|
+
'mtimeMs': mms,
|
|
369
|
+
'ctimeMs': mms,
|
|
370
|
+
'birthtimeMs': ms,
|
|
371
|
+
'atime': date,
|
|
372
|
+
'mtime': mdate,
|
|
373
|
+
'ctime': mdate,
|
|
374
|
+
'birthtime': date
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* --- 获取对象是否存在,存在则返回 stats 对象,否则返回 null ---
|
|
385
|
+
* @param path 对象路径
|
|
386
|
+
* @param options 选项
|
|
387
|
+
*/
|
|
388
|
+
export async function stats(path: string, options: {
|
|
389
|
+
'files'?: Record<string, Blob | string>;
|
|
390
|
+
'current'?: string;
|
|
391
|
+
} = {}): Promise<types.IStats | null> {
|
|
392
|
+
path = tool.urlResolve('/', path);
|
|
393
|
+
let fpath = path.slice(8);
|
|
394
|
+
if (path.startsWith('/clickgo/')) {
|
|
395
|
+
if (!clickgoFiles.includes(fpath)) {
|
|
396
|
+
if (!clickgoFiles.includes(fpath + '/')) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
fpath += '/';
|
|
400
|
+
}
|
|
401
|
+
return getClickGoStats(fpath);
|
|
402
|
+
}
|
|
403
|
+
else if (path.startsWith('/storage/')) {
|
|
404
|
+
// --- TODO ---
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
else if (path.startsWith('/mounted/')) {
|
|
408
|
+
// --- TODO ---
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
else if (path.startsWith('/package/')) {
|
|
412
|
+
if (!options.files) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
if (options.files[fpath]) {
|
|
416
|
+
// --- 文件 ---
|
|
417
|
+
const file = options.files[fpath];
|
|
418
|
+
const date = new Date();
|
|
419
|
+
const ms = date.getTime();
|
|
420
|
+
let size = 0;
|
|
421
|
+
if (typeof file !== 'string') {
|
|
422
|
+
size = file.size;
|
|
423
|
+
}
|
|
424
|
+
else {
|
|
425
|
+
size = new Blob([file]).size;
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
isFile: function() {
|
|
429
|
+
return true;
|
|
430
|
+
},
|
|
431
|
+
isDirectory: function() {
|
|
432
|
+
return false;
|
|
433
|
+
},
|
|
434
|
+
isSymbolicLink: function() {
|
|
435
|
+
return false;
|
|
436
|
+
},
|
|
437
|
+
'size': size,
|
|
438
|
+
'blksize': size,
|
|
439
|
+
'atimeMs': ms,
|
|
440
|
+
'mtimeMs': ms,
|
|
441
|
+
'ctimeMs': ms,
|
|
442
|
+
'birthtimeMs': ms,
|
|
443
|
+
'atime': date,
|
|
444
|
+
'mtime': date,
|
|
445
|
+
'ctime': date,
|
|
446
|
+
'birthtime': date
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
// --- 检测是否是文件夹 ---
|
|
450
|
+
if (!fpath.endsWith('/')) {
|
|
451
|
+
fpath += '/';
|
|
452
|
+
}
|
|
453
|
+
for (const p in options.files) {
|
|
454
|
+
if (!p.startsWith(fpath)) {
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
// --- 文件夹 ---
|
|
458
|
+
const date = new Date();
|
|
459
|
+
const ms = date.getTime();
|
|
460
|
+
return {
|
|
461
|
+
isFile: function() {
|
|
462
|
+
return false;
|
|
463
|
+
},
|
|
464
|
+
isDirectory: function() {
|
|
465
|
+
return true;
|
|
466
|
+
},
|
|
467
|
+
isSymbolicLink: function() {
|
|
468
|
+
return false;
|
|
469
|
+
},
|
|
470
|
+
'size': 0,
|
|
471
|
+
'blksize': 0,
|
|
472
|
+
'atimeMs': ms,
|
|
473
|
+
'mtimeMs': ms,
|
|
474
|
+
'ctimeMs': ms,
|
|
475
|
+
'birthtimeMs': ms,
|
|
476
|
+
'atime': date,
|
|
477
|
+
'mtime': date,
|
|
478
|
+
'ctime': date,
|
|
479
|
+
'birthtime': date
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
else if (path.startsWith('/current/')) {
|
|
485
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
options.current = options.current.slice(0, -1);
|
|
489
|
+
return stats(options.current + fpath, options);
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* --- 判断是否是目录或目录是否存在,是的话返回 stats ---
|
|
498
|
+
* @param path 判断路径
|
|
499
|
+
* @param options 选项
|
|
500
|
+
*/
|
|
501
|
+
export async function isDir(path: string, options: {
|
|
502
|
+
'files'?: Record<string, Blob | string>;
|
|
503
|
+
'current'?: string;
|
|
504
|
+
} = {}): Promise<types.IStats | false> {
|
|
505
|
+
const pstats = await stats(path, options);
|
|
506
|
+
if (!pstats || !pstats.isDirectory()) {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
return pstats;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* --- 判断是否是文件或文件是否存在,是的话返回 stats ---
|
|
514
|
+
* @param path 判断路径
|
|
515
|
+
*/
|
|
516
|
+
export async function isFile(path: string, options: {
|
|
517
|
+
'files'?: Record<string, Blob | string>;
|
|
518
|
+
'current'?: string;
|
|
519
|
+
} = {}): Promise<types.IStats | false> {
|
|
520
|
+
const pstats = await stats(path, options);
|
|
521
|
+
if (!pstats || !pstats.isFile()) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
return pstats;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* --- 深度创建目录,如果最末目录存在,则自动创建成功 ---
|
|
529
|
+
* @param path 要创建的路径,如 /a/b/c/
|
|
530
|
+
* @param mode 权限
|
|
531
|
+
*/
|
|
532
|
+
export async function mkdir(path: string, mode: number = 0o755, options: {
|
|
533
|
+
'current'?: string;
|
|
534
|
+
} = {}): Promise<boolean> {
|
|
535
|
+
path = tool.urlResolve('/', path);
|
|
536
|
+
if (await isDir(path, options)) {
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
const fpath = path.slice(8);
|
|
540
|
+
if (path.startsWith('/clickgo/')) {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
else if (path.startsWith('/storage/')) {
|
|
544
|
+
// --- TODO ---
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
else if (path.startsWith('/mounted/')) {
|
|
548
|
+
// --- TODO ---
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
else if (path.startsWith('/package/')) {
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
else if (path.startsWith('/current/')) {
|
|
555
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
options.current = options.current.slice(0, -1);
|
|
559
|
+
return mkdir(options.current + fpath, mode, options);
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* --- 删除空目录 ---
|
|
568
|
+
* @param path 要删除的目录
|
|
569
|
+
*/
|
|
570
|
+
export async function rmdir(path: string, options: {
|
|
571
|
+
'current'?: string;
|
|
572
|
+
} = {}): Promise<boolean> {
|
|
573
|
+
path = tool.urlResolve('/', path);
|
|
574
|
+
const fpath = path.slice(8);
|
|
575
|
+
if (path.startsWith('/clickgo/')) {
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
else if (path.startsWith('/storage/')) {
|
|
579
|
+
// --- TODO ---
|
|
580
|
+
return false;
|
|
581
|
+
}
|
|
582
|
+
else if (path.startsWith('/mounted/')) {
|
|
583
|
+
// --- TODO ---
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
else if (path.startsWith('/package/')) {
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
else if (path.startsWith('/current/')) {
|
|
590
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
options.current = options.current.slice(0, -1);
|
|
594
|
+
return rmdir(options.current + fpath, options);
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* --- 删除一个非空目录 ---
|
|
603
|
+
* --- [ Danger ] [ 危险 ] ---
|
|
604
|
+
*/
|
|
605
|
+
export async function rmdirDeep(path: string, options: {
|
|
606
|
+
'current'?: string;
|
|
607
|
+
} = {}): Promise<boolean> {
|
|
608
|
+
path = tool.urlResolve('/', path);
|
|
609
|
+
if (!path.endsWith('/')) {
|
|
610
|
+
path += '/';
|
|
611
|
+
}
|
|
612
|
+
const list = await readDir(path, options);
|
|
613
|
+
for (const item of list) {
|
|
614
|
+
const stat = await stats(path + item.name, options);
|
|
615
|
+
if (!stat) {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
if (stat.isDirectory()) {
|
|
619
|
+
// --- 目录 ---
|
|
620
|
+
const rtn = await rmdirDeep(path + item.name, options);
|
|
621
|
+
if (!rtn) {
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
const rtn = await unlink(path + item.name, options);
|
|
627
|
+
if (!rtn) {
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return rmdir(path, options);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* --- 修改权限
|
|
637
|
+
* @param path 要修改的路径
|
|
638
|
+
* @param mod 权限
|
|
639
|
+
*/
|
|
640
|
+
export async function chmod(path: string, mod: string | number, options: {
|
|
641
|
+
'current'?: string;
|
|
642
|
+
} = {}): Promise<boolean> {
|
|
643
|
+
path = tool.urlResolve('/', path);
|
|
644
|
+
const fpath = path.slice(8);
|
|
645
|
+
if (path.startsWith('/clickgo/')) {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
else if (path.startsWith('/storage/')) {
|
|
649
|
+
// --- TODO ---
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
else if (path.startsWith('/mounted/')) {
|
|
653
|
+
// --- TODO ---
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
else if (path.startsWith('/package/')) {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
else if (path.startsWith('/current/')) {
|
|
660
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
661
|
+
return false;
|
|
662
|
+
}
|
|
663
|
+
options.current = options.current.slice(0, -1);
|
|
664
|
+
return chmod(options.current + fpath, mod, options);
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* --- 重命名/移动 文件文件夹 ---
|
|
673
|
+
* @param oldPath 老名
|
|
674
|
+
* @param newPath 新名
|
|
675
|
+
*/
|
|
676
|
+
export async function rename(oldPath: string, newPath: string, options: {
|
|
677
|
+
'current'?: string;
|
|
678
|
+
} = {}): Promise<boolean> {
|
|
679
|
+
oldPath = tool.urlResolve('/', oldPath);
|
|
680
|
+
newPath = tool.urlResolve('/', newPath);
|
|
681
|
+
if (!oldPath.startsWith(newPath.slice(0, 9))) {
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
const ofpath = oldPath.slice(8);
|
|
685
|
+
const nfpath = newPath.slice(8);
|
|
686
|
+
if (oldPath.startsWith('/clickgo/')) {
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
else if (oldPath.startsWith('/storage/')) {
|
|
690
|
+
// --- TODO ---
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
else if (oldPath.startsWith('/mounted/')) {
|
|
694
|
+
// --- TODO ---
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
else if (oldPath.startsWith('/package/')) {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
else if (oldPath.startsWith('/current/')) {
|
|
701
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
options.current = options.current.slice(0, -1);
|
|
705
|
+
return rename(options.current + ofpath, options.current + nfpath, options);
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* --- 获取文件夹下文件列表 ---
|
|
714
|
+
* @param path 文件夹路径
|
|
715
|
+
*/
|
|
716
|
+
export async function readDir(path: string, options: {
|
|
717
|
+
'encoding'?: BufferEncoding;
|
|
718
|
+
'files'?: Record<string, Blob | string>;
|
|
719
|
+
'current'?: string;
|
|
720
|
+
} = {}): Promise<types.IDirent[]> {
|
|
721
|
+
path = tool.urlResolve('/', path);
|
|
722
|
+
if (path === '/') {
|
|
723
|
+
const list = [
|
|
724
|
+
{
|
|
725
|
+
isFile: function() {
|
|
726
|
+
return false;
|
|
727
|
+
},
|
|
728
|
+
isDirectory: function() {
|
|
729
|
+
return true;
|
|
730
|
+
},
|
|
731
|
+
isSymbolicLink: function() {
|
|
732
|
+
return false;
|
|
733
|
+
},
|
|
734
|
+
'name': 'clickgo'
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
isFile: function() {
|
|
738
|
+
return false;
|
|
739
|
+
},
|
|
740
|
+
isDirectory: function() {
|
|
741
|
+
return true;
|
|
742
|
+
},
|
|
743
|
+
isSymbolicLink: function() {
|
|
744
|
+
return false;
|
|
745
|
+
},
|
|
746
|
+
'name': 'storage'
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
isFile: function() {
|
|
750
|
+
return false;
|
|
751
|
+
},
|
|
752
|
+
isDirectory: function() {
|
|
753
|
+
return true;
|
|
754
|
+
},
|
|
755
|
+
isSymbolicLink: function() {
|
|
756
|
+
return false;
|
|
757
|
+
},
|
|
758
|
+
'name': 'mounted'
|
|
759
|
+
}
|
|
760
|
+
];
|
|
761
|
+
if (options.files) {
|
|
762
|
+
list.push({
|
|
763
|
+
isFile: function() {
|
|
764
|
+
return false;
|
|
765
|
+
},
|
|
766
|
+
isDirectory: function() {
|
|
767
|
+
return true;
|
|
768
|
+
},
|
|
769
|
+
isSymbolicLink: function() {
|
|
770
|
+
return false;
|
|
771
|
+
},
|
|
772
|
+
'name': 'package'
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
if (options.current) {
|
|
776
|
+
list.push({
|
|
777
|
+
isFile: function() {
|
|
778
|
+
return false;
|
|
779
|
+
},
|
|
780
|
+
isDirectory: function() {
|
|
781
|
+
return false;
|
|
782
|
+
},
|
|
783
|
+
isSymbolicLink: function() {
|
|
784
|
+
return true;
|
|
785
|
+
},
|
|
786
|
+
'name': 'current'
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
return list;
|
|
790
|
+
}
|
|
791
|
+
if (!path.endsWith('/')) {
|
|
792
|
+
path += '/';
|
|
793
|
+
}
|
|
794
|
+
const fpath = path.slice(8);
|
|
795
|
+
if (path.startsWith('/clickgo/')) {
|
|
796
|
+
const list: types.IDirent[] = [];
|
|
797
|
+
for (const item of clickgoFiles) {
|
|
798
|
+
if (!item.startsWith(fpath)) {
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if (fpath === item) {
|
|
802
|
+
// --- 是自己 ---
|
|
803
|
+
continue;
|
|
804
|
+
}
|
|
805
|
+
const rpath = item.slice(fpath.length);
|
|
806
|
+
if (rpath.includes('/')) {
|
|
807
|
+
// --- 可能是下级 ---
|
|
808
|
+
if (rpath.endsWith('/')) {
|
|
809
|
+
// --- 是个文件夹 ---
|
|
810
|
+
if (rpath.slice(0, -1).includes('/')) {
|
|
811
|
+
// --- 是好几层下面的文件夹了 ---
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
list.push({
|
|
815
|
+
isFile: function() {
|
|
816
|
+
return false;
|
|
817
|
+
},
|
|
818
|
+
isDirectory: function() {
|
|
819
|
+
return true;
|
|
820
|
+
},
|
|
821
|
+
isSymbolicLink: function() {
|
|
822
|
+
return false;
|
|
823
|
+
},
|
|
824
|
+
'name': rpath.slice(0, -1)
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
// --- 本层文件 ---
|
|
830
|
+
list.push({
|
|
831
|
+
isFile: function() {
|
|
832
|
+
return true;
|
|
833
|
+
},
|
|
834
|
+
isDirectory: function() {
|
|
835
|
+
return false;
|
|
836
|
+
},
|
|
837
|
+
isSymbolicLink: function() {
|
|
838
|
+
return false;
|
|
839
|
+
},
|
|
840
|
+
'name': rpath
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
return list;
|
|
844
|
+
}
|
|
845
|
+
else if (path.startsWith('/storage/')) {
|
|
846
|
+
// --- TODO ---
|
|
847
|
+
return [];
|
|
848
|
+
}
|
|
849
|
+
else if (path.startsWith('/mounted/')) {
|
|
850
|
+
// --- TODO ---
|
|
851
|
+
return [];
|
|
852
|
+
}
|
|
853
|
+
else if (path.startsWith('/package/')) {
|
|
854
|
+
if (!options.files) {
|
|
855
|
+
return [];
|
|
856
|
+
}
|
|
857
|
+
const list: types.IDirent[] = [];
|
|
858
|
+
const dirs: string[] = [];
|
|
859
|
+
for (const p in options.files) {
|
|
860
|
+
if (!p.startsWith(fpath)) {
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
const rpath = p.slice(fpath.length);
|
|
864
|
+
const sio = rpath.indexOf('/');
|
|
865
|
+
if (sio !== -1) {
|
|
866
|
+
// --- 一定是下级文件,因此加入文件夹项目到 dirs ---
|
|
867
|
+
const name = rpath.slice(0, sio);
|
|
868
|
+
if (!dirs.includes(name)) {
|
|
869
|
+
dirs.push(name);
|
|
870
|
+
list.push({
|
|
871
|
+
isFile: function() {
|
|
872
|
+
return false;
|
|
873
|
+
},
|
|
874
|
+
isDirectory: function() {
|
|
875
|
+
return true;
|
|
876
|
+
},
|
|
877
|
+
isSymbolicLink: function() {
|
|
878
|
+
return false;
|
|
879
|
+
},
|
|
880
|
+
'name': name
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
// --- 本层文件 ---
|
|
886
|
+
list.push({
|
|
887
|
+
isFile: function() {
|
|
888
|
+
return true;
|
|
889
|
+
},
|
|
890
|
+
isDirectory: function() {
|
|
891
|
+
return false;
|
|
892
|
+
},
|
|
893
|
+
isSymbolicLink: function() {
|
|
894
|
+
return false;
|
|
895
|
+
},
|
|
896
|
+
'name': rpath
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
return list;
|
|
900
|
+
}
|
|
901
|
+
else if (path.startsWith('/current/')) {
|
|
902
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
903
|
+
return [];
|
|
904
|
+
}
|
|
905
|
+
options.current = options.current.slice(0, -1);
|
|
906
|
+
return readDir(options.current + fpath, options);
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
return [];
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* --- 复制文件夹里的内容到另一个地方,失败不会回滚 ---
|
|
915
|
+
* @param from 源,末尾加 /
|
|
916
|
+
* @param to 目标,末尾加 /
|
|
917
|
+
* @param options 选项
|
|
918
|
+
*/
|
|
919
|
+
export async function copyFolder(from: string, to: string, options: {
|
|
920
|
+
'ignore'?: RegExp[];
|
|
921
|
+
'current'?: string;
|
|
922
|
+
} = {}): Promise<number> {
|
|
923
|
+
from = tool.urlResolve('/', from);
|
|
924
|
+
to = tool.urlResolve('/', to);
|
|
925
|
+
if (!from.startsWith(to.slice(0, 9))) {
|
|
926
|
+
return 0;
|
|
927
|
+
}
|
|
928
|
+
const ffpath = from.slice(8);
|
|
929
|
+
const tfpath = from.slice(8);
|
|
930
|
+
if (from.startsWith('/clickgo/')) {
|
|
931
|
+
return 0;
|
|
932
|
+
}
|
|
933
|
+
else if (from.startsWith('/storage/')) {
|
|
934
|
+
// --- TODO ---
|
|
935
|
+
return 0;
|
|
936
|
+
}
|
|
937
|
+
else if (from.startsWith('/mounted/')) {
|
|
938
|
+
// --- TODO ---
|
|
939
|
+
return 0;
|
|
940
|
+
}
|
|
941
|
+
else if (from.startsWith('/package/')) {
|
|
942
|
+
return 0;
|
|
943
|
+
}
|
|
944
|
+
else if (from.startsWith('/current/')) {
|
|
945
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
946
|
+
return 0;
|
|
947
|
+
}
|
|
948
|
+
options.current = options.current.slice(0, -1);
|
|
949
|
+
return copyFolder(options.current + ffpath, options.current + tfpath, options);
|
|
950
|
+
}
|
|
951
|
+
else {
|
|
952
|
+
return 0;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* --- 复制文件 ---
|
|
958
|
+
* @param src 源文件
|
|
959
|
+
* @param dest 目标文件
|
|
960
|
+
*/
|
|
961
|
+
export async function copyFile(src: string, dest: string, options: {
|
|
962
|
+
'current'?: string;
|
|
963
|
+
} = {}): Promise<boolean> {
|
|
964
|
+
src = tool.urlResolve('/', src);
|
|
965
|
+
dest = tool.urlResolve('/', dest);
|
|
966
|
+
if (!src.startsWith(dest.slice(0, 9))) {
|
|
967
|
+
return false;
|
|
968
|
+
}
|
|
969
|
+
const sfpath = src.slice(8);
|
|
970
|
+
const dfpath = dest.slice(8);
|
|
971
|
+
if (src.startsWith('/clickgo/')) {
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
else if (src.startsWith('/storage/')) {
|
|
975
|
+
// --- TODO ---
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
else if (src.startsWith('/mounted/')) {
|
|
979
|
+
// --- TODO ---
|
|
980
|
+
return false;
|
|
981
|
+
}
|
|
982
|
+
else if (src.startsWith('/package/')) {
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
else if (src.startsWith('/current/')) {
|
|
986
|
+
if (!options.current || !options.current.endsWith('/')) {
|
|
987
|
+
return false;
|
|
988
|
+
}
|
|
989
|
+
options.current = options.current.slice(0, -1);
|
|
990
|
+
return copyFile(options.current + sfpath, options.current + dfpath, options);
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
}
|