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.
Files changed (268) hide show
  1. package/admin/assets/AccountsPage-DB7ljNHT.js +3 -0
  2. package/admin/assets/ArrayField-Qj2iHD7I.js +2 -0
  3. package/admin/assets/ConfigFilePage-TErmt0PK.js +4 -0
  4. package/admin/assets/CustomHtmlPage-CVmk-_JS.js +2 -0
  5. package/admin/assets/DataTable-DmnQXyFO.js +119 -0
  6. package/admin/assets/DateTimeField-BUWd2w9-.js +1 -0
  7. package/admin/assets/EventManager-9vwKg8as.js +1 -0
  8. package/admin/assets/FileField-DoBaSTxF.js +117 -0
  9. package/admin/assets/InternetPage-Biuer1tb.js +8 -0
  10. package/admin/assets/LangPage-Vd24DNK5.js +2 -0
  11. package/admin/assets/LogsPage-BdvttEqM.js +3 -0
  12. package/admin/assets/MenuButton-CHLgqU5W.js +1 -0
  13. package/admin/assets/MonitorPage-DlXkZUmT.js +2 -0
  14. package/admin/assets/OptionsPage-Zi8FmrjE.js +3 -0
  15. package/admin/assets/PluginsPage-BzwEIFt5.js +1 -0
  16. package/admin/assets/Tabs-CVipqhB9.js +1 -0
  17. package/admin/assets/TextEditor-BeS-mR2I.css +1 -0
  18. package/admin/assets/TextEditor-clbVugYM.js +34 -0
  19. package/admin/assets/VfsPathField-YUCV9PfL.js +1 -0
  20. package/admin/assets/{af-BDoqNBMg.js → af-B1Gi_bpo.js} +1 -1
  21. package/admin/assets/agentIcons-DW9QViyx.js +1 -0
  22. package/admin/assets/{am-DXBvKJ3X.js → am-B-dZnhsM.js} +1 -1
  23. package/admin/assets/{ar-BJIoxKTh.js → ar-DsQGp6kZ.js} +1 -1
  24. package/admin/assets/{ar-dz-C9l3uc_3.js → ar-dz-CxlYiKtP.js} +1 -1
  25. package/admin/assets/{ar-iq-DKH8u2lX.js → ar-iq-y4_Puo4B.js} +1 -1
  26. package/admin/assets/{ar-kw-D3gJalQp.js → ar-kw-DTOZ0VC7.js} +1 -1
  27. package/admin/assets/{ar-ly-BZJzWFeR.js → ar-ly-lPV9gApv.js} +1 -1
  28. package/admin/assets/{ar-ma-DabiOCr0.js → ar-ma-DZqLSpeB.js} +1 -1
  29. package/admin/assets/{ar-sa-C7suzSCY.js → ar-sa-v-5vsIOR.js} +1 -1
  30. package/admin/assets/{ar-tn-BvBucEx0.js → ar-tn-a4q8lhXy.js} +1 -1
  31. package/admin/assets/{az-CPUppH2M.js → az-Cj4yq-j-.js} +1 -1
  32. package/admin/assets/baseUrl-Do5CYuBZ.js +2 -0
  33. package/admin/assets/{be-vzvhX2dl.js → be-B-3QlFM8.js} +1 -1
  34. package/admin/assets/{bg-09Xbj_Y3.js → bg-A8-93xdK.js} +1 -1
  35. package/admin/assets/{bi-CvhiuiE9.js → bi-bwwEWGG2.js} +1 -1
  36. package/admin/assets/{bm-DtJAZAkZ.js → bm-DpLmtYXC.js} +1 -1
  37. package/admin/assets/{bn-Dnzh2x6I.js → bn-DPL_BgLw.js} +1 -1
  38. package/admin/assets/{bn-bd-xEy4797g.js → bn-bd-DukmY_R5.js} +1 -1
  39. package/admin/assets/{bo-vuPmptG3.js → bo-68KDEKGI.js} +1 -1
  40. package/admin/assets/{br-C9xH4OD3.js → br-gA86uwqY.js} +1 -1
  41. package/admin/assets/{bs-BB_uCUp1.js → bs-SfthSp42.js} +1 -1
  42. package/admin/assets/{ca-D7LKaf98.js → ca-8qUtZKFe.js} +1 -1
  43. package/admin/assets/{cs-DMFTNdNC.js → cs-CTHcqPCX.js} +1 -1
  44. package/admin/assets/{cv-DNUjJwnO.js → cv-BhBW7hcO.js} +1 -1
  45. package/admin/assets/{cy-qmAOTPwC.js → cy-BYTS4PNY.js} +1 -1
  46. package/admin/assets/{da-DDX54iM9.js → da-BiwfOrRd.js} +1 -1
  47. package/admin/assets/{de-BBmlsfbM.js → de-BFqyDxvo.js} +1 -1
  48. package/admin/assets/{de-at-CuMZqYfu.js → de-at-C7zUXXVL.js} +1 -1
  49. package/admin/assets/{de-ch-s8OXKb-5.js → de-ch-BVHosAzt.js} +1 -1
  50. package/admin/assets/dist-DC2A6cD2.js +1 -0
  51. package/admin/assets/{dv-Cjs3QDBN.js → dv-dvig99nB.js} +1 -1
  52. package/admin/assets/{el-BsAOrUDP.js → el-BtJcYjoT.js} +1 -1
  53. package/admin/assets/en-B26lXzkx.js +1 -0
  54. package/admin/assets/{en-au-8tMHM2OT.js → en-au-CWzJ565h.js} +1 -1
  55. package/admin/assets/{en-ca-c9jM4M_8.js → en-ca-nou5bZ6q.js} +1 -1
  56. package/admin/assets/{en-in-D2ULzXMp.js → en-gb-kl3giCnh.js} +1 -1
  57. package/admin/assets/{en-ie-D4_e3kV6.js → en-ie-C55e3apX.js} +1 -1
  58. package/admin/assets/{en-il-BoaZOven.js → en-il-HYabE6j7.js} +1 -1
  59. package/admin/assets/{en-tt-Cn72OR4N.js → en-in-Bri93cNM.js} +1 -1
  60. package/admin/assets/{en-nz-grrmB3dg.js → en-nz-BqhpzeuX.js} +1 -1
  61. package/admin/assets/{en-sg-Cmqxpoir.js → en-sg-q8xv-C8V.js} +1 -1
  62. package/admin/assets/{en-gb-unFIXx9f.js → en-tt-BqzQXPA5.js} +1 -1
  63. package/admin/assets/{eo-Cqxmc0TT.js → eo-5Y962E4S.js} +1 -1
  64. package/admin/assets/{es-CkjKdFZ3.js → es-DOQ7swrW.js} +1 -1
  65. package/admin/assets/{es-do-CTcTshup.js → es-do-Diob7juh.js} +1 -1
  66. package/admin/assets/{es-mx-DMXgMMxs.js → es-mx-miy7RFDh.js} +1 -1
  67. package/admin/assets/{es-pr-DNCV91L3.js → es-pr-CZa5Iw2n.js} +1 -1
  68. package/admin/assets/{es-us-C7o2mlIH.js → es-us-BX03CBQf.js} +1 -1
  69. package/admin/assets/{esm-DvUbRuYx.js → esm-Dtaf0rWK.js} +1 -1
  70. package/admin/assets/{et-DaPpH-gK.js → et-DGWOmMlC.js} +1 -1
  71. package/admin/assets/{eu-DfKyFruu.js → eu-D1Qj8RMh.js} +1 -1
  72. package/admin/assets/{fa-xWrBjePb.js → fa--TfwKnl9.js} +1 -1
  73. package/admin/assets/{fi-BOWDMsDS.js → fi-Dcbkv925.js} +1 -1
  74. package/admin/assets/{fo-B3FupW8R.js → fo-BPGX_WF4.js} +1 -1
  75. package/admin/assets/{fr-DZBDBkwp.js → fr-CNsDHzNh.js} +1 -1
  76. package/admin/assets/{fr-ca-CRePPJJ5.js → fr-ca-BsNs3KIJ.js} +1 -1
  77. package/admin/assets/{fr-ch-pFKvYU8D.js → fr-ch-BBmdtvlB.js} +1 -1
  78. package/admin/assets/{fy-0k7RzHUH.js → fy-q3UE-mqp.js} +1 -1
  79. package/admin/assets/{ga-D31rB2ys.js → ga-C-DOgnYh.js} +1 -1
  80. package/admin/assets/{gd-CbgY-FFE.js → gd-mLMfry_l.js} +1 -1
  81. package/admin/assets/{gl-DrtvS9gt.js → gl-CO8Bj9I_.js} +1 -1
  82. package/admin/assets/{gom-latn-Dkke3GyE.js → gom-latn-myglMmHV.js} +1 -1
  83. package/admin/assets/{gu-BlEipYhR.js → gu-Bdz1OpMU.js} +1 -1
  84. package/admin/assets/{he-BrHT6OLB.js → he-C4lqk2AA.js} +1 -1
  85. package/admin/assets/{hi-Bx0zlAgr.js → hi-h0Byc_UN.js} +1 -1
  86. package/admin/assets/{hr-CoFYGbr6.js → hr-CZV7Za1n.js} +1 -1
  87. package/admin/assets/{ht-Df5xKo4a.js → ht-Dg2blNvl.js} +1 -1
  88. package/admin/assets/{hu-BFTN3q6-.js → hu-DbOVhd9K.js} +1 -1
  89. package/admin/assets/{hy-am-DuZTJQed.js → hy-am-E48Q7kWE.js} +1 -1
  90. package/admin/assets/icons-3rupj9Xr.js +1 -0
  91. package/admin/assets/{id-DBZ5nPkf.js → id-Cd4yWevW.js} +1 -1
  92. package/admin/assets/{index-eeRX25da.css → index-11n5z0Ez.css} +1 -1
  93. package/admin/assets/index-Dsq-sVu9.js +477 -0
  94. package/admin/assets/{is-CmeHm47B.js → is-BJZYSfrA.js} +1 -1
  95. package/admin/assets/{it-Cv3fjOA9.js → it-BRaqqFz4.js} +1 -1
  96. package/admin/assets/{it-ch-DCTyHA3T.js → it-ch-BkLqxaff.js} +1 -1
  97. package/admin/assets/{ja-DHNVXAM5.js → ja-X_I-25cD.js} +1 -1
  98. package/admin/assets/{jv-BdNIxhWv.js → jv-BBBx3R3v.js} +1 -1
  99. package/admin/assets/{ka-D-joD0QB.js → ka-D9Yrujqk.js} +1 -1
  100. package/admin/assets/{kk-8wRNNjxz.js → kk-Bz450_o8.js} +1 -1
  101. package/admin/assets/{km-dUWOg1uK.js → km-CKOAqvHM.js} +1 -1
  102. package/admin/assets/{kn-CwQbzel0.js → kn-k2xTBR7a.js} +1 -1
  103. package/admin/assets/{ko-D_gn9Sfw.js → ko-BCvoCYib.js} +1 -1
  104. package/admin/assets/{ku-Dh3MRML3.js → ku-D6Rloctr.js} +1 -1
  105. package/admin/assets/{ky-CWYuRvil.js → ky-C54qnEzk.js} +1 -1
  106. package/admin/assets/{lb-Cc2xsSIx.js → lb-y3rkrWB2.js} +1 -1
  107. package/admin/assets/{lo-CkwNFDBz.js → lo-nA_dPnOU.js} +1 -1
  108. package/admin/assets/{lt-HWyFDLXu.js → lt-hjPmCCML.js} +1 -1
  109. package/admin/assets/{lv-C7Zx3OLO.js → lv-Bh4qmUa_.js} +1 -1
  110. package/admin/assets/{me-DH3AqnfF.js → me-e32suC_n.js} +1 -1
  111. package/admin/assets/{mi-DMmUry6m.js → mi-Dwbsuyr9.js} +1 -1
  112. package/admin/assets/{mk-D6MNZrzX.js → mk-DjUXwKRn.js} +1 -1
  113. package/admin/assets/{ml-B_ur8Zqf.js → ml-CWjFAViF.js} +1 -1
  114. package/admin/assets/{mn-DeL8Ry7x.js → mn-BU4qTvEl.js} +1 -1
  115. package/admin/assets/{mr-C01qRXFL.js → mr-Bvnduzfb.js} +1 -1
  116. package/admin/assets/{ms-DFUZJzOm.js → ms-XuKVAni7.js} +1 -1
  117. package/admin/assets/{ms-my-CUYHkebs.js → ms-my-CDbnYp0g.js} +1 -1
  118. package/admin/assets/{mt-CE8t5jSj.js → mt-PDuxJf8J.js} +1 -1
  119. package/admin/assets/{my-Df96A8jZ.js → my-7N6SHnsD.js} +1 -1
  120. package/admin/assets/{nb-D0837ewu.js → nb-Cu3KUtTR.js} +1 -1
  121. package/admin/assets/{ne-BdK5TR0R.js → ne-CYW0uI2a.js} +1 -1
  122. package/admin/assets/{nl-oYKmSma6.js → nl-DL7cw699.js} +1 -1
  123. package/admin/assets/{nl-be-BfVC0cur.js → nl-be-B3NSF-2w.js} +1 -1
  124. package/admin/assets/{nn-IEUPnyc8.js → nn-BHITVkGB.js} +1 -1
  125. package/admin/assets/{oc-lnc-g_wSIROr.js → oc-lnc-dp3c4Bty.js} +1 -1
  126. package/admin/assets/{pa-in-gWhcLmZE.js → pa-in-BxgIlP1T.js} +1 -1
  127. package/admin/assets/{pl-H3-LoxVn.js → pl-GrG6yjkB.js} +1 -1
  128. package/admin/assets/{pt-br-B8UcNR6_.js → pt-br-BxTgeF95.js} +1 -1
  129. package/admin/assets/{pt-hiX8RqVw.js → pt-cVbj6hL6.js} +1 -1
  130. package/admin/assets/{rn-BKupViLr.js → rn-DiVJ2_Jy.js} +1 -1
  131. package/admin/assets/{ro-DmhgIvml.js → ro-D1WeRyXR.js} +1 -1
  132. package/admin/assets/rolldown-runtime-CacB07dV.js +1 -0
  133. package/admin/assets/routing-Cjyek7O_.js +1 -0
  134. package/admin/assets/{ru-CcpO-b8g.js → ru-DZCu4V9W.js} +1 -1
  135. package/admin/assets/{rw-DWX_5ZK0.js → rw-DfFpVc_g.js} +1 -1
  136. package/admin/assets/{sd-Dat8YgLw.js → sd-yDyBYxtQ.js} +1 -1
  137. package/admin/assets/{se-nTBYCwx3.js → se-EJKibocp.js} +1 -1
  138. package/admin/assets/sha512-DJtGRwVu.js +1 -0
  139. package/admin/assets/{si-DG88w5El.js → si-BCBV00rO.js} +1 -1
  140. package/admin/assets/{sk-s5FxW36J.js → sk-B2VvG_mo.js} +1 -1
  141. package/admin/assets/{sl-B-HyGrpA.js → sl-BTYmt65M.js} +1 -1
  142. package/admin/assets/{sq-DPFtfEC8.js → sq-BaLGKAHu.js} +1 -1
  143. package/admin/assets/{sr-bBPg1zz4.js → sr-DarE-95b.js} +1 -1
  144. package/admin/assets/{sr-cyrl-cXkYHMcP.js → sr-cyrl-CrwXFtkw.js} +1 -1
  145. package/admin/assets/{ss-7nSqXJN7.js → ss-CbBe1RUs.js} +1 -1
  146. package/admin/assets/{sv-B8mG1QK4.js → sv-Baw7Je9j.js} +1 -1
  147. package/admin/assets/{sv-fi-CotGRXSH.js → sv-fi-CVHORXkw.js} +1 -1
  148. package/admin/assets/{sw-fAxU-Uck.js → sw-k1H_OzIi.js} +1 -1
  149. package/admin/assets/{ta-Dl0Z1pan.js → ta-Bc4ZaZT9.js} +1 -1
  150. package/admin/assets/{te-DmcKasFF.js → te-ClciACZi.js} +1 -1
  151. package/admin/assets/{tet-D6mmvYpQ.js → tet-B75w1oSG.js} +1 -1
  152. package/admin/assets/{tg-39Hf2-oO.js → tg-CuH2ARRk.js} +1 -1
  153. package/admin/assets/{th-BSmTJSFN.js → th-LvExAoJ6.js} +1 -1
  154. package/admin/assets/{tk-m85E5igC.js → tk-2CFwTMKo.js} +1 -1
  155. package/admin/assets/{tl-ph-BlYxVvNt.js → tl-ph-CNJ_lViV.js} +1 -1
  156. package/admin/assets/{tlh-D1POnIeE.js → tlh-9UbKQkHo.js} +1 -1
  157. package/admin/assets/{tr-BurtfrLy.js → tr-Cu4P4OMe.js} +1 -1
  158. package/admin/assets/{tzl-BvyD0lOf.js → tzl-D97WsZW-.js} +1 -1
  159. package/admin/assets/{tzm-DZHS0dK6.js → tzm-CURkYgMY.js} +1 -1
  160. package/admin/assets/{tzm-latn-yNW7CNCH.js → tzm-latn-BRGsWK-9.js} +1 -1
  161. package/admin/assets/{ug-cn-rxs2pf5Y.js → ug-cn-Cd4exKOc.js} +1 -1
  162. package/admin/assets/{uk-CgZM-NnA.js → uk-DEXM3PIH.js} +1 -1
  163. package/admin/assets/{ur-YxIR8yYD.js → ur-DpUycj07.js} +1 -1
  164. package/admin/assets/{uz-CE9QsQMG.js → uz-CznjzufA.js} +1 -1
  165. package/admin/assets/{uz-latn-DiVdSW6G.js → uz-latn-Baga58FN.js} +1 -1
  166. package/admin/assets/{vi-DKARV6qf.js → vi-BrZWcL2C.js} +1 -1
  167. package/admin/assets/{x-pseudo-BuAdOrXQ.js → x-pseudo-BY6J4I7_.js} +1 -1
  168. package/admin/assets/{yo-BcB85DhC.js → yo-Dkeaym0x.js} +1 -1
  169. package/admin/assets/{zh-DLmxfxzl.js → zh-C2fOZUKR.js} +1 -1
  170. package/admin/assets/{zh-cn-ILRADZJi.js → zh-cn-CiA7hMZm.js} +1 -1
  171. package/admin/assets/{zh-hk-fT7fQk9_.js → zh-hk-EkoniWh0.js} +1 -1
  172. package/admin/assets/{zh-tw-NqRXGP6v.js → zh-tw-KDgos-kB.js} +1 -1
  173. package/admin/index.html +3 -2
  174. package/frontend/assets/{dist-legacy-BPToXNXu.js → dist-legacy-BNFW04Ss.js} +1 -1
  175. package/frontend/assets/index-legacy-2akkos6t.js +1 -0
  176. package/frontend/assets/{lib-legacy-CBk-_G90.js → lib-legacy-CO5spnaN.js} +1 -1
  177. package/frontend/assets/{sha512-legacy-CWrR2rkQ.js → sha512-legacy-BuJAJxVV.js} +1 -1
  178. package/frontend/index.html +1 -1
  179. package/npm-shrinkwrap.json +6 -6
  180. package/package.json +1 -1
  181. package/src/AsapStream.js +18 -14
  182. package/src/SendList.js +42 -16
  183. package/src/ThrottledStream.js +3 -3
  184. package/src/acme.js +23 -10
  185. package/src/adminApis.js +7 -6
  186. package/src/api.accounts.js +11 -7
  187. package/src/api.auth.js +8 -5
  188. package/src/api.cert.js +1 -1
  189. package/src/api.get_file_list.js +14 -11
  190. package/src/api.lang.js +3 -2
  191. package/src/api.monitor.js +1 -1
  192. package/src/api.net.js +3 -1
  193. package/src/api.plugins.js +2 -0
  194. package/src/api.vfs.js +9 -12
  195. package/src/apiMiddleware.js +5 -1
  196. package/src/basicWeb.js +3 -2
  197. package/src/block.js +1 -1
  198. package/src/commands.js +6 -5
  199. package/src/comments.js +29 -13
  200. package/src/config.js +7 -4
  201. package/src/consoleLog.js +14 -12
  202. package/src/const.js +1 -1
  203. package/src/cross-const.js +27 -1
  204. package/src/cross.js +37 -34
  205. package/src/ddns.js +1 -4
  206. package/src/events.js +1 -4
  207. package/src/fileAttr.js +1 -1
  208. package/src/frontEndApis.js +7 -3
  209. package/src/geo.js +8 -2
  210. package/src/github.js +10 -9
  211. package/src/index.js +2 -1
  212. package/src/ips.js +24 -3
  213. package/src/langs/hfs-lang-ar.json +1 -0
  214. package/src/langs/hfs-lang-bg.json +1 -0
  215. package/src/langs/hfs-lang-de.json +1 -0
  216. package/src/langs/hfs-lang-el.json +1 -0
  217. package/src/langs/hfs-lang-es.json +1 -0
  218. package/src/langs/hfs-lang-fi.json +1 -0
  219. package/src/langs/hfs-lang-fr.json +1 -0
  220. package/src/langs/hfs-lang-hu.json +1 -0
  221. package/src/langs/hfs-lang-it.json +1 -0
  222. package/src/langs/hfs-lang-ja.json +1 -0
  223. package/src/langs/hfs-lang-ko.json +1 -0
  224. package/src/langs/hfs-lang-lt.json +1 -0
  225. package/src/langs/hfs-lang-ms.json +1 -0
  226. package/src/langs/hfs-lang-nl.json +1 -0
  227. package/src/langs/hfs-lang-pl.json +1 -0
  228. package/src/langs/hfs-lang-pt-br.json +1 -0
  229. package/src/langs/hfs-lang-ro.json +1 -0
  230. package/src/langs/hfs-lang-ru.json +1 -0
  231. package/src/langs/hfs-lang-sr-latn.json +1 -0
  232. package/src/langs/hfs-lang-sr.json +1 -0
  233. package/src/langs/hfs-lang-th.json +1 -0
  234. package/src/langs/hfs-lang-tr.json +1 -0
  235. package/src/langs/hfs-lang-uk.json +1 -0
  236. package/src/langs/hfs-lang-vi.json +1 -0
  237. package/src/langs/hfs-lang-zh-tw.json +1 -0
  238. package/src/langs/hfs-lang-zh.json +1 -0
  239. package/src/listen.js +17 -13
  240. package/src/log.js +11 -2
  241. package/src/middlewares.js +4 -4
  242. package/src/misc.js +3 -3
  243. package/src/multipartUpload.js +1 -2
  244. package/src/nat.js +1 -1
  245. package/src/outboundProxy.js +7 -0
  246. package/src/perm.js +5 -3
  247. package/src/plugins.js +60 -37
  248. package/src/selfCheck.js +4 -3
  249. package/src/serveFile.js +3 -3
  250. package/src/serveGuiAndSharedFiles.js +7 -6
  251. package/src/serveGuiFiles.js +15 -2
  252. package/src/serverCode.js +27 -0
  253. package/src/throttler.js +3 -2
  254. package/src/update.js +9 -3
  255. package/src/upload.js +29 -11
  256. package/src/uploadOwners.js +95 -0
  257. package/src/util-files.js +4 -2
  258. package/src/util-http.js +3 -1
  259. package/src/vfs.js +67 -30
  260. package/src/walkDir.js +24 -11
  261. package/src/watchLoad.js +13 -2
  262. package/src/webdav.js +33 -31
  263. package/src/zip.js +1 -1
  264. package/admin/assets/dist-CHP3cW_n.js +0 -1
  265. package/admin/assets/en-DQ460ET7.js +0 -1
  266. package/admin/assets/index-BGIe1xaR.js +0 -760
  267. package/admin/assets/sha512-CZz40GwW.js +0 -1
  268. 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)('open_browser_at_start', true);
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)('port', 80);
113
- const listenInterface = (0, config_1.defineConfig)('listen_interface', '');
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 >PLEASE< 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
+ 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?.replace(/DNS:/g, '').split(/, */) });
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)('cert', '', load);
209
- exports.privateKey = (0, config_1.defineConfig)('private_key', '', load);
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)('https_port', const_1.PORT_DISABLED);
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
- if (code)
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
- if (code === 'EACCES' && port < 1024 && !srv.busy) // on Windows, when port is used by a service, we get EACCES
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' || srv.busy)
281
- srv.error = `port ${port} busy: ${await srv.busy || "unknown process"}`;
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
- return this.stream = (0, util_files_1.createFileWithPath)(this.path, { flags: 'a' })
86
- ?.on('error', () => this.stream = undefined);
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 });
@@ -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)('force_https', true);
25
- (0, config_1.defineConfig)('ignore_proxies', false);
26
- const allowAuthorizationHeader = (0, config_1.defineConfig)('authorization_header', true);
27
- exports.sessionDuration = (0, config_1.defineConfig)('session_duration', Number(process.env.SESSION_DURATION) || misc_1.DAY / 1000, v => v * 1000);
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
- void iterator.return?.();
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
  }
@@ -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, events_1.once)(bb, 'finish');
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)('mapped_port', 0);
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
@@ -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 && u !== usernameWas && getAccount(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)('accounts', {});
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)(500);
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
- (0, misc_1.patchKey)(pl, 'unload', was => () => {
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 was === 'function')
231
- return was(...arguments);
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 === exports.SERVER_CODE_ID || (lastStatus === ctx.status && lastBody === ctx.body))
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 !== exports.SERVER_CODE_ID) // we already printed 'reloading'
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 = '.'; // a name that will surely be not found among plugin folders
393
- const serverCode = (0, config_1.defineConfig)('server_code', '', async (script, { k }) => {
394
- try {
395
- (await serverCode.compiled())?.unload();
396
- }
397
- catch { }
398
- const res = {};
399
- try {
400
- new Function('exports,require', script)(res, require); // parse
401
- await initPlugin(res);
402
- res.getCustomHtml = () => (0, misc_1.callable)(res.customHtml) || {};
403
- return new Plugin(exports.SERVER_CODE_ID, '', res, lodash_1.default.noop);
404
- }
405
- catch (e) {
406
- return console.error(k + ':', e.message || String(e));
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 === exports.SERVER_CODE_ID)
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 === exports.SERVER_CODE_ID)
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)('enable_plugins', ['antibrute']);
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)('plugins_config', {});
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) + (const_1.IS_WINDOWS ? '\\' : '/');
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
- for (const x of subbedConfigs)
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 family = !(0, net_1.isIP)(parsed.hostname) ? undefined : (0, net_1.isIPv6)(parsed.hostname) ? 6 : 4;
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 = 8_000;
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', parsed.hostname)
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)('allowed_referer', '');
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)('mime', {}, obj => {
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)('cache_control_disk_files', 5);
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 events_2 = require("events");
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 promises_1 = require("fs/promises");
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, promises_1.rm)(source, { recursive: true });
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, events_2.once)(stream, 'finish');
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
  }
@@ -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: Object.fromEntries((0, plugins_1.mapPlugins)((p, id) => [id, p.frontend_js?.map(f => f.includes('//') ? f : pub + id + '/' + f)])),
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.path,
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
+ }