hfs 3.1.5 → 3.2.0-beta2
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/admin/assets/af-DWEYq388.js +1 -0
- package/admin/assets/am-DgLbAgj6.js +1 -0
- package/admin/assets/ar-DgEWkO74.js +1 -0
- package/admin/assets/ar-dz-DHq--Sr8.js +1 -0
- package/admin/assets/ar-iq-D1r3nsb9.js +1 -0
- package/admin/assets/ar-kw-C85fGHwp.js +1 -0
- package/admin/assets/ar-ly-Bq6pjGjs.js +1 -0
- package/admin/assets/ar-ma-SGvmh6Mj.js +1 -0
- package/admin/assets/ar-sa-Bv8hiFi6.js +1 -0
- package/admin/assets/ar-tn-Bdvo77v3.js +1 -0
- package/admin/assets/az-7fPndoov.js +1 -0
- package/admin/assets/be-wjXeIeAK.js +1 -0
- package/admin/assets/bg-DZwBvjzH.js +1 -0
- package/admin/assets/bi-qCtxvMhO.js +1 -0
- package/admin/assets/bm-BVPWvreb.js +1 -0
- package/admin/assets/bn-CdWvye7d.js +1 -0
- package/admin/assets/bn-bd-B5-3blmz.js +1 -0
- package/admin/assets/bo-BEVcgyN2.js +1 -0
- package/admin/assets/br-DGQD6fFs.js +1 -0
- package/admin/assets/bs-So4MoRua.js +1 -0
- package/admin/assets/ca-Bk5ytG4g.js +1 -0
- package/admin/assets/cs-C4NU8eW-.js +1 -0
- package/admin/assets/cv-Ct-s-zrW.js +1 -0
- package/admin/assets/cy-ojtDPj8_.js +1 -0
- package/admin/assets/da-CYGin6vm.js +1 -0
- package/admin/assets/de-BEbJ01zH.js +1 -0
- package/admin/assets/de-at-C7UwE1rJ.js +1 -0
- package/admin/assets/de-ch-CsZ7YQc_.js +1 -0
- package/admin/assets/dv-DaVliwLd.js +1 -0
- package/admin/assets/el-DM_KqKEP.js +1 -0
- package/admin/assets/en-DedtOfaf.js +1 -0
- package/admin/assets/en-au-52Bzk5D9.js +1 -0
- package/admin/assets/en-ca-3pzEPK2N.js +1 -0
- package/admin/assets/en-gb-BrwDQS2G.js +1 -0
- package/admin/assets/en-ie-BUXSHrkL.js +1 -0
- package/admin/assets/en-il-a22drDCn.js +1 -0
- package/admin/assets/en-in-BUjecjkp.js +1 -0
- package/admin/assets/en-nz-Bbo7tnB_.js +1 -0
- package/admin/assets/en-sg-CZVDddmd.js +1 -0
- package/admin/assets/en-tt-DmSGwRia.js +1 -0
- package/admin/assets/eo-B71nkHZU.js +1 -0
- package/admin/assets/es-Dk6VCuuk.js +1 -0
- package/admin/assets/es-do-DMErY8ol.js +1 -0
- package/admin/assets/es-mx-BMRmqa3u.js +1 -0
- package/admin/assets/es-pr-CtBQz48p.js +1 -0
- package/admin/assets/es-us-CrDl5pnO.js +1 -0
- package/admin/assets/et-CO9OHqio.js +1 -0
- package/admin/assets/eu-Bip44atW.js +1 -0
- package/admin/assets/fa-CHbJ_dTM.js +1 -0
- package/admin/assets/fi-DKfoLmaQ.js +1 -0
- package/admin/assets/fo-DG1kOEfw.js +1 -0
- package/admin/assets/fr-DV73GZR4.js +1 -0
- package/admin/assets/fr-ca-BK-RoZiC.js +1 -0
- package/admin/assets/fr-ch-DjqEC5E_.js +1 -0
- package/admin/assets/fy-znrRQdeC.js +1 -0
- package/admin/assets/ga-BlZeKu0N.js +1 -0
- package/admin/assets/gd-BmrycMnC.js +1 -0
- package/admin/assets/gl-CuT8e5mi.js +1 -0
- package/admin/assets/gom-latn-BSWVd0A6.js +1 -0
- package/admin/assets/gu-BHK6LfvD.js +1 -0
- package/admin/assets/he-DPoTUevR.js +1 -0
- package/admin/assets/hi-BRuLafoW.js +1 -0
- package/admin/assets/hr-Bzge-10P.js +1 -0
- package/admin/assets/ht-Ck9BCna1.js +1 -0
- package/admin/assets/hu-CzqqbYmU.js +1 -0
- package/admin/assets/hy-am-C-eV4E8v.js +1 -0
- package/admin/assets/id-Dv8GZQvB.js +1 -0
- package/admin/assets/{index-DTxjaflW.js → index-BPIX0qPj.js} +1 -1
- package/admin/assets/index-CFWd-FDo.css +1 -0
- package/admin/assets/index-D3HviM6x.js +889 -0
- package/admin/assets/is-CK6VY3M_.js +1 -0
- package/admin/assets/it-1gtki4a5.js +1 -0
- package/admin/assets/it-ch-C0Mj3-pC.js +1 -0
- package/admin/assets/ja-Dl3AfnM1.js +1 -0
- package/admin/assets/jv-CznX-tGV.js +1 -0
- package/admin/assets/ka-BNjZxCug.js +1 -0
- package/admin/assets/kk-80J_xldf.js +1 -0
- package/admin/assets/km-CuWChDRB.js +1 -0
- package/admin/assets/kn-BZp_PBdl.js +1 -0
- package/admin/assets/ko-RirpyUl_.js +1 -0
- package/admin/assets/ku-Dz8ACD5w.js +1 -0
- package/admin/assets/ky-BN3ylOhj.js +1 -0
- package/admin/assets/lb-D7h_YoEn.js +1 -0
- package/admin/assets/lo-BrlbTUPD.js +1 -0
- package/admin/assets/lt-cXuHFdTa.js +1 -0
- package/admin/assets/lv-CjIpv13Q.js +1 -0
- package/admin/assets/me-B-jTh39Z.js +1 -0
- package/admin/assets/mi-D06xdVmt.js +1 -0
- package/admin/assets/mk-iLbuxyOf.js +1 -0
- package/admin/assets/ml-2W0y6Zb2.js +1 -0
- package/admin/assets/mn-6zbvKjeb.js +1 -0
- package/admin/assets/mr-7-jrgLyw.js +1 -0
- package/admin/assets/ms-CRH6rEXt.js +1 -0
- package/admin/assets/ms-my-BIJmu2S-.js +1 -0
- package/admin/assets/mt-CbXmxK-D.js +1 -0
- package/admin/assets/my-BvMUsxU8.js +1 -0
- package/admin/assets/nb-DvKHgF7L.js +1 -0
- package/admin/assets/ne-DiZZ3Lm6.js +1 -0
- package/admin/assets/nl-be-Dy7PYRbC.js +1 -0
- package/admin/assets/nl-t_2A_VAT.js +1 -0
- package/admin/assets/nn-Cw7EwosO.js +1 -0
- package/admin/assets/oc-lnc-CuFfB75K.js +1 -0
- package/admin/assets/pa-in-DOFyZ-Ft.js +1 -0
- package/admin/assets/pl-BD7FyCJj.js +1 -0
- package/admin/assets/pt-DSKLLE_u.js +1 -0
- package/admin/assets/pt-br-BhL4gb5Z.js +1 -0
- package/admin/assets/rn-DoHoZZPd.js +1 -0
- package/admin/assets/ro-B0v-_lH0.js +1 -0
- package/admin/assets/ru-BMVOk5eA.js +1 -0
- package/admin/assets/rw-CWF0w6eL.js +1 -0
- package/admin/assets/sd-DO2rrjch.js +1 -0
- package/admin/assets/se-CAolO9WQ.js +1 -0
- package/admin/assets/{sha512-D936QW8l.js → sha512-ZlUYj4Hr.js} +1 -1
- package/admin/assets/si-D03dHfb5.js +1 -0
- package/admin/assets/sk-_GcZGaN3.js +1 -0
- package/admin/assets/sl-Cb1lUGab.js +1 -0
- package/admin/assets/sq-Czzt23Tr.js +1 -0
- package/admin/assets/sr-D76dVqKJ.js +1 -0
- package/admin/assets/sr-cyrl-CuoFbJjW.js +1 -0
- package/admin/assets/ss-g-fGaM29.js +1 -0
- package/admin/assets/sv-CZxc8I45.js +1 -0
- package/admin/assets/sv-fi-D8REJeLz.js +1 -0
- package/admin/assets/sw-B1n3PjWG.js +1 -0
- package/admin/assets/ta-K5mexJNT.js +1 -0
- package/admin/assets/te-DT6dj5B6.js +1 -0
- package/admin/assets/tet-DXwYNm_H.js +1 -0
- package/admin/assets/tg-BCcZKcE2.js +1 -0
- package/admin/assets/th-CeeeseFX.js +1 -0
- package/admin/assets/tk-CJ6KW44d.js +1 -0
- package/admin/assets/tl-ph-DzE8lDmm.js +1 -0
- package/admin/assets/tlh-ER6KiMxG.js +1 -0
- package/admin/assets/tr-D48NGNpr.js +1 -0
- package/admin/assets/tzl-5AsmDTYM.js +1 -0
- package/admin/assets/tzm-2AzZ1YPf.js +1 -0
- package/admin/assets/tzm-latn-Cd5hPQuT.js +1 -0
- package/admin/assets/ug-cn-DU-MZ9Vx.js +1 -0
- package/admin/assets/uk-hn6vcOkn.js +1 -0
- package/admin/assets/ur-jOObtqB6.js +1 -0
- package/admin/assets/uz-BlftYfHF.js +1 -0
- package/admin/assets/uz-latn-BpuI0ccM.js +1 -0
- package/admin/assets/vi-DVo3LusT.js +1 -0
- package/admin/assets/x-pseudo-BuVzNhqi.js +1 -0
- package/admin/assets/yo-BvWGwb4m.js +1 -0
- package/admin/assets/zh-D9-tfba1.js +1 -0
- package/admin/assets/zh-cn-CFL5sbIW.js +1 -0
- package/admin/assets/zh-hk-DRP8u65r.js +1 -0
- package/admin/assets/zh-tw-CtVs1ihI.js +1 -0
- package/admin/index.html +2 -2
- package/frontend/assets/index-legacy-D3BTBYs5.js +9 -0
- package/frontend/assets/{index-legacy-vmpqwZZf.js → index-legacy-DcrWtKxQ.js} +1 -1
- package/frontend/assets/{sha512-legacy-wI89-UHR.js → sha512-legacy-DJvEwScE.js} +1 -1
- package/frontend/index.html +1 -1
- package/npm-shrinkwrap.json +144 -71
- package/package.json +9 -9
- package/plugins/antibrute/plugin.js +150 -19
- package/plugins/list-uploader/public/main.js +1 -1
- package/src/acme.js +11 -7
- package/src/api.accounts.js +3 -3
- package/src/api.auth.js +3 -1
- package/src/api.get_file_list.js +17 -13
- package/src/api.monitor.js +47 -43
- package/src/api.net.js +4 -3
- package/src/api.vfs.js +1 -1
- package/src/basicWeb.js +1 -1
- package/src/commands.js +54 -1
- package/src/comments.js +7 -4
- package/src/config.js +9 -5
- package/src/consoleLog.js +39 -1
- package/src/const.js +4 -0
- package/src/cross.js +33 -6
- package/src/errorPages.js +20 -10
- package/src/events.js +3 -3
- package/src/expiringCache.js +8 -7
- package/src/fileAttr.js +73 -15
- package/src/frontEndApis.js +20 -15
- package/src/index.js +2 -1
- package/src/langs/hfs-lang-ar.json +2 -1
- package/src/langs/hfs-lang-bg.json +2 -1
- package/src/langs/hfs-lang-de.json +2 -1
- package/src/langs/hfs-lang-el.json +2 -1
- package/src/langs/hfs-lang-es.json +2 -1
- package/src/langs/hfs-lang-fi.json +2 -1
- package/src/langs/hfs-lang-fr.json +2 -1
- package/src/langs/hfs-lang-hu.json +2 -1
- package/src/langs/hfs-lang-it.json +2 -1
- package/src/langs/hfs-lang-ja.json +2 -1
- package/src/langs/hfs-lang-ko.json +2 -1
- package/src/langs/hfs-lang-lt.json +2 -1
- package/src/langs/hfs-lang-ms.json +2 -0
- package/src/langs/hfs-lang-nl.json +2 -1
- package/src/langs/hfs-lang-pt-br.json +2 -1
- package/src/langs/hfs-lang-ro.json +2 -1
- package/src/langs/hfs-lang-ru.json +2 -1
- package/src/langs/hfs-lang-sr-latn.json +2 -1
- package/src/langs/hfs-lang-sr.json +2 -1
- package/src/langs/hfs-lang-th.json +2 -1
- package/src/langs/hfs-lang-tr.json +2 -1
- package/src/langs/hfs-lang-uk.json +2 -1
- package/src/langs/hfs-lang-vi.json +2 -1
- package/src/langs/hfs-lang-zh-tw.json +2 -1
- package/src/langs/hfs-lang-zh.json +2 -1
- package/src/listen.js +2 -1
- package/src/log.js +27 -2
- package/src/middlewares.js +8 -2
- package/src/misc.js +14 -0
- package/src/nat.js +61 -21
- package/src/outboundProxy.js +1 -1
- package/src/perm.js +5 -1
- package/src/plugins.js +34 -5
- package/src/roots.js +1 -1
- package/src/selfCheck.js +2 -1
- package/src/serveGuiAndSharedFiles.js +18 -7
- package/src/serveGuiFiles.js +2 -1
- package/src/update.js +10 -18
- package/src/urlList.js +32 -0
- package/src/util-files.js +24 -9
- package/src/util-http.js +4 -0
- package/src/vfs.js +21 -9
- package/src/walkDir.js +7 -1
- package/src/webdav.js +4 -1
- package/src/zip.js +3 -2
- package/admin/assets/index-B66w-a0v.css +0 -1
- package/admin/assets/index-Df6vYR7s.js +0 -822
- package/frontend/assets/index-legacy-emBsvICj.js +0 -9
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
"Create folder": "สร้างโฟลเดอร์",
|
|
58
58
|
"Pick files": "เลือกไฟล์",
|
|
59
59
|
"Pick folder": "เลือกโฟลเดอร์",
|
|
60
|
-
"
|
|
60
|
+
"ready_to_upload": "{n,plural,one{# ไฟล์} other{# ไฟล์}}, {size}, พร้อมอัปโหลด",
|
|
61
|
+
"Send": "ส่ง",
|
|
61
62
|
"Clear": "ล้าง",
|
|
62
63
|
"failed_upload": "ไม่สามารถอัปโหลด {name}",
|
|
63
64
|
"confirm_resume": "ดำเนินการอัปโหลดต่อหรือไม่?",
|
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
"Create folder": "Klasör Oluştur",
|
|
58
58
|
"Pick files": "Dosya(lar)ı Seç",
|
|
59
59
|
"Pick folder": "Klasör Seç",
|
|
60
|
-
"
|
|
60
|
+
"ready_to_upload": "{n,plural,one{# dosya} other{# dosya}}, {size}, yüklemeye hazır",
|
|
61
|
+
"Send": "Gönder",
|
|
61
62
|
"Clear": "Temizle",
|
|
62
63
|
"failed_upload": "{name} UPLOAD edilemedi (veri aktarım sorunu)",
|
|
63
64
|
"confirm_resume": "UPLOAD kaldığı yerden devam etsin mi?",
|
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
"Create folder": "Створити теку",
|
|
58
58
|
"Pick files": "Вибрати файли",
|
|
59
59
|
"Pick folder": "Вибрати теку",
|
|
60
|
-
"
|
|
60
|
+
"ready_to_upload": "{n,plural, one{# файл} few{# файли} many{# файлів}}, {size}, готово до завантаження",
|
|
61
|
+
"Send": "Завантажити",
|
|
61
62
|
"Clear": "Очистити",
|
|
62
63
|
"failed_upload": "Не вдалося завантажити {name}",
|
|
63
64
|
"confirm_resume": "Відновити завантаження?",
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"Create folder": "Tạo folder...",
|
|
59
59
|
"Pick files": "Chọn file...",
|
|
60
60
|
"Pick folder": "Chọn folder...",
|
|
61
|
-
"
|
|
61
|
+
"ready_to_upload": "{n,plural,one{# file} other{# file}}, {size}, sẵn sàng tải lên",
|
|
62
|
+
"Send": "Gửi",
|
|
62
63
|
"Clear": "Dọn.",
|
|
63
64
|
"failed_upload": "Không thể upload {name}!",
|
|
64
65
|
"confirm_resume": "Tiếp tục upload?",
|
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
"Create folder": "建立資料夾",
|
|
58
58
|
"Pick files": "選擇檔案",
|
|
59
59
|
"Pick folder": "選擇資料夾",
|
|
60
|
-
"
|
|
60
|
+
"ready_to_upload": "{n,plural,one{# 個檔案} other{# 個檔案}}, {size},準備上傳",
|
|
61
|
+
"Send": "傳送",
|
|
61
62
|
"Clear": "清空",
|
|
62
63
|
"failed_upload": "無法上傳 {name}",
|
|
63
64
|
"confirm_resume": "繼續上傳?",
|
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
"Create folder": "创建文件夹",
|
|
58
58
|
"Pick files": "选择文件",
|
|
59
59
|
"Pick folder": "选择文件夹",
|
|
60
|
-
"
|
|
60
|
+
"ready_to_upload": "{n,plural,other{# 个文件}},{size},准备上传",
|
|
61
|
+
"Send": "发送",
|
|
61
62
|
"Clear": "清除",
|
|
62
63
|
"failed_upload": "无法上传 {name}",
|
|
63
64
|
"confirm_resume": "是否重命名?",
|
package/src/listen.js
CHANGED
|
@@ -68,6 +68,7 @@ const persistence_1 = require("./persistence");
|
|
|
68
68
|
const argv_1 = require("./argv");
|
|
69
69
|
const consoleLog_1 = require("./consoleLog");
|
|
70
70
|
const first_1 = require("./first");
|
|
71
|
+
const fileAttr_1 = require("./fileAttr");
|
|
71
72
|
let httpSrv;
|
|
72
73
|
let httpsSrv;
|
|
73
74
|
// the update relaunch can keep a bridge process alive, so we proactively close listeners here to release ports before the next binary binds; do it before (5) the storage file is closed, because sockets write there
|
|
@@ -86,7 +87,7 @@ const commonServerOptions = {
|
|
|
86
87
|
};
|
|
87
88
|
// these are properties that can be assigned to the server object
|
|
88
89
|
const commonServerAssign = { headersTimeout: 30_000, timeout: misc_1.MINUTE }; // 'headersTimeout' is not recognized by type lib, and 'timeout' is not effective when passed in parameters
|
|
89
|
-
const readyToListen = Promise.all([persistence_1.storedMap.isOpening(), events_1.default.once('app')]);
|
|
90
|
+
const readyToListen = Promise.all([persistence_1.storedMap.isOpening(), fileAttr_1.fileAttrDb.isOpening(), events_1.default.once('app')]);
|
|
90
91
|
const considerHttp = (0, misc_1.debounceAsync)(async () => {
|
|
91
92
|
await readyToListen;
|
|
92
93
|
void stopServer(httpSrv);
|
package/src/log.js
CHANGED
|
@@ -38,6 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
};
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.logMw = exports.loggers = void 0;
|
|
41
|
+
exports.zipLogFile = zipLogFile;
|
|
41
42
|
exports.getRotatedFiles = getRotatedFiles;
|
|
42
43
|
const events_1 = require("events");
|
|
43
44
|
const config_1 = require("./config");
|
|
@@ -48,6 +49,9 @@ const util_files_1 = require("./util-files");
|
|
|
48
49
|
const auth_1 = require("./auth");
|
|
49
50
|
const misc_1 = require("./misc");
|
|
50
51
|
const path_1 = require("path");
|
|
52
|
+
const yazl_1 = __importDefault(require("yazl"));
|
|
53
|
+
const promises_1 = require("stream/promises");
|
|
54
|
+
const promises_2 = require("fs/promises");
|
|
51
55
|
const events_2 = __importDefault(require("./events"));
|
|
52
56
|
const connections_1 = require("./connections");
|
|
53
57
|
const index_1 = require("./index");
|
|
@@ -143,6 +147,7 @@ const logMw = async (ctx, next) => {
|
|
|
143
147
|
const newPath = (0, misc_1.strinsert)(path, path.length - (0, path_1.extname)(path).length, suffix);
|
|
144
148
|
try { // other logging requests shouldn't happen while we are renaming. Since this is very infrequent we can tolerate solving this by making it sync.
|
|
145
149
|
(0, fs_1.renameSync)(path, newPath);
|
|
150
|
+
void zipLogFile(newPath, last).catch(console.error);
|
|
146
151
|
}
|
|
147
152
|
catch (e) { // ok, rename failed, but this doesn't mean we ain't gonna log
|
|
148
153
|
console.error(e.message || String(e));
|
|
@@ -177,7 +182,7 @@ const logMw = async (ctx, next) => {
|
|
|
177
182
|
debounce(() => // once in a while we check if the file is still good (not deleted, etc), or we'll reopen it
|
|
178
183
|
(0, util_files_1.statWithTimeout)(logger.path).catch(() => logger.reopen())); // async = smoother but we may lose some entries
|
|
179
184
|
stream.write(util.format(format, ctx.ip, user || '-', date, ctx.method, uri, ctx.req.httpVersion, ctx.status, length?.toString() ?? '-', lodash_1.default.isEmpty(extra) ? '' : JSON.stringify(JSON.stringify(extra))));
|
|
180
|
-
});
|
|
185
|
+
}).catch(e => console.error('log completion:', e.message || String(e)));
|
|
181
186
|
};
|
|
182
187
|
exports.logMw = logMw;
|
|
183
188
|
events_2.default.once('app', () => {
|
|
@@ -185,13 +190,33 @@ events_2.default.once('app', () => {
|
|
|
185
190
|
lodash_1.default.merge(this.state, { logExtra: { ...anything, params } }); // params will be considered as parameters of the API
|
|
186
191
|
};
|
|
187
192
|
});
|
|
193
|
+
async function zipLogFile(path, touch) {
|
|
194
|
+
const zipPath = path + '.zip';
|
|
195
|
+
try {
|
|
196
|
+
const zip = new yazl_1.default.ZipFile();
|
|
197
|
+
const output = (0, fs_1.createWriteStream)(zipPath);
|
|
198
|
+
zip.addFile(path, (0, path_1.basename)(path));
|
|
199
|
+
zip.end();
|
|
200
|
+
await (0, promises_1.pipeline)(zip.outputStream, output);
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
await (0, promises_2.rm)(zipPath, { force: true }).catch(() => { });
|
|
204
|
+
throw e;
|
|
205
|
+
}
|
|
206
|
+
await (0, promises_2.utimes)(zipPath, touch, touch).catch(console.error);
|
|
207
|
+
await events_2.default.emitAsync('logRotated', { path, zipPath });
|
|
208
|
+
await (0, promises_2.unlink)(path).catch(console.error);
|
|
209
|
+
}
|
|
188
210
|
function doubleDigit(n) {
|
|
189
211
|
return n > 9 ? n : '0' + n;
|
|
190
212
|
}
|
|
191
213
|
async function getRotatedFiles() {
|
|
192
214
|
return Object.fromEntries(await Promise.all(exports.loggers.map(async (x) => {
|
|
193
215
|
const mask = (0, misc_1.strinsert)(x.path, x.path.length - (0, path_1.extname)(x.path).length, '-2*'); // including 2, initial digit of the year, will only take rotated files and not "-error"
|
|
194
|
-
|
|
216
|
+
const list = await Promise.all(['', '.zip'].map(async (postfix) => // before 3.2 rotated logs were not zipped, and even today there's a very small (negligible) chance that the zipping fails
|
|
217
|
+
(await (0, fast_glob_1.default)(mask + postfix, { stats: true }))
|
|
218
|
+
.map(x => ({ path: x.path, size: x.stats?.size }))));
|
|
219
|
+
return [x.name, list.flat()];
|
|
195
220
|
})));
|
|
196
221
|
}
|
|
197
222
|
// dump console.error to file
|
package/src/middlewares.js
CHANGED
|
@@ -52,7 +52,6 @@ const headRequests = async (ctx, next) => {
|
|
|
52
52
|
exports.headRequests = headRequests;
|
|
53
53
|
let proxyDetected;
|
|
54
54
|
const someSecurity = (ctx, next) => {
|
|
55
|
-
ctx.request.ip = (0, connections_1.normalizeIp)(ctx.ip);
|
|
56
55
|
const ss = ctx.session;
|
|
57
56
|
if (ss?.username && !ss?.[misc_1.ALLOW_SESSION_IP_CHANGE])
|
|
58
57
|
if (!ss.ip)
|
|
@@ -91,6 +90,8 @@ function getProxyDetected() {
|
|
|
91
90
|
return proxyDetected && { from: proxyDetected.socket.remoteAddress, for: proxyDetected.get('X-Forwarded-For') };
|
|
92
91
|
}
|
|
93
92
|
const prepareState = async (ctx, next) => {
|
|
93
|
+
// normalize once so auth, filters and logging agree on the same client address
|
|
94
|
+
ctx.request.ip = (0, connections_1.normalizeIp)(ctx.ip);
|
|
94
95
|
const s = ctx.session;
|
|
95
96
|
if (s?.username) {
|
|
96
97
|
if (s.ts < auth_1.invalidateSessionBefore.get(s?.username))
|
|
@@ -99,7 +100,8 @@ const prepareState = async (ctx, next) => {
|
|
|
99
100
|
}
|
|
100
101
|
// calculate these once and for all
|
|
101
102
|
ctx.state.connection = (0, connections_1.socket2connection)(ctx.socket);
|
|
102
|
-
|
|
103
|
+
// explicit credentials and existing sessions must take precedence, so a matching IP cannot override a chosen account
|
|
104
|
+
let a = await urlLogin() || await getHttpAccount() || !s?.username && autoLogin();
|
|
103
105
|
const loggedInNotBySession = a;
|
|
104
106
|
ctx.state.account = a ||= (0, perm_1.getAccount)(s?.username, false); // with least precedence, we consider session
|
|
105
107
|
if (a)
|
|
@@ -133,6 +135,10 @@ const prepareState = async (ctx, next) => {
|
|
|
133
135
|
}
|
|
134
136
|
catch { }
|
|
135
137
|
}
|
|
138
|
+
function autoLogin() {
|
|
139
|
+
// keep the mask direct so group inheritance cannot make identity depend on account order
|
|
140
|
+
return Object.values(perm_1.accounts.get()).find(a => (0, perm_1.accountCanLogin)(a) && a.auto_login_net && (0, misc_1.netMatches)(ctx.ip, a.auto_login_net, true));
|
|
141
|
+
}
|
|
136
142
|
};
|
|
137
143
|
exports.prepareState = prepareState;
|
|
138
144
|
function failAllowNet(ctx, a) {
|
package/src/misc.js
CHANGED
|
@@ -26,6 +26,7 @@ exports.same = same;
|
|
|
26
26
|
exports.asyncGeneratorToReadable = asyncGeneratorToReadable;
|
|
27
27
|
exports.apiAssertTypes = apiAssertTypes;
|
|
28
28
|
exports.createStreamLimiter = createStreamLimiter;
|
|
29
|
+
exports.retrySync = retrySync;
|
|
29
30
|
const path_1 = require("path");
|
|
30
31
|
__exportStar(require("./util-http"), exports);
|
|
31
32
|
__exportStar(require("./util-files"), exports);
|
|
@@ -155,3 +156,16 @@ function createStreamLimiter(limit) {
|
|
|
155
156
|
}
|
|
156
157
|
});
|
|
157
158
|
}
|
|
159
|
+
function retrySync(cb, attempts = 20, sleep = 500) {
|
|
160
|
+
const sleepSyncBuffer = new Int32Array(new SharedArrayBuffer(4));
|
|
161
|
+
for (let retry = 0;; retry++) {
|
|
162
|
+
try {
|
|
163
|
+
return cb();
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
if (e?.code !== 'EBUSY' || retry >= attempts)
|
|
167
|
+
throw e;
|
|
168
|
+
Atomics.wait(sleepSyncBuffer, 0, 0, sleep);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
package/src/nat.js
CHANGED
|
@@ -3,7 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getNatInfo = exports.getPublicIps = exports.
|
|
6
|
+
exports.getNatInfo = exports.getPublicIps = exports.upnpEnabled = exports.mappedPort = exports.defaultBaseUrl = void 0;
|
|
7
|
+
exports.upnpMappingParam = upnpMappingParam;
|
|
8
|
+
exports.getUpnpClient = getUpnpClient;
|
|
7
9
|
const valtio_1 = require("valtio");
|
|
8
10
|
const nat_upnp_1 = require("@rejetto/nat-upnp");
|
|
9
11
|
const debounceAsync_1 = require("./debounceAsync");
|
|
@@ -16,6 +18,7 @@ const net_1 = require("net");
|
|
|
16
18
|
const listen_1 = require("./listen");
|
|
17
19
|
const child_process_1 = require("child_process");
|
|
18
20
|
const const_1 = require("./const");
|
|
21
|
+
const config_1 = require("./config");
|
|
19
22
|
exports.defaultBaseUrl = (0, valtio_1.proxy)({
|
|
20
23
|
proto: 'http',
|
|
21
24
|
publicIps: [],
|
|
@@ -30,20 +33,23 @@ exports.defaultBaseUrl = (0, valtio_1.proxy)({
|
|
|
30
33
|
return `${this.proto}://${(0, cross_1.ipForUrl)(ip || 'localhost')}${!port || port === defPort ? '' : ':' + port}`;
|
|
31
34
|
}
|
|
32
35
|
});
|
|
33
|
-
exports.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (v === exports.defaultBaseUrl.externalIp)
|
|
36
|
+
exports.mappedPort = (0, config_1.defineConfig)('mapped_port', 0);
|
|
37
|
+
exports.upnpEnabled = (0, config_1.defineConfig)(cross_1.CFG.upnp_enabled, true);
|
|
38
|
+
let upnpClient;
|
|
39
|
+
// poll external ip – when UPnP is enabled, asking the modem is inexpensive, so it can be done often
|
|
40
|
+
(0, cross_1.repeat)(cross_1.MINUTE, async () => {
|
|
41
|
+
if (!await exports.upnpEnabled.getWhenReady())
|
|
42
|
+
return;
|
|
43
|
+
const v = await getUpnpClient().getPublicIp().catch(() => '');
|
|
44
|
+
if (!v || v === exports.defaultBaseUrl.externalIp)
|
|
43
45
|
return;
|
|
44
46
|
exports.getPublicIps.clearRetain();
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
+
exports.defaultBaseUrl.externalIp = v;
|
|
48
|
+
});
|
|
49
|
+
function upnpMappingParam(privatePort, publicPort, description = 'hfs', ttl = 0) {
|
|
50
|
+
// nat-upnp-rejetto requires the object form of `public` to preserve the host field correctly
|
|
51
|
+
return { private: privatePort, public: { host: '', port: publicPort }, description, ttl };
|
|
52
|
+
}
|
|
47
53
|
exports.getPublicIps = (0, debounceAsync_1.debounceAsync)(async () => {
|
|
48
54
|
const res = await (0, github_1.getProjectInfo)();
|
|
49
55
|
const groupedByVersion = Object.values(lodash_1.default.groupBy(res.publicIpServices, x => x.v ?? 4));
|
|
@@ -70,25 +76,36 @@ exports.getPublicIps = (0, debounceAsync_1.debounceAsync)(async () => {
|
|
|
70
76
|
throw "no good";
|
|
71
77
|
return validIps;
|
|
72
78
|
}))));
|
|
73
|
-
|
|
79
|
+
const ret = exports.defaultBaseUrl.publicIps = lodash_1.default.uniq(ips.flat());
|
|
80
|
+
if (!ret.length) // don't keep empty results for long
|
|
81
|
+
setTimeout(() => exports.getPublicIps.clearRetain(), 5_000);
|
|
82
|
+
return ret;
|
|
74
83
|
}, { retain: 10 * cross_1.MINUTE });
|
|
75
84
|
exports.getNatInfo = (0, debounceAsync_1.debounceAsync)(async () => {
|
|
85
|
+
const upnp = await exports.upnpEnabled.getWhenReady() ? getUpnpClient() : null;
|
|
76
86
|
const gatewayIpPromise = findGateway().catch(() => undefined);
|
|
77
|
-
const
|
|
87
|
+
const gw = upnp && await (0, cross_1.haveTimeout)(10_000, upnp.getGateway()).catch(() => null);
|
|
78
88
|
const status = await (0, listen_1.getServerStatus)();
|
|
79
|
-
|
|
80
|
-
console.debug("Mappings found:", mappings?.map(x => x.description).join(', ') || "none");
|
|
89
|
+
let mappings = gw && await (0, cross_1.haveTimeout)(5_000, upnp.getMappings())?.catch(() => null);
|
|
90
|
+
console.debug(gw ? "Mappings found:" : "Mappings not queried:", mappings?.map(x => x.description).join(', ') || (gw ? "none" : upnp ? "gateway not found" : "UPnP disabled"));
|
|
81
91
|
const localIps = await (0, listen_1.getIps)(false);
|
|
82
92
|
const gatewayIp = await gatewayIpPromise;
|
|
83
|
-
const localIp =
|
|
93
|
+
const localIp = gw?.address || (gatewayIp ? lodash_1.default.maxBy(localIps, x => (0, cross_1.inCommon)(x, gatewayIp)) : localIps[0]);
|
|
84
94
|
const internalPort = status?.https?.listening && status.https.port || status?.http?.listening && status.http.port || undefined;
|
|
85
|
-
|
|
95
|
+
let mapped = lodash_1.default.find(mappings, x => x.private.host === localIp && x.private.port === internalPort);
|
|
96
|
+
if (upnp && mappings && localIp && internalPort && !mapped && exports.mappedPort.get())
|
|
97
|
+
// restore the HFS-created mapping after routers that forget UPnP state across reboots
|
|
98
|
+
await (0, cross_1.haveTimeout)(5_000, upnp.createMapping(upnpMappingParam(internalPort, exports.mappedPort.get()))).then(async () => {
|
|
99
|
+
// confirm router state after restore instead of trusting the AddPortMapping result
|
|
100
|
+
mappings = await (0, cross_1.haveTimeout)(5_000, upnp.getMappings());
|
|
101
|
+
mapped = lodash_1.default.find(mappings, x => x.private.host === localIp && x.private.port === internalPort);
|
|
102
|
+
}).catch(e => console.warn('UPnP mapping restore failed:', e?.message || String(e)));
|
|
86
103
|
const externalPort = mapped?.public.port;
|
|
87
104
|
if (localIp)
|
|
88
105
|
exports.defaultBaseUrl.localIp = localIp;
|
|
89
106
|
exports.defaultBaseUrl.port = externalPort || internalPort || 0;
|
|
90
107
|
return {
|
|
91
|
-
upnp: Boolean(
|
|
108
|
+
upnp: Boolean(gw),
|
|
92
109
|
localIp,
|
|
93
110
|
gatewayIp,
|
|
94
111
|
externalIp: exports.defaultBaseUrl.externalIp,
|
|
@@ -99,7 +116,30 @@ exports.getNatInfo = (0, debounceAsync_1.debounceAsync)(async () => {
|
|
|
99
116
|
proto: status?.https?.listening ? 'https' : status?.http?.listening ? 'http' : '',
|
|
100
117
|
};
|
|
101
118
|
}, { reuseRunning: true });
|
|
102
|
-
|
|
119
|
+
exports.upnpEnabled.sub(v => {
|
|
120
|
+
exports.getNatInfo.clearRetain();
|
|
121
|
+
if (v) {
|
|
122
|
+
getUpnpClient().getGateway().then(res => console.log("UPnP found", res.gateway.description), e => console.debug('UPnP failed:', e.message || String(e)));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// closing the client guarantees the disabled setting also stops any existing SSDP socket
|
|
126
|
+
upnpClient?.close();
|
|
127
|
+
upnpClient = undefined;
|
|
128
|
+
exports.defaultBaseUrl.externalIp = '';
|
|
129
|
+
exports.getPublicIps.clearRetain();
|
|
130
|
+
});
|
|
131
|
+
config_1.configReady.then(exports.getNatInfo).catch(() => { });
|
|
132
|
+
function getUpnpClient() {
|
|
133
|
+
if (!exports.upnpEnabled.get()) // keep this guard upstream so disabled UPnP cannot leak SSDP traffic through callers
|
|
134
|
+
throw Error("UPnP disabled");
|
|
135
|
+
if (!upnpClient) {
|
|
136
|
+
upnpClient = new nat_upnp_1.Client({ timeout: 4_000 });
|
|
137
|
+
const originalMethod = upnpClient.getGateway;
|
|
138
|
+
// other client methods call getGateway too, so this will ensure they reuse this same result
|
|
139
|
+
upnpClient.getGateway = (0, debounceAsync_1.debounceAsync)(() => originalMethod.apply(upnpClient), { retain: cross_1.HOUR, retainFailure: 30_000 });
|
|
140
|
+
}
|
|
141
|
+
return upnpClient;
|
|
142
|
+
}
|
|
103
143
|
function findGateway() {
|
|
104
144
|
return new Promise((resolve, reject) => (0, child_process_1.exec)(const_1.IS_WINDOWS || const_1.IS_MAC ? 'netstat -rn' : 'route -n', (err, out) => {
|
|
105
145
|
if (err)
|
package/src/outboundProxy.js
CHANGED
|
@@ -22,7 +22,7 @@ const outboundProxy = (0, config_1.defineConfig)(cross_1.CFG.outbound_proxy, '',
|
|
|
22
22
|
return '';
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
-
config_1.configReady.then(async (
|
|
25
|
+
config_1.configReady.then(async (startedWithoutConfig) => {
|
|
26
26
|
if (!const_1.IS_WINDOWS || !startedWithoutConfig)
|
|
27
27
|
return;
|
|
28
28
|
// try to read Windows system setting for proxy
|
package/src/perm.js
CHANGED
|
@@ -18,6 +18,7 @@ exports.addAccount = addAccount;
|
|
|
18
18
|
exports.delAccount = delAccount;
|
|
19
19
|
exports.getFromAccount = getFromAccount;
|
|
20
20
|
exports.accountHasPassword = accountHasPassword;
|
|
21
|
+
exports.accountHasLoginMethod = accountHasLoginMethod;
|
|
21
22
|
exports.accountCanLogin = accountCanLogin;
|
|
22
23
|
exports.accountIsDisabled = accountIsDisabled;
|
|
23
24
|
exports.accountCanLoginAdmin = accountCanLoginAdmin;
|
|
@@ -208,8 +209,11 @@ function getFromAccount(account, getter) {
|
|
|
208
209
|
function accountHasPassword(account) {
|
|
209
210
|
return Boolean(account.password || account.srp);
|
|
210
211
|
}
|
|
212
|
+
function accountHasLoginMethod(account) {
|
|
213
|
+
return Boolean(accountHasPassword(account) || account.plugin?.auth || account.auto_login_net);
|
|
214
|
+
}
|
|
211
215
|
function accountCanLogin(account) {
|
|
212
|
-
return (
|
|
216
|
+
return accountHasLoginMethod(account) && !accountIsDisabled(account);
|
|
213
217
|
}
|
|
214
218
|
function accountIsDisabled(account) {
|
|
215
219
|
return Boolean(account.disabled
|
package/src/plugins.js
CHANGED
|
@@ -157,9 +157,30 @@ async function initPlugin(pl, morePassedToInit) {
|
|
|
157
157
|
const timeouts = [];
|
|
158
158
|
const controlledEvents = Object.create(events_1.default, (0, misc_1.objFromKeys)(['on', 'once', 'multi'], k => ({
|
|
159
159
|
value() {
|
|
160
|
+
if (k === 'multi')
|
|
161
|
+
arguments[0] = (0, misc_1.objSameKeys)(arguments[0], trap);
|
|
162
|
+
else
|
|
163
|
+
arguments[1] = trap(arguments[1]);
|
|
160
164
|
const ret = events_1.default[k](...arguments);
|
|
161
165
|
undoEvents.push(ret);
|
|
162
166
|
return ret;
|
|
167
|
+
function trap(cb) {
|
|
168
|
+
return (...args) => {
|
|
169
|
+
try {
|
|
170
|
+
if (!lodash_1.default.isFunction(cb))
|
|
171
|
+
return;
|
|
172
|
+
const ret = cb(...args);
|
|
173
|
+
return ret instanceof Promise ? ret.catch(printError) : ret;
|
|
174
|
+
}
|
|
175
|
+
catch (e) {
|
|
176
|
+
printError(e);
|
|
177
|
+
}
|
|
178
|
+
function printError(e) {
|
|
179
|
+
const { event } = args.at(-1);
|
|
180
|
+
console.error(`plugin ${morePassedToInit?.id || '?'} on event ${event}:`, e);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
163
184
|
}
|
|
164
185
|
})));
|
|
165
186
|
const res = await pl.init?.({
|
|
@@ -174,8 +195,16 @@ async function initPlugin(pl, morePassedToInit) {
|
|
|
174
195
|
timeouts.push(ret); // intervals can be canceled by clearTimeout (source: MDN)
|
|
175
196
|
return ret;
|
|
176
197
|
},
|
|
177
|
-
setTimeout() {
|
|
178
|
-
|
|
198
|
+
setTimeout(cb, delay = 0, ...args) {
|
|
199
|
+
let ret;
|
|
200
|
+
if (lodash_1.default.isFunction(cb)) {
|
|
201
|
+
const original = cb;
|
|
202
|
+
cb = function () {
|
|
203
|
+
lodash_1.default.pull(timeouts, ret);
|
|
204
|
+
return original.apply(this, args);
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
ret = setTimeout(cb, delay, ...args); // 'any' to allow build of frontend and admin
|
|
179
208
|
timeouts.push(ret);
|
|
180
209
|
return ret;
|
|
181
210
|
},
|
|
@@ -426,17 +455,17 @@ async function rescan() {
|
|
|
426
455
|
const patterns = [exports.PATH + '/*'];
|
|
427
456
|
if (const_1.APP_PATH !== process.cwd())
|
|
428
457
|
patterns.unshift((0, misc_1.escapeGlobPath)(const_1.APP_PATH) + '/' + patterns[0]); // first search bundled plugins, because otherwise they won't be loaded because of the folders with same name in .hfs/plugins (used for storage)
|
|
429
|
-
const existing =
|
|
458
|
+
const existing = new Set();
|
|
430
459
|
for (const { path, dirent } of await (0, fast_glob_1.default)(patterns, { onlyFiles: false, suppressErrors: true, objectMode: true })) {
|
|
431
460
|
if (!dirent.isDirectory() || path.endsWith(exports.DISABLING_SUFFIX))
|
|
432
461
|
continue;
|
|
433
462
|
const id = path.split('/').slice(-1)[0];
|
|
434
|
-
existing.
|
|
463
|
+
existing.add(id);
|
|
435
464
|
if (!pluginWatchers.has(id))
|
|
436
465
|
pluginWatchers.set(id, watchPlugin(id, (0, path_1.join)(path, exports.PLUGIN_MAIN_FILE)));
|
|
437
466
|
}
|
|
438
467
|
for (const [id, cancelWatcher] of pluginWatchers.entries())
|
|
439
|
-
if (!existing.
|
|
468
|
+
if (!existing.has(id)) {
|
|
440
469
|
enablePlugin(id, false);
|
|
441
470
|
cancelWatcher();
|
|
442
471
|
pluginWatchers.delete(id);
|
package/src/roots.js
CHANGED
|
@@ -32,7 +32,7 @@ const rootsMiddleware = (ctx, next) => (() => {
|
|
|
32
32
|
}
|
|
33
33
|
if (lodash_1.default.isEmpty(exports.roots.get()))
|
|
34
34
|
return;
|
|
35
|
-
const root = ctx.state.root = exports.roots.compiled()
|
|
35
|
+
const root = ctx.state.root = exports.roots.compiled()(ctx.host);
|
|
36
36
|
if (!ctx.state.skipFilters && forceAddress.get()
|
|
37
37
|
&& root === undefined && !(0, misc_1.isLocalHost)(ctx) && ctx.host !== listen_1.baseUrl.compiled())
|
|
38
38
|
return (0, connections_1.disconnect)(ctx, forceAddress.key()); // returning truthy will not call next
|
package/src/selfCheck.js
CHANGED
|
@@ -38,7 +38,8 @@ async function selfCheck(url) {
|
|
|
38
38
|
console.debug(svc);
|
|
39
39
|
body = applySymbols(body);
|
|
40
40
|
serviceUrl = applySymbols(serviceUrl);
|
|
41
|
-
const
|
|
41
|
+
const timeout = 8_000;
|
|
42
|
+
const res = await (0, cross_1.haveTimeout)(timeout, (0, util_http_1.httpString)(serviceUrl, { family, timeout, ...rest, body }));
|
|
42
43
|
const success = new RegExp(regexpSuccess).test(res);
|
|
43
44
|
const failure = new RegExp(regexpFailure).test(res);
|
|
44
45
|
if (success === failure)
|
|
@@ -28,6 +28,7 @@ const comments_1 = require("./comments");
|
|
|
28
28
|
const basicWeb_1 = require("./basicWeb");
|
|
29
29
|
const icons_1 = require("./icons");
|
|
30
30
|
const plugins_1 = require("./plugins");
|
|
31
|
+
const roots_1 = require("./roots");
|
|
31
32
|
const serveFrontendFiles = (0, serveGuiFiles_1.serveGuiFiles)(process.env.FRONTEND_PROXY, cross_const_1.FRONTEND_URI);
|
|
32
33
|
const serveFrontendPrefixed = (0, koa_mount_1.default)(cross_const_1.FRONTEND_URI.slice(0, -1), serveFrontendFiles);
|
|
33
34
|
const serveAdminFiles = (0, serveGuiFiles_1.serveGuiFiles)(process.env.ADMIN_PROXY, cross_const_1.ADMIN_URI);
|
|
@@ -110,6 +111,7 @@ const serveSharedFiles = async (ctx, next) => {
|
|
|
110
111
|
if ((await events_1.default.emitAsync('deleting', { node, ctx }))?.isDefaultPrevented())
|
|
111
112
|
return ctx.status = cross_const_1.HTTP_FAILED_DEPENDENCY;
|
|
112
113
|
await (0, promises_1.rm)(source, { recursive: true });
|
|
114
|
+
await (0, misc_1.deleteStoredFileAttrs)(source);
|
|
113
115
|
void (0, comments_1.setCommentFor)(source, ''); // necessary only to clean a possible descript.ion or kvstorage
|
|
114
116
|
return ctx.status = cross_const_1.HTTP_OK;
|
|
115
117
|
}
|
|
@@ -118,11 +120,10 @@ const serveSharedFiles = async (ctx, next) => {
|
|
|
118
120
|
return ctx.status = cross_const_1.HTTP_SERVER_ERROR;
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
|
-
if (
|
|
122
|
-
const found = await (0, vfs_1.
|
|
123
|
-
if (found && /\.html?/i.test(
|
|
123
|
+
if (path.endsWith('/') && !get) { // final slash needed on browsers to make resource urls working with html pages
|
|
124
|
+
const found = await (0, vfs_1.getDefaultFile)(node, ctx);
|
|
125
|
+
if (found && /\.html?/i.test((0, vfs_1.getNodeName)(node = found)))
|
|
124
126
|
ctx.state.considerAsGui = true;
|
|
125
|
-
node = found ?? node;
|
|
126
127
|
}
|
|
127
128
|
if (get === 'icon')
|
|
128
129
|
return (0, serveFile_1.serveFile)(ctx, node.icon || '|'); // pipe to cause not-found
|
|
@@ -159,9 +160,19 @@ async function sendFolderList(node, ctx) {
|
|
|
159
160
|
ctx.type = 'text';
|
|
160
161
|
if (prepend === undefined || prepend === '*') { // * = force auto-detection even if we have baseUrl set
|
|
161
162
|
const { URL } = ctx;
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
const requestBase = URL.protocol + '//' + URL.host + ctx.state.revProxyPath;
|
|
164
|
+
const configuredBaseUrl = prepend === undefined && listen_1.baseUrl.get();
|
|
165
|
+
const configuredRoot = configuredBaseUrl && roots_1.roots.compiled()(listen_1.baseUrl.compiled() || '');
|
|
166
|
+
const pathInConfiguredRoot = configuredRoot && (configuredRoot === '/' ? ctx.path
|
|
167
|
+
: ctx.path.startsWith(configuredRoot) ? ctx.path.slice(configuredRoot.length - 1)
|
|
168
|
+
: ctx.path === configuredRoot.slice(0, -1) ? '/'
|
|
169
|
+
: false);
|
|
170
|
+
// base_url may expose a host-rooted home; use it only for VFS paths inside that host root
|
|
171
|
+
const [base, path] = !configuredBaseUrl ? [requestBase, ctx.path]
|
|
172
|
+
: pathInConfiguredRoot === false ? [requestBase, ctx.path]
|
|
173
|
+
: [configuredBaseUrl, pathInConfiguredRoot || ctx.path];
|
|
174
|
+
// redo the encoding our way, keeping unicode chars unchanged. decode each segment separately because decodeURI preserves reserved escapes like %3A, which pathEncode would double-encode
|
|
175
|
+
prepend = base + (0, misc_1.pathDecodeSegments)(path, misc_1.pathEncode);
|
|
165
176
|
}
|
|
166
177
|
const walker = (0, vfs_1.walkNode)(node, { ctx, depth: depth === '*' ? Infinity : Number(depth), parallelizeRecursion: false }); // parallelization produces out-of-order results, and we don't want it like that here
|
|
167
178
|
ctx.body = (0, misc_1.asyncGeneratorToReadable)((0, misc_1.filterMapGenerator)(walker, async (el) => {
|
package/src/serveGuiFiles.js
CHANGED
|
@@ -39,7 +39,7 @@ function serveStatic(uri) {
|
|
|
39
39
|
return ctx.status = const_1.HTTP_METHOD_NOT_ALLOWED;
|
|
40
40
|
const serveApp = shouldServeApp(ctx);
|
|
41
41
|
const fullPath = (0, path_1.join)(__dirname, '..', folder, serveApp ? '/index.html' : ctx.path);
|
|
42
|
-
const content = await (0, misc_1.parseFile)(fullPath, raw => serveApp || !raw.length ? raw : adjustBundlerLinks(ctx, uri, raw))
|
|
42
|
+
const content = await (0, misc_1.parseFile)(fullPath, raw => serveApp || !raw.length ? raw : adjustBundlerLinks(ctx, uri, raw), 1000)
|
|
43
43
|
.catch(e => {
|
|
44
44
|
if (e?.code !== 'ENOENT') // not supposed to happen, and yet a user reported a strange behavior
|
|
45
45
|
console.error(`serveStatic/parseFile: ${String(e)}`);
|
|
@@ -170,6 +170,7 @@ function serveProxied(port, uri) {
|
|
|
170
170
|
let proxy;
|
|
171
171
|
import('koa-better-http-proxy').then(lib => // dynamic import to avoid having this in final distribution
|
|
172
172
|
proxy = lib.default('127.0.0.1:' + port, {
|
|
173
|
+
parseReqBody: false, // the dev GUI proxy serves app/assets, so avoid koa-better-http-proxy trying to reread ctx.req
|
|
173
174
|
proxyReqPathResolver: (ctx) => shouldServeApp(ctx) ? '/' : ctx.path,
|
|
174
175
|
userResDecorator(res, data, ctx) {
|
|
175
176
|
return shouldServeApp(ctx) ? treatIndex(ctx, uri, String(data))
|
package/src/update.js
CHANGED
|
@@ -27,6 +27,7 @@ const first_1 = require("./first");
|
|
|
27
27
|
const persistence_1 = require("./persistence");
|
|
28
28
|
const lodash_1 = __importDefault(require("lodash"));
|
|
29
29
|
const argv_1 = require("./argv");
|
|
30
|
+
const promises_2 = require("stream/promises");
|
|
30
31
|
const updateToBeta = (0, config_1.defineConfig)('update_to_beta', false);
|
|
31
32
|
const autoCheckUpdate = (0, config_1.defineConfig)('auto_check_update', true);
|
|
32
33
|
const lastCheckUpdate = persistence_1.storedMap.singleSync('lastCheckUpdate', 0);
|
|
@@ -127,9 +128,7 @@ async function update(tagOrUrl = '') {
|
|
|
127
128
|
throw "No update has been found";
|
|
128
129
|
const plat = '-' + (0, misc_1.xlate)(process.platform, { win32: 'windows', darwin: 'mac' });
|
|
129
130
|
const assetSearch = `${plat}-${process.arch}`;
|
|
130
|
-
const
|
|
131
|
-
const asset = update.assets.find((x) => x.name.includes(assetSearch) && x.name.endsWith('.zip'))
|
|
132
|
-
|| update.assets.find((x) => x.name.endsWith(legacyAssetSearch));
|
|
131
|
+
const asset = update.assets.find((x) => x.name.includes(assetSearch) && x.name.endsWith('.zip'));
|
|
133
132
|
if (!asset)
|
|
134
133
|
throw `Asset not found: ${assetSearch}`;
|
|
135
134
|
url = asset.browser_download_url;
|
|
@@ -139,7 +138,13 @@ async function update(tagOrUrl = '') {
|
|
|
139
138
|
const temp = LOCAL_UPDATE + '-temp';
|
|
140
139
|
await (0, promises_1.rm)(temp, { force: true });
|
|
141
140
|
try {
|
|
142
|
-
|
|
141
|
+
const stream = await (0, misc_1.httpStream)(url);
|
|
142
|
+
const total = Number(stream.headers['content-length']) || 0;
|
|
143
|
+
let downloadedSize = 0;
|
|
144
|
+
const progress = total && setInterval(() => console.log("Download progress", (0, misc_1.formatPerc)(downloadedSize / total)), 5_000);
|
|
145
|
+
stream.on('data', chunk => downloadedSize += chunk.length);
|
|
146
|
+
await (0, promises_2.pipeline)(stream, (0, fs_1.createWriteStream)(temp))
|
|
147
|
+
.finally(() => clearInterval(progress));
|
|
143
148
|
}
|
|
144
149
|
catch (e) {
|
|
145
150
|
await (0, promises_1.rm)(temp).catch(() => { }); // no leftovers
|
|
@@ -182,7 +187,7 @@ async function update(tagOrUrl = '') {
|
|
|
182
187
|
(0, fs_1.renameSync)(bin, oldBin);
|
|
183
188
|
if (!preserveTerminal) {
|
|
184
189
|
try {
|
|
185
|
-
|
|
190
|
+
(0, misc_1.retrySync)(() => (0, fs_1.renameSync)(newBin, (0, path_1.join)(binPath, binFile)));
|
|
186
191
|
}
|
|
187
192
|
catch (e) {
|
|
188
193
|
try {
|
|
@@ -209,19 +214,6 @@ async function update(tagOrUrl = '') {
|
|
|
209
214
|
throw e?.message || String(e);
|
|
210
215
|
}
|
|
211
216
|
}
|
|
212
|
-
function renameSyncWithBusyRetry(src, dest) {
|
|
213
|
-
const sleepSyncBuffer = new Int32Array(new SharedArrayBuffer(4));
|
|
214
|
-
for (let retry = 0;; retry++) {
|
|
215
|
-
try {
|
|
216
|
-
return (0, fs_1.renameSync)(src, dest);
|
|
217
|
-
}
|
|
218
|
-
catch (e) {
|
|
219
|
-
if (e?.code !== 'EBUSY' || retry >= 20)
|
|
220
|
-
throw e;
|
|
221
|
-
Atomics.wait(sleepSyncBuffer, 0, 0, 500);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
217
|
if (argv_1.argv.updating) { // we were launched with a temporary name, restore original name to avoid breaking references
|
|
226
218
|
const bin = process.execPath;
|
|
227
219
|
const dest = (0, path_1.join)((0, path_1.dirname)(bin), argv_1.argv.updating);
|