mongoku 2.0.1 → 2.0.3
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/Dockerfile +27 -0
- package/README.md +8 -0
- package/build/client/_app/immutable/assets/0.BHsXFfsc.css.gz +0 -0
- package/build/client/_app/immutable/assets/PrettyJson.CtngcfyW.css.gz +0 -0
- package/build/client/_app/immutable/chunks/{W-qL5_8w.js → BAM9w9EL.js} +1 -1
- package/build/client/_app/immutable/chunks/BAM9w9EL.js.br +0 -0
- package/build/client/_app/immutable/chunks/BAM9w9EL.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BKCvWebR.js +23 -0
- package/build/client/_app/immutable/chunks/BKCvWebR.js.br +0 -0
- package/build/client/_app/immutable/chunks/BKCvWebR.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Dbyirw23.js → BMa204Dm.js} +3 -3
- package/build/client/_app/immutable/chunks/BMa204Dm.js.br +0 -0
- package/build/client/_app/immutable/chunks/BMa204Dm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BN_N9-2s.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BY57TmdO.js +1 -0
- package/build/client/_app/immutable/chunks/BY57TmdO.js.br +0 -0
- package/build/client/_app/immutable/chunks/BY57TmdO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BaB-cr9N.js +2 -0
- package/build/client/_app/immutable/chunks/BaB-cr9N.js.br +0 -0
- package/build/client/_app/immutable/chunks/BaB-cr9N.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BdR-m9Ad.js +1 -0
- package/build/client/_app/immutable/chunks/BdR-m9Ad.js.br +0 -0
- package/build/client/_app/immutable/chunks/BdR-m9Ad.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ByjLCPv2.js +1 -0
- package/build/client/_app/immutable/chunks/ByjLCPv2.js.br +0 -0
- package/build/client/_app/immutable/chunks/ByjLCPv2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BzAcxkRZ.js +4 -0
- package/build/client/_app/immutable/chunks/BzAcxkRZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/BzAcxkRZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CyQLXPZI.js +2 -0
- package/build/client/_app/immutable/chunks/CyQLXPZI.js.br +0 -0
- package/build/client/_app/immutable/chunks/CyQLXPZI.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D17Lj1H7.js +1 -0
- package/build/client/_app/immutable/chunks/D17Lj1H7.js.br +0 -0
- package/build/client/_app/immutable/chunks/D17Lj1H7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D4VhtiDg.js +1 -0
- package/build/client/_app/immutable/chunks/D4VhtiDg.js.br +0 -0
- package/build/client/_app/immutable/chunks/D4VhtiDg.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D72Du7TF.js +1 -0
- package/build/client/_app/immutable/chunks/D72Du7TF.js.br +2 -0
- package/build/client/_app/immutable/chunks/D72Du7TF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DZSJW-Zm.js → XYFbSe2V.js} +1 -1
- package/build/client/_app/immutable/chunks/XYFbSe2V.js.br +0 -0
- package/build/client/_app/immutable/chunks/XYFbSe2V.js.gz +0 -0
- package/build/client/_app/immutable/chunks/iHsChU5o.js +1 -0
- package/build/client/_app/immutable/chunks/iHsChU5o.js.br +0 -0
- package/build/client/_app/immutable/chunks/iHsChU5o.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DzSHMQR7.js → p23QjN60.js} +1 -1
- package/build/client/_app/immutable/chunks/p23QjN60.js.br +2 -0
- package/build/client/_app/immutable/chunks/p23QjN60.js.gz +0 -0
- package/build/client/_app/immutable/chunks/sBKW2MYH.js +1 -0
- package/build/client/_app/immutable/chunks/sBKW2MYH.js.br +0 -0
- package/build/client/_app/immutable/chunks/sBKW2MYH.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BEuO9qGi.js → uMNMODvc.js} +1 -1
- package/build/client/_app/immutable/chunks/uMNMODvc.js.br +0 -0
- package/build/client/_app/immutable/chunks/uMNMODvc.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.9nC_873E.js +2 -0
- package/build/client/_app/immutable/entry/app.9nC_873E.js.br +0 -0
- package/build/client/_app/immutable/entry/app.9nC_873E.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.Bn88Alw2.js +1 -0
- package/build/client/_app/immutable/entry/start.Bn88Alw2.js.br +2 -0
- package/build/client/_app/immutable/entry/start.Bn88Alw2.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.B2lBDs71.js → 0.COxTCtn2.js} +1 -1
- package/build/client/_app/immutable/nodes/0.COxTCtn2.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.COxTCtn2.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.Bc8yPK_D.js +1 -0
- package/build/client/_app/immutable/nodes/1.Bc8yPK_D.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.Bc8yPK_D.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.CI2GcqTf.js +1 -0
- package/build/client/_app/immutable/nodes/3.CI2GcqTf.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.CI2GcqTf.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{4.BqBphmVX.js → 4.ChSdW7ac.js} +1 -1
- package/build/client/_app/immutable/nodes/4.ChSdW7ac.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.ChSdW7ac.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{5.BkAWRgl4.js → 5.DaMML2go.js} +1 -1
- package/build/client/_app/immutable/nodes/5.DaMML2go.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DaMML2go.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.Dcq0qwvO.js +1 -0
- package/build/client/_app/immutable/nodes/6.Dcq0qwvO.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.Dcq0qwvO.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.CU-ncPes.js +1 -0
- package/build/client/_app/immutable/nodes/7.CU-ncPes.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.CU-ncPes.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/server/chunks/{0-BGoFMRn2.js → 0-C1NyHW8A.js} +3 -3
- package/build/server/chunks/{0-BGoFMRn2.js.map → 0-C1NyHW8A.js.map} +1 -1
- package/build/server/chunks/1-CThf4W5r.js +9 -0
- package/build/server/chunks/{1-Ch0AY58K.js.map → 1-CThf4W5r.js.map} +1 -1
- package/build/server/chunks/{3-Bmk9Qof9.js → 3-CJf0NbiV.js} +3 -3
- package/build/server/chunks/{3-Bmk9Qof9.js.map → 3-CJf0NbiV.js.map} +1 -1
- package/build/server/chunks/{4-DnpScx2e.js → 4-Dfbpsagm.js} +3 -3
- package/build/server/chunks/{4-DnpScx2e.js.map → 4-Dfbpsagm.js.map} +1 -1
- package/build/server/chunks/{5-Bw8bTTBj.js → 5-DLB6GOjf.js} +3 -3
- package/build/server/chunks/{5-Bw8bTTBj.js.map → 5-DLB6GOjf.js.map} +1 -1
- package/build/server/chunks/{6-D8zNoyW_.js → 6-DfCARDKO.js} +10 -5
- package/build/server/chunks/6-DfCARDKO.js.map +1 -0
- package/build/server/chunks/{7-Cim4qVL3.js → 7-B5o4OymX.js} +3 -3
- package/build/server/chunks/{7-Cim4qVL3.js.map → 7-B5o4OymX.js.map} +1 -1
- package/build/server/chunks/{Panel-DTdYk-gk.js → Panel-Box8ld78.js} +3 -3
- package/build/server/chunks/Panel-Box8ld78.js.map +1 -0
- package/build/server/chunks/{PrettyJson-wIuHvehp.js → PrettyJson-C2c9xGNo.js} +5 -5
- package/build/server/chunks/PrettyJson-C2c9xGNo.js.map +1 -0
- package/build/server/chunks/{TooltipTable-J278e1CA.js → TooltipTable-0z4HkHlH.js} +3 -1
- package/build/server/chunks/TooltipTable-0z4HkHlH.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-D7RD9tLN.js → _layout.svelte-CU7AVRSZ.js} +14 -11
- package/build/server/chunks/_layout.svelte-CU7AVRSZ.js.map +1 -0
- package/build/server/chunks/{_page.svelte-4PYYtPii.js → _page.svelte-B_T8EFk3.js} +7 -6
- package/build/server/chunks/_page.svelte-B_T8EFk3.js.map +1 -0
- package/build/server/chunks/{_page.svelte-Be5VTn1e.js → _page.svelte-CflJkclc.js} +7 -6
- package/build/server/chunks/_page.svelte-CflJkclc.js.map +1 -0
- package/build/server/chunks/{_page.svelte-DZ59Sr3N.js → _page.svelte-D1QIZqLv.js} +65 -42
- package/build/server/chunks/_page.svelte-D1QIZqLv.js.map +1 -0
- package/build/server/chunks/{_page.svelte-DIPUjSjm.js → _page.svelte-PuIVQ52p.js} +20 -8
- package/build/server/chunks/_page.svelte-PuIVQ52p.js.map +1 -0
- package/build/server/chunks/_page.svelte-zlHeUnJK.js +101 -0
- package/build/server/chunks/_page.svelte-zlHeUnJK.js.map +1 -0
- package/build/server/chunks/{context-BU6gUbSI.js → async-BfR0vYHo.js} +8 -46
- package/build/server/chunks/async-BfR0vYHo.js.map +1 -0
- package/build/server/chunks/client-bj7sePQc.js +7 -0
- package/build/server/chunks/{client-BZtxOgbR.js.map → client-bj7sePQc.js.map} +1 -1
- package/build/server/chunks/{client2-LilSGYJA.js → client2-BX4-xbM4.js} +6 -5
- package/build/server/chunks/{client2-LilSGYJA.js.map → client2-BX4-xbM4.js.map} +1 -1
- package/build/server/chunks/error.svelte-D5sIJv3P.js +18 -0
- package/build/server/chunks/error.svelte-D5sIJv3P.js.map +1 -0
- package/build/server/chunks/event-DVH-6ISX.js +64 -0
- package/build/server/chunks/event-DVH-6ISX.js.map +1 -0
- package/build/server/chunks/{exports-DiyTWREe.js → exports-Cv9LZeD1.js} +2 -39
- package/build/server/chunks/exports-Cv9LZeD1.js.map +1 -0
- package/build/server/chunks/hooks.server-_TKfdzGA.js +26 -0
- package/build/server/chunks/hooks.server-_TKfdzGA.js.map +1 -0
- package/build/server/chunks/{index-XFKuhGxM.js → index-EKt9-9bV.js} +2 -2
- package/build/server/chunks/{index-XFKuhGxM.js.map → index-EKt9-9bV.js.map} +1 -1
- package/build/server/chunks/{index3-CUhdJ7wv.js → index3-DquTjlmp.js} +3 -3
- package/build/server/chunks/index3-DquTjlmp.js.map +1 -0
- package/build/server/chunks/{notifications.svelte-CrTIrfvL.js → notifications.svelte-CIqkoPWX.js} +6 -12
- package/build/server/chunks/notifications.svelte-CIqkoPWX.js.map +1 -0
- package/build/server/chunks/remote-xxtqbu-BFzaWXy0.js +234 -0
- package/build/server/chunks/remote-xxtqbu-BFzaWXy0.js.map +1 -0
- package/build/server/chunks/{routing-zIlQn_LN.js → routing-Ddj5b-2K.js} +25 -4
- package/build/server/chunks/routing-Ddj5b-2K.js.map +1 -0
- package/build/server/chunks/{server2-DfQrrcsk.js → server2-BOoUfKd7.js} +3 -3
- package/build/server/chunks/{server2-DfQrrcsk.js.map → server2-BOoUfKd7.js.map} +1 -1
- package/build/server/chunks/{state.svelte-s-CFCWQX.js → state.svelte-CRHA9yov.js} +3 -2
- package/build/server/chunks/{state.svelte-s-CFCWQX.js.map → state.svelte-CRHA9yov.js.map} +1 -1
- package/build/server/chunks/utils-DGYJFmnf.js +39 -0
- package/build/server/chunks/utils-DGYJFmnf.js.map +1 -0
- package/build/server/index.js +71 -24
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +9 -30
- package/build/server/manifest.js.map +1 -1
- package/cli.ts +148 -0
- package/dist/cli.js +3 -4
- package/ecosystem.config.js +9 -0
- package/package.json +88 -81
- package/src/api/servers.remote.ts +98 -0
- package/src/app.css +228 -0
- package/src/app.d.ts +16 -0
- package/src/app.html +11 -0
- package/src/hooks.server.ts +34 -0
- package/src/lib/components/Breadcrumbs.svelte +133 -0
- package/src/lib/components/JsonValue.svelte +248 -0
- package/src/lib/components/Notifications.svelte +81 -0
- package/src/lib/components/Panel.svelte +37 -0
- package/src/lib/components/PrettyJson.svelte +187 -0
- package/src/lib/components/SearchBox.svelte +160 -0
- package/src/lib/components/TooltipTable.svelte +137 -0
- package/src/lib/server/HostsManager.ts +105 -0
- package/src/lib/server/JsonEncoder.ts +62 -0
- package/src/lib/server/mongo.ts +199 -0
- package/src/lib/stores/notifications.svelte.ts +45 -0
- package/src/lib/types.ts +56 -0
- package/src/lib/utils/filters.ts +25 -0
- package/src/lib/utils/jsonParser.ts +125 -0
- package/src/routes/+layout.server.ts +7 -0
- package/src/routes/+layout.svelte +27 -0
- package/src/routes/+page.server.ts +6 -0
- package/src/routes/servers/+page.server.ts +53 -0
- package/src/routes/servers/+page.svelte +196 -0
- package/src/routes/servers/[server]/databases/+page.server.ts +47 -0
- package/src/routes/servers/[server]/databases/+page.svelte +88 -0
- package/src/routes/servers/[server]/databases/[database]/collections/+page.server.ts +21 -0
- package/src/routes/servers/[server]/databases/[database]/collections/+page.svelte +110 -0
- package/src/routes/servers/[server]/databases/[database]/collections/[collection]/+page.server.ts +106 -0
- package/src/routes/servers/[server]/databases/[database]/collections/[collection]/+page.svelte +174 -0
- package/src/routes/servers/[server]/databases/[database]/collections/[collection]/documents/[document]/+page.server.ts +25 -0
- package/src/routes/servers/[server]/databases/[database]/collections/[collection]/documents/[document]/+page.svelte +90 -0
- package/src/tests/api/readonly.test.ts +89 -0
- package/src/tests/setup.ts +19 -0
- package/svelte.config.js +28 -0
- package/tsconfig.cli.json +15 -0
- package/tsconfig.json +19 -0
- package/vite.config.ts +7 -0
- package/build/client/_app/immutable/chunks/B-Z1NsR7.js +0 -1
- package/build/client/_app/immutable/chunks/B-Z1NsR7.js.br +0 -0
- package/build/client/_app/immutable/chunks/B-Z1NsR7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BEuO9qGi.js.br +0 -0
- package/build/client/_app/immutable/chunks/BEuO9qGi.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BuIrCFA_.js +0 -1
- package/build/client/_app/immutable/chunks/BuIrCFA_.js.br +0 -0
- package/build/client/_app/immutable/chunks/BuIrCFA_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C9b4uNov.js +0 -1
- package/build/client/_app/immutable/chunks/C9b4uNov.js.br +0 -0
- package/build/client/_app/immutable/chunks/C9b4uNov.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CIYBIyvN.js +0 -1
- package/build/client/_app/immutable/chunks/CIYBIyvN.js.br +0 -0
- package/build/client/_app/immutable/chunks/CIYBIyvN.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CWUhQKKC.js +0 -1
- package/build/client/_app/immutable/chunks/CWUhQKKC.js.br +0 -0
- package/build/client/_app/immutable/chunks/CWUhQKKC.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CrJSDly5.js +0 -2
- package/build/client/_app/immutable/chunks/CrJSDly5.js.br +0 -0
- package/build/client/_app/immutable/chunks/CrJSDly5.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DRy_44ib.js +0 -1
- package/build/client/_app/immutable/chunks/DRy_44ib.js.br +0 -0
- package/build/client/_app/immutable/chunks/DRy_44ib.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DS5yzhLt.js +0 -1
- package/build/client/_app/immutable/chunks/DS5yzhLt.js.br +0 -0
- package/build/client/_app/immutable/chunks/DS5yzhLt.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DZSJW-Zm.js.br +0 -0
- package/build/client/_app/immutable/chunks/DZSJW-Zm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dbyirw23.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dbyirw23.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DsnmJJEf.js +0 -1
- package/build/client/_app/immutable/chunks/DsnmJJEf.js.br +0 -2
- package/build/client/_app/immutable/chunks/DsnmJJEf.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DzSHMQR7.js.br +0 -0
- package/build/client/_app/immutable/chunks/DzSHMQR7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/W-qL5_8w.js.br +0 -0
- package/build/client/_app/immutable/chunks/W-qL5_8w.js.gz +0 -0
- package/build/client/_app/immutable/chunks/aB6hH3ax.js +0 -3
- package/build/client/_app/immutable/chunks/aB6hH3ax.js.br +0 -0
- package/build/client/_app/immutable/chunks/aB6hH3ax.js.gz +0 -0
- package/build/client/_app/immutable/chunks/nJ6DKbk8.js +0 -1
- package/build/client/_app/immutable/chunks/nJ6DKbk8.js.br +0 -0
- package/build/client/_app/immutable/chunks/nJ6DKbk8.js.gz +0 -0
- package/build/client/_app/immutable/chunks/zeTqHWB5.js +0 -2
- package/build/client/_app/immutable/chunks/zeTqHWB5.js.br +0 -0
- package/build/client/_app/immutable/chunks/zeTqHWB5.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CW91YXu8.js +0 -2
- package/build/client/_app/immutable/entry/app.CW91YXu8.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CW91YXu8.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CidwPVgO.js +0 -1
- package/build/client/_app/immutable/entry/start.CidwPVgO.js.br +0 -2
- package/build/client/_app/immutable/entry/start.CidwPVgO.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.B2lBDs71.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.B2lBDs71.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.CueZCvtl.js +0 -1
- package/build/client/_app/immutable/nodes/1.CueZCvtl.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.CueZCvtl.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.alYAlI3j.js +0 -1
- package/build/client/_app/immutable/nodes/3.alYAlI3j.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.alYAlI3j.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.BqBphmVX.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.BqBphmVX.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.BkAWRgl4.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.BkAWRgl4.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.KI4mq5ln.js +0 -1
- package/build/client/_app/immutable/nodes/6.KI4mq5ln.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.KI4mq5ln.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.Brjrw0K8.js +0 -1
- package/build/client/_app/immutable/nodes/7.Brjrw0K8.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.Brjrw0K8.js.gz +0 -0
- package/build/server/chunks/1-Ch0AY58K.js +0 -9
- package/build/server/chunks/6-D8zNoyW_.js.map +0 -1
- package/build/server/chunks/Panel-DTdYk-gk.js.map +0 -1
- package/build/server/chunks/PrettyJson-wIuHvehp.js.map +0 -1
- package/build/server/chunks/TooltipTable-J278e1CA.js.map +0 -1
- package/build/server/chunks/_layout.svelte-D7RD9tLN.js.map +0 -1
- package/build/server/chunks/_page.svelte-4PYYtPii.js.map +0 -1
- package/build/server/chunks/_page.svelte-Be5VTn1e.js.map +0 -1
- package/build/server/chunks/_page.svelte-DIPUjSjm.js.map +0 -1
- package/build/server/chunks/_page.svelte-DZ59Sr3N.js.map +0 -1
- package/build/server/chunks/_page.svelte-sdKGQrEF.js +0 -91
- package/build/server/chunks/_page.svelte-sdKGQrEF.js.map +0 -1
- package/build/server/chunks/_server.ts-BIRebnb3.js +0 -20
- package/build/server/chunks/_server.ts-BIRebnb3.js.map +0 -1
- package/build/server/chunks/_server.ts-MnKJDybo.js +0 -19
- package/build/server/chunks/_server.ts-MnKJDybo.js.map +0 -1
- package/build/server/chunks/_server.ts-Tog8k3Mh.js +0 -52
- package/build/server/chunks/_server.ts-Tog8k3Mh.js.map +0 -1
- package/build/server/chunks/client-BZtxOgbR.js +0 -7
- package/build/server/chunks/context-BU6gUbSI.js.map +0 -1
- package/build/server/chunks/error.svelte-BfneeC-d.js +0 -16
- package/build/server/chunks/error.svelte-BfneeC-d.js.map +0 -1
- package/build/server/chunks/exports-DiyTWREe.js.map +0 -1
- package/build/server/chunks/hooks.server-DNhdbQNi.js +0 -7
- package/build/server/chunks/hooks.server-DNhdbQNi.js.map +0 -1
- package/build/server/chunks/index3-CUhdJ7wv.js.map +0 -1
- package/build/server/chunks/notifications.svelte-CrTIrfvL.js.map +0 -1
- package/build/server/chunks/routing-zIlQn_LN.js.map +0 -1
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { notificationStore } from "$lib/stores/notifications.svelte";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div class="notifications">
|
|
6
|
+
{#each notificationStore.items as notification (notification.id)}
|
|
7
|
+
<div class="notification notification-{notification.type}">
|
|
8
|
+
<span>{notification.message}</span>
|
|
9
|
+
<button onclick={() => notificationStore.remove(notification.id)}>×</button>
|
|
10
|
+
</div>
|
|
11
|
+
{/each}
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<style lang="postcss">
|
|
15
|
+
.notifications {
|
|
16
|
+
position: fixed;
|
|
17
|
+
top: 80px;
|
|
18
|
+
right: 20px;
|
|
19
|
+
z-index: 1000;
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
gap: 10px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.notification {
|
|
26
|
+
padding: 15px 20px;
|
|
27
|
+
border-radius: 4px;
|
|
28
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
gap: 15px;
|
|
32
|
+
min-width: 300px;
|
|
33
|
+
max-width: 500px;
|
|
34
|
+
animation: slideIn 0.3s ease-out;
|
|
35
|
+
|
|
36
|
+
button {
|
|
37
|
+
background: none;
|
|
38
|
+
border: none;
|
|
39
|
+
font-size: 24px;
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
opacity: 0.7;
|
|
42
|
+
margin-left: auto;
|
|
43
|
+
padding: 0;
|
|
44
|
+
width: 24px;
|
|
45
|
+
height: 24px;
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
|
|
50
|
+
&:hover {
|
|
51
|
+
opacity: 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.notification-error {
|
|
57
|
+
background-color: #dc3545;
|
|
58
|
+
color: white;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.notification-success {
|
|
62
|
+
background-color: #28a745;
|
|
63
|
+
color: white;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.notification-info {
|
|
67
|
+
background-color: #17a2b8;
|
|
68
|
+
color: white;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@keyframes slideIn {
|
|
72
|
+
from {
|
|
73
|
+
transform: translateX(400px);
|
|
74
|
+
opacity: 0;
|
|
75
|
+
}
|
|
76
|
+
to {
|
|
77
|
+
transform: translateX(0);
|
|
78
|
+
opacity: 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
title?: string | Snippet;
|
|
7
|
+
actions?: Snippet;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { children, title, actions, class: className }: Props = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div class="panel rounded-md {className}">
|
|
15
|
+
{#if title}
|
|
16
|
+
<div class="px-4 py-2 flex justify-between items-center text-lg uppercase font-medium">
|
|
17
|
+
{#if typeof title === "string"}
|
|
18
|
+
<span>{title}</span>
|
|
19
|
+
{:else}
|
|
20
|
+
{@render title()}
|
|
21
|
+
{/if}
|
|
22
|
+
{#if actions}
|
|
23
|
+
<div class="flex gap-2 items-center">
|
|
24
|
+
{@render actions()}
|
|
25
|
+
</div>
|
|
26
|
+
{/if}
|
|
27
|
+
</div>
|
|
28
|
+
{/if}
|
|
29
|
+
{@render children?.()}
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<style lang="postcss">
|
|
33
|
+
.panel {
|
|
34
|
+
background-color: var(--color-2);
|
|
35
|
+
border: var(--border);
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { resolve } from "$app/paths";
|
|
3
|
+
import { notificationStore } from "$lib/stores/notifications.svelte";
|
|
4
|
+
import type { MongoDocument } from "$lib/types";
|
|
5
|
+
import { parseJSON } from "$lib/utils/jsonParser";
|
|
6
|
+
import JsonValue from "./JsonValue.svelte";
|
|
7
|
+
import Panel from "./Panel.svelte";
|
|
8
|
+
|
|
9
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
json: MongoDocument;
|
|
13
|
+
autoCollapse?: boolean;
|
|
14
|
+
readOnly?: boolean;
|
|
15
|
+
onedit?: (json: any) => void;
|
|
16
|
+
onremove?: () => void;
|
|
17
|
+
server: string;
|
|
18
|
+
database: string;
|
|
19
|
+
collection: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
json,
|
|
24
|
+
autoCollapse = false,
|
|
25
|
+
readOnly = false,
|
|
26
|
+
onedit,
|
|
27
|
+
onremove,
|
|
28
|
+
server,
|
|
29
|
+
database,
|
|
30
|
+
collection,
|
|
31
|
+
}: Props = $props();
|
|
32
|
+
|
|
33
|
+
let editorVisible = $state(false);
|
|
34
|
+
let editJson = $state("");
|
|
35
|
+
let removing = $state(false);
|
|
36
|
+
let editorRef = $state<HTMLTextAreaElement>();
|
|
37
|
+
|
|
38
|
+
// Extract timestamp from ObjectId
|
|
39
|
+
function getTimestampFromObjectId(objectId: string): Date | null {
|
|
40
|
+
try {
|
|
41
|
+
// ObjectId is 24 hex characters, first 8 represent timestamp
|
|
42
|
+
const timestamp = parseInt(objectId.substring(0, 8), 16);
|
|
43
|
+
return new Date(timestamp * 1000);
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Custom serializer that converts ObjectId objects back to new ObjectId() format
|
|
50
|
+
function serializeForEditing(obj: any, depth = 0): string {
|
|
51
|
+
const indent = " ".repeat(depth);
|
|
52
|
+
const nextIndent = " ".repeat(depth + 1);
|
|
53
|
+
|
|
54
|
+
if (obj === null) return "null";
|
|
55
|
+
if (obj === undefined) return "undefined";
|
|
56
|
+
if (typeof obj === "string") return JSON.stringify(obj);
|
|
57
|
+
if (typeof obj === "number") return obj.toString();
|
|
58
|
+
if (typeof obj === "boolean") return obj.toString();
|
|
59
|
+
|
|
60
|
+
if (Array.isArray(obj)) {
|
|
61
|
+
if (obj.length === 0) return "[]";
|
|
62
|
+
const items = obj.map((item) => `${nextIndent}${serializeForEditing(item, depth + 1)}`).join(",\n");
|
|
63
|
+
return `[\n${items}\n${indent}]`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (typeof obj === "object") {
|
|
67
|
+
// Handle special MongoDB types
|
|
68
|
+
if (obj.$type === "ObjectId") {
|
|
69
|
+
return `new ObjectId("${obj.$value}")`;
|
|
70
|
+
}
|
|
71
|
+
if (obj.$type === "Date") {
|
|
72
|
+
return `new Date("${obj.$value}")`;
|
|
73
|
+
}
|
|
74
|
+
if (obj.$type === "RegExp") {
|
|
75
|
+
return `new RegExp("${obj.$value.$pattern}", "${obj.$value.$flags}")`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Handle regular objects
|
|
79
|
+
const keys = Object.keys(obj);
|
|
80
|
+
if (keys.length === 0) return "{}";
|
|
81
|
+
|
|
82
|
+
const pairs = keys
|
|
83
|
+
.map((key) => {
|
|
84
|
+
const value = serializeForEditing(obj[key], depth + 1);
|
|
85
|
+
return `${nextIndent}${JSON.stringify(key)}: ${value}`;
|
|
86
|
+
})
|
|
87
|
+
.join(",\n");
|
|
88
|
+
|
|
89
|
+
return `{\n${pairs}\n${indent}}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return JSON.stringify(obj);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function enableEditor() {
|
|
96
|
+
editJson = serializeForEditing(json);
|
|
97
|
+
editorVisible = true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function disableEditor() {
|
|
101
|
+
editorVisible = false;
|
|
102
|
+
editJson = "";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function save() {
|
|
106
|
+
try {
|
|
107
|
+
const updatedJson = parseJSON(editJson);
|
|
108
|
+
disableEditor();
|
|
109
|
+
onedit?.(updatedJson);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
notificationStore.notifyError(err, "Invalid JSON");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function showRemove() {
|
|
116
|
+
removing = true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function cancelRemove() {
|
|
120
|
+
removing = false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function confirmRemove() {
|
|
124
|
+
onremove?.();
|
|
125
|
+
}
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<Panel class="group">
|
|
129
|
+
{#snippet title()}
|
|
130
|
+
{#if json._id}
|
|
131
|
+
<div class="">
|
|
132
|
+
<a
|
|
133
|
+
type="button"
|
|
134
|
+
class="bg-transparent border-none text-[var(--text)] no-underline cursor-pointer text-xl font-inherit p-0 hover:underline"
|
|
135
|
+
href={resolve(
|
|
136
|
+
`/servers/${encodeURIComponent(server)}/databases/${encodeURIComponent(database)}/collections/${encodeURIComponent(collection)}/documents/${json._id?.$value}`,
|
|
137
|
+
)}
|
|
138
|
+
>
|
|
139
|
+
{json._id?.$value}
|
|
140
|
+
</a>
|
|
141
|
+
{#if json._id?.$value}
|
|
142
|
+
{@const timestamp = getTimestampFromObjectId(json._id.$value)}
|
|
143
|
+
{#if timestamp}
|
|
144
|
+
<span class="ml-2 text-md text-[var(--text-secondary,#888)]">{timestamp.toLocaleString()}</span>
|
|
145
|
+
{/if}
|
|
146
|
+
{/if}
|
|
147
|
+
</div>
|
|
148
|
+
{/if}
|
|
149
|
+
{/snippet}
|
|
150
|
+
|
|
151
|
+
{#snippet actions()}
|
|
152
|
+
{#if !readOnly}
|
|
153
|
+
<div class="hidden group-hover:block">
|
|
154
|
+
<button class="btn btn-outline-light btn-sm ml-2 -my-2" onclick={enableEditor}>Edit</button>
|
|
155
|
+
<button class="btn btn-outline-danger btn-sm ml-2 -my-2" onclick={showRemove}>Remove</button>
|
|
156
|
+
</div>
|
|
157
|
+
{/if}
|
|
158
|
+
{/snippet}
|
|
159
|
+
|
|
160
|
+
<div class="p-4 relative border-t border-[var(--border-color)]">
|
|
161
|
+
<div class="font-mono text-sm leading-tight whitespace-pre-wrap break-words relative">
|
|
162
|
+
<JsonValue value={json} {autoCollapse} collapsed={false} />
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div class="absolute h-full z-[100] w-full top-0 left-0" class:hidden={!editorVisible} class:block={editorVisible}>
|
|
166
|
+
<div class="absolute z-10 right-5 top-4">
|
|
167
|
+
<button class="btn btn-success ml-2" onclick={save}>Save</button>
|
|
168
|
+
<button class="btn btn-default ml-2" onclick={disableEditor}>Cancel</button>
|
|
169
|
+
</div>
|
|
170
|
+
<textarea
|
|
171
|
+
bind:this={editorRef}
|
|
172
|
+
bind:value={editJson}
|
|
173
|
+
class="w-full min-h-[300px] font-mono text-sm leading-relaxed p-2.5 bg-[var(--color-1)] text-[var(--text)] border border-[var(--border-color)] rounded resize-y"
|
|
174
|
+
></textarea>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
{#if removing}
|
|
178
|
+
<div class="absolute z-10 top-0 left-0 w-full h-full rounded bg-[var(--text-inverse)] opacity-70">
|
|
179
|
+
<p class="text-[var(--text)] text-center mt-5 text-2xl">Are you sure?</p>
|
|
180
|
+
<div class="absolute w-full h-full top-0 flex justify-center items-center">
|
|
181
|
+
<button class="btn btn-danger m-5" onclick={confirmRemove}>Yes - Remove</button>
|
|
182
|
+
<button class="btn btn-success m-5" onclick={cancelRemove}>No - Cancel</button>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
{/if}
|
|
186
|
+
</div>
|
|
187
|
+
</Panel>
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { goto } from "$app/navigation";
|
|
3
|
+
import { resolve } from "$app/paths";
|
|
4
|
+
import { page } from "$app/state";
|
|
5
|
+
import type { SearchParams } from "$lib/types";
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
params: SearchParams;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { params = $bindable() }: Props = $props();
|
|
12
|
+
|
|
13
|
+
// Show optional fields - start with all hidden
|
|
14
|
+
let showOptionalFields = $state(
|
|
15
|
+
params.sort !== "{}" || params.project !== "{}" || params.skip !== 0 || params.limit !== 20,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
let counter = $state(Math.random());
|
|
19
|
+
|
|
20
|
+
let queryInput: HTMLInputElement | undefined;
|
|
21
|
+
|
|
22
|
+
$effect(() => {
|
|
23
|
+
if (queryInput) {
|
|
24
|
+
queryInput.setSelectionRange(1, 1 /* queryInput.value.length - 1 */);
|
|
25
|
+
queryInput.focus();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
async function submit(event: SubmitEvent) {
|
|
30
|
+
event.preventDefault();
|
|
31
|
+
counter++;
|
|
32
|
+
const formData = new FormData(form);
|
|
33
|
+
await goto(
|
|
34
|
+
resolve(
|
|
35
|
+
(page.url.pathname +
|
|
36
|
+
"?" +
|
|
37
|
+
[...formData.entries()]
|
|
38
|
+
.map((e) => encodeURIComponent(e[0]) + "=" + encodeURIComponent(e[1] as string))
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
.join("&")) as any,
|
|
41
|
+
),
|
|
42
|
+
{
|
|
43
|
+
keepFocus: true,
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let form: HTMLFormElement | undefined;
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<form class="flex items-stretch w-full" method="GET" action="?" onsubmit={submit} bind:this={form}>
|
|
52
|
+
<!-- Parameters group -->
|
|
53
|
+
<div class="flex-grow">
|
|
54
|
+
<!-- Query input (always shown) -->
|
|
55
|
+
<div class="flex items-stretch w-full h-10">
|
|
56
|
+
<div
|
|
57
|
+
class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] {!showOptionalFields
|
|
58
|
+
? 'border-b rounded-bl-md'
|
|
59
|
+
: 'border-b-0'} bg-[var(--color-1)] rounded-tl-md"
|
|
60
|
+
>
|
|
61
|
+
Query:
|
|
62
|
+
</div>
|
|
63
|
+
<input
|
|
64
|
+
type="text"
|
|
65
|
+
bind:this={queryInput}
|
|
66
|
+
bind:value={params.query}
|
|
67
|
+
placeholder={"{}"}
|
|
68
|
+
name="query"
|
|
69
|
+
class="flex-grow border-0 bg-[var(--color-3)] pl-2.5 font-mono"
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<input type="hidden" value={counter} name="v" />
|
|
74
|
+
<!-- Sort input -->
|
|
75
|
+
{#if showOptionalFields}
|
|
76
|
+
<div class="flex items-stretch w-full h-10">
|
|
77
|
+
<div
|
|
78
|
+
class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] border-b-0 bg-[var(--color-1)]"
|
|
79
|
+
>
|
|
80
|
+
Sort:
|
|
81
|
+
</div>
|
|
82
|
+
<input
|
|
83
|
+
type="text"
|
|
84
|
+
bind:value={params.sort}
|
|
85
|
+
name="sort"
|
|
86
|
+
placeholder={"{}"}
|
|
87
|
+
class="flex-grow border-0 border-t border-[var(--color-4)] bg-[var(--color-3)] pl-2.5 font-mono"
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<!-- Skip input -->
|
|
92
|
+
<div class="flex items-stretch w-full h-10">
|
|
93
|
+
<div
|
|
94
|
+
class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] border-b-0 bg-[var(--color-1)]"
|
|
95
|
+
>
|
|
96
|
+
Skip:
|
|
97
|
+
</div>
|
|
98
|
+
<input
|
|
99
|
+
type="number"
|
|
100
|
+
bind:value={params.skip}
|
|
101
|
+
name="skip"
|
|
102
|
+
min="0"
|
|
103
|
+
class="flex-grow border-0 border-t border-[var(--color-4)] bg-[var(--color-3)] pl-2.5 font-mono"
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- Limit input -->
|
|
108
|
+
<div class="flex items-stretch w-full h-10">
|
|
109
|
+
<div
|
|
110
|
+
class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] border-b-0 bg-[var(--color-1)]"
|
|
111
|
+
>
|
|
112
|
+
Limit:
|
|
113
|
+
</div>
|
|
114
|
+
<input
|
|
115
|
+
type="number"
|
|
116
|
+
bind:value={params.limit}
|
|
117
|
+
name="limit"
|
|
118
|
+
min="1"
|
|
119
|
+
class="flex-grow border-0 border-t border-[var(--color-4)] bg-[var(--color-3)] pl-2.5 font-mono"
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<!-- Project input -->
|
|
124
|
+
<div class="flex items-stretch w-full h-10">
|
|
125
|
+
<div
|
|
126
|
+
class="min-w-[100px] flex justify-center items-center border border-[var(--color-4)] border-b bg-[var(--color-1)] rounded-bl-md"
|
|
127
|
+
>
|
|
128
|
+
Project:
|
|
129
|
+
</div>
|
|
130
|
+
<input
|
|
131
|
+
type="text"
|
|
132
|
+
bind:value={params.project}
|
|
133
|
+
name="project"
|
|
134
|
+
placeholder={"{}"}
|
|
135
|
+
class="flex-grow border-0 border-t border-[var(--color-4)] bg-[var(--color-3)] pl-2.5 font-mono"
|
|
136
|
+
/>
|
|
137
|
+
</div>
|
|
138
|
+
{/if}
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<!-- Toggle optional fields button -->
|
|
142
|
+
<button
|
|
143
|
+
class="btn btn-default !w-12 !rounded-none !border-r-0 text-2xl leading-none font-bold !py-1.5"
|
|
144
|
+
type="button"
|
|
145
|
+
onclick={() => {
|
|
146
|
+
showOptionalFields = !showOptionalFields;
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{showOptionalFields ? "−" : "+"}
|
|
150
|
+
</button>
|
|
151
|
+
|
|
152
|
+
<!-- Search button -->
|
|
153
|
+
<button class="btn btn-success !rounded-l-none !rounded-r-md font-bold !py-1.5" type="submit"> GO! </button>
|
|
154
|
+
</form>
|
|
155
|
+
|
|
156
|
+
<style lang="postcss">
|
|
157
|
+
input {
|
|
158
|
+
border-radius: 0;
|
|
159
|
+
}
|
|
160
|
+
</style>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { tick, type Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
interface TableColumn {
|
|
5
|
+
header: string;
|
|
6
|
+
key: string;
|
|
7
|
+
align?: "left" | "right" | "center";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface TableRow {
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
columns,
|
|
16
|
+
rows,
|
|
17
|
+
children,
|
|
18
|
+
hideHeader,
|
|
19
|
+
}: {
|
|
20
|
+
columns: TableColumn[];
|
|
21
|
+
rows: TableRow[];
|
|
22
|
+
children: Snippet;
|
|
23
|
+
hideHeader?: boolean;
|
|
24
|
+
} = $props();
|
|
25
|
+
|
|
26
|
+
let showTooltip = $state(false);
|
|
27
|
+
let tooltipElement = $state<HTMLDivElement>();
|
|
28
|
+
let containerElement = $state<HTMLDivElement>();
|
|
29
|
+
|
|
30
|
+
let tooltipPosition = $state({
|
|
31
|
+
left: "",
|
|
32
|
+
right: "",
|
|
33
|
+
top: "",
|
|
34
|
+
bottom: "",
|
|
35
|
+
marginTop: "",
|
|
36
|
+
marginBottom: "",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function handleMouseEnter() {
|
|
40
|
+
showTooltip = true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function handleMouseLeave() {
|
|
44
|
+
showTooltip = false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
$effect(() => {
|
|
48
|
+
if (showTooltip && tooltipElement && containerElement) {
|
|
49
|
+
tick().then(() => {
|
|
50
|
+
if (!containerElement || !tooltipElement) return;
|
|
51
|
+
|
|
52
|
+
const tooltipRect = tooltipElement.getBoundingClientRect();
|
|
53
|
+
const viewportWidth = window.innerWidth;
|
|
54
|
+
const viewportHeight = window.innerHeight;
|
|
55
|
+
|
|
56
|
+
// Reset positioning
|
|
57
|
+
tooltipPosition = {
|
|
58
|
+
left: "",
|
|
59
|
+
right: "",
|
|
60
|
+
top: "",
|
|
61
|
+
bottom: "",
|
|
62
|
+
marginTop: "",
|
|
63
|
+
marginBottom: "",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Position horizontally
|
|
67
|
+
if (tooltipRect.right > viewportWidth) {
|
|
68
|
+
tooltipPosition.right = "0";
|
|
69
|
+
} else {
|
|
70
|
+
tooltipPosition.left = "0";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Position vertically
|
|
74
|
+
if (tooltipRect.bottom > viewportHeight) {
|
|
75
|
+
tooltipPosition.bottom = "100%";
|
|
76
|
+
tooltipPosition.marginBottom = "5px";
|
|
77
|
+
} else {
|
|
78
|
+
tooltipPosition.top = "100%";
|
|
79
|
+
tooltipPosition.marginTop = "5px";
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
</script>
|
|
85
|
+
|
|
86
|
+
<div class="relative inline-block" bind:this={containerElement}>
|
|
87
|
+
<button class="dotted text-center" onmouseenter={handleMouseEnter} onmouseleave={handleMouseLeave}>
|
|
88
|
+
{@render children?.()}
|
|
89
|
+
</button>
|
|
90
|
+
{#if showTooltip}
|
|
91
|
+
<div
|
|
92
|
+
class="absolute bg-[var(--color-2)] border border-[var(--color-3)] rounded p-0 z-[1000] whitespace-nowrap shadow-lg"
|
|
93
|
+
bind:this={tooltipElement}
|
|
94
|
+
style:left={tooltipPosition.left}
|
|
95
|
+
style:right={tooltipPosition.right}
|
|
96
|
+
style:top={tooltipPosition.top}
|
|
97
|
+
style:bottom={tooltipPosition.bottom}
|
|
98
|
+
style:margin-top={tooltipPosition.marginTop}
|
|
99
|
+
style:margin-bottom={tooltipPosition.marginBottom}
|
|
100
|
+
>
|
|
101
|
+
<table class="w-full border-collapse text-base font-medium">
|
|
102
|
+
{#if !hideHeader}
|
|
103
|
+
<thead>
|
|
104
|
+
<tr>
|
|
105
|
+
{#each columns as column, index (index)}
|
|
106
|
+
<th
|
|
107
|
+
class="px-2 py-1 border-b border-[var(--color-3)] bg-[var(--color-4)] font-bold"
|
|
108
|
+
class:text-left={column.align === "left" || !column.align}
|
|
109
|
+
class:text-right={column.align === "right"}
|
|
110
|
+
class:text-center={column.align === "center"}
|
|
111
|
+
>
|
|
112
|
+
{column.header}
|
|
113
|
+
</th>
|
|
114
|
+
{/each}
|
|
115
|
+
</tr>
|
|
116
|
+
</thead>
|
|
117
|
+
{/if}
|
|
118
|
+
<tbody>
|
|
119
|
+
{#each rows as row, index (index)}
|
|
120
|
+
<tr>
|
|
121
|
+
{#each columns as column, index (index)}
|
|
122
|
+
<td
|
|
123
|
+
class="px-2 py-1 border-b border-[var(--color-3)]"
|
|
124
|
+
class:text-left={column.align === "left" || !column.align}
|
|
125
|
+
class:text-right={column.align === "right"}
|
|
126
|
+
class:text-center={column.align === "center"}
|
|
127
|
+
>
|
|
128
|
+
{row[column.key]}
|
|
129
|
+
</td>
|
|
130
|
+
{/each}
|
|
131
|
+
</tr>
|
|
132
|
+
{/each}
|
|
133
|
+
</tbody>
|
|
134
|
+
</table>
|
|
135
|
+
</div>
|
|
136
|
+
{/if}
|
|
137
|
+
</div>
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
|
|
5
|
+
export interface Host {
|
|
6
|
+
path: string;
|
|
7
|
+
_id: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const DEFAULT_HOSTS = process.env.MONGOKU_DEFAULT_HOST
|
|
11
|
+
? process.env.MONGOKU_DEFAULT_HOST.split(";")
|
|
12
|
+
: ["localhost:27017"];
|
|
13
|
+
const DATABASE_FILE = process.env.MONGOKU_DATABASE_FILE || path.join(os.homedir(), ".mongoku.db");
|
|
14
|
+
|
|
15
|
+
export class HostsManager {
|
|
16
|
+
private _hosts: Map<string, string> = new Map(); // path -> _id
|
|
17
|
+
|
|
18
|
+
async load() {
|
|
19
|
+
let first = false;
|
|
20
|
+
try {
|
|
21
|
+
await fs.promises.stat(DATABASE_FILE);
|
|
22
|
+
} catch {
|
|
23
|
+
first = true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!first) {
|
|
27
|
+
await this._loadFromFile();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (first || this._hosts.size === 0) {
|
|
31
|
+
// Initialize with default hosts
|
|
32
|
+
for (const hostname of DEFAULT_HOSTS) {
|
|
33
|
+
this._hosts.set(hostname, this._generateId());
|
|
34
|
+
}
|
|
35
|
+
await this._saveToFile();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private async _loadFromFile(): Promise<void> {
|
|
40
|
+
const content = await fs.promises.readFile(DATABASE_FILE, "utf8");
|
|
41
|
+
const lines = content
|
|
42
|
+
.trim()
|
|
43
|
+
.split("\n")
|
|
44
|
+
.filter((line) => line.trim());
|
|
45
|
+
|
|
46
|
+
const newHosts = new Map<string, string>();
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
const host = JSON.parse(line);
|
|
49
|
+
if (host && typeof host.path === "string") {
|
|
50
|
+
// Use existing _id if available, generate new one if not
|
|
51
|
+
const id = host._id || this._generateId();
|
|
52
|
+
newHosts.set(host.path, id);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this._hosts = newHosts;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private async _saveToFile(): Promise<void> {
|
|
59
|
+
const lines = Array.from(this._hosts).map(([hostPath, id]) => JSON.stringify({ path: hostPath, _id: id }));
|
|
60
|
+
await fs.promises.writeFile(DATABASE_FILE, lines.join("\n") + "\n", "utf8");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private _generateId(): string {
|
|
64
|
+
// Generate a NeDB-compatible ID (16 characters, alphanumeric)
|
|
65
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
66
|
+
let result = "";
|
|
67
|
+
for (let i = 0; i < 16; i++) {
|
|
68
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async getHosts(): Promise<Host[]> {
|
|
74
|
+
return Array.from(this._hosts).map(([hostPath, id]) => ({ path: hostPath, _id: id }));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async add(hostPath: string): Promise<string> {
|
|
78
|
+
// Use existing ID if host already exists, generate new one if not
|
|
79
|
+
let id = this._hosts.get(hostPath);
|
|
80
|
+
if (!id) {
|
|
81
|
+
id = this._generateId();
|
|
82
|
+
this._hosts.set(hostPath, id);
|
|
83
|
+
}
|
|
84
|
+
await this._saveToFile();
|
|
85
|
+
return id;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async remove(hostPath: string): Promise<void> {
|
|
89
|
+
// Remove exact matches and regex pattern matches
|
|
90
|
+
const toRemove = Array.from(this._hosts.keys()).filter((existingPath) => {
|
|
91
|
+
try {
|
|
92
|
+
const regex = new RegExp(hostPath);
|
|
93
|
+
return existingPath === hostPath || regex.test(existingPath);
|
|
94
|
+
} catch {
|
|
95
|
+
// If hostPath is not a valid regex, just do exact match
|
|
96
|
+
return existingPath === hostPath;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
for (const host of toRemove) {
|
|
101
|
+
this._hosts.delete(host);
|
|
102
|
+
}
|
|
103
|
+
await this._saveToFile();
|
|
104
|
+
}
|
|
105
|
+
}
|