hfs 3.2.2 → 3.3.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/AccountsPage-DB7ljNHT.js +3 -0
- package/admin/assets/ArrayField-Qj2iHD7I.js +2 -0
- package/admin/assets/ConfigFilePage-TErmt0PK.js +4 -0
- package/admin/assets/CustomHtmlPage-CVmk-_JS.js +2 -0
- package/admin/assets/DataTable-DmnQXyFO.js +119 -0
- package/admin/assets/DateTimeField-BUWd2w9-.js +1 -0
- package/admin/assets/EventManager-9vwKg8as.js +1 -0
- package/admin/assets/FileField-DoBaSTxF.js +117 -0
- package/admin/assets/InternetPage-Biuer1tb.js +8 -0
- package/admin/assets/LangPage-Vd24DNK5.js +2 -0
- package/admin/assets/LogsPage-BdvttEqM.js +3 -0
- package/admin/assets/MenuButton-CHLgqU5W.js +1 -0
- package/admin/assets/MonitorPage-DlXkZUmT.js +2 -0
- package/admin/assets/OptionsPage-Zi8FmrjE.js +3 -0
- package/admin/assets/PluginsPage-BzwEIFt5.js +1 -0
- package/admin/assets/Tabs-CVipqhB9.js +1 -0
- package/admin/assets/TextEditor-BeS-mR2I.css +1 -0
- package/admin/assets/TextEditor-clbVugYM.js +34 -0
- package/admin/assets/VfsPathField-YUCV9PfL.js +1 -0
- package/admin/assets/{af-BDoqNBMg.js → af-B1Gi_bpo.js} +1 -1
- package/admin/assets/agentIcons-DW9QViyx.js +1 -0
- package/admin/assets/{am-DXBvKJ3X.js → am-B-dZnhsM.js} +1 -1
- package/admin/assets/{ar-BJIoxKTh.js → ar-DsQGp6kZ.js} +1 -1
- package/admin/assets/{ar-dz-C9l3uc_3.js → ar-dz-CxlYiKtP.js} +1 -1
- package/admin/assets/{ar-iq-DKH8u2lX.js → ar-iq-y4_Puo4B.js} +1 -1
- package/admin/assets/{ar-kw-D3gJalQp.js → ar-kw-DTOZ0VC7.js} +1 -1
- package/admin/assets/{ar-ly-BZJzWFeR.js → ar-ly-lPV9gApv.js} +1 -1
- package/admin/assets/{ar-ma-DabiOCr0.js → ar-ma-DZqLSpeB.js} +1 -1
- package/admin/assets/{ar-sa-C7suzSCY.js → ar-sa-v-5vsIOR.js} +1 -1
- package/admin/assets/{ar-tn-BvBucEx0.js → ar-tn-a4q8lhXy.js} +1 -1
- package/admin/assets/{az-CPUppH2M.js → az-Cj4yq-j-.js} +1 -1
- package/admin/assets/baseUrl-Do5CYuBZ.js +2 -0
- package/admin/assets/{be-vzvhX2dl.js → be-B-3QlFM8.js} +1 -1
- package/admin/assets/{bg-09Xbj_Y3.js → bg-A8-93xdK.js} +1 -1
- package/admin/assets/{bi-CvhiuiE9.js → bi-bwwEWGG2.js} +1 -1
- package/admin/assets/{bm-DtJAZAkZ.js → bm-DpLmtYXC.js} +1 -1
- package/admin/assets/{bn-Dnzh2x6I.js → bn-DPL_BgLw.js} +1 -1
- package/admin/assets/{bn-bd-xEy4797g.js → bn-bd-DukmY_R5.js} +1 -1
- package/admin/assets/{bo-vuPmptG3.js → bo-68KDEKGI.js} +1 -1
- package/admin/assets/{br-C9xH4OD3.js → br-gA86uwqY.js} +1 -1
- package/admin/assets/{bs-BB_uCUp1.js → bs-SfthSp42.js} +1 -1
- package/admin/assets/{ca-D7LKaf98.js → ca-8qUtZKFe.js} +1 -1
- package/admin/assets/{cs-DMFTNdNC.js → cs-CTHcqPCX.js} +1 -1
- package/admin/assets/{cv-DNUjJwnO.js → cv-BhBW7hcO.js} +1 -1
- package/admin/assets/{cy-qmAOTPwC.js → cy-BYTS4PNY.js} +1 -1
- package/admin/assets/{da-DDX54iM9.js → da-BiwfOrRd.js} +1 -1
- package/admin/assets/{de-BBmlsfbM.js → de-BFqyDxvo.js} +1 -1
- package/admin/assets/{de-at-CuMZqYfu.js → de-at-C7zUXXVL.js} +1 -1
- package/admin/assets/{de-ch-s8OXKb-5.js → de-ch-BVHosAzt.js} +1 -1
- package/admin/assets/dist-DC2A6cD2.js +1 -0
- package/admin/assets/{dv-Cjs3QDBN.js → dv-dvig99nB.js} +1 -1
- package/admin/assets/{el-BsAOrUDP.js → el-BtJcYjoT.js} +1 -1
- package/admin/assets/en-B26lXzkx.js +1 -0
- package/admin/assets/{en-au-8tMHM2OT.js → en-au-CWzJ565h.js} +1 -1
- package/admin/assets/{en-ca-c9jM4M_8.js → en-ca-nou5bZ6q.js} +1 -1
- package/admin/assets/{en-in-D2ULzXMp.js → en-gb-kl3giCnh.js} +1 -1
- package/admin/assets/{en-ie-D4_e3kV6.js → en-ie-C55e3apX.js} +1 -1
- package/admin/assets/{en-il-BoaZOven.js → en-il-HYabE6j7.js} +1 -1
- package/admin/assets/{en-tt-Cn72OR4N.js → en-in-Bri93cNM.js} +1 -1
- package/admin/assets/{en-nz-grrmB3dg.js → en-nz-BqhpzeuX.js} +1 -1
- package/admin/assets/{en-sg-Cmqxpoir.js → en-sg-q8xv-C8V.js} +1 -1
- package/admin/assets/{en-gb-unFIXx9f.js → en-tt-BqzQXPA5.js} +1 -1
- package/admin/assets/{eo-Cqxmc0TT.js → eo-5Y962E4S.js} +1 -1
- package/admin/assets/{es-CkjKdFZ3.js → es-DOQ7swrW.js} +1 -1
- package/admin/assets/{es-do-CTcTshup.js → es-do-Diob7juh.js} +1 -1
- package/admin/assets/{es-mx-DMXgMMxs.js → es-mx-miy7RFDh.js} +1 -1
- package/admin/assets/{es-pr-DNCV91L3.js → es-pr-CZa5Iw2n.js} +1 -1
- package/admin/assets/{es-us-C7o2mlIH.js → es-us-BX03CBQf.js} +1 -1
- package/admin/assets/{esm-DvUbRuYx.js → esm-Dtaf0rWK.js} +1 -1
- package/admin/assets/{et-DaPpH-gK.js → et-DGWOmMlC.js} +1 -1
- package/admin/assets/{eu-DfKyFruu.js → eu-D1Qj8RMh.js} +1 -1
- package/admin/assets/{fa-xWrBjePb.js → fa--TfwKnl9.js} +1 -1
- package/admin/assets/{fi-BOWDMsDS.js → fi-Dcbkv925.js} +1 -1
- package/admin/assets/{fo-B3FupW8R.js → fo-BPGX_WF4.js} +1 -1
- package/admin/assets/{fr-DZBDBkwp.js → fr-CNsDHzNh.js} +1 -1
- package/admin/assets/{fr-ca-CRePPJJ5.js → fr-ca-BsNs3KIJ.js} +1 -1
- package/admin/assets/{fr-ch-pFKvYU8D.js → fr-ch-BBmdtvlB.js} +1 -1
- package/admin/assets/{fy-0k7RzHUH.js → fy-q3UE-mqp.js} +1 -1
- package/admin/assets/{ga-D31rB2ys.js → ga-C-DOgnYh.js} +1 -1
- package/admin/assets/{gd-CbgY-FFE.js → gd-mLMfry_l.js} +1 -1
- package/admin/assets/{gl-DrtvS9gt.js → gl-CO8Bj9I_.js} +1 -1
- package/admin/assets/{gom-latn-Dkke3GyE.js → gom-latn-myglMmHV.js} +1 -1
- package/admin/assets/{gu-BlEipYhR.js → gu-Bdz1OpMU.js} +1 -1
- package/admin/assets/{he-BrHT6OLB.js → he-C4lqk2AA.js} +1 -1
- package/admin/assets/{hi-Bx0zlAgr.js → hi-h0Byc_UN.js} +1 -1
- package/admin/assets/{hr-CoFYGbr6.js → hr-CZV7Za1n.js} +1 -1
- package/admin/assets/{ht-Df5xKo4a.js → ht-Dg2blNvl.js} +1 -1
- package/admin/assets/{hu-BFTN3q6-.js → hu-DbOVhd9K.js} +1 -1
- package/admin/assets/{hy-am-DuZTJQed.js → hy-am-E48Q7kWE.js} +1 -1
- package/admin/assets/icons-3rupj9Xr.js +1 -0
- package/admin/assets/{id-DBZ5nPkf.js → id-Cd4yWevW.js} +1 -1
- package/admin/assets/{index-eeRX25da.css → index-11n5z0Ez.css} +1 -1
- package/admin/assets/index-Dsq-sVu9.js +477 -0
- package/admin/assets/{is-CmeHm47B.js → is-BJZYSfrA.js} +1 -1
- package/admin/assets/{it-Cv3fjOA9.js → it-BRaqqFz4.js} +1 -1
- package/admin/assets/{it-ch-DCTyHA3T.js → it-ch-BkLqxaff.js} +1 -1
- package/admin/assets/{ja-DHNVXAM5.js → ja-X_I-25cD.js} +1 -1
- package/admin/assets/{jv-BdNIxhWv.js → jv-BBBx3R3v.js} +1 -1
- package/admin/assets/{ka-D-joD0QB.js → ka-D9Yrujqk.js} +1 -1
- package/admin/assets/{kk-8wRNNjxz.js → kk-Bz450_o8.js} +1 -1
- package/admin/assets/{km-dUWOg1uK.js → km-CKOAqvHM.js} +1 -1
- package/admin/assets/{kn-CwQbzel0.js → kn-k2xTBR7a.js} +1 -1
- package/admin/assets/{ko-D_gn9Sfw.js → ko-BCvoCYib.js} +1 -1
- package/admin/assets/{ku-Dh3MRML3.js → ku-D6Rloctr.js} +1 -1
- package/admin/assets/{ky-CWYuRvil.js → ky-C54qnEzk.js} +1 -1
- package/admin/assets/{lb-Cc2xsSIx.js → lb-y3rkrWB2.js} +1 -1
- package/admin/assets/{lo-CkwNFDBz.js → lo-nA_dPnOU.js} +1 -1
- package/admin/assets/{lt-HWyFDLXu.js → lt-hjPmCCML.js} +1 -1
- package/admin/assets/{lv-C7Zx3OLO.js → lv-Bh4qmUa_.js} +1 -1
- package/admin/assets/{me-DH3AqnfF.js → me-e32suC_n.js} +1 -1
- package/admin/assets/{mi-DMmUry6m.js → mi-Dwbsuyr9.js} +1 -1
- package/admin/assets/{mk-D6MNZrzX.js → mk-DjUXwKRn.js} +1 -1
- package/admin/assets/{ml-B_ur8Zqf.js → ml-CWjFAViF.js} +1 -1
- package/admin/assets/{mn-DeL8Ry7x.js → mn-BU4qTvEl.js} +1 -1
- package/admin/assets/{mr-C01qRXFL.js → mr-Bvnduzfb.js} +1 -1
- package/admin/assets/{ms-DFUZJzOm.js → ms-XuKVAni7.js} +1 -1
- package/admin/assets/{ms-my-CUYHkebs.js → ms-my-CDbnYp0g.js} +1 -1
- package/admin/assets/{mt-CE8t5jSj.js → mt-PDuxJf8J.js} +1 -1
- package/admin/assets/{my-Df96A8jZ.js → my-7N6SHnsD.js} +1 -1
- package/admin/assets/{nb-D0837ewu.js → nb-Cu3KUtTR.js} +1 -1
- package/admin/assets/{ne-BdK5TR0R.js → ne-CYW0uI2a.js} +1 -1
- package/admin/assets/{nl-oYKmSma6.js → nl-DL7cw699.js} +1 -1
- package/admin/assets/{nl-be-BfVC0cur.js → nl-be-B3NSF-2w.js} +1 -1
- package/admin/assets/{nn-IEUPnyc8.js → nn-BHITVkGB.js} +1 -1
- package/admin/assets/{oc-lnc-g_wSIROr.js → oc-lnc-dp3c4Bty.js} +1 -1
- package/admin/assets/{pa-in-gWhcLmZE.js → pa-in-BxgIlP1T.js} +1 -1
- package/admin/assets/{pl-H3-LoxVn.js → pl-GrG6yjkB.js} +1 -1
- package/admin/assets/{pt-br-B8UcNR6_.js → pt-br-BxTgeF95.js} +1 -1
- package/admin/assets/{pt-hiX8RqVw.js → pt-cVbj6hL6.js} +1 -1
- package/admin/assets/{rn-BKupViLr.js → rn-DiVJ2_Jy.js} +1 -1
- package/admin/assets/{ro-DmhgIvml.js → ro-D1WeRyXR.js} +1 -1
- package/admin/assets/rolldown-runtime-CacB07dV.js +1 -0
- package/admin/assets/routing-Cjyek7O_.js +1 -0
- package/admin/assets/{ru-CcpO-b8g.js → ru-DZCu4V9W.js} +1 -1
- package/admin/assets/{rw-DWX_5ZK0.js → rw-DfFpVc_g.js} +1 -1
- package/admin/assets/{sd-Dat8YgLw.js → sd-yDyBYxtQ.js} +1 -1
- package/admin/assets/{se-nTBYCwx3.js → se-EJKibocp.js} +1 -1
- package/admin/assets/sha512-DJtGRwVu.js +1 -0
- package/admin/assets/{si-DG88w5El.js → si-BCBV00rO.js} +1 -1
- package/admin/assets/{sk-s5FxW36J.js → sk-B2VvG_mo.js} +1 -1
- package/admin/assets/{sl-B-HyGrpA.js → sl-BTYmt65M.js} +1 -1
- package/admin/assets/{sq-DPFtfEC8.js → sq-BaLGKAHu.js} +1 -1
- package/admin/assets/{sr-bBPg1zz4.js → sr-DarE-95b.js} +1 -1
- package/admin/assets/{sr-cyrl-cXkYHMcP.js → sr-cyrl-CrwXFtkw.js} +1 -1
- package/admin/assets/{ss-7nSqXJN7.js → ss-CbBe1RUs.js} +1 -1
- package/admin/assets/{sv-B8mG1QK4.js → sv-Baw7Je9j.js} +1 -1
- package/admin/assets/{sv-fi-CotGRXSH.js → sv-fi-CVHORXkw.js} +1 -1
- package/admin/assets/{sw-fAxU-Uck.js → sw-k1H_OzIi.js} +1 -1
- package/admin/assets/{ta-Dl0Z1pan.js → ta-Bc4ZaZT9.js} +1 -1
- package/admin/assets/{te-DmcKasFF.js → te-ClciACZi.js} +1 -1
- package/admin/assets/{tet-D6mmvYpQ.js → tet-B75w1oSG.js} +1 -1
- package/admin/assets/{tg-39Hf2-oO.js → tg-CuH2ARRk.js} +1 -1
- package/admin/assets/{th-BSmTJSFN.js → th-LvExAoJ6.js} +1 -1
- package/admin/assets/{tk-m85E5igC.js → tk-2CFwTMKo.js} +1 -1
- package/admin/assets/{tl-ph-BlYxVvNt.js → tl-ph-CNJ_lViV.js} +1 -1
- package/admin/assets/{tlh-D1POnIeE.js → tlh-9UbKQkHo.js} +1 -1
- package/admin/assets/{tr-BurtfrLy.js → tr-Cu4P4OMe.js} +1 -1
- package/admin/assets/{tzl-BvyD0lOf.js → tzl-D97WsZW-.js} +1 -1
- package/admin/assets/{tzm-DZHS0dK6.js → tzm-CURkYgMY.js} +1 -1
- package/admin/assets/{tzm-latn-yNW7CNCH.js → tzm-latn-BRGsWK-9.js} +1 -1
- package/admin/assets/{ug-cn-rxs2pf5Y.js → ug-cn-Cd4exKOc.js} +1 -1
- package/admin/assets/{uk-CgZM-NnA.js → uk-DEXM3PIH.js} +1 -1
- package/admin/assets/{ur-YxIR8yYD.js → ur-DpUycj07.js} +1 -1
- package/admin/assets/{uz-CE9QsQMG.js → uz-CznjzufA.js} +1 -1
- package/admin/assets/{uz-latn-DiVdSW6G.js → uz-latn-Baga58FN.js} +1 -1
- package/admin/assets/{vi-DKARV6qf.js → vi-BrZWcL2C.js} +1 -1
- package/admin/assets/{x-pseudo-BuAdOrXQ.js → x-pseudo-BY6J4I7_.js} +1 -1
- package/admin/assets/{yo-BcB85DhC.js → yo-Dkeaym0x.js} +1 -1
- package/admin/assets/{zh-DLmxfxzl.js → zh-C2fOZUKR.js} +1 -1
- package/admin/assets/{zh-cn-ILRADZJi.js → zh-cn-CiA7hMZm.js} +1 -1
- package/admin/assets/{zh-hk-fT7fQk9_.js → zh-hk-EkoniWh0.js} +1 -1
- package/admin/assets/{zh-tw-NqRXGP6v.js → zh-tw-KDgos-kB.js} +1 -1
- package/admin/index.html +3 -2
- package/frontend/assets/{dist-legacy-BPToXNXu.js → dist-legacy-BNFW04Ss.js} +1 -1
- package/frontend/assets/index-legacy-2akkos6t.js +1 -0
- package/frontend/assets/{lib-legacy-CBk-_G90.js → lib-legacy-CO5spnaN.js} +1 -1
- package/frontend/assets/{sha512-legacy-CWrR2rkQ.js → sha512-legacy-BuJAJxVV.js} +1 -1
- package/frontend/index.html +1 -1
- package/npm-shrinkwrap.json +6 -6
- package/package.json +1 -1
- 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 +14 -11
- 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 +9 -12
- package/src/apiMiddleware.js +5 -1
- package/src/basicWeb.js +3 -2
- package/src/block.js +1 -1
- package/src/commands.js +6 -5
- package/src/comments.js +29 -13
- package/src/config.js +7 -4
- package/src/consoleLog.js +14 -12
- package/src/const.js +1 -1
- package/src/cross-const.js +27 -1
- package/src/cross.js +37 -34
- package/src/ddns.js +1 -4
- package/src/events.js +1 -4
- package/src/fileAttr.js +1 -1
- package/src/frontEndApis.js +7 -3
- package/src/geo.js +8 -2
- package/src/github.js +10 -9
- package/src/index.js +2 -1
- package/src/ips.js +24 -3
- 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 +1 -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 +17 -13
- package/src/log.js +11 -2
- package/src/middlewares.js +4 -4
- package/src/misc.js +3 -3
- package/src/multipartUpload.js +1 -2
- package/src/nat.js +1 -1
- package/src/outboundProxy.js +7 -0
- package/src/perm.js +5 -3
- package/src/plugins.js +60 -37
- package/src/selfCheck.js +4 -3
- package/src/serveFile.js +3 -3
- package/src/serveGuiAndSharedFiles.js +7 -6
- package/src/serveGuiFiles.js +15 -2
- package/src/serverCode.js +27 -0
- package/src/throttler.js +3 -2
- package/src/update.js +9 -3
- package/src/upload.js +29 -11
- package/src/uploadOwners.js +95 -0
- package/src/util-files.js +4 -2
- package/src/util-http.js +3 -1
- package/src/vfs.js +67 -30
- package/src/walkDir.js +24 -11
- package/src/watchLoad.js +13 -2
- package/src/webdav.js +33 -31
- package/src/zip.js +1 -1
- package/admin/assets/dist-CHP3cW_n.js +0 -1
- package/admin/assets/en-DQ460ET7.js +0 -1
- package/admin/assets/index-BGIe1xaR.js +0 -760
- package/admin/assets/sha512-CZz40GwW.js +0 -1
- package/frontend/assets/index-legacy-CmusDwvl.js +0 -1
package/src/api.accounts.js
CHANGED
|
@@ -11,25 +11,29 @@ const const_1 = require("./const");
|
|
|
11
11
|
const auth_1 = require("./auth");
|
|
12
12
|
const misc_1 = require("./misc");
|
|
13
13
|
const api_vfs_1 = require("./api.vfs");
|
|
14
|
-
function
|
|
15
|
-
|
|
14
|
+
function serializeAccount(ac) {
|
|
15
|
+
if (!ac)
|
|
16
|
+
return;
|
|
17
|
+
const hasLogin = (0, perm_1.accountHasLoginMethod)(ac);
|
|
18
|
+
return {
|
|
16
19
|
...lodash_1.default.omit(ac, ['password', 'hashed_password', 'srp']),
|
|
17
20
|
username: ac.username, // omit won't copy it because it's a hidden prop
|
|
18
21
|
hasPassword: (0, perm_1.accountHasPassword)(ac),
|
|
19
|
-
isGroup: !
|
|
22
|
+
isGroup: !hasLogin,
|
|
20
23
|
adminActualAccess: (0, perm_1.accountCanLoginAdmin)(ac),
|
|
21
|
-
canLogin:
|
|
24
|
+
canLogin: hasLogin ? (0, perm_1.accountCanLogin)(ac) : undefined,
|
|
22
25
|
canChangePassword: (0, perm_1.accountCanChangePassword)(ac),
|
|
23
26
|
invalidated: auth_1.invalidateSessionBefore.get(ac.username),
|
|
24
27
|
directMembers: Object.values(perm_1.accounts.get()).filter(a => a.belongs?.includes(ac.username)).map(x => x.username),
|
|
25
28
|
members: (0, misc_1.with_)(Object.values(perm_1.accounts.get()), accounts => {
|
|
26
29
|
const ret = [];
|
|
30
|
+
// breadth-first traversal follows indirect memberships and excludes already collected accounts
|
|
27
31
|
let news = [ac.username];
|
|
28
32
|
while (news.length) {
|
|
29
33
|
news = accounts.filter(a => !ret.includes(a.username) && a.belongs?.some(x => news.includes(x))).map(x => x.username);
|
|
30
34
|
ret.push(...news);
|
|
31
35
|
}
|
|
32
|
-
return
|
|
36
|
+
return ret.sort();
|
|
33
37
|
})
|
|
34
38
|
};
|
|
35
39
|
}
|
|
@@ -41,11 +45,11 @@ exports.default = {
|
|
|
41
45
|
},
|
|
42
46
|
get_account({ username }, ctx) {
|
|
43
47
|
(0, misc_1.apiAssertTypes)({ string_undefined: { username } });
|
|
44
|
-
return
|
|
48
|
+
return serializeAccount((0, perm_1.getAccount)(username || (0, auth_1.getCurrentUsername)(ctx)))
|
|
45
49
|
|| new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
46
50
|
},
|
|
47
51
|
get_accounts() {
|
|
48
|
-
return { list: (0, misc_1.onlyTruthy)(Object.values(perm_1.accounts.get()).map(
|
|
52
|
+
return { list: (0, misc_1.onlyTruthy)(Object.values(perm_1.accounts.get()).map(serializeAccount)) };
|
|
49
53
|
},
|
|
50
54
|
get_admins() {
|
|
51
55
|
return { list: lodash_1.default.filter(perm_1.accounts.get(), perm_1.accountCanLoginAdmin).map(ac => ac.username) };
|
package/src/api.auth.js
CHANGED
|
@@ -14,14 +14,18 @@ const auth_1 = require("./auth");
|
|
|
14
14
|
const config_1 = require("./config");
|
|
15
15
|
const events_1 = __importDefault(require("./events"));
|
|
16
16
|
const misc_1 = require("./misc");
|
|
17
|
+
const uploadOwners_1 = require("./uploadOwners");
|
|
17
18
|
const node_crypto_1 = require("node:crypto");
|
|
18
19
|
const ongoingLogins = {}; // store data that doesn't fit session object
|
|
19
|
-
const keepSessionAlive = (0, config_1.defineConfig)(
|
|
20
|
+
const keepSessionAlive = (0, config_1.defineConfig)(misc_1.CFG.keep_session_alive, true);
|
|
20
21
|
const fakeSrpSecret = (0, node_crypto_1.randomBytes)(32);
|
|
21
22
|
const refresh_session = async ({}, ctx) => {
|
|
23
|
+
if (!ctx.session)
|
|
24
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
|
|
25
|
+
(0, uploadOwners_1.getSessionId)(ctx); // anonymous upload ownership must be bound before any abortible upload request
|
|
22
26
|
const username = (0, auth_1.getCurrentUsername)(ctx);
|
|
23
27
|
const isAdmin = (0, adminApis_1.ctxAdminAccess)(ctx) || undefined;
|
|
24
|
-
return
|
|
28
|
+
return {
|
|
25
29
|
username,
|
|
26
30
|
expandedUsername: Array.from((0, perm_1.expandUsername)(username)),
|
|
27
31
|
isAdmin,
|
|
@@ -75,7 +79,7 @@ exports.authApis = {
|
|
|
75
79
|
// keep the public handshake identifier independent of predictable application PRNG state
|
|
76
80
|
const sid = (0, node_crypto_1.randomUUID)();
|
|
77
81
|
ongoingLogins[sid] = srpServer;
|
|
78
|
-
setTimeout(() => delete ongoingLogins[sid], 60_000);
|
|
82
|
+
setTimeout(() => delete ongoingLogins[sid], 60_000); // client must complete api sequence (loginSrp2) within 1 minute or will be discarded to avoid memory leaks
|
|
79
83
|
ctx.session.loggingIn = { username, sid }; // temporarily store until process is complete
|
|
80
84
|
return rest;
|
|
81
85
|
}
|
|
@@ -110,7 +114,7 @@ exports.authApis = {
|
|
|
110
114
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
111
115
|
try {
|
|
112
116
|
const M2 = await step1.step2(BigInt(pubKey), BigInt(proof))
|
|
113
|
-
.catch(() => { throw ''; });
|
|
117
|
+
.catch(() => { throw ''; }); // falsy value for later
|
|
114
118
|
await (0, auth_1.setLoggedIn)(ctx, username);
|
|
115
119
|
return {
|
|
116
120
|
proof: String(M2),
|
|
@@ -128,7 +132,6 @@ exports.authApis = {
|
|
|
128
132
|
delete ongoingLogins[sid];
|
|
129
133
|
}
|
|
130
134
|
},
|
|
131
|
-
// this api is here for consistency, but frontend is actually using
|
|
132
135
|
async logout({}, ctx) {
|
|
133
136
|
if (!ctx.session)
|
|
134
137
|
return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
|
package/src/api.cert.js
CHANGED
|
@@ -17,7 +17,7 @@ exports.default = {
|
|
|
17
17
|
cert.validity.notBefore = new Date();
|
|
18
18
|
cert.validity.notAfter = new Date();
|
|
19
19
|
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
|
|
20
|
-
const attrs = Object.entries(attributes
|
|
20
|
+
const attrs = Object.entries(attributes ?? {}).map(x => ({ name: x[0], value: x[1] }));
|
|
21
21
|
cert.setSubject(attrs);
|
|
22
22
|
cert.setIssuer(attrs);
|
|
23
23
|
cert.sign(keys.privateKey);
|
package/src/api.get_file_list.js
CHANGED
|
@@ -75,7 +75,8 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
75
75
|
for await (const sub of walker) {
|
|
76
76
|
let name = (0, vfs_1.getNodeName)(sub);
|
|
77
77
|
name = (0, path_1.basename)(name) || name; // on Windows, basename('C:') === ''
|
|
78
|
-
if (filterName && !filterName(name)
|
|
78
|
+
if (filterName && !filterName(name)
|
|
79
|
+
|| fileMask && !(0, vfs_1.nodeIsFolder)(sub) && !fileMask(name)
|
|
79
80
|
|| filterComment && !filterComment(await (0, comments_1.getCommentFor)(sub.source) || ''))
|
|
80
81
|
continue;
|
|
81
82
|
const entry = await nodeToDirEntry(ctx, sub);
|
|
@@ -83,7 +84,7 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
83
84
|
continue;
|
|
84
85
|
const cbParams = { entry, ctx, listUri: uri, node: sub };
|
|
85
86
|
try {
|
|
86
|
-
const res = await Promise.all(onDirEntryHandlers.map(({ id, cb }) =>
|
|
87
|
+
const res = await Promise.all(onDirEntryHandlers.map(({ id, cb }) => (0, misc_1.callAsPromise)(() => cb(cbParams)).catch(error => { throw { id, error }; })));
|
|
87
88
|
if (res.some(x => x === false))
|
|
88
89
|
continue;
|
|
89
90
|
}
|
|
@@ -111,14 +112,16 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
111
112
|
const name = (0, vfs_1.getNodeName)(node);
|
|
112
113
|
const isFolder = (0, vfs_1.nodeIsFolder)(node);
|
|
113
114
|
try {
|
|
114
|
-
const [web, comment, st] =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
const [web, comment, st] = node.see_without_probing
|
|
116
|
+
? [undefined, node.comment, undefined]
|
|
117
|
+
: await Promise.all([
|
|
118
|
+
(0, vfs_1.getDefaultFile)(node, ctx).then(x => x ? true : undefined),
|
|
119
|
+
node.comment ?? (0, comments_1.getCommentFor)(source),
|
|
120
|
+
(0, vfs_1.nodeStats)(node).catch(e => {
|
|
121
|
+
if (!isFolder || !node.children?.length) // folders with virtual children, keep them
|
|
122
|
+
throw e;
|
|
123
|
+
})
|
|
124
|
+
]);
|
|
122
125
|
// permissions of entries are sent as a difference with permissions of parent
|
|
123
126
|
const pl = node.can_list === misc_1.WHO_NO_ONE ? 'l'
|
|
124
127
|
: !(0, vfs_1.hasPermission)(node, 'can_list', ctx) ? 'L'
|
|
@@ -146,7 +149,7 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
|
|
|
146
149
|
};
|
|
147
150
|
}
|
|
148
151
|
catch {
|
|
149
|
-
return null;
|
|
152
|
+
return null; // ignore files with problems
|
|
150
153
|
}
|
|
151
154
|
function filesInsideCould(n = node) {
|
|
152
155
|
return (0, vfs_1.masksCouldGivePermission)(n.masks, 'can_read')
|
package/src/api.lang.js
CHANGED
|
@@ -48,8 +48,9 @@ const apis = {
|
|
|
48
48
|
code = (0, lang_1.file2code)(code);
|
|
49
49
|
validateCode(code);
|
|
50
50
|
const fn = (0, lang_1.code2file)(code);
|
|
51
|
-
const s =
|
|
52
|
-
|
|
51
|
+
const s = String(content);
|
|
52
|
+
const o = (0, misc_1.tryJson)(s);
|
|
53
|
+
if (!o?.translate)
|
|
53
54
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE, "bad content for file " + fn);
|
|
54
55
|
await (0, promises_1.writeFile)(fn, s, 'utf8');
|
|
55
56
|
}
|
package/src/api.monitor.js
CHANGED
|
@@ -47,7 +47,7 @@ exports.default = {
|
|
|
47
47
|
connectionUpdated(conn, change) {
|
|
48
48
|
if (conn.socket.closed || lodash_1.default.isEmpty(change))
|
|
49
49
|
return;
|
|
50
|
-
if (change.ctx) {
|
|
50
|
+
if (change.ctx) { // serialize ctx once so all monitor subscribers reuse the resulting change
|
|
51
51
|
Object.assign(change, fromCtx(change.ctx));
|
|
52
52
|
change.ctx = undefined;
|
|
53
53
|
}
|
package/src/api.net.js
CHANGED
|
@@ -61,7 +61,7 @@ exports.default = {
|
|
|
61
61
|
if (external)
|
|
62
62
|
await (0, nat_1.getUpnpClient)().createMapping((0, nat_1.upnpMappingParam)(internal || internalPort, external))
|
|
63
63
|
.catch(res => {
|
|
64
|
-
throw new apiMiddleware_1.ApiError(
|
|
64
|
+
throw new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, res.errorCode === 718 ? "Port not available" : res.errorDescription || res.message || "unknown error");
|
|
65
65
|
});
|
|
66
66
|
nat_1.mappedPort.set(external || 0); // remember only successful HFS mappings
|
|
67
67
|
return {};
|
|
@@ -84,6 +84,8 @@ exports.default = {
|
|
|
84
84
|
},
|
|
85
85
|
async make_cert({ domain, email, altNames }) {
|
|
86
86
|
(0, misc_1.apiAssertTypes)({ string: { domain }, string_undefined: { email }, array_undefined: { altNames } });
|
|
87
|
+
if (!domain)
|
|
88
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad params');
|
|
87
89
|
if (altNames?.some((name) => typeof name !== 'string'))
|
|
88
90
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad altNames');
|
|
89
91
|
await (0, acme_1.makeCert)(domain, email, altNames).catch(e => {
|
package/src/api.plugins.js
CHANGED
|
@@ -169,6 +169,8 @@ const apis = {
|
|
|
169
169
|
},
|
|
170
170
|
async uninstall_plugin({ id, deleteConfig }) {
|
|
171
171
|
assertPluginId(id);
|
|
172
|
+
if (!(0, plugins_1.getPluginInfo)(id))
|
|
173
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
|
|
172
174
|
await (0, plugins_1.stopPlugin)(id);
|
|
173
175
|
await (0, promises_1.rm)(plugins_1.PATH + '/' + id, { recursive: true, force: true });
|
|
174
176
|
if (deleteConfig)
|
package/src/api.vfs.js
CHANGED
|
@@ -26,7 +26,7 @@ async function urlToNodeOriginal(uri) {
|
|
|
26
26
|
exports.default = {
|
|
27
27
|
async get_vfs() {
|
|
28
28
|
return { root: await recur() };
|
|
29
|
-
async function recur(node = vfs_1.vfs) {
|
|
29
|
+
async function recur(node = vfs_1.vfs.compiled()) {
|
|
30
30
|
const { source } = node;
|
|
31
31
|
const stats = await (0, vfs_1.nodeStats)(node);
|
|
32
32
|
const isFolder = (0, vfs_1.nodeIsFolder)(node);
|
|
@@ -61,7 +61,7 @@ exports.default = {
|
|
|
61
61
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad name');
|
|
62
62
|
// check for siblings with the same name
|
|
63
63
|
const parent = await urlToNodeOriginal((0, path_1.dirname)(uri));
|
|
64
|
-
if (parent?.children?.find(x => (0, vfs_1.
|
|
64
|
+
if (parent?.children?.find(x => x !== n && (0, vfs_1.isSameFilenameAs)(props.name)(x)))
|
|
65
65
|
return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT, 'name already present');
|
|
66
66
|
}
|
|
67
67
|
Object.assign(n, sanitizeVfsProps(props));
|
|
@@ -107,28 +107,24 @@ exports.default = {
|
|
|
107
107
|
return {};
|
|
108
108
|
},
|
|
109
109
|
// legacy – not currently used by the UI
|
|
110
|
-
async add_vfs({ parent, source, name, ...rest }) {
|
|
110
|
+
async add_vfs({ parent, source, name, skip_source_check, ...rest }) {
|
|
111
111
|
if (!source && !name)
|
|
112
112
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'name or source required');
|
|
113
113
|
if (name && !(0, misc_1.isValidFileName)(name))
|
|
114
114
|
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad name');
|
|
115
|
-
const parentNode = parent ? await urlToNodeOriginal(parent) : vfs_1.vfs;
|
|
115
|
+
const parentNode = parent ? await urlToNodeOriginal(parent) : vfs_1.vfs.compiled();
|
|
116
116
|
if (!parentNode)
|
|
117
117
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'parent not found');
|
|
118
118
|
if (!(0, vfs_1.nodeIsFolder)(parentNode))
|
|
119
119
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE, 'parent not a folder');
|
|
120
120
|
if ((0, misc_1.isWindowsDrive)(source))
|
|
121
121
|
source += '\\'; // slash must be included, otherwise it will refer to the cwd of that drive
|
|
122
|
-
const isFolder = source && await (0, misc_1.isDirectory)(source);
|
|
123
|
-
if (source && isFolder === undefined)
|
|
122
|
+
const isFolder = source && (skip_source_check ? (0, misc_1.hasFinalSlash)(source) : await (0, misc_1.isDirectory)(source));
|
|
123
|
+
if (source && !skip_source_check && isFolder === undefined)
|
|
124
124
|
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'source not found');
|
|
125
125
|
const child = { source, name, ...sanitizeVfsProps(rest) };
|
|
126
126
|
name = (0, vfs_1.getNodeName)(child); // could be not given as input
|
|
127
|
-
|
|
128
|
-
const noExt = ext ? name.slice(0, -ext.length) : name;
|
|
129
|
-
let idx = 2;
|
|
130
|
-
while (parentNode.children?.find((0, vfs_1.isSameFilenameAs)(name)))
|
|
131
|
-
name = `${noExt} ${idx++}${ext}`;
|
|
127
|
+
name = (0, vfs_1.getFreeVfsName)(parentNode.children, name);
|
|
132
128
|
child.name = name;
|
|
133
129
|
simplifyName(child);
|
|
134
130
|
(parentNode.children ||= []).unshift(child);
|
|
@@ -190,9 +186,10 @@ exports.default = {
|
|
|
190
186
|
list.add({ n, k: 'd' });
|
|
191
187
|
}
|
|
192
188
|
catch (error) {
|
|
189
|
+
list.error(String(error));
|
|
193
190
|
console.debug(error);
|
|
194
191
|
}
|
|
195
|
-
return;
|
|
192
|
+
return list.close();
|
|
196
193
|
}
|
|
197
194
|
const sendPropsAsap = (0, util_os_1.getDiskSpace)(path).then(x => x && list.props(x));
|
|
198
195
|
try {
|
package/src/apiMiddleware.js
CHANGED
|
@@ -12,6 +12,7 @@ const misc_1 = require("./misc");
|
|
|
12
12
|
const const_1 = require("./const");
|
|
13
13
|
const config_1 = require("./config");
|
|
14
14
|
const plugins_1 = require("./plugins");
|
|
15
|
+
const SendList_1 = require("./SendList");
|
|
15
16
|
class ApiError extends Error {
|
|
16
17
|
status;
|
|
17
18
|
constructor(status, message) {
|
|
@@ -55,6 +56,9 @@ function apiMiddleware(apis) {
|
|
|
55
56
|
}
|
|
56
57
|
if (isAsyncGenerator(res))
|
|
57
58
|
res = (0, misc_1.asyncGeneratorToReadable)(res);
|
|
59
|
+
// return the initial list as regular JSON when the caller did not request live updates
|
|
60
|
+
if (res instanceof SendList_1.SendListReadable && ctx.get('accept') !== 'text/event-stream')
|
|
61
|
+
res = await res.collectInitial();
|
|
58
62
|
if (res instanceof stream_1.Readable) { // Readable, we'll go SSE-mode
|
|
59
63
|
res.pipe((0, sse_1.default)(ctx));
|
|
60
64
|
const resAsReadable = res; // satisfy ts
|
|
@@ -64,7 +68,7 @@ function apiMiddleware(apis) {
|
|
|
64
68
|
}
|
|
65
69
|
if (res instanceof ApiError)
|
|
66
70
|
return send(res.status, res.message);
|
|
67
|
-
if (res instanceof Error) // generic error/exception
|
|
71
|
+
if (res instanceof Error) // generic error/exception. expose unexpected errors to help diagnose bugs reported by users
|
|
68
72
|
return send(const_1.HTTP_BAD_REQUEST, res.stack || res.message || String(res));
|
|
69
73
|
ctx.body = res;
|
|
70
74
|
function send(status, body) {
|
package/src/basicWeb.js
CHANGED
|
@@ -13,7 +13,7 @@ const misc_1 = require("./misc");
|
|
|
13
13
|
const lodash_1 = __importDefault(require("lodash"));
|
|
14
14
|
const adminApis_1 = require("./adminApis");
|
|
15
15
|
const customHtml_1 = require("./customHtml");
|
|
16
|
-
const autoBasic = (0, config_1.defineConfig)(
|
|
16
|
+
const autoBasic = (0, config_1.defineConfig)(misc_1.CFG.auto_basic, true, v => lodash_1.default.isString(v) ? new RegExp(v, 'i') : null);
|
|
17
17
|
function basicWeb(ctx, node) {
|
|
18
18
|
const { get } = ctx.query;
|
|
19
19
|
if (get === 'login') {
|
|
@@ -41,7 +41,8 @@ function basicWeb(ctx, node) {
|
|
|
41
41
|
const stream = (0, misc_1.asyncGeneratorToReadable)((0, misc_1.filterMapGenerator)(walker, async (el) => {
|
|
42
42
|
const isFolder = (0, vfs_1.nodeIsFolder)(el);
|
|
43
43
|
const name = (0, vfs_1.getNodeName)(el) + (isFolder ? '/' : '');
|
|
44
|
-
|
|
44
|
+
const forceThis = isFolder && (el.see_without_probing || !await (0, vfs_1.getDefaultFile)(el, ctx)) ? force : '';
|
|
45
|
+
return `<li>${a((0, misc_1.pathEncode)(name) + forceThis, name)}\n`;
|
|
45
46
|
}));
|
|
46
47
|
ctx.body = stream;
|
|
47
48
|
stream.push(`<meta name="viewport" content="width=device-width" />`);
|
package/src/block.js
CHANGED
|
@@ -13,7 +13,7 @@ const connections_1 = require("./connections");
|
|
|
13
13
|
const misc_1 = require("./misc");
|
|
14
14
|
const net_1 = require("net");
|
|
15
15
|
const lodash_1 = __importDefault(require("lodash"));
|
|
16
|
-
exports.block = (0, config_1.defineConfig)(
|
|
16
|
+
exports.block = (0, config_1.defineConfig)(misc_1.CFG.block, [], rules => {
|
|
17
17
|
const now = new Date();
|
|
18
18
|
const ret = !Array.isArray(rules) ? []
|
|
19
19
|
: (0, misc_1.onlyTruthy)(rules.map(rule => {
|
package/src/commands.js
CHANGED
|
@@ -54,7 +54,8 @@ const api_monitor_1 = __importStar(require("./api.monitor"));
|
|
|
54
54
|
const connections_1 = require("./connections");
|
|
55
55
|
const argv_1 = require("./argv");
|
|
56
56
|
const listen_2 = require("./listen");
|
|
57
|
-
|
|
57
|
+
const debug = (0, config_1.defineConfig)(cross_1.CFG.debug, Boolean(argv_1.argv.debug || process.env.HFS_DEBUG || const_1.DEV));
|
|
58
|
+
debug.dontStore();
|
|
58
59
|
if (!argv_1.argv.updating && !config_1.showHelp) {
|
|
59
60
|
try {
|
|
60
61
|
// Not sure if the try is necessary for when stdin is unavailable, but someone reported a problem using nohup https://github.com/rejetto/hfs/issues/74 and I've found this example try-catching https://github.com/DefinitelyTyped/DefinitelyTyped/blob/dda83a906914489e09ca28afea12948529015d4a/types/node/readline.d.ts#L489
|
|
@@ -80,7 +81,7 @@ if (!argv_1.argv.updating && !config_1.showHelp) {
|
|
|
80
81
|
for (const k of ['log', 'warn', 'error', 'debug']) {
|
|
81
82
|
const original = console[k];
|
|
82
83
|
console[k] = (...args) => {
|
|
83
|
-
if (k === 'debug' && !
|
|
84
|
+
if (k === 'debug' && !debug.get())
|
|
84
85
|
return;
|
|
85
86
|
if (!first_1.quitting && tty)
|
|
86
87
|
clean();
|
|
@@ -96,7 +97,7 @@ if (!argv_1.argv.updating && !config_1.showHelp) {
|
|
|
96
97
|
catch {
|
|
97
98
|
console.log("Console commands not available");
|
|
98
99
|
const original = console.debug;
|
|
99
|
-
console.debug = (...args) =>
|
|
100
|
+
console.debug = (...args) => debug.get() && original(...args);
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
async function parseCommandLine(line) {
|
|
@@ -198,8 +199,8 @@ const commands = {
|
|
|
198
199
|
debug: {
|
|
199
200
|
params: '',
|
|
200
201
|
cb() {
|
|
201
|
-
|
|
202
|
-
console.log(`Debug messages ${
|
|
202
|
+
debug.set(v => !v);
|
|
203
|
+
console.log(`Debug messages ${debug.get() ? "on" : "off"}`);
|
|
203
204
|
}
|
|
204
205
|
},
|
|
205
206
|
'start-plugin': {
|
package/src/comments.js
CHANGED
|
@@ -16,15 +16,16 @@ const misc_1 = require("./misc");
|
|
|
16
16
|
const lodash_1 = __importDefault(require("lodash"));
|
|
17
17
|
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
|
18
18
|
const promises_1 = require("node:fs/promises");
|
|
19
|
+
const promises_2 = require("stream/promises");
|
|
19
20
|
const expiringCache_1 = require("./expiringCache");
|
|
20
21
|
exports.DESCRIPT_ION = 'descript.ion';
|
|
21
22
|
exports.DESCRIPT_ION_ALT = 'DESCRIPT.ION';
|
|
22
23
|
const commentsStorage = (0, config_1.defineConfig)(cross_1.CFG.comments_storage, '', v => ['', 'attr+ion'].includes(v)); // compiled tell us if we are using descript.ion
|
|
23
|
-
(0, config_1.defineConfig)(
|
|
24
|
+
(0, config_1.defineConfig)(cross_1.CFG.descript_ion, true, (v, more) => {
|
|
24
25
|
if (!v && more.version?.olderThan('0.57.0-alpha1'))
|
|
25
26
|
commentsStorage.set('attr');
|
|
26
27
|
});
|
|
27
|
-
const descriptIonEncoding = (0, config_1.defineConfig)(
|
|
28
|
+
const descriptIonEncoding = (0, config_1.defineConfig)(cross_1.CFG.descript_ion_encoding, 'utf8');
|
|
28
29
|
function readFromDescriptIon(path) {
|
|
29
30
|
return usingDescriptIon() && readDescriptIon((0, path_1.dirname)(path)).then(x => x.get((0, path_1.basename)(path)), () => undefined);
|
|
30
31
|
}
|
|
@@ -39,16 +40,29 @@ async function getCommentFor(path) {
|
|
|
39
40
|
]).then(([fromAttr, fromIon]) => fromAttr || fromIon || undefined);
|
|
40
41
|
}
|
|
41
42
|
async function setCommentFor(path, comment) {
|
|
42
|
-
if (commentsStorage.get())
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
if (commentsStorage.get())
|
|
44
|
+
return prep((0, misc_1.storeFileAttr)(path, COMMENT_ATTR, comment || undefined)).then(v => {
|
|
45
|
+
if (v)
|
|
46
|
+
void setCommentDescriptIon(path, '').catch(() => { });
|
|
47
|
+
return v;
|
|
48
|
+
});
|
|
49
|
+
// should we also remove from file-attr, similarly as above we remove from descript.ion? not sure, but for the time we won't because #1 storeFileAttr is not really deleting, and we would store a lot of empty attributes, #2 more people will switch from descript.ion to attr (because introduced later) than the opposite
|
|
50
|
+
return prep(setCommentDescriptIon(path, comment));
|
|
51
|
+
function prep(p) {
|
|
52
|
+
return p.then(v => v !== false, (e) => {
|
|
53
|
+
console.error(`${comment ? "Error setting comment" : "Error removing comment"}: ${String(e)}`);
|
|
54
|
+
return false;
|
|
55
|
+
});
|
|
45
56
|
}
|
|
46
|
-
return setCommentDescriptIon(path, comment); // should we also remove from file-attr? not sure, but for the time we won't because #1 storeFileAttr is not really deleting, and we would store a lot of empty attributes, #2 more people will switch from descript.ion to attr (because introduced later) than the opposite
|
|
47
57
|
}
|
|
48
|
-
const setCommentDescriptIon = (0, misc_1.singleWorkerFromBatchWorker)(
|
|
49
|
-
const byFolder = lodash_1.default.groupBy(jobs, job => (0, path_1.dirname)(job[0]));
|
|
50
|
-
|
|
51
|
-
const comments = await readDescriptIon(folder).catch(
|
|
58
|
+
const setCommentDescriptIon = (0, misc_1.singleWorkerFromBatchWorker)((jobs) => {
|
|
59
|
+
const byFolder = lodash_1.default.groupBy(jobs, job => (0, path_1.dirname)(job[0])); // jobs in the same folder share one write and its success or failure
|
|
60
|
+
const resultByFolder = lodash_1.default.mapValues(byFolder, async (jobs, folder) => {
|
|
61
|
+
const comments = await readDescriptIon(folder).catch(e => {
|
|
62
|
+
if (e?.code !== 'ENOENT')
|
|
63
|
+
throw e;
|
|
64
|
+
return new Map();
|
|
65
|
+
});
|
|
52
66
|
for (const [path, comment] of jobs) {
|
|
53
67
|
const file = path.slice(folder.length + 1);
|
|
54
68
|
if (!comment)
|
|
@@ -58,7 +72,7 @@ const setCommentDescriptIon = (0, misc_1.singleWorkerFromBatchWorker)(async (job
|
|
|
58
72
|
}
|
|
59
73
|
const path = await filePathHelper(folder);
|
|
60
74
|
if (!comments.size)
|
|
61
|
-
return (0, promises_1.
|
|
75
|
+
return (0, promises_1.rm)(path, { force: true });
|
|
62
76
|
// encode comments in descript.ion format
|
|
63
77
|
const ws = await (0, util_files_1.createSafeWriteStream)(path);
|
|
64
78
|
comments.forEach((comment, filename) => {
|
|
@@ -70,8 +84,10 @@ const setCommentDescriptIon = (0, misc_1.singleWorkerFromBatchWorker)(async (job
|
|
|
70
84
|
ws.write(MULTILINE_SUFFIX, 'binary');
|
|
71
85
|
ws.write('\n');
|
|
72
86
|
});
|
|
73
|
-
|
|
74
|
-
|
|
87
|
+
ws.end();
|
|
88
|
+
await (0, promises_2.finished)(ws);
|
|
89
|
+
});
|
|
90
|
+
return jobs.map(([path]) => resultByFolder[(0, path_1.dirname)(path)]);
|
|
75
91
|
});
|
|
76
92
|
function areCommentsEnabled() {
|
|
77
93
|
return true; // true since we introduced comments in file-attr
|
package/src/config.js
CHANGED
|
@@ -12,6 +12,7 @@ exports.getWholeConfig = getWholeConfig;
|
|
|
12
12
|
exports.setConfig = setConfig;
|
|
13
13
|
exports.subMultipleConfigs = subMultipleConfigs;
|
|
14
14
|
const const_1 = require("./const");
|
|
15
|
+
const cross_const_1 = require("./cross-const");
|
|
15
16
|
const watchLoad_1 = require("./watchLoad");
|
|
16
17
|
const yaml_1 = __importDefault(require("yaml"));
|
|
17
18
|
const lodash_1 = __importDefault(require("lodash"));
|
|
@@ -25,6 +26,7 @@ const argv_1 = require("./argv");
|
|
|
25
26
|
const util_files_1 = require("./util-files");
|
|
26
27
|
// keep definition of config properties
|
|
27
28
|
const configProps = {};
|
|
29
|
+
const dontStore = [];
|
|
28
30
|
let started = false; // this will tell the difference for subscribeConfig()s that are called before or after config is loaded
|
|
29
31
|
let state = {}; // current state of config properties
|
|
30
32
|
const filePath = (0, cross_1.with_)(argv_1.argv.config || process.env.HFS_CONFIG, p => {
|
|
@@ -62,7 +64,7 @@ class Version extends String {
|
|
|
62
64
|
exports.Version = Version;
|
|
63
65
|
const CONFIG_CHANGE_EVENT_PREFIX = 'config.';
|
|
64
66
|
exports.currentVersion = new Version(const_1.VERSION);
|
|
65
|
-
const configVersion = defineConfig(
|
|
67
|
+
const configVersion = defineConfig(cross_const_1.CFG.version, const_1.VERSION, v => new Version(v));
|
|
66
68
|
function defineConfig(k, defaultValue, compiler) {
|
|
67
69
|
configProps[k] = { defaultValue };
|
|
68
70
|
const object = {
|
|
@@ -104,7 +106,8 @@ function defineConfig(k, defaultValue, compiler) {
|
|
|
104
106
|
compiled = v;
|
|
105
107
|
const was = getConfig(k);
|
|
106
108
|
return events_1.default.emitAsync(CONFIG_CHANGE_EVENT_PREFIX + k, was, was, const_1.VERSION, true);
|
|
107
|
-
}
|
|
109
|
+
},
|
|
110
|
+
dontStore() { dontStore.push(k); },
|
|
108
111
|
};
|
|
109
112
|
let compiled = compiler?.(defaultValue, { k, version: exports.currentVersion, defaultValue, object });
|
|
110
113
|
if (compiler)
|
|
@@ -185,11 +188,11 @@ const saveDebounced = (0, debounceAsync_1.debounceAsync)(async () => {
|
|
|
185
188
|
const aWeekAgo = Date.now() - cross_1.DAY * 7;
|
|
186
189
|
if (await (0, util_files_1.statWithTimeout)(bak).then(x => aWeekAgo > x.mtimeMs, () => true))
|
|
187
190
|
await (0, promises_1.copyFile)(filePath, bak).catch(() => { }); // ignore errors
|
|
188
|
-
await exports.configFile.save(stringify({
|
|
191
|
+
await exports.configFile.save(stringify(lodash_1.default.omit({
|
|
189
192
|
...state,
|
|
190
193
|
version: const_1.VERSION,
|
|
191
194
|
platform: `${process.platform}-${process.arch}${(0, cross_1.prefix)('-', !const_1.IS_BINARY && (0, path_1.basename)(process.execPath))}`,
|
|
192
|
-
})).catch(err => console.error('Failed at saving config file, please ensure it is writable.', String(err)));
|
|
195
|
+
}, dontStore))).catch(err => console.error('Failed at saving config file, please ensure it is writable.', String(err)));
|
|
193
196
|
});
|
|
194
197
|
const saveConfigAsap = () => void saveDebounced();
|
|
195
198
|
exports.saveConfigAsap = saveConfigAsap;
|
package/src/consoleLog.js
CHANGED
|
@@ -11,7 +11,11 @@ const fs_1 = require("fs");
|
|
|
11
11
|
const argv_1 = require("./argv");
|
|
12
12
|
exports.consoleLog = [];
|
|
13
13
|
const originalConsoleLog = console.log;
|
|
14
|
-
|
|
14
|
+
let f = argv_1.argv.consoleFile ? (0, fs_1.createWriteStream)(argv_1.argv.consoleFile, { flags: 'a', encoding: 'utf8' }) : null;
|
|
15
|
+
f?.on('error', err => {
|
|
16
|
+
f = null; // stop using a failed stream so later logs cannot repeat the same error
|
|
17
|
+
console.error("Cannot write console file", argv_1.argv.consoleFile, String(err));
|
|
18
|
+
});
|
|
15
19
|
let terminalOutputBroken = false;
|
|
16
20
|
for (const stream of [process.stdout, process.stderr])
|
|
17
21
|
stream.on('error', err => {
|
|
@@ -24,19 +28,17 @@ for (const k of ['log', 'warn', 'error', 'debug']) {
|
|
|
24
28
|
const original = console[k];
|
|
25
29
|
console[k] = (...args) => {
|
|
26
30
|
const ts = new Date();
|
|
31
|
+
const msg = safeJoin(args); // if args contains a symbol, join will throw
|
|
27
32
|
if (k === 'debug')
|
|
28
33
|
args.unshift('DBG');
|
|
29
|
-
else
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (k !== 'log')
|
|
38
|
-
args.unshift('!');
|
|
39
|
-
}
|
|
34
|
+
else if (k !== 'log')
|
|
35
|
+
args.unshift('!');
|
|
36
|
+
const rec = { ts, k, msg };
|
|
37
|
+
exports.consoleLog.push(rec);
|
|
38
|
+
if (exports.consoleLog.length > 100_000) // limit to avoid infinite space
|
|
39
|
+
exports.consoleLog.splice(0, 1_000);
|
|
40
|
+
events_1.default.emit('console', rec);
|
|
41
|
+
f?.write(`${(0, cross_1.formatTimestamp)(ts)} [${k}] ${msg}\n`);
|
|
40
42
|
if (!terminalOutputBroken) {
|
|
41
43
|
try {
|
|
42
44
|
return original((0, cross_1.formatTime)(ts), ...args);
|
package/src/const.js
CHANGED
|
@@ -49,7 +49,7 @@ const path_1 = require("path");
|
|
|
49
49
|
const cross_1 = require("./cross");
|
|
50
50
|
const argv_1 = require("./argv");
|
|
51
51
|
__exportStar(require("./cross-const"), exports);
|
|
52
|
-
exports.API_VERSION = 13.
|
|
52
|
+
exports.API_VERSION = 13.2;
|
|
53
53
|
exports.COMPATIBLE_API_VERSION = 1; // the day we break with the past, we'll update this
|
|
54
54
|
// you can add arguments with this file, currently used for the update process on mac/linux.
|
|
55
55
|
// we are using homedir because it's the only stable path both the old and new process can agree on (open() doesn't preserve cwd)
|
package/src/cross-const.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HTTP_MESSAGES = exports.HTTP_INSUFFICIENT_STORAGE = exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_SERVER_ERROR = exports.HTTP_TOO_MANY_REQUESTS = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_LOCKED = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_PRECONDITION_FAILED = exports.HTTP_LENGTH_REQUIRED = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_MOVED_PERMANENTLY = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_CREATED = exports.HTTP_OK = exports.BASIC_AUTHENTICATE_HEADER = exports.EMBEDDED_LANGUAGE = exports.MASK_IN_TESTS = exports.HIDE_IN_TESTS = exports.ALLOW_SESSION_IP_CHANGE = exports.PREVIOUS_TAG = exports.MTIME_CHECK = exports.UPLOAD_TEMP_HASH = exports.HFS_REPO = exports.PLUGIN_CUSTOM_REST_PREFIX = exports.NBSP = exports.PORT_DISABLED = exports.ICONS_URI = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = void 0;
|
|
3
|
+
exports.HTTP_MESSAGES = exports.HTTP_INSUFFICIENT_STORAGE = exports.HTTP_SERVICE_UNAVAILABLE = exports.HTTP_SERVER_ERROR = exports.HTTP_TOO_MANY_REQUESTS = exports.HTTP_FAILED_DEPENDENCY = exports.HTTP_LOCKED = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_PRECONDITION_FAILED = exports.HTTP_LENGTH_REQUIRED = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_MOVED_PERMANENTLY = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_CREATED = exports.HTTP_OK = exports.CFG = exports.BASIC_AUTHENTICATE_HEADER = exports.EMBEDDED_LANGUAGE = exports.MASK_IN_TESTS = exports.HIDE_IN_TESTS = exports.ALLOW_SESSION_IP_CHANGE = exports.PREVIOUS_TAG = exports.MTIME_CHECK = exports.UPLOAD_TEMP_HASH = exports.UPLOAD_TEMP_PREFIX = exports.HFS_REPO = exports.PLUGIN_CUSTOM_REST_PREFIX = exports.NBSP = exports.PORT_DISABLED = exports.ICONS_URI = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = void 0;
|
|
4
4
|
exports.SPECIAL_URI = '/~/';
|
|
5
5
|
exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
|
|
6
6
|
exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
|
|
@@ -11,6 +11,7 @@ exports.PORT_DISABLED = -1;
|
|
|
11
11
|
exports.NBSP = '\xA0';
|
|
12
12
|
exports.PLUGIN_CUSTOM_REST_PREFIX = '_';
|
|
13
13
|
exports.HFS_REPO = 'rejetto/hfs';
|
|
14
|
+
exports.UPLOAD_TEMP_PREFIX = 'hfs$upload-';
|
|
14
15
|
exports.UPLOAD_TEMP_HASH = 'upload-temp-hash';
|
|
15
16
|
exports.MTIME_CHECK = 'x-mtime-check';
|
|
16
17
|
exports.PREVIOUS_TAG = 'previous';
|
|
@@ -19,6 +20,31 @@ exports.HIDE_IN_TESTS = 'hideInTests'; // elements that have variable size, wher
|
|
|
19
20
|
exports.MASK_IN_TESTS = 'maskInTests';
|
|
20
21
|
exports.EMBEDDED_LANGUAGE = 'en'; // frontend includes this language in the code, and not need to import the translation-json
|
|
21
22
|
exports.BASIC_AUTHENTICATE_HEADER = 'Basic realm="HFS"';
|
|
23
|
+
exports.CFG = constMap([
|
|
24
|
+
'accounts', 'acme_domain', 'acme_renew', 'admin_net', 'allowed_referer',
|
|
25
|
+
'authorization_header', 'auto_basic', 'auto_check_update', 'auto_play_seconds',
|
|
26
|
+
'base_url', 'block', 'cache_control_disk_files', 'cert', 'comments_storage',
|
|
27
|
+
'create-admin', 'debug', 'delete_unfinished_uploads_after', 'descript_ion',
|
|
28
|
+
'descript_ion_encoding', 'disable_custom_html', 'disableTranslation', 'dont_log_net',
|
|
29
|
+
'dont_overwrite_uploading', 'dynamic_dns_url', 'enable_plugins', 'error_log',
|
|
30
|
+
'favicon', 'file_menu_on_link', 'file_timeout', 'folders_first', 'force_address',
|
|
31
|
+
'force_https', 'force_lang', 'force_webdav_login',
|
|
32
|
+
'geo_allow', 'geo_allow_unknown', 'geo_enable', 'geo_ignore_net', 'geo_list',
|
|
33
|
+
'ignore_proxies', 'invert_order', 'keep_session_alive', 'listen_interface',
|
|
34
|
+
'localhost_admin', 'log', 'log_api', 'log_gui', 'log_host', 'log_rotation', 'log_spam',
|
|
35
|
+
'log_ua', 'mapped_port', 'max_downloads', 'max_downloads_per_account',
|
|
36
|
+
'max_downloads_per_ip', 'max_kbps', 'max_kbps_per_ip', 'menu_at_top', 'mime',
|
|
37
|
+
'min_available_mb', 'open_browser_at_start', 'outbound_interface', 'outbound_proxy', 'own_upload_delete_hours',
|
|
38
|
+
'page_size', 'plugins_config', 'port', 'private_key', 'proxies', 'roots', 'server_code',
|
|
39
|
+
'session_duration', 'show_hidden_files', 'show_uploader', 'size_1024',
|
|
40
|
+
'smart_unc_folder_detection', 'sort_by', 'sort_numerics', 'split_uploads',
|
|
41
|
+
'suspend_plugins', 'theme', 'tile_size', 'title', 'title_with_path', 'track_ips',
|
|
42
|
+
'update_to_beta', 'upnp_enabled', 'version', 'vfs', 'webdav_initial_auth',
|
|
43
|
+
'zip_calculate_size_for_seconds', 'https_port'
|
|
44
|
+
]);
|
|
45
|
+
function constMap(a) {
|
|
46
|
+
return Object.fromEntries(a.map(x => [x, x]));
|
|
47
|
+
}
|
|
22
48
|
exports.HTTP_OK = 200;
|
|
23
49
|
exports.HTTP_CREATED = 201;
|
|
24
50
|
exports.HTTP_NO_CONTENT = 204;
|