hfs 3.2.1 → 3.3.0-alpha1
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-U8Sl5sCM.js → af-CevhRske.js} +1 -1
- package/admin/assets/{am-n9TdXja0.js → am-4ouVTwuo.js} +1 -1
- package/admin/assets/{ar-CUpxfuw5.js → ar-71cK8dbb.js} +1 -1
- package/admin/assets/{ar-dz-DC58Z9cj.js → ar-dz-CO7oh1gG.js} +1 -1
- package/admin/assets/{ar-iq-BDkrto6W.js → ar-iq-CjG6db8J.js} +1 -1
- package/admin/assets/{ar-kw-Bz806GvG.js → ar-kw-DMsdR225.js} +1 -1
- package/admin/assets/{ar-ly-DW7JaKir.js → ar-ly-ZhxIwUOA.js} +1 -1
- package/admin/assets/{ar-ma-BuveKbkq.js → ar-ma-Bo3Z1RyK.js} +1 -1
- package/admin/assets/{ar-sa-DRvob1Jp.js → ar-sa-Db44biae.js} +1 -1
- package/admin/assets/{ar-tn-Cae7pfui.js → ar-tn-DNWRixE0.js} +1 -1
- package/admin/assets/{az-B7PpjwCU.js → az-B6LcmVzB.js} +1 -1
- package/admin/assets/{be-DSy_sPdD.js → be-Dp0KhO5x.js} +1 -1
- package/admin/assets/{bg-Djacfatx.js → bg-TsOJJvBm.js} +1 -1
- package/admin/assets/{bi-DOLGYGEN.js → bi-DstczOGo.js} +1 -1
- package/admin/assets/{bm-BmR133he.js → bm-Bf09NN15.js} +1 -1
- package/admin/assets/{bn-DsjLUmm3.js → bn-Da3tHLgx.js} +1 -1
- package/admin/assets/{bn-bd-C3SafRtu.js → bn-bd-CeSalisC.js} +1 -1
- package/admin/assets/{bo-DsMRUw9A.js → bo-Ctxe0IKQ.js} +1 -1
- package/admin/assets/{br-BxR8Zm95.js → br-Y3dPq3Tj.js} +1 -1
- package/admin/assets/{bs-gH68uvCb.js → bs-BbAlSeZx.js} +1 -1
- package/admin/assets/{ca-DfGL180b.js → ca-8-Nn97gi.js} +1 -1
- package/admin/assets/{cs-D4lFVAq6.js → cs-BD5ArFNW.js} +1 -1
- package/admin/assets/{cv-CFyqoRN7.js → cv-CdMFVXGr.js} +1 -1
- package/admin/assets/{cy-DuubtylZ.js → cy-B3QEYlIT.js} +1 -1
- package/admin/assets/{da-Cp7c-T-B.js → da-Bju2YQ9G.js} +1 -1
- package/admin/assets/{de-CeQXU72n.js → de-DdzCTquR.js} +1 -1
- package/admin/assets/{de-at-Cx01BLTk.js → de-at-DgyKCkPl.js} +1 -1
- package/admin/assets/{de-ch-K5Vn_bWQ.js → de-ch-Dk9qkzvn.js} +1 -1
- package/admin/assets/{dist-CfPsVFTp.js → dist-KY_8edrw.js} +1 -1
- package/admin/assets/{dv-DB4pdLQj.js → dv-C5KWK8o4.js} +1 -1
- package/admin/assets/{el-CNA5lg_z.js → el-BDL_lPlM.js} +1 -1
- package/admin/assets/{en-Bpua-Wb5.js → en-Ds3H1Wec.js} +1 -1
- package/admin/assets/{en-au-CKwJ7DKC.js → en-au-CgltfNUb.js} +1 -1
- package/admin/assets/{en-ca-D8vKBgWW.js → en-ca-DEsKzbWO.js} +1 -1
- package/admin/assets/{en-in-Bhx4y1uu.js → en-gb-21AYC3Ik.js} +1 -1
- package/admin/assets/{en-ie-0gd4plad.js → en-ie-aN3g-yqK.js} +1 -1
- package/admin/assets/{en-il-BYIz25Pw.js → en-il-CZl2d7NK.js} +1 -1
- package/admin/assets/{en-tt-Bk-if7Re.js → en-in-C7Kv4c3w.js} +1 -1
- package/admin/assets/{en-nz-CtIJOjgh.js → en-nz-DA_sIaGR.js} +1 -1
- package/admin/assets/{en-sg-iygWx8GW.js → en-sg-CP5PJpZf.js} +1 -1
- package/admin/assets/{en-gb-C3jRv20Y.js → en-tt-B5vDLhL7.js} +1 -1
- package/admin/assets/{eo-DAvER5ej.js → eo-VE1KDD1u.js} +1 -1
- package/admin/assets/{es-C98a0Ggy.js → es-SUzi5oTU.js} +1 -1
- package/admin/assets/{es-do-TCakoZXN.js → es-do-Iri0eWm8.js} +1 -1
- package/admin/assets/{es-mx-BCj7c6Zn.js → es-mx-DwneKeOc.js} +1 -1
- package/admin/assets/{es-pr-rsR3Q8oM.js → es-pr-C4zPUdA1.js} +1 -1
- package/admin/assets/{es-us-DC9NM1XV.js → es-us-BPS67etc.js} +1 -1
- package/admin/assets/{esm-BQaMpob3.js → esm-CyNQ7bBW.js} +1 -1
- package/admin/assets/{et-tJ-d1AZX.js → et-Ciqbfae0.js} +1 -1
- package/admin/assets/{eu-xzUajtaj.js → eu-CGhOwQpe.js} +1 -1
- package/admin/assets/{fa-kR15gQkC.js → fa-BWZiAv_E.js} +1 -1
- package/admin/assets/{fi-DQjRBOgG.js → fi-DJmgkVsJ.js} +1 -1
- package/admin/assets/{fo-BHMXHU45.js → fo-DVez5N-g.js} +1 -1
- package/admin/assets/{fr-DGS71Nvo.js → fr-D206itJo.js} +1 -1
- package/admin/assets/{fr-ca-Bkzyy0tT.js → fr-ca-C6JiEHfi.js} +1 -1
- package/admin/assets/{fr-ch-BRrWrRWV.js → fr-ch-DUTEuSU_.js} +1 -1
- package/admin/assets/{fy-Dkv180q7.js → fy-C_r06zH-.js} +1 -1
- package/admin/assets/{ga-xsoXuVDY.js → ga-CrdQqAN5.js} +1 -1
- package/admin/assets/{gd-cLPVqYdG.js → gd-iWHfhKAH.js} +1 -1
- package/admin/assets/{gl-BUG0DHTt.js → gl-zgfy-pQ0.js} +1 -1
- package/admin/assets/{gom-latn-BKRncAa1.js → gom-latn-CMZ7Vj7V.js} +1 -1
- package/admin/assets/{gu-BpmXErqK.js → gu-DNqIMjZc.js} +1 -1
- package/admin/assets/{he-DNxd9A8z.js → he-OJBbYFzL.js} +1 -1
- package/admin/assets/{hi-dNFE6t75.js → hi-ByrMhKpX.js} +1 -1
- package/admin/assets/{hr-DNZiZtn4.js → hr-CfRZXzCu.js} +1 -1
- package/admin/assets/{ht-B1LoCeln.js → ht-DDrMtLzJ.js} +1 -1
- package/admin/assets/{hu-q6SYps3j.js → hu-Ckeq3R6_.js} +1 -1
- package/admin/assets/{hy-am-BLa2HL3i.js → hy-am-OpZy7Nvg.js} +1 -1
- package/admin/assets/{id-4zvXdXSK.js → id-BE8-Ys79.js} +1 -1
- package/admin/assets/{index-D2UH6ajh.js → index-34fRsXFH.js} +128 -123
- package/admin/assets/{is-B7wmPDNy.js → is-BOxJcH5N.js} +1 -1
- package/admin/assets/{it-CGm31WTp.js → it-BFlrIXvP.js} +1 -1
- package/admin/assets/{it-ch-HfVNoF64.js → it-ch-ILAp-lZZ.js} +1 -1
- package/admin/assets/{ja-RGFi1Jc2.js → ja-CbbYAYyn.js} +1 -1
- package/admin/assets/{jv-BclbE5yu.js → jv-CuLkRQCH.js} +1 -1
- package/admin/assets/{ka-D3ZrxVjT.js → ka-BFeRDN-I.js} +1 -1
- package/admin/assets/{kk-B7aIapNy.js → kk-CMvQxaPk.js} +1 -1
- package/admin/assets/{km-DPwsGner.js → km-BdQbDUNF.js} +1 -1
- package/admin/assets/{kn-CuXmm_DG.js → kn-CE5AS6Nx.js} +1 -1
- package/admin/assets/{ko-BlPgy9aF.js → ko-Ctl1mHOt.js} +1 -1
- package/admin/assets/{ku-B6w51rl_.js → ku-Blbo7opr.js} +1 -1
- package/admin/assets/{ky-RLmSGZmf.js → ky-iCha_cZX.js} +1 -1
- package/admin/assets/{lb-AKcfj05J.js → lb-BaP0S6h1.js} +1 -1
- package/admin/assets/{lo-VBKOzJn1.js → lo-B_Q4zUMa.js} +1 -1
- package/admin/assets/{lt-53JaHFcf.js → lt-BOaP1KGX.js} +1 -1
- package/admin/assets/{lv-DGOgmu9w.js → lv-S1XMSUX1.js} +1 -1
- package/admin/assets/{me-zNrvFUXX.js → me-ChOLVJHC.js} +1 -1
- package/admin/assets/{mi-BBDE1uK4.js → mi-CnoIUOGl.js} +1 -1
- package/admin/assets/{mk-2LiSrAS9.js → mk-BoEaxVXE.js} +1 -1
- package/admin/assets/{ml-BEwbrVql.js → ml-BuvmVn6P.js} +1 -1
- package/admin/assets/{mn-LuMGd2Ic.js → mn-2bvGd1HI.js} +1 -1
- package/admin/assets/{mr-C_dFhleR.js → mr-BAoAWQmy.js} +1 -1
- package/admin/assets/{ms-BHnmZJI4.js → ms-C2eT9r-f.js} +1 -1
- package/admin/assets/{ms-my-FcVPbGSE.js → ms-my-D2ZXdcAf.js} +1 -1
- package/admin/assets/{mt-DzhvOV5Z.js → mt-BL5JZC4T.js} +1 -1
- package/admin/assets/{my-Mmp3v0PH.js → my-CuJ4L0Ji.js} +1 -1
- package/admin/assets/{nb-CYPvI8e3.js → nb-CjE6cT80.js} +1 -1
- package/admin/assets/{ne-BtVsV5BN.js → ne-BL9I0cb1.js} +1 -1
- package/admin/assets/{nl-C6aRDFNr.js → nl-Dt3mtMln.js} +1 -1
- package/admin/assets/{nl-be-X-pQSY5N.js → nl-be-Am1mjZK9.js} +1 -1
- package/admin/assets/{nn-C1m9zYBT.js → nn-ChSewyYy.js} +1 -1
- package/admin/assets/{oc-lnc-CFT7xZs-.js → oc-lnc-x6_ChCE9.js} +1 -1
- package/admin/assets/{pa-in-Bby4AmI2.js → pa-in-DVyN8o2P.js} +1 -1
- package/admin/assets/{pl-MmIf2tXf.js → pl-CqcuAUGV.js} +1 -1
- package/admin/assets/{pt-DHCh_z9j.js → pt-B2BPpD7K.js} +1 -1
- package/admin/assets/{pt-br-Cu8EXyIK.js → pt-br-oBVRyUzP.js} +1 -1
- package/admin/assets/{rn-DZUJ5hf0.js → rn-B3e3nkfp.js} +1 -1
- package/admin/assets/{ro-BRHg1GDk.js → ro-C2tWFx0Q.js} +1 -1
- package/admin/assets/{ru-Wv8fMN5W.js → ru-CCsEUnFL.js} +1 -1
- package/admin/assets/{rw-B_XP1EHM.js → rw-CaCkWlGA.js} +1 -1
- package/admin/assets/{sd-_X3CAPlB.js → sd-90Zixcez.js} +1 -1
- package/admin/assets/{se-FItkWaOK.js → se-DaZXpLsK.js} +1 -1
- package/admin/assets/{sha512-UQMP5wnF.js → sha512-DlGRFb_l.js} +1 -1
- package/admin/assets/{si-DhVYEiDt.js → si-CSoBAkgi.js} +1 -1
- package/admin/assets/{sk-cWZR9gF4.js → sk-pgQaqa2z.js} +1 -1
- package/admin/assets/{sl-CJckZkV8.js → sl-Cr5kSdA7.js} +1 -1
- package/admin/assets/{sq-B7eoM8fS.js → sq-Pxi2MZOP.js} +1 -1
- package/admin/assets/{sr-SfiQA7kg.js → sr-Fdc9-lC-.js} +1 -1
- package/admin/assets/{sr-cyrl-Db8Rrl0U.js → sr-cyrl-06jNWqpg.js} +1 -1
- package/admin/assets/{ss-Bian2WDB.js → ss-BjEzSMit.js} +1 -1
- package/admin/assets/{sv-D-Dsf8uN.js → sv-CqWqUpcA.js} +1 -1
- package/admin/assets/{sv-fi-BBv1GPVJ.js → sv-fi-CsO2xQHd.js} +1 -1
- package/admin/assets/{sw-BIzZvuq6.js → sw-xQUdgYhn.js} +1 -1
- package/admin/assets/{ta-C2JoHqar.js → ta-epbFrZ1M.js} +1 -1
- package/admin/assets/{te-ByKFS-hT.js → te-DusTwU6q.js} +1 -1
- package/admin/assets/{tet-Bq9OQkFH.js → tet-ylc9v4gl.js} +1 -1
- package/admin/assets/{tg-BPqYCigR.js → tg-2QfVTAJf.js} +1 -1
- package/admin/assets/{th-m4ghaYsj.js → th-QIm0Mh7I.js} +1 -1
- package/admin/assets/{tk-CFxWTO9D.js → tk-140KMZeW.js} +1 -1
- package/admin/assets/{tl-ph-BiBY15io.js → tl-ph-S7IZkbx3.js} +1 -1
- package/admin/assets/{tlh-CyBfXfLa.js → tlh-C2fYb34-.js} +1 -1
- package/admin/assets/{tr-69QiF4Yn.js → tr-Be-IM6i4.js} +1 -1
- package/admin/assets/{tzl-BG9jvswA.js → tzl-DMy30zde.js} +1 -1
- package/admin/assets/{tzm-IJGtGn0k.js → tzm-Cu882cHO.js} +1 -1
- package/admin/assets/{tzm-latn-CAelnev_.js → tzm-latn-BYxpYHet.js} +1 -1
- package/admin/assets/{ug-cn-CKlXJ1Cr.js → ug-cn-Cl8Jqk2E.js} +1 -1
- package/admin/assets/{uk-DazXNZ9b.js → uk-B-8XymtC.js} +1 -1
- package/admin/assets/{ur-BFAjqt9p.js → ur-ChGuAlvQ.js} +1 -1
- package/admin/assets/{uz-CjoAOeqe.js → uz-QbfaW7fT.js} +1 -1
- package/admin/assets/{uz-latn-CzIzARA0.js → uz-latn-DIQivLCy.js} +1 -1
- package/admin/assets/{vi-DZuOT62g.js → vi-C2CmYlDu.js} +1 -1
- package/admin/assets/{x-pseudo-DlpsSPis.js → x-pseudo-BWgm1XlP.js} +1 -1
- package/admin/assets/{yo-BvI53ejW.js → yo-8bNbPP6u.js} +1 -1
- package/admin/assets/{zh-ci4y7UyJ.js → zh-CeNSRZ4r.js} +1 -1
- package/admin/assets/{zh-cn-CwOh6Lhp.js → zh-cn-BlbF5C_X.js} +1 -1
- package/admin/assets/{zh-hk-CPgMd5VF.js → zh-hk-kg1c_VSK.js} +1 -1
- package/admin/assets/{zh-tw-DJiWMOSA.js → zh-tw-BCl4UsPA.js} +1 -1
- package/admin/index.html +1 -1
- package/frontend/assets/{dist-legacy-CFPnugjA.js → dist-legacy-DYE1EOcc.js} +1 -1
- package/frontend/assets/index-legacy-Br-Lpsst.js +1 -0
- package/frontend/assets/{lib-legacy-CCULxbTk.js → lib-legacy-C4Gw_0kX.js} +1 -1
- package/frontend/assets/{sha512-legacy-D8zcvvqa.js → sha512-legacy-D3RbPSpl.js} +1 -1
- package/frontend/index.html +1 -1
- package/npm-shrinkwrap.json +16 -12
- package/package.json +6 -5
- package/src/AsapStream.js +18 -14
- package/src/SendList.js +42 -16
- package/src/ThrottledStream.js +3 -3
- package/src/acme.js +23 -10
- package/src/adminApis.js +7 -6
- package/src/api.accounts.js +11 -7
- package/src/api.auth.js +8 -5
- package/src/api.cert.js +1 -1
- package/src/api.get_file_list.js +4 -3
- package/src/api.lang.js +3 -2
- package/src/api.monitor.js +1 -1
- package/src/api.net.js +3 -1
- package/src/api.plugins.js +2 -0
- package/src/api.vfs.js +5 -4
- package/src/apiMiddleware.js +5 -1
- package/src/auth.js +4 -1
- package/src/basicWeb.js +1 -1
- package/src/block.js +5 -4
- package/src/commands.js +6 -5
- package/src/comments.js +29 -13
- package/src/config.js +7 -4
- package/src/connections.js +3 -2
- package/src/consoleLog.js +5 -1
- package/src/cross-const.js +27 -1
- package/src/cross.js +32 -33
- package/src/ddns.js +1 -4
- package/src/events.js +1 -4
- package/src/fileAttr.js +6 -2
- package/src/frontEndApis.js +7 -3
- package/src/geo.js +8 -2
- package/src/github.js +10 -9
- package/src/index.js +1 -1
- package/src/ips.js +24 -3
- package/src/langs/embedded.js +2 -1
- package/src/langs/hfs-lang-ar.json +1 -0
- package/src/langs/hfs-lang-bg.json +1 -0
- package/src/langs/hfs-lang-de.json +1 -0
- package/src/langs/hfs-lang-el.json +1 -0
- package/src/langs/hfs-lang-es.json +1 -0
- package/src/langs/hfs-lang-fi.json +1 -0
- package/src/langs/hfs-lang-fr.json +1 -0
- package/src/langs/hfs-lang-hu.json +1 -0
- package/src/langs/hfs-lang-it.json +1 -0
- package/src/langs/hfs-lang-ja.json +1 -0
- package/src/langs/hfs-lang-ko.json +1 -0
- package/src/langs/hfs-lang-lt.json +1 -0
- package/src/langs/hfs-lang-ms.json +1 -0
- package/src/langs/hfs-lang-nl.json +1 -0
- package/src/langs/hfs-lang-pl.json +182 -0
- package/src/langs/hfs-lang-pt-br.json +1 -0
- package/src/langs/hfs-lang-ro.json +1 -0
- package/src/langs/hfs-lang-ru.json +1 -0
- package/src/langs/hfs-lang-sr-latn.json +1 -0
- package/src/langs/hfs-lang-sr.json +1 -0
- package/src/langs/hfs-lang-th.json +1 -0
- package/src/langs/hfs-lang-tr.json +1 -0
- package/src/langs/hfs-lang-uk.json +1 -0
- package/src/langs/hfs-lang-vi.json +1 -0
- package/src/langs/hfs-lang-zh-tw.json +1 -0
- package/src/langs/hfs-lang-zh.json +1 -0
- package/src/listen.js +18 -14
- package/src/log.js +11 -2
- package/src/middlewares.js +8 -6
- package/src/misc.js +3 -3
- package/src/multipartUpload.js +1 -2
- package/src/nat.js +5 -3
- package/src/outboundProxy.js +8 -0
- package/src/perm.js +5 -3
- package/src/plugins.js +58 -36
- package/src/selfCheck.js +8 -7
- package/src/serveFile.js +8 -6
- package/src/serveGuiAndSharedFiles.js +7 -6
- package/src/serveGuiFiles.js +13 -1
- package/src/serverCode.js +27 -0
- package/src/throttler.js +3 -2
- package/src/update.js +9 -3
- package/src/upload.js +30 -12
- package/src/uploadOwners.js +95 -0
- package/src/util-files.js +4 -2
- package/src/util-http.js +3 -1
- package/src/vfs.js +36 -28
- package/src/walkDir.js +24 -11
- package/src/watchLoad.js +13 -2
- package/src/webdav.js +16 -2
- package/src/zip.js +4 -3
- package/frontend/assets/index-legacy-CCOgiwQJ.js +0 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.uploadOwners = exports.ownUploadDeleteHours = void 0;
|
|
7
|
+
exports.setUploadOwner = setUploadOwner;
|
|
8
|
+
exports.moveUploadOwner = moveUploadOwner;
|
|
9
|
+
exports.deleteUploadOwner = deleteUploadOwner;
|
|
10
|
+
exports.getSessionId = getSessionId;
|
|
11
|
+
const kvstorage_1 = require("@rejetto/kvstorage");
|
|
12
|
+
const misc_1 = require("./misc");
|
|
13
|
+
const first_1 = require("./first");
|
|
14
|
+
const config_1 = require("./config");
|
|
15
|
+
const auth_1 = require("./auth");
|
|
16
|
+
const events_1 = __importDefault(require("./events"));
|
|
17
|
+
exports.ownUploadDeleteHours = (0, config_1.defineConfig)(misc_1.CFG.own_upload_delete_hours, 24);
|
|
18
|
+
exports.uploadOwners = new kvstorage_1.KvStorage({
|
|
19
|
+
rewriteLater: true,
|
|
20
|
+
});
|
|
21
|
+
exports.uploadOwners.open('upload-owners.kv').catch(e => console.error("Upload owners won't work correctly", e));
|
|
22
|
+
(0, first_1.onProcessExit)(() => exports.uploadOwners.close());
|
|
23
|
+
setInterval(() => {
|
|
24
|
+
if (!exports.uploadOwners.isOpen())
|
|
25
|
+
return;
|
|
26
|
+
const keys = Array.from(exports.uploadOwners.keys());
|
|
27
|
+
for (const k of keys) {
|
|
28
|
+
const owner = exports.uploadOwners.getSync(k);
|
|
29
|
+
if (owner && isExpired(owner))
|
|
30
|
+
void exports.uploadOwners.del(k);
|
|
31
|
+
}
|
|
32
|
+
}, misc_1.MINUTE);
|
|
33
|
+
events_1.default.on('checkVfsPermission', ({ node, perm, ctx }) => {
|
|
34
|
+
if (perm !== 'can_delete' || !node.source)
|
|
35
|
+
return;
|
|
36
|
+
const { vfsPath } = node;
|
|
37
|
+
if (!vfsPath || !exports.ownUploadDeleteHours.get() || !exports.uploadOwners.isOpen())
|
|
38
|
+
return;
|
|
39
|
+
const owner = exports.uploadOwners.getSync(cleanVfsPath(vfsPath));
|
|
40
|
+
if (!owner)
|
|
41
|
+
return;
|
|
42
|
+
if (isExpired(owner)) {
|
|
43
|
+
deleteUploadOwner(vfsPath);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const username = (0, auth_1.getCurrentUsername)(ctx);
|
|
47
|
+
const { sessionId } = owner;
|
|
48
|
+
if (sessionId && sessionId === ctx.session?.sessionId || username && owner.username === username)
|
|
49
|
+
return 0;
|
|
50
|
+
});
|
|
51
|
+
function setUploadOwner(vfsPath, ctx) {
|
|
52
|
+
if (!exports.uploadOwners.isOpen() || !exports.ownUploadDeleteHours.get())
|
|
53
|
+
return;
|
|
54
|
+
const username = (0, auth_1.getCurrentUsername)(ctx) || undefined;
|
|
55
|
+
return exports.uploadOwners.put(cleanVfsPath(vfsPath), {
|
|
56
|
+
username,
|
|
57
|
+
sessionId: username ? undefined : getSessionId(ctx),
|
|
58
|
+
ip: ctx.ip,
|
|
59
|
+
created: new Date(),
|
|
60
|
+
})?.catch(e => {
|
|
61
|
+
console.error("Couldn't store upload owner for", vfsPath, String(e.message || e));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async function moveUploadOwner(fromPath, toPath) {
|
|
65
|
+
if (!exports.uploadOwners.isOpen())
|
|
66
|
+
return;
|
|
67
|
+
const from = cleanVfsPath(fromPath);
|
|
68
|
+
const affected = Array.from(exports.uploadOwners.keys()).filter(k => isSameOrInside(from, k));
|
|
69
|
+
if (!affected.length)
|
|
70
|
+
return;
|
|
71
|
+
const to = cleanVfsPath(toPath);
|
|
72
|
+
const owners = affected.map(k => ({ k, owner: exports.uploadOwners.getSync(k) }));
|
|
73
|
+
// ownership is keyed by VFS path, so HFS moves must carry descendant upload records too
|
|
74
|
+
await Promise.all(owners.map(({ k, owner }) => owner && exports.uploadOwners.put(to + k.slice(from.length), owner)));
|
|
75
|
+
await Promise.all(affected.map(k => exports.uploadOwners.del(k)));
|
|
76
|
+
}
|
|
77
|
+
function deleteUploadOwner(vfsPath) {
|
|
78
|
+
if (exports.uploadOwners.isOpen())
|
|
79
|
+
// deleting a folder must clear ownership for uploaded files below it too
|
|
80
|
+
for (const k of Array.from(exports.uploadOwners.keys()).filter(k => isSameOrInside(cleanVfsPath(vfsPath), k)))
|
|
81
|
+
void exports.uploadOwners.del(k);
|
|
82
|
+
}
|
|
83
|
+
function isExpired(owner) {
|
|
84
|
+
const hours = exports.ownUploadDeleteHours.get();
|
|
85
|
+
return !hours || Number(owner.created) + hours * misc_1.HOUR <= Date.now();
|
|
86
|
+
}
|
|
87
|
+
function getSessionId(ctx) {
|
|
88
|
+
return ctx.session.sessionId ||= (0, misc_1.randomId)(30);
|
|
89
|
+
}
|
|
90
|
+
function cleanVfsPath(path) {
|
|
91
|
+
return '/' + path.replace(/^\/+|\/+$/g, '');
|
|
92
|
+
}
|
|
93
|
+
function isSameOrInside(parent, path) {
|
|
94
|
+
return path === parent || path.startsWith(parent + '/');
|
|
95
|
+
}
|
package/src/util-files.js
CHANGED
|
@@ -31,7 +31,7 @@ const const_1 = require("./const");
|
|
|
31
31
|
const promises_2 = require("stream/promises");
|
|
32
32
|
const stat_1 = require("./stat");
|
|
33
33
|
const unzipper_1 = __importDefault(require("unzipper"));
|
|
34
|
-
const fileTimeout = (0, config_1.defineConfig)(
|
|
34
|
+
const fileTimeout = (0, config_1.defineConfig)(cross_1.CFG.file_timeout, 3, x => x * 1000);
|
|
35
35
|
// a smart (and a bit arbitrary) way to decide if we need the stat-workers functionality. Without it, we may be a bit faster. We'll see with experience if we need a dedicated configuration.
|
|
36
36
|
const disableStatWorkers = Number(process.env.UV_THREADPOOL_SIZE) >= 10;
|
|
37
37
|
// some paths (mostly offline networked ones) can take 20+ seconds to fail
|
|
@@ -106,7 +106,9 @@ function escapeGlobPath(path) {
|
|
|
106
106
|
async function unzip(stream, cb) {
|
|
107
107
|
const extracted = new Map();
|
|
108
108
|
let chain = Promise.resolve();
|
|
109
|
-
|
|
109
|
+
const parser = unzipper_1.default.Parse();
|
|
110
|
+
stream.once('error', e => parser.destroy(e)); // pipe doesn't forward source errors, so make the parser reject and release its resources
|
|
111
|
+
return new Promise((resolve, reject) => stream.pipe(parser)
|
|
110
112
|
.on('close', () => chain.then(resolve, reject))
|
|
111
113
|
.on('error', reject)
|
|
112
114
|
.on('entry', (entry) => chain = chain.then(async () => {
|
package/src/util-http.js
CHANGED
|
@@ -64,6 +64,7 @@ async function httpWithBody(url, options) {
|
|
|
64
64
|
function httpStream(url, { body, proxy, jar, noRedirect, httpThrow = true, ...options } = {}, redirected = []) {
|
|
65
65
|
const controller = new AbortController();
|
|
66
66
|
options.signal ??= controller.signal;
|
|
67
|
+
options.localAddress ??= httpStream.defaultLocalAddress;
|
|
67
68
|
return Object.assign(new Promise(async (resolve, reject) => {
|
|
68
69
|
proxy ??= httpStream.defaultProxy;
|
|
69
70
|
options.headers ??= {};
|
|
@@ -140,7 +141,7 @@ function httpStream(url, { body, proxy, jar, noRedirect, httpThrow = true, ...op
|
|
|
140
141
|
if (options.timeout) // node only emits the timeout event, so destroy the request to unblock callers waiting for the body
|
|
141
142
|
req.setTimeout(options.timeout, () => req.destroy(Object.assign(Error('timeout'), { code: 'ETIMEDOUT' })));
|
|
142
143
|
if (body && body instanceof node_stream_1.Readable)
|
|
143
|
-
body.
|
|
144
|
+
body.on('error', e => req.destroy(e)).pipe(req);
|
|
144
145
|
else
|
|
145
146
|
req.end(body);
|
|
146
147
|
function connect() {
|
|
@@ -149,6 +150,7 @@ function httpStream(url, { body, proxy, jar, noRedirect, httpThrow = true, ...op
|
|
|
149
150
|
(proxyParsed.protocol === 'https:' ? node_https_1.default : node_http_1.default).request({
|
|
150
151
|
...proxyParsed,
|
|
151
152
|
auth: undefined, // void proxyParsed.auth
|
|
153
|
+
localAddress: options.localAddress, // the proxy socket is the actual outbound connection when tunneling HTTPS
|
|
152
154
|
method: 'CONNECT',
|
|
153
155
|
path,
|
|
154
156
|
headers: { Host: path, ...proxyAuth }
|
package/src/vfs.js
CHANGED
|
@@ -37,7 +37,12 @@ const comments_1 = require("./comments");
|
|
|
37
37
|
const walkDir_1 = require("./walkDir");
|
|
38
38
|
const node_stream_1 = require("node:stream");
|
|
39
39
|
const adminApis_1 = require("./adminApis");
|
|
40
|
-
const showHiddenFiles = (0, config_1.defineConfig)(
|
|
40
|
+
const showHiddenFiles = (0, config_1.defineConfig)(misc_1.CFG.show_hidden_files, false);
|
|
41
|
+
function setVfsPath(node, vfsPath, parent) {
|
|
42
|
+
return (0, misc_1.setHidden)(node, {
|
|
43
|
+
vfsPath: (0, misc_1.enforceFinal)('/', parent?.vfsPath) + (0, misc_1.pathEncode)(vfsPath)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
41
46
|
function permsFromParent(parent, child) {
|
|
42
47
|
const ret = {};
|
|
43
48
|
for (const k of misc_1.PERM_KEYS) {
|
|
@@ -81,20 +86,20 @@ function normalizeFilename(x) {
|
|
|
81
86
|
return (const_1.IS_WINDOWS || const_1.IS_MAC ? x.toLocaleLowerCase() : x).normalize();
|
|
82
87
|
}
|
|
83
88
|
async function applyParentToChild(child, parent, name) {
|
|
84
|
-
|
|
89
|
+
name ||= child ? getNodeName(child) : '';
|
|
90
|
+
const ret = setVfsPath({
|
|
85
91
|
original: child, // this can be overridden by passing an 'original' in `child`
|
|
86
92
|
...child,
|
|
87
93
|
isFolder: child?.isFolder ?? (child?.children?.length > 0 || undefined), // isFolder is hidden in original node, so we must copy it explicitly
|
|
88
94
|
isTemp: true,
|
|
89
95
|
parent,
|
|
90
|
-
};
|
|
91
|
-
name ||= child ? getNodeName(child) : '';
|
|
96
|
+
}, name, parent);
|
|
92
97
|
inheritMasks(ret, parent, name);
|
|
93
98
|
await parentMaskApplier(parent)(ret, name);
|
|
94
99
|
inheritFromParent(ret);
|
|
95
100
|
return ret;
|
|
96
101
|
}
|
|
97
|
-
async function urlToNode(url, ctx, parent = exports.vfs, allowMissing // true means missing path segments still resolve to temporary nodes with a computed source path
|
|
102
|
+
async function urlToNode(url, ctx, parent = exports.vfs.compiled(), allowMissing // true means missing path segments still resolve to temporary nodes with a computed source path
|
|
98
103
|
) {
|
|
99
104
|
let initialSlashes = 0;
|
|
100
105
|
while (url[initialSlashes] === '/')
|
|
@@ -112,6 +117,7 @@ async function urlToNode(url, ctx, parent = exports.vfs, allowMissing // true me
|
|
|
112
117
|
const ret = await getNodeByName(name, parent, assumeFolder);
|
|
113
118
|
if (!ret)
|
|
114
119
|
return;
|
|
120
|
+
setVfsPath(ret, name, parent);
|
|
115
121
|
if (rest || ret?.original)
|
|
116
122
|
return urlToNode(rest, ctx, ret, allowMissing);
|
|
117
123
|
if (ret.source)
|
|
@@ -164,7 +170,7 @@ async function getNodeByName(name, parent, assumeMissingToBeFolder = false) {
|
|
|
164
170
|
return ret;
|
|
165
171
|
}
|
|
166
172
|
}
|
|
167
|
-
const smartUncFolderDetection = (0, config_1.defineConfig)(
|
|
173
|
+
const smartUncFolderDetection = (0, config_1.defineConfig)(misc_1.CFG.smart_unc_folder_detection, false);
|
|
168
174
|
async function setIsFolder(node) {
|
|
169
175
|
if (!node.source)
|
|
170
176
|
return;
|
|
@@ -174,26 +180,27 @@ async function setIsFolder(node) {
|
|
|
174
180
|
(0, misc_1.setHidden)(node, { isFolder });
|
|
175
181
|
return isFolder;
|
|
176
182
|
}
|
|
177
|
-
|
|
178
|
-
(0, config_1.defineConfig)(
|
|
179
|
-
|
|
183
|
+
// compiled is the stable mutable tree, while get() may return a fresh clone of the default
|
|
184
|
+
exports.vfs = (0, config_1.defineConfig)(misc_1.CFG.vfs, {}, x => setVfsPath(structuredClone(x && typeof x === 'object' ? x : {}), '')); // ensure the type is right
|
|
185
|
+
exports.vfs.sub(async () => {
|
|
186
|
+
await reviewVfs();
|
|
180
187
|
console.log('VFS ready');
|
|
181
188
|
});
|
|
182
|
-
async function reviewVfs(
|
|
189
|
+
async function reviewVfs() {
|
|
183
190
|
await (async function recur(node) {
|
|
184
191
|
if (node.source && !node.children?.length && node.isFolder === undefined)
|
|
185
192
|
await setIsFolder(node);
|
|
186
193
|
if (node.children)
|
|
187
194
|
await Promise.allSettled(node.children.map(recur));
|
|
188
|
-
})(
|
|
189
|
-
exports.vfs = data;
|
|
195
|
+
})(exports.vfs.compiled());
|
|
190
196
|
}
|
|
191
197
|
exports.saveVfs = (0, misc_1.debounceAsync)(async () => {
|
|
192
|
-
await reviewVfs();
|
|
193
|
-
|
|
198
|
+
await reviewVfs(); // refresh runtime-derived folder flags before saving mutated VFS state
|
|
199
|
+
exports.vfs.set(exports.vfs.compiled()); // sync the mutable runtime tree back to config state before persisting it
|
|
200
|
+
(0, config_1.saveConfigAsap)();
|
|
194
201
|
});
|
|
195
202
|
function isRoot(node) {
|
|
196
|
-
return node === exports.vfs;
|
|
203
|
+
return node === exports.vfs.compiled();
|
|
197
204
|
}
|
|
198
205
|
function getNodeName(node) {
|
|
199
206
|
if (isRoot(node))
|
|
@@ -266,12 +273,9 @@ function statusCodeForMissingPerm(node, perm, ctx, assign = true) {
|
|
|
266
273
|
} while (1);
|
|
267
274
|
if ((0, misc_1.isWhoObject)(who) || isWhoVfsPerms(who))
|
|
268
275
|
throw Error(`permission type-guard: ${JSON.stringify(who)}`);
|
|
269
|
-
const
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
if (first !== undefined)
|
|
273
|
-
return first;
|
|
274
|
-
}
|
|
276
|
+
const first = lodash_1.default.max(events_1.default.emit('checkVfsPermission', { who, node, perm, ctx }));
|
|
277
|
+
if (first !== undefined)
|
|
278
|
+
return first;
|
|
275
279
|
return simpleWhoToError(who, ctx)
|
|
276
280
|
?? (0, misc_1.throw_)(Error(`invalid permission: ${perm}=${(0, misc_1.try_)(() => JSON.stringify(who))}`));
|
|
277
281
|
}
|
|
@@ -306,7 +310,7 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
306
310
|
const nodeName = getNodeName(child);
|
|
307
311
|
const name = prefixPath + nodeName;
|
|
308
312
|
taken?.add(normalizeFilename(name));
|
|
309
|
-
const item = { ...child, original: child, name, parent };
|
|
313
|
+
const item = setVfsPath({ ...child, original: child, name, parent }, name, parent);
|
|
310
314
|
if (await cantSee(item))
|
|
311
315
|
return;
|
|
312
316
|
if (item.source && !item.children?.length) // real items must be accessible, unless there's more to it
|
|
@@ -349,7 +353,7 @@ async function* walkNode(parent, { ctx, depth = Infinity, prefixPath = '', requi
|
|
|
349
353
|
const name = prefixPath + (renamed || path);
|
|
350
354
|
if (taken?.has(normalizeFilename(name))) // taken by vfs node above
|
|
351
355
|
return false; // false just in case it's a folder
|
|
352
|
-
const item = { name, isFolder, source: (0, path_1.join)(source, path), parent, stats: entry.stats };
|
|
356
|
+
const item = setVfsPath({ name, isFolder, source: (0, path_1.join)(source, path), parent, stats: entry.stats }, name, parent);
|
|
353
357
|
// masks containing '/' must be matched against the relative path while keeping walkDir recursion enabled
|
|
354
358
|
await pathMaskApplier(item, renamed || path);
|
|
355
359
|
if (await cantSee(item)) // can't see: don't produce and don't recur
|
|
@@ -482,13 +486,17 @@ events_1.default.on('accountRenamed', ({ from, to }) => {
|
|
|
482
486
|
if (n.masks)
|
|
483
487
|
Object.values(n.masks).forEach(renameInNode);
|
|
484
488
|
n.children?.forEach(renameInNode);
|
|
485
|
-
})(exports.vfs);
|
|
489
|
+
})(exports.vfs.compiled());
|
|
486
490
|
(0, exports.saveVfs)();
|
|
487
491
|
function renameInPerm(a) {
|
|
488
|
-
if (
|
|
492
|
+
if ((0, misc_1.isWhoObject)(a)) {
|
|
493
|
+
renameInPerm(a.this);
|
|
494
|
+
renameInPerm(a.children);
|
|
489
495
|
return;
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
496
|
+
}
|
|
497
|
+
if (Array.isArray(a))
|
|
498
|
+
for (let i = 0; i < a.length; i++)
|
|
499
|
+
if (a[i] === from)
|
|
500
|
+
a[i] = to;
|
|
493
501
|
}
|
|
494
502
|
});
|
package/src/walkDir.js
CHANGED
|
@@ -23,12 +23,12 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
23
23
|
return new Promise(async (resolve, reject) => {
|
|
24
24
|
if (!await (0, util_files_1.isDirectory)(path))
|
|
25
25
|
return reject(Error('ENOTDIR'));
|
|
26
|
-
dirQ.add(() => readDir('', depth)
|
|
26
|
+
dirQ.add(() => readDir('', depth, [path])
|
|
27
27
|
.then(res => {
|
|
28
28
|
Promise.resolve(res?.branchDone).then(resolve);
|
|
29
29
|
}, reject));
|
|
30
30
|
});
|
|
31
|
-
async function readDir(relativePath, depth) {
|
|
31
|
+
async function readDir(relativePath, depth, ancestorPaths) {
|
|
32
32
|
if (stopped)
|
|
33
33
|
return;
|
|
34
34
|
const base = (0, path_1.join)(path, relativePath);
|
|
@@ -39,6 +39,8 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
39
39
|
const pluginReceiver = lodash_1.default.isFunction(res) && res || null;
|
|
40
40
|
const pluginIterator = lodash_1.default.isFunction(res?.[Symbol.asyncIterator] || res?.[Symbol.iterator]) && res;
|
|
41
41
|
if (const_1.IS_WINDOWS && !pluginIterator) { // use native apis to read the 'hidden' attribute
|
|
42
|
+
// fswin callbacks cannot await, so track their work before closing the branch
|
|
43
|
+
const entriesWorking = [];
|
|
42
44
|
const direntMethods = {
|
|
43
45
|
isDir: false,
|
|
44
46
|
isFile() { return !this.isDir; },
|
|
@@ -46,14 +48,16 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
46
48
|
isBlockDevice() { return false; },
|
|
47
49
|
isCharacterDevice() { return false; },
|
|
48
50
|
};
|
|
49
|
-
await new Promise(
|
|
50
|
-
if (event !== 'FOUND')
|
|
51
|
-
|
|
51
|
+
await new Promise((resolve, reject) => fswin_1.default.find(base + '\\*', (event, f) => {
|
|
52
|
+
if (event !== 'FOUND') {
|
|
53
|
+
Promise.all(entriesWorking).then(() => resolve());
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
52
56
|
if (stopped)
|
|
53
57
|
return true; // stop signal
|
|
54
58
|
if (!hidden && f.IS_HIDDEN)
|
|
55
59
|
return;
|
|
56
|
-
work(Object.assign(Object.create(direntMethods), {
|
|
60
|
+
entriesWorking.push(work(Object.assign(Object.create(direntMethods), {
|
|
57
61
|
isDir: f.IS_DIRECTORY,
|
|
58
62
|
name: f.LONG_NAME,
|
|
59
63
|
stats: {
|
|
@@ -63,7 +67,7 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
63
67
|
isFile: () => !f.IS_DIRECTORY,
|
|
64
68
|
isDirectory: () => f.IS_DIRECTORY,
|
|
65
69
|
}
|
|
66
|
-
}));
|
|
70
|
+
}), Boolean(f.REPARSE_POINT_TAG)).catch(reject));
|
|
67
71
|
}, true));
|
|
68
72
|
}
|
|
69
73
|
else
|
|
@@ -72,7 +76,9 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
72
76
|
break;
|
|
73
77
|
if (!hidden && !const_1.IS_WINDOWS && entry.name[0] === '.')
|
|
74
78
|
continue;
|
|
75
|
-
|
|
79
|
+
// stat follows links, so preserve their identity for cycle detection
|
|
80
|
+
const isSymlink = entry.isSymbolicLink?.();
|
|
81
|
+
const stats = isSymlink && await (0, util_files_1.statWithTimeout)((0, path_1.join)(base, entry.name)).catch(() => null);
|
|
76
82
|
if (stats === null)
|
|
77
83
|
continue;
|
|
78
84
|
if (stats)
|
|
@@ -80,7 +86,7 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
80
86
|
const expanded = entry;
|
|
81
87
|
if (stats)
|
|
82
88
|
expanded.stats = stats;
|
|
83
|
-
await work(expanded);
|
|
89
|
+
await work(expanded, isSymlink);
|
|
84
90
|
}
|
|
85
91
|
pluginReceiver?.(!stopped);
|
|
86
92
|
const branchDone = Promise.allSettled(subDirsDone);
|
|
@@ -90,7 +96,7 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
90
96
|
closingQ.push(relativePath); // ok, we'll ask next one to carry this info
|
|
91
97
|
// don't return the promise directly, as this job ends here, but communicate to caller the promise for the whole branch
|
|
92
98
|
return { branchDone, n };
|
|
93
|
-
async function work(entry) {
|
|
99
|
+
async function work(entry, isSymlink = false) {
|
|
94
100
|
entry.path = (relativePath && relativePath + '/') + entry.name;
|
|
95
101
|
pluginReceiver?.(entry);
|
|
96
102
|
if (last && closingQ.length) // pending entries
|
|
@@ -104,9 +110,16 @@ function walkDir(path, { depth = 0, hidden = true, parallelizeRecursion = false,
|
|
|
104
110
|
n++;
|
|
105
111
|
if (!depth || !entry.isDirectory())
|
|
106
112
|
return;
|
|
113
|
+
const entryPath = (0, path_1.join)(base, entry.name);
|
|
114
|
+
if (isSymlink) {
|
|
115
|
+
// compare only with ancestors so separate links to the same folder still work
|
|
116
|
+
const [target, ...ancestors] = await Promise.all([entryPath, ...ancestorPaths].map(x => (0, promises_1.realpath)(x).catch(() => '')));
|
|
117
|
+
if (!target || ancestors.includes(target))
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
107
120
|
const branchDone = (0, cross_1.pendingPromise)(); // per-job
|
|
108
121
|
subDirsDone.push(branchDone);
|
|
109
|
-
const job = () => readDir(entry.path, depth - 1) // recur
|
|
122
|
+
const job = () => readDir(entry.path, depth - 1, [...ancestorPaths, entryPath]) // recur
|
|
110
123
|
.then(x => x, () => { }) // mute errors
|
|
111
124
|
.then(res => {
|
|
112
125
|
if (!res?.n)
|
package/src/watchLoad.js
CHANGED
|
@@ -12,8 +12,9 @@ const debounceAsync_1 = require("./debounceAsync");
|
|
|
12
12
|
const events_1 = require("./events");
|
|
13
13
|
function watchLoad(path, parser, { failedOnFirstAttempt, immediateFirst } = {}) {
|
|
14
14
|
let doing = false;
|
|
15
|
+
let cancelled = false;
|
|
15
16
|
let watcher;
|
|
16
|
-
const debounced = (0, debounceAsync_1.debounceAsync)(load, { wait: 500, maxWait: 1000, reuseRunning: true });
|
|
17
|
+
const debounced = (0, debounceAsync_1.debounceAsync)(load, { wait: 500, maxWait: 1000, reuseRunning: true, cancelable: true });
|
|
17
18
|
let retry;
|
|
18
19
|
let last;
|
|
19
20
|
const emitter = new events_1.BetterEventEmitter();
|
|
@@ -27,6 +28,8 @@ function watchLoad(path, parser, { failedOnFirstAttempt, immediateFirst } = {})
|
|
|
27
28
|
install(true);
|
|
28
29
|
return { unwatch, save, emitter, getText: () => last, getPath: () => path };
|
|
29
30
|
function install(first = false) {
|
|
31
|
+
if (cancelled)
|
|
32
|
+
return;
|
|
30
33
|
try {
|
|
31
34
|
watcher = (0, fs_1.watch)(path, () => {
|
|
32
35
|
if (!save.isWorking())
|
|
@@ -43,6 +46,12 @@ function watchLoad(path, parser, { failedOnFirstAttempt, immediateFirst } = {})
|
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
function unwatch() {
|
|
49
|
+
// a pending or running load must not resurrect a watcher cancelled by its owner
|
|
50
|
+
cancelled = true;
|
|
51
|
+
debounced.cancel();
|
|
52
|
+
closeWatcher();
|
|
53
|
+
}
|
|
54
|
+
function closeWatcher() {
|
|
46
55
|
watcher?.close();
|
|
47
56
|
clearTimeout(retry);
|
|
48
57
|
watcher = undefined;
|
|
@@ -61,12 +70,14 @@ function watchLoad(path, parser, { failedOnFirstAttempt, immediateFirst } = {})
|
|
|
61
70
|
return '';
|
|
62
71
|
throw e;
|
|
63
72
|
});
|
|
73
|
+
if (cancelled)
|
|
74
|
+
return;
|
|
64
75
|
if (text === last)
|
|
65
76
|
return;
|
|
66
77
|
last = text;
|
|
67
78
|
emitter.emit('change', last);
|
|
68
79
|
console.debug('Loaded', path);
|
|
69
|
-
|
|
80
|
+
closeWatcher();
|
|
70
81
|
install(); // reinstall, as the original file could have been renamed. We watch by the name.
|
|
71
82
|
await parser(text);
|
|
72
83
|
}
|
package/src/webdav.js
CHANGED
|
@@ -24,6 +24,7 @@ const expiringCache_1 = require("./expiringCache");
|
|
|
24
24
|
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
25
25
|
const lodash_1 = __importDefault(require("lodash"));
|
|
26
26
|
const fileAttr_1 = require("./fileAttr");
|
|
27
|
+
const uploadOwners_1 = require("./uploadOwners");
|
|
27
28
|
const forceWebdavLogin = (0, config_1.defineConfig)(cross_1.CFG.force_webdav_login, true, compileWebdavAgentRegex);
|
|
28
29
|
const webdavInitialAuth = (0, config_1.defineConfig)(cross_1.CFG.webdav_initial_auth, 'WebDAVFS', compileWebdavAgentRegex);
|
|
29
30
|
const webdavPrompted = (0, expiringCache_1.expiringCache)(cross_1.DAY);
|
|
@@ -151,6 +152,7 @@ const webdav = async (ctx, next) => {
|
|
|
151
152
|
if (node?.source)
|
|
152
153
|
await (0, promises_1.rm)(node.source)
|
|
153
154
|
.then(() => (0, fileAttr_1.deleteStoredFileAttrs)(node.source))
|
|
155
|
+
.then(() => (0, uploadOwners_1.deleteUploadOwner)(path))
|
|
154
156
|
.catch(() => { });
|
|
155
157
|
}
|
|
156
158
|
if (x && ctx.length === undefined) // missing length can make PUT fail
|
|
@@ -206,7 +208,7 @@ const webdav = async (ctx, next) => {
|
|
|
206
208
|
if ((0, path_1.dirname)(path) === (0, path_1.dirname)(dest)) // rename case. `path` is is encoded, so we test before decoding `dest`
|
|
207
209
|
try {
|
|
208
210
|
// decode the single path segment so reserved chars like %2C become their real name on rename
|
|
209
|
-
await (0, frontEndApis_1.requestedRename)(node, (0, cross_1.safeDecodeURIComponent)((0, path_1.basename)(dest), ''), ctx);
|
|
211
|
+
await (0, frontEndApis_1.requestedRename)(node, (0, cross_1.safeDecodeURIComponent)((0, path_1.basename)(dest), ''), ctx, path);
|
|
210
212
|
releaseWebdavLock(path); // RFC 4918 says MOVE must not carry locks to destination, so clear source lock on success
|
|
211
213
|
return ctx.status = cross_1.HTTP_CREATED;
|
|
212
214
|
}
|
|
@@ -246,6 +248,18 @@ const webdav = async (ctx, next) => {
|
|
|
246
248
|
}
|
|
247
249
|
async function handleLock() {
|
|
248
250
|
setWebdavHeaders();
|
|
251
|
+
// a lock reserves a future write, so authorize it against the existing resource or its parent
|
|
252
|
+
const node = await (0, vfs_1.urlToNode)(path, ctx);
|
|
253
|
+
const permissionNode = node || await (0, vfs_1.urlToNode)((0, path_1.dirname)(path), ctx);
|
|
254
|
+
if (!permissionNode)
|
|
255
|
+
return ctx.status = cross_1.HTTP_CONFLICT;
|
|
256
|
+
const missingWritePerm = node && canOverwrite.has(path + (0, cross_1.prefix)('|', (0, auth_1.getCurrentUsername)(ctx))) ? 0
|
|
257
|
+
: (0, vfs_1.statusCodeForMissingPerm)(permissionNode, node ? 'can_delete' : 'can_upload', ctx);
|
|
258
|
+
if (missingWritePerm) {
|
|
259
|
+
if (ctx.status === cross_1.HTTP_UNAUTHORIZED)
|
|
260
|
+
setWebdavHeaders(true);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
249
263
|
const body = ctx.length || ctx.get('content-length') || ctx.get('transfer-encoding') ? await (0, consumers_1.text)(ctx.req) : '';
|
|
250
264
|
const token = getProvidedLockToken();
|
|
251
265
|
let seconds = Number(ctx.get('timeout').split(',').find(x => /^Second-\d+$/i.test(x.trim()))?.trim().split('-', 2)[1]);
|
|
@@ -321,13 +335,13 @@ const webdav = async (ctx, next) => {
|
|
|
321
335
|
res.write(`<response>
|
|
322
336
|
<href>${lodash_1.default.escape(outPath + (append ? (0, cross_1.pathEncode)(name, true) + (isDir ? '/' : '') : ''))}</href>
|
|
323
337
|
<propstat>
|
|
324
|
-
<status>HTTP/1.1 200 OK</status>
|
|
325
338
|
<prop>
|
|
326
339
|
${(0, cross_1.prefix)('<getlastmodified>', st?.mtime?.toGMTString(), '</getlastmodified>')}
|
|
327
340
|
${(0, cross_1.prefix)('<creationdate>', (st?.birthtime || st?.ctime)?.toISOString().replace(/\..*/, '-00:00'), '</creationdate>')}
|
|
328
341
|
${isDir ? '<resourcetype><collection/></resourcetype>'
|
|
329
342
|
: `<resourcetype/><getcontentlength>${st?.size}</getcontentlength>`}
|
|
330
343
|
</prop>
|
|
344
|
+
<status>HTTP/1.1 200 OK</status>
|
|
331
345
|
</propstat>
|
|
332
346
|
</response>
|
|
333
347
|
`);
|
package/src/zip.js
CHANGED
|
@@ -39,8 +39,9 @@ async function zipStreamFromFolder(node, ctx) {
|
|
|
39
39
|
continue;
|
|
40
40
|
if ((0, vfs_1.nodeIsFolder)(subNode)) { // a directory needs to be walked
|
|
41
41
|
if ((0, vfs_1.hasPermission)(subNode, 'can_list', ctx) && (0, vfs_1.hasPermission)(subNode, 'can_archive', ctx)) {
|
|
42
|
-
|
|
43
|
-
yield
|
|
42
|
+
const archivePath = (0, misc_1.pathDecodeSegments)(uri);
|
|
43
|
+
yield { ...subNode, name: archivePath }; // keep empty selected folders at the same path used for their contents
|
|
44
|
+
yield* (0, vfs_1.walkNode)(subNode, { ctx, prefixPath: archivePath + '/', requiredPerm: 'can_archive' });
|
|
44
45
|
}
|
|
45
46
|
continue;
|
|
46
47
|
}
|
|
@@ -93,4 +94,4 @@ async function zipStreamFromFolder(node, ctx) {
|
|
|
93
94
|
ctx.state.archive = 'zip';
|
|
94
95
|
(0, serveFile_1.monitorAsDownload)(ctx, size, range?.start);
|
|
95
96
|
}
|
|
96
|
-
const zipSeconds = (0, config_1.defineConfig)(
|
|
97
|
+
const zipSeconds = (0, config_1.defineConfig)(misc_1.CFG.zip_calculate_size_for_seconds, 5);
|