clickgo 3.1.5-dev14 → 3.1.6-dev15
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/README.md +7 -7
- package/dist/app/demo/app.js +28 -2
- package/dist/app/demo/config.json +17 -1
- package/dist/app/demo/form/control/box/box.js +66 -0
- package/dist/app/demo/form/control/box/box.xml +18 -0
- package/dist/app/demo/form/control/button/button.js +24 -1
- package/dist/app/demo/form/control/check/check.js +24 -1
- package/dist/app/demo/form/control/dialog/dialog.js +24 -1
- package/dist/app/demo/form/control/file/file.js +24 -1
- package/dist/app/demo/form/control/flow/flow.js +24 -1
- package/dist/app/demo/form/control/form/form.js +24 -1
- package/dist/app/demo/form/control/layout/layout.js +57 -0
- package/dist/app/demo/form/control/layout/layout.xml +16 -0
- package/dist/app/demo/form/control/list/list.js +24 -1
- package/dist/app/demo/form/control/list/list.xml +8 -2
- package/dist/app/demo/form/control/marquee/marquee.js +24 -2
- package/dist/app/demo/form/control/marquee/marquee.xml +2 -5
- package/dist/app/demo/form/control/menu/menu.js +24 -1
- package/dist/app/demo/form/control/monaco/monaco.js +24 -1
- package/dist/app/demo/form/control/nav/nav.js +52 -0
- package/dist/app/demo/form/control/nav/nav.xml +43 -0
- package/dist/app/demo/form/control/panel/panel.js +67 -0
- package/dist/app/demo/form/control/panel/panel.xml +11 -0
- package/dist/app/demo/form/control/panel/test1.js +58 -0
- package/dist/app/demo/form/control/panel/test1.xml +16 -0
- package/dist/app/demo/form/control/panel/test2.xml +3 -0
- package/dist/app/demo/form/control/property/property.js +24 -1
- package/dist/app/demo/form/control/radio/radio.js +24 -1
- package/dist/app/demo/form/control/scroll/scroll.js +25 -1
- package/dist/app/demo/form/control/scroll/scroll.xml +5 -2
- package/dist/app/demo/form/control/select/select.js +24 -1
- package/dist/app/demo/form/control/tab/tab.js +24 -1
- package/dist/app/demo/form/control/table/table.js +164 -0
- package/dist/app/demo/form/control/table/table.xml +35 -0
- package/dist/app/demo/form/control/text/text.js +24 -1
- package/dist/app/demo/form/control/vflow/vflow.js +24 -1
- package/dist/app/demo/form/event/form/form.js +24 -1
- package/dist/app/demo/form/event/other/other.js +24 -1
- package/dist/app/demo/form/event/screen/screen.js +24 -1
- package/dist/app/demo/form/event/task/task.js +24 -1
- package/dist/app/demo/form/main.js +84 -33
- package/dist/app/demo/form/main.xml +5 -0
- package/dist/app/demo/form/method/aform/aform.js +28 -2
- package/dist/app/demo/form/method/aform/sd.js +24 -1
- package/dist/app/demo/form/method/core/core.js +24 -1
- package/dist/app/demo/form/method/dom/dom.js +48 -2
- package/dist/app/demo/form/method/dom/dom.xml +11 -0
- package/dist/app/demo/form/method/form/form.js +35 -1
- package/dist/app/demo/form/method/form/form.xml +2 -0
- package/dist/app/demo/form/method/fs/fs.js +138 -4
- package/dist/app/demo/form/method/fs/fs.xml +11 -1
- package/dist/app/demo/form/method/fs/text.js +24 -1
- package/dist/app/demo/form/method/native/native.js +24 -1
- package/dist/app/demo/form/method/system/system.js +24 -1
- package/dist/app/demo/form/method/task/task.js +31 -4
- package/dist/app/demo/form/method/task/task.xml +6 -1
- package/dist/app/demo/form/method/theme/theme.js +24 -1
- package/dist/app/demo/form/method/tool/tool.js +35 -1
- package/dist/app/demo/form/method/zip/zip.js +29 -3
- package/dist/app/task/app.js +28 -2
- package/dist/app/task/form/bar/bar.js +24 -1
- package/dist/clickgo.js +33 -10
- package/dist/control/box.cgc +0 -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/nav.cgc +0 -0
- package/dist/control/property.cgc +0 -0
- package/dist/control/table.cgc +0 -0
- package/dist/control/task.cgc +0 -0
- package/dist/global.css +1 -1
- package/dist/lib/control.js +53 -12
- package/dist/lib/control.ts +25 -5
- package/dist/lib/core.js +44 -45
- package/dist/lib/core.ts +17 -41
- package/dist/lib/dom.js +322 -108
- package/dist/lib/dom.ts +394 -127
- package/dist/lib/form.js +441 -58
- package/dist/lib/form.ts +525 -74
- package/dist/lib/fs.js +485 -224
- package/dist/lib/fs.ts +493 -287
- package/dist/lib/native.js +24 -1
- package/dist/lib/task.js +143 -136
- package/dist/lib/task.ts +124 -127
- package/dist/lib/theme.js +27 -4
- package/dist/lib/tool.js +19 -2
- package/dist/lib/tool.ts +23 -1
- package/dist/lib/zip.js +29 -3
- package/dist/lib/zip.ts +1 -1
- package/dist/theme/familiar.cgt +0 -0
- package/package.json +4 -6
- package/types/index.d.ts +42 -34
package/dist/lib/fs.ts
CHANGED
|
@@ -8,8 +8,30 @@
|
|
|
8
8
|
import * as types from '../../types';
|
|
9
9
|
import * as tool from './tool';
|
|
10
10
|
import * as task from './task';
|
|
11
|
+
import * as form from './form';
|
|
12
|
+
import * as core from './core';
|
|
13
|
+
import * as native from './native';
|
|
11
14
|
|
|
12
|
-
const clickgoFiles = ['/app/', '/app/demo/', '/app/demo/app.js', '/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/flow/', '/app/demo/form/control/flow/flow.css', '/app/demo/form/control/flow/flow.js', '/app/demo/form/control/flow/flow.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/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/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/vflow/', '/app/demo/form/control/vflow/vflow.css', '/app/demo/form/control/vflow/vflow.js', '/app/demo/form/control/vflow/vflow.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/other/', '/app/demo/form/event/other/other.js', '/app/demo/form/event/other/other.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/aform/', '/app/demo/form/method/aform/aform.js', '/app/demo/form/method/aform/aform.xml', '/app/demo/form/method/aform/sd.js', '/app/demo/form/method/aform/sd.xml', '/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/native/', '/app/demo/form/method/native/native.js', '/app/demo/form/method/native/native.xml', '/app/demo/form/method/system/', '/app/demo/form/method/system/system.js', '/app/demo/form/method/system/system.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/app.js', '/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'];
|
|
15
|
+
const clickgoFiles = ['/app/', '/app/demo/', '/app/demo/app.js', '/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/box/', '/app/demo/form/control/box/box.js', '/app/demo/form/control/box/box.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/flow/', '/app/demo/form/control/flow/flow.css', '/app/demo/form/control/flow/flow.js', '/app/demo/form/control/flow/flow.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/img/', '/app/demo/form/control/img/img.xml', '/app/demo/form/control/label/', '/app/demo/form/control/label/label.xml', '/app/demo/form/control/layout/', '/app/demo/form/control/layout/layout.js', '/app/demo/form/control/layout/layout.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/nav/', '/app/demo/form/control/nav/nav.js', '/app/demo/form/control/nav/nav.xml', '/app/demo/form/control/panel/', '/app/demo/form/control/panel/panel.js', '/app/demo/form/control/panel/panel.xml', '/app/demo/form/control/panel/test1.js', '/app/demo/form/control/panel/test1.xml', '/app/demo/form/control/panel/test2.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/table/', '/app/demo/form/control/table/table.js', '/app/demo/form/control/table/table.xml', '/app/demo/form/control/text/', '/app/demo/form/control/text/text.js', '/app/demo/form/control/text/text.xml', '/app/demo/form/control/vflow/', '/app/demo/form/control/vflow/vflow.css', '/app/demo/form/control/vflow/vflow.js', '/app/demo/form/control/vflow/vflow.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/other/', '/app/demo/form/event/other/other.js', '/app/demo/form/event/other/other.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/aform/', '/app/demo/form/method/aform/aform.js', '/app/demo/form/method/aform/aform.xml', '/app/demo/form/method/aform/sd.js', '/app/demo/form/method/aform/sd.xml', '/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/native/', '/app/demo/form/method/native/native.js', '/app/demo/form/method/native/native.xml', '/app/demo/form/method/system/', '/app/demo/form/method/system/system.js', '/app/demo/form/method/system/system.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/app.js', '/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/box.cgc', '/control/common.cgc', '/control/form.cgc', '/control/monaco.cgc', '/control/nav.cgc', '/control/property.cgc', '/control/table.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'];
|
|
16
|
+
|
|
17
|
+
/** --- fs lib 用到的语言包 --- */
|
|
18
|
+
const localeData: Record<string, {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
20
|
+
'apply-unmount': string;
|
|
21
|
+
}> = {
|
|
22
|
+
'en': {
|
|
23
|
+
'apply-unmount': 'Are you sure to unmount the "?" mount point?',
|
|
24
|
+
},
|
|
25
|
+
'sc': {
|
|
26
|
+
'apply-unmount': '确定卸载“?”挂载点吗?'
|
|
27
|
+
},
|
|
28
|
+
'tc': {
|
|
29
|
+
'apply-unmount': '確定卸載「?」掛載點嗎?'
|
|
30
|
+
},
|
|
31
|
+
'ja': {
|
|
32
|
+
'apply-unmount': '「?」マウント ポイントをアンマウントしますか?'
|
|
33
|
+
}
|
|
34
|
+
};
|
|
13
35
|
|
|
14
36
|
/** --- 已经挂载的列表 --- */
|
|
15
37
|
const mounts: Record<string, types.IMountHandler> = {};
|
|
@@ -17,23 +39,24 @@ const mounts: Record<string, types.IMountHandler> = {};
|
|
|
17
39
|
/** --- 根据 mounted 的 path 获取挂载点 name --- */
|
|
18
40
|
function getMountName(path: string): string {
|
|
19
41
|
const io = path.slice(9).indexOf('/');
|
|
20
|
-
return path.slice(9, io + 9);
|
|
42
|
+
return io === -1 ? path.slice(9) : path.slice(9, io + 9);
|
|
21
43
|
}
|
|
22
44
|
|
|
23
45
|
/**
|
|
24
46
|
* --- 挂载到 mounted 目录下 ---
|
|
25
47
|
* @param name 目录名
|
|
26
|
-
* @param handler
|
|
48
|
+
* @param handler 回调相关
|
|
49
|
+
* @param taskId App 模式下无效
|
|
27
50
|
*/
|
|
28
|
-
export function mount(name: string, handler: types.IMountHandler): boolean {
|
|
51
|
+
export function mount(name: string, handler: types.IMountHandler, taskId?: number): boolean {
|
|
29
52
|
if (mounts[name]) {
|
|
30
53
|
return false;
|
|
31
54
|
}
|
|
32
55
|
if (!/^[a-zA-Z][\w]+$/.test(name)) {
|
|
33
56
|
return false;
|
|
34
57
|
}
|
|
35
|
-
if (
|
|
36
|
-
const t = task.list[
|
|
58
|
+
if (taskId) {
|
|
59
|
+
const t = task.list[taskId];
|
|
37
60
|
if (t) {
|
|
38
61
|
const val = 'fs./mounted/' + name + '/w';
|
|
39
62
|
if (!t.runtime.permissions.includes(val)) {
|
|
@@ -41,6 +64,7 @@ export function mount(name: string, handler: types.IMountHandler): boolean {
|
|
|
41
64
|
}
|
|
42
65
|
}
|
|
43
66
|
}
|
|
67
|
+
handler.date = new Date();
|
|
44
68
|
mounts[name] = handler;
|
|
45
69
|
return true;
|
|
46
70
|
}
|
|
@@ -48,13 +72,13 @@ export function mount(name: string, handler: types.IMountHandler): boolean {
|
|
|
48
72
|
/**
|
|
49
73
|
* --- 卸载 mounted ---
|
|
50
74
|
* @param name 目录名
|
|
51
|
-
* @param taskId 校验 taskId,App 模式下无效
|
|
52
75
|
*/
|
|
53
|
-
export function unmount(name: string
|
|
76
|
+
export async function unmount(name: string): Promise<boolean> {
|
|
54
77
|
if (!mounts[name]) {
|
|
55
78
|
return true;
|
|
56
79
|
}
|
|
57
|
-
|
|
80
|
+
const loc = localeData[core.config.locale]?.['apply-unmount'] ?? localeData['en']['apply-unmount'];
|
|
81
|
+
if (!await form.superConfirm(loc.replace('?', '/mount/' + name + '/'))) {
|
|
58
82
|
return false;
|
|
59
83
|
}
|
|
60
84
|
delete mounts[name];
|
|
@@ -64,31 +88,26 @@ export function unmount(name: string, taskId?: number): boolean {
|
|
|
64
88
|
export async function getContent(path: string, options?: {
|
|
65
89
|
'start'?: number;
|
|
66
90
|
'end'?: number;
|
|
67
|
-
'files'?: Record<string, Blob | string>;
|
|
68
|
-
'current'?: string;
|
|
69
91
|
'progress'?: (loaded: number, total: number) => void | Promise<void>;
|
|
70
|
-
}): Promise<string | Blob | null>;
|
|
92
|
+
}, taskId?: number): Promise<string | Blob | null>;
|
|
71
93
|
export async function getContent(path: string, options: BufferEncoding | {
|
|
72
94
|
'encoding': BufferEncoding;
|
|
73
95
|
'start'?: number;
|
|
74
96
|
'end'?: number;
|
|
75
|
-
'files'?: Record<string, Blob | string>;
|
|
76
|
-
'current'?: string;
|
|
77
97
|
'progress'?: (loaded: number, total: number) => void | Promise<void>;
|
|
78
|
-
}): Promise<string | null>;
|
|
98
|
+
}, taskId?: number): Promise<string | null>;
|
|
79
99
|
/**
|
|
80
100
|
* --- 读取完整文件或一段 ---
|
|
81
101
|
* @param path 文件路径
|
|
82
102
|
* @param options 编码或选项
|
|
103
|
+
* @param taskId App 模式下无效
|
|
83
104
|
*/
|
|
84
105
|
export async function getContent(path: string, options?: BufferEncoding | {
|
|
85
106
|
'encoding'?: BufferEncoding;
|
|
86
107
|
'start'?: number;
|
|
87
108
|
'end'?: number;
|
|
88
|
-
'files'?: Record<string, Blob | string>;
|
|
89
|
-
'current'?: string;
|
|
90
109
|
'progress'?: (loaded: number, total: number) => void | Promise<void>;
|
|
91
|
-
}): Promise<Blob | string | null> {
|
|
110
|
+
}, taskId?: number): Promise<Blob | string | null> {
|
|
92
111
|
path = tool.urlResolve('/', path);
|
|
93
112
|
const fpath = path.slice(8);
|
|
94
113
|
if (typeof options === 'string') {
|
|
@@ -150,23 +169,43 @@ export async function getContent(path: string, options?: BufferEncoding | {
|
|
|
150
169
|
return null;
|
|
151
170
|
}
|
|
152
171
|
}
|
|
153
|
-
else if (path.startsWith('/storage/')) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
172
|
+
else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
|
|
173
|
+
const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
|
|
174
|
+
if (!r[0]) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
if (path.startsWith('/mounted/')) {
|
|
178
|
+
const name = getMountName(path);
|
|
179
|
+
const hanlder = mounts[name];
|
|
180
|
+
if (!hanlder) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
return hanlder.getContent?.(path.slice(9 + name.length), options) ?? null;
|
|
184
|
+
}
|
|
185
|
+
// --- storage ---
|
|
186
|
+
if (options.progress) {
|
|
187
|
+
// --- native 暂不支持 progress ---
|
|
188
|
+
delete options.progress;
|
|
189
|
+
}
|
|
190
|
+
const rtn = await native.invoke('cg-fs-getContent', native.getToken(), fpath, options);
|
|
191
|
+
if (!rtn) {
|
|
161
192
|
return null;
|
|
162
193
|
}
|
|
163
|
-
|
|
194
|
+
if (typeof rtn === 'string') {
|
|
195
|
+
return rtn;
|
|
196
|
+
}
|
|
197
|
+
return new Blob([rtn], {
|
|
198
|
+
'type': tool.getMimeByPath(path).mime
|
|
199
|
+
});
|
|
164
200
|
}
|
|
165
|
-
else if (path.startsWith('/package/')) {
|
|
166
|
-
if (!
|
|
201
|
+
else if (path.startsWith('/package/') || path.startsWith('/current/')) {
|
|
202
|
+
if (!taskId) {
|
|
167
203
|
return null;
|
|
168
204
|
}
|
|
169
|
-
|
|
205
|
+
if (path.startsWith('/current/')) {
|
|
206
|
+
return getContent(task.list[taskId].current + fpath, options, taskId);
|
|
207
|
+
}
|
|
208
|
+
const file = task.list[taskId].app.files[fpath];
|
|
170
209
|
if (!file) {
|
|
171
210
|
return null;
|
|
172
211
|
}
|
|
@@ -176,7 +215,10 @@ export async function getContent(path: string, options?: BufferEncoding | {
|
|
|
176
215
|
}
|
|
177
216
|
if (!options.encoding) {
|
|
178
217
|
// --- 没有编码则返回 blob ---
|
|
179
|
-
|
|
218
|
+
if (start === undefined && end === undefined) {
|
|
219
|
+
return file;
|
|
220
|
+
}
|
|
221
|
+
return file.slice(start, end, file.type);
|
|
180
222
|
}
|
|
181
223
|
const encoding = options.encoding;
|
|
182
224
|
return new Promise(function(resolve) {
|
|
@@ -187,13 +229,6 @@ export async function getContent(path: string, options?: BufferEncoding | {
|
|
|
187
229
|
fr.readAsText(file, encoding);
|
|
188
230
|
});
|
|
189
231
|
}
|
|
190
|
-
else if (path.startsWith('/current/')) {
|
|
191
|
-
if (!options.current) {
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
|
|
195
|
-
return getContent(current + fpath, options);
|
|
196
|
-
}
|
|
197
232
|
else {
|
|
198
233
|
return null;
|
|
199
234
|
}
|
|
@@ -204,39 +239,46 @@ export async function getContent(path: string, options?: BufferEncoding | {
|
|
|
204
239
|
* @param path 文件路径
|
|
205
240
|
* @param data 要写入的内容
|
|
206
241
|
* @param options 选项
|
|
242
|
+
* @param taskId App 模式下无效
|
|
207
243
|
*/
|
|
208
244
|
export async function putContent(path: string, data: string | Blob, options: {
|
|
209
245
|
'encoding'?: BufferEncoding | null;
|
|
210
246
|
'mode'?: string | number;
|
|
211
247
|
'flag'?: string | number;
|
|
212
|
-
|
|
213
|
-
} = {}): Promise<boolean> {
|
|
248
|
+
} = {}, taskId?: number): Promise<boolean> {
|
|
214
249
|
path = tool.urlResolve('/', path);
|
|
215
250
|
const fpath = path.slice(8);
|
|
216
251
|
if (path.startsWith('/clickgo/')) {
|
|
217
252
|
return false;
|
|
218
253
|
}
|
|
219
|
-
else if (path.startsWith('/storage/')) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
else if (path.startsWith('/mounted/')) {
|
|
224
|
-
const name = getMountName(path);
|
|
225
|
-
const hanlder = mounts[name];
|
|
226
|
-
if (!hanlder) {
|
|
254
|
+
else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
|
|
255
|
+
const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
|
|
256
|
+
if (!r[0]) {
|
|
227
257
|
return false;
|
|
228
258
|
}
|
|
229
|
-
|
|
259
|
+
if (path.startsWith('/mounted/')) {
|
|
260
|
+
const name = getMountName(path);
|
|
261
|
+
const hanlder = mounts[name];
|
|
262
|
+
if (!hanlder) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
return hanlder.putContent?.(path.slice(9 + name.length), data, options) ?? false;
|
|
266
|
+
}
|
|
267
|
+
// --- storage ---
|
|
268
|
+
let buf: Uint8Array | undefined = undefined;
|
|
269
|
+
if (data instanceof Blob) {
|
|
270
|
+
buf = new Uint8Array(await data.arrayBuffer());
|
|
271
|
+
}
|
|
272
|
+
return native.invoke('cg-fs-putContent', native.getToken(), fpath, buf ?? data, options);
|
|
230
273
|
}
|
|
231
274
|
else if (path.startsWith('/package/')) {
|
|
232
275
|
return false;
|
|
233
276
|
}
|
|
234
277
|
else if (path.startsWith('/current/')) {
|
|
235
|
-
if (!
|
|
278
|
+
if (!taskId) {
|
|
236
279
|
return false;
|
|
237
280
|
}
|
|
238
|
-
|
|
239
|
-
return putContent(current + fpath, data, options);
|
|
281
|
+
return putContent(task.list[taskId].current + fpath, data, options, taskId);
|
|
240
282
|
}
|
|
241
283
|
else {
|
|
242
284
|
return false;
|
|
@@ -248,47 +290,36 @@ export async function putContent(path: string, data: string | Blob, options: {
|
|
|
248
290
|
* @param path 要读取的路径
|
|
249
291
|
* @param options 选项
|
|
250
292
|
*/
|
|
251
|
-
export async function readLink(path: string,
|
|
252
|
-
'encoding'?: BufferEncoding;
|
|
253
|
-
/** --- 不以 / 结尾的路径 --- */
|
|
254
|
-
'current'?: string;
|
|
255
|
-
}): Promise<string | null> {
|
|
293
|
+
export async function readLink(path: string, encoding?: BufferEncoding, taskId?: number): Promise<string | null> {
|
|
256
294
|
path = tool.urlResolve('/', path);
|
|
257
295
|
const fpath = path.slice(8);
|
|
258
|
-
if (typeof options === 'string') {
|
|
259
|
-
options = {
|
|
260
|
-
'encoding': options
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
else if (!options) {
|
|
264
|
-
options = {};
|
|
265
|
-
}
|
|
266
296
|
if (path.startsWith('/clickgo/')) {
|
|
267
297
|
return null;
|
|
268
298
|
}
|
|
269
|
-
else if (path.startsWith('/storage/')) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
}
|
|
273
|
-
else if (path.startsWith('/mounted/')) {
|
|
274
|
-
const name = getMountName(path);
|
|
275
|
-
const hanlder = mounts[name];
|
|
276
|
-
if (!hanlder) {
|
|
299
|
+
else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
|
|
300
|
+
const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
|
|
301
|
+
if (!r[0]) {
|
|
277
302
|
return null;
|
|
278
303
|
}
|
|
279
|
-
|
|
304
|
+
if (path.startsWith('/mounted/')) {
|
|
305
|
+
const name = getMountName(path);
|
|
306
|
+
const hanlder = mounts[name];
|
|
307
|
+
if (!hanlder) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
return hanlder.readLink?.(path.slice(9 + name.length), encoding) ?? null;
|
|
311
|
+
}
|
|
312
|
+
// --- storage ---
|
|
313
|
+
return native.invoke('cg-fs-readLink', native.getToken(), fpath, encoding);
|
|
280
314
|
}
|
|
281
315
|
else if (path.startsWith('/package/')) {
|
|
282
316
|
return null;
|
|
283
317
|
}
|
|
284
318
|
else if (path.startsWith('/current/')) {
|
|
285
|
-
if (!
|
|
319
|
+
if (!taskId) {
|
|
286
320
|
return null;
|
|
287
321
|
}
|
|
288
|
-
|
|
289
|
-
return options.current.slice(0, -1);
|
|
290
|
-
}
|
|
291
|
-
return options.current;
|
|
322
|
+
return task.list[taskId].current;
|
|
292
323
|
}
|
|
293
324
|
else {
|
|
294
325
|
return null;
|
|
@@ -299,45 +330,46 @@ export async function readLink(path: string, options?: BufferEncoding | {
|
|
|
299
330
|
* --- 把源文件创建一个 link ---
|
|
300
331
|
* @param filePath 源文件
|
|
301
332
|
* @param linkPath 连接路径
|
|
302
|
-
* @param
|
|
333
|
+
* @param type 选项
|
|
334
|
+
* @param taskId App 模式下无效
|
|
303
335
|
*/
|
|
304
|
-
export async function symlink(filePath: string, linkPath: string,
|
|
305
|
-
'type'?: 'dir' | 'file' | 'junction';
|
|
306
|
-
'current'?: string;
|
|
307
|
-
} = {}): Promise<boolean> {
|
|
336
|
+
export async function symlink(filePath: string, linkPath: string, type?: 'dir' | 'file' | 'junction', taskId?: number): Promise<boolean> {
|
|
308
337
|
filePath = tool.urlResolve('/', filePath);
|
|
309
338
|
linkPath = tool.urlResolve('/', linkPath);
|
|
310
339
|
if (filePath.startsWith('/clickgo/')) {
|
|
311
340
|
return false;
|
|
312
341
|
}
|
|
313
|
-
else if (filePath.startsWith('/storage/')) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
else if (filePath.startsWith('/mounted/')) {
|
|
318
|
-
const fname = getMountName(filePath);
|
|
319
|
-
const lname = getMountName(linkPath);
|
|
320
|
-
if (fname !== lname) {
|
|
342
|
+
else if (filePath.startsWith('/storage/') || filePath.startsWith('/mounted/')) {
|
|
343
|
+
const r = await task.checkPermission('fs.' + filePath + 'w', false, undefined, taskId);
|
|
344
|
+
if (!r[0]) {
|
|
321
345
|
return false;
|
|
322
346
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
347
|
+
if (filePath.startsWith('/mounted/')) {
|
|
348
|
+
const fname = getMountName(filePath);
|
|
349
|
+
const lname = getMountName(linkPath);
|
|
350
|
+
if (fname !== lname) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
const hanlder = mounts[fname];
|
|
354
|
+
if (!hanlder) {
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
return hanlder.symlink?.(filePath.slice(9 + fname.length), linkPath.slice(9 + fname.length), type) ?? false;
|
|
326
358
|
}
|
|
327
|
-
|
|
359
|
+
// --- storage ---
|
|
360
|
+
return native.invoke('cg-fs-symlink', native.getToken(), filePath.slice(8), linkPath.slice(8), type);
|
|
328
361
|
}
|
|
329
362
|
else if (filePath.startsWith('/package/')) {
|
|
330
363
|
return false;
|
|
331
364
|
}
|
|
332
365
|
else if (filePath.startsWith('/current/')) {
|
|
333
|
-
if (!
|
|
366
|
+
if (!taskId) {
|
|
334
367
|
return false;
|
|
335
368
|
}
|
|
336
|
-
const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
|
|
337
369
|
if (linkPath.startsWith('/current/')) {
|
|
338
|
-
linkPath = current + linkPath.slice(8);
|
|
370
|
+
linkPath = task.list[taskId].current + linkPath.slice(8);
|
|
339
371
|
}
|
|
340
|
-
return symlink(current + filePath.slice(8), linkPath,
|
|
372
|
+
return symlink(task.list[taskId].current + filePath.slice(8), linkPath, type, taskId);
|
|
341
373
|
}
|
|
342
374
|
else {
|
|
343
375
|
return false;
|
|
@@ -347,36 +379,38 @@ export async function symlink(filePath: string, linkPath: string, options: {
|
|
|
347
379
|
/**
|
|
348
380
|
* --- 删除一个文件 ---
|
|
349
381
|
* @param path 要删除的文件路径
|
|
382
|
+
* @param taskId App 模式下无效
|
|
350
383
|
*/
|
|
351
|
-
export async function unlink(path: string,
|
|
352
|
-
'current'?: string;
|
|
353
|
-
} = {}): Promise<boolean> {
|
|
384
|
+
export async function unlink(path: string, taskId?: number): Promise<boolean> {
|
|
354
385
|
path = tool.urlResolve('/', path);
|
|
355
386
|
const fpath = path.slice(8);
|
|
356
387
|
if (path.startsWith('/clickgo/')) {
|
|
357
388
|
return false;
|
|
358
389
|
}
|
|
359
|
-
else if (path.startsWith('/storage/')) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
else if (path.startsWith('/mounted/')) {
|
|
364
|
-
const name = getMountName(path);
|
|
365
|
-
const hanlder = mounts[name];
|
|
366
|
-
if (!hanlder) {
|
|
390
|
+
else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
|
|
391
|
+
const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
|
|
392
|
+
if (!r[0]) {
|
|
367
393
|
return false;
|
|
368
394
|
}
|
|
369
|
-
|
|
395
|
+
if (path.startsWith('/mounted/')) {
|
|
396
|
+
const name = getMountName(path);
|
|
397
|
+
const hanlder = mounts[name];
|
|
398
|
+
if (!hanlder) {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
return hanlder.unlink?.(path.slice(9 + name.length)) ?? false;
|
|
402
|
+
}
|
|
403
|
+
// --- storage ---
|
|
404
|
+
return native.invoke('cg-fs-unlink', native.getToken(), fpath);
|
|
370
405
|
}
|
|
371
406
|
else if (path.startsWith('/package/')) {
|
|
372
407
|
return false;
|
|
373
408
|
}
|
|
374
409
|
else if (path.startsWith('/current/')) {
|
|
375
|
-
if (!
|
|
410
|
+
if (!taskId) {
|
|
376
411
|
return false;
|
|
377
412
|
}
|
|
378
|
-
|
|
379
|
-
return unlink(current + fpath, options);
|
|
413
|
+
return unlink(task.list[taskId].current + fpath, taskId);
|
|
380
414
|
}
|
|
381
415
|
else {
|
|
382
416
|
return false;
|
|
@@ -420,7 +454,11 @@ async function getClickGoStats(path: string): Promise<types.IStats | null> {
|
|
|
420
454
|
});
|
|
421
455
|
const hdate = res.headers.get('date');
|
|
422
456
|
const hmdate = res.headers.get('last-modified');
|
|
423
|
-
|
|
457
|
+
let hlength = res.headers.get('content-range');
|
|
458
|
+
if (hlength) {
|
|
459
|
+
const lio = hlength.lastIndexOf('/');
|
|
460
|
+
hlength = hlength.slice(lio + 1);
|
|
461
|
+
}
|
|
424
462
|
let date = new Date();
|
|
425
463
|
let mdate = date;
|
|
426
464
|
if (hdate) {
|
|
@@ -462,13 +500,63 @@ async function getClickGoStats(path: string): Promise<types.IStats | null> {
|
|
|
462
500
|
/**
|
|
463
501
|
* --- 获取对象是否存在,存在则返回 stats 对象,否则返回 null ---
|
|
464
502
|
* @param path 对象路径
|
|
465
|
-
* @param
|
|
503
|
+
* @param taskId App 模式下无效
|
|
466
504
|
*/
|
|
467
|
-
export async function stats(path: string,
|
|
468
|
-
'files'?: Record<string, Blob | string>;
|
|
469
|
-
'current'?: string;
|
|
470
|
-
} = {}): Promise<types.IStats | null> {
|
|
505
|
+
export async function stats(path: string, taskId?: number): Promise<types.IStats | null> {
|
|
471
506
|
path = tool.urlResolve('/', path);
|
|
507
|
+
if (path.endsWith('/')) {
|
|
508
|
+
path = path.slice(0, -1);
|
|
509
|
+
}
|
|
510
|
+
if (['', '/clickgo', '/storage', '/mounted', '/package'].includes(path)) {
|
|
511
|
+
const date = new Date();
|
|
512
|
+
const ms = date.getTime();
|
|
513
|
+
return {
|
|
514
|
+
isFile: function() {
|
|
515
|
+
return false;
|
|
516
|
+
},
|
|
517
|
+
isDirectory: function() {
|
|
518
|
+
return true;
|
|
519
|
+
},
|
|
520
|
+
isSymbolicLink: function() {
|
|
521
|
+
return false;
|
|
522
|
+
},
|
|
523
|
+
'size': 0,
|
|
524
|
+
'blksize': 0,
|
|
525
|
+
'atimeMs': ms,
|
|
526
|
+
'mtimeMs': ms,
|
|
527
|
+
'ctimeMs': ms,
|
|
528
|
+
'birthtimeMs': ms,
|
|
529
|
+
'atime': date,
|
|
530
|
+
'mtime': date,
|
|
531
|
+
'ctime': date,
|
|
532
|
+
'birthtime': date
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
if (path === '/current') {
|
|
536
|
+
const date = new Date();
|
|
537
|
+
const ms = date.getTime();
|
|
538
|
+
return {
|
|
539
|
+
isFile: function() {
|
|
540
|
+
return false;
|
|
541
|
+
},
|
|
542
|
+
isDirectory: function() {
|
|
543
|
+
return false;
|
|
544
|
+
},
|
|
545
|
+
isSymbolicLink: function() {
|
|
546
|
+
return true;
|
|
547
|
+
},
|
|
548
|
+
'size': 0,
|
|
549
|
+
'blksize': 0,
|
|
550
|
+
'atimeMs': ms,
|
|
551
|
+
'mtimeMs': ms,
|
|
552
|
+
'ctimeMs': ms,
|
|
553
|
+
'birthtimeMs': ms,
|
|
554
|
+
'atime': date,
|
|
555
|
+
'mtime': date,
|
|
556
|
+
'ctime': date,
|
|
557
|
+
'birthtime': date
|
|
558
|
+
};
|
|
559
|
+
}
|
|
472
560
|
let fpath = path.slice(8);
|
|
473
561
|
if (path.startsWith('/clickgo/')) {
|
|
474
562
|
if (!clickgoFiles.includes(fpath)) {
|
|
@@ -480,8 +568,35 @@ export async function stats(path: string, options: {
|
|
|
480
568
|
return getClickGoStats(fpath);
|
|
481
569
|
}
|
|
482
570
|
else if (path.startsWith('/storage/')) {
|
|
483
|
-
|
|
484
|
-
|
|
571
|
+
const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
|
|
572
|
+
if (!r[0]) {
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
575
|
+
const item = await native.invoke('cg-fs-stats', native.getToken(), fpath);
|
|
576
|
+
if (!item) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
isFile: function() {
|
|
581
|
+
return item.isFile;
|
|
582
|
+
},
|
|
583
|
+
isDirectory: function() {
|
|
584
|
+
return item.isDirectory;
|
|
585
|
+
},
|
|
586
|
+
isSymbolicLink: function() {
|
|
587
|
+
return item.isSymbolicLink;
|
|
588
|
+
},
|
|
589
|
+
'size': item.size,
|
|
590
|
+
'blksize': item.blksize,
|
|
591
|
+
'atimeMs': item.atimeMs,
|
|
592
|
+
'mtimeMs': item.mtimeMs,
|
|
593
|
+
'ctimeMs': item.ctimeMs,
|
|
594
|
+
'birthtimeMs': item.birthtimeMs,
|
|
595
|
+
'atime': item.atime,
|
|
596
|
+
'mtime': item.mtime,
|
|
597
|
+
'ctime': item.ctime,
|
|
598
|
+
'birthtime': item.birthtime
|
|
599
|
+
};
|
|
485
600
|
}
|
|
486
601
|
else if (path.startsWith('/mounted/')) {
|
|
487
602
|
const name = getMountName(path);
|
|
@@ -489,15 +604,46 @@ export async function stats(path: string, options: {
|
|
|
489
604
|
if (!hanlder) {
|
|
490
605
|
return null;
|
|
491
606
|
}
|
|
492
|
-
|
|
607
|
+
if (path === '/mounted/' + name) {
|
|
608
|
+
const ms = hanlder.date!.getTime();
|
|
609
|
+
return {
|
|
610
|
+
isFile: function() {
|
|
611
|
+
return false;
|
|
612
|
+
},
|
|
613
|
+
isDirectory: function() {
|
|
614
|
+
return true;
|
|
615
|
+
},
|
|
616
|
+
isSymbolicLink: function() {
|
|
617
|
+
return false;
|
|
618
|
+
},
|
|
619
|
+
'size': 0,
|
|
620
|
+
'blksize': 0,
|
|
621
|
+
'atimeMs': ms,
|
|
622
|
+
'mtimeMs': ms,
|
|
623
|
+
'ctimeMs': ms,
|
|
624
|
+
'birthtimeMs': ms,
|
|
625
|
+
'atime': hanlder.date!,
|
|
626
|
+
'mtime': hanlder.date!,
|
|
627
|
+
'ctime': hanlder.date!,
|
|
628
|
+
'birthtime': hanlder.date!
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
|
|
632
|
+
if (!r[0]) {
|
|
633
|
+
return null;
|
|
634
|
+
}
|
|
635
|
+
return hanlder.stats?.(path.slice(9 + name.length)) ?? null;
|
|
493
636
|
}
|
|
494
|
-
else if (path.startsWith('/package/')) {
|
|
495
|
-
if (!
|
|
637
|
+
else if (path.startsWith('/package/') || path.startsWith('/current/')) {
|
|
638
|
+
if (!taskId) {
|
|
496
639
|
return null;
|
|
497
640
|
}
|
|
498
|
-
if (
|
|
641
|
+
if (path.startsWith('/current/')) {
|
|
642
|
+
return stats(task.list[taskId].current + fpath, taskId);
|
|
643
|
+
}
|
|
644
|
+
if (task.list[taskId].app.files[fpath]) {
|
|
499
645
|
// --- 文件 ---
|
|
500
|
-
const file =
|
|
646
|
+
const file = task.list[taskId].app.files[fpath];
|
|
501
647
|
const date = new Date();
|
|
502
648
|
const ms = date.getTime();
|
|
503
649
|
let size = 0;
|
|
@@ -533,7 +679,7 @@ export async function stats(path: string, options: {
|
|
|
533
679
|
if (!fpath.endsWith('/')) {
|
|
534
680
|
fpath += '/';
|
|
535
681
|
}
|
|
536
|
-
for (const p in
|
|
682
|
+
for (const p in task.list[taskId].app.files) {
|
|
537
683
|
if (!p.startsWith(fpath)) {
|
|
538
684
|
continue;
|
|
539
685
|
}
|
|
@@ -564,13 +710,6 @@ export async function stats(path: string, options: {
|
|
|
564
710
|
}
|
|
565
711
|
return null;
|
|
566
712
|
}
|
|
567
|
-
else if (path.startsWith('/current/')) {
|
|
568
|
-
if (!options.current) {
|
|
569
|
-
return null;
|
|
570
|
-
}
|
|
571
|
-
const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
|
|
572
|
-
return stats(current + fpath, options);
|
|
573
|
-
}
|
|
574
713
|
else {
|
|
575
714
|
return null;
|
|
576
715
|
}
|
|
@@ -578,15 +717,12 @@ export async function stats(path: string, options: {
|
|
|
578
717
|
|
|
579
718
|
/**
|
|
580
719
|
* --- 判断是否是目录或目录是否存在,是的话返回 stats ---
|
|
581
|
-
* @param path 判断路径
|
|
582
|
-
* @param
|
|
720
|
+
* @param path 判断路径woml
|
|
721
|
+
* @param taskId App 模式下无效
|
|
583
722
|
*/
|
|
584
|
-
export async function isDir(path: string,
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
} = {}): Promise<types.IStats | false> {
|
|
588
|
-
const pstats = await stats(path, options);
|
|
589
|
-
if (!pstats || !pstats.isDirectory()) {
|
|
723
|
+
export async function isDir(path: string, taskId?: number): Promise<types.IStats | false> {
|
|
724
|
+
const pstats = await stats(path, taskId);
|
|
725
|
+
if (!pstats?.isDirectory()) {
|
|
590
726
|
return false;
|
|
591
727
|
}
|
|
592
728
|
return pstats;
|
|
@@ -595,13 +731,11 @@ export async function isDir(path: string, options: {
|
|
|
595
731
|
/**
|
|
596
732
|
* --- 判断是否是文件或文件是否存在,是的话返回 stats ---
|
|
597
733
|
* @param path 判断路径
|
|
734
|
+
* @param taskId App 模式下无效
|
|
598
735
|
*/
|
|
599
|
-
export async function isFile(path: string,
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
} = {}): Promise<types.IStats | false> {
|
|
603
|
-
const pstats = await stats(path, options);
|
|
604
|
-
if (!pstats || !pstats.isFile()) {
|
|
736
|
+
export async function isFile(path: string, taskId?: number): Promise<types.IStats | false> {
|
|
737
|
+
const pstats = await stats(path, taskId);
|
|
738
|
+
if (!pstats?.isFile()) {
|
|
605
739
|
return false;
|
|
606
740
|
}
|
|
607
741
|
return pstats;
|
|
@@ -611,39 +745,41 @@ export async function isFile(path: string, options: {
|
|
|
611
745
|
* --- 深度创建目录,如果最末目录存在,则自动创建成功 ---
|
|
612
746
|
* @param path 要创建的路径,如 /a/b/c/
|
|
613
747
|
* @param mode 权限
|
|
748
|
+
* @param taskId App 模式下无效
|
|
614
749
|
*/
|
|
615
|
-
export async function mkdir(path: string, mode: number = 0o755,
|
|
616
|
-
'current'?: string;
|
|
617
|
-
} = {}): Promise<boolean> {
|
|
750
|
+
export async function mkdir(path: string, mode: number = 0o755, taskId?: number): Promise<boolean> {
|
|
618
751
|
path = tool.urlResolve('/', path);
|
|
619
|
-
if (await isDir(path,
|
|
752
|
+
if (await isDir(path, taskId)) {
|
|
620
753
|
return true;
|
|
621
754
|
}
|
|
622
755
|
const fpath = path.slice(8);
|
|
623
756
|
if (path.startsWith('/clickgo/')) {
|
|
624
757
|
return false;
|
|
625
758
|
}
|
|
626
|
-
else if (path.startsWith('/storage/')) {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
else if (path.startsWith('/mounted/')) {
|
|
631
|
-
const name = getMountName(path);
|
|
632
|
-
const hanlder = mounts[name];
|
|
633
|
-
if (!hanlder) {
|
|
759
|
+
else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
|
|
760
|
+
const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
|
|
761
|
+
if (!r[0]) {
|
|
634
762
|
return false;
|
|
635
763
|
}
|
|
636
|
-
|
|
764
|
+
if (path.startsWith('/mounted/')) {
|
|
765
|
+
const name = getMountName(path);
|
|
766
|
+
const hanlder = mounts[name];
|
|
767
|
+
if (!hanlder) {
|
|
768
|
+
return false;
|
|
769
|
+
}
|
|
770
|
+
return hanlder.mkdir?.(path.slice(9 + name.length), mode) ?? false;
|
|
771
|
+
}
|
|
772
|
+
// --- storage ---
|
|
773
|
+
return native.invoke('cg-fs-mkdir', native.getToken(), fpath, mode);
|
|
637
774
|
}
|
|
638
775
|
else if (path.startsWith('/package/')) {
|
|
639
776
|
return false;
|
|
640
777
|
}
|
|
641
778
|
else if (path.startsWith('/current/')) {
|
|
642
|
-
if (!
|
|
779
|
+
if (!taskId) {
|
|
643
780
|
return false;
|
|
644
781
|
}
|
|
645
|
-
|
|
646
|
-
return mkdir(current + fpath, mode, options);
|
|
782
|
+
return mkdir(task.list[taskId].current + fpath, mode, taskId);
|
|
647
783
|
}
|
|
648
784
|
else {
|
|
649
785
|
return false;
|
|
@@ -653,36 +789,38 @@ export async function mkdir(path: string, mode: number = 0o755, options: {
|
|
|
653
789
|
/**
|
|
654
790
|
* --- 删除空目录 ---
|
|
655
791
|
* @param path 要删除的目录
|
|
792
|
+
* @param taskId App 模式下无效
|
|
656
793
|
*/
|
|
657
|
-
export async function rmdir(path: string,
|
|
658
|
-
'current'?: string;
|
|
659
|
-
} = {}): Promise<boolean> {
|
|
794
|
+
export async function rmdir(path: string, taskId?: number): Promise<boolean> {
|
|
660
795
|
path = tool.urlResolve('/', path);
|
|
661
796
|
const fpath = path.slice(8);
|
|
662
797
|
if (path.startsWith('/clickgo/')) {
|
|
663
798
|
return false;
|
|
664
799
|
}
|
|
665
|
-
else if (path.startsWith('/storage/')) {
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
}
|
|
669
|
-
else if (path.startsWith('/mounted/')) {
|
|
670
|
-
const name = getMountName(path);
|
|
671
|
-
const hanlder = mounts[name];
|
|
672
|
-
if (!hanlder) {
|
|
800
|
+
else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
|
|
801
|
+
const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
|
|
802
|
+
if (!r[0]) {
|
|
673
803
|
return false;
|
|
674
804
|
}
|
|
675
|
-
|
|
805
|
+
if (path.startsWith('/mounted/')) {
|
|
806
|
+
const name = getMountName(path);
|
|
807
|
+
const hanlder = mounts[name];
|
|
808
|
+
if (!hanlder) {
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
return hanlder.rmdir?.(path.slice(9 + name.length)) ?? false;
|
|
812
|
+
}
|
|
813
|
+
// --- storage ---
|
|
814
|
+
return native.invoke('cg-fs-rmdir', native.getToken(), fpath);
|
|
676
815
|
}
|
|
677
816
|
else if (path.startsWith('/package/')) {
|
|
678
817
|
return false;
|
|
679
818
|
}
|
|
680
819
|
else if (path.startsWith('/current/')) {
|
|
681
|
-
if (!
|
|
820
|
+
if (!taskId) {
|
|
682
821
|
return false;
|
|
683
822
|
}
|
|
684
|
-
|
|
685
|
-
return rmdir(current + fpath, options);
|
|
823
|
+
return rmdir(task.list[taskId].current + fpath, taskId);
|
|
686
824
|
}
|
|
687
825
|
else {
|
|
688
826
|
return false;
|
|
@@ -692,71 +830,73 @@ export async function rmdir(path: string, options: {
|
|
|
692
830
|
/**
|
|
693
831
|
* --- 删除一个非空目录 ---
|
|
694
832
|
* --- [ Danger ] [ 危险 ] ---
|
|
833
|
+
* @param path 目录路径
|
|
834
|
+
* @param taskId App 模式下无效
|
|
695
835
|
*/
|
|
696
|
-
export async function rmdirDeep(path: string,
|
|
697
|
-
'current'?: string;
|
|
698
|
-
} = {}): Promise<boolean> {
|
|
836
|
+
export async function rmdirDeep(path: string, taskId?: number): Promise<boolean> {
|
|
699
837
|
path = tool.urlResolve('/', path);
|
|
700
838
|
if (!path.endsWith('/')) {
|
|
701
839
|
path += '/';
|
|
702
840
|
}
|
|
703
|
-
const list = await readDir(path,
|
|
841
|
+
const list = await readDir(path, undefined, taskId);
|
|
704
842
|
for (const item of list) {
|
|
705
|
-
const stat = await stats(path + item.name,
|
|
843
|
+
const stat = await stats(path + item.name, taskId);
|
|
706
844
|
if (!stat) {
|
|
707
845
|
return false;
|
|
708
846
|
}
|
|
709
847
|
if (stat.isDirectory()) {
|
|
710
848
|
// --- 目录 ---
|
|
711
|
-
const rtn = await rmdirDeep(path + item.name,
|
|
849
|
+
const rtn = await rmdirDeep(path + item.name, taskId);
|
|
712
850
|
if (!rtn) {
|
|
713
851
|
return false;
|
|
714
852
|
}
|
|
715
853
|
}
|
|
716
854
|
else {
|
|
717
|
-
const rtn = await unlink(path + item.name,
|
|
855
|
+
const rtn = await unlink(path + item.name, taskId);
|
|
718
856
|
if (!rtn) {
|
|
719
857
|
return false;
|
|
720
858
|
}
|
|
721
859
|
}
|
|
722
860
|
}
|
|
723
|
-
return rmdir(path,
|
|
861
|
+
return rmdir(path, taskId);
|
|
724
862
|
}
|
|
725
863
|
|
|
726
864
|
/**
|
|
727
|
-
* --- 修改权限
|
|
865
|
+
* --- 修改权限 ---
|
|
728
866
|
* @param path 要修改的路径
|
|
729
867
|
* @param mod 权限
|
|
868
|
+
* @param taskId App 模式下无效
|
|
730
869
|
*/
|
|
731
|
-
export async function chmod(path: string, mod: string | number,
|
|
732
|
-
'current'?: string;
|
|
733
|
-
} = {}): Promise<boolean> {
|
|
870
|
+
export async function chmod(path: string, mod: string | number, taskId?: number): Promise<boolean> {
|
|
734
871
|
path = tool.urlResolve('/', path);
|
|
735
872
|
const fpath = path.slice(8);
|
|
736
873
|
if (path.startsWith('/clickgo/')) {
|
|
737
874
|
return false;
|
|
738
875
|
}
|
|
739
|
-
else if (path.startsWith('/storage/')) {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}
|
|
743
|
-
else if (path.startsWith('/mounted/')) {
|
|
744
|
-
const name = getMountName(path);
|
|
745
|
-
const hanlder = mounts[name];
|
|
746
|
-
if (!hanlder) {
|
|
876
|
+
else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
|
|
877
|
+
const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
|
|
878
|
+
if (!r[0]) {
|
|
747
879
|
return false;
|
|
748
880
|
}
|
|
749
|
-
|
|
881
|
+
if (path.startsWith('/mounted/')) {
|
|
882
|
+
const name = getMountName(path);
|
|
883
|
+
const hanlder = mounts[name];
|
|
884
|
+
if (!hanlder) {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
return hanlder.chmod?.(path.slice(9 + name.length), mod) ?? false;
|
|
888
|
+
}
|
|
889
|
+
// --- storage ---
|
|
890
|
+
return native.invoke('cg-fs-chmod', native.getToken(), fpath, mod);
|
|
750
891
|
}
|
|
751
892
|
else if (path.startsWith('/package/')) {
|
|
752
893
|
return false;
|
|
753
894
|
}
|
|
754
895
|
else if (path.startsWith('/current/')) {
|
|
755
|
-
if (!
|
|
896
|
+
if (!taskId) {
|
|
756
897
|
return false;
|
|
757
898
|
}
|
|
758
|
-
|
|
759
|
-
return chmod(current + fpath, mod, options);
|
|
899
|
+
return chmod(task.list[taskId].current + fpath, mod, taskId);
|
|
760
900
|
}
|
|
761
901
|
else {
|
|
762
902
|
return false;
|
|
@@ -768,9 +908,7 @@ export async function chmod(path: string, mod: string | number, options: {
|
|
|
768
908
|
* @param oldPath 老名
|
|
769
909
|
* @param newPath 新名
|
|
770
910
|
*/
|
|
771
|
-
export async function rename(oldPath: string, newPath: string,
|
|
772
|
-
'current'?: string;
|
|
773
|
-
} = {}): Promise<boolean> {
|
|
911
|
+
export async function rename(oldPath: string, newPath: string, taskId?: number): Promise<boolean> {
|
|
774
912
|
oldPath = tool.urlResolve('/', oldPath);
|
|
775
913
|
newPath = tool.urlResolve('/', newPath);
|
|
776
914
|
if (!oldPath.startsWith(newPath.slice(0, 9))) {
|
|
@@ -781,23 +919,38 @@ export async function rename(oldPath: string, newPath: string, options: {
|
|
|
781
919
|
if (oldPath.startsWith('/clickgo/')) {
|
|
782
920
|
return false;
|
|
783
921
|
}
|
|
784
|
-
else if (oldPath.startsWith('/storage/')) {
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
922
|
+
else if (oldPath.startsWith('/storage/') || oldPath.startsWith('/mounted/')) {
|
|
923
|
+
let r = await task.checkPermission('fs.' + oldPath + 'w', false, undefined, taskId);
|
|
924
|
+
if (!r[0]) {
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
r = await task.checkPermission('fs.' + newPath + 'w', false, undefined, taskId);
|
|
928
|
+
if (!r[0]) {
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
if (oldPath.startsWith('/mounted/')) {
|
|
932
|
+
const fname = getMountName(oldPath);
|
|
933
|
+
const lname = getMountName(newPath);
|
|
934
|
+
if (fname !== lname) {
|
|
935
|
+
return false;
|
|
936
|
+
}
|
|
937
|
+
const hanlder = mounts[fname];
|
|
938
|
+
if (!hanlder) {
|
|
939
|
+
return false;
|
|
940
|
+
}
|
|
941
|
+
return hanlder.rename?.(oldPath.slice(9 + fname.length), newPath.slice(9 + fname.length)) ?? false;
|
|
942
|
+
}
|
|
943
|
+
// --- storage ---
|
|
944
|
+
return native.invoke('cg-fs-rename', native.getToken(), ofpath, nfpath);
|
|
791
945
|
}
|
|
792
946
|
else if (oldPath.startsWith('/package/')) {
|
|
793
947
|
return false;
|
|
794
948
|
}
|
|
795
949
|
else if (oldPath.startsWith('/current/')) {
|
|
796
|
-
if (!
|
|
950
|
+
if (!taskId) {
|
|
797
951
|
return false;
|
|
798
952
|
}
|
|
799
|
-
|
|
800
|
-
return rename(current + ofpath, current + nfpath, options);
|
|
953
|
+
return rename(task.list[taskId].current + ofpath, task.list[taskId].current + nfpath, taskId);
|
|
801
954
|
}
|
|
802
955
|
else {
|
|
803
956
|
return false;
|
|
@@ -807,12 +960,10 @@ export async function rename(oldPath: string, newPath: string, options: {
|
|
|
807
960
|
/**
|
|
808
961
|
* --- 获取文件夹下文件列表 ---
|
|
809
962
|
* @param path 文件夹路径
|
|
963
|
+
* @param encoding 编码
|
|
964
|
+
* @param taskId App 模式下无效
|
|
810
965
|
*/
|
|
811
|
-
export async function readDir(path: string,
|
|
812
|
-
'encoding'?: BufferEncoding;
|
|
813
|
-
'files'?: Record<string, Blob | string>;
|
|
814
|
-
'current'?: string;
|
|
815
|
-
} = {}): Promise<types.IDirent[]> {
|
|
966
|
+
export async function readDir(path: string, encoding?: BufferEncoding, taskId?: number): Promise<types.IDirent[]> {
|
|
816
967
|
path = tool.urlResolve('/', path);
|
|
817
968
|
if (path === '/') {
|
|
818
969
|
const list = [
|
|
@@ -853,7 +1004,7 @@ export async function readDir(path: string, options: {
|
|
|
853
1004
|
'name': 'mounted'
|
|
854
1005
|
}
|
|
855
1006
|
];
|
|
856
|
-
if (
|
|
1007
|
+
if (taskId) {
|
|
857
1008
|
list.push({
|
|
858
1009
|
isFile: function() {
|
|
859
1010
|
return false;
|
|
@@ -867,7 +1018,7 @@ export async function readDir(path: string, options: {
|
|
|
867
1018
|
'name': 'package'
|
|
868
1019
|
});
|
|
869
1020
|
}
|
|
870
|
-
if (
|
|
1021
|
+
if (taskId) {
|
|
871
1022
|
list.push({
|
|
872
1023
|
isFile: function() {
|
|
873
1024
|
return false;
|
|
@@ -937,21 +1088,65 @@ export async function readDir(path: string, options: {
|
|
|
937
1088
|
}
|
|
938
1089
|
return list;
|
|
939
1090
|
}
|
|
940
|
-
else if (path.startsWith('/storage/')) {
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
1091
|
+
else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
|
|
1092
|
+
const list: types.IDirent[] = [];
|
|
1093
|
+
if (path === '/mounted/') {
|
|
1094
|
+
for (const name in mounts) {
|
|
1095
|
+
list.push({
|
|
1096
|
+
isFile: function() {
|
|
1097
|
+
return false;
|
|
1098
|
+
},
|
|
1099
|
+
isDirectory: function() {
|
|
1100
|
+
return true;
|
|
1101
|
+
},
|
|
1102
|
+
isSymbolicLink: function() {
|
|
1103
|
+
return false;
|
|
1104
|
+
},
|
|
1105
|
+
'name': name
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
return list;
|
|
1109
|
+
}
|
|
1110
|
+
const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
|
|
1111
|
+
if (!r[0]) {
|
|
1112
|
+
return [];
|
|
1113
|
+
}
|
|
1114
|
+
if (path.startsWith('/mounted/')) {
|
|
1115
|
+
const name = getMountName(path);
|
|
1116
|
+
const hanlder = mounts[name];
|
|
1117
|
+
if (!hanlder) {
|
|
1118
|
+
return [];
|
|
1119
|
+
}
|
|
1120
|
+
return hanlder.readDir?.(path.slice(9 + name.length), encoding) ?? [];
|
|
1121
|
+
}
|
|
1122
|
+
// --- storage ---
|
|
1123
|
+
const ls = await native.invoke('cg-fs-readDir', native.getToken(), fpath, encoding);
|
|
1124
|
+
for (const item of ls) {
|
|
1125
|
+
list.push({
|
|
1126
|
+
isFile: function() {
|
|
1127
|
+
return item.isFile;
|
|
1128
|
+
},
|
|
1129
|
+
isDirectory: function() {
|
|
1130
|
+
return item.isDirectory;
|
|
1131
|
+
},
|
|
1132
|
+
isSymbolicLink: function() {
|
|
1133
|
+
return item.isSymbolicLink;
|
|
1134
|
+
},
|
|
1135
|
+
'name': item.name
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
return list;
|
|
947
1139
|
}
|
|
948
|
-
else if (path.startsWith('/package/')) {
|
|
949
|
-
if (!
|
|
1140
|
+
else if (path.startsWith('/package/') || path.startsWith('/current/')) {
|
|
1141
|
+
if (!taskId) {
|
|
950
1142
|
return [];
|
|
951
1143
|
}
|
|
1144
|
+
if (path.startsWith('/current/')) {
|
|
1145
|
+
return readDir(task.list[taskId].current + fpath, encoding, taskId);
|
|
1146
|
+
}
|
|
952
1147
|
const list: types.IDirent[] = [];
|
|
953
1148
|
const dirs: string[] = [];
|
|
954
|
-
for (const p in
|
|
1149
|
+
for (const p in task.list[taskId].app.files) {
|
|
955
1150
|
if (!p.startsWith(fpath)) {
|
|
956
1151
|
continue;
|
|
957
1152
|
}
|
|
@@ -993,13 +1188,6 @@ export async function readDir(path: string, options: {
|
|
|
993
1188
|
}
|
|
994
1189
|
return list;
|
|
995
1190
|
}
|
|
996
|
-
else if (path.startsWith('/current/')) {
|
|
997
|
-
if (!options.current) {
|
|
998
|
-
return [];
|
|
999
|
-
}
|
|
1000
|
-
const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
|
|
1001
|
-
return readDir(current + fpath, options);
|
|
1002
|
-
}
|
|
1003
1191
|
else {
|
|
1004
1192
|
return [];
|
|
1005
1193
|
}
|
|
@@ -1009,53 +1197,56 @@ export async function readDir(path: string, options: {
|
|
|
1009
1197
|
* --- 复制文件夹里的内容到另一个地方,失败不会回滚 ---
|
|
1010
1198
|
* @param from 源,末尾加 /
|
|
1011
1199
|
* @param to 目标,末尾加 /
|
|
1012
|
-
* @param
|
|
1200
|
+
* @param ignore 忽略的文件
|
|
1201
|
+
* @param taskId App 模式下无效
|
|
1013
1202
|
*/
|
|
1014
|
-
export async function copyFolder(from: string, to: string,
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
from = tool.urlResolve('/', from);
|
|
1019
|
-
to = tool.urlResolve('/', to);
|
|
1020
|
-
if (!from.startsWith(to.slice(0, 9))) {
|
|
1021
|
-
return 0;
|
|
1022
|
-
}
|
|
1023
|
-
const ffpath = from.slice(8);
|
|
1024
|
-
const tfpath = from.slice(8);
|
|
1025
|
-
if (from.startsWith('/clickgo/')) {
|
|
1026
|
-
return 0;
|
|
1027
|
-
}
|
|
1028
|
-
else if (from.startsWith('/storage/')) {
|
|
1029
|
-
// --- TODO ---
|
|
1030
|
-
return 0;
|
|
1031
|
-
}
|
|
1032
|
-
else if (from.startsWith('/mounted/')) {
|
|
1033
|
-
// --- TODO ---
|
|
1034
|
-
return 0;
|
|
1035
|
-
}
|
|
1036
|
-
else if (from.startsWith('/package/')) {
|
|
1203
|
+
export async function copyFolder(from: string, to: string, ignore: RegExp[] = [], taskId?: number): Promise<number> {
|
|
1204
|
+
let num = 0;
|
|
1205
|
+
// --- 如果源目录不存在或不是目录,则直接成功 :) ---
|
|
1206
|
+
if (!await isDir(from, taskId)) {
|
|
1037
1207
|
return 0;
|
|
1038
1208
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1209
|
+
// --- 遍历源目录文件和文件夹,准备复制 ---
|
|
1210
|
+
const flist = await readDir(from, undefined, taskId);
|
|
1211
|
+
/** --- to 目录是否检查是否存在,空目录不复制,所以确定有 item file 的时候才创建 --- */
|
|
1212
|
+
let checkTo = false;
|
|
1213
|
+
for (const item of flist) {
|
|
1214
|
+
if (item.isDirectory()) {
|
|
1215
|
+
const r = await copyFolder(from + item.name + '/', to + item.name + '/', ignore, taskId);
|
|
1216
|
+
if (r === -1) {
|
|
1217
|
+
return r;
|
|
1218
|
+
}
|
|
1219
|
+
else {
|
|
1220
|
+
num += r;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
else if (item.isFile()) {
|
|
1224
|
+
// --- 先判断本文件是否被排除 ---
|
|
1225
|
+
if (ignore.length > 0 && tool.match(item.name, ignore)) {
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
if (!checkTo) {
|
|
1229
|
+
if (!await mkdir(to, undefined, taskId)) {
|
|
1230
|
+
return -1;
|
|
1231
|
+
}
|
|
1232
|
+
checkTo = true;
|
|
1233
|
+
}
|
|
1234
|
+
if (!(await copyFile(from + item.name, to + item.name, taskId))) {
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
++num;
|
|
1042
1238
|
}
|
|
1043
|
-
const current = options.current.endsWith('/') ? options.current.slice(0, -1) : options.current;
|
|
1044
|
-
return copyFolder(current + ffpath, current + tfpath, options);
|
|
1045
|
-
}
|
|
1046
|
-
else {
|
|
1047
|
-
return 0;
|
|
1048
1239
|
}
|
|
1240
|
+
return num;
|
|
1049
1241
|
}
|
|
1050
1242
|
|
|
1051
1243
|
/**
|
|
1052
1244
|
* --- 复制文件 ---
|
|
1053
1245
|
* @param src 源文件
|
|
1054
1246
|
* @param dest 目标文件
|
|
1247
|
+
* @param taskId App 模式下无效
|
|
1055
1248
|
*/
|
|
1056
|
-
export async function copyFile(src: string, dest: string,
|
|
1057
|
-
'current'?: string;
|
|
1058
|
-
} = {}): Promise<boolean> {
|
|
1249
|
+
export async function copyFile(src: string, dest: string, taskId?: number): Promise<boolean> {
|
|
1059
1250
|
src = tool.urlResolve('/', src);
|
|
1060
1251
|
dest = tool.urlResolve('/', dest);
|
|
1061
1252
|
if (!src.startsWith(dest.slice(0, 9))) {
|
|
@@ -1066,23 +1257,38 @@ export async function copyFile(src: string, dest: string, options: {
|
|
|
1066
1257
|
if (src.startsWith('/clickgo/')) {
|
|
1067
1258
|
return false;
|
|
1068
1259
|
}
|
|
1069
|
-
else if (src.startsWith('/storage/')) {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1260
|
+
else if (src.startsWith('/storage/') || dest.startsWith('/mounted/')) {
|
|
1261
|
+
let r = await task.checkPermission('fs.' + src + 'r', false, undefined, taskId);
|
|
1262
|
+
if (!r[0]) {
|
|
1263
|
+
return false;
|
|
1264
|
+
}
|
|
1265
|
+
r = await task.checkPermission('fs.' + dest + 'w', false, undefined, taskId);
|
|
1266
|
+
if (!r[0]) {
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
if (src.startsWith('/mounted/')) {
|
|
1270
|
+
const fname = getMountName(src);
|
|
1271
|
+
const lname = getMountName(dest);
|
|
1272
|
+
if (fname !== lname) {
|
|
1273
|
+
return false;
|
|
1274
|
+
}
|
|
1275
|
+
const hanlder = mounts[fname];
|
|
1276
|
+
if (!hanlder) {
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
return hanlder.copyFile?.(src.slice(9 + fname.length), dest.slice(9 + fname.length)) ?? false;
|
|
1280
|
+
}
|
|
1281
|
+
// --- storage ---
|
|
1282
|
+
return native.invoke('cg-fs-copyFile', native.getToken(), sfpath, dfpath);
|
|
1076
1283
|
}
|
|
1077
1284
|
else if (src.startsWith('/package/')) {
|
|
1078
1285
|
return false;
|
|
1079
1286
|
}
|
|
1080
1287
|
else if (src.startsWith('/current/')) {
|
|
1081
|
-
if (!
|
|
1288
|
+
if (!taskId) {
|
|
1082
1289
|
return false;
|
|
1083
1290
|
}
|
|
1084
|
-
|
|
1085
|
-
return copyFile(current + sfpath, current + dfpath, options);
|
|
1291
|
+
return copyFile(task.list[taskId].current + sfpath, task.list[taskId].current + dfpath, taskId);
|
|
1086
1292
|
}
|
|
1087
1293
|
else {
|
|
1088
1294
|
return false;
|