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.
- package/README.md +24 -16
- package/electron/main.js +72 -8
- package/electron/preload.js +6 -0
- package/out/404/index.html +2 -2
- package/out/404.html +2 -2
- package/out/__next.__PAGE__.txt +6 -6
- package/out/__next._full.txt +23 -20
- package/out/__next._head.txt +3 -3
- package/out/__next._index.txt +8 -6
- package/out/__next._tree.txt +6 -4
- package/out/_next/static/chunks/0-n3pg7th.zza.js +1 -0
- package/out/_next/static/chunks/0.4j.0k5a64vg.js +1 -0
- package/out/_next/static/chunks/0.e2avjgna_b2.js +1 -0
- package/out/_next/static/chunks/0.ozi1_x2.m.~.js +1 -0
- package/out/_next/static/chunks/0.t5wlt51zou5.js +1 -0
- package/out/_next/static/chunks/0.w4hkvap~bva.js +1 -0
- package/out/_next/static/chunks/00d9h1tddnnnd.js +1 -0
- package/out/_next/static/chunks/00tkdqwxch-3s.js +1 -0
- package/out/_next/static/chunks/01l3o90g~1z42.js +1 -0
- package/out/_next/static/chunks/01mfky9camw6i.js +1 -0
- package/out/_next/static/chunks/01r.v-pqs1vrm.js +1 -0
- package/out/_next/static/chunks/03edqrb4zdj~g.js +31 -0
- package/out/_next/static/chunks/03h_6oo-gqkhz.js +1 -0
- package/out/_next/static/chunks/{0ho~log~~-jwp.css → 03h~nhgj0hv3p.css} +1 -1
- package/out/_next/static/chunks/04hcgsanv1hhu.js +1 -0
- package/out/_next/static/chunks/05g2q0w5b34.g.js +1 -0
- package/out/_next/static/chunks/05of77xycbt8~.js +1 -0
- package/out/_next/static/chunks/05zwemzfjx3sh.js +1 -0
- package/out/_next/static/chunks/06dpc5df94.v1.js +1 -0
- package/out/_next/static/chunks/06e1~1-z_ic9a.js +1 -0
- package/out/_next/static/chunks/075s7sn.ns~u5.js +1 -0
- package/out/_next/static/chunks/07dynrbvd3.f4.js +1 -0
- package/out/_next/static/chunks/07p~uva5pwgwe.js +1 -0
- package/out/_next/static/chunks/07r9nn-pzlgg1.js +1 -0
- package/out/_next/static/chunks/07td.jq7xff84.css +1 -0
- package/out/_next/static/chunks/08.72abkgwy9g.js +1 -0
- package/out/_next/static/chunks/08576xhv~~jck.js +1 -0
- package/out/_next/static/chunks/08u211~k~qu52.js +1 -0
- package/out/_next/static/chunks/098.p.2-zm4p7.js +1 -0
- package/out/_next/static/chunks/09ngvtajm7e5y.js +1 -0
- package/out/_next/static/chunks/09ps~-43n5qyo.js +1 -0
- package/out/_next/static/chunks/09v7_0gclxr46.js +1 -0
- package/out/_next/static/chunks/09yql86dir9c4.js +1 -0
- package/out/_next/static/chunks/09zmlfljowj1~.js +1 -0
- package/out/_next/static/chunks/0_s~ebb-7b2hr.js +1 -0
- package/out/_next/static/chunks/0_w-0-2z5oqd_.js +1 -0
- package/out/_next/static/chunks/0adx~d-j05c9d.css +24 -0
- package/out/_next/static/chunks/0ao1lbi4b.sfa.js +1 -0
- package/out/_next/static/chunks/0aq.rc9woa2nz.js +1 -0
- package/out/_next/static/chunks/0bld2u_ld~va2.js +1 -0
- package/out/_next/static/chunks/0bliugh5lxw55.js +1 -0
- package/out/_next/static/chunks/0cn9a7aimbdzq.js +1 -0
- package/out/_next/static/chunks/0d3f-nk3c.2re.js +1 -0
- package/out/_next/static/chunks/0dtohpf7~3d12.js +1 -0
- package/out/_next/static/chunks/0e-3e8h7g99yf.js +1 -0
- package/out/_next/static/chunks/0e531nije_ln2.js +1 -0
- package/out/_next/static/chunks/0e5zvj_rh0z3m.js +1 -0
- package/out/_next/static/chunks/0etes81d_cihn.js +1 -0
- package/out/_next/static/chunks/0f4y~rkk-n81e.js +1 -0
- package/out/_next/static/chunks/0fk~0~p7ivfn1.js +1 -0
- package/out/_next/static/chunks/0fw6juc~lsj3z.js +1 -0
- package/out/_next/static/chunks/0g0u7785a73vo.js +1 -0
- package/out/_next/static/chunks/0g_a~e050bgzg.css +1 -0
- package/out/_next/static/chunks/0g_fpgh7drfda.js +1 -0
- package/out/_next/static/chunks/{0n~dq4kpx9xxx.js → 0gcsdf57gcm6h.js} +1 -1
- package/out/_next/static/chunks/0gwian.hp3-92.js +1 -0
- package/out/_next/static/chunks/0gze5uso1mbe9.js +1 -0
- package/out/_next/static/chunks/0h4r.qtmpa6eh.js +1 -0
- package/out/_next/static/chunks/0hf.aosc-7172.js +1 -0
- package/out/_next/static/chunks/0hgz35c1ejbs9.js +1 -0
- package/out/_next/static/chunks/0hpev4am9jpmu.css +1 -0
- package/out/_next/static/chunks/0hrw-r.xmvmsq.js +1 -0
- package/out/_next/static/chunks/0hzg4al.v~8~m.js +1 -0
- package/out/_next/static/chunks/0ip9xrols_83o.js +1 -0
- package/out/_next/static/chunks/0j4-d0qf.v~kn.js +1 -0
- package/out/_next/static/chunks/0jhdeq.j9_02m.js +1 -0
- package/out/_next/static/chunks/0jy63h3i-y69i.js +1 -0
- package/out/_next/static/chunks/0kdnx_u-60k9s.js +1 -0
- package/out/_next/static/chunks/0kq~edq42o1-c.js +1 -0
- package/out/_next/static/chunks/0l5_.uqb-uqb8.js +1 -0
- package/out/_next/static/chunks/0l682p362d-5w.js +1 -0
- package/out/_next/static/chunks/0m68p9txef5rs.js +1 -0
- package/out/_next/static/chunks/0mex8svsiv-2l.js +1 -0
- package/out/_next/static/chunks/0mme-fm5d2oz2.js +1 -0
- package/out/_next/static/chunks/0myp4sjagr~h0.js +1 -0
- package/out/_next/static/chunks/0myq9gs8szydh.js +1 -0
- package/out/_next/static/chunks/0n.qlfk~z7o.6.js +1 -0
- package/out/_next/static/chunks/0n4t80gjc3q5h.js +1 -0
- package/out/_next/static/chunks/0o9ce4cyf76by.js +736 -0
- package/out/_next/static/chunks/0oz3yl6_-716p.js +1 -0
- package/out/_next/static/chunks/0p0sv~fuddvgr.js +1 -0
- package/out/_next/static/chunks/0pt.5cg1t09qs.js +1 -0
- package/out/_next/static/chunks/0q0ksgxg98xgd.js +1 -0
- package/out/_next/static/chunks/0qgx9t4jx16ua.css +1 -0
- package/out/_next/static/chunks/0qqupeexg83u7.js +1 -0
- package/out/_next/static/chunks/0rb-ri481.kc9.js +1 -0
- package/out/_next/static/chunks/0rsnmahfd.59p.js +1 -0
- package/out/_next/static/chunks/0rt6rgnvr-s_p.js +1 -0
- package/out/_next/static/chunks/0runh28p_gg6..js +1 -0
- package/out/_next/static/chunks/0shy.t1fwqcev.js +1 -0
- package/out/_next/static/chunks/{0d3shmwh5_nmn.js → 0t2xr05rlu96l.js} +1 -1
- package/out/_next/static/chunks/0t6h56rhg1y5i.js +1 -0
- package/out/_next/static/chunks/0tdqd1zunusgk.js +1 -0
- package/out/_next/static/chunks/0ujbnp38x63ek.js +1 -0
- package/out/_next/static/chunks/0ukyg~tkm~h2m.css +1 -0
- package/out/_next/static/chunks/0v68pdrp54lb-.js +1 -0
- package/out/_next/static/chunks/0vsm0m5sxrb.3.js +1 -0
- package/out/_next/static/chunks/0vzlz.iboqo3c.js +1 -0
- package/out/_next/static/chunks/0w87vbpkf-ogd.js +1 -0
- package/out/_next/static/chunks/0wtf0xsiicxx6.js +1 -0
- package/out/_next/static/chunks/0xdwau5k2augv.css +4 -0
- package/out/_next/static/chunks/0xgg0~kmf3gd-.js +1 -0
- package/out/_next/static/chunks/0xj24-70ptdzp.js +1 -0
- package/out/_next/static/chunks/0xxlx772fr3x4.js +1 -0
- package/out/_next/static/chunks/0y.li-~3oybew.js +1 -0
- package/out/_next/static/chunks/0yl2t7cs-n_ng.js +1 -0
- package/out/_next/static/chunks/0yq3kh.hchtm_.js +1 -0
- package/out/_next/static/chunks/0ys0l5au.9c2c.js +1 -0
- package/out/_next/static/chunks/0z48pmi6buytt.js +1 -0
- package/out/_next/static/chunks/0zapnvgy89mg..js +1 -0
- package/out/_next/static/chunks/0~.-vxi5oc.r0.js +1 -0
- package/out/_next/static/chunks/0~3ik-hfp9s-7.js +1 -0
- package/out/_next/static/chunks/0~4f5p6tvn1lq.js +1 -0
- package/out/_next/static/chunks/0~_0ys.2whxbw.js +1 -0
- package/out/_next/static/chunks/0~_ui9l7.2sxf.js +1 -0
- package/out/_next/static/chunks/1037jlyw5~7ht.js +1 -0
- package/out/_next/static/chunks/1045hfzu533z0.js +1 -0
- package/out/_next/static/chunks/104e5nmc.c-pl.js +1 -0
- package/out/_next/static/chunks/109taw1pbh-0b.js +1 -0
- package/out/_next/static/chunks/10x7~onqwp338.js +1 -0
- package/out/_next/static/chunks/10ynz1dy483wf.js +1 -0
- package/out/_next/static/chunks/11hds.mg~4_r-.js +1 -0
- package/out/_next/static/chunks/11ibzaklcauw~.js +1 -0
- package/out/_next/static/chunks/11z.0s6.42b.p.js +1 -0
- package/out/_next/static/chunks/12-9n56l0y3yr.js +1 -0
- package/out/_next/static/chunks/126enaq~f7scl.js +1 -0
- package/out/_next/static/chunks/12nr19.nnn6s3.js +5 -0
- package/out/_next/static/chunks/{0qub_r0x_r-e9.css → 12pep-2t-qg4n.css} +1 -1
- package/out/_next/static/chunks/1380op_pfk.qo.js +1 -0
- package/out/_next/static/chunks/146oiw1bggtn4.js +1 -0
- package/out/_next/static/chunks/14_inksek_rth.js +2 -0
- package/out/_next/static/chunks/14_po2rb_arn4.js +1 -0
- package/out/_next/static/chunks/14a4fwbiq.l3z.js +1 -0
- package/out/_next/static/chunks/14cowsqn95m1k.js +1 -0
- package/out/_next/static/chunks/14dtd3l03v.kx.js +1 -0
- package/out/_next/static/chunks/14tm3qa-v9o-4.js +1 -0
- package/out/_next/static/chunks/15-o4kb-evqd7.js +1 -0
- package/out/_next/static/chunks/153-sz7s.qml2.js +1 -0
- package/out/_next/static/chunks/157z7bowux3xj.js +1 -0
- package/out/_next/static/chunks/15m1_677az2cm.js +1 -0
- package/out/_next/static/chunks/15v.~.ne6ogkk.js +1 -0
- package/out/_next/static/chunks/16i.qbk8t8gf_.js +1 -0
- package/out/_next/static/chunks/16u9f35gylw8l.js +1 -0
- package/out/_next/static/chunks/16xls5tt_68lx.js +1 -0
- package/out/_next/static/chunks/17ajyb5ogk5yj.js +1 -0
- package/out/_next/static/chunks/17dyfxbq8yz8n.js +1 -0
- package/out/_next/static/chunks/180zln9pcq9ih.js +1 -0
- package/out/_next/static/chunks/1814izi5gh.kp.js +1 -0
- package/out/_next/static/chunks/turbopack-0xta0kqwzkf28.js +1 -0
- package/out/_next/static/media/KaTeX_AMS-Regular.0b~8ki5y928w2.woff +0 -0
- package/out/_next/static/media/KaTeX_AMS-Regular.0p1vbqd84i2~o.woff2 +0 -0
- package/out/_next/static/media/KaTeX_AMS-Regular.173t6ktr7uf-w.ttf +0 -0
- package/out/_next/static/media/KaTeX_Caligraphic-Bold.01-pzluls4zgb.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Caligraphic-Bold.0x2v1lwn~880f.woff +0 -0
- package/out/_next/static/media/KaTeX_Caligraphic-Bold.16zv5fax0h0ka.ttf +0 -0
- package/out/_next/static/media/KaTeX_Caligraphic-Regular.02i3z7wig438t.ttf +0 -0
- package/out/_next/static/media/KaTeX_Caligraphic-Regular.0rysu1t-ncjq8.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Caligraphic-Regular.10927swgekwun.woff +0 -0
- package/out/_next/static/media/KaTeX_Fraktur-Bold.0e-16u10iuyyf.woff +0 -0
- package/out/_next/static/media/KaTeX_Fraktur-Bold.0et27v~3~4uhe.ttf +0 -0
- package/out/_next/static/media/KaTeX_Fraktur-Bold.0w23i72~hprpq.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Fraktur-Regular.0b.riegzdfue2.woff +0 -0
- package/out/_next/static/media/KaTeX_Fraktur-Regular.0rekyoa-52fj_.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Fraktur-Regular.0vjwa15znhk~4.ttf +0 -0
- package/out/_next/static/media/KaTeX_Main-Bold.09i7~607shf-h.ttf +0 -0
- package/out/_next/static/media/KaTeX_Main-Bold.09lmynrorhcbw.woff +0 -0
- package/out/_next/static/media/KaTeX_Main-Bold.16pfc63_du6mx.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Main-BoldItalic.0cp37g7x1q8h6.woff +0 -0
- package/out/_next/static/media/KaTeX_Main-BoldItalic.0d54rk08rx11s.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Main-BoldItalic.15j6k~hix2t_0.ttf +0 -0
- package/out/_next/static/media/KaTeX_Main-Italic.0382gqciexmbu.woff +0 -0
- package/out/_next/static/media/KaTeX_Main-Italic.06o5nq0_91v60.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Main-Italic.0su4i6mm18-wo.ttf +0 -0
- package/out/_next/static/media/KaTeX_Main-Regular.08zh8z.7shijf.ttf +0 -0
- package/out/_next/static/media/KaTeX_Main-Regular.0diheg01zyoph.woff +0 -0
- package/out/_next/static/media/KaTeX_Main-Regular.0kaf-ag2_wkm-.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Math-BoldItalic.0ajzxypnbx1h1.ttf +0 -0
- package/out/_next/static/media/KaTeX_Math-BoldItalic.0ck1myuerwyqw.woff +0 -0
- package/out/_next/static/media/KaTeX_Math-BoldItalic.0ja97dn.cpc87.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Math-Italic.09xkhecjcn5r9.woff +0 -0
- package/out/_next/static/media/KaTeX_Math-Italic.0x23a-bmp-5tg.ttf +0 -0
- package/out/_next/static/media/KaTeX_Math-Italic.0zrha2c4sl2je.woff2 +0 -0
- package/out/_next/static/media/KaTeX_SansSerif-Bold.05a9.pc1j_zx9.woff2 +0 -0
- package/out/_next/static/media/KaTeX_SansSerif-Bold.0jcl-ayi1uun0.woff +0 -0
- package/out/_next/static/media/KaTeX_SansSerif-Bold.0re8y.dm7.mt5.ttf +0 -0
- package/out/_next/static/media/KaTeX_SansSerif-Italic.0a0234dc3s62j.woff2 +0 -0
- package/out/_next/static/media/KaTeX_SansSerif-Italic.0judofdln9731.woff +0 -0
- package/out/_next/static/media/KaTeX_SansSerif-Italic.10z1iap9pfus8.ttf +0 -0
- package/out/_next/static/media/KaTeX_SansSerif-Regular.0h9yjlugq4q_e.woff +0 -0
- package/out/_next/static/media/KaTeX_SansSerif-Regular.0v6gcj32-czft.woff2 +0 -0
- package/out/_next/static/media/KaTeX_SansSerif-Regular.0zm18kga42ebc.ttf +0 -0
- package/out/_next/static/media/KaTeX_Script-Regular.0c4.h-mer83d_.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Script-Regular.0q14y6zkzlpob.ttf +0 -0
- package/out/_next/static/media/KaTeX_Script-Regular.0ze6v4r_-99oy.woff +0 -0
- package/out/_next/static/media/KaTeX_Size1-Regular.013x6a4ierotp.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Size1-Regular.0kidw0oi.m68o.woff +0 -0
- package/out/_next/static/media/KaTeX_Size1-Regular.0m6y-i6wfokni.ttf +0 -0
- package/out/_next/static/media/KaTeX_Size2-Regular.0blpmluwilgbg.woff +0 -0
- package/out/_next/static/media/KaTeX_Size2-Regular.0d5inmyp-tyv3.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Size2-Regular.0wnhnvj-.k9d5.ttf +0 -0
- package/out/_next/static/media/KaTeX_Size3-Regular.01h0xm_sfctj3.woff +0 -0
- package/out/_next/static/media/KaTeX_Size3-Regular.0iukctyhw5j56.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Size3-Regular.0jl8mqyf4gzpn.ttf +0 -0
- package/out/_next/static/media/KaTeX_Size4-Regular.0w3.rb_c4stzk.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Size4-Regular.0wr_9l81-mu06.ttf +0 -0
- package/out/_next/static/media/KaTeX_Size4-Regular.12tvaesf3.zl3.woff +0 -0
- package/out/_next/static/media/KaTeX_Typewriter-Regular.0c4zdxz~8frhm.woff2 +0 -0
- package/out/_next/static/media/KaTeX_Typewriter-Regular.0cgrzn5l3kao5.woff +0 -0
- package/out/_next/static/media/KaTeX_Typewriter-Regular.128~qc3858otl.ttf +0 -0
- package/out/_not-found/__next._full.txt +21 -19
- package/out/_not-found/__next._head.txt +3 -3
- package/out/_not-found/__next._index.txt +8 -6
- package/out/_not-found/__next._not-found.__PAGE__.txt +4 -4
- package/out/_not-found/__next._not-found.txt +3 -3
- package/out/_not-found/__next._tree.txt +3 -2
- package/out/_not-found/index.html +2 -2
- package/out/_not-found/index.txt +21 -19
- package/out/admin/__next._full.txt +23 -0
- package/out/admin/__next._head.txt +5 -0
- package/out/admin/__next._index.txt +9 -0
- package/out/admin/__next._tree.txt +5 -0
- package/out/admin/__next.admin.__PAGE__.txt +9 -0
- package/out/admin/__next.admin.txt +5 -0
- package/out/admin/index.html +15 -0
- package/out/admin/index.txt +23 -0
- package/out/app/__next._full.txt +15 -13
- package/out/app/__next._head.txt +3 -3
- package/out/app/__next._index.txt +8 -6
- package/out/app/__next._tree.txt +4 -2
- package/out/app/__next.app.__PAGE__.txt +4 -4
- package/out/app/__next.app.txt +3 -4
- package/out/app/index.html +2 -2
- package/out/app/index.txt +15 -13
- package/out/chat/__next._full.txt +16 -14
- package/out/chat/__next._head.txt +3 -3
- package/out/chat/__next._index.txt +8 -6
- package/out/chat/__next._tree.txt +5 -3
- package/out/chat/__next.chat.__PAGE__.txt +4 -4
- package/out/chat/__next.chat.txt +4 -5
- package/out/chat/index.html +2 -2
- package/out/chat/index.txt +16 -14
- package/out/chat/join/__next._full.txt +25 -0
- package/out/chat/join/__next._head.txt +5 -0
- package/out/chat/join/__next._index.txt +9 -0
- package/out/chat/join/__next._tree.txt +5 -0
- package/out/chat/join/__next.chat.join.__PAGE__.txt +9 -0
- package/out/chat/join/__next.chat.join.txt +5 -0
- package/out/chat/join/__next.chat.txt +5 -0
- package/out/chat/join/index.html +15 -0
- package/out/chat/join/index.txt +25 -0
- package/out/download/__next._full.txt +34 -33
- package/out/download/__next._head.txt +3 -3
- package/out/download/__next._index.txt +8 -6
- package/out/download/__next._tree.txt +6 -4
- package/out/download/__next.download.__PAGE__.txt +13 -14
- package/out/download/__next.download.txt +3 -3
- package/out/download/index.html +2 -2
- package/out/download/index.txt +34 -33
- package/out/index.html +2 -2
- package/out/index.txt +23 -20
- package/out/note/__next._full.txt +24 -0
- package/out/{changelog → note}/__next._head.txt +3 -3
- package/out/note/__next._index.txt +9 -0
- package/out/note/__next._tree.txt +4 -0
- package/out/note/__next.note.__PAGE__.txt +9 -0
- package/out/note/__next.note.txt +5 -0
- package/out/note/index.html +15 -0
- package/out/note/index.txt +24 -0
- package/out/ping/__next._full.txt +23 -20
- package/out/ping/__next._head.txt +3 -3
- package/out/ping/__next._index.txt +8 -6
- package/out/ping/__next._tree.txt +6 -4
- package/out/ping/__next.ping.__PAGE__.txt +5 -5
- package/out/ping/__next.ping.txt +4 -4
- package/out/ping/index.html +2 -2
- package/out/ping/index.txt +23 -20
- package/out/web3/__next._full.txt +16 -14
- package/out/web3/__next._head.txt +3 -3
- package/out/web3/__next._index.txt +8 -6
- package/out/web3/__next._tree.txt +5 -3
- package/out/web3/__next.web3.__PAGE__.txt +4 -4
- package/out/web3/__next.web3.txt +4 -5
- package/out/web3/ed25519/__next._full.txt +14 -12
- package/out/web3/ed25519/__next._head.txt +3 -3
- package/out/web3/ed25519/__next._index.txt +8 -6
- package/out/web3/ed25519/__next._tree.txt +5 -3
- package/out/web3/ed25519/__next.web3.ed25519.__PAGE__.txt +2 -2
- package/out/web3/ed25519/__next.web3.ed25519.txt +3 -3
- package/out/web3/ed25519/__next.web3.txt +4 -5
- package/out/web3/ed25519/index.html +1 -1
- package/out/web3/ed25519/index.txt +14 -12
- package/out/web3/index.html +2 -2
- package/out/web3/index.txt +16 -14
- package/out/web3/tools/__next._full.txt +14 -12
- package/out/web3/tools/__next._head.txt +3 -3
- package/out/web3/tools/__next._index.txt +8 -6
- package/out/web3/tools/__next._tree.txt +5 -3
- package/out/web3/tools/__next.web3.tools.__PAGE__.txt +2 -2
- package/out/web3/tools/__next.web3.tools.txt +3 -3
- package/out/web3/tools/__next.web3.txt +4 -5
- package/out/web3/tools/index.html +1 -1
- package/out/web3/tools/index.txt +14 -12
- package/package.json +30 -13
- package/server/index.js +188 -901
- package/server/src/config.js +5 -1
- package/server/src/core/channelAttachment.js +68 -0
- package/server/src/core/cid.js +6 -71
- package/server/src/core/cidTopic.js +29 -0
- package/server/src/core/mostLink.js +88 -0
- package/server/src/http/access.js +123 -0
- package/server/src/http/app.js +1095 -0
- package/server/src/http/errors.js +35 -0
- package/server/src/http/nodeLogs.js +53 -0
- package/server/src/http/nodeStatus.js +146 -0
- package/server/src/http/staticFiles.js +84 -0
- package/server/src/http/uploads.js +114 -0
- package/server/src/index.js +1539 -301
- package/server/src/node/config.js +191 -0
- package/server/src/node/logs.js +94 -0
- package/server/src/utils/api.js +359 -8
- package/server/src/utils/auth.js +63 -0
- package/server/src/utils/dateTime.js +30 -0
- package/server/src/utils/downloadMessages.js +89 -0
- package/server/src/utils/errors.js +14 -0
- package/server/src/utils/mostWallet.js +185 -1
- package/server/src/utils/mp.js +2 -26
- package/server/src/utils/noteBackup.js +116 -0
- package/server/src/utils/noteUtils.js +128 -0
- package/server/src/utils/userIdentity.js +8 -61
- package/out/_next/static/chunks/003jnm.v5tzw5.js +0 -1
- package/out/_next/static/chunks/00re8v.gbcywn.js +0 -1
- package/out/_next/static/chunks/00s106sbq8t9v.js +0 -1
- package/out/_next/static/chunks/012hi627qrdnn.js +0 -1
- package/out/_next/static/chunks/0174xh3wfsjm1.js +0 -2
- package/out/_next/static/chunks/02~o2nmo5pmy1.js +0 -1
- package/out/_next/static/chunks/07t.dhhokszz5.css +0 -1
- package/out/_next/static/chunks/0_wia9ofmsi1c.css +0 -2
- package/out/_next/static/chunks/0ah8fihozo2_u.js +0 -5
- package/out/_next/static/chunks/0bzupvr5gt3k9.js +0 -31
- package/out/_next/static/chunks/0e_h0d3ekzks8.css +0 -1
- package/out/_next/static/chunks/0gdluj423gso1.js +0 -1
- package/out/_next/static/chunks/0gmoiq06srjay.css +0 -1
- package/out/_next/static/chunks/0imkasy7kb67u.js +0 -1
- package/out/_next/static/chunks/0jjc_b9q_ldi2.js +0 -1
- package/out/_next/static/chunks/0jl~j62iz2uvr.js +0 -1
- package/out/_next/static/chunks/0lqslm813wk_h.js +0 -1
- package/out/_next/static/chunks/0q782fxxd0lx~.js +0 -1
- package/out/_next/static/chunks/0slwj0c46k5cu.js +0 -1
- package/out/_next/static/chunks/0sorqk.oc6b7j.css +0 -1
- package/out/_next/static/chunks/0tapzqc6hgvx-.js +0 -1
- package/out/_next/static/chunks/0xsc7z5x8n7wg.js +0 -1
- package/out/_next/static/chunks/0zm~gys2jwl0g.js +0 -1
- package/out/_next/static/chunks/turbopack-0a_g3u0ud~jb8.js +0 -1
- package/out/changelog/__next._full.txt +0 -22
- package/out/changelog/__next._index.txt +0 -7
- package/out/changelog/__next._tree.txt +0 -3
- package/out/changelog/__next.changelog.__PAGE__.txt +0 -10
- package/out/changelog/__next.changelog.txt +0 -5
- package/out/changelog/index.html +0 -15
- package/out/changelog/index.txt +0 -22
- package/out/docs/__next._full.txt +0 -22
- package/out/docs/__next._head.txt +0 -5
- package/out/docs/__next._index.txt +0 -7
- package/out/docs/__next._tree.txt +0 -3
- package/out/docs/__next.docs.__PAGE__.txt +0 -10
- package/out/docs/__next.docs.txt +0 -5
- package/out/docs/getting-started/__next._full.txt +0 -22
- package/out/docs/getting-started/__next._head.txt +0 -5
- package/out/docs/getting-started/__next._index.txt +0 -7
- package/out/docs/getting-started/__next._tree.txt +0 -3
- package/out/docs/getting-started/__next.docs.getting-started.__PAGE__.txt +0 -10
- package/out/docs/getting-started/__next.docs.getting-started.txt +0 -5
- package/out/docs/getting-started/__next.docs.txt +0 -5
- package/out/docs/getting-started/index.html +0 -15
- package/out/docs/getting-started/index.txt +0 -22
- package/out/docs/index.html +0 -15
- package/out/docs/index.txt +0 -22
- /package/out/_next/static/{iOB2EBwOGZ0iYW7Lbg9u_ → t7ZIeQpVvjz4a7-5Tt-VK}/_buildManifest.js +0 -0
- /package/out/_next/static/{iOB2EBwOGZ0iYW7Lbg9u_ → t7ZIeQpVvjz4a7-5Tt-VK}/_clientMiddlewareManifest.js +0 -0
- /package/out/_next/static/{iOB2EBwOGZ0iYW7Lbg9u_ → t7ZIeQpVvjz4a7-5Tt-VK}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { verifyMessage } from 'ethers'
|
|
2
|
+
import { mostSignMessage } from './mostWallet.js'
|
|
3
|
+
|
|
4
|
+
export const AUTH_MAX_AGE_MS = 5 * 60 * 1000
|
|
5
|
+
|
|
6
|
+
export function normalizeAddress(address) {
|
|
7
|
+
const value = String(address || '').trim()
|
|
8
|
+
return /^0x[a-fA-F0-9]{40}$/.test(value) ? value.toLowerCase() : ''
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function buildAuthMessage(timestamp, method, path) {
|
|
12
|
+
return `${timestamp}:${String(method || 'GET').toUpperCase()}:${normalizeAuthPath(path)}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function normalizeAuthPath(path) {
|
|
16
|
+
try {
|
|
17
|
+
return new URL(path, 'http://most.box').pathname
|
|
18
|
+
} catch {
|
|
19
|
+
return String(path || '').split('?')[0] || '/'
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function buildAuthHeaders(identity, method, path) {
|
|
24
|
+
if (!identity?.danger) return {}
|
|
25
|
+
const timestamp = Date.now().toString()
|
|
26
|
+
const message = buildAuthMessage(timestamp, method, path)
|
|
27
|
+
const { address, signature } = await mostSignMessage(identity.danger, message)
|
|
28
|
+
return {
|
|
29
|
+
Authorization: `${address},${timestamp},${signature}`,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function verifyAuthHeader(header, method, path, options = {}) {
|
|
34
|
+
const [addressRaw, timestampRaw, signature] = String(header || '').split(',')
|
|
35
|
+
const address = normalizeAddress(addressRaw)
|
|
36
|
+
const timestamp = Number(timestampRaw)
|
|
37
|
+
const now = options.now || Date.now()
|
|
38
|
+
|
|
39
|
+
if (!address || !Number.isFinite(timestamp) || !signature) {
|
|
40
|
+
return { ok: false, error: 'Missing or invalid authorization' }
|
|
41
|
+
}
|
|
42
|
+
if (Math.abs(now - timestamp) > (options.maxAgeMs || AUTH_MAX_AGE_MS)) {
|
|
43
|
+
return { ok: false, error: 'Authorization expired' }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const message = buildAuthMessage(timestampRaw, method, path)
|
|
48
|
+
const recovered = normalizeAddress(verifyMessage(message, signature))
|
|
49
|
+
if (recovered !== address) {
|
|
50
|
+
return { ok: false, error: 'Authorization address mismatch' }
|
|
51
|
+
}
|
|
52
|
+
return { ok: true, address }
|
|
53
|
+
} catch {
|
|
54
|
+
return { ok: false, error: 'Invalid authorization signature' }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function parseInviteList(value = '') {
|
|
59
|
+
return String(value || '')
|
|
60
|
+
.split(',')
|
|
61
|
+
.map(item => item.trim())
|
|
62
|
+
.filter(Boolean)
|
|
63
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import dayjs from 'dayjs'
|
|
2
|
+
|
|
3
|
+
export function formatDate(time) {
|
|
4
|
+
if (!time) return ''
|
|
5
|
+
const date = dayjs(Number(time))
|
|
6
|
+
const hour = date.hour()
|
|
7
|
+
let timeOfDay
|
|
8
|
+
|
|
9
|
+
if (hour >= 0 && hour < 3) {
|
|
10
|
+
timeOfDay = '凌晨'
|
|
11
|
+
} else if (hour >= 3 && hour < 6) {
|
|
12
|
+
timeOfDay = '拂晓'
|
|
13
|
+
} else if (hour >= 6 && hour < 9) {
|
|
14
|
+
timeOfDay = '早晨'
|
|
15
|
+
} else if (hour >= 9 && hour < 12) {
|
|
16
|
+
timeOfDay = '上午'
|
|
17
|
+
} else if (hour >= 12 && hour < 15) {
|
|
18
|
+
timeOfDay = '下午'
|
|
19
|
+
} else if (hour >= 15 && hour < 18) {
|
|
20
|
+
timeOfDay = '傍晚'
|
|
21
|
+
} else if (hour >= 18 && hour < 21) {
|
|
22
|
+
timeOfDay = '晚上'
|
|
23
|
+
} else {
|
|
24
|
+
timeOfDay = '深夜'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return date.format(`YYYY年M月D日 ${timeOfDay}h:m`)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const formatTime = formatDate
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { parseMostLink } from '../core/mostLink.js'
|
|
2
|
+
|
|
3
|
+
const DOWNLOAD_CHECK_MESSAGES = {
|
|
4
|
+
timeout:
|
|
5
|
+
'检测等待超时,暂时没有等到在线种子响应。请确认分享者或其他下载者仍在线做种,稍后再检测。',
|
|
6
|
+
offline: '无法连接本地节点,请确认 MostBox 后端正在运行后再检测。',
|
|
7
|
+
missingApi: '当前后端还没有检测接口,请重启 MostBox 后端后再试。',
|
|
8
|
+
validation:
|
|
9
|
+
'链接格式不正确,请粘贴完整的 most://<cid>?filename=... 分享链接。',
|
|
10
|
+
nameConflict: '下载目录已有同名文件,请先重命名或移走后再检测。',
|
|
11
|
+
noPeer:
|
|
12
|
+
'暂时没有发现在线种子。请确认分享者或其他下载者仍在线做种,稍后再检测。',
|
|
13
|
+
permission: '下载目录不可写,请检查目录权限后再检测。',
|
|
14
|
+
starting: '本地节点还没有启动完成,请稍等几秒后重新检测。',
|
|
15
|
+
server: '本地节点检测时出错,请稍后重试或查看节点日志。',
|
|
16
|
+
fallback: '检测未通过,请确认链接完整、发布者在线且本机网络正常。',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const LINK_VALIDATION_MESSAGES = {
|
|
20
|
+
'Link must be a valid most:// URL':
|
|
21
|
+
'链接无法解析,请粘贴完整的 most://<cid>?filename=... 分享链接。',
|
|
22
|
+
'Link must use most:// protocol': '链接协议不正确,应以 most:// 开头。',
|
|
23
|
+
'Link path is not supported':
|
|
24
|
+
'链接里不应包含路径,请使用 most://<cid>?filename=... 格式。',
|
|
25
|
+
'Invalid CID format': 'CID 无效,请确认 most:// 后面的内容没有缺失或被截断。',
|
|
26
|
+
'Invalid CID format: CID v1 required':
|
|
27
|
+
'CID 格式不符合 MostBox 要求,请确认分享链接完整。',
|
|
28
|
+
'CID digest must be 32 bytes':
|
|
29
|
+
'CID 格式不符合 MostBox 要求,请确认分享链接完整。',
|
|
30
|
+
'filename is required':
|
|
31
|
+
'链接缺少 filename 参数,请复制完整分享链接后再检测。',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getDownloadCheckErrorMessageFromPayload(
|
|
35
|
+
data = {},
|
|
36
|
+
errorName = ''
|
|
37
|
+
) {
|
|
38
|
+
if (errorName === 'TimeoutError') return DOWNLOAD_CHECK_MESSAGES.timeout
|
|
39
|
+
if (!data.status) return DOWNLOAD_CHECK_MESSAGES.offline
|
|
40
|
+
if (data.status === 404) return DOWNLOAD_CHECK_MESSAGES.missingApi
|
|
41
|
+
|
|
42
|
+
switch (data.code) {
|
|
43
|
+
case 'VALIDATION_ERROR':
|
|
44
|
+
return DOWNLOAD_CHECK_MESSAGES.validation
|
|
45
|
+
case 'CONFLICT':
|
|
46
|
+
return data.error
|
|
47
|
+
? `${data.error},请先处理同名文件后再下载。`
|
|
48
|
+
: DOWNLOAD_CHECK_MESSAGES.nameConflict
|
|
49
|
+
case 'PEER_NOT_FOUND':
|
|
50
|
+
return DOWNLOAD_CHECK_MESSAGES.noPeer
|
|
51
|
+
case 'PERMISSION_ERROR':
|
|
52
|
+
return data.error
|
|
53
|
+
? `下载目录不可写:${data.error}`
|
|
54
|
+
: DOWNLOAD_CHECK_MESSAGES.permission
|
|
55
|
+
case 'ENGINE_NOT_INITIALIZED':
|
|
56
|
+
return DOWNLOAD_CHECK_MESSAGES.starting
|
|
57
|
+
default:
|
|
58
|
+
break
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (data.status === 503) return DOWNLOAD_CHECK_MESSAGES.noPeer
|
|
62
|
+
if (data.status >= 500) return DOWNLOAD_CHECK_MESSAGES.server
|
|
63
|
+
return data.error
|
|
64
|
+
? `检测未通过:${data.error}`
|
|
65
|
+
: DOWNLOAD_CHECK_MESSAGES.fallback
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getDownloadLinkValidationMessage(link = '') {
|
|
69
|
+
const value = String(link || '').trim()
|
|
70
|
+
if (!value) return '请先粘贴 most:// 分享链接。'
|
|
71
|
+
|
|
72
|
+
const result = parseMostLink(value)
|
|
73
|
+
if (!result.error) {
|
|
74
|
+
return result.fileName?.trim()
|
|
75
|
+
? null
|
|
76
|
+
: LINK_VALIDATION_MESSAGES['filename is required']
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (result.error.startsWith('Unsupported query parameter: ')) {
|
|
80
|
+
const unsupportedParam = result.error.slice(
|
|
81
|
+
'Unsupported query parameter: '.length
|
|
82
|
+
)
|
|
83
|
+
return `链接包含暂不支持的参数 ${unsupportedParam},请只保留 filename。`
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
LINK_VALIDATION_MESSAGES[result.error] || DOWNLOAD_CHECK_MESSAGES.validation
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -58,6 +58,20 @@ export class PermissionError extends AppError {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
export class ConflictError extends AppError {
|
|
62
|
+
constructor(message = 'Resource conflict') {
|
|
63
|
+
super(message, 'CONFLICT')
|
|
64
|
+
this.name = 'ConflictError'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class StorageCapacityError extends AppError {
|
|
69
|
+
constructor(message = 'Storage capacity exceeded') {
|
|
70
|
+
super(message, 'STORAGE_CAPACITY_ERROR')
|
|
71
|
+
this.name = 'StorageCapacityError'
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
61
75
|
export class EngineNotInitializedError extends AppError {
|
|
62
76
|
constructor(message = 'Engine not initialized. Call start() first.') {
|
|
63
77
|
super(message, 'ENGINE_NOT_INITIALIZED')
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
decodeBase64,
|
|
3
|
+
encodeBase64,
|
|
2
4
|
pbkdf2,
|
|
3
5
|
sha256,
|
|
4
6
|
getBytes,
|
|
@@ -10,8 +12,15 @@ import {
|
|
|
10
12
|
import nacl from 'tweetnacl'
|
|
11
13
|
|
|
12
14
|
const SALT_PREFIX = '/most.box/'
|
|
13
|
-
const PBKDF2_ITERATIONS =
|
|
15
|
+
const PBKDF2_ITERATIONS = 50_000
|
|
14
16
|
const PBKDF2_KEY_LENGTH = 32
|
|
17
|
+
const BOX_TOKEN_VERSION = 1
|
|
18
|
+
const BOX_TIMESTAMP_BYTES = 8
|
|
19
|
+
const BOX_TOKEN_HEADER_BYTES =
|
|
20
|
+
1 + BOX_TIMESTAMP_BYTES + nacl.secretbox.nonceLength
|
|
21
|
+
const BOX_TOKEN_MIN_BYTES =
|
|
22
|
+
BOX_TOKEN_HEADER_BYTES + nacl.secretbox.overheadLength
|
|
23
|
+
const BOX_LABEL = new TextEncoder().encode('MP-AE')
|
|
15
24
|
|
|
16
25
|
export function mostWallet(username, password) {
|
|
17
26
|
const salt = toUtf8Bytes(SALT_PREFIX + username)
|
|
@@ -40,3 +49,178 @@ export function most25519(danger) {
|
|
|
40
49
|
ed_public_key: hexlify(ed25519KeyPair.publicKey),
|
|
41
50
|
}
|
|
42
51
|
}
|
|
52
|
+
|
|
53
|
+
export async function mostSignMessage(danger, message) {
|
|
54
|
+
const account = HDNodeWallet.fromPhrase(mostMnemonic(danger))
|
|
55
|
+
return {
|
|
56
|
+
address: account.address,
|
|
57
|
+
signature: await account.signMessage(message),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function mostEncode(text, danger) {
|
|
62
|
+
const bytes = new TextEncoder().encode(text)
|
|
63
|
+
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength)
|
|
64
|
+
const key = getBytes(danger).slice(0, nacl.secretbox.keyLength)
|
|
65
|
+
const encrypted = nacl.secretbox(bytes, nonce, key)
|
|
66
|
+
|
|
67
|
+
return ['mp://1', encodeBase64(nonce), encodeBase64(encrypted)].join('.')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function mostDecode(data, danger) {
|
|
71
|
+
const [prefix, nonce64, encrypted64] = String(data || '').split('.')
|
|
72
|
+
if (prefix !== 'mp://1' || !nonce64 || !encrypted64) return ''
|
|
73
|
+
|
|
74
|
+
const key = getBytes(danger).slice(0, nacl.secretbox.keyLength)
|
|
75
|
+
const decrypted = nacl.secretbox.open(
|
|
76
|
+
decodeBase64(encrypted64),
|
|
77
|
+
decodeBase64(nonce64),
|
|
78
|
+
key
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return decrypted ? new TextDecoder().decode(decrypted) : ''
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function concatBytes(parts) {
|
|
85
|
+
const total = parts.reduce((sum, part) => sum + part.length, 0)
|
|
86
|
+
const output = new Uint8Array(total)
|
|
87
|
+
let offset = 0
|
|
88
|
+
for (const part of parts) {
|
|
89
|
+
output.set(part, offset)
|
|
90
|
+
offset += part.length
|
|
91
|
+
}
|
|
92
|
+
return output
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function encodeBase64Url(bytes) {
|
|
96
|
+
return encodeBase64(bytes)
|
|
97
|
+
.replaceAll('+', '-')
|
|
98
|
+
.replaceAll('/', '_')
|
|
99
|
+
.replaceAll('=', '')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function decodeBase64Url(value) {
|
|
103
|
+
const token = String(value || '').trim()
|
|
104
|
+
if (!token || !/^[A-Za-z0-9_-]+$/.test(token)) return null
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const standard = token
|
|
108
|
+
.replaceAll('-', '+')
|
|
109
|
+
.replaceAll('_', '/')
|
|
110
|
+
.padEnd(Math.ceil(token.length / 4) * 4, '=')
|
|
111
|
+
return decodeBase64(standard)
|
|
112
|
+
} catch {
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function encodeTimestampMs(value) {
|
|
118
|
+
const output = new Uint8Array(BOX_TIMESTAMP_BYTES)
|
|
119
|
+
const view = new DataView(output.buffer)
|
|
120
|
+
view.setBigUint64(0, BigInt(value), false)
|
|
121
|
+
return output
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function decodeTimestampMs(bytes) {
|
|
125
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
126
|
+
return Number(view.getBigUint64(0, false))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function readBoxTokenPayload(data) {
|
|
130
|
+
const payload = decodeBase64Url(data)
|
|
131
|
+
if (
|
|
132
|
+
!payload ||
|
|
133
|
+
payload.length < BOX_TOKEN_MIN_BYTES ||
|
|
134
|
+
payload[0] !== BOX_TOKEN_VERSION
|
|
135
|
+
) {
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const timestampBytes = payload.slice(1, 1 + BOX_TIMESTAMP_BYTES)
|
|
140
|
+
const nonce = payload.slice(1 + BOX_TIMESTAMP_BYTES, BOX_TOKEN_HEADER_BYTES)
|
|
141
|
+
const encrypted = payload.slice(BOX_TOKEN_HEADER_BYTES)
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
version: payload[0],
|
|
145
|
+
timestampMs: decodeTimestampMs(timestampBytes),
|
|
146
|
+
nonce,
|
|
147
|
+
encrypted,
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function deriveDirectionalBoxKey(
|
|
152
|
+
senderPublicKey,
|
|
153
|
+
recipientPublicKey,
|
|
154
|
+
sharedKey
|
|
155
|
+
) {
|
|
156
|
+
return nacl
|
|
157
|
+
.hash(
|
|
158
|
+
concatBytes([BOX_LABEL, senderPublicKey, recipientPublicKey, sharedKey])
|
|
159
|
+
)
|
|
160
|
+
.slice(0, nacl.secretbox.keyLength)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function mostBoxEncrypt(text, { senderPrivateKey, recipientPublicKey }) {
|
|
164
|
+
const senderSecretKey = getBytes(senderPrivateKey)
|
|
165
|
+
const senderPublicKey =
|
|
166
|
+
nacl.box.keyPair.fromSecretKey(senderSecretKey).publicKey
|
|
167
|
+
const recipientPublicKeyBytes = getBytes(recipientPublicKey)
|
|
168
|
+
const sharedKey = nacl.box.before(recipientPublicKeyBytes, senderSecretKey)
|
|
169
|
+
const boxKey = deriveDirectionalBoxKey(
|
|
170
|
+
senderPublicKey,
|
|
171
|
+
recipientPublicKeyBytes,
|
|
172
|
+
sharedKey
|
|
173
|
+
)
|
|
174
|
+
const nonce = nacl.randomBytes(nacl.secretbox.nonceLength)
|
|
175
|
+
const encrypted = nacl.secretbox(
|
|
176
|
+
new TextEncoder().encode(text),
|
|
177
|
+
nonce,
|
|
178
|
+
boxKey
|
|
179
|
+
)
|
|
180
|
+
const payload = concatBytes([
|
|
181
|
+
new Uint8Array([BOX_TOKEN_VERSION]),
|
|
182
|
+
encodeTimestampMs(Date.now()),
|
|
183
|
+
nonce,
|
|
184
|
+
encrypted,
|
|
185
|
+
])
|
|
186
|
+
|
|
187
|
+
return encodeBase64Url(payload)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function parseMostBoxToken(data) {
|
|
191
|
+
const payload = readBoxTokenPayload(data)
|
|
192
|
+
if (!payload) return null
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
version: payload.version,
|
|
196
|
+
timestampMs: payload.timestampMs,
|
|
197
|
+
nonce: encodeBase64Url(payload.nonce),
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function mostBoxDecrypt(data, { senderPublicKey, recipientPrivateKey }) {
|
|
202
|
+
const payload = readBoxTokenPayload(data)
|
|
203
|
+
if (!payload) return ''
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const recipientSecretKey = getBytes(recipientPrivateKey)
|
|
207
|
+
const recipientPublicKey =
|
|
208
|
+
nacl.box.keyPair.fromSecretKey(recipientSecretKey).publicKey
|
|
209
|
+
const senderPublicKeyBytes = getBytes(senderPublicKey)
|
|
210
|
+
const sharedKey = nacl.box.before(senderPublicKeyBytes, recipientSecretKey)
|
|
211
|
+
const boxKey = deriveDirectionalBoxKey(
|
|
212
|
+
senderPublicKeyBytes,
|
|
213
|
+
recipientPublicKey,
|
|
214
|
+
sharedKey
|
|
215
|
+
)
|
|
216
|
+
const decrypted = nacl.secretbox.open(
|
|
217
|
+
payload.encrypted,
|
|
218
|
+
payload.nonce,
|
|
219
|
+
boxKey
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
return decrypted ? new TextDecoder().decode(decrypted) : ''
|
|
223
|
+
} catch {
|
|
224
|
+
return ''
|
|
225
|
+
}
|
|
226
|
+
}
|
package/server/src/utils/mp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getBytes } from 'ethers'
|
|
2
|
-
import dayjs from 'dayjs'
|
|
3
2
|
import nacl from 'tweetnacl'
|
|
4
3
|
import { generateAvatar } from './avatar.js'
|
|
4
|
+
import { formatTime } from './dateTime.js'
|
|
5
5
|
|
|
6
6
|
const BASE36_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'
|
|
7
7
|
|
|
@@ -77,29 +77,5 @@ export const getIPNS = (private_key, ed_public_key) => {
|
|
|
77
77
|
return 'k' + baseXEncode(cidBytes, BASE36_ALPHABET)
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
export const formatTime = time => {
|
|
81
|
-
if (!time) return ''
|
|
82
|
-
const date = dayjs(Number(time))
|
|
83
|
-
const hour = date.hour()
|
|
84
|
-
let timeOfDay
|
|
85
|
-
if (hour >= 0 && hour < 3) {
|
|
86
|
-
timeOfDay = '凌晨'
|
|
87
|
-
} else if (hour >= 3 && hour < 6) {
|
|
88
|
-
timeOfDay = '拂晓'
|
|
89
|
-
} else if (hour >= 6 && hour < 9) {
|
|
90
|
-
timeOfDay = '早晨'
|
|
91
|
-
} else if (hour >= 9 && hour < 12) {
|
|
92
|
-
timeOfDay = '上午'
|
|
93
|
-
} else if (hour >= 12 && hour < 15) {
|
|
94
|
-
timeOfDay = '下午'
|
|
95
|
-
} else if (hour >= 15 && hour < 18) {
|
|
96
|
-
timeOfDay = '傍晚'
|
|
97
|
-
} else if (hour >= 18 && hour < 21) {
|
|
98
|
-
timeOfDay = '薄暮'
|
|
99
|
-
} else {
|
|
100
|
-
timeOfDay = '深夜'
|
|
101
|
-
}
|
|
102
|
-
return date.format(`YYYY年M月D日 ${timeOfDay}h点`)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
80
|
export const avatar = generateAvatar
|
|
81
|
+
export { formatTime }
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { mostDecode, mostEncode, mostSignMessage } from './mostWallet.js'
|
|
2
|
+
import { calculateNoteCid } from './noteUtils.js'
|
|
3
|
+
|
|
4
|
+
export const NOTE_BACKUP_API_URL = 'https://api.most.box/auth/backup'
|
|
5
|
+
|
|
6
|
+
async function readBackupApiError(response, fallback) {
|
|
7
|
+
const data = await response
|
|
8
|
+
.clone()
|
|
9
|
+
.json()
|
|
10
|
+
.catch(() => null)
|
|
11
|
+
return data?.error || fallback
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function encryptNotesBackup(notes, danger) {
|
|
15
|
+
return mostEncode(JSON.stringify({ notes: notes || [] }), danger)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function decryptNotesBackup(content, danger) {
|
|
19
|
+
if (!String(content || '').startsWith('mp://1')) {
|
|
20
|
+
throw new Error('无效的备份数据格式')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const decrypted = mostDecode(content, danger)
|
|
24
|
+
if (!decrypted) {
|
|
25
|
+
throw new Error('解密失败,请确认当前 Web3 登录账号正确')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const data = JSON.parse(decrypted)
|
|
29
|
+
if (!Array.isArray(data.notes)) {
|
|
30
|
+
throw new Error('备份数据缺少 notes')
|
|
31
|
+
}
|
|
32
|
+
return data
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function calculateNotesBackupCid(notes) {
|
|
36
|
+
return calculateNoteCid(JSON.stringify({ notes: notes || [] }))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function getBackupAuthHeaders(
|
|
40
|
+
wallet,
|
|
41
|
+
method,
|
|
42
|
+
url = NOTE_BACKUP_API_URL
|
|
43
|
+
) {
|
|
44
|
+
const timestamp = Date.now().toString()
|
|
45
|
+
const path = new URL(url).pathname
|
|
46
|
+
const message = `${timestamp}:${String(method).toUpperCase()}:${path}`
|
|
47
|
+
const { address, signature } = await mostSignMessage(wallet.danger, message)
|
|
48
|
+
return {
|
|
49
|
+
Authorization: `${address},${timestamp},${signature}`,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function buildNotesBackupUpload(wallet, notes) {
|
|
54
|
+
const payload = JSON.stringify({ notes: notes || [] })
|
|
55
|
+
const cid = await calculateNoteCid(payload)
|
|
56
|
+
const encrypted = encryptNotesBackup(notes, wallet.danger)
|
|
57
|
+
return {
|
|
58
|
+
cid,
|
|
59
|
+
body: encrypted,
|
|
60
|
+
headers: {
|
|
61
|
+
'Content-Type': 'text/plain',
|
|
62
|
+
'x-backup-cid': cid,
|
|
63
|
+
...(await getBackupAuthHeaders(wallet, 'PUT', NOTE_BACKUP_API_URL)),
|
|
64
|
+
},
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function uploadNotesBackup(
|
|
69
|
+
wallet,
|
|
70
|
+
notes,
|
|
71
|
+
url = NOTE_BACKUP_API_URL
|
|
72
|
+
) {
|
|
73
|
+
const upload = await buildNotesBackupUpload(wallet, notes)
|
|
74
|
+
const response = await fetch(url, {
|
|
75
|
+
method: 'PUT',
|
|
76
|
+
headers: upload.headers,
|
|
77
|
+
body: upload.body,
|
|
78
|
+
})
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw new Error(await readBackupApiError(response, '云备份失败'))
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
cid: upload.cid,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function downloadNotesBackup(wallet, url = NOTE_BACKUP_API_URL) {
|
|
88
|
+
const response = await fetch(url, {
|
|
89
|
+
method: 'GET',
|
|
90
|
+
headers: await getBackupAuthHeaders(wallet, 'GET', url),
|
|
91
|
+
})
|
|
92
|
+
if (response.status === 404) {
|
|
93
|
+
return {
|
|
94
|
+
found: false,
|
|
95
|
+
cid: '',
|
|
96
|
+
time: 0,
|
|
97
|
+
notes: [],
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
throw new Error(await readBackupApiError(response, '云端恢复失败'))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const encrypted = await response.text()
|
|
105
|
+
if (!encrypted) {
|
|
106
|
+
throw new Error('云端无备份数据')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const data = decryptNotesBackup(encrypted, wallet.danger)
|
|
110
|
+
return {
|
|
111
|
+
found: true,
|
|
112
|
+
cid: response.headers.get('x-backup-cid') || '',
|
|
113
|
+
time: Number(response.headers.get('x-backup-time') || 0),
|
|
114
|
+
notes: data.notes,
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { CID } from 'multiformats/cid'
|
|
2
|
+
import * as raw from 'multiformats/codecs/raw'
|
|
3
|
+
import { sha256 } from 'multiformats/hashes/sha2'
|
|
4
|
+
|
|
5
|
+
export function normalizeNotePath(input = '') {
|
|
6
|
+
return String(input)
|
|
7
|
+
.replace(/\\/g, '/')
|
|
8
|
+
.split('/')
|
|
9
|
+
.map(part => part.trim())
|
|
10
|
+
.filter(part => part && part !== '.')
|
|
11
|
+
.filter(part => part !== '..')
|
|
12
|
+
.join('/')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getNoteFullPath(note) {
|
|
16
|
+
const path = normalizeNotePath(note?.path || '')
|
|
17
|
+
const name = String(note?.name || '').trim()
|
|
18
|
+
return path ? `${path}/${name}` : name
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function validateNoteName(name) {
|
|
22
|
+
const value = String(name || '').trim()
|
|
23
|
+
if (!value) return { valid: false, error: '请输入名称' }
|
|
24
|
+
if (value.includes('/')) return { valid: false, error: '名称不能包含 /' }
|
|
25
|
+
if (value.includes('\\')) return { valid: false, error: '名称不能包含 \\' }
|
|
26
|
+
return { valid: true, name: value }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function calculateNoteCid(content = '') {
|
|
30
|
+
const bytes = new TextEncoder().encode(String(content))
|
|
31
|
+
const hash = await sha256.digest(bytes)
|
|
32
|
+
return CID.create(1, raw.code, hash).toString()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function filterNotesByPath(notes, currentPath = '', query = '') {
|
|
36
|
+
const normalizedCurrentPath = normalizeNotePath(currentPath)
|
|
37
|
+
const normalizedQuery = query.trim().toLowerCase()
|
|
38
|
+
const files = Array.isArray(notes) ? notes : []
|
|
39
|
+
|
|
40
|
+
if (normalizedQuery) {
|
|
41
|
+
return files
|
|
42
|
+
.filter(note =>
|
|
43
|
+
String(note.name || '')
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.includes(normalizedQuery)
|
|
46
|
+
)
|
|
47
|
+
.sort(sortNotesForExplorer)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const directItems = []
|
|
51
|
+
const inferredDirs = new Map()
|
|
52
|
+
|
|
53
|
+
for (const note of files) {
|
|
54
|
+
const notePath = normalizeNotePath(note.path || '')
|
|
55
|
+
|
|
56
|
+
if (notePath === normalizedCurrentPath) {
|
|
57
|
+
directItems.push(note)
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
normalizedCurrentPath === '' ||
|
|
63
|
+
notePath.startsWith(`${normalizedCurrentPath}/`)
|
|
64
|
+
) {
|
|
65
|
+
const relativePath =
|
|
66
|
+
normalizedCurrentPath === ''
|
|
67
|
+
? notePath
|
|
68
|
+
: notePath.slice(normalizedCurrentPath.length + 1)
|
|
69
|
+
const firstSegment = relativePath.split('/').filter(Boolean)[0]
|
|
70
|
+
|
|
71
|
+
if (firstSegment && !inferredDirs.has(firstSegment)) {
|
|
72
|
+
inferredDirs.set(firstSegment, {
|
|
73
|
+
name: firstSegment,
|
|
74
|
+
type: 'directory',
|
|
75
|
+
path: normalizedCurrentPath,
|
|
76
|
+
size: 0,
|
|
77
|
+
cid: `__dir__${normalizedCurrentPath}/${firstSegment}`,
|
|
78
|
+
created_at: note.created_at || Date.now(),
|
|
79
|
+
updated_at: note.updated_at || note.created_at || Date.now(),
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return [...inferredDirs.values(), ...directItems].sort(sortNotesForExplorer)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function renameNotesByPath(notes, oldFullPath, targetPath, targetName) {
|
|
89
|
+
const oldPath = normalizeNotePath(oldFullPath)
|
|
90
|
+
const newPath = normalizeNotePath(targetPath)
|
|
91
|
+
const cleanName = String(targetName || '').trim()
|
|
92
|
+
const targetFullPath = normalizeNotePath(
|
|
93
|
+
newPath ? `${newPath}/${cleanName}` : cleanName
|
|
94
|
+
)
|
|
95
|
+
const now = Date.now()
|
|
96
|
+
|
|
97
|
+
return notes.map(note => {
|
|
98
|
+
const fullPath = getNoteFullPath(note)
|
|
99
|
+
|
|
100
|
+
if (fullPath === oldPath) {
|
|
101
|
+
return { ...note, path: newPath, name: cleanName, updated_at: now }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (fullPath.startsWith(`${oldPath}/`)) {
|
|
105
|
+
const relativePath = fullPath.slice(oldPath.length + 1)
|
|
106
|
+
const nextFullPath = normalizeNotePath(
|
|
107
|
+
`${targetFullPath}/${relativePath}`
|
|
108
|
+
)
|
|
109
|
+
const lastSlash = nextFullPath.lastIndexOf('/')
|
|
110
|
+
return {
|
|
111
|
+
...note,
|
|
112
|
+
path: lastSlash === -1 ? '' : nextFullPath.slice(0, lastSlash),
|
|
113
|
+
name: nextFullPath.slice(lastSlash + 1),
|
|
114
|
+
updated_at: now,
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return note
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function sortNotesForExplorer(a, b) {
|
|
123
|
+
if (a.type === 'directory' && b.type !== 'directory') return -1
|
|
124
|
+
if (a.type !== 'directory' && b.type === 'directory') return 1
|
|
125
|
+
return (
|
|
126
|
+
(b.updated_at || b.created_at || 0) - (a.updated_at || a.created_at || 0)
|
|
127
|
+
)
|
|
128
|
+
}
|