mongoku 2.2.4 → 2.4.0
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/build/client/_app/immutable/assets/0.C-Y-CVnr.css +1 -0
- package/build/client/_app/immutable/assets/0.C-Y-CVnr.css.br +0 -0
- package/build/client/_app/immutable/assets/0.C-Y-CVnr.css.gz +0 -0
- package/build/client/_app/immutable/assets/10.BrydhPds.css +1 -0
- package/build/client/_app/immutable/assets/10.BrydhPds.css.br +0 -0
- package/build/client/_app/immutable/assets/10.BrydhPds.css.gz +0 -0
- package/build/client/_app/immutable/assets/12.I1sSDJ6i.css +1 -0
- package/build/client/_app/immutable/assets/12.I1sSDJ6i.css.br +0 -0
- package/build/client/_app/immutable/assets/12.I1sSDJ6i.css.gz +0 -0
- package/build/client/_app/immutable/assets/Modal.zD4dMMyk.css +1 -0
- package/build/client/_app/immutable/assets/Modal.zD4dMMyk.css.br +0 -0
- package/build/client/_app/immutable/assets/Modal.zD4dMMyk.css.gz +0 -0
- package/build/client/_app/immutable/chunks/0oUK0vJa.js +1 -0
- package/build/client/_app/immutable/chunks/0oUK0vJa.js.br +0 -0
- package/build/client/_app/immutable/chunks/0oUK0vJa.js.gz +0 -0
- package/build/client/_app/immutable/chunks/3xIOsPOb.js +16 -0
- package/build/client/_app/immutable/chunks/3xIOsPOb.js.br +0 -0
- package/build/client/_app/immutable/chunks/3xIOsPOb.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B83FMsIQ.js +24 -0
- package/build/client/_app/immutable/chunks/B83FMsIQ.js.br +0 -0
- package/build/client/_app/immutable/chunks/B83FMsIQ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BGXqurs_.js +1 -0
- package/build/client/_app/immutable/chunks/BGXqurs_.js.br +0 -0
- package/build/client/_app/immutable/chunks/BGXqurs_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BPbCnJEo.js +1 -0
- package/build/client/_app/immutable/chunks/BPbCnJEo.js.br +0 -0
- package/build/client/_app/immutable/chunks/BPbCnJEo.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BXUq66wC.js +1 -0
- package/build/client/_app/immutable/chunks/BXUq66wC.js.br +0 -0
- package/build/client/_app/immutable/chunks/BXUq66wC.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BZY48V2X.js +14 -0
- package/build/client/_app/immutable/chunks/BZY48V2X.js.br +0 -0
- package/build/client/_app/immutable/chunks/BZY48V2X.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bq7qZpXP.js +1 -0
- package/build/client/_app/immutable/chunks/Bq7qZpXP.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bq7qZpXP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BxoAxRg6.js +1 -0
- package/build/client/_app/immutable/chunks/BxoAxRg6.js.br +0 -0
- package/build/client/_app/immutable/chunks/BxoAxRg6.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ByCRZ7zS.js +2 -0
- package/build/client/_app/immutable/chunks/ByCRZ7zS.js.br +0 -0
- package/build/client/_app/immutable/chunks/ByCRZ7zS.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{D4iRRB8h.js → C1g0HmEj.js} +1 -1
- package/build/client/_app/immutable/chunks/C1g0HmEj.js.br +0 -0
- package/build/client/_app/immutable/chunks/C1g0HmEj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DGtnJf56.js +1 -0
- package/build/client/_app/immutable/chunks/DGtnJf56.js.br +0 -0
- package/build/client/_app/immutable/chunks/DGtnJf56.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DR9xdhsg.js → DIw4Wyr4.js} +1 -1
- package/build/client/_app/immutable/chunks/DIw4Wyr4.js.br +0 -0
- package/build/client/_app/immutable/chunks/DIw4Wyr4.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dn0_-jEe.js +1 -0
- package/build/client/_app/immutable/chunks/Dn0_-jEe.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dn0_-jEe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/UJuWD2W4.js +1 -0
- package/build/client/_app/immutable/chunks/UJuWD2W4.js.br +0 -0
- package/build/client/_app/immutable/chunks/UJuWD2W4.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Dx56OlI-.js → _Sxqiax7.js} +1 -1
- package/build/client/_app/immutable/chunks/_Sxqiax7.js.br +0 -0
- package/build/client/_app/immutable/chunks/_Sxqiax7.js.gz +0 -0
- package/build/client/_app/immutable/chunks/_kKBJjiB.js +1 -0
- package/build/client/_app/immutable/chunks/_kKBJjiB.js.br +0 -0
- package/build/client/_app/immutable/chunks/_kKBJjiB.js.gz +0 -0
- package/build/client/_app/immutable/chunks/pj4OZpZf.js +4 -0
- package/build/client/_app/immutable/chunks/pj4OZpZf.js.br +0 -0
- package/build/client/_app/immutable/chunks/pj4OZpZf.js.gz +0 -0
- package/build/client/_app/immutable/chunks/reNJcONQ.js +1 -0
- package/build/client/_app/immutable/chunks/reNJcONQ.js.br +0 -0
- package/build/client/_app/immutable/chunks/reNJcONQ.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.DV8my7lq.js +2 -0
- package/build/client/_app/immutable/entry/app.DV8my7lq.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DV8my7lq.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CHeMkIYN.js +1 -0
- package/build/client/_app/immutable/entry/start.CHeMkIYN.js.br +0 -0
- package/build/client/_app/immutable/entry/start.CHeMkIYN.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.CNNOoOHl.js +1 -0
- package/build/client/_app/immutable/nodes/0.CNNOoOHl.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.CNNOoOHl.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.C-65KEEJ.js +1 -0
- package/build/client/_app/immutable/nodes/1.C-65KEEJ.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.C-65KEEJ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.BQ-b15Yh.js +1 -0
- package/build/client/_app/immutable/nodes/10.BQ-b15Yh.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.BQ-b15Yh.js.gz +0 -0
- package/build/client/_app/immutable/nodes/11.iU-GIxdi.js +1 -0
- package/build/client/_app/immutable/nodes/11.iU-GIxdi.js.br +0 -0
- package/build/client/_app/immutable/nodes/11.iU-GIxdi.js.gz +0 -0
- package/build/client/_app/immutable/nodes/12.BneOFPb1.js +1 -0
- package/build/client/_app/immutable/nodes/12.BneOFPb1.js.br +0 -0
- package/build/client/_app/immutable/nodes/12.BneOFPb1.js.gz +0 -0
- package/build/client/_app/immutable/nodes/13.BsUY4XVc.js +66 -0
- package/build/client/_app/immutable/nodes/13.BsUY4XVc.js.br +0 -0
- package/build/client/_app/immutable/nodes/13.BsUY4XVc.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{2.DwwrLLW4.js → 2.DqvBb3C7.js} +1 -1
- package/build/client/_app/immutable/nodes/2.DqvBb3C7.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DqvBb3C7.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{3.t20GootO.js → 3.27swwcG4.js} +1 -1
- package/build/client/_app/immutable/nodes/3.27swwcG4.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.27swwcG4.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{4.CXHjkPtf.js → 4.CcholrFe.js} +1 -1
- package/build/client/_app/immutable/nodes/4.CcholrFe.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.CcholrFe.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{5.C9tcr5B0.js → 5.CzudN9q-.js} +1 -1
- package/build/client/_app/immutable/nodes/5.CzudN9q-.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.CzudN9q-.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.S2O-z1Ox.js +1 -0
- package/build/client/_app/immutable/nodes/7.S2O-z1Ox.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.S2O-z1Ox.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.aa4EbN0k.js +1 -0
- package/build/client/_app/immutable/nodes/8.aa4EbN0k.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.aa4EbN0k.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.9dZgqvLb.js +2 -0
- package/build/client/_app/immutable/nodes/9.9dZgqvLb.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.9dZgqvLb.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-BHG1s0_H.js +22 -0
- package/build/server/chunks/0-BHG1s0_H.js.map +1 -0
- package/build/server/chunks/1-Xz1zN5FT.js +9 -0
- package/build/server/chunks/1-Xz1zN5FT.js.map +1 -0
- package/build/server/chunks/{10-DGUQMXHt.js → 10-CaYvpZVK.js} +6 -5
- package/build/server/chunks/10-CaYvpZVK.js.map +1 -0
- package/build/server/chunks/{11-CxBnyyqR.js → 11-uJK96PLt.js} +6 -5
- package/build/server/chunks/11-uJK96PLt.js.map +1 -0
- package/build/server/chunks/{12-K_JVUnsk.js → 12-CvEEILKx.js} +14 -13
- package/build/server/chunks/12-CvEEILKx.js.map +1 -0
- package/build/server/chunks/{13-Cay35aHK.js → 13-Cvk7ClFs.js} +6 -5
- package/build/server/chunks/{13-Cay35aHK.js.map → 13-Cvk7ClFs.js.map} +1 -1
- package/build/server/chunks/{2-ClkERMhY.js → 2-BheEgghx.js} +2 -2
- package/build/server/chunks/{2-ClkERMhY.js.map → 2-BheEgghx.js.map} +1 -1
- package/build/server/chunks/{3-CTpqmxdo.js → 3-BwrTscBH.js} +2 -2
- package/build/server/chunks/{3-CTpqmxdo.js.map → 3-BwrTscBH.js.map} +1 -1
- package/build/server/chunks/{4-CUH4fZn8.js → 4-D28kbpdN.js} +2 -2
- package/build/server/chunks/{4-CUH4fZn8.js.map → 4-D28kbpdN.js.map} +1 -1
- package/build/server/chunks/{5-BSEdxfut.js → 5--wY8V1OC.js} +2 -2
- package/build/server/chunks/{5-BSEdxfut.js.map → 5--wY8V1OC.js.map} +1 -1
- package/build/server/chunks/{7-DvU-_ADW.js → 7-DTmi6ASF.js} +6 -5
- package/build/server/chunks/7-DTmi6ASF.js.map +1 -0
- package/build/server/chunks/{8-CA3-CJtS.js → 8-CHIpUo4j.js} +6 -5
- package/build/server/chunks/8-CHIpUo4j.js.map +1 -0
- package/build/server/chunks/{9-jMOS3lPS.js → 9-C3nw_taL.js} +6 -5
- package/build/server/chunks/9-C3nw_taL.js.map +1 -0
- package/build/server/chunks/{JsonValue-WMdIUvRM.js → JsonValue-DiECF6Zd.js} +11 -4
- package/build/server/chunks/JsonValue-DiECF6Zd.js.map +1 -0
- package/build/server/chunks/{Modal-zVnoAfFx.js → Modal-BzZ9u6V6.js} +3 -3
- package/build/server/chunks/Modal-BzZ9u6V6.js.map +1 -0
- package/build/server/chunks/Panel-DEP08QXV.js +48 -0
- package/build/server/chunks/Panel-DEP08QXV.js.map +1 -0
- package/build/server/chunks/{PrettyJson-D3CylSVd.js → PrettyJson-ElzPxPF4.js} +55 -40
- package/build/server/chunks/PrettyJson-ElzPxPF4.js.map +1 -0
- package/build/server/chunks/Tooltip-DK6Aok7l.js +35 -0
- package/build/server/chunks/Tooltip-DK6Aok7l.js.map +1 -0
- package/build/server/chunks/{TooltipTable-DCu3GLqO.js → TooltipTable-DUYKVtrh.js} +3 -3
- package/build/server/chunks/{TooltipTable-DCu3GLqO.js.map → TooltipTable-DUYKVtrh.js.map} +1 -1
- package/build/server/chunks/_layout.svelte-CCi6mq1k.js +159 -0
- package/build/server/chunks/_layout.svelte-CCi6mq1k.js.map +1 -0
- package/build/server/chunks/{_page.svelte-C-13GpOe.js → _page.svelte-B4BL5PJR.js} +18 -16
- package/build/server/chunks/_page.svelte-B4BL5PJR.js.map +1 -0
- package/build/server/chunks/{_page.svelte-BRxX_vQR.js → _page.svelte-BUheieJ5.js} +26 -15
- package/build/server/chunks/_page.svelte-BUheieJ5.js.map +1 -0
- package/build/server/chunks/{_page.svelte-BBHqwhWF.js → _page.svelte-Bj297hp1.js} +16 -15
- package/build/server/chunks/_page.svelte-Bj297hp1.js.map +1 -0
- package/build/server/chunks/{_page.svelte-2w3fCax2.js → _page.svelte-BvjNfjgw.js} +14 -14
- package/build/server/chunks/{_page.svelte-2w3fCax2.js.map → _page.svelte-BvjNfjgw.js.map} +1 -1
- package/build/server/chunks/_page.svelte-DDe4hyAB.js +387 -0
- package/build/server/chunks/_page.svelte-DDe4hyAB.js.map +1 -0
- package/build/server/chunks/{_page.svelte-Ca9u_35e.js → _page.svelte-DEe2Kbbw.js} +72 -12
- package/build/server/chunks/_page.svelte-DEe2Kbbw.js.map +1 -0
- package/build/server/chunks/_page.svelte-DJFPwyij.js +323 -0
- package/build/server/chunks/_page.svelte-DJFPwyij.js.map +1 -0
- package/build/server/chunks/client-Cp5ag8tA.js +7 -0
- package/build/server/chunks/{client-BFlyU7ZU.js.map → client-Cp5ag8tA.js.map} +1 -1
- package/build/server/chunks/{client2-DYaMKWrc.js → client2-Dos6p_Xm.js} +3 -4
- package/build/server/chunks/{client2-DYaMKWrc.js.map → client2-Dos6p_Xm.js.map} +1 -1
- package/build/server/chunks/{error.svelte-BENdaVOT.js → error.svelte-BBEhCLUJ.js} +6 -7
- package/build/server/chunks/{error.svelte-BENdaVOT.js.map → error.svelte-BBEhCLUJ.js.map} +1 -1
- package/build/server/chunks/{index2-DBFlGzWV.js → index2-DzKnqQNM.js} +2 -2
- package/build/server/chunks/{index2-DBFlGzWV.js.map → index2-DzKnqQNM.js.map} +1 -1
- package/build/server/chunks/{index3-CoOO2o9t.js → index3-DANRthiW.js} +2 -2
- package/build/server/chunks/{index3-CoOO2o9t.js.map → index3-DANRthiW.js.map} +1 -1
- package/build/server/chunks/{mongo-Db8Vgimy.js → mongo-BHk5yAj9.js} +90 -19
- package/build/server/chunks/mongo-BHk5yAj9.js.map +1 -0
- package/build/server/chunks/{remote-xxtqbu-AjxdDZZ_.js → remote-xxtqbu-DBe3CqEJ.js} +6 -6
- package/build/server/chunks/remote-xxtqbu-DBe3CqEJ.js.map +1 -0
- package/build/server/chunks/{server2-_mGVuBoG.js → server2-YmCaJGWy.js} +2 -2
- package/build/server/chunks/{server2-_mGVuBoG.js.map → server2-YmCaJGWy.js.map} +1 -1
- package/build/server/chunks/{servers.remote-DrkuAWHP.js → servers.remote-HDmNH7n4.js} +245 -15
- package/build/server/chunks/servers.remote-HDmNH7n4.js.map +1 -0
- package/build/server/chunks/{shared-Buki-xt5.js → shared-CQd3A1I4.js} +2 -2
- package/build/server/chunks/{shared-Buki-xt5.js.map → shared-CQd3A1I4.js.map} +1 -1
- package/build/server/chunks/{state.svelte-C8IWmp_n.js → state.svelte-Dm0prnTp.js} +2 -2
- package/build/server/chunks/{state.svelte-C8IWmp_n.js.map → state.svelte-Dm0prnTp.js.map} +1 -1
- package/build/server/chunks/{event-DVH-6ISX.js → utils-D1ahxout.js} +39 -2
- package/build/server/chunks/utils-D1ahxout.js.map +1 -0
- package/build/server/index.js +5 -6
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +15 -15
- package/build/server/manifest.js.map +1 -1
- package/package.json +3 -2
- package/src/api/servers.remote.ts +275 -11
- package/src/app.css +178 -135
- package/src/app.html +23 -10
- package/src/lib/actions/portal.ts +42 -0
- package/src/lib/components/Breadcrumbs.svelte +20 -60
- package/src/lib/components/JsonValue.svelte +30 -1
- package/src/lib/components/Modal.svelte +5 -5
- package/src/lib/components/Notifications.svelte +42 -21
- package/src/lib/components/PageSwitcher.svelte +16 -10
- package/src/lib/components/Panel.svelte +19 -15
- package/src/lib/components/PrettyJson.svelte +102 -90
- package/src/lib/components/ReplicaSetSelector.svelte +144 -0
- package/src/lib/components/SearchBox.svelte +117 -96
- package/src/lib/components/ThemeSwitcher.svelte +191 -36
- package/src/lib/components/Tooltip.svelte +23 -14
- package/src/lib/server/mongo.ts +129 -26
- package/src/lib/utils/formatSignificantDigits.ts +30 -0
- package/src/lib/utils/formatTimeAgo.ts +20 -0
- package/src/lib/utils/hostnames.ts +86 -0
- package/src/lib/utils/jsonParser.ts +50 -0
- package/src/lib/utils/longestCommonSuffix.ts +47 -0
- package/src/lib/utils/sum.ts +10 -0
- package/src/routes/+layout.svelte +36 -17
- package/src/routes/servers/+page.svelte +6 -7
- package/src/routes/servers/[server]/databases/+page.svelte +71 -2
- package/src/routes/servers/[server]/databases/[database]/collections/+page.svelte +13 -3
- package/src/routes/servers/[server]/databases/[database]/collections/[collection]/documents/+page.svelte +292 -57
- package/src/routes/servers/[server]/databases/[database]/collections/[collection]/indexes/+page.server.ts +8 -8
- package/src/routes/servers/[server]/databases/[database]/collections/[collection]/indexes/+page.svelte +628 -105
- package/src/routes/servers/[server]/databases/[database]/collections/[collection]/mappings/+page.svelte +26 -12
- package/build/client/_app/immutable/assets/0.COnAcXN4.css +0 -1
- package/build/client/_app/immutable/assets/0.COnAcXN4.css.br +0 -0
- package/build/client/_app/immutable/assets/0.COnAcXN4.css.gz +0 -0
- package/build/client/_app/immutable/assets/10.59aTjJn9.css +0 -1
- package/build/client/_app/immutable/assets/10.59aTjJn9.css.br +0 -0
- package/build/client/_app/immutable/assets/10.59aTjJn9.css.gz +0 -0
- package/build/client/_app/immutable/assets/12.BdCOhvz0.css +0 -1
- package/build/client/_app/immutable/assets/12.BdCOhvz0.css.br +0 -0
- package/build/client/_app/immutable/assets/12.BdCOhvz0.css.gz +0 -0
- package/build/client/_app/immutable/assets/Modal.DIIFkbGB.css +0 -1
- package/build/client/_app/immutable/assets/Modal.DIIFkbGB.css.br +0 -0
- package/build/client/_app/immutable/assets/Modal.DIIFkbGB.css.gz +0 -0
- package/build/client/_app/immutable/assets/Panel.B3aWupye.css +0 -1
- package/build/client/_app/immutable/assets/Panel.B3aWupye.css.br +0 -0
- package/build/client/_app/immutable/assets/Panel.B3aWupye.css.gz +0 -0
- package/build/client/_app/immutable/assets/cuprum-cyrillic-400-normal.Cnibl3-L.woff2 +0 -0
- package/build/client/_app/immutable/assets/cuprum-cyrillic-400-normal.RKIjpA76.woff +0 -0
- package/build/client/_app/immutable/assets/cuprum-cyrillic-ext-400-normal.CT5q4ZVh.woff2 +0 -0
- package/build/client/_app/immutable/assets/cuprum-cyrillic-ext-400-normal.iCCFJ4Gn.woff +0 -0
- package/build/client/_app/immutable/assets/cuprum-latin-400-normal.Cbwtr8a4.woff +0 -0
- package/build/client/_app/immutable/assets/cuprum-latin-400-normal.CjFvNwMJ.woff2 +0 -0
- package/build/client/_app/immutable/assets/cuprum-latin-ext-400-normal.BWTJtpjo.woff2 +0 -0
- package/build/client/_app/immutable/assets/cuprum-latin-ext-400-normal.BZXayy47.woff +0 -0
- package/build/client/_app/immutable/assets/cuprum-vietnamese-400-normal.uXRi1gw5.woff +0 -0
- package/build/client/_app/immutable/assets/rajdhani-devanagari-400-normal.BdIzgbsr.woff +0 -0
- package/build/client/_app/immutable/assets/rajdhani-devanagari-400-normal.CTuj2HZW.woff2 +0 -0
- package/build/client/_app/immutable/assets/rajdhani-latin-400-normal.C6_q4usG.woff +0 -0
- package/build/client/_app/immutable/assets/rajdhani-latin-400-normal.CurJOxDW.woff2 +0 -0
- package/build/client/_app/immutable/assets/rajdhani-latin-ext-400-normal.DACPYgMx.woff2 +0 -0
- package/build/client/_app/immutable/assets/rajdhani-latin-ext-400-normal.Der7ynDE.woff +0 -0
- package/build/client/_app/immutable/chunks/3iRu_dUj.js +0 -2
- package/build/client/_app/immutable/chunks/3iRu_dUj.js.br +0 -0
- package/build/client/_app/immutable/chunks/3iRu_dUj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/5nAxkTTP.js +0 -1
- package/build/client/_app/immutable/chunks/5nAxkTTP.js.br +0 -5
- package/build/client/_app/immutable/chunks/5nAxkTTP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BBg-V8FE.js +0 -1
- package/build/client/_app/immutable/chunks/BBg-V8FE.js.br +0 -0
- package/build/client/_app/immutable/chunks/BBg-V8FE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BE8UzJuY.js +0 -1
- package/build/client/_app/immutable/chunks/BE8UzJuY.js.br +0 -0
- package/build/client/_app/immutable/chunks/BE8UzJuY.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BQEBQyfZ.js +0 -1
- package/build/client/_app/immutable/chunks/BQEBQyfZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/BQEBQyfZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BUNgwIS0.js +0 -1
- package/build/client/_app/immutable/chunks/BUNgwIS0.js.br +0 -0
- package/build/client/_app/immutable/chunks/BUNgwIS0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BvKY81hz.js +0 -1
- package/build/client/_app/immutable/chunks/BvKY81hz.js.br +0 -0
- package/build/client/_app/immutable/chunks/BvKY81hz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C0PCwpqt.js +0 -23
- package/build/client/_app/immutable/chunks/C0PCwpqt.js.br +0 -0
- package/build/client/_app/immutable/chunks/C0PCwpqt.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C0rm4mmq.js +0 -1
- package/build/client/_app/immutable/chunks/C0rm4mmq.js.br +0 -0
- package/build/client/_app/immutable/chunks/C0rm4mmq.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C2sjtvBO.js +0 -1
- package/build/client/_app/immutable/chunks/C2sjtvBO.js.br +0 -0
- package/build/client/_app/immutable/chunks/C2sjtvBO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C82Rp5eQ.js +0 -1
- package/build/client/_app/immutable/chunks/C82Rp5eQ.js.br +0 -0
- package/build/client/_app/immutable/chunks/C82Rp5eQ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CGSOl_q_.js +0 -4
- package/build/client/_app/immutable/chunks/CGSOl_q_.js.br +0 -0
- package/build/client/_app/immutable/chunks/CGSOl_q_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CjNnAkTB.js +0 -1
- package/build/client/_app/immutable/chunks/CjNnAkTB.js.br +0 -0
- package/build/client/_app/immutable/chunks/CjNnAkTB.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cx2cA4gg.js +0 -1
- package/build/client/_app/immutable/chunks/Cx2cA4gg.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cx2cA4gg.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D4iRRB8h.js.br +0 -0
- package/build/client/_app/immutable/chunks/D4iRRB8h.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D5ccpaXt.js +0 -1
- package/build/client/_app/immutable/chunks/D5ccpaXt.js.br +0 -0
- package/build/client/_app/immutable/chunks/D5ccpaXt.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D7E46CWw.js +0 -1
- package/build/client/_app/immutable/chunks/D7E46CWw.js.br +0 -0
- package/build/client/_app/immutable/chunks/D7E46CWw.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DR9xdhsg.js.br +0 -0
- package/build/client/_app/immutable/chunks/DR9xdhsg.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DVRQRZnh.js +0 -1
- package/build/client/_app/immutable/chunks/DVRQRZnh.js.br +0 -0
- package/build/client/_app/immutable/chunks/DVRQRZnh.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DcPEQ0_V.js +0 -29
- package/build/client/_app/immutable/chunks/DcPEQ0_V.js.br +0 -0
- package/build/client/_app/immutable/chunks/DcPEQ0_V.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dx56OlI-.js.br +0 -2
- package/build/client/_app/immutable/chunks/Dx56OlI-.js.gz +0 -0
- package/build/client/_app/immutable/chunks/PXh00dfp.js +0 -2
- package/build/client/_app/immutable/chunks/PXh00dfp.js.br +0 -0
- package/build/client/_app/immutable/chunks/PXh00dfp.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.DrdSPfGo.js +0 -2
- package/build/client/_app/immutable/entry/app.DrdSPfGo.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DrdSPfGo.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DGG3CpnK.js +0 -1
- package/build/client/_app/immutable/entry/start.DGG3CpnK.js.br +0 -2
- package/build/client/_app/immutable/entry/start.DGG3CpnK.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.kCLtccjl.js +0 -5
- package/build/client/_app/immutable/nodes/0.kCLtccjl.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.kCLtccjl.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.Dr8p9JHT.js +0 -1
- package/build/client/_app/immutable/nodes/1.Dr8p9JHT.js.br +0 -3
- package/build/client/_app/immutable/nodes/1.Dr8p9JHT.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.B4uwvHd0.js +0 -2
- package/build/client/_app/immutable/nodes/10.B4uwvHd0.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.B4uwvHd0.js.gz +0 -0
- package/build/client/_app/immutable/nodes/11.qfhS2VqU.js +0 -1
- package/build/client/_app/immutable/nodes/11.qfhS2VqU.js.br +0 -0
- package/build/client/_app/immutable/nodes/11.qfhS2VqU.js.gz +0 -0
- package/build/client/_app/immutable/nodes/12.DkZDCIfS.js +0 -1
- package/build/client/_app/immutable/nodes/12.DkZDCIfS.js.br +0 -0
- package/build/client/_app/immutable/nodes/12.DkZDCIfS.js.gz +0 -0
- package/build/client/_app/immutable/nodes/13.DFVzMgyY.js +0 -66
- package/build/client/_app/immutable/nodes/13.DFVzMgyY.js.br +0 -0
- package/build/client/_app/immutable/nodes/13.DFVzMgyY.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DwwrLLW4.js.br +0 -1
- package/build/client/_app/immutable/nodes/2.DwwrLLW4.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.t20GootO.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.t20GootO.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.CXHjkPtf.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.CXHjkPtf.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.C9tcr5B0.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.C9tcr5B0.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.D14iWySv.js +0 -1
- package/build/client/_app/immutable/nodes/7.D14iWySv.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.D14iWySv.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.BSXe_VNA.js +0 -1
- package/build/client/_app/immutable/nodes/8.BSXe_VNA.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.BSXe_VNA.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.BXMJq3Z_.js +0 -2
- package/build/client/_app/immutable/nodes/9.BXMJq3Z_.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.BXMJq3Z_.js.gz +0 -0
- package/build/server/chunks/0-BozXjQlk.js +0 -22
- package/build/server/chunks/0-BozXjQlk.js.map +0 -1
- package/build/server/chunks/1-C_LPMO7A.js +0 -9
- package/build/server/chunks/1-C_LPMO7A.js.map +0 -1
- package/build/server/chunks/10-DGUQMXHt.js.map +0 -1
- package/build/server/chunks/11-CxBnyyqR.js.map +0 -1
- package/build/server/chunks/12-K_JVUnsk.js.map +0 -1
- package/build/server/chunks/7-DvU-_ADW.js.map +0 -1
- package/build/server/chunks/8-CA3-CJtS.js.map +0 -1
- package/build/server/chunks/9-jMOS3lPS.js.map +0 -1
- package/build/server/chunks/JsonValue-WMdIUvRM.js.map +0 -1
- package/build/server/chunks/Modal-zVnoAfFx.js.map +0 -1
- package/build/server/chunks/Panel-yg5YKL4d.js +0 -38
- package/build/server/chunks/Panel-yg5YKL4d.js.map +0 -1
- package/build/server/chunks/PrettyJson-D3CylSVd.js.map +0 -1
- package/build/server/chunks/Tooltip-ys6l541_.js +0 -28
- package/build/server/chunks/Tooltip-ys6l541_.js.map +0 -1
- package/build/server/chunks/_layout.svelte-DMm1Ill8.js +0 -168
- package/build/server/chunks/_layout.svelte-DMm1Ill8.js.map +0 -1
- package/build/server/chunks/_page.svelte-BBHqwhWF.js.map +0 -1
- package/build/server/chunks/_page.svelte-BRxX_vQR.js.map +0 -1
- package/build/server/chunks/_page.svelte-C-13GpOe.js.map +0 -1
- package/build/server/chunks/_page.svelte-Ca9u_35e.js.map +0 -1
- package/build/server/chunks/_page.svelte-Cm_vgRRR.js +0 -206
- package/build/server/chunks/_page.svelte-Cm_vgRRR.js.map +0 -1
- package/build/server/chunks/_page.svelte-DoOBFRSo.js +0 -299
- package/build/server/chunks/_page.svelte-DoOBFRSo.js.map +0 -1
- package/build/server/chunks/client-BFlyU7ZU.js +0 -7
- package/build/server/chunks/event-DVH-6ISX.js.map +0 -1
- package/build/server/chunks/mongo-Db8Vgimy.js.map +0 -1
- package/build/server/chunks/remote-xxtqbu-AjxdDZZ_.js.map +0 -1
- package/build/server/chunks/servers.remote-DrkuAWHP.js.map +0 -1
- package/build/server/chunks/utils-kjxf7BZO.js +0 -39
- package/build/server/chunks/utils-kjxf7BZO.js.map +0 -1
package/src/lib/server/mongo.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger } from "$lib/server/logger";
|
|
2
2
|
import type { CollectionJSON, CollectionMappings, Mappings } from "$lib/types";
|
|
3
|
+
import { resolveSrv } from "dns/promises";
|
|
3
4
|
import { MongoClient, type Collection } from "mongodb";
|
|
4
5
|
import { URL } from "url";
|
|
5
6
|
import { HostsManager } from "./HostsManager";
|
|
@@ -88,8 +89,52 @@ export async function getCollectionJson(
|
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Extract node hosts from a MongoDB connection string
|
|
94
|
+
* Handles both mongodb:// and mongodb+srv:// formats
|
|
95
|
+
*/
|
|
96
|
+
export async function extractNodesFromConnectionString(connectionString: string): Promise<string[]> {
|
|
97
|
+
const url = new URL(connectionString);
|
|
98
|
+
|
|
99
|
+
if (url.protocol === "mongodb+srv:") {
|
|
100
|
+
// For SRV, resolve DNS records
|
|
101
|
+
const hostname = url.hostname;
|
|
102
|
+
const srvRecords = await resolveSrv(`_mongodb._tcp.${hostname}`);
|
|
103
|
+
return srvRecords
|
|
104
|
+
.sort((a, b) => a.priority - b.priority || b.weight - a.weight)
|
|
105
|
+
.map((record) => `${record.name}:${record.port}`)
|
|
106
|
+
.sort();
|
|
107
|
+
}
|
|
108
|
+
if (url.protocol === "mongodb:") {
|
|
109
|
+
// For standard mongodb://, extract hosts from the connection string
|
|
110
|
+
// Format: mongodb://[username:password@]host1[:port1][,host2[:port2],...]/[database][?options]
|
|
111
|
+
const hostsString = url.host;
|
|
112
|
+
if (!hostsString) {
|
|
113
|
+
throw new Error("No hosts found in connection string");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Split by comma to get all hosts and sort lexically
|
|
117
|
+
return hostsString
|
|
118
|
+
.split(",")
|
|
119
|
+
.map((host) => host.trim())
|
|
120
|
+
.filter((host) => host.length > 0)
|
|
121
|
+
.sort();
|
|
122
|
+
}
|
|
123
|
+
throw new TypeError(`Unsupported protocol: ${url.protocol}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
91
126
|
class MongoClientWithMappings extends MongoClient {
|
|
127
|
+
url: string;
|
|
92
128
|
mappings: Record<string, Record<string, Mappings>> = {};
|
|
129
|
+
_id: string;
|
|
130
|
+
name: string;
|
|
131
|
+
|
|
132
|
+
constructor(url: string, _id: string, name: string) {
|
|
133
|
+
super(url);
|
|
134
|
+
this.url = url;
|
|
135
|
+
this._id = _id;
|
|
136
|
+
this.name = name;
|
|
137
|
+
}
|
|
93
138
|
|
|
94
139
|
async getMappings(dbName: string, collectionName: string, opts?: { forceRefresh?: boolean }): Promise<Mappings> {
|
|
95
140
|
if (opts?.forceRefresh || !this.mappings[dbName]?.[collectionName]) {
|
|
@@ -111,7 +156,7 @@ class MongoConnections {
|
|
|
111
156
|
* Todo: better system where we can have mutiple servers with same hostname, and labels for each server that
|
|
112
157
|
* would be displayed in the UI instead of the hostname.
|
|
113
158
|
*/
|
|
114
|
-
private clients: Map<string, MongoClientWithMappings> = new Map();
|
|
159
|
+
private clients: Map<string, MongoClientWithMappings> = new Map(); // _id -> MongoClientWithMappings
|
|
115
160
|
private clientIds: Map<string, string> = new Map(); // hostname -> _id
|
|
116
161
|
private hostsManager: HostsManager;
|
|
117
162
|
private countTimeout = parseInt(process.env.MONGOKU_COUNT_TIMEOUT!, 10) || 30_000;
|
|
@@ -144,8 +189,8 @@ class MongoConnections {
|
|
|
144
189
|
const hostname = url.host || host.path;
|
|
145
190
|
|
|
146
191
|
if (!this.clients.has(hostname)) {
|
|
147
|
-
const client = new MongoClientWithMappings(urlStr);
|
|
148
|
-
this.clients.set(
|
|
192
|
+
const client = new MongoClientWithMappings(urlStr, host._id, hostname);
|
|
193
|
+
this.clients.set(host._id, client);
|
|
149
194
|
this.clientIds.set(hostname, host._id);
|
|
150
195
|
}
|
|
151
196
|
} catch (err) {
|
|
@@ -155,7 +200,11 @@ class MongoConnections {
|
|
|
155
200
|
}
|
|
156
201
|
|
|
157
202
|
getClient(name: string): MongoClientWithMappings {
|
|
158
|
-
const
|
|
203
|
+
const clientId = this.clientIds.get(name) || this.clientIds.get(`${name}:27017`);
|
|
204
|
+
if (!clientId) {
|
|
205
|
+
throw new Error(`Client not found: ${name}`);
|
|
206
|
+
}
|
|
207
|
+
const client = this.clients.get(clientId);
|
|
159
208
|
if (!client) {
|
|
160
209
|
throw new Error(`Client not found: ${name}`);
|
|
161
210
|
}
|
|
@@ -163,13 +212,11 @@ class MongoConnections {
|
|
|
163
212
|
}
|
|
164
213
|
|
|
165
214
|
listClients(): Array<{ name: string; _id: string; client: MongoClient }> {
|
|
166
|
-
return Array.from(this.clients.
|
|
167
|
-
.
|
|
168
|
-
.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
client: client as MongoClient,
|
|
172
|
-
}));
|
|
215
|
+
return Array.from(this.clients.values()).map((client) => ({
|
|
216
|
+
name: client.name,
|
|
217
|
+
_id: client._id,
|
|
218
|
+
client: client as MongoClient,
|
|
219
|
+
}));
|
|
173
220
|
}
|
|
174
221
|
|
|
175
222
|
getCountTimeout() {
|
|
@@ -197,8 +244,8 @@ class MongoConnections {
|
|
|
197
244
|
const hostname = url.host || hostPath;
|
|
198
245
|
|
|
199
246
|
if (!this.clients.has(hostname)) {
|
|
200
|
-
const client = new MongoClientWithMappings(urlStr);
|
|
201
|
-
this.clients.set(
|
|
247
|
+
const client = new MongoClientWithMappings(urlStr, id, hostname);
|
|
248
|
+
this.clients.set(id, client);
|
|
202
249
|
this.clientIds.set(hostname, id);
|
|
203
250
|
}
|
|
204
251
|
} catch (err) {
|
|
@@ -218,27 +265,83 @@ class MongoConnections {
|
|
|
218
265
|
this.clientIds.delete(name);
|
|
219
266
|
}
|
|
220
267
|
|
|
221
|
-
async reconnectClient(
|
|
222
|
-
// Try to find the connection string with the same logic as getClient
|
|
223
|
-
const connectionString = this.hostsManager.getHost(name);
|
|
224
|
-
|
|
225
|
-
if (!connectionString) {
|
|
226
|
-
throw new Error(`Connection string not found for client: ${name}`);
|
|
227
|
-
}
|
|
228
|
-
|
|
268
|
+
async reconnectClient(id: string) {
|
|
229
269
|
// Close the old client
|
|
230
|
-
const oldClient = this.clients.get(
|
|
231
|
-
if (oldClient) {
|
|
232
|
-
|
|
270
|
+
const oldClient = this.clients.get(id);
|
|
271
|
+
if (!oldClient) {
|
|
272
|
+
throw new Error(`Client not found: ${id}`);
|
|
233
273
|
}
|
|
234
274
|
|
|
275
|
+
oldClient.close().catch((err) => logger.error(`Error closing old client ${id}:`, err));
|
|
276
|
+
|
|
235
277
|
// Create a new client
|
|
236
|
-
const newClient = new MongoClientWithMappings(
|
|
237
|
-
this.clients.set(
|
|
278
|
+
const newClient = new MongoClientWithMappings(oldClient.url, id, oldClient.name);
|
|
279
|
+
this.clients.set(id, newClient);
|
|
238
280
|
|
|
239
281
|
// Test the connection
|
|
240
282
|
await newClient.connect();
|
|
241
283
|
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Get the list of nodes from a server's connection string
|
|
287
|
+
*/
|
|
288
|
+
async getServerNodes(serverId: string): Promise<string[]> {
|
|
289
|
+
const client = this.getClient(serverId);
|
|
290
|
+
return extractNodesFromConnectionString(client.url);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Fetch index stats from a specific node using directConnection
|
|
295
|
+
*/
|
|
296
|
+
async getIndexStatsFromNode(
|
|
297
|
+
serverId: string,
|
|
298
|
+
node: string,
|
|
299
|
+
database: string,
|
|
300
|
+
collection: string,
|
|
301
|
+
): Promise<Record<string, { ops: number; since: Date; host: string }>> {
|
|
302
|
+
const client = this.getClient(serverId);
|
|
303
|
+
const url = new URL(client.url);
|
|
304
|
+
|
|
305
|
+
// Build direct connection URL
|
|
306
|
+
// Keep credentials and other params, but set the host to the specific node
|
|
307
|
+
const directUrl = new URL(url.toString());
|
|
308
|
+
directUrl.protocol = "mongodb:";
|
|
309
|
+
directUrl.host = node;
|
|
310
|
+
directUrl.searchParams.set("directConnection", "true");
|
|
311
|
+
|
|
312
|
+
// Keep TLS if it was in the original URL
|
|
313
|
+
if (url.searchParams.has("tls") || url.searchParams.has("ssl") || url.protocol === "mongodb+srv:") {
|
|
314
|
+
directUrl.searchParams.set("tls", "true");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Create a temporary client for this specific node
|
|
318
|
+
let nodeClient: MongoClient | null = null;
|
|
319
|
+
// todo: use await using when possible
|
|
320
|
+
try {
|
|
321
|
+
nodeClient = new MongoClient(directUrl.toString());
|
|
322
|
+
await nodeClient.connect();
|
|
323
|
+
|
|
324
|
+
const coll = nodeClient.db(database).collection(collection);
|
|
325
|
+
const statsResult = await coll.aggregate([{ $indexStats: {} }]).toArray();
|
|
326
|
+
|
|
327
|
+
const indexStats = Object.fromEntries(
|
|
328
|
+
statsResult.map((stat) => [
|
|
329
|
+
stat.name,
|
|
330
|
+
{
|
|
331
|
+
ops: stat.accesses?.ops || 0,
|
|
332
|
+
since: stat.accesses?.since || new Date(),
|
|
333
|
+
host: stat.host || node,
|
|
334
|
+
},
|
|
335
|
+
]),
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
return indexStats;
|
|
339
|
+
} finally {
|
|
340
|
+
if (nodeClient) {
|
|
341
|
+
await nodeClient.close();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
242
345
|
}
|
|
243
346
|
|
|
244
347
|
// Singleton instance
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a number with significant digits
|
|
3
|
+
* @param value The number to format
|
|
4
|
+
* @param digits Number of significant digits (default: 3)
|
|
5
|
+
* @returns Formatted string with appropriate suffix (k, M, B)
|
|
6
|
+
*/
|
|
7
|
+
export function formatSignificantDigits(value: number, digits = 3): string {
|
|
8
|
+
if (value === 0) return "0";
|
|
9
|
+
|
|
10
|
+
const absValue = Math.abs(value);
|
|
11
|
+
const sign = value < 0 ? "-" : "";
|
|
12
|
+
|
|
13
|
+
// Determine the order of magnitude
|
|
14
|
+
if (absValue >= 1_000_000_000) {
|
|
15
|
+
// Billions
|
|
16
|
+
const billions = absValue / 1_000_000_000;
|
|
17
|
+
return `${sign}${billions.toPrecision(digits)}B`;
|
|
18
|
+
} else if (absValue >= 1_000_000) {
|
|
19
|
+
// Millions
|
|
20
|
+
const millions = absValue / 1_000_000;
|
|
21
|
+
return `${sign}${millions.toPrecision(digits)}M`;
|
|
22
|
+
} else if (absValue >= 1_000) {
|
|
23
|
+
// Thousands
|
|
24
|
+
const thousands = absValue / 1_000;
|
|
25
|
+
return `${sign}${thousands.toPrecision(digits)}k`;
|
|
26
|
+
} else {
|
|
27
|
+
// Less than 1000
|
|
28
|
+
return `${sign}${absValue.toPrecision(Math.min(digits, Math.ceil(Math.log10(absValue + 1))))}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a timestamp as relative time ago (e.g., "5m 30s ago", "2h 15m ago")
|
|
3
|
+
*/
|
|
4
|
+
export function formatTimeAgo(timestamp: Date | null): string {
|
|
5
|
+
if (!timestamp) return "";
|
|
6
|
+
|
|
7
|
+
const now = new Date();
|
|
8
|
+
const elapsed = now.getTime() - timestamp.getTime();
|
|
9
|
+
const seconds = Math.floor(elapsed / 1000);
|
|
10
|
+
const minutes = Math.floor(seconds / 60);
|
|
11
|
+
const hours = Math.floor(minutes / 60);
|
|
12
|
+
|
|
13
|
+
if (hours > 0) {
|
|
14
|
+
return `${hours}h ${minutes % 60}m ago`;
|
|
15
|
+
} else if (minutes > 0) {
|
|
16
|
+
return `${minutes}m ${seconds % 60}s ago`;
|
|
17
|
+
} else {
|
|
18
|
+
return `${seconds}s ago`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the common prefix of an array of strings
|
|
3
|
+
*/
|
|
4
|
+
export function findCommonPrefix(strings: string[]): string {
|
|
5
|
+
if (strings.length === 0) return "";
|
|
6
|
+
if (strings.length === 1) return "";
|
|
7
|
+
|
|
8
|
+
let prefix = strings[0];
|
|
9
|
+
for (let i = 1; i < strings.length; i++) {
|
|
10
|
+
while (strings[i].indexOf(prefix) !== 0) {
|
|
11
|
+
prefix = prefix.substring(0, prefix.length - 1);
|
|
12
|
+
if (prefix === "") return "";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return prefix;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Find the common suffix of an array of strings
|
|
20
|
+
*/
|
|
21
|
+
export function findCommonSuffix(strings: string[]): string {
|
|
22
|
+
if (strings.length === 0) return "";
|
|
23
|
+
if (strings.length === 1) return "";
|
|
24
|
+
|
|
25
|
+
// Reverse all strings, find common prefix, then reverse back
|
|
26
|
+
const reversed = strings.map((s) => s.split("").reverse().join(""));
|
|
27
|
+
const reversedPrefix = findCommonPrefix(reversed);
|
|
28
|
+
return reversedPrefix.split("").reverse().join("");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Extract the unique part of a hostname by removing common prefix and suffix
|
|
33
|
+
* Returns the shortened form with "..." markers
|
|
34
|
+
*/
|
|
35
|
+
export function shortenHostname(hostname: string, commonPrefix: string, commonSuffix: string): string {
|
|
36
|
+
if (!commonPrefix && !commonSuffix) {
|
|
37
|
+
return hostname;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let result = hostname;
|
|
41
|
+
|
|
42
|
+
// Remove prefix
|
|
43
|
+
if (commonPrefix && result.startsWith(commonPrefix)) {
|
|
44
|
+
result = result.slice(commonPrefix.length);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Remove suffix
|
|
48
|
+
if (commonSuffix && result.endsWith(commonSuffix)) {
|
|
49
|
+
result = result.slice(0, -commonSuffix.length);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// If nothing left, return the original
|
|
53
|
+
if (result.length === 0) {
|
|
54
|
+
return hostname;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add ellipsis markers
|
|
58
|
+
const hasPrefix = commonPrefix.length > 0;
|
|
59
|
+
const hasSuffix = commonSuffix.length > 0;
|
|
60
|
+
|
|
61
|
+
if (hasPrefix && hasSuffix) {
|
|
62
|
+
return `…${result}…`;
|
|
63
|
+
} else if (hasPrefix) {
|
|
64
|
+
return `…${result}`;
|
|
65
|
+
} else if (hasSuffix) {
|
|
66
|
+
return `${result}…`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get shortened hostnames for a list of hosts
|
|
74
|
+
* Returns an object mapping full hostname to shortened version
|
|
75
|
+
*/
|
|
76
|
+
export function getShortenedHostnames(hosts: string[]): Record<string, string> {
|
|
77
|
+
if (hosts.length <= 1) {
|
|
78
|
+
// No need to shorten if only one host
|
|
79
|
+
return Object.fromEntries(hosts.map((h) => [h, h]));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const commonPrefix = findCommonPrefix(hosts);
|
|
83
|
+
const commonSuffix = findCommonSuffix(hosts);
|
|
84
|
+
|
|
85
|
+
return Object.fromEntries(hosts.map((host) => [host, shortenHostname(host, commonPrefix, commonSuffix)]));
|
|
86
|
+
}
|
|
@@ -141,3 +141,53 @@ export function parseJSON(text: string, opts?: { allowArray?: boolean }): unknow
|
|
|
141
141
|
|
|
142
142
|
return buildObject(objExpression);
|
|
143
143
|
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Serializes an object to a string format that can be parsed back with parseJSON.
|
|
147
|
+
* Handles MongoDB special types like ObjectId, Date, and RegExp.
|
|
148
|
+
*/
|
|
149
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
150
|
+
export function serializeForEditing(obj: any, depth = 0): string {
|
|
151
|
+
const indent = "\t".repeat(depth);
|
|
152
|
+
const nextIndent = "\t".repeat(depth + 1);
|
|
153
|
+
|
|
154
|
+
if (obj === null) return "null";
|
|
155
|
+
if (obj === undefined) return "undefined";
|
|
156
|
+
if (typeof obj === "string") return JSON.stringify(obj);
|
|
157
|
+
if (typeof obj === "number") return obj.toString();
|
|
158
|
+
if (typeof obj === "boolean") return obj.toString();
|
|
159
|
+
|
|
160
|
+
if (Array.isArray(obj)) {
|
|
161
|
+
if (obj.length === 0) return "[]";
|
|
162
|
+
const items = obj.map((item) => `${nextIndent}${serializeForEditing(item, depth + 1)}`).join(",\n");
|
|
163
|
+
return `[\n${items}\n${indent}]`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (typeof obj === "object") {
|
|
167
|
+
// Handle special MongoDB types
|
|
168
|
+
if (obj.$type === "ObjectId") {
|
|
169
|
+
return `new ObjectId("${obj.$value}")`;
|
|
170
|
+
}
|
|
171
|
+
if (obj.$type === "Date") {
|
|
172
|
+
return `new Date("${obj.$value}")`;
|
|
173
|
+
}
|
|
174
|
+
if (obj.$type === "RegExp") {
|
|
175
|
+
return `new RegExp("${obj.$value.$pattern}", "${obj.$value.$flags}")`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Handle regular objects
|
|
179
|
+
const keys = Object.keys(obj);
|
|
180
|
+
if (keys.length === 0) return "{}";
|
|
181
|
+
|
|
182
|
+
const pairs = keys
|
|
183
|
+
.map((key) => {
|
|
184
|
+
const value = serializeForEditing(obj[key], depth + 1);
|
|
185
|
+
return `${nextIndent}${JSON.stringify(key)}: ${value}`;
|
|
186
|
+
})
|
|
187
|
+
.join(",\n");
|
|
188
|
+
|
|
189
|
+
return `{\n${pairs}\n${indent}}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return JSON.stringify(obj);
|
|
193
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the longest common suffix between two strings
|
|
3
|
+
*/
|
|
4
|
+
export function longestCommonSuffix(str1: string, str2: string): string {
|
|
5
|
+
if (str1.length < str2.length && str2.endsWith(str1)) {
|
|
6
|
+
return str1;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
if (str2.length < str1.length && str1.endsWith(str2)) {
|
|
10
|
+
return str2;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let suffix = "";
|
|
14
|
+
let i = str1.length - 1;
|
|
15
|
+
let j = str2.length - 1;
|
|
16
|
+
|
|
17
|
+
while (i >= 0 && j >= 0 && str1[i] === str2[j]) {
|
|
18
|
+
suffix = str1.slice(i);
|
|
19
|
+
i--;
|
|
20
|
+
j--;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return suffix;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Find which string in an array has the longest common suffix with a target string
|
|
28
|
+
* Returns the matching string or null if no good match is found
|
|
29
|
+
*/
|
|
30
|
+
export function findBestSuffixMatch(target: string, candidates: string[]): string | null {
|
|
31
|
+
if (!target || candidates.length === 0) return null;
|
|
32
|
+
|
|
33
|
+
let bestMatch: string | null = null;
|
|
34
|
+
let longestSuffixLength = 0;
|
|
35
|
+
|
|
36
|
+
for (const candidate of candidates) {
|
|
37
|
+
const commonSuffix = longestCommonSuffix(target, candidate);
|
|
38
|
+
if (commonSuffix.length > longestSuffixLength) {
|
|
39
|
+
longestSuffixLength = commonSuffix.length;
|
|
40
|
+
bestMatch = candidate;
|
|
41
|
+
} else if (commonSuffix.length === longestSuffixLength) {
|
|
42
|
+
bestMatch = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return bestMatch;
|
|
47
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { resolve } from "$app/paths";
|
|
3
|
+
import { createPortal } from "$lib/actions/portal";
|
|
3
4
|
import Breadcrumbs from "$lib/components/Breadcrumbs.svelte";
|
|
4
5
|
import Notifications from "$lib/components/Notifications.svelte";
|
|
5
6
|
import OriginWarning from "$lib/components/OriginWarning.svelte";
|
|
@@ -15,26 +16,44 @@
|
|
|
15
16
|
</svelte:head>
|
|
16
17
|
|
|
17
18
|
<div style="min-height: 100vh">
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
<!-- App bar -->
|
|
20
|
+
<header class="sticky top-0 z-50 border-b border-[var(--border-color)] bg-[var(--background-color)]/80">
|
|
21
|
+
<div class="max-w-[96rem] mx-auto px-4 sm:px-6 lg:px-8">
|
|
22
|
+
<div class="h-14 flex items-center gap-3">
|
|
23
|
+
<!-- Logo -->
|
|
24
|
+
<a href={resolve("/")} class="inline-flex items-center gap-2 group no-underline hover:no-underline">
|
|
25
|
+
<span
|
|
26
|
+
class="inline-flex items-center justify-center w-7 h-7 rounded-md bg-black dark:bg-white text-white dark:text-black text-sm font-semibold select-none"
|
|
27
|
+
>
|
|
28
|
+
M
|
|
29
|
+
</span>
|
|
30
|
+
<span class="text-lg font-semibold tracking-tight" style="color: var(--text);">Mongoku</span>
|
|
31
|
+
</a>
|
|
32
|
+
|
|
33
|
+
<div class="hidden md:block w-px h-5 bg-[var(--border-color)]"></div>
|
|
34
|
+
|
|
35
|
+
<!-- Breadcrumbs -->
|
|
36
|
+
<Breadcrumbs />
|
|
37
|
+
|
|
38
|
+
<div class="ml-auto flex items-center gap-2">
|
|
39
|
+
<!-- View tabs -->
|
|
40
|
+
<PageSwitcher class="" />
|
|
41
|
+
<ThemeSwitcher />
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
24
44
|
</div>
|
|
25
|
-
</
|
|
45
|
+
</header>
|
|
26
46
|
|
|
27
47
|
<OriginWarning serverOrigin={data.serverOrigin} readOnly={data.readOnly} />
|
|
28
48
|
|
|
29
|
-
|
|
49
|
+
<!-- Main -->
|
|
50
|
+
<main class="max-w-[96rem] mx-auto px-4 sm:px-6 lg:px-8 py-6 md:py-10">
|
|
30
51
|
<Notifications />
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
</div>
|
|
52
|
+
<div class="flex flex-col gap-6">
|
|
53
|
+
{@render children()}
|
|
54
|
+
</div>
|
|
55
|
+
</main>
|
|
34
56
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
border-bottom: var(--border);
|
|
39
|
-
}
|
|
40
|
-
</style>
|
|
57
|
+
<!-- Portal container for tooltips and other overlay content -->
|
|
58
|
+
<div use:createPortal></div>
|
|
59
|
+
</div>
|
|
@@ -73,17 +73,16 @@
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
async function retryServerConnection(server: Server) {
|
|
76
|
-
|
|
77
|
-
retryingServers.add(name);
|
|
76
|
+
retryingServers.add(server._id);
|
|
78
77
|
|
|
79
78
|
try {
|
|
80
|
-
await retryConnection(
|
|
79
|
+
await retryConnection(server._id);
|
|
81
80
|
notificationStore.notifySuccess("Connection restored successfully");
|
|
82
81
|
await invalidateAll();
|
|
83
82
|
} catch (error) {
|
|
84
83
|
notificationStore.notifyError(error, "Failed to retry connection");
|
|
85
84
|
} finally {
|
|
86
|
-
retryingServers.delete(
|
|
85
|
+
retryingServers.delete(server._id);
|
|
87
86
|
}
|
|
88
87
|
}
|
|
89
88
|
</script>
|
|
@@ -171,16 +170,16 @@
|
|
|
171
170
|
{/if}
|
|
172
171
|
{/await}
|
|
173
172
|
</td>
|
|
174
|
-
<td style="width:
|
|
173
|
+
<td style="width: 250px">
|
|
175
174
|
<div class="flex gap-2 justify-end">
|
|
176
175
|
{#await server.details then details}
|
|
177
176
|
{#if "error" in details && details.error}
|
|
178
177
|
<button
|
|
179
178
|
class="btn btn-default btn-sm -my-2 hidden group-hover:inline"
|
|
180
179
|
onclick={() => retryServerConnection(server)}
|
|
181
|
-
disabled={retryingServers.has(
|
|
180
|
+
disabled={retryingServers.has(server._id)}
|
|
182
181
|
>
|
|
183
|
-
{retryingServers.has(
|
|
182
|
+
{retryingServers.has(server._id) ? "Retrying..." : "Retry"}
|
|
184
183
|
</button>
|
|
185
184
|
{/if}
|
|
186
185
|
{/await}
|
|
@@ -1,11 +1,51 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { dropDatabase as dropDatabaseCommand } from "$api/servers.remote";
|
|
3
|
+
import { invalidateAll } from "$app/navigation";
|
|
2
4
|
import { resolve } from "$app/paths";
|
|
5
|
+
import Modal from "$lib/components/Modal.svelte";
|
|
3
6
|
import Panel from "$lib/components/Panel.svelte";
|
|
4
7
|
import TooltipTable from "$lib/components/TooltipTable.svelte";
|
|
8
|
+
import { notificationStore } from "$lib/stores/notifications.svelte";
|
|
5
9
|
import { formatBytes } from "$lib/utils/filters";
|
|
6
10
|
import type { PageData } from "./$types";
|
|
7
11
|
|
|
8
12
|
let { data }: { data: PageData } = $props();
|
|
13
|
+
|
|
14
|
+
type Database = PageData["databases"][number];
|
|
15
|
+
|
|
16
|
+
let showDropModal = $state(false);
|
|
17
|
+
let databaseToDrop = $state<Database | null>(null);
|
|
18
|
+
let isDropping = $state(false);
|
|
19
|
+
|
|
20
|
+
function openDropModal(database: Database) {
|
|
21
|
+
databaseToDrop = database;
|
|
22
|
+
showDropModal = true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function closeDropModal() {
|
|
26
|
+
showDropModal = false;
|
|
27
|
+
databaseToDrop = null;
|
|
28
|
+
isDropping = false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function confirmDrop() {
|
|
32
|
+
if (!databaseToDrop || isDropping) return;
|
|
33
|
+
|
|
34
|
+
isDropping = true;
|
|
35
|
+
try {
|
|
36
|
+
await dropDatabaseCommand({
|
|
37
|
+
server: data.server,
|
|
38
|
+
database: databaseToDrop.name,
|
|
39
|
+
});
|
|
40
|
+
notificationStore.notifySuccess(`Database "${databaseToDrop.name}" dropped successfully`);
|
|
41
|
+
closeDropModal();
|
|
42
|
+
// Reload the page to get updated databases
|
|
43
|
+
await invalidateAll();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
notificationStore.notifyError(error, "Failed to drop database");
|
|
46
|
+
isDropping = false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
9
49
|
</script>
|
|
10
50
|
|
|
11
51
|
<Panel title="Databases on {data.server}">
|
|
@@ -15,12 +55,13 @@
|
|
|
15
55
|
<th>Name</th>
|
|
16
56
|
<th>Collections</th>
|
|
17
57
|
<th>Size</th>
|
|
58
|
+
<th></th>
|
|
18
59
|
</tr>
|
|
19
60
|
</thead>
|
|
20
61
|
<tbody>
|
|
21
62
|
{#if data.databases && data.databases.length > 0}
|
|
22
63
|
{#each data.databases as database (database.name)}
|
|
23
|
-
<tr>
|
|
64
|
+
<tr class="group">
|
|
24
65
|
<td>
|
|
25
66
|
<a
|
|
26
67
|
href={resolve(
|
|
@@ -74,11 +115,23 @@
|
|
|
74
115
|
</TooltipTable>
|
|
75
116
|
{/if}
|
|
76
117
|
</td>
|
|
118
|
+
<td style="width: 100px">
|
|
119
|
+
<div class="flex justify-end">
|
|
120
|
+
{#if !data.readOnly}
|
|
121
|
+
<button
|
|
122
|
+
class="btn btn-outline-danger btn-sm -my-2 hidden group-hover:inline"
|
|
123
|
+
onclick={() => openDropModal(database)}
|
|
124
|
+
>
|
|
125
|
+
Drop
|
|
126
|
+
</button>
|
|
127
|
+
{/if}
|
|
128
|
+
</div>
|
|
129
|
+
</td>
|
|
77
130
|
</tr>
|
|
78
131
|
{/each}
|
|
79
132
|
{:else}
|
|
80
133
|
<tr>
|
|
81
|
-
<td colspan="
|
|
134
|
+
<td colspan="4">
|
|
82
135
|
<div class="text-center">No databases...</div>
|
|
83
136
|
</td>
|
|
84
137
|
</tr>
|
|
@@ -86,3 +139,19 @@
|
|
|
86
139
|
</tbody>
|
|
87
140
|
</table>
|
|
88
141
|
</Panel>
|
|
142
|
+
|
|
143
|
+
<Modal show={showDropModal} onclose={closeDropModal} title="Drop Database">
|
|
144
|
+
<p>
|
|
145
|
+
Are you sure you want to drop the database <strong>{databaseToDrop?.name}</strong>? This action cannot be undone.
|
|
146
|
+
</p>
|
|
147
|
+
{#snippet footer()}
|
|
148
|
+
<button class="btn btn-default btn-sm" onclick={closeDropModal} disabled={isDropping}>Cancel</button>
|
|
149
|
+
<button class="btn btn-outline-danger btn-sm" onclick={confirmDrop} disabled={isDropping}>
|
|
150
|
+
{#if isDropping}
|
|
151
|
+
Dropping...
|
|
152
|
+
{:else}
|
|
153
|
+
Drop Database
|
|
154
|
+
{/if}
|
|
155
|
+
</button>
|
|
156
|
+
{/snippet}
|
|
157
|
+
</Modal>
|