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/listen.js
CHANGED
|
@@ -69,11 +69,12 @@ const argv_1 = require("./argv");
|
|
|
69
69
|
const consoleLog_1 = require("./consoleLog");
|
|
70
70
|
const first_1 = require("./first");
|
|
71
71
|
const fileAttr_1 = require("./fileAttr");
|
|
72
|
+
const uploadOwners_1 = require("./uploadOwners");
|
|
72
73
|
let httpSrv;
|
|
73
74
|
let httpsSrv;
|
|
74
75
|
// the update relaunch can keep a bridge process alive, so we proactively close listeners here to release ports before the next binary binds; do it before (5) the storage file is closed, because sockets write there
|
|
75
76
|
(0, first_1.onProcessExit)(() => Promise.all([stopServer(httpSrv), stopServer(httpsSrv)]), 5);
|
|
76
|
-
const openBrowserAtStart = (0, config_1.defineConfig)(
|
|
77
|
+
const openBrowserAtStart = (0, config_1.defineConfig)(misc_1.CFG.open_browser_at_start, true);
|
|
77
78
|
exports.baseUrl = (0, config_1.defineConfig)(misc_1.CFG.base_url, '', x => /(?<=\/\/)[^\/]+/.exec(x)?.[0]); // compiled is host only
|
|
78
79
|
async function getBaseUrlOrDefault() {
|
|
79
80
|
return exports.baseUrl.get() || await nat_1.defaultBaseUrl.get();
|
|
@@ -87,7 +88,7 @@ const commonServerOptions = {
|
|
|
87
88
|
};
|
|
88
89
|
// these are properties that can be assigned to the server object
|
|
89
90
|
const commonServerAssign = { headersTimeout: 30_000, timeout: misc_1.MINUTE }; // 'headersTimeout' is not recognized by type lib, and 'timeout' is not effective when passed in parameters
|
|
90
|
-
const readyToListen = Promise.all([persistence_1.storedMap.isOpening(), fileAttr_1.fileAttrDb.isOpening(), events_1.default.once('app')]);
|
|
91
|
+
const readyToListen = Promise.all([persistence_1.storedMap.isOpening(), fileAttr_1.fileAttrDb.isOpening(), uploadOwners_1.uploadOwners.isOpening(), events_1.default.once('app')]);
|
|
91
92
|
const considerHttp = (0, misc_1.debounceAsync)(async () => {
|
|
92
93
|
await readyToListen;
|
|
93
94
|
void stopServer(httpSrv);
|
|
@@ -109,8 +110,8 @@ const considerHttp = (0, misc_1.debounceAsync)(async () => {
|
|
|
109
110
|
}
|
|
110
111
|
});
|
|
111
112
|
let openOnce = true;
|
|
112
|
-
exports.portCfg = (0, config_1.defineConfig)(
|
|
113
|
-
const listenInterface = (0, config_1.defineConfig)(
|
|
113
|
+
exports.portCfg = (0, config_1.defineConfig)(misc_1.CFG.port, 80);
|
|
114
|
+
const listenInterface = (0, config_1.defineConfig)(misc_1.CFG.listen_interface, '');
|
|
114
115
|
(0, config_1.subMultipleConfigs)(considerHttp, [exports.portCfg, listenInterface]);
|
|
115
116
|
function openAdmin() {
|
|
116
117
|
for (const srv of [httpSrv, httpsSrv]) {
|
|
@@ -123,7 +124,7 @@ function openAdmin() {
|
|
|
123
124
|
const baseUrl = `${srv.name}://${hostname}:${a.port}`;
|
|
124
125
|
(0, open_1.default)(baseUrl + const_1.ADMIN_URI, { wait: true }).catch(async (e) => {
|
|
125
126
|
console.debug(String(e));
|
|
126
|
-
console.warn("Cannot launch browser on this machine
|
|
127
|
+
console.warn("Cannot launch browser on this machine – open your browser and reach one of these (you may need a different address)", ...Object.values(await getUrls()).flat().map(x => '\n - ' + x + const_1.ADMIN_URI));
|
|
127
128
|
if (!(0, adminApis_1.anyAccountCanLoginAdmin)())
|
|
128
129
|
(0, consoleLog_1.consoleHint)(`you can enter this command: create-admin YOUR_PASSWORD`);
|
|
129
130
|
});
|
|
@@ -138,7 +139,8 @@ function getCertObject() {
|
|
|
138
139
|
const all = new crypto_1.X509Certificate(c);
|
|
139
140
|
const some = lodash_1.default.pick(all, ['subject', 'issuer', 'validFrom', 'validTo']);
|
|
140
141
|
const ret = lodash_1.default.mapValues(some, v => v?.includes('=') ? Object.fromEntries(v.split('\n').map(x => x.split('='))) : v);
|
|
141
|
-
return Object.assign(ret, { altNames: all.subjectAltName?.
|
|
142
|
+
return Object.assign(ret, { altNames: all.subjectAltName?.split(/, */)
|
|
143
|
+
.map(x => x.replace(/^(?:DNS:|IP Address:)/, '')) });
|
|
142
144
|
}
|
|
143
145
|
const considerHttps = (0, misc_1.debounceAsync)(async () => {
|
|
144
146
|
await readyToListen;
|
|
@@ -205,8 +207,8 @@ const considerHttps = (0, misc_1.debounceAsync)(async () => {
|
|
|
205
207
|
nat_1.defaultBaseUrl.proto = 'https';
|
|
206
208
|
nat_1.defaultBaseUrl.port = getCurrentPort(httpsSrv) ?? 0;
|
|
207
209
|
}, { wait: 200 }); // give time to have key and cert ready
|
|
208
|
-
exports.cert = (0, config_1.defineConfig)(
|
|
209
|
-
exports.privateKey = (0, config_1.defineConfig)(
|
|
210
|
+
exports.cert = (0, config_1.defineConfig)(misc_1.CFG.cert, '', load);
|
|
211
|
+
exports.privateKey = (0, config_1.defineConfig)(misc_1.CFG.private_key, '', load);
|
|
210
212
|
const httpsNeeds = [exports.cert, exports.privateKey];
|
|
211
213
|
function load(v, { object }) {
|
|
212
214
|
object.watcher?.unwatch();
|
|
@@ -216,7 +218,7 @@ function load(v, { object }) {
|
|
|
216
218
|
object.watcher = (0, watchLoad_1.watchLoad)(v, x => object.setCompiled(x), { immediateFirst: true });
|
|
217
219
|
return '';
|
|
218
220
|
}
|
|
219
|
-
exports.httpsPortCfg = (0, config_1.defineConfig)(
|
|
221
|
+
exports.httpsPortCfg = (0, config_1.defineConfig)(misc_1.CFG.https_port, const_1.PORT_DISABLED);
|
|
220
222
|
(0, config_1.subMultipleConfigs)(considerHttps, [exports.httpsPortCfg, listenInterface, ...httpsNeeds]);
|
|
221
223
|
const genericInterfaceNames = {
|
|
222
224
|
'0.0.0.0': "any IPv4",
|
|
@@ -273,12 +275,14 @@ function startServer(srv, { port, host }) {
|
|
|
273
275
|
srv.error = String(e);
|
|
274
276
|
srv.busy = undefined;
|
|
275
277
|
const { code } = e;
|
|
276
|
-
|
|
278
|
+
// share the lookup with status readers, but classify the error from its resolved result
|
|
279
|
+
if (code === 'EADDRINUSE' || code === 'EACCES')
|
|
277
280
|
srv.busy = (0, find_process_1.default)('port', port).then(res => res?.map(x => (0, misc_1.prefix)("Service", x.name === 'svchost.exe' && x.cmd.split(x.name)[1]?.trim()) || x.name).join(' + '), () => '');
|
|
278
|
-
|
|
281
|
+
const busy = await srv.busy;
|
|
282
|
+
if (code === 'EACCES' && port < 1024 && !busy) // on Windows, when port is used by a service, we get EACCES
|
|
279
283
|
srv.error = `lacking permission on port ${port}, try with permission (${const_1.IS_WINDOWS ? 'administrator' : 'sudo'}) or port > 1024`;
|
|
280
|
-
if (code === 'EADDRINUSE' ||
|
|
281
|
-
srv.error = `port ${port} busy: ${
|
|
284
|
+
if (code === 'EADDRINUSE' || busy)
|
|
285
|
+
srv.error = `port ${port} busy: ${busy || "unknown process"}`;
|
|
282
286
|
if (!silence)
|
|
283
287
|
console.error(srv.name, srv.error);
|
|
284
288
|
resolve(0);
|
package/src/log.js
CHANGED
|
@@ -82,8 +82,14 @@ class Logger {
|
|
|
82
82
|
this.reopen();
|
|
83
83
|
}
|
|
84
84
|
reopen() {
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
const { path } = this;
|
|
86
|
+
const s = this.stream = (0, util_files_1.createFileWithPath)(path, { flags: 'a' });
|
|
87
|
+
s?.on('error', e => {
|
|
88
|
+
if (this.stream === s) // a late error from an old stream must not detach its replacement
|
|
89
|
+
this.stream = undefined;
|
|
90
|
+
console.error("Cannot write log file", path, String(e));
|
|
91
|
+
});
|
|
92
|
+
return s;
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
// we'll have names same as config keys. These are used also by the get_log api.
|
|
@@ -103,6 +109,7 @@ const logRotation = (0, config_1.defineConfig)(misc_1.CFG.log_rotation, 'weekly'
|
|
|
103
109
|
const dontLogNet = (0, config_1.defineConfig)(misc_1.CFG.dont_log_net, '127.0.0.1|::1', v => (0, misc_1.makeNetMatcher)(v));
|
|
104
110
|
const logUA = (0, config_1.defineConfig)(misc_1.CFG.log_ua, false);
|
|
105
111
|
const logSpam = (0, config_1.defineConfig)(misc_1.CFG.log_spam, false);
|
|
112
|
+
const logHost = (0, config_1.defineConfig)(misc_1.CFG.log_host, false);
|
|
106
113
|
const debounce = lodash_1.default.debounce(cb => cb(), 1000); // with this technique, i'll be able to debounce some code respecting the references in its closure
|
|
107
114
|
const logMw = async (ctx, next) => {
|
|
108
115
|
const reqStart = new Date(); // request start
|
|
@@ -176,6 +183,8 @@ const logMw = async (ctx, next) => {
|
|
|
176
183
|
ctx.logExtra({ country: conn.country });
|
|
177
184
|
if (logUA.get())
|
|
178
185
|
ctx.logExtra({ ua: ctx.get('user-agent') || undefined });
|
|
186
|
+
if (logHost.get())
|
|
187
|
+
ctx.logExtra({ host: ctx.get('host') || undefined });
|
|
179
188
|
const extra = ctx.state.logExtra;
|
|
180
189
|
if (events_2.default.anyListener(logger.name)) // small optimization: this event can happen often, while most times there's no listener, and the parameters object is constructed pointlessly. A benchmark measured it 20% faster (just the line), while maybe it was not necessary.
|
|
181
190
|
events_2.default.emit(logger.name, { ctx, length, user, ts: reqEnd, uri, extra });
|
package/src/middlewares.js
CHANGED
|
@@ -21,10 +21,10 @@ const config_1 = require("./config");
|
|
|
21
21
|
const koa_session_1 = __importDefault(require("koa-session"));
|
|
22
22
|
const index_1 = require("./index");
|
|
23
23
|
const events_1 = __importDefault(require("./events"));
|
|
24
|
-
const forceHttps = (0, config_1.defineConfig)(
|
|
25
|
-
(0, config_1.defineConfig)(
|
|
26
|
-
const allowAuthorizationHeader = (0, config_1.defineConfig)(
|
|
27
|
-
exports.sessionDuration = (0, config_1.defineConfig)(
|
|
24
|
+
const forceHttps = (0, config_1.defineConfig)(misc_1.CFG.force_https, true);
|
|
25
|
+
(0, config_1.defineConfig)(misc_1.CFG.ignore_proxies, false);
|
|
26
|
+
const allowAuthorizationHeader = (0, config_1.defineConfig)(misc_1.CFG.authorization_header, true);
|
|
27
|
+
exports.sessionDuration = (0, config_1.defineConfig)(misc_1.CFG.session_duration, Number(process.env.SESSION_DURATION) || misc_1.DAY / 1000, v => v * 1000);
|
|
28
28
|
exports.gzipper = (0, koa_compress_1.default)({
|
|
29
29
|
threshold: 2048,
|
|
30
30
|
gzip: { flush: zlib_1.constants.Z_SYNC_FLUSH }, // flush is necessary for SSE, at least in Chrome145
|
package/src/misc.js
CHANGED
|
@@ -120,15 +120,15 @@ function asyncGeneratorToReadable(generator) {
|
|
|
120
120
|
const iterator = generator[Symbol.asyncIterator]();
|
|
121
121
|
return new stream_1.Readable({
|
|
122
122
|
objectMode: true,
|
|
123
|
-
destroy() {
|
|
124
|
-
|
|
123
|
+
destroy(error, callback) {
|
|
124
|
+
Promise.resolve(iterator.return?.()).then(() => callback(error), callback);
|
|
125
125
|
},
|
|
126
126
|
read() {
|
|
127
127
|
iterator.next().then(it => {
|
|
128
128
|
if (it.done)
|
|
129
129
|
this.emit('ending');
|
|
130
130
|
return this.push(it.done ? null : it.value);
|
|
131
|
-
});
|
|
131
|
+
}, e => this.destroy(e));
|
|
132
132
|
}
|
|
133
133
|
});
|
|
134
134
|
}
|
package/src/multipartUpload.js
CHANGED
|
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.handleMultipartUpload = handleMultipartUpload;
|
|
7
7
|
const busboy_1 = __importDefault(require("busboy"));
|
|
8
|
-
const events_1 = require("events");
|
|
9
8
|
const vfs_1 = require("./vfs");
|
|
10
9
|
const path_1 = require("path");
|
|
11
10
|
const upload_1 = require("./upload");
|
|
@@ -46,7 +45,7 @@ async function handleMultipartUpload(ctx, node) {
|
|
|
46
45
|
ctx.status = cross_const_1.HTTP_BAD_REQUEST;
|
|
47
46
|
});
|
|
48
47
|
ctx.req.pipe(bb);
|
|
49
|
-
await (0,
|
|
48
|
+
await new Promise(res => (0, first_1.onFirstEvent)(bb, ['finish', 'error'], res)); // parser errors are handled above as 400 and must complete the request without rejecting
|
|
50
49
|
await Promise.all(fileJobs);
|
|
51
50
|
if (!ctx.state.uploads?.length) {
|
|
52
51
|
if (!errors.length)
|
package/src/nat.js
CHANGED
|
@@ -33,7 +33,7 @@ exports.defaultBaseUrl = (0, valtio_1.proxy)({
|
|
|
33
33
|
return `${this.proto}://${(0, cross_1.ipForUrl)(ip || 'localhost')}${!port || port === defPort ? '' : ':' + port}`;
|
|
34
34
|
}
|
|
35
35
|
});
|
|
36
|
-
exports.mappedPort = (0, config_1.defineConfig)(
|
|
36
|
+
exports.mappedPort = (0, config_1.defineConfig)(cross_1.CFG.mapped_port, 0);
|
|
37
37
|
exports.upnpEnabled = (0, config_1.defineConfig)(cross_1.CFG.upnp_enabled, true);
|
|
38
38
|
let upnpClient;
|
|
39
39
|
// poll external ip – when UPnP is enabled, asking the modem is inexpensive, so it can be done often
|
package/src/outboundProxy.js
CHANGED
|
@@ -5,6 +5,13 @@ const util_http_1 = require("./util-http");
|
|
|
5
5
|
const util_os_1 = require("./util-os");
|
|
6
6
|
const const_1 = require("./const");
|
|
7
7
|
const cross_1 = require("./cross");
|
|
8
|
+
const node_net_1 = require("node:net");
|
|
9
|
+
(0, config_1.defineConfig)(cross_1.CFG.outbound_interface, '', v => {
|
|
10
|
+
if (!v || (0, node_net_1.isIP)(v))
|
|
11
|
+
return util_http_1.httpStream.defaultLocalAddress = v || undefined;
|
|
12
|
+
console.warn("Invalid outbound interface IP", v);
|
|
13
|
+
util_http_1.httpStream.defaultLocalAddress = undefined;
|
|
14
|
+
});
|
|
8
15
|
const outboundProxy = (0, config_1.defineConfig)(cross_1.CFG.outbound_proxy, '', v => {
|
|
9
16
|
util_http_1.httpStream.defaultProxy = undefined;
|
|
10
17
|
if (!v)
|
package/src/perm.js
CHANGED
|
@@ -59,7 +59,7 @@ function getAccount(username, normalize = true) {
|
|
|
59
59
|
function saveSrpInfo(account, salt, verifier) {
|
|
60
60
|
account.srp = String(salt) + '|' + String(verifier);
|
|
61
61
|
}
|
|
62
|
-
const createAdminConfig = (0, config_1.defineConfig)('create-admin', '');
|
|
62
|
+
const createAdminConfig = (0, config_1.defineConfig)(misc_1.CFG['create-admin'], '');
|
|
63
63
|
createAdminConfig.sub(v => {
|
|
64
64
|
if (!v)
|
|
65
65
|
return;
|
|
@@ -79,7 +79,9 @@ async function updateAccount(account, change) {
|
|
|
79
79
|
await change?.(account);
|
|
80
80
|
else {
|
|
81
81
|
const u = normalizeUsername(change.username || '');
|
|
82
|
-
if (u
|
|
82
|
+
if (!u)
|
|
83
|
+
delete change.username;
|
|
84
|
+
else if (u !== usernameWas && getAccount(u))
|
|
83
85
|
throw "username already exists";
|
|
84
86
|
Object.assign(account, lodash_1.default.mapValues(change, x => x || undefined));
|
|
85
87
|
}
|
|
@@ -111,7 +113,7 @@ async function updateAccount(account, change) {
|
|
|
111
113
|
saveAccountsAsap();
|
|
112
114
|
}
|
|
113
115
|
const saveAccountsAsap = config_1.saveConfigAsap;
|
|
114
|
-
exports.accounts = (0, config_1.defineConfig)(
|
|
116
|
+
exports.accounts = (0, config_1.defineConfig)(misc_1.CFG.accounts, {});
|
|
115
117
|
exports.accounts.sub(lodash_1.default.debounce(obj => {
|
|
116
118
|
// consider some validation here, in case of manual edit of the config
|
|
117
119
|
lodash_1.default.each(obj, (rec, k) => {
|
package/src/plugins.js
CHANGED
|
@@ -80,6 +80,7 @@ const perm_1 = require("./perm");
|
|
|
80
80
|
const auth_1 = require("./auth");
|
|
81
81
|
const icons_1 = require("./icons");
|
|
82
82
|
const listen_1 = require("./listen");
|
|
83
|
+
const serverCode_1 = require("./serverCode");
|
|
83
84
|
exports.PATH = 'plugins';
|
|
84
85
|
exports.DISABLING_SUFFIX = '-disabled';
|
|
85
86
|
exports.DELETE_ME_SUFFIX = '-delete_me' + exports.DISABLING_SUFFIX;
|
|
@@ -121,7 +122,7 @@ async function startPlugin(id) {
|
|
|
121
122
|
}
|
|
122
123
|
async function waitRunning(id, state = true) {
|
|
123
124
|
while (isPluginRunning(id) !== state) {
|
|
124
|
-
await (0, misc_1.wait)(
|
|
125
|
+
await (0, misc_1.wait)(50);
|
|
125
126
|
const error = getError(id);
|
|
126
127
|
if (error)
|
|
127
128
|
throw Error(error);
|
|
@@ -152,9 +153,10 @@ function findPluginByRepo(repo) {
|
|
|
152
153
|
function getPluginConfigFields(id) {
|
|
153
154
|
return plugins.get(id)?.getData().config;
|
|
154
155
|
}
|
|
155
|
-
async function initPlugin(pl, morePassedToInit) {
|
|
156
|
+
async function initPlugin(pl, morePassedToInit, onInitError) {
|
|
156
157
|
const undoEvents = [];
|
|
157
158
|
const timeouts = [];
|
|
159
|
+
let unload = pl.unload;
|
|
158
160
|
const controlledEvents = Object.create(events_1.default, (0, misc_1.objFromKeys)(['on', 'once', 'multi'], k => ({
|
|
159
161
|
value() {
|
|
160
162
|
if (k === 'multi')
|
|
@@ -183,7 +185,7 @@ async function initPlugin(pl, morePassedToInit) {
|
|
|
183
185
|
}
|
|
184
186
|
}
|
|
185
187
|
})));
|
|
186
|
-
const res = await pl.init?.({
|
|
188
|
+
const res = await (0, misc_1.callAsPromise)(() => pl.init?.({
|
|
187
189
|
Const, require,
|
|
188
190
|
// intercept all subscriptions, so to be able to undo them on unload
|
|
189
191
|
events: controlledEvents,
|
|
@@ -220,18 +222,25 @@ async function initPlugin(pl, morePassedToInit) {
|
|
|
220
222
|
customApiCall, notifyClient: frontEndApis_1.notifyClient, addBlock: block_1.addBlock, ctxBelongsTo: perm_1.ctxBelongsTo, getConnections: connections_1.getConnections, normalizeFilename: vfs_1.normalizeFilename,
|
|
221
223
|
getCurrentUsername: auth_1.getCurrentUsername, getAccount: perm_1.getAccount, getUsernames: perm_1.getUsernames, addAccount: perm_1.addAccount, delAccount: perm_1.delAccount, updateAccount: perm_1.updateAccount, renameAccount: perm_1.renameAccount,
|
|
222
224
|
...morePassedToInit
|
|
225
|
+
})).catch(async (e) => {
|
|
226
|
+
// failed initialization must not leave any partially allocated plugin resources active
|
|
227
|
+
await cleanup().catch(console.error);
|
|
228
|
+
await onInitError?.();
|
|
229
|
+
throw e;
|
|
223
230
|
});
|
|
224
231
|
Object.assign(pl, typeof res === 'function' ? { unload: res } : res);
|
|
225
|
-
|
|
232
|
+
unload = pl.unload;
|
|
233
|
+
(0, misc_1.patchKey)(pl, 'unload', () => cleanup);
|
|
234
|
+
events_1.default.emit('pluginInitialized', pl);
|
|
235
|
+
return pl;
|
|
236
|
+
async function cleanup() {
|
|
226
237
|
for (const x of timeouts)
|
|
227
238
|
clearTimeout(x);
|
|
228
239
|
for (const cb of undoEvents)
|
|
229
240
|
cb();
|
|
230
|
-
if (typeof
|
|
231
|
-
return
|
|
232
|
-
}
|
|
233
|
-
events_1.default.emit('pluginInitialized', pl);
|
|
234
|
-
return pl;
|
|
241
|
+
if (typeof unload === 'function')
|
|
242
|
+
return unload();
|
|
243
|
+
}
|
|
235
244
|
}
|
|
236
245
|
const pluginsMiddleware = async (ctx, next) => {
|
|
237
246
|
const after = {};
|
|
@@ -286,7 +295,7 @@ const pluginsMiddleware = async (ctx, next) => {
|
|
|
286
295
|
printError(id, e);
|
|
287
296
|
}
|
|
288
297
|
function printChange(id) {
|
|
289
|
-
if (id
|
|
298
|
+
if (id.startsWith(exports.SERVER_CODE_ID) || (lastStatus === ctx.status && lastBody === ctx.body))
|
|
290
299
|
return;
|
|
291
300
|
console.debug("Plugin changed response:", id);
|
|
292
301
|
lastStatus = ctx.status;
|
|
@@ -355,6 +364,7 @@ class Plugin {
|
|
|
355
364
|
get depend() { return this.data?.depend; }
|
|
356
365
|
get afterPlugin() { return this.data?.afterPlugin; }
|
|
357
366
|
get beforePlugin() { return this.data?.beforePlugin; }
|
|
367
|
+
get disableDefaultStyle() { return this.data?.disableDefaultStyle; }
|
|
358
368
|
get middleware() {
|
|
359
369
|
return this.data?.middleware;
|
|
360
370
|
}
|
|
@@ -382,34 +392,44 @@ class Plugin {
|
|
|
382
392
|
console.log('Error unloading plugin', id, String(e));
|
|
383
393
|
}
|
|
384
394
|
await this.onUnload();
|
|
385
|
-
if (!reloading && id
|
|
395
|
+
if (!reloading && !id.startsWith(exports.SERVER_CODE_ID)) // we already printed 'reloading'
|
|
386
396
|
console.log('Unloaded plugin', id);
|
|
387
397
|
if (this.data)
|
|
388
398
|
this.data.unload = undefined;
|
|
389
399
|
}
|
|
390
400
|
}
|
|
391
401
|
exports.Plugin = Plugin;
|
|
392
|
-
exports.SERVER_CODE_ID = '
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
402
|
+
exports.SERVER_CODE_ID = '/'; // a name that will surely be not found among plugin folders
|
|
403
|
+
let unloadServerCode = lodash_1.default.noop;
|
|
404
|
+
(0, config_1.defineConfig)(misc_1.CFG.server_code, '').sub(async (text) => {
|
|
405
|
+
await unloadServerCode();
|
|
406
|
+
const asPlugins = await Promise.all((0, serverCode_1.parseServerCode)(text).map(async ({ name, code }, i) => {
|
|
407
|
+
const res = {};
|
|
408
|
+
try {
|
|
409
|
+
new Function('exports,require', code)(res, require); // parse
|
|
410
|
+
await initPlugin(res);
|
|
411
|
+
res.getCustomHtml = () => (0, misc_1.callable)(res.customHtml) || {};
|
|
412
|
+
// server-code sections can share or omit names, so include the index to keep internal plugin ids unique
|
|
413
|
+
return new Plugin(exports.SERVER_CODE_ID + i + ':' + name, '', res, lodash_1.default.noop);
|
|
414
|
+
}
|
|
415
|
+
catch (e) {
|
|
416
|
+
return console.error(name + ':', e.message || String(e));
|
|
417
|
+
}
|
|
418
|
+
}));
|
|
419
|
+
// server_code is a side-effect config, so keep its cleanup outside compiled()
|
|
420
|
+
unloadServerCode = () => Promise.all(asPlugins.map(p => {
|
|
421
|
+
try {
|
|
422
|
+
return p?.unload();
|
|
423
|
+
} // catch sync errors
|
|
424
|
+
catch (e) {
|
|
425
|
+
console.error(e);
|
|
426
|
+
}
|
|
427
|
+
})).catch(console.error);
|
|
408
428
|
});
|
|
409
429
|
function mapPlugins(cb, includeServerCode = true) {
|
|
410
430
|
let i = 0;
|
|
411
431
|
return Array.from(plugins).map(([plName, pl]) => {
|
|
412
|
-
if (!includeServerCode && plName
|
|
432
|
+
if (!includeServerCode && plName.startsWith(exports.SERVER_CODE_ID))
|
|
413
433
|
return;
|
|
414
434
|
try {
|
|
415
435
|
return cb(pl, plName, i++);
|
|
@@ -421,7 +441,7 @@ function mapPlugins(cb, includeServerCode = true) {
|
|
|
421
441
|
}
|
|
422
442
|
function firstPlugin(cb, includeServerCode = true) {
|
|
423
443
|
for (const [plName, pl] of plugins.entries()) {
|
|
424
|
-
if (!includeServerCode && plName
|
|
444
|
+
if (!includeServerCode && plName.startsWith(exports.SERVER_CODE_ID))
|
|
425
445
|
continue;
|
|
426
446
|
try {
|
|
427
447
|
const ret = cb(pl, plName);
|
|
@@ -444,10 +464,10 @@ if (!(0, fs_1.existsSync)(exports.PATH))
|
|
|
444
464
|
}
|
|
445
465
|
catch { }
|
|
446
466
|
exports.pluginsWatcher = (0, misc_1.watchDir)(exports.PATH, rescanAsap);
|
|
447
|
-
exports.enablePlugins = (0, config_1.defineConfig)(
|
|
467
|
+
exports.enablePlugins = (0, config_1.defineConfig)(misc_1.CFG.enable_plugins, ['antibrute']);
|
|
448
468
|
exports.enablePlugins.sub(rescanAsap);
|
|
449
469
|
exports.suspendPlugins = (0, config_1.defineConfig)(misc_1.CFG.suspend_plugins, false);
|
|
450
|
-
exports.pluginsConfig = (0, config_1.defineConfig)(
|
|
470
|
+
exports.pluginsConfig = (0, config_1.defineConfig)(misc_1.CFG.plugins_config, {});
|
|
451
471
|
exports.PLUGIN_MAIN_FILE = 'plugin.js';
|
|
452
472
|
const pluginWatchers = new Map();
|
|
453
473
|
async function rescan() {
|
|
@@ -548,7 +568,7 @@ function watchPlugin(id, path) {
|
|
|
548
568
|
throw Error(pluginData.badApi);
|
|
549
569
|
await alreadyRunning?.unload(true);
|
|
550
570
|
console.debug("Starting plugin", id);
|
|
551
|
-
const storageDir = (0, path_1.resolve)(exports.PATH, id, exports.STORAGE_FOLDER) +
|
|
571
|
+
const storageDir = (0, path_1.resolve)(exports.PATH, id, exports.STORAGE_FOLDER) + path_1.sep;
|
|
552
572
|
await (0, promises_1.mkdir)(storageDir, { recursive: true });
|
|
553
573
|
const openDbs = [];
|
|
554
574
|
const subbedConfigs = [];
|
|
@@ -607,7 +627,7 @@ function watchPlugin(id, path) {
|
|
|
607
627
|
async i18n(ctx) {
|
|
608
628
|
return (0, i18n_1.i18nFromTranslations)(await (0, lang_1.getLangData)(ctx), const_1.EMBEDDED_LANGUAGE);
|
|
609
629
|
},
|
|
610
|
-
});
|
|
630
|
+
}, unloadResources);
|
|
611
631
|
const folder = (0, path_1.dirname)(module);
|
|
612
632
|
const { sections, unwatch } = (0, customHtml_1.watchLoadCustomHtml)(folder);
|
|
613
633
|
pluginData.getCustomHtml = () => Object.assign(Object.fromEntries(sections), (0, misc_1.callable)(pluginData.customHtml) || {});
|
|
@@ -615,10 +635,7 @@ function watchPlugin(id, path) {
|
|
|
615
635
|
const plugin = new Plugin(id, folder, pluginData, async () => {
|
|
616
636
|
unwatchIcons();
|
|
617
637
|
unwatch();
|
|
618
|
-
|
|
619
|
-
x();
|
|
620
|
-
await Promise.allSettled(openDbs.map(x => x.close()));
|
|
621
|
-
openDbs.length = 0;
|
|
638
|
+
await unloadResources();
|
|
622
639
|
});
|
|
623
640
|
pluginReady.resolve();
|
|
624
641
|
if (alreadyRunning)
|
|
@@ -630,6 +647,12 @@ function watchPlugin(id, path) {
|
|
|
630
647
|
events_1.default.emit(wasInstalled ? 'pluginStarted' : 'pluginInstalled', plugin);
|
|
631
648
|
}
|
|
632
649
|
events_1.default.emit('pluginStarted:' + id);
|
|
650
|
+
async function unloadResources() {
|
|
651
|
+
for (const x of subbedConfigs)
|
|
652
|
+
x();
|
|
653
|
+
await Promise.allSettled(openDbs.map(x => x.close()));
|
|
654
|
+
openDbs.length = 0;
|
|
655
|
+
}
|
|
633
656
|
}
|
|
634
657
|
catch (e) {
|
|
635
658
|
await markItInactive();
|
package/src/selfCheck.js
CHANGED
|
@@ -24,7 +24,8 @@ async function selfCheck(url) {
|
|
|
24
24
|
const prjInfo = await (0, github_1.getProjectInfo)();
|
|
25
25
|
console.log(`Checking server ${url}`);
|
|
26
26
|
const parsed = new URL(url);
|
|
27
|
-
const
|
|
27
|
+
const hostname = (0, cross_1.normalizeHost)(parsed.hostname);
|
|
28
|
+
const family = !(0, net_1.isIP)(hostname) ? undefined : (0, net_1.isIPv6)(hostname) ? 6 : 4;
|
|
28
29
|
try {
|
|
29
30
|
++activeSelfChecks;
|
|
30
31
|
for (const services of lodash_1.default.chunk(lodash_1.default.shuffle(prjInfo.selfCheckServices), 2)) {
|
|
@@ -38,7 +39,7 @@ async function selfCheck(url) {
|
|
|
38
39
|
console.debug(svc);
|
|
39
40
|
body = applySymbols(body);
|
|
40
41
|
serviceUrl = applySymbols(serviceUrl);
|
|
41
|
-
const timeout =
|
|
42
|
+
const timeout = 9_000;
|
|
42
43
|
const res = await (0, cross_1.haveTimeout)(timeout, (0, util_http_1.httpString)(serviceUrl, { family, timeout, ...rest, body }));
|
|
43
44
|
const success = new RegExp(regexpSuccess).test(res);
|
|
44
45
|
const failure = new RegExp(regexpFailure).test(res);
|
|
@@ -60,7 +61,7 @@ async function selfCheck(url) {
|
|
|
60
61
|
--activeSelfChecks;
|
|
61
62
|
}
|
|
62
63
|
function applySymbols(s) {
|
|
63
|
-
return s?.replace('$IP',
|
|
64
|
+
return s?.replace('$IP', hostname)
|
|
64
65
|
.replace('$PORT', parsed.port || (parsed.protocol === 'https:' ? '443' : '80'))
|
|
65
66
|
.replace('$URL', url.replace(/\/$/, '') + CHECK_URL);
|
|
66
67
|
}
|
package/src/serveFile.js
CHANGED
|
@@ -25,7 +25,7 @@ const errorPages_1 = require("./errorPages");
|
|
|
25
25
|
const stream_1 = require("stream");
|
|
26
26
|
const crypto_1 = require("crypto");
|
|
27
27
|
const iconv_lite_1 = __importDefault(require("iconv-lite"));
|
|
28
|
-
const allowedReferer = (0, config_1.defineConfig)(
|
|
28
|
+
const allowedReferer = (0, config_1.defineConfig)(misc_1.CFG.allowed_referer, '');
|
|
29
29
|
const maxDownloads = downloadLimiter((0, config_1.defineConfig)(misc_1.CFG.max_downloads, 0), () => true);
|
|
30
30
|
const maxDownloadsPerIp = downloadLimiter((0, config_1.defineConfig)(misc_1.CFG.max_downloads_per_ip, 0), ctx => ctx.ip);
|
|
31
31
|
const maxDownloadsPerAccount = downloadLimiter((0, config_1.defineConfig)(misc_1.CFG.max_downloads_per_account, 0), ctx => (0, auth_1.getCurrentUsername)(ctx) || undefined);
|
|
@@ -66,13 +66,13 @@ async function serveFileNode(ctx, node) {
|
|
|
66
66
|
if (await maxDownloadsPerAccount(ctx) === undefined) // returning false will not execute other limits
|
|
67
67
|
await maxDownloads(ctx) || await maxDownloadsPerIp(ctx);
|
|
68
68
|
}
|
|
69
|
-
const mimeCfg = (0, config_1.defineConfig)(
|
|
69
|
+
const mimeCfg = (0, config_1.defineConfig)(misc_1.CFG.mime, {}, obj => {
|
|
70
70
|
const matchers = Object.keys(obj).map(k => (0, misc_1.makeMatcher)(k));
|
|
71
71
|
const values = Object.values(obj);
|
|
72
72
|
return (name) => values[matchers.findIndex(matcher => matcher(name))];
|
|
73
73
|
});
|
|
74
74
|
// after this number of seconds, the browser should check the server to see if there's a newer version of the file
|
|
75
|
-
const cacheControlDiskFiles = (0, config_1.defineConfig)(
|
|
75
|
+
const cacheControlDiskFiles = (0, config_1.defineConfig)(misc_1.CFG.cache_control_disk_files, 5);
|
|
76
76
|
async function serveFile(ctx, filePath, mime, cached) {
|
|
77
77
|
if (!filePath)
|
|
78
78
|
return;
|
|
@@ -11,7 +11,7 @@ const events_1 = __importDefault(require("./events"));
|
|
|
11
11
|
const cross_const_1 = require("./cross-const");
|
|
12
12
|
const upload_1 = require("./upload");
|
|
13
13
|
const multipartUpload_1 = require("./multipartUpload");
|
|
14
|
-
const
|
|
14
|
+
const promises_1 = require("stream/promises");
|
|
15
15
|
const stream_1 = require("stream");
|
|
16
16
|
const serveFile_1 = require("./serveFile");
|
|
17
17
|
const const_1 = require("./const");
|
|
@@ -24,11 +24,12 @@ const misc_1 = require("./misc");
|
|
|
24
24
|
const roots_1 = require("./roots");
|
|
25
25
|
const xxhashjs_1 = __importDefault(require("xxhashjs"));
|
|
26
26
|
const fs_1 = __importDefault(require("fs"));
|
|
27
|
-
const
|
|
27
|
+
const promises_2 = require("fs/promises");
|
|
28
28
|
const comments_1 = require("./comments");
|
|
29
29
|
const basicWeb_1 = require("./basicWeb");
|
|
30
30
|
const icons_1 = require("./icons");
|
|
31
31
|
const plugins_1 = require("./plugins");
|
|
32
|
+
const uploadOwners_1 = require("./uploadOwners");
|
|
32
33
|
const serveFrontendFiles = (0, serveGuiFiles_1.serveGuiFiles)(process.env.FRONTEND_PROXY, cross_const_1.FRONTEND_URI);
|
|
33
34
|
const serveFrontendPrefixed = (0, koa_mount_1.default)(cross_const_1.FRONTEND_URI.slice(0, -1), serveFrontendFiles);
|
|
34
35
|
const serveAdminFiles = (0, serveGuiFiles_1.serveGuiFiles)(process.env.ADMIN_PROXY, cross_const_1.ADMIN_URI);
|
|
@@ -70,7 +71,7 @@ const serveSharedFiles = async (ctx, next) => {
|
|
|
70
71
|
const decPath = (0, misc_1.safeDecodeURIComponent)(path, '');
|
|
71
72
|
const fn = (0, path_1.basename)(decPath);
|
|
72
73
|
const folderUri = (0, misc_1.pathEncode)((0, path_1.dirname)(decPath)); // re-encode to get readable urls
|
|
73
|
-
const folder = await (0, vfs_1.urlToNode)(folderUri, ctx, vfs_1.vfs, true); // we don't require the folder to already exist, but to be mapped on disk AND to have proper permissions
|
|
74
|
+
const folder = await (0, vfs_1.urlToNode)(folderUri, ctx, vfs_1.vfs.compiled(), true); // we don't require the folder to already exist, but to be mapped on disk AND to have proper permissions
|
|
74
75
|
if (!folder)
|
|
75
76
|
return (0, errorPages_1.sendErrorPage)(ctx, cross_const_1.HTTP_NOT_FOUND);
|
|
76
77
|
ctx.state.uploadPath = decPath;
|
|
@@ -110,8 +111,9 @@ const serveSharedFiles = async (ctx, next) => {
|
|
|
110
111
|
try {
|
|
111
112
|
if ((await events_1.default.emitAsync('deleting', { node, ctx }))?.isDefaultPrevented())
|
|
112
113
|
return ctx.status = cross_const_1.HTTP_FAILED_DEPENDENCY;
|
|
113
|
-
await (0,
|
|
114
|
+
await (0, promises_2.rm)(source, { recursive: true });
|
|
114
115
|
await (0, misc_1.deleteStoredFileAttrs)(source);
|
|
116
|
+
(0, uploadOwners_1.deleteUploadOwner)(path);
|
|
115
117
|
void (0, comments_1.setCommentFor)(source, ''); // necessary only to clean a possible descript.ion or kvstorage
|
|
116
118
|
return ctx.status = cross_const_1.HTTP_OK;
|
|
117
119
|
}
|
|
@@ -189,9 +191,8 @@ async function calcHash(fn, limit = Infinity) {
|
|
|
189
191
|
done();
|
|
190
192
|
}
|
|
191
193
|
});
|
|
192
|
-
fs_1.default.createReadStream(fn, { end: limit - 1 }).pipe(stream);
|
|
193
194
|
console.debug('Hashing', fn);
|
|
194
|
-
await (0,
|
|
195
|
+
await (0, promises_1.pipeline)(fs_1.default.createReadStream(fn, { end: limit - 1 }), stream);
|
|
195
196
|
console.debug('Hashed', fn);
|
|
196
197
|
return hash.digest().toString(16);
|
|
197
198
|
}
|
package/src/serveGuiFiles.js
CHANGED
|
@@ -104,6 +104,18 @@ async function treatIndex(ctx, filesUri, body) {
|
|
|
104
104
|
})));
|
|
105
105
|
const timestamp = await getFaviconTimestamp();
|
|
106
106
|
const lang = await (0, lang_1.getLangData)(ctx);
|
|
107
|
+
let group = 0; // this represents the loading-group: all plugins with the same group can be loaded concurrently
|
|
108
|
+
const loadScripts = (0, misc_1.onlyTruthy)((0, plugins_1.mapPlugins)((p, id) => {
|
|
109
|
+
const js = p.frontend_js?.map(f => f.includes('//') ? f : pub + id + '/' + f);
|
|
110
|
+
if (!js?.length)
|
|
111
|
+
return;
|
|
112
|
+
if (p.afterPlugin) // an afterPlugin constraint needs a loading barrier before this plugin's scripts
|
|
113
|
+
group++;
|
|
114
|
+
const ret = { id, group, js };
|
|
115
|
+
if (p.beforePlugin) // a beforePlugin constraint creates a loading barrier after this plugin's scripts
|
|
116
|
+
group++;
|
|
117
|
+
return ret;
|
|
118
|
+
}));
|
|
107
119
|
return body
|
|
108
120
|
.replace(/((?:src|href) *= *['"])\/?(?!([a-z]+:\/)?\/)(?!\?)/g, '$1' + ctx.state.revProxyPath + filesUri)
|
|
109
121
|
.replace(/<(\/)?(head|body)>/g, (all, isClose, name) => {
|
|
@@ -120,11 +132,12 @@ async function treatIndex(ctx, filesUri, body) {
|
|
|
120
132
|
pathSeparator: path_1.sep,
|
|
121
133
|
session: session instanceof apiMiddleware_1.ApiError ? null : session,
|
|
122
134
|
plugins,
|
|
123
|
-
loadScripts
|
|
135
|
+
loadScripts,
|
|
124
136
|
prefixUrl: ctx.state.revProxyPath || '',
|
|
125
137
|
proxyDetected: Boolean((0, middlewares_1.getProxyDetected)()),
|
|
126
138
|
dontOverwriteUploading: upload_1.dontOverwriteUploading.get(),
|
|
127
139
|
splitUploads: splitUploads.get(),
|
|
140
|
+
disableDefaultStyle: (0, plugins_1.mapPlugins)(p => p.disableDefaultStyle).some(Boolean),
|
|
128
141
|
kb: size1024.compiled(),
|
|
129
142
|
forceTheme: (0, plugins_1.mapPlugins)(p => lodash_1.default.isString(p.isTheme) ? p.isTheme : undefined).find(Boolean),
|
|
130
143
|
customHtml: lodash_1.default.omit((0, customHtml_1.getAllSections)(), ['top', 'bottom', 'htmlHead', 'style']), // exclude the sections we already apply in this phase
|
|
@@ -194,7 +207,7 @@ function serveProxied(port, uri) {
|
|
|
194
207
|
import('koa-better-http-proxy').then(lib => // dynamic import to avoid having this in final distribution
|
|
195
208
|
proxy = lib.default('127.0.0.1:' + port, {
|
|
196
209
|
parseReqBody: false, // the dev GUI proxy serves app/assets, so avoid koa-better-http-proxy trying to reread ctx.req
|
|
197
|
-
proxyReqPathResolver: (ctx) => shouldServeApp(ctx) ? '/' : ctx.
|
|
210
|
+
proxyReqPathResolver: (ctx) => shouldServeApp(ctx) ? '/' : ctx.url,
|
|
198
211
|
userResDecorator(_res, data, ctx) {
|
|
199
212
|
return shouldServeApp(ctx) ? treatIndex(ctx, uri, String(data))
|
|
200
213
|
: adjustBundlerLinks(ctx, uri, data);
|
|
@@ -0,0 +1,27 @@
|
|
|
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.SERVER_CODE_SPLIT = void 0;
|
|
7
|
+
exports.parseServerCode = parseServerCode;
|
|
8
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
9
|
+
const cross_1 = require("./cross");
|
|
10
|
+
exports.SERVER_CODE_SPLIT = '//---';
|
|
11
|
+
function parseServerCode(text) {
|
|
12
|
+
const split = text.split(new RegExp('(?:^|\n)' + lodash_1.default.escapeRegExp(exports.SERVER_CODE_SPLIT), 'g'));
|
|
13
|
+
const ret = [];
|
|
14
|
+
let first = true;
|
|
15
|
+
for (const part of split)
|
|
16
|
+
if (first) { // first is special, no name
|
|
17
|
+
first = false;
|
|
18
|
+
const code = part.trim();
|
|
19
|
+
if (code)
|
|
20
|
+
ret.push({ name: '', code });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const [name, code] = (0, cross_1.splitAt)('\n', part);
|
|
24
|
+
ret.push({ name, code });
|
|
25
|
+
}
|
|
26
|
+
return ret;
|
|
27
|
+
}
|