clickgo 3.16.16 → 3.16.18

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.
Files changed (63) hide show
  1. package/README.md +1 -1
  2. package/dist/app/demo/form/method/native/native.js +26 -0
  3. package/dist/app/demo/form/method/native/native.xml +2 -0
  4. package/dist/clickgo.d.ts +17 -0
  5. package/dist/clickgo.js +1 -1
  6. package/dist/control/arteditor.cgc +0 -0
  7. package/dist/control/box.cgc +0 -0
  8. package/dist/control/captcha.cgc +0 -0
  9. package/dist/control/common.cgc +0 -0
  10. package/dist/control/desc.cgc +0 -0
  11. package/dist/control/drawer.cgc +0 -0
  12. package/dist/control/echarts.cgc +0 -0
  13. package/dist/control/form.cgc +0 -0
  14. package/dist/control/iconview.cgc +0 -0
  15. package/dist/control/jodit.cgc +0 -0
  16. package/dist/control/map.cgc +0 -0
  17. package/dist/control/monaco.cgc +0 -0
  18. package/dist/control/mpegts.cgc +0 -0
  19. package/dist/control/nav.cgc +0 -0
  20. package/dist/control/page.cgc +0 -0
  21. package/dist/control/pdf.cgc +0 -0
  22. package/dist/control/property.cgc +0 -0
  23. package/dist/control/qrcode.cgc +0 -0
  24. package/dist/control/table.cgc +0 -0
  25. package/dist/control/task.cgc +0 -0
  26. package/dist/control/tplink.cgc +0 -0
  27. package/dist/control/tuieditor.cgc +0 -0
  28. package/dist/control/tuiviewer.cgc +0 -0
  29. package/dist/control/xterm.cgc +0 -0
  30. package/dist/index.d.ts +51 -0
  31. package/dist/lib/control.d.ts +53 -0
  32. package/dist/lib/core.d.ts +47 -0
  33. package/dist/lib/dom.d.ts +74 -0
  34. package/dist/lib/dom.js +7 -7
  35. package/dist/lib/form.d.ts +222 -0
  36. package/dist/lib/fs.d.ts +35 -0
  37. package/dist/lib/fs.js +2 -2
  38. package/dist/lib/native.d.ts +36 -0
  39. package/dist/lib/native.js +8 -0
  40. package/dist/lib/storage.d.ts +6 -0
  41. package/dist/lib/task.d.ts +32 -0
  42. package/dist/lib/task.js +7 -1
  43. package/dist/lib/theme.d.ts +8 -0
  44. package/dist/lib/tool.d.ts +120 -0
  45. package/dist/lib/zip.d.ts +40 -0
  46. package/dist/theme/blue.cgt +0 -0
  47. package/dist/theme/byterun.cgt +0 -0
  48. package/dist/theme/light.cgt +0 -0
  49. package/package.json +7 -5
  50. package/dist/clickgo.ts +0 -68
  51. package/dist/index.ts +0 -282
  52. package/dist/lib/control.ts +0 -751
  53. package/dist/lib/core.ts +0 -1145
  54. package/dist/lib/dom.ts +0 -2728
  55. package/dist/lib/form.ts +0 -3829
  56. package/dist/lib/fs.ts +0 -1324
  57. package/dist/lib/native.ts +0 -236
  58. package/dist/lib/storage.ts +0 -229
  59. package/dist/lib/task.ts +0 -2160
  60. package/dist/lib/theme.ts +0 -199
  61. package/dist/lib/tool.ts +0 -1278
  62. package/dist/lib/zip.ts +0 -444
  63. package/eslint.config.js +0 -22
package/dist/lib/fs.ts DELETED
@@ -1,1324 +0,0 @@
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
- import * as task from './task';
11
- import * as form from './form';
12
- import * as core from './core';
13
- import * as native from './native';
14
-
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/alayout2/', '/app/demo/form/control/alayout2/alayout2.js', '/app/demo/form/control/alayout2/alayout2.xml', '/app/demo/form/control/alert/', '/app/demo/form/control/alert/alert.js', '/app/demo/form/control/alert/alert.xml', '/app/demo/form/control/arteditor/', '/app/demo/form/control/arteditor/arteditor.js', '/app/demo/form/control/arteditor/arteditor.xml', '/app/demo/form/control/arteditor/img.js', '/app/demo/form/control/arteditor/img.xml', '/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/captcha/', '/app/demo/form/control/captcha/captcha.js', '/app/demo/form/control/captcha/captcha.xml', '/app/demo/form/control/check/', '/app/demo/form/control/check/check.js', '/app/demo/form/control/check/check.xml', '/app/demo/form/control/circle/', '/app/demo/form/control/circle/circle.xml', '/app/demo/form/control/content/', '/app/demo/form/control/content/content.js', '/app/demo/form/control/content/content.xml', '/app/demo/form/control/date/', '/app/demo/form/control/date/date.js', '/app/demo/form/control/date/date.xml', '/app/demo/form/control/datepanel/', '/app/demo/form/control/datepanel/datepanel.js', '/app/demo/form/control/datepanel/datepanel.xml', '/app/demo/form/control/daterange/', '/app/demo/form/control/daterange/daterange.js', '/app/demo/form/control/daterange/daterange.xml', '/app/demo/form/control/delete/', '/app/demo/form/control/delete/delete.js', '/app/demo/form/control/delete/delete.xml', '/app/demo/form/control/desc/', '/app/demo/form/control/desc/desc.js', '/app/demo/form/control/desc/desc.xml', '/app/demo/form/control/dialog/', '/app/demo/form/control/dialog/dialog.js', '/app/demo/form/control/dialog/dialog.xml', '/app/demo/form/control/drawer/', '/app/demo/form/control/drawer/drawer.js', '/app/demo/form/control/drawer/drawer.xml', '/app/demo/form/control/echarts/', '/app/demo/form/control/echarts/echarts.js', '/app/demo/form/control/echarts/echarts.xml', '/app/demo/form/control/empty/', '/app/demo/form/control/empty/empty.js', '/app/demo/form/control/empty/empty.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/grid/', '/app/demo/form/control/grid/grid.js', '/app/demo/form/control/grid/grid.xml', '/app/demo/form/control/group/', '/app/demo/form/control/group/group.js', '/app/demo/form/control/group/group.xml', '/app/demo/form/control/hske/', '/app/demo/form/control/hske/hske.js', '/app/demo/form/control/hske/hske.xml', '/app/demo/form/control/html/', '/app/demo/form/control/html/html.js', '/app/demo/form/control/html/html.xml', '/app/demo/form/control/icon/', '/app/demo/form/control/icon/icon.xml', '/app/demo/form/control/iconview/', '/app/demo/form/control/iconview/iconview.js', '/app/demo/form/control/iconview/iconview.xml', '/app/demo/form/control/img/', '/app/demo/form/control/img/img.xml', '/app/demo/form/control/imgviewer/', '/app/demo/form/control/imgviewer/imgviewer.js', '/app/demo/form/control/imgviewer/imgviewer.xml', '/app/demo/form/control/jodit/', '/app/demo/form/control/jodit/jodit.js', '/app/demo/form/control/jodit/jodit.xml', '/app/demo/form/control/label/', '/app/demo/form/control/label/label.js', '/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/link/', '/app/demo/form/control/link/link.js', '/app/demo/form/control/link/link.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/map/', '/app/demo/form/control/map/map.js', '/app/demo/form/control/map/map.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/mpegts/', '/app/demo/form/control/mpegts/mpegts.js', '/app/demo/form/control/mpegts/mpegts.xml', '/app/demo/form/control/nav/', '/app/demo/form/control/nav/nav.js', '/app/demo/form/control/nav/nav.xml', '/app/demo/form/control/page/', '/app/demo/form/control/page/page.js', '/app/demo/form/control/page/page.xml', '/app/demo/form/control/palette/', '/app/demo/form/control/palette/palette.js', '/app/demo/form/control/palette/palette.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/pdf/', '/app/demo/form/control/pdf/pdf.js', '/app/demo/form/control/pdf/pdf.xml', '/app/demo/form/control/pdf/test.pdf', '/app/demo/form/control/progress/', '/app/demo/form/control/progress/progress.js', '/app/demo/form/control/progress/progress.xml', '/app/demo/form/control/property/', '/app/demo/form/control/property/property.js', '/app/demo/form/control/property/property.xml', '/app/demo/form/control/qrcode/', '/app/demo/form/control/qrcode/qrcode.js', '/app/demo/form/control/qrcode/qrcode.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/setting/', '/app/demo/form/control/setting/setting.xml', '/app/demo/form/control/sgroup/', '/app/demo/form/control/sgroup/sgroup.xml', '/app/demo/form/control/step/', '/app/demo/form/control/step/step.js', '/app/demo/form/control/step/step.xml', '/app/demo/form/control/svg/', '/app/demo/form/control/svg/svg.js', '/app/demo/form/control/svg/svg.xml', '/app/demo/form/control/switch/', '/app/demo/form/control/switch/switch.js', '/app/demo/form/control/switch/switch.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/tag/', '/app/demo/form/control/tag/tag.js', '/app/demo/form/control/tag/tag.xml', '/app/demo/form/control/text/', '/app/demo/form/control/text/text.js', '/app/demo/form/control/text/text.xml', '/app/demo/form/control/timeline/', '/app/demo/form/control/timeline/timeline.js', '/app/demo/form/control/timeline/timeline.xml', '/app/demo/form/control/tip/', '/app/demo/form/control/tip/tip.js', '/app/demo/form/control/tip/tip.xml', '/app/demo/form/control/tplink/', '/app/demo/form/control/tplink/tplink.js', '/app/demo/form/control/tplink/tplink.xml', '/app/demo/form/control/tuieditor/', '/app/demo/form/control/tuieditor/tuieditor.js', '/app/demo/form/control/tuieditor/tuieditor.xml', '/app/demo/form/control/tuiviewer/', '/app/demo/form/control/tuiviewer/tuiviewer.js', '/app/demo/form/control/tuiviewer/tuiviewer.xml', '/app/demo/form/control/uploader/', '/app/demo/form/control/uploader/uploader.js', '/app/demo/form/control/uploader/uploader.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/control/video/', '/app/demo/form/control/video/video.js', '/app/demo/form/control/video/video.xml', '/app/demo/form/control/web/', '/app/demo/form/control/web/web.js', '/app/demo/form/control/web/web.xml', '/app/demo/form/control/xterm/', '/app/demo/form/control/xterm/xterm.js', '/app/demo/form/control/xterm/xterm.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/acontrol/', '/app/demo/form/method/acontrol/acontrol.js', '/app/demo/form/method/acontrol/acontrol.xml', '/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.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/storage/', '/app/demo/form/method/storage/storage.js', '/app/demo/form/method/storage/storage.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/form/solution/', '/app/demo/form/solution/backpanel/', '/app/demo/form/solution/backpanel/backpanel.js', '/app/demo/form/solution/backpanel/backpanel.xml', '/app/demo/global.css', '/app/demo/res/', '/app/demo/res/custombtn.cgc', '/app/demo/res/icon.svg', '/app/demo/res/img.jpg', '/app/demo/res/marker.svg', '/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/video.mp4', '/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/arteditor.cgc', '/control/box.cgc', '/control/captcha.cgc', '/control/common.cgc', '/control/desc.cgc', '/control/drawer.cgc', '/control/echarts.cgc', '/control/form.cgc', '/control/iconview.cgc', '/control/jodit.cgc', '/control/map.cgc', '/control/monaco.cgc', '/control/mpegts.cgc', '/control/nav.cgc', '/control/page.cgc', '/control/pdf.cgc', '/control/property.cgc', '/control/qrcode.cgc', '/control/table.cgc', '/control/task.cgc', '/control/tplink.cgc', '/control/tuieditor.cgc', '/control/tuiviewer.cgc', '/control/web.cgc', '/control/xterm.cgc', '/ext/', '/ext/toastui-editor-all.min.js', '/ext/tplinkhd.min.js', '/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/storage.js', '/lib/storage.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', '/notosans-regular.ttf', '/theme/', '/theme/blue.cgt', '/theme/byterun.cgt', '/theme/light.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
- 'ko': {
35
- 'apply-unmount': '"?" 마운트 지점을 언마운트하시겠습니까?'
36
- },
37
- 'th': {
38
- 'apply-unmount': 'คุณแน่ใจหรือว่าต้องการยกเลิกการติดตั้งจุดติดตั้ง "?"'
39
- },
40
- 'es': {
41
- 'apply-unmount': '¿Está seguro de que desea desmontar el punto de montaje "?"'
42
- },
43
- 'de': {
44
- 'apply-unmount': 'Möchten Sie den Einhängepunkt "?" wirklich demontieren?'
45
- },
46
- 'fr': {
47
- 'apply-unmount': 'Voulez-vous vraiment démonter le point de montage "?"'
48
- },
49
- 'pt': {
50
- 'apply-unmount': 'Tem certeza de que deseja desmontar o ponto de montagem "?"'
51
- },
52
- 'ru': {
53
- 'apply-unmount': 'Вы уверены, что хотите отмонтировать точку монтирования "?"'
54
- },
55
- 'vi': {
56
- 'apply-unmount': 'Bạn có chắc chắn muốn tháo dỡ điểm gắn "?" không?'
57
- }
58
- };
59
-
60
- /** --- 已经挂载的列表 --- */
61
- const mounts: Record<string, types.IMountHandler> = {};
62
-
63
- /** --- 根据 mounted 的 path 获取挂载点 name --- */
64
- function getMountName(path: string): string {
65
- const io = path.slice(9).indexOf('/');
66
- return io === -1 ? path.slice(9) : path.slice(9, io + 9);
67
- }
68
-
69
- /**
70
- * --- 挂载到 mounted 目录下 ---
71
- * @param name 目录名
72
- * @param handler 回调相关
73
- * @param taskId App 模式下无效
74
- */
75
- export function mount(name: string, handler: types.IMountHandler, taskId?: number): boolean {
76
- if (mounts[name]) {
77
- return false;
78
- }
79
- if (!/^[a-zA-Z][\w]+$/.test(name)) {
80
- return false;
81
- }
82
- if (taskId) {
83
- const t = task.list[taskId];
84
- if (t) {
85
- const val = 'fs./mounted/' + name + '/w';
86
- if (!t.runtime.permissions.includes(val)) {
87
- t.runtime.permissions.push(val);
88
- }
89
- }
90
- }
91
- handler.date = new Date();
92
- mounts[name] = handler;
93
- return true;
94
- }
95
-
96
- /**
97
- * --- 卸载 mounted ---
98
- * @param name 目录名
99
- */
100
- export async function unmount(name: string): Promise<boolean> {
101
- if (!mounts[name]) {
102
- return true;
103
- }
104
- const loc = localeData[core.config.locale]?.['apply-unmount'] ?? localeData['en']['apply-unmount'];
105
- if (!await form.superConfirm(loc.replace('?', '/mount/' + tool.escapeHTML(name) + '/'))) {
106
- return false;
107
- }
108
- delete mounts[name];
109
- return true;
110
- }
111
-
112
- export async function getContent(path: string, options?: {
113
- 'start'?: number;
114
- 'end'?: number;
115
- 'progress'?: (loaded: number, total: number) => void | Promise<void>;
116
- 'cache'?: string;
117
- }, taskId?: number): Promise<string | Blob | null>;
118
- export async function getContent(path: string, options: BufferEncoding | {
119
- 'encoding': BufferEncoding;
120
- 'start'?: number;
121
- 'end'?: number;
122
- 'progress'?: (loaded: number, total: number) => void | Promise<void>;
123
- 'cache'?: string;
124
- }, taskId?: number): Promise<string | null>;
125
- /**
126
- * --- 读取完整文件或一段 ---
127
- * @param path 文件路径
128
- * @param options 编码或选项
129
- * @param taskId App 模式下无效
130
- */
131
- export async function getContent(path: string, options?: BufferEncoding | {
132
- 'encoding'?: BufferEncoding;
133
- 'start'?: number;
134
- 'end'?: number;
135
- 'progress'?: (loaded: number, total: number) => void | Promise<void>;
136
- 'cache'?: string;
137
- }, taskId?: number): Promise<Blob | string | null> {
138
- path = tool.urlResolve('/', path);
139
- const fpath = path.slice(8);
140
- if (typeof options === 'string') {
141
- options = {
142
- 'encoding': options
143
- };
144
- }
145
- else if (!options) {
146
- options = {};
147
- }
148
- const encoding = options.encoding;
149
- const start = options.start;
150
- const end = options.end;
151
- if (path.startsWith('/clickgo/') || path.startsWith('http:') || path.startsWith('https:') || path.startsWith('file:')) {
152
- let ourl: string = '';
153
- if (path.startsWith('/clickgo/')) {
154
- if (!clickgoFiles.includes(fpath)) {
155
- return null;
156
- }
157
- ourl = tool.urlResolve(__dirname, './').slice(0, -1) + fpath;
158
- }
159
- else {
160
- ourl = path;
161
- }
162
- try {
163
- /** --- 后缀 --- */
164
- const rand = '?' + (options.cache ?? Math.random().toString());
165
- let blob: Blob | null = null;
166
- const headers: Record<string, string> = {};
167
- if (start ?? end) {
168
- headers['range'] = `bytes=${start ?? '0'}-${end ?? ''}`;
169
- }
170
- if (options.progress) {
171
- blob = await tool.request(ourl + (!ourl.startsWith(loader.cdn) ? rand : ''), {
172
- 'headers': headers,
173
- 'progress': options.progress,
174
- 'responseType': 'blob'
175
- });
176
- }
177
- else {
178
- blob = await (await fetch(ourl + (!ourl.startsWith(loader.cdn) ? rand : ''), {
179
- 'headers': headers
180
- })).blob();
181
- }
182
- if (!blob) {
183
- return null;
184
- }
185
- if (!encoding) {
186
- return blob;
187
- }
188
- return await new Promise(function(resolve) {
189
- const fr = new FileReader();
190
- fr.addEventListener('load', function() {
191
- resolve(fr.result as string | null);
192
- });
193
- fr.readAsText(blob, encoding);
194
- });
195
- }
196
- catch {
197
- return null;
198
- }
199
- }
200
- else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
201
- const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
202
- if (!r[0]) {
203
- return null;
204
- }
205
- if (path.startsWith('/mounted/')) {
206
- const name = getMountName(path);
207
- const hanlder = mounts[name];
208
- if (!hanlder) {
209
- return null;
210
- }
211
- return hanlder.getContent?.(path.slice(9 + name.length), options) ?? null;
212
- }
213
- // --- storage ---
214
- if (options.progress) {
215
- // --- native 暂不支持 progress ---
216
- delete options.progress;
217
- }
218
- const rtn = await native.invoke('cg-fs-getContent', native.getToken(), fpath, options);
219
- if (!rtn) {
220
- return null;
221
- }
222
- if (typeof rtn === 'string') {
223
- return rtn;
224
- }
225
- return new Blob([rtn], {
226
- 'type': tool.getMimeByPath(path).mime
227
- });
228
- }
229
- else if (path.startsWith('/package/') || path.startsWith('/current/')) {
230
- if (!taskId) {
231
- return null;
232
- }
233
- if (path.startsWith('/current/')) {
234
- return getContent(task.list[taskId].current + fpath, options, taskId);
235
- }
236
- const file = task.list[taskId].app.files[fpath];
237
- if (!file) {
238
- return null;
239
- }
240
- if (typeof file === 'string') {
241
- // --- 是文本直接返回 ---
242
- return file;
243
- }
244
- if (!options.encoding) {
245
- // --- 没有编码则返回 blob ---
246
- if (start === undefined && end === undefined) {
247
- return file;
248
- }
249
- return file.slice(start, end, file.type);
250
- }
251
- const encoding = options.encoding;
252
- return new Promise(function(resolve) {
253
- const fr = new FileReader();
254
- fr.addEventListener('load', function() {
255
- resolve(fr.result as string | null);
256
- });
257
- fr.readAsText(file, encoding);
258
- });
259
- }
260
- else {
261
- return null;
262
- }
263
- }
264
-
265
- /**
266
- * --- 写入文件内容 ---
267
- * @param path 文件路径
268
- * @param data 要写入的内容
269
- * @param options 选项
270
- * @param taskId App 模式下无效
271
- */
272
- export async function putContent(path: string, data: string | Blob, options: {
273
- 'encoding'?: BufferEncoding | null;
274
- 'mode'?: string | number;
275
- 'flag'?: string | number;
276
- } = {}, taskId?: number): Promise<boolean> {
277
- path = tool.urlResolve('/', path);
278
- const fpath = path.slice(8);
279
- if (path.startsWith('/clickgo/')) {
280
- return false;
281
- }
282
- else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
283
- const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
284
- if (!r[0]) {
285
- return false;
286
- }
287
- if (path.startsWith('/mounted/')) {
288
- const name = getMountName(path);
289
- const hanlder = mounts[name];
290
- if (!hanlder) {
291
- return false;
292
- }
293
- return hanlder.putContent?.(path.slice(9 + name.length), data, options) ?? false;
294
- }
295
- // --- storage ---
296
- let buf: Uint8Array | undefined = undefined;
297
- if (data instanceof Blob) {
298
- buf = new Uint8Array(await data.arrayBuffer());
299
- }
300
- return native.invoke('cg-fs-putContent', native.getToken(), fpath, buf ?? data, options);
301
- }
302
- else if (path.startsWith('/package/')) {
303
- return false;
304
- }
305
- else if (path.startsWith('/current/')) {
306
- if (!taskId) {
307
- return false;
308
- }
309
- return putContent(task.list[taskId].current + fpath, data, options, taskId);
310
- }
311
- else {
312
- return false;
313
- }
314
- }
315
-
316
- /**
317
- * --- 读取链接的 target ---
318
- * @param path 要读取的路径
319
- * @param options 选项
320
- */
321
- export async function readLink(path: string, encoding?: BufferEncoding, taskId?: number): Promise<string | null> {
322
- path = tool.urlResolve('/', path);
323
- const fpath = path.slice(8);
324
- if (path.startsWith('/clickgo/')) {
325
- return null;
326
- }
327
- else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
328
- const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
329
- if (!r[0]) {
330
- return null;
331
- }
332
- if (path.startsWith('/mounted/')) {
333
- const name = getMountName(path);
334
- const hanlder = mounts[name];
335
- if (!hanlder) {
336
- return null;
337
- }
338
- return hanlder.readLink?.(path.slice(9 + name.length), encoding) ?? null;
339
- }
340
- // --- storage ---
341
- return native.invoke('cg-fs-readLink', native.getToken(), fpath, encoding);
342
- }
343
- else if (path.startsWith('/package/')) {
344
- return null;
345
- }
346
- else if (path.startsWith('/current/')) {
347
- if (!taskId) {
348
- return null;
349
- }
350
- return task.list[taskId].current;
351
- }
352
- else {
353
- return null;
354
- }
355
- }
356
-
357
- /**
358
- * --- 把源文件创建一个 link ---
359
- * @param filePath 源文件
360
- * @param linkPath 连接路径
361
- * @param type 选项
362
- * @param taskId App 模式下无效
363
- */
364
- export async function symlink(filePath: string, linkPath: string, type?: 'dir' | 'file' | 'junction', taskId?: number): Promise<boolean> {
365
- filePath = tool.urlResolve('/', filePath);
366
- linkPath = tool.urlResolve('/', linkPath);
367
- if (filePath.startsWith('/clickgo/')) {
368
- return false;
369
- }
370
- else if (filePath.startsWith('/storage/') || filePath.startsWith('/mounted/')) {
371
- const r = await task.checkPermission('fs.' + filePath + 'w', false, undefined, taskId);
372
- if (!r[0]) {
373
- return false;
374
- }
375
- if (filePath.startsWith('/mounted/')) {
376
- const fname = getMountName(filePath);
377
- const lname = getMountName(linkPath);
378
- if (fname !== lname) {
379
- return false;
380
- }
381
- const hanlder = mounts[fname];
382
- if (!hanlder) {
383
- return false;
384
- }
385
- return hanlder.symlink?.(filePath.slice(9 + fname.length), linkPath.slice(9 + fname.length), type) ?? false;
386
- }
387
- // --- storage ---
388
- return native.invoke('cg-fs-symlink', native.getToken(), filePath.slice(8), linkPath.slice(8), type);
389
- }
390
- else if (filePath.startsWith('/package/')) {
391
- return false;
392
- }
393
- else if (filePath.startsWith('/current/')) {
394
- if (!taskId) {
395
- return false;
396
- }
397
- if (linkPath.startsWith('/current/')) {
398
- linkPath = task.list[taskId].current + linkPath.slice(8);
399
- }
400
- return symlink(task.list[taskId].current + filePath.slice(8), linkPath, type, taskId);
401
- }
402
- else {
403
- return false;
404
- }
405
- }
406
-
407
- /**
408
- * --- 删除一个文件 ---
409
- * @param path 要删除的文件路径
410
- * @param taskId App 模式下无效
411
- */
412
- export async function unlink(path: string, taskId?: number): Promise<boolean> {
413
- path = tool.urlResolve('/', path);
414
- const fpath = path.slice(8);
415
- if (path.startsWith('/clickgo/')) {
416
- return false;
417
- }
418
- else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
419
- const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
420
- if (!r[0]) {
421
- return false;
422
- }
423
- if (path.startsWith('/mounted/')) {
424
- const name = getMountName(path);
425
- const hanlder = mounts[name];
426
- if (!hanlder) {
427
- return false;
428
- }
429
- return hanlder.unlink?.(path.slice(9 + name.length)) ?? false;
430
- }
431
- // --- storage ---
432
- return native.invoke('cg-fs-unlink', native.getToken(), fpath);
433
- }
434
- else if (path.startsWith('/package/')) {
435
- return false;
436
- }
437
- else if (path.startsWith('/current/')) {
438
- if (!taskId) {
439
- return false;
440
- }
441
- return unlink(task.list[taskId].current + fpath, taskId);
442
- }
443
- else {
444
- return false;
445
- }
446
- }
447
-
448
- async function getClickGoStats(path: string): Promise<types.IStats | null> {
449
- if (path.endsWith('/')) {
450
- // --- 文件夹 ---
451
- const date = new Date();
452
- const ms = date.getTime();
453
- return {
454
- isFile: function() {
455
- return false;
456
- },
457
- isDirectory: function() {
458
- return true;
459
- },
460
- isSymbolicLink: function() {
461
- return false;
462
- },
463
- 'size': 0,
464
- 'blksize': 0,
465
- 'atimeMs': ms,
466
- 'mtimeMs': ms,
467
- 'ctimeMs': ms,
468
- 'birthtimeMs': ms,
469
- 'atime': date,
470
- 'mtime': date,
471
- 'ctime': date,
472
- 'birthtime': date
473
- };
474
- }
475
- else {
476
- // --- 文件 ---
477
- try {
478
- const res = await fetch(tool.urlResolve(__dirname, './').slice(0, -1) + path, {
479
- 'headers': {
480
- 'range': `bytes=0-1`
481
- }
482
- });
483
- const hdate = res.headers.get('date');
484
- const hmdate = res.headers.get('last-modified');
485
- let hlength = res.headers.get('content-range');
486
- if (hlength) {
487
- const lio = hlength.lastIndexOf('/');
488
- hlength = hlength.slice(lio + 1);
489
- }
490
- let date = new Date();
491
- let mdate = date;
492
- if (hdate) {
493
- date = new Date(hdate);
494
- }
495
- if (hmdate) {
496
- mdate = new Date(hmdate);
497
- }
498
- const ms = date.getTime();
499
- const mms = mdate.getTime();
500
- return {
501
- isFile: function() {
502
- return true;
503
- },
504
- isDirectory: function() {
505
- return false;
506
- },
507
- isSymbolicLink: function() {
508
- return false;
509
- },
510
- 'size': hlength ? parseInt(hlength) : 0,
511
- 'blksize': hlength ? parseInt(hlength) : 0,
512
- 'atimeMs': ms,
513
- 'mtimeMs': mms,
514
- 'ctimeMs': mms,
515
- 'birthtimeMs': ms,
516
- 'atime': date,
517
- 'mtime': mdate,
518
- 'ctime': mdate,
519
- 'birthtime': date
520
- };
521
- }
522
- catch {
523
- return null;
524
- }
525
- }
526
- }
527
-
528
- /**
529
- * --- 获取对象是否存在,存在则返回 stats 对象,否则返回 null ---
530
- * @param path 对象路径
531
- * @param taskId App 模式下无效
532
- */
533
- export async function stats(path: string, taskId?: number): Promise<types.IStats | null> {
534
- path = tool.urlResolve('/', path);
535
- if (path.endsWith('/')) {
536
- path = path.slice(0, -1);
537
- }
538
- if (['', '/clickgo', '/storage', '/mounted', '/package'].includes(path)) {
539
- const date = new Date();
540
- const ms = date.getTime();
541
- return {
542
- isFile: function() {
543
- return false;
544
- },
545
- isDirectory: function() {
546
- return true;
547
- },
548
- isSymbolicLink: function() {
549
- return false;
550
- },
551
- 'size': 0,
552
- 'blksize': 0,
553
- 'atimeMs': ms,
554
- 'mtimeMs': ms,
555
- 'ctimeMs': ms,
556
- 'birthtimeMs': ms,
557
- 'atime': date,
558
- 'mtime': date,
559
- 'ctime': date,
560
- 'birthtime': date
561
- };
562
- }
563
- if (path === '/current') {
564
- const date = new Date();
565
- const ms = date.getTime();
566
- return {
567
- isFile: function() {
568
- return false;
569
- },
570
- isDirectory: function() {
571
- return false;
572
- },
573
- isSymbolicLink: function() {
574
- return true;
575
- },
576
- 'size': 0,
577
- 'blksize': 0,
578
- 'atimeMs': ms,
579
- 'mtimeMs': ms,
580
- 'ctimeMs': ms,
581
- 'birthtimeMs': ms,
582
- 'atime': date,
583
- 'mtime': date,
584
- 'ctime': date,
585
- 'birthtime': date
586
- };
587
- }
588
- let fpath = path.slice(8);
589
- if (path.startsWith('/clickgo/')) {
590
- if (!clickgoFiles.includes(fpath)) {
591
- if (!clickgoFiles.includes(fpath + '/')) {
592
- return null;
593
- }
594
- fpath += '/';
595
- }
596
- return getClickGoStats(fpath);
597
- }
598
- else if (path.startsWith('/storage/')) {
599
- const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
600
- if (!r[0]) {
601
- return null;
602
- }
603
- const item = await native.invoke('cg-fs-stats', native.getToken(), fpath);
604
- if (!item) {
605
- return null;
606
- }
607
- return {
608
- isFile: function() {
609
- return item.isFile;
610
- },
611
- isDirectory: function() {
612
- return item.isDirectory;
613
- },
614
- isSymbolicLink: function() {
615
- return item.isSymbolicLink;
616
- },
617
- 'size': item.size,
618
- 'blksize': item.blksize,
619
- 'atimeMs': item.atimeMs,
620
- 'mtimeMs': item.mtimeMs,
621
- 'ctimeMs': item.ctimeMs,
622
- 'birthtimeMs': item.birthtimeMs,
623
- 'atime': item.atime,
624
- 'mtime': item.mtime,
625
- 'ctime': item.ctime,
626
- 'birthtime': item.birthtime
627
- };
628
- }
629
- else if (path.startsWith('/mounted/')) {
630
- const name = getMountName(path);
631
- const hanlder = mounts[name];
632
- if (!hanlder) {
633
- return null;
634
- }
635
- if (path === '/mounted/' + name) {
636
- const ms = hanlder.date!.getTime();
637
- return {
638
- isFile: function() {
639
- return false;
640
- },
641
- isDirectory: function() {
642
- return true;
643
- },
644
- isSymbolicLink: function() {
645
- return false;
646
- },
647
- 'size': 0,
648
- 'blksize': 0,
649
- 'atimeMs': ms,
650
- 'mtimeMs': ms,
651
- 'ctimeMs': ms,
652
- 'birthtimeMs': ms,
653
- 'atime': hanlder.date!,
654
- 'mtime': hanlder.date!,
655
- 'ctime': hanlder.date!,
656
- 'birthtime': hanlder.date!
657
- };
658
- }
659
- const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
660
- if (!r[0]) {
661
- return null;
662
- }
663
- return hanlder.stats?.(path.slice(9 + name.length)) ?? null;
664
- }
665
- else if (path.startsWith('/package/') || path.startsWith('/current/')) {
666
- if (!taskId) {
667
- return null;
668
- }
669
- if (path.startsWith('/current/')) {
670
- return stats(task.list[taskId].current + fpath, taskId);
671
- }
672
- if (task.list[taskId].app.files[fpath]) {
673
- // --- 文件 ---
674
- const file = task.list[taskId].app.files[fpath];
675
- const date = new Date();
676
- const ms = date.getTime();
677
- let size = 0;
678
- if (typeof file !== 'string') {
679
- size = file.size;
680
- }
681
- else {
682
- size = new Blob([file]).size;
683
- }
684
- return {
685
- isFile: function() {
686
- return true;
687
- },
688
- isDirectory: function() {
689
- return false;
690
- },
691
- isSymbolicLink: function() {
692
- return false;
693
- },
694
- 'size': size,
695
- 'blksize': size,
696
- 'atimeMs': ms,
697
- 'mtimeMs': ms,
698
- 'ctimeMs': ms,
699
- 'birthtimeMs': ms,
700
- 'atime': date,
701
- 'mtime': date,
702
- 'ctime': date,
703
- 'birthtime': date
704
- };
705
- }
706
- // --- 检测是否是文件夹 ---
707
- if (!fpath.endsWith('/')) {
708
- fpath += '/';
709
- }
710
- for (const p in task.list[taskId].app.files) {
711
- if (!p.startsWith(fpath)) {
712
- continue;
713
- }
714
- // --- 文件夹 ---
715
- const date = new Date();
716
- const ms = date.getTime();
717
- return {
718
- isFile: function() {
719
- return false;
720
- },
721
- isDirectory: function() {
722
- return true;
723
- },
724
- isSymbolicLink: function() {
725
- return false;
726
- },
727
- 'size': 0,
728
- 'blksize': 0,
729
- 'atimeMs': ms,
730
- 'mtimeMs': ms,
731
- 'ctimeMs': ms,
732
- 'birthtimeMs': ms,
733
- 'atime': date,
734
- 'mtime': date,
735
- 'ctime': date,
736
- 'birthtime': date
737
- };
738
- }
739
- return null;
740
- }
741
- else {
742
- return null;
743
- }
744
- }
745
-
746
- /**
747
- * --- 判断是否是目录或目录是否存在,是的话返回 stats ---
748
- * @param path 判断路径woml
749
- * @param taskId App 模式下无效
750
- */
751
- export async function isDir(path: string, taskId?: number): Promise<types.IStats | false> {
752
- const pstats = await stats(path, taskId);
753
- if (!pstats?.isDirectory()) {
754
- return false;
755
- }
756
- return pstats;
757
- }
758
-
759
- /**
760
- * --- 判断是否是文件或文件是否存在,是的话返回 stats ---
761
- * @param path 判断路径
762
- * @param taskId App 模式下无效
763
- */
764
- export async function isFile(path: string, taskId?: number): Promise<types.IStats | false> {
765
- const pstats = await stats(path, taskId);
766
- if (!pstats?.isFile()) {
767
- return false;
768
- }
769
- return pstats;
770
- }
771
-
772
- /**
773
- * --- 深度创建目录,如果最末目录存在,则自动创建成功 ---
774
- * @param path 要创建的路径,如 /a/b/c/
775
- * @param mode 权限
776
- * @param taskId App 模式下无效
777
- */
778
- export async function mkdir(path: string, mode: number = 0o755, taskId?: number): Promise<boolean> {
779
- path = tool.urlResolve('/', path);
780
- if (await isDir(path, taskId)) {
781
- return true;
782
- }
783
- const fpath = path.slice(8);
784
- if (path.startsWith('/clickgo/')) {
785
- return false;
786
- }
787
- else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
788
- const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
789
- if (!r[0]) {
790
- return false;
791
- }
792
- if (path.startsWith('/mounted/')) {
793
- const name = getMountName(path);
794
- const hanlder = mounts[name];
795
- if (!hanlder) {
796
- return false;
797
- }
798
- return hanlder.mkdir?.(path.slice(9 + name.length), mode) ?? false;
799
- }
800
- // --- storage ---
801
- return native.invoke('cg-fs-mkdir', native.getToken(), fpath, mode);
802
- }
803
- else if (path.startsWith('/package/')) {
804
- return false;
805
- }
806
- else if (path.startsWith('/current/')) {
807
- if (!taskId) {
808
- return false;
809
- }
810
- return mkdir(task.list[taskId].current + fpath, mode, taskId);
811
- }
812
- else {
813
- return false;
814
- }
815
- }
816
-
817
- /**
818
- * --- 删除空目录 ---
819
- * @param path 要删除的目录
820
- * @param taskId App 模式下无效
821
- */
822
- export async function rmdir(path: string, taskId?: number): Promise<boolean> {
823
- path = tool.urlResolve('/', path);
824
- const fpath = path.slice(8);
825
- if (path.startsWith('/clickgo/')) {
826
- return false;
827
- }
828
- else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
829
- const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
830
- if (!r[0]) {
831
- return false;
832
- }
833
- if (path.startsWith('/mounted/')) {
834
- const name = getMountName(path);
835
- const hanlder = mounts[name];
836
- if (!hanlder) {
837
- return false;
838
- }
839
- return hanlder.rmdir?.(path.slice(9 + name.length)) ?? false;
840
- }
841
- // --- storage ---
842
- return native.invoke('cg-fs-rmdir', native.getToken(), fpath);
843
- }
844
- else if (path.startsWith('/package/')) {
845
- return false;
846
- }
847
- else if (path.startsWith('/current/')) {
848
- if (!taskId) {
849
- return false;
850
- }
851
- return rmdir(task.list[taskId].current + fpath, taskId);
852
- }
853
- else {
854
- return false;
855
- }
856
- }
857
-
858
- /**
859
- * --- 删除一个非空目录 ---
860
- * --- [ Danger ] [ 危险 ] ---
861
- * @param path 目录路径
862
- * @param taskId App 模式下无效
863
- */
864
- export async function rmdirDeep(path: string, taskId?: number): Promise<boolean> {
865
- path = tool.urlResolve('/', path);
866
- if (!path.endsWith('/')) {
867
- path += '/';
868
- }
869
- const list = await readDir(path, undefined, taskId);
870
- for (const item of list) {
871
- const stat = await stats(path + item.name, taskId);
872
- if (!stat) {
873
- return false;
874
- }
875
- if (stat.isDirectory()) {
876
- // --- 目录 ---
877
- const rtn = await rmdirDeep(path + item.name, taskId);
878
- if (!rtn) {
879
- return false;
880
- }
881
- }
882
- else {
883
- const rtn = await unlink(path + item.name, taskId);
884
- if (!rtn) {
885
- return false;
886
- }
887
- }
888
- }
889
- return rmdir(path, taskId);
890
- }
891
-
892
- /**
893
- * --- 修改权限 ---
894
- * @param path 要修改的路径
895
- * @param mod 权限
896
- * @param taskId App 模式下无效
897
- */
898
- export async function chmod(path: string, mod: string | number, taskId?: number): Promise<boolean> {
899
- path = tool.urlResolve('/', path);
900
- const fpath = path.slice(8);
901
- if (path.startsWith('/clickgo/')) {
902
- return false;
903
- }
904
- else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
905
- const r = await task.checkPermission('fs.' + path + 'w', false, undefined, taskId);
906
- if (!r[0]) {
907
- return false;
908
- }
909
- if (path.startsWith('/mounted/')) {
910
- const name = getMountName(path);
911
- const hanlder = mounts[name];
912
- if (!hanlder) {
913
- return false;
914
- }
915
- return hanlder.chmod?.(path.slice(9 + name.length), mod) ?? false;
916
- }
917
- // --- storage ---
918
- return native.invoke('cg-fs-chmod', native.getToken(), fpath, mod);
919
- }
920
- else if (path.startsWith('/package/')) {
921
- return false;
922
- }
923
- else if (path.startsWith('/current/')) {
924
- if (!taskId) {
925
- return false;
926
- }
927
- return chmod(task.list[taskId].current + fpath, mod, taskId);
928
- }
929
- else {
930
- return false;
931
- }
932
- }
933
-
934
- /**
935
- * --- 重命名/移动 文件文件夹 ---
936
- * @param oldPath 老名
937
- * @param newPath 新名
938
- */
939
- export async function rename(oldPath: string, newPath: string, taskId?: number): Promise<boolean> {
940
- oldPath = tool.urlResolve('/', oldPath);
941
- newPath = tool.urlResolve('/', newPath);
942
- if (!oldPath.startsWith(newPath.slice(0, 9))) {
943
- return false;
944
- }
945
- const ofpath = oldPath.slice(8);
946
- const nfpath = newPath.slice(8);
947
- if (oldPath.startsWith('/clickgo/')) {
948
- return false;
949
- }
950
- else if (oldPath.startsWith('/storage/') || oldPath.startsWith('/mounted/')) {
951
- let r = await task.checkPermission('fs.' + oldPath + 'w', false, undefined, taskId);
952
- if (!r[0]) {
953
- return false;
954
- }
955
- r = await task.checkPermission('fs.' + newPath + 'w', false, undefined, taskId);
956
- if (!r[0]) {
957
- return false;
958
- }
959
- if (oldPath.startsWith('/mounted/')) {
960
- const fname = getMountName(oldPath);
961
- const lname = getMountName(newPath);
962
- if (fname !== lname) {
963
- return false;
964
- }
965
- const hanlder = mounts[fname];
966
- if (!hanlder) {
967
- return false;
968
- }
969
- return hanlder.rename?.(oldPath.slice(9 + fname.length), newPath.slice(9 + fname.length)) ?? false;
970
- }
971
- // --- storage ---
972
- return native.invoke('cg-fs-rename', native.getToken(), ofpath, nfpath);
973
- }
974
- else if (oldPath.startsWith('/package/')) {
975
- return false;
976
- }
977
- else if (oldPath.startsWith('/current/')) {
978
- if (!taskId) {
979
- return false;
980
- }
981
- return rename(task.list[taskId].current + ofpath, task.list[taskId].current + nfpath, taskId);
982
- }
983
- else {
984
- return false;
985
- }
986
- }
987
-
988
- /**
989
- * --- 获取文件夹下文件列表 ---
990
- * @param path 文件夹路径
991
- * @param encoding 编码
992
- * @param taskId App 模式下无效
993
- */
994
- export async function readDir(path: string, encoding?: BufferEncoding, taskId?: number): Promise<types.IDirent[]> {
995
- path = tool.urlResolve('/', path);
996
- if (path === '/') {
997
- const list = [
998
- {
999
- isFile: function() {
1000
- return false;
1001
- },
1002
- isDirectory: function() {
1003
- return true;
1004
- },
1005
- isSymbolicLink: function() {
1006
- return false;
1007
- },
1008
- 'name': 'clickgo'
1009
- },
1010
- {
1011
- isFile: function() {
1012
- return false;
1013
- },
1014
- isDirectory: function() {
1015
- return true;
1016
- },
1017
- isSymbolicLink: function() {
1018
- return false;
1019
- },
1020
- 'name': 'storage'
1021
- },
1022
- {
1023
- isFile: function() {
1024
- return false;
1025
- },
1026
- isDirectory: function() {
1027
- return true;
1028
- },
1029
- isSymbolicLink: function() {
1030
- return false;
1031
- },
1032
- 'name': 'mounted'
1033
- }
1034
- ];
1035
- if (taskId) {
1036
- list.push({
1037
- isFile: function() {
1038
- return false;
1039
- },
1040
- isDirectory: function() {
1041
- return true;
1042
- },
1043
- isSymbolicLink: function() {
1044
- return false;
1045
- },
1046
- 'name': 'package'
1047
- });
1048
- }
1049
- if (taskId) {
1050
- list.push({
1051
- isFile: function() {
1052
- return false;
1053
- },
1054
- isDirectory: function() {
1055
- return false;
1056
- },
1057
- isSymbolicLink: function() {
1058
- return true;
1059
- },
1060
- 'name': 'current'
1061
- });
1062
- }
1063
- return list;
1064
- }
1065
- if (!path.endsWith('/')) {
1066
- path += '/';
1067
- }
1068
- const fpath = path.slice(8);
1069
- if (path.startsWith('/clickgo/')) {
1070
- const list: types.IDirent[] = [];
1071
- for (const item of clickgoFiles) {
1072
- if (!item.startsWith(fpath)) {
1073
- continue;
1074
- }
1075
- if (fpath === item) {
1076
- // --- 是自己 ---
1077
- continue;
1078
- }
1079
- const rpath = item.slice(fpath.length);
1080
- if (rpath.includes('/')) {
1081
- // --- 可能是下级 ---
1082
- if (rpath.endsWith('/')) {
1083
- // --- 是个文件夹 ---
1084
- if (rpath.slice(0, -1).includes('/')) {
1085
- // --- 是好几层下面的文件夹了 ---
1086
- continue;
1087
- }
1088
- list.push({
1089
- isFile: function() {
1090
- return false;
1091
- },
1092
- isDirectory: function() {
1093
- return true;
1094
- },
1095
- isSymbolicLink: function() {
1096
- return false;
1097
- },
1098
- 'name': rpath.slice(0, -1)
1099
- });
1100
- }
1101
- continue;
1102
- }
1103
- // --- 本层文件 ---
1104
- list.push({
1105
- isFile: function() {
1106
- return true;
1107
- },
1108
- isDirectory: function() {
1109
- return false;
1110
- },
1111
- isSymbolicLink: function() {
1112
- return false;
1113
- },
1114
- 'name': rpath
1115
- });
1116
- }
1117
- return list;
1118
- }
1119
- else if (path.startsWith('/storage/') || path.startsWith('/mounted/')) {
1120
- const list: types.IDirent[] = [];
1121
- if (path === '/mounted/') {
1122
- for (const name in mounts) {
1123
- list.push({
1124
- isFile: function() {
1125
- return false;
1126
- },
1127
- isDirectory: function() {
1128
- return true;
1129
- },
1130
- isSymbolicLink: function() {
1131
- return false;
1132
- },
1133
- 'name': name
1134
- });
1135
- }
1136
- return list;
1137
- }
1138
- const r = await task.checkPermission('fs.' + path + 'r', false, undefined, taskId);
1139
- if (!r[0]) {
1140
- return [];
1141
- }
1142
- if (path.startsWith('/mounted/')) {
1143
- const name = getMountName(path);
1144
- const hanlder = mounts[name];
1145
- if (!hanlder) {
1146
- return [];
1147
- }
1148
- return hanlder.readDir?.(path.slice(9 + name.length), encoding) ?? [];
1149
- }
1150
- // --- storage ---
1151
- const ls = await native.invoke('cg-fs-readDir', native.getToken(), fpath, encoding);
1152
- for (const item of ls) {
1153
- list.push({
1154
- isFile: function() {
1155
- return item.isFile;
1156
- },
1157
- isDirectory: function() {
1158
- return item.isDirectory;
1159
- },
1160
- isSymbolicLink: function() {
1161
- return item.isSymbolicLink;
1162
- },
1163
- 'name': item.name
1164
- });
1165
- }
1166
- return list;
1167
- }
1168
- else if (path.startsWith('/package/') || path.startsWith('/current/')) {
1169
- if (!taskId) {
1170
- return [];
1171
- }
1172
- if (path.startsWith('/current/')) {
1173
- return readDir(task.list[taskId].current + fpath, encoding, taskId);
1174
- }
1175
- const list: types.IDirent[] = [];
1176
- const dirs: string[] = [];
1177
- for (const p in task.list[taskId].app.files) {
1178
- if (!p.startsWith(fpath)) {
1179
- continue;
1180
- }
1181
- const rpath = p.slice(fpath.length);
1182
- const sio = rpath.indexOf('/');
1183
- if (sio !== -1) {
1184
- // --- 一定是下级文件,因此加入文件夹项目到 dirs ---
1185
- const name = rpath.slice(0, sio);
1186
- if (!dirs.includes(name)) {
1187
- dirs.push(name);
1188
- list.push({
1189
- isFile: function() {
1190
- return false;
1191
- },
1192
- isDirectory: function() {
1193
- return true;
1194
- },
1195
- isSymbolicLink: function() {
1196
- return false;
1197
- },
1198
- 'name': name
1199
- });
1200
- }
1201
- continue;
1202
- }
1203
- // --- 本层文件 ---
1204
- list.push({
1205
- isFile: function() {
1206
- return true;
1207
- },
1208
- isDirectory: function() {
1209
- return false;
1210
- },
1211
- isSymbolicLink: function() {
1212
- return false;
1213
- },
1214
- 'name': rpath
1215
- });
1216
- }
1217
- return list;
1218
- }
1219
- else {
1220
- return [];
1221
- }
1222
- }
1223
-
1224
- /**
1225
- * --- 复制文件夹里的内容到另一个地方,失败不会回滚 ---
1226
- * @param from 源,末尾加 /
1227
- * @param to 目标,末尾加 /
1228
- * @param ignore 忽略的文件
1229
- * @param taskId App 模式下无效
1230
- */
1231
- export async function copyFolder(from: string, to: string, ignore: RegExp[] = [], taskId?: number): Promise<number> {
1232
- let num = 0;
1233
- // --- 如果源目录不存在或不是目录,则直接成功 :) ---
1234
- if (!await isDir(from, taskId)) {
1235
- return 0;
1236
- }
1237
- // --- 遍历源目录文件和文件夹,准备复制 ---
1238
- const flist = await readDir(from, undefined, taskId);
1239
- /** --- to 目录是否检查是否存在,空目录不复制,所以确定有 item file 的时候才创建 --- */
1240
- let checkTo = false;
1241
- for (const item of flist) {
1242
- if (item.isDirectory()) {
1243
- const r = await copyFolder(from + item.name + '/', to + item.name + '/', ignore, taskId);
1244
- if (r === -1) {
1245
- return r;
1246
- }
1247
- else {
1248
- num += r;
1249
- }
1250
- }
1251
- else if (item.isFile()) {
1252
- // --- 先判断本文件是否被排除 ---
1253
- if (ignore.length > 0 && tool.match(item.name, ignore)) {
1254
- continue;
1255
- }
1256
- if (!checkTo) {
1257
- if (!await mkdir(to, undefined, taskId)) {
1258
- return -1;
1259
- }
1260
- checkTo = true;
1261
- }
1262
- if (!(await copyFile(from + item.name, to + item.name, taskId))) {
1263
- continue;
1264
- }
1265
- ++num;
1266
- }
1267
- }
1268
- return num;
1269
- }
1270
-
1271
- /**
1272
- * --- 复制文件 ---
1273
- * @param src 源文件
1274
- * @param dest 目标文件
1275
- * @param taskId App 模式下无效
1276
- */
1277
- export async function copyFile(src: string, dest: string, taskId?: number): Promise<boolean> {
1278
- src = tool.urlResolve('/', src);
1279
- dest = tool.urlResolve('/', dest);
1280
- if (!src.startsWith(dest.slice(0, 9))) {
1281
- return false;
1282
- }
1283
- const sfpath = src.slice(8);
1284
- const dfpath = dest.slice(8);
1285
- if (src.startsWith('/clickgo/')) {
1286
- return false;
1287
- }
1288
- else if (src.startsWith('/storage/') || dest.startsWith('/mounted/')) {
1289
- let r = await task.checkPermission('fs.' + src + 'r', false, undefined, taskId);
1290
- if (!r[0]) {
1291
- return false;
1292
- }
1293
- r = await task.checkPermission('fs.' + dest + 'w', false, undefined, taskId);
1294
- if (!r[0]) {
1295
- return false;
1296
- }
1297
- if (src.startsWith('/mounted/')) {
1298
- const fname = getMountName(src);
1299
- const lname = getMountName(dest);
1300
- if (fname !== lname) {
1301
- return false;
1302
- }
1303
- const hanlder = mounts[fname];
1304
- if (!hanlder) {
1305
- return false;
1306
- }
1307
- return hanlder.copyFile?.(src.slice(9 + fname.length), dest.slice(9 + fname.length)) ?? false;
1308
- }
1309
- // --- storage ---
1310
- return native.invoke('cg-fs-copyFile', native.getToken(), sfpath, dfpath);
1311
- }
1312
- else if (src.startsWith('/package/')) {
1313
- return false;
1314
- }
1315
- else if (src.startsWith('/current/')) {
1316
- if (!taskId) {
1317
- return false;
1318
- }
1319
- return copyFile(task.list[taskId].current + sfpath, task.list[taskId].current + dfpath, taskId);
1320
- }
1321
- else {
1322
- return false;
1323
- }
1324
- }