most-box 0.1.0 → 0.1.2

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 (390) hide show
  1. package/README.md +24 -16
  2. package/electron/main.js +72 -8
  3. package/electron/preload.js +6 -0
  4. package/out/404/index.html +2 -2
  5. package/out/404.html +2 -2
  6. package/out/__next.__PAGE__.txt +6 -6
  7. package/out/__next._full.txt +23 -20
  8. package/out/__next._head.txt +3 -3
  9. package/out/__next._index.txt +8 -6
  10. package/out/__next._tree.txt +6 -4
  11. package/out/_next/static/chunks/0-n3pg7th.zza.js +1 -0
  12. package/out/_next/static/chunks/0.4j.0k5a64vg.js +1 -0
  13. package/out/_next/static/chunks/0.e2avjgna_b2.js +1 -0
  14. package/out/_next/static/chunks/0.ozi1_x2.m.~.js +1 -0
  15. package/out/_next/static/chunks/0.t5wlt51zou5.js +1 -0
  16. package/out/_next/static/chunks/0.w4hkvap~bva.js +1 -0
  17. package/out/_next/static/chunks/00d9h1tddnnnd.js +1 -0
  18. package/out/_next/static/chunks/00tkdqwxch-3s.js +1 -0
  19. package/out/_next/static/chunks/01l3o90g~1z42.js +1 -0
  20. package/out/_next/static/chunks/01mfky9camw6i.js +1 -0
  21. package/out/_next/static/chunks/01r.v-pqs1vrm.js +1 -0
  22. package/out/_next/static/chunks/03edqrb4zdj~g.js +31 -0
  23. package/out/_next/static/chunks/03h_6oo-gqkhz.js +1 -0
  24. package/out/_next/static/chunks/{0ho~log~~-jwp.css → 03h~nhgj0hv3p.css} +1 -1
  25. package/out/_next/static/chunks/04hcgsanv1hhu.js +1 -0
  26. package/out/_next/static/chunks/05g2q0w5b34.g.js +1 -0
  27. package/out/_next/static/chunks/05of77xycbt8~.js +1 -0
  28. package/out/_next/static/chunks/05zwemzfjx3sh.js +1 -0
  29. package/out/_next/static/chunks/06dpc5df94.v1.js +1 -0
  30. package/out/_next/static/chunks/06e1~1-z_ic9a.js +1 -0
  31. package/out/_next/static/chunks/075s7sn.ns~u5.js +1 -0
  32. package/out/_next/static/chunks/07dynrbvd3.f4.js +1 -0
  33. package/out/_next/static/chunks/07p~uva5pwgwe.js +1 -0
  34. package/out/_next/static/chunks/07r9nn-pzlgg1.js +1 -0
  35. package/out/_next/static/chunks/07td.jq7xff84.css +1 -0
  36. package/out/_next/static/chunks/08.72abkgwy9g.js +1 -0
  37. package/out/_next/static/chunks/08576xhv~~jck.js +1 -0
  38. package/out/_next/static/chunks/08u211~k~qu52.js +1 -0
  39. package/out/_next/static/chunks/098.p.2-zm4p7.js +1 -0
  40. package/out/_next/static/chunks/09ngvtajm7e5y.js +1 -0
  41. package/out/_next/static/chunks/09ps~-43n5qyo.js +1 -0
  42. package/out/_next/static/chunks/09v7_0gclxr46.js +1 -0
  43. package/out/_next/static/chunks/09yql86dir9c4.js +1 -0
  44. package/out/_next/static/chunks/09zmlfljowj1~.js +1 -0
  45. package/out/_next/static/chunks/0_s~ebb-7b2hr.js +1 -0
  46. package/out/_next/static/chunks/0_w-0-2z5oqd_.js +1 -0
  47. package/out/_next/static/chunks/0adx~d-j05c9d.css +24 -0
  48. package/out/_next/static/chunks/0ao1lbi4b.sfa.js +1 -0
  49. package/out/_next/static/chunks/0aq.rc9woa2nz.js +1 -0
  50. package/out/_next/static/chunks/0bld2u_ld~va2.js +1 -0
  51. package/out/_next/static/chunks/0bliugh5lxw55.js +1 -0
  52. package/out/_next/static/chunks/0cn9a7aimbdzq.js +1 -0
  53. package/out/_next/static/chunks/0d3f-nk3c.2re.js +1 -0
  54. package/out/_next/static/chunks/0dtohpf7~3d12.js +1 -0
  55. package/out/_next/static/chunks/0e-3e8h7g99yf.js +1 -0
  56. package/out/_next/static/chunks/0e531nije_ln2.js +1 -0
  57. package/out/_next/static/chunks/0e5zvj_rh0z3m.js +1 -0
  58. package/out/_next/static/chunks/0etes81d_cihn.js +1 -0
  59. package/out/_next/static/chunks/0f4y~rkk-n81e.js +1 -0
  60. package/out/_next/static/chunks/0fk~0~p7ivfn1.js +1 -0
  61. package/out/_next/static/chunks/0fw6juc~lsj3z.js +1 -0
  62. package/out/_next/static/chunks/0g0u7785a73vo.js +1 -0
  63. package/out/_next/static/chunks/0g_a~e050bgzg.css +1 -0
  64. package/out/_next/static/chunks/0g_fpgh7drfda.js +1 -0
  65. package/out/_next/static/chunks/{0n~dq4kpx9xxx.js → 0gcsdf57gcm6h.js} +1 -1
  66. package/out/_next/static/chunks/0gwian.hp3-92.js +1 -0
  67. package/out/_next/static/chunks/0gze5uso1mbe9.js +1 -0
  68. package/out/_next/static/chunks/0h4r.qtmpa6eh.js +1 -0
  69. package/out/_next/static/chunks/0hf.aosc-7172.js +1 -0
  70. package/out/_next/static/chunks/0hgz35c1ejbs9.js +1 -0
  71. package/out/_next/static/chunks/0hpev4am9jpmu.css +1 -0
  72. package/out/_next/static/chunks/0hrw-r.xmvmsq.js +1 -0
  73. package/out/_next/static/chunks/0hzg4al.v~8~m.js +1 -0
  74. package/out/_next/static/chunks/0ip9xrols_83o.js +1 -0
  75. package/out/_next/static/chunks/0j4-d0qf.v~kn.js +1 -0
  76. package/out/_next/static/chunks/0jhdeq.j9_02m.js +1 -0
  77. package/out/_next/static/chunks/0jy63h3i-y69i.js +1 -0
  78. package/out/_next/static/chunks/0kdnx_u-60k9s.js +1 -0
  79. package/out/_next/static/chunks/0kq~edq42o1-c.js +1 -0
  80. package/out/_next/static/chunks/0l5_.uqb-uqb8.js +1 -0
  81. package/out/_next/static/chunks/0l682p362d-5w.js +1 -0
  82. package/out/_next/static/chunks/0m68p9txef5rs.js +1 -0
  83. package/out/_next/static/chunks/0mex8svsiv-2l.js +1 -0
  84. package/out/_next/static/chunks/0mme-fm5d2oz2.js +1 -0
  85. package/out/_next/static/chunks/0myp4sjagr~h0.js +1 -0
  86. package/out/_next/static/chunks/0myq9gs8szydh.js +1 -0
  87. package/out/_next/static/chunks/0n.qlfk~z7o.6.js +1 -0
  88. package/out/_next/static/chunks/0n4t80gjc3q5h.js +1 -0
  89. package/out/_next/static/chunks/0o9ce4cyf76by.js +736 -0
  90. package/out/_next/static/chunks/0oz3yl6_-716p.js +1 -0
  91. package/out/_next/static/chunks/0p0sv~fuddvgr.js +1 -0
  92. package/out/_next/static/chunks/0pt.5cg1t09qs.js +1 -0
  93. package/out/_next/static/chunks/0q0ksgxg98xgd.js +1 -0
  94. package/out/_next/static/chunks/0qgx9t4jx16ua.css +1 -0
  95. package/out/_next/static/chunks/0qqupeexg83u7.js +1 -0
  96. package/out/_next/static/chunks/0rb-ri481.kc9.js +1 -0
  97. package/out/_next/static/chunks/0rsnmahfd.59p.js +1 -0
  98. package/out/_next/static/chunks/0rt6rgnvr-s_p.js +1 -0
  99. package/out/_next/static/chunks/0runh28p_gg6..js +1 -0
  100. package/out/_next/static/chunks/0shy.t1fwqcev.js +1 -0
  101. package/out/_next/static/chunks/{0d3shmwh5_nmn.js → 0t2xr05rlu96l.js} +1 -1
  102. package/out/_next/static/chunks/0t6h56rhg1y5i.js +1 -0
  103. package/out/_next/static/chunks/0tdqd1zunusgk.js +1 -0
  104. package/out/_next/static/chunks/0ujbnp38x63ek.js +1 -0
  105. package/out/_next/static/chunks/0ukyg~tkm~h2m.css +1 -0
  106. package/out/_next/static/chunks/0v68pdrp54lb-.js +1 -0
  107. package/out/_next/static/chunks/0vsm0m5sxrb.3.js +1 -0
  108. package/out/_next/static/chunks/0vzlz.iboqo3c.js +1 -0
  109. package/out/_next/static/chunks/0w87vbpkf-ogd.js +1 -0
  110. package/out/_next/static/chunks/0wtf0xsiicxx6.js +1 -0
  111. package/out/_next/static/chunks/0xdwau5k2augv.css +4 -0
  112. package/out/_next/static/chunks/0xgg0~kmf3gd-.js +1 -0
  113. package/out/_next/static/chunks/0xj24-70ptdzp.js +1 -0
  114. package/out/_next/static/chunks/0xxlx772fr3x4.js +1 -0
  115. package/out/_next/static/chunks/0y.li-~3oybew.js +1 -0
  116. package/out/_next/static/chunks/0yl2t7cs-n_ng.js +1 -0
  117. package/out/_next/static/chunks/0yq3kh.hchtm_.js +1 -0
  118. package/out/_next/static/chunks/0ys0l5au.9c2c.js +1 -0
  119. package/out/_next/static/chunks/0z48pmi6buytt.js +1 -0
  120. package/out/_next/static/chunks/0zapnvgy89mg..js +1 -0
  121. package/out/_next/static/chunks/0~.-vxi5oc.r0.js +1 -0
  122. package/out/_next/static/chunks/0~3ik-hfp9s-7.js +1 -0
  123. package/out/_next/static/chunks/0~4f5p6tvn1lq.js +1 -0
  124. package/out/_next/static/chunks/0~_0ys.2whxbw.js +1 -0
  125. package/out/_next/static/chunks/0~_ui9l7.2sxf.js +1 -0
  126. package/out/_next/static/chunks/1037jlyw5~7ht.js +1 -0
  127. package/out/_next/static/chunks/1045hfzu533z0.js +1 -0
  128. package/out/_next/static/chunks/104e5nmc.c-pl.js +1 -0
  129. package/out/_next/static/chunks/109taw1pbh-0b.js +1 -0
  130. package/out/_next/static/chunks/10x7~onqwp338.js +1 -0
  131. package/out/_next/static/chunks/10ynz1dy483wf.js +1 -0
  132. package/out/_next/static/chunks/11hds.mg~4_r-.js +1 -0
  133. package/out/_next/static/chunks/11ibzaklcauw~.js +1 -0
  134. package/out/_next/static/chunks/11z.0s6.42b.p.js +1 -0
  135. package/out/_next/static/chunks/12-9n56l0y3yr.js +1 -0
  136. package/out/_next/static/chunks/126enaq~f7scl.js +1 -0
  137. package/out/_next/static/chunks/12nr19.nnn6s3.js +5 -0
  138. package/out/_next/static/chunks/{0qub_r0x_r-e9.css → 12pep-2t-qg4n.css} +1 -1
  139. package/out/_next/static/chunks/1380op_pfk.qo.js +1 -0
  140. package/out/_next/static/chunks/146oiw1bggtn4.js +1 -0
  141. package/out/_next/static/chunks/14_inksek_rth.js +2 -0
  142. package/out/_next/static/chunks/14_po2rb_arn4.js +1 -0
  143. package/out/_next/static/chunks/14a4fwbiq.l3z.js +1 -0
  144. package/out/_next/static/chunks/14cowsqn95m1k.js +1 -0
  145. package/out/_next/static/chunks/14dtd3l03v.kx.js +1 -0
  146. package/out/_next/static/chunks/14tm3qa-v9o-4.js +1 -0
  147. package/out/_next/static/chunks/15-o4kb-evqd7.js +1 -0
  148. package/out/_next/static/chunks/153-sz7s.qml2.js +1 -0
  149. package/out/_next/static/chunks/157z7bowux3xj.js +1 -0
  150. package/out/_next/static/chunks/15m1_677az2cm.js +1 -0
  151. package/out/_next/static/chunks/15v.~.ne6ogkk.js +1 -0
  152. package/out/_next/static/chunks/16i.qbk8t8gf_.js +1 -0
  153. package/out/_next/static/chunks/16u9f35gylw8l.js +1 -0
  154. package/out/_next/static/chunks/16xls5tt_68lx.js +1 -0
  155. package/out/_next/static/chunks/17ajyb5ogk5yj.js +1 -0
  156. package/out/_next/static/chunks/17dyfxbq8yz8n.js +1 -0
  157. package/out/_next/static/chunks/180zln9pcq9ih.js +1 -0
  158. package/out/_next/static/chunks/1814izi5gh.kp.js +1 -0
  159. package/out/_next/static/chunks/turbopack-0xta0kqwzkf28.js +1 -0
  160. package/out/_next/static/media/KaTeX_AMS-Regular.0b~8ki5y928w2.woff +0 -0
  161. package/out/_next/static/media/KaTeX_AMS-Regular.0p1vbqd84i2~o.woff2 +0 -0
  162. package/out/_next/static/media/KaTeX_AMS-Regular.173t6ktr7uf-w.ttf +0 -0
  163. package/out/_next/static/media/KaTeX_Caligraphic-Bold.01-pzluls4zgb.woff2 +0 -0
  164. package/out/_next/static/media/KaTeX_Caligraphic-Bold.0x2v1lwn~880f.woff +0 -0
  165. package/out/_next/static/media/KaTeX_Caligraphic-Bold.16zv5fax0h0ka.ttf +0 -0
  166. package/out/_next/static/media/KaTeX_Caligraphic-Regular.02i3z7wig438t.ttf +0 -0
  167. package/out/_next/static/media/KaTeX_Caligraphic-Regular.0rysu1t-ncjq8.woff2 +0 -0
  168. package/out/_next/static/media/KaTeX_Caligraphic-Regular.10927swgekwun.woff +0 -0
  169. package/out/_next/static/media/KaTeX_Fraktur-Bold.0e-16u10iuyyf.woff +0 -0
  170. package/out/_next/static/media/KaTeX_Fraktur-Bold.0et27v~3~4uhe.ttf +0 -0
  171. package/out/_next/static/media/KaTeX_Fraktur-Bold.0w23i72~hprpq.woff2 +0 -0
  172. package/out/_next/static/media/KaTeX_Fraktur-Regular.0b.riegzdfue2.woff +0 -0
  173. package/out/_next/static/media/KaTeX_Fraktur-Regular.0rekyoa-52fj_.woff2 +0 -0
  174. package/out/_next/static/media/KaTeX_Fraktur-Regular.0vjwa15znhk~4.ttf +0 -0
  175. package/out/_next/static/media/KaTeX_Main-Bold.09i7~607shf-h.ttf +0 -0
  176. package/out/_next/static/media/KaTeX_Main-Bold.09lmynrorhcbw.woff +0 -0
  177. package/out/_next/static/media/KaTeX_Main-Bold.16pfc63_du6mx.woff2 +0 -0
  178. package/out/_next/static/media/KaTeX_Main-BoldItalic.0cp37g7x1q8h6.woff +0 -0
  179. package/out/_next/static/media/KaTeX_Main-BoldItalic.0d54rk08rx11s.woff2 +0 -0
  180. package/out/_next/static/media/KaTeX_Main-BoldItalic.15j6k~hix2t_0.ttf +0 -0
  181. package/out/_next/static/media/KaTeX_Main-Italic.0382gqciexmbu.woff +0 -0
  182. package/out/_next/static/media/KaTeX_Main-Italic.06o5nq0_91v60.woff2 +0 -0
  183. package/out/_next/static/media/KaTeX_Main-Italic.0su4i6mm18-wo.ttf +0 -0
  184. package/out/_next/static/media/KaTeX_Main-Regular.08zh8z.7shijf.ttf +0 -0
  185. package/out/_next/static/media/KaTeX_Main-Regular.0diheg01zyoph.woff +0 -0
  186. package/out/_next/static/media/KaTeX_Main-Regular.0kaf-ag2_wkm-.woff2 +0 -0
  187. package/out/_next/static/media/KaTeX_Math-BoldItalic.0ajzxypnbx1h1.ttf +0 -0
  188. package/out/_next/static/media/KaTeX_Math-BoldItalic.0ck1myuerwyqw.woff +0 -0
  189. package/out/_next/static/media/KaTeX_Math-BoldItalic.0ja97dn.cpc87.woff2 +0 -0
  190. package/out/_next/static/media/KaTeX_Math-Italic.09xkhecjcn5r9.woff +0 -0
  191. package/out/_next/static/media/KaTeX_Math-Italic.0x23a-bmp-5tg.ttf +0 -0
  192. package/out/_next/static/media/KaTeX_Math-Italic.0zrha2c4sl2je.woff2 +0 -0
  193. package/out/_next/static/media/KaTeX_SansSerif-Bold.05a9.pc1j_zx9.woff2 +0 -0
  194. package/out/_next/static/media/KaTeX_SansSerif-Bold.0jcl-ayi1uun0.woff +0 -0
  195. package/out/_next/static/media/KaTeX_SansSerif-Bold.0re8y.dm7.mt5.ttf +0 -0
  196. package/out/_next/static/media/KaTeX_SansSerif-Italic.0a0234dc3s62j.woff2 +0 -0
  197. package/out/_next/static/media/KaTeX_SansSerif-Italic.0judofdln9731.woff +0 -0
  198. package/out/_next/static/media/KaTeX_SansSerif-Italic.10z1iap9pfus8.ttf +0 -0
  199. package/out/_next/static/media/KaTeX_SansSerif-Regular.0h9yjlugq4q_e.woff +0 -0
  200. package/out/_next/static/media/KaTeX_SansSerif-Regular.0v6gcj32-czft.woff2 +0 -0
  201. package/out/_next/static/media/KaTeX_SansSerif-Regular.0zm18kga42ebc.ttf +0 -0
  202. package/out/_next/static/media/KaTeX_Script-Regular.0c4.h-mer83d_.woff2 +0 -0
  203. package/out/_next/static/media/KaTeX_Script-Regular.0q14y6zkzlpob.ttf +0 -0
  204. package/out/_next/static/media/KaTeX_Script-Regular.0ze6v4r_-99oy.woff +0 -0
  205. package/out/_next/static/media/KaTeX_Size1-Regular.013x6a4ierotp.woff2 +0 -0
  206. package/out/_next/static/media/KaTeX_Size1-Regular.0kidw0oi.m68o.woff +0 -0
  207. package/out/_next/static/media/KaTeX_Size1-Regular.0m6y-i6wfokni.ttf +0 -0
  208. package/out/_next/static/media/KaTeX_Size2-Regular.0blpmluwilgbg.woff +0 -0
  209. package/out/_next/static/media/KaTeX_Size2-Regular.0d5inmyp-tyv3.woff2 +0 -0
  210. package/out/_next/static/media/KaTeX_Size2-Regular.0wnhnvj-.k9d5.ttf +0 -0
  211. package/out/_next/static/media/KaTeX_Size3-Regular.01h0xm_sfctj3.woff +0 -0
  212. package/out/_next/static/media/KaTeX_Size3-Regular.0iukctyhw5j56.woff2 +0 -0
  213. package/out/_next/static/media/KaTeX_Size3-Regular.0jl8mqyf4gzpn.ttf +0 -0
  214. package/out/_next/static/media/KaTeX_Size4-Regular.0w3.rb_c4stzk.woff2 +0 -0
  215. package/out/_next/static/media/KaTeX_Size4-Regular.0wr_9l81-mu06.ttf +0 -0
  216. package/out/_next/static/media/KaTeX_Size4-Regular.12tvaesf3.zl3.woff +0 -0
  217. package/out/_next/static/media/KaTeX_Typewriter-Regular.0c4zdxz~8frhm.woff2 +0 -0
  218. package/out/_next/static/media/KaTeX_Typewriter-Regular.0cgrzn5l3kao5.woff +0 -0
  219. package/out/_next/static/media/KaTeX_Typewriter-Regular.128~qc3858otl.ttf +0 -0
  220. package/out/_not-found/__next._full.txt +21 -19
  221. package/out/_not-found/__next._head.txt +3 -3
  222. package/out/_not-found/__next._index.txt +8 -6
  223. package/out/_not-found/__next._not-found.__PAGE__.txt +4 -4
  224. package/out/_not-found/__next._not-found.txt +3 -3
  225. package/out/_not-found/__next._tree.txt +3 -2
  226. package/out/_not-found/index.html +2 -2
  227. package/out/_not-found/index.txt +21 -19
  228. package/out/admin/__next._full.txt +23 -0
  229. package/out/admin/__next._head.txt +5 -0
  230. package/out/admin/__next._index.txt +9 -0
  231. package/out/admin/__next._tree.txt +5 -0
  232. package/out/admin/__next.admin.__PAGE__.txt +9 -0
  233. package/out/admin/__next.admin.txt +5 -0
  234. package/out/admin/index.html +15 -0
  235. package/out/admin/index.txt +23 -0
  236. package/out/app/__next._full.txt +15 -13
  237. package/out/app/__next._head.txt +3 -3
  238. package/out/app/__next._index.txt +8 -6
  239. package/out/app/__next._tree.txt +4 -2
  240. package/out/app/__next.app.__PAGE__.txt +4 -4
  241. package/out/app/__next.app.txt +3 -4
  242. package/out/app/index.html +2 -2
  243. package/out/app/index.txt +15 -13
  244. package/out/chat/__next._full.txt +16 -14
  245. package/out/chat/__next._head.txt +3 -3
  246. package/out/chat/__next._index.txt +8 -6
  247. package/out/chat/__next._tree.txt +5 -3
  248. package/out/chat/__next.chat.__PAGE__.txt +4 -4
  249. package/out/chat/__next.chat.txt +4 -5
  250. package/out/chat/index.html +2 -2
  251. package/out/chat/index.txt +16 -14
  252. package/out/chat/join/__next._full.txt +25 -0
  253. package/out/chat/join/__next._head.txt +5 -0
  254. package/out/chat/join/__next._index.txt +9 -0
  255. package/out/chat/join/__next._tree.txt +5 -0
  256. package/out/chat/join/__next.chat.join.__PAGE__.txt +9 -0
  257. package/out/chat/join/__next.chat.join.txt +5 -0
  258. package/out/chat/join/__next.chat.txt +5 -0
  259. package/out/chat/join/index.html +15 -0
  260. package/out/chat/join/index.txt +25 -0
  261. package/out/download/__next._full.txt +34 -33
  262. package/out/download/__next._head.txt +3 -3
  263. package/out/download/__next._index.txt +8 -6
  264. package/out/download/__next._tree.txt +6 -4
  265. package/out/download/__next.download.__PAGE__.txt +13 -14
  266. package/out/download/__next.download.txt +3 -3
  267. package/out/download/index.html +2 -2
  268. package/out/download/index.txt +34 -33
  269. package/out/index.html +2 -2
  270. package/out/index.txt +23 -20
  271. package/out/note/__next._full.txt +24 -0
  272. package/out/{changelog → note}/__next._head.txt +3 -3
  273. package/out/note/__next._index.txt +9 -0
  274. package/out/note/__next._tree.txt +4 -0
  275. package/out/note/__next.note.__PAGE__.txt +9 -0
  276. package/out/note/__next.note.txt +5 -0
  277. package/out/note/index.html +15 -0
  278. package/out/note/index.txt +24 -0
  279. package/out/ping/__next._full.txt +23 -20
  280. package/out/ping/__next._head.txt +3 -3
  281. package/out/ping/__next._index.txt +8 -6
  282. package/out/ping/__next._tree.txt +6 -4
  283. package/out/ping/__next.ping.__PAGE__.txt +5 -5
  284. package/out/ping/__next.ping.txt +4 -4
  285. package/out/ping/index.html +2 -2
  286. package/out/ping/index.txt +23 -20
  287. package/out/web3/__next._full.txt +16 -14
  288. package/out/web3/__next._head.txt +3 -3
  289. package/out/web3/__next._index.txt +8 -6
  290. package/out/web3/__next._tree.txt +5 -3
  291. package/out/web3/__next.web3.__PAGE__.txt +4 -4
  292. package/out/web3/__next.web3.txt +4 -5
  293. package/out/web3/ed25519/__next._full.txt +14 -12
  294. package/out/web3/ed25519/__next._head.txt +3 -3
  295. package/out/web3/ed25519/__next._index.txt +8 -6
  296. package/out/web3/ed25519/__next._tree.txt +5 -3
  297. package/out/web3/ed25519/__next.web3.ed25519.__PAGE__.txt +2 -2
  298. package/out/web3/ed25519/__next.web3.ed25519.txt +3 -3
  299. package/out/web3/ed25519/__next.web3.txt +4 -5
  300. package/out/web3/ed25519/index.html +1 -1
  301. package/out/web3/ed25519/index.txt +14 -12
  302. package/out/web3/index.html +2 -2
  303. package/out/web3/index.txt +16 -14
  304. package/out/web3/tools/__next._full.txt +14 -12
  305. package/out/web3/tools/__next._head.txt +3 -3
  306. package/out/web3/tools/__next._index.txt +8 -6
  307. package/out/web3/tools/__next._tree.txt +5 -3
  308. package/out/web3/tools/__next.web3.tools.__PAGE__.txt +2 -2
  309. package/out/web3/tools/__next.web3.tools.txt +3 -3
  310. package/out/web3/tools/__next.web3.txt +4 -5
  311. package/out/web3/tools/index.html +1 -1
  312. package/out/web3/tools/index.txt +14 -12
  313. package/package.json +30 -13
  314. package/server/index.js +188 -901
  315. package/server/src/config.js +5 -1
  316. package/server/src/core/channelAttachment.js +68 -0
  317. package/server/src/core/cid.js +6 -71
  318. package/server/src/core/cidTopic.js +29 -0
  319. package/server/src/core/mostLink.js +88 -0
  320. package/server/src/http/access.js +123 -0
  321. package/server/src/http/app.js +1095 -0
  322. package/server/src/http/errors.js +35 -0
  323. package/server/src/http/nodeLogs.js +53 -0
  324. package/server/src/http/nodeStatus.js +146 -0
  325. package/server/src/http/staticFiles.js +84 -0
  326. package/server/src/http/uploads.js +114 -0
  327. package/server/src/index.js +1539 -301
  328. package/server/src/node/config.js +191 -0
  329. package/server/src/node/logs.js +94 -0
  330. package/server/src/utils/api.js +359 -8
  331. package/server/src/utils/auth.js +63 -0
  332. package/server/src/utils/dateTime.js +30 -0
  333. package/server/src/utils/downloadMessages.js +89 -0
  334. package/server/src/utils/errors.js +14 -0
  335. package/server/src/utils/mostWallet.js +185 -1
  336. package/server/src/utils/mp.js +2 -26
  337. package/server/src/utils/noteBackup.js +116 -0
  338. package/server/src/utils/noteUtils.js +128 -0
  339. package/server/src/utils/userIdentity.js +8 -61
  340. package/out/_next/static/chunks/003jnm.v5tzw5.js +0 -1
  341. package/out/_next/static/chunks/00re8v.gbcywn.js +0 -1
  342. package/out/_next/static/chunks/00s106sbq8t9v.js +0 -1
  343. package/out/_next/static/chunks/012hi627qrdnn.js +0 -1
  344. package/out/_next/static/chunks/0174xh3wfsjm1.js +0 -2
  345. package/out/_next/static/chunks/02~o2nmo5pmy1.js +0 -1
  346. package/out/_next/static/chunks/07t.dhhokszz5.css +0 -1
  347. package/out/_next/static/chunks/0_wia9ofmsi1c.css +0 -2
  348. package/out/_next/static/chunks/0ah8fihozo2_u.js +0 -5
  349. package/out/_next/static/chunks/0bzupvr5gt3k9.js +0 -31
  350. package/out/_next/static/chunks/0e_h0d3ekzks8.css +0 -1
  351. package/out/_next/static/chunks/0gdluj423gso1.js +0 -1
  352. package/out/_next/static/chunks/0gmoiq06srjay.css +0 -1
  353. package/out/_next/static/chunks/0imkasy7kb67u.js +0 -1
  354. package/out/_next/static/chunks/0jjc_b9q_ldi2.js +0 -1
  355. package/out/_next/static/chunks/0jl~j62iz2uvr.js +0 -1
  356. package/out/_next/static/chunks/0lqslm813wk_h.js +0 -1
  357. package/out/_next/static/chunks/0q782fxxd0lx~.js +0 -1
  358. package/out/_next/static/chunks/0slwj0c46k5cu.js +0 -1
  359. package/out/_next/static/chunks/0sorqk.oc6b7j.css +0 -1
  360. package/out/_next/static/chunks/0tapzqc6hgvx-.js +0 -1
  361. package/out/_next/static/chunks/0xsc7z5x8n7wg.js +0 -1
  362. package/out/_next/static/chunks/0zm~gys2jwl0g.js +0 -1
  363. package/out/_next/static/chunks/turbopack-0a_g3u0ud~jb8.js +0 -1
  364. package/out/changelog/__next._full.txt +0 -22
  365. package/out/changelog/__next._index.txt +0 -7
  366. package/out/changelog/__next._tree.txt +0 -3
  367. package/out/changelog/__next.changelog.__PAGE__.txt +0 -10
  368. package/out/changelog/__next.changelog.txt +0 -5
  369. package/out/changelog/index.html +0 -15
  370. package/out/changelog/index.txt +0 -22
  371. package/out/docs/__next._full.txt +0 -22
  372. package/out/docs/__next._head.txt +0 -5
  373. package/out/docs/__next._index.txt +0 -7
  374. package/out/docs/__next._tree.txt +0 -3
  375. package/out/docs/__next.docs.__PAGE__.txt +0 -10
  376. package/out/docs/__next.docs.txt +0 -5
  377. package/out/docs/getting-started/__next._full.txt +0 -22
  378. package/out/docs/getting-started/__next._head.txt +0 -5
  379. package/out/docs/getting-started/__next._index.txt +0 -7
  380. package/out/docs/getting-started/__next._tree.txt +0 -3
  381. package/out/docs/getting-started/__next.docs.getting-started.__PAGE__.txt +0 -10
  382. package/out/docs/getting-started/__next.docs.getting-started.txt +0 -5
  383. package/out/docs/getting-started/__next.docs.txt +0 -5
  384. package/out/docs/getting-started/index.html +0 -15
  385. package/out/docs/getting-started/index.txt +0 -22
  386. package/out/docs/index.html +0 -15
  387. package/out/docs/index.txt +0 -22
  388. /package/out/_next/static/{iOB2EBwOGZ0iYW7Lbg9u_ → t7ZIeQpVvjz4a7-5Tt-VK}/_buildManifest.js +0 -0
  389. /package/out/_next/static/{iOB2EBwOGZ0iYW7Lbg9u_ → t7ZIeQpVvjz4a7-5Tt-VK}/_clientMiddlewareManifest.js +0 -0
  390. /package/out/_next/static/{iOB2EBwOGZ0iYW7Lbg9u_ → t7ZIeQpVvjz4a7-5Tt-VK}/_ssgManifest.js +0 -0
@@ -0,0 +1,1095 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { Hono } from 'hono'
4
+ import { cors } from 'hono/cors'
5
+ import { parseMostLink, validateCidString } from '../core/cid.js'
6
+ import { sanitizeFilename } from '../utils/security.js'
7
+ import { normalizeAddress, verifyAuthHeader } from '../utils/auth.js'
8
+ import {
9
+ DEFAULT_NODE_HOST,
10
+ DEFAULT_NODE_PORT,
11
+ createNodeConfigStore,
12
+ evaluateStorageLimits,
13
+ normalizeRemoteInvites,
14
+ } from '../node/config.js'
15
+ import { createNodeLogger } from '../node/logs.js'
16
+ import {
17
+ getAllowedOrigins,
18
+ getInvalidInviteResponse,
19
+ getRequestPath,
20
+ hasValidInvite,
21
+ isLocalRequest,
22
+ isLocalUpgradeRequest,
23
+ isLoopbackRemoteAddress,
24
+ isPublicListenHost,
25
+ isRemoteAccessRequest,
26
+ remoteInviteConfigured,
27
+ } from './access.js'
28
+ import { badRequestOrAppError, errorJson } from './errors.js'
29
+ import { listFilteredNodeLogs } from './nodeLogs.js'
30
+ import {
31
+ buildNodeStatus,
32
+ buildOpenApiSpec,
33
+ getNetworkAddresses,
34
+ getPackageVersion,
35
+ } from './nodeStatus.js'
36
+ import { parseMultipartBusboy } from './uploads.js'
37
+ import { getMimeType, registerStaticRoutes } from './staticFiles.js'
38
+
39
+ const RATE_LIMIT_WINDOW = 60 * 1000
40
+ const RATE_LIMIT_MAX_REQUESTS = 120
41
+ export { UPLOAD_TMP_DIR } from './uploads.js'
42
+
43
+ // --- 配置 ---
44
+ const defaultConfigStore = createNodeConfigStore()
45
+ const CONFIG_DIR = defaultConfigStore.configDir
46
+ const PORT = DEFAULT_NODE_PORT
47
+ const HOST = DEFAULT_NODE_HOST
48
+
49
+
50
+
51
+ export function getDataPath(configStore = defaultConfigStore) {
52
+ return configStore.getDataPath()
53
+ }
54
+
55
+ function resolveDataPathForSave(inputPath) {
56
+ let dataPath = String(inputPath || '').trim()
57
+ let basePath = dataPath
58
+
59
+ if (!dataPath) {
60
+ return { dataPath: '' }
61
+ }
62
+
63
+ if (dataPath.match(/^[A-Za-z]:\\$/)) {
64
+ basePath = dataPath
65
+ dataPath = path.join(dataPath, 'most-data')
66
+ }
67
+
68
+ if (!fs.existsSync(basePath)) {
69
+ return { error: '目录不存在' }
70
+ }
71
+
72
+ if (!fs.existsSync(dataPath)) {
73
+ fs.mkdirSync(dataPath, { recursive: true })
74
+ }
75
+
76
+ return { dataPath }
77
+ }
78
+
79
+
80
+
81
+ // --- Hono 应用工厂 ---
82
+ export function createApp(engine, options = {}) {
83
+ const appPort = options.port || PORT
84
+ const appHost = options.host || HOST
85
+ const configStore = options.configStore || defaultConfigStore
86
+ const nodeLogger =
87
+ options.nodeLogger || createNodeLogger(configStore.configDir || CONFIG_DIR)
88
+ const wssRef = options.wssRef || { current: null }
89
+ const serverInstanceRef = options.serverInstanceRef || { current: null }
90
+ function getRemoteInviteSet() {
91
+ const invites =
92
+ options.remoteInvites === undefined
93
+ ? configStore.getNodeConfig().remoteInvites
94
+ : normalizeRemoteInvites(options.remoteInvites)
95
+ return new Set(invites)
96
+ }
97
+
98
+ // 速率限制(每个 app 实例独立)
99
+ const rateLimitMap = new Map()
100
+ function checkRateLimit(clientIp) {
101
+ const now = Date.now()
102
+ if (!rateLimitMap.has(clientIp)) {
103
+ rateLimitMap.set(clientIp, [])
104
+ }
105
+ const requests = rateLimitMap.get(clientIp)
106
+ while (requests.length > 0 && requests[0] < now - RATE_LIMIT_WINDOW) {
107
+ requests.shift()
108
+ }
109
+ if (requests.length === 0) {
110
+ rateLimitMap.delete(clientIp)
111
+ }
112
+ if (requests.length >= RATE_LIMIT_MAX_REQUESTS) {
113
+ return false
114
+ }
115
+ requests.push(now)
116
+ return true
117
+ }
118
+
119
+ function rateLimitMiddleware() {
120
+ return async (c, next) => {
121
+ const clientIp =
122
+ c.req.header('x-forwarded-for') ||
123
+ c.env?.incoming?.socket?.remoteAddress ||
124
+ 'unknown'
125
+ if (!checkRateLimit(clientIp)) {
126
+ return c.json({ error: 'Too many requests' }, 429)
127
+ }
128
+ await next()
129
+ }
130
+ }
131
+
132
+ function isValidInvite(c) {
133
+ const invite = String(c.req.header('x-mostbox-invite') || '').trim()
134
+ return hasValidInvite(getRemoteInviteSet(), invite)
135
+ }
136
+
137
+ function isRemoteRequest(c) {
138
+ return isRemoteAccessRequest({
139
+ invite: c.req.header('x-mostbox-invite'),
140
+ origin: c.req.header('origin'),
141
+ listenHost: appHost,
142
+ local: isLocalRequest(c),
143
+ })
144
+ }
145
+
146
+ function isPublicFileDownloadPath(path) {
147
+ return /^\/api\/files\/[^/]+\/download$/.test(path)
148
+ }
149
+
150
+ function requiresUserAuth(path) {
151
+ if (isPublicFileDownloadPath(path)) {
152
+ return false
153
+ }
154
+
155
+ return (
156
+ path === '/api/files' ||
157
+ path === '/api/publish' ||
158
+ path === '/api/download/check' ||
159
+ path === '/api/download' ||
160
+ path === '/api/download/cancel' ||
161
+ path === '/api/trash' ||
162
+ path === '/api/move' ||
163
+ path === '/api/folder/rename' ||
164
+ path.startsWith('/api/files/') ||
165
+ path.startsWith('/api/trash/') ||
166
+ path.startsWith('/api/channels')
167
+ )
168
+ }
169
+
170
+ function isAdminApi(path) {
171
+ return (
172
+ path.startsWith('/api/admin/') ||
173
+ path === '/api/node/config' ||
174
+ path === '/api/node/policy' ||
175
+ path === '/api/node/logs' ||
176
+ path === '/api/shutdown'
177
+ )
178
+ }
179
+
180
+ function authMiddleware() {
181
+ return async (c, next) => {
182
+ const path = getRequestPath(c)
183
+
184
+ if (isRemoteRequest(c) && !isValidInvite(c)) {
185
+ return getInvalidInviteResponse(c)
186
+ }
187
+
188
+ if (isRemoteRequest(c) && isAdminApi(path)) {
189
+ return c.json(
190
+ {
191
+ error: 'Remote users cannot access node administration',
192
+ code: 'REMOTE_ADMIN_FORBIDDEN',
193
+ },
194
+ 403
195
+ )
196
+ }
197
+
198
+ const authHeader = c.req.header('authorization')
199
+ if (authHeader) {
200
+ if (isPublicFileDownloadPath(path)) {
201
+ await next()
202
+ return
203
+ }
204
+
205
+ const auth = verifyAuthHeader(authHeader, c.req.method, path)
206
+ if (!auth.ok) {
207
+ return c.json({ error: auth.error, code: 'UNAUTHORIZED' }, 401)
208
+ }
209
+ c.set('userAddress', auth.address)
210
+ }
211
+
212
+ if (requiresUserAuth(path) && !c.get('userAddress')) {
213
+ return c.json({ error: 'Login required', code: 'LOGIN_REQUIRED' }, 401)
214
+ }
215
+
216
+ await next()
217
+ }
218
+ }
219
+
220
+ // WebSocket 广播
221
+ const channelSubscriptions = new Map()
222
+
223
+ function wsBroadcast(event, data) {
224
+ const payload = JSON.stringify({ event, data })
225
+ const wss = wssRef.current
226
+ if (wss) {
227
+ wss.clients.forEach(client => {
228
+ if (client.readyState === 1) {
229
+ try {
230
+ client.send(payload)
231
+ } catch (err) {
232
+ console.warn('[WS] Failed to send to client:', err.message)
233
+ }
234
+ }
235
+ })
236
+ }
237
+ }
238
+
239
+ async function broadcastNodeStatus() {
240
+ try {
241
+ const status = await buildNodeStatus(engine, configStore, appPort)
242
+ wsBroadcast('node:status', status)
243
+ return status
244
+ } catch (err) {
245
+ const entry = nodeLogger.append({
246
+ level: 'error',
247
+ event: 'node:status:error',
248
+ message: err.message,
249
+ })
250
+ wsBroadcast('node:log', entry)
251
+ return null
252
+ }
253
+ }
254
+
255
+ function appendNodeLog(input) {
256
+ const entry = nodeLogger.append(input)
257
+ wsBroadcast('node:log', entry)
258
+ return entry
259
+ }
260
+
261
+ function wsSendToChannel(channelName, event, data) {
262
+ const payload = JSON.stringify({ event, data })
263
+ const subscribers = channelSubscriptions.get(channelName)
264
+ if (subscribers) {
265
+ subscribers.forEach(ws => {
266
+ if (ws.readyState === 1) {
267
+ try {
268
+ ws.send(payload)
269
+ } catch (err) {
270
+ console.warn(
271
+ '[WS] Failed to send to channel subscriber:',
272
+ err.message
273
+ )
274
+ }
275
+ }
276
+ })
277
+ }
278
+ }
279
+
280
+ function subscribeToChannel(ws, channelName) {
281
+ if (!channelSubscriptions.has(channelName)) {
282
+ channelSubscriptions.set(channelName, new Set())
283
+ }
284
+ channelSubscriptions.get(channelName).add(ws)
285
+ }
286
+
287
+ function unsubscribeFromChannel(ws, channelName) {
288
+ const subscribers = channelSubscriptions.get(channelName)
289
+ if (subscribers) {
290
+ subscribers.delete(ws)
291
+ if (subscribers.size === 0) {
292
+ channelSubscriptions.delete(channelName)
293
+ }
294
+ }
295
+ }
296
+
297
+ function cleanupWsSubscriptions(ws) {
298
+ for (const [channel, subscribers] of channelSubscriptions) {
299
+ subscribers.delete(ws)
300
+ if (subscribers.size === 0) {
301
+ channelSubscriptions.delete(channel)
302
+ }
303
+ }
304
+ }
305
+
306
+ function validateWebSocketRequest(req) {
307
+ const url = new URL(req.url, `http://localhost:${appPort}`)
308
+ const invite = String(url.searchParams.get('invite') || '').trim()
309
+ const remote = isRemoteAccessRequest({
310
+ invite,
311
+ origin: req.headers.origin,
312
+ listenHost: appHost,
313
+ local: isLocalUpgradeRequest(req),
314
+ })
315
+ if (!remote) return true
316
+
317
+ const wsInviteSet = new Set(configStore.getNodeConfig().remoteInvites)
318
+ if (!hasValidInvite(wsInviteSet, invite)) {
319
+ return false
320
+ }
321
+
322
+ const address = url.searchParams.get('address') || ''
323
+ const timestamp = url.searchParams.get('timestamp') || ''
324
+ const signature = url.searchParams.get('signature') || ''
325
+ const auth = verifyAuthHeader(
326
+ `${address},${timestamp},${signature}`,
327
+ 'GET',
328
+ '/ws'
329
+ )
330
+ return auth.ok
331
+ }
332
+
333
+ // 将广播函数挂载到 engine 上供外部测试使用
334
+ engine.wsBroadcast = wsBroadcast
335
+ engine.wsSendToChannel = wsSendToChannel
336
+
337
+ const app = new Hono()
338
+
339
+ // CORS 中间件
340
+ app.use(
341
+ '/api/*',
342
+ cors({
343
+ origin: getAllowedOrigins(appPort),
344
+ credentials: true,
345
+ })
346
+ )
347
+
348
+ // 速率限制中间件
349
+ app.use('/api/*', rateLimitMiddleware())
350
+ app.use('/api/*', authMiddleware())
351
+
352
+ // 全局错误处理
353
+ app.onError((err, c) => {
354
+ console.error('[API Error]', err)
355
+ try {
356
+ const errorLogDir = configStore.configDir || CONFIG_DIR
357
+ const errorLogPath = path.join(errorLogDir, 'server-error.log')
358
+ if (!fs.existsSync(errorLogDir)) {
359
+ fs.mkdirSync(errorLogDir, { recursive: true })
360
+ }
361
+ fs.appendFileSync(
362
+ errorLogPath,
363
+ `[${new Date().toISOString()}] ${err.stack}\n`
364
+ )
365
+ } catch {}
366
+ return c.json({ error: err.message, code: err.code }, 500)
367
+ })
368
+
369
+ // --- 配置路由 ---
370
+ app.get('/api/node-id', c => {
371
+ return c.json({ id: engine.getNodeId() })
372
+ })
373
+
374
+ app.get('/api/remote/capabilities', c => {
375
+ const remoteInviteSet = getRemoteInviteSet()
376
+ return c.json({
377
+ remoteAccess:
378
+ isPublicListenHost(appHost) && remoteInviteConfigured(remoteInviteSet),
379
+ inviteRequired: true,
380
+ inviteConfigured: remoteInviteConfigured(remoteInviteSet),
381
+ authenticated: Boolean(c.get('userAddress')),
382
+ userAddress: c.get('userAddress') || null,
383
+ adminAvailable: !isRemoteRequest(c),
384
+ listenHost: appHost,
385
+ })
386
+ })
387
+
388
+ app.get('/api/config', c => {
389
+ const config = configStore.loadRawConfig()
390
+ return c.json({ dataPath: config.dataPath || '' })
391
+ })
392
+
393
+ app.post('/api/config', async c => {
394
+ const body = await c.req.json()
395
+ const patch = {}
396
+
397
+ if (body.resetStorage) {
398
+ patch.dataPath = ''
399
+ } else if (body.dataPath !== undefined) {
400
+ const resolved = resolveDataPathForSave(body.dataPath)
401
+ if (resolved.error) return c.json({ error: resolved.error }, 400)
402
+ patch.dataPath = resolved.dataPath
403
+ }
404
+
405
+ const { success } = configStore.saveNodeConfigPatch(patch)
406
+ appendNodeLog({
407
+ event: 'node:config:updated',
408
+ message: 'Node config updated',
409
+ data: { dataPath: getDataPath(configStore) },
410
+ })
411
+ await broadcastNodeStatus()
412
+ return c.json({ success, dataPath: getDataPath(configStore) })
413
+ })
414
+
415
+ app.get('/api/config/data-path', c => {
416
+ const config = configStore.getNodeConfig()
417
+ const isDefault = !config.dataPath
418
+ const dataPath = getDataPath(configStore)
419
+ return c.json({ dataPath, isDefault })
420
+ })
421
+
422
+ app.get('/api/node/status', async c => {
423
+ try {
424
+ return c.json(await buildNodeStatus(engine, configStore, appPort))
425
+ } catch (err) {
426
+ return errorJson(c, err)
427
+ }
428
+ })
429
+
430
+ app.get('/api/node/config', c => {
431
+ const config = configStore.getNodeConfig()
432
+ return c.json({
433
+ ...config,
434
+ dataPath: getDataPath(configStore),
435
+ configuredDataPath: config.dataPath,
436
+ isDefaultDataPath: !config.dataPath,
437
+ currentHost: appHost,
438
+ currentPort: appPort,
439
+ remoteInvites: config.remoteInvites,
440
+ })
441
+ })
442
+
443
+ app.post('/api/node/config', async c => {
444
+ const body = await c.req.json()
445
+ const patch = { ...body }
446
+
447
+ if (body.resetStorage) {
448
+ patch.dataPath = ''
449
+ } else if (body.dataPath !== undefined) {
450
+ const resolved = resolveDataPathForSave(body.dataPath)
451
+ if (resolved.error) return c.json({ error: resolved.error }, 400)
452
+ patch.dataPath = resolved.dataPath
453
+ }
454
+
455
+ const { success, config } = configStore.saveNodeConfigPatch(patch)
456
+ engine.setMaxFileSize(config.maxFileSizeBytes)
457
+ appendNodeLog({
458
+ event: 'node:config:updated',
459
+ message: 'Node daemon config updated',
460
+ data: {
461
+ dataPath: getDataPath(configStore),
462
+ port: config.port,
463
+ capacityBytes: config.capacityBytes,
464
+ remoteInviteCount: config.remoteInvites.length,
465
+ },
466
+ })
467
+ await broadcastNodeStatus()
468
+ return c.json({ success, ...config, dataPath: getDataPath(configStore) })
469
+ })
470
+
471
+ app.get('/api/node/policy', c => {
472
+ const config = configStore.getNodeConfig()
473
+ return c.json({
474
+ maxFileSizeBytes: config.maxFileSizeBytes,
475
+ })
476
+ })
477
+
478
+ app.post('/api/node/policy', async c => {
479
+ const body = await c.req.json()
480
+ const { success, config } = configStore.saveNodeConfigPatch({
481
+ maxFileSizeBytes: body.maxFileSizeBytes,
482
+ })
483
+ engine.setMaxFileSize(config.maxFileSizeBytes)
484
+ const policy = {
485
+ maxFileSizeBytes: config.maxFileSizeBytes,
486
+ }
487
+ appendNodeLog({
488
+ event: 'node:policy:updated',
489
+ message: 'Node storage limits updated',
490
+ data: policy,
491
+ })
492
+ await broadcastNodeStatus()
493
+ return c.json({ success, ...policy })
494
+ })
495
+
496
+ app.post('/api/node/policy/evaluate', async c => {
497
+ const body = await c.req.json()
498
+ const decision = evaluateStorageLimits(configStore.getNodeConfig(), body)
499
+ return c.json(decision)
500
+ })
501
+
502
+ app.get('/api/node/logs', c => {
503
+ const limit = Number(c.req.query('limit') || 100)
504
+ const filter = c.req.query('filter') || 'all'
505
+ const query = c.req.query('q') || ''
506
+ const result = listFilteredNodeLogs(nodeLogger, { limit, filter, query })
507
+ return c.json({
508
+ logFile: nodeLogger.logFile,
509
+ filter: result.filter,
510
+ query: result.query,
511
+ logs: result.logs,
512
+ })
513
+ })
514
+
515
+ app.delete('/api/node/logs', c => {
516
+ const success = nodeLogger.clear()
517
+ const clearedAt = new Date().toISOString()
518
+ wsBroadcast('node:logs:cleared', { clearedAt })
519
+ return c.json({ success, clearedAt })
520
+ })
521
+
522
+ app.get('/api/node/diagnostics', async c => {
523
+ try {
524
+ const status = await buildNodeStatus(engine, configStore, appPort)
525
+ return c.json({
526
+ generatedAt: new Date().toISOString(),
527
+ packageVersion: getPackageVersion(),
528
+ platform: process.platform,
529
+ nodeVersion: process.version,
530
+ status,
531
+ logFile: nodeLogger.logFile,
532
+ logs: nodeLogger.list(200),
533
+ })
534
+ } catch (err) {
535
+ return errorJson(c, err)
536
+ }
537
+ })
538
+
539
+ app.get('/api/admin/users', c => {
540
+ return c.json({ users: engine.listUsers() })
541
+ })
542
+
543
+ app.delete('/api/admin/users/:address/data', async c => {
544
+ const address = normalizeAddress(c.req.param('address'))
545
+ if (!address) {
546
+ return c.json({ error: 'valid address is required' }, 400)
547
+ }
548
+ try {
549
+ const result = await engine.clearUserData(address)
550
+ appendNodeLog({
551
+ event: 'node:user-data:cleared',
552
+ message: 'User data cleared',
553
+ data: result,
554
+ })
555
+ await broadcastNodeStatus()
556
+ return c.json({ success: true, ...result })
557
+ } catch (err) {
558
+ return errorJson(c, err)
559
+ }
560
+ })
561
+
562
+ app.get('/api/openapi.json', c => {
563
+ return c.json(buildOpenApiSpec(appPort))
564
+ })
565
+
566
+ // --- 网络路由 ---
567
+ app.get('/api/network-status', c => {
568
+ return c.json(engine.getNetworkStatus())
569
+ })
570
+
571
+ app.get('/api/network', c => {
572
+ return c.json(getNetworkAddresses(appPort))
573
+ })
574
+
575
+ // --- 节点保种路由 ---
576
+ app.get('/api/node/holdings', c => {
577
+ try {
578
+ return c.json(engine.listHoldings())
579
+ } catch (err) {
580
+ return errorJson(c, err)
581
+ }
582
+ })
583
+
584
+ app.post('/api/node/holdings', async c => {
585
+ try {
586
+ const body = await c.req.json()
587
+ const holding = await engine.addHolding(body)
588
+ appendNodeLog({
589
+ event: 'node:holding:added',
590
+ message: 'Node holding added',
591
+ data: { cid: holding.cid, size: holding.size },
592
+ })
593
+ await broadcastNodeStatus()
594
+ return c.json({ success: true, holding })
595
+ } catch (err) {
596
+ return errorJson(c, err)
597
+ }
598
+ })
599
+
600
+ app.post('/api/p2p/pull', async c => {
601
+ try {
602
+ const body = await c.req.json()
603
+ const timeout =
604
+ body.timeout === undefined ? undefined : Number(body.timeout)
605
+ const result = await engine.pullByCid({
606
+ ...body,
607
+ timeout: Number.isFinite(timeout) && timeout > 0 ? timeout : undefined,
608
+ })
609
+ appendNodeLog({
610
+ event: 'node:pull:success',
611
+ message: 'P2P pull completed',
612
+ data: { cid: result.cid, taskId: result.taskId },
613
+ })
614
+ await broadcastNodeStatus()
615
+ return c.json({ success: true, ...result })
616
+ } catch (err) {
617
+ appendNodeLog({
618
+ level: 'error',
619
+ event: 'node:pull:error',
620
+ message: err.message,
621
+ data: { code: err.code || 'UNKNOWN' },
622
+ })
623
+ return errorJson(c, err)
624
+ }
625
+ })
626
+
627
+ // --- 文件路由 ---
628
+ app.get('/api/files', c => {
629
+ return c.json(
630
+ engine.listPublishedFiles({ ownerAddress: c.get('userAddress') })
631
+ )
632
+ })
633
+
634
+ app.post('/api/publish', async c => {
635
+ const req = c.env.incoming
636
+ const result = await parseMultipartBusboy(
637
+ req,
638
+ configStore.getNodeConfig().maxFileSizeBytes
639
+ )
640
+
641
+ if (!result || !result.filename) {
642
+ return c.json({ error: 'No file provided' }, 400)
643
+ }
644
+
645
+ try {
646
+ const publishResult = await engine.publishFile(
647
+ result.filePath,
648
+ result.filename,
649
+ { localPath: null, ownerAddress: c.get('userAddress') }
650
+ )
651
+ return c.json({ success: true, ...publishResult })
652
+ } finally {
653
+ fs.unlink(result.filePath, () => {})
654
+ }
655
+ })
656
+
657
+ app.post('/api/download/check', async c => {
658
+ const body = await c.req.json()
659
+ if (!body.link) {
660
+ return c.json({ error: 'link is required' }, 400)
661
+ }
662
+
663
+ const parsed = parseMostLink(body.link)
664
+ if (parsed.error) {
665
+ return c.json({ error: parsed.error }, 400)
666
+ }
667
+
668
+ const existingFile = engine
669
+ .getPublishedFiles({ ownerAddress: c.get('userAddress') })
670
+ .find(f => f.cid === parsed.cid)
671
+ if (existingFile) {
672
+ return c.json({
673
+ success: true,
674
+ available: true,
675
+ cid: parsed.cid,
676
+ fileName: existingFile.fileName,
677
+ size: Number(existingFile.size) || null,
678
+ alreadyExists: true,
679
+ })
680
+ }
681
+
682
+ if (engine.hasDownloadNameConflict(parsed.fileName)) {
683
+ return c.json(
684
+ {
685
+ error: `已有同名文件: ${parsed.fileName}`,
686
+ code: 'CONFLICT',
687
+ },
688
+ 409
689
+ )
690
+ }
691
+
692
+ try {
693
+ const result = await engine.checkDownloadAvailability(body.link, {
694
+ ownerAddress: c.get('userAddress'),
695
+ })
696
+ return c.json({ success: true, ...result })
697
+ } catch (err) {
698
+ return errorJson(c, err)
699
+ }
700
+ })
701
+
702
+ app.post('/api/download', async c => {
703
+ const body = await c.req.json()
704
+ if (!body.link) {
705
+ return c.json({ error: 'link is required' }, 400)
706
+ }
707
+
708
+ const taskId = `dl_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
709
+
710
+ const parsed = parseMostLink(body.link)
711
+ if (parsed.error) {
712
+ return c.json({ error: parsed.error }, 400)
713
+ }
714
+
715
+ const existingFile = engine
716
+ .getPublishedFiles({ ownerAddress: c.get('userAddress') })
717
+ .find(f => f.cid === parsed.cid)
718
+ if (existingFile) {
719
+ console.log(`[MostBox] File already exists: ${existingFile.fileName}`)
720
+ try {
721
+ const result = await engine.downloadFile(body.link, taskId, {
722
+ ownerAddress: c.get('userAddress'),
723
+ })
724
+ return c.json({ success: true, ...result })
725
+ } catch (err) {
726
+ return errorJson(c, err)
727
+ }
728
+ }
729
+
730
+ if (engine.hasDownloadNameConflict(parsed.fileName)) {
731
+ return c.json(
732
+ {
733
+ error: `已有同名文件: ${parsed.fileName}`,
734
+ code: 'CONFLICT',
735
+ },
736
+ 409
737
+ )
738
+ }
739
+
740
+ engine
741
+ .downloadFile(body.link, taskId, { ownerAddress: c.get('userAddress') })
742
+ .catch(err => {
743
+ if (err.message === 'Download cancelled') {
744
+ wsBroadcast('download:cancelled', { taskId })
745
+ } else {
746
+ wsBroadcast('download:error', { taskId, error: err.message })
747
+ }
748
+ })
749
+
750
+ return c.json({ success: true, taskId })
751
+ })
752
+
753
+ app.post('/api/download/cancel', async c => {
754
+ const body = await c.req.json()
755
+ if (!body.taskId) {
756
+ return c.json({ error: 'taskId is required' }, 400)
757
+ }
758
+ engine.cancelDownload(body.taskId)
759
+ return c.json({ success: true })
760
+ })
761
+
762
+ app.delete('/api/files/:cid', async c => {
763
+ const cid = c.req.param('cid')
764
+ const cidValidation = validateCidString(cid)
765
+ if (!cidValidation.valid) {
766
+ return c.json({ error: cidValidation.error }, 400)
767
+ }
768
+ const result = await engine.deletePublishedFile(cid, {
769
+ ownerAddress: c.get('userAddress'),
770
+ })
771
+ return c.json(result)
772
+ })
773
+
774
+ app.post('/api/move', async c => {
775
+ const body = await c.req.json()
776
+ if (!body.cid || !body.newFileName) {
777
+ return c.json({ error: 'cid and newFileName are required' }, 400)
778
+ }
779
+ const cidValidation = validateCidString(body.cid)
780
+ if (!cidValidation.valid) {
781
+ return c.json({ error: cidValidation.error }, 400)
782
+ }
783
+ const cleanFileName = sanitizeFilename(body.newFileName)
784
+ if (
785
+ !cleanFileName ||
786
+ cleanFileName === 'unnamed' ||
787
+ body.newFileName.length > 255
788
+ ) {
789
+ return c.json({ error: 'Invalid filename' }, 400)
790
+ }
791
+ try {
792
+ const result = engine.moveFile(body.cid, cleanFileName, {
793
+ ownerAddress: c.get('userAddress'),
794
+ })
795
+ return c.json({ success: true, ...result })
796
+ } catch (err) {
797
+ return c.json({ error: err.message }, 400)
798
+ }
799
+ })
800
+
801
+ app.get('/api/files/:cid/download', async c => {
802
+ const cid = c.req.param('cid')
803
+ const cidValidation = validateCidString(cid)
804
+ if (!cidValidation.valid) {
805
+ return c.json({ error: cidValidation.error }, 400)
806
+ }
807
+
808
+ const rangeHeader = c.req.header('range')
809
+
810
+ try {
811
+ if (rangeHeader) {
812
+ const rangeMatch = rangeHeader.match(/bytes=(\d+)-(\d*)/)
813
+ if (rangeMatch) {
814
+ const start = parseInt(rangeMatch[1], 10)
815
+ const end = rangeMatch[2] ? parseInt(rangeMatch[2], 10) : undefined
816
+ const offset = start
817
+ const limit = end !== undefined ? end - start + 1 : undefined
818
+
819
+ const result = await engine.readFileRaw(cid, {
820
+ offset,
821
+ limit,
822
+ public: true,
823
+ })
824
+ const contentType = getMimeType(result.fileName)
825
+
826
+ c.header('Content-Type', contentType)
827
+ c.header('Content-Length', String(result.buffer.length))
828
+ c.header(
829
+ 'Content-Range',
830
+ `bytes ${offset}-${offset + result.buffer.length - 1}/${result.totalSize}`
831
+ )
832
+ c.header('Accept-Ranges', 'bytes')
833
+ c.status(206)
834
+ return c.body(result.buffer)
835
+ }
836
+ }
837
+
838
+ const result = await engine.readFileRaw(cid, {
839
+ public: true,
840
+ })
841
+ const contentType = getMimeType(result.fileName)
842
+ c.header('Content-Type', contentType)
843
+ c.header('Content-Length', String(result.totalSize))
844
+ c.header('Accept-Ranges', 'bytes')
845
+ c.header(
846
+ 'Content-Disposition',
847
+ `inline; filename="${encodeURIComponent(result.fileName)}"`
848
+ )
849
+ return c.body(result.buffer)
850
+ } catch (err) {
851
+ if (err.message === 'File not found') {
852
+ return c.json({ error: err.message }, 404)
853
+ }
854
+ return c.json({ error: err.message }, 400)
855
+ }
856
+ })
857
+
858
+ // --- 回收站路由 ---
859
+ app.get('/api/trash', c => {
860
+ return c.json(engine.listTrashFiles({ ownerAddress: c.get('userAddress') }))
861
+ })
862
+
863
+ app.post('/api/trash/:cid/restore', async c => {
864
+ const cid = c.req.param('cid')
865
+ const cidValidation = validateCidString(cid)
866
+ if (!cidValidation.valid) {
867
+ return c.json({ error: cidValidation.error }, 400)
868
+ }
869
+ try {
870
+ const result = await engine.restoreTrashFile(cid, {
871
+ ownerAddress: c.get('userAddress'),
872
+ })
873
+ return c.json({ success: true, files: result })
874
+ } catch (err) {
875
+ return c.json({ error: err.message }, 400)
876
+ }
877
+ })
878
+
879
+ app.delete('/api/trash/:cid', async c => {
880
+ const cid = c.req.param('cid')
881
+ const cidValidation = validateCidString(cid)
882
+ if (!cidValidation.valid) {
883
+ return c.json({ error: cidValidation.error }, 400)
884
+ }
885
+ const result = await engine.permanentDeleteTrashFile(cid, {
886
+ ownerAddress: c.get('userAddress'),
887
+ })
888
+ return c.json({ success: true, trashFiles: result })
889
+ })
890
+
891
+ app.delete('/api/trash', async c => {
892
+ const result = await engine.emptyTrash({
893
+ ownerAddress: c.get('userAddress'),
894
+ })
895
+ return c.json({ success: true, trashFiles: result })
896
+ })
897
+
898
+ // --- 收藏路由 ---
899
+ app.post('/api/files/:cid/star', async c => {
900
+ const cid = c.req.param('cid')
901
+ const cidValidation = validateCidString(cid)
902
+ if (!cidValidation.valid) {
903
+ return c.json({ error: cidValidation.error }, 400)
904
+ }
905
+ try {
906
+ const result = engine.toggleStarred(cid, {
907
+ ownerAddress: c.get('userAddress'),
908
+ })
909
+ return c.json({ success: true, ...result })
910
+ } catch (err) {
911
+ return c.json({ error: err.message }, 400)
912
+ }
913
+ })
914
+
915
+ // --- 显示名路由 ---
916
+ app.get('/api/display-name', c => {
917
+ return c.json({ displayName: engine.getDisplayName() })
918
+ })
919
+
920
+ app.post('/api/display-name', async c => {
921
+ const body = await c.req.json()
922
+ if (!body.name || !body.name.trim()) {
923
+ return c.json({ error: 'name is required' }, 400)
924
+ }
925
+ const trimmed = body.name.trim()
926
+ if (trimmed.length > 100) {
927
+ return c.json({ error: 'Name too long (max 100 chars)' }, 400)
928
+ }
929
+ if (/[<>]/.test(trimmed)) {
930
+ return c.json({ error: 'Name contains invalid characters' }, 400)
931
+ }
932
+ const success = engine.setDisplayName(trimmed)
933
+ return c.json({ success, displayName: engine.getDisplayName() })
934
+ })
935
+
936
+ // --- 频道路由 ---
937
+ app.post('/api/channels', async c => {
938
+ const body = await c.req.json()
939
+ if (!body.name || !body.name.trim()) {
940
+ return c.json({ error: 'name is required' }, 400)
941
+ }
942
+ try {
943
+ const result = await engine.createChannel(
944
+ body.name.trim(),
945
+ body.type || 'personal',
946
+ { ownerAddress: c.get('userAddress') }
947
+ )
948
+ return c.json({ success: true, ...result })
949
+ } catch (err) {
950
+ return c.json({ error: err.message }, 400)
951
+ }
952
+ })
953
+
954
+ app.get('/api/channels', c => {
955
+ return c.json(engine.listChannels({ ownerAddress: c.get('userAddress') }))
956
+ })
957
+
958
+ app.delete('/api/channels/:name', async c => {
959
+ const name = c.req.param('name')
960
+ try {
961
+ const result = await engine.leaveChannel(name, {
962
+ ownerAddress: c.get('userAddress'),
963
+ })
964
+ return c.json({ success: true, channels: result })
965
+ } catch (err) {
966
+ return c.json({ error: err.message }, 400)
967
+ }
968
+ })
969
+
970
+ app.get('/api/channels/:name/messages', async c => {
971
+ const name = c.req.param('name')
972
+ const limit = parseInt(c.req.query('limit') || '100', 10)
973
+ const offset = parseInt(c.req.query('offset') || '0', 10)
974
+ try {
975
+ const messages = await engine.getChannelMessages(name, {
976
+ limit,
977
+ offset,
978
+ ownerAddress: c.get('userAddress'),
979
+ })
980
+ return c.json(messages)
981
+ } catch (err) {
982
+ return badRequestOrAppError(c, err)
983
+ }
984
+ })
985
+
986
+ app.post('/api/channels/:name/messages', async c => {
987
+ const name = c.req.param('name')
988
+ const body = await c.req.json()
989
+ if (!body.content || !body.content.trim()) {
990
+ return c.json({ error: 'content is required' }, 400)
991
+ }
992
+ if (!body.author || !body.authorName) {
993
+ return c.json({ error: 'author and authorName are required' }, 400)
994
+ }
995
+ if (!/^0x[a-fA-F0-9]{40}$/.test(body.author)) {
996
+ return c.json({ error: 'Invalid author format' }, 400)
997
+ }
998
+ if (normalizeAddress(body.author) !== c.get('userAddress')) {
999
+ return c.json({ error: 'message author must match logged-in user' }, 403)
1000
+ }
1001
+ if (body.authorName.length > 50) {
1002
+ return c.json({ error: 'authorName too long' }, 400)
1003
+ }
1004
+ try {
1005
+ const message = await engine.sendMessage(
1006
+ name,
1007
+ body.content,
1008
+ body.author,
1009
+ body.authorName,
1010
+ { ownerAddress: c.get('userAddress'), attachment: body.attachment }
1011
+ )
1012
+ return c.json({ success: true, message })
1013
+ } catch (err) {
1014
+ return badRequestOrAppError(c, err)
1015
+ }
1016
+ })
1017
+
1018
+ app.get('/api/channels/:name/peers', c => {
1019
+ try {
1020
+ return c.json(
1021
+ engine.getChannelPeers(c.req.param('name'), {
1022
+ ownerAddress: c.get('userAddress'),
1023
+ })
1024
+ )
1025
+ } catch (err) {
1026
+ return badRequestOrAppError(c, err)
1027
+ }
1028
+ })
1029
+
1030
+ app.put('/api/channels/:name/remark', async c => {
1031
+ const name = c.req.param('name')
1032
+ const body = await c.req.json()
1033
+ try {
1034
+ const remark = engine.setChannelRemark(name, body.remark, {
1035
+ ownerAddress: c.get('userAddress'),
1036
+ })
1037
+ return c.json({ success: true, remark })
1038
+ } catch (err) {
1039
+ return c.json({ error: err.message }, 400)
1040
+ }
1041
+ })
1042
+
1043
+ // --- 文件夹重命名 ---
1044
+ app.post('/api/folder/rename', async c => {
1045
+ const body = await c.req.json()
1046
+ if (!body.oldPath || !body.newPath) {
1047
+ return c.json({ error: 'oldPath and newPath are required' }, 400)
1048
+ }
1049
+ if (body.oldPath.length > 500 || body.newPath.length > 500) {
1050
+ return c.json({ error: 'Path too long' }, 400)
1051
+ }
1052
+ if (body.oldPath.includes('..') || body.newPath.includes('..')) {
1053
+ return c.json({ error: 'Path traversal not allowed' }, 400)
1054
+ }
1055
+ try {
1056
+ const result = engine.renameFolder(body.oldPath, body.newPath, {
1057
+ ownerAddress: c.get('userAddress'),
1058
+ })
1059
+ return c.json({ success: true, ...result })
1060
+ } catch (err) {
1061
+ return c.json({ error: err.message }, 400)
1062
+ }
1063
+ })
1064
+
1065
+ // --- 关机路由 ---
1066
+ app.post('/api/shutdown', c => {
1067
+ const clientIp = c.env.incoming?.socket?.remoteAddress || 'unknown'
1068
+ if (!isLoopbackRemoteAddress(clientIp)) {
1069
+ return c.json({ error: 'Forbidden' }, 403)
1070
+ }
1071
+ c.json({ success: true })
1072
+ console.log('[MostBox] Shutdown requested via API...')
1073
+ setTimeout(async () => {
1074
+ await engine.stop()
1075
+ if (serverInstanceRef.current) serverInstanceRef.current.close()
1076
+ console.log('[MostBox] Server stopped.')
1077
+ process.exit(0)
1078
+ }, 100)
1079
+ return c.body(null)
1080
+ })
1081
+
1082
+ registerStaticRoutes(app)
1083
+
1084
+ return {
1085
+ app,
1086
+ wsBroadcast,
1087
+ wsSendToChannel,
1088
+ broadcastNodeStatus,
1089
+ appendNodeLog,
1090
+ subscribeToChannel,
1091
+ unsubscribeFromChannel,
1092
+ cleanupWsSubscriptions,
1093
+ validateWebSocketRequest,
1094
+ }
1095
+ }