diff --git a/frontend/src/components/codeeditor.svelte b/frontend/src/components/codeeditor.svelte
new file mode 100644
index 0000000..262eaea
--- /dev/null
+++ b/frontend/src/components/codeeditor.svelte
@@ -0,0 +1,57 @@
+
+
+
+
+
diff --git a/frontend/src/components/icon.svelte b/frontend/src/components/icon.svelte
index 5a024b0..a30708e 100644
--- a/frontend/src/components/icon.svelte
+++ b/frontend/src/components/icon.svelte
@@ -142,5 +142,7 @@
{:else if name === 'loading'}
+ {:else if name === 'shell'}
+
{/if}
diff --git a/frontend/src/components/objecteditor.svelte b/frontend/src/components/objecteditor.svelte
index ecb7359..756d9d4 100644
--- a/frontend/src/components/objecteditor.svelte
+++ b/frontend/src/components/objecteditor.svelte
@@ -1,41 +1,16 @@
-
-
-
+
diff --git a/frontend/src/lib/stores/hosttree.js b/frontend/src/lib/stores/hosttree.js
index c5c08d1..5806cab 100644
--- a/frontend/src/lib/stores/hosttree.js
+++ b/frontend/src/lib/stores/hosttree.js
@@ -16,6 +16,7 @@ import {
DropCollection,
DropDatabase,
DropIndex,
+ ExecuteShellScript,
GetIndexes,
Hosts,
OpenCollection,
@@ -239,6 +240,11 @@ async function refresh() {
});
});
};
+
+ collection.executeShellScript = async function(script) {
+ const result = await ExecuteShellScript(hostKey, dbKey, collKey, script);
+ return result;
+ };
}
await refresh();
diff --git a/frontend/src/organisms/connection/collection/index.svelte b/frontend/src/organisms/connection/collection/index.svelte
index a9a9aaf..dd33f35 100644
--- a/frontend/src/organisms/connection/collection/index.svelte
+++ b/frontend/src/organisms/connection/collection/index.svelte
@@ -9,6 +9,7 @@
import Indexes from './indexes.svelte';
import Insert from './insert.svelte';
import Remove from './remove.svelte';
+ import Shell from './shell.svelte';
import Stats from './stats.svelte';
import Update from './update.svelte';
@@ -50,6 +51,7 @@
{ key: 'remove', icon: 'trash', title: 'Remove' },
{ key: 'indexes', icon: 'list', title: 'Indexes' },
{ key: 'aggregate', icon: 're', title: 'Aggregate' },
+ { key: 'shell', icon: 'shell', title: 'Shell' },
]}
bind:selectedKey={tab} />
@@ -61,6 +63,7 @@
{:else if tab === 'remove'}
{:else if tab === 'indexes'}
{:else if tab === 'aggregate'}
+ {:else if tab === 'shell'}
{/if}
{/key}
diff --git a/frontend/src/organisms/connection/collection/shell.svelte b/frontend/src/organisms/connection/collection/shell.svelte
new file mode 100644
index 0000000..03581fd
--- /dev/null
+++ b/frontend/src/organisms/connection/collection/shell.svelte
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+ {#if result.errorTitle || result.errorDescription}
+
+
+
+ {:else}
+
{result.output || ''}
+ {/if}
+
+
+
+
+ {#key result}
+
+ {#if result?.status}
+ Exit code: {result.status}
+ {/if}
+
+ {/key}
+
+
+
+
+
+
diff --git a/frontend/wailsjs/go/app/App.d.ts b/frontend/wailsjs/go/app/App.d.ts
index 9e3dd2c..e1268e5 100755
--- a/frontend/wailsjs/go/app/App.d.ts
+++ b/frontend/wailsjs/go/app/App.d.ts
@@ -20,6 +20,8 @@ export function DropIndex(arg1:string,arg2:string,arg3:string,arg4:string):Promi
export function Environment():Promise;
+export function ExecuteShellScript(arg1:string,arg2:string,arg3:string,arg4:string):Promise;
+
export function FindItems(arg1:string,arg2:string,arg3:string,arg4:string):Promise;
export function GetIndexes(arg1:string,arg2:string,arg3:string):Promise;
diff --git a/frontend/wailsjs/go/app/App.js b/frontend/wailsjs/go/app/App.js
index 5552e87..885d7c2 100755
--- a/frontend/wailsjs/go/app/App.js
+++ b/frontend/wailsjs/go/app/App.js
@@ -30,6 +30,10 @@ export function Environment() {
return window['go']['app']['App']['Environment']();
}
+export function ExecuteShellScript(arg1, arg2, arg3, arg4) {
+ return window['go']['app']['App']['ExecuteShellScript'](arg1, arg2, arg3, arg4);
+}
+
export function FindItems(arg1, arg2, arg3, arg4) {
return window['go']['app']['App']['FindItems'](arg1, arg2, arg3, arg4);
}
diff --git a/internal/app/app.go b/internal/app/app.go
index 684090c..1795e4a 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -26,6 +26,7 @@ type EnvironmentInfo struct {
HasMongoExport bool `json:"hasMongoExport"`
HasMongoDump bool `json:"hasMongoDump"`
+ HasMongoShell bool `json:"hasMongoShell"`
HomeDirectory string `json:"homeDirectory"`
DataDirectory string `json:"dataDirectory"`
@@ -50,6 +51,9 @@ func NewApp(version string) *App {
_, err = exec.LookPath("mongoexport")
a.Env.HasMongoExport = err == nil
+ _, err = exec.LookPath("mongosh")
+ a.Env.HasMongoShell = err == nil
+
a.Env.HomeDirectory, err = os.UserHomeDir()
if err != nil {
panic(errors.New("encountered an error while getting home directory"))
diff --git a/internal/app/collection_shell.go b/internal/app/collection_shell.go
new file mode 100644
index 0000000..b9c2455
--- /dev/null
+++ b/internal/app/collection_shell.go
@@ -0,0 +1,83 @@
+package app
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path"
+
+ "github.com/google/uuid"
+ "github.com/wailsapp/wails/v2/pkg/runtime"
+)
+
+type ExecuteShellScriptResult struct {
+ Output string `json:"output"`
+ Status int `json:"status"`
+ ErrorTitle string `json:"errorTitle"`
+ ErrorDescription string `json:"errorDescription"`
+}
+
+func (a *App) ExecuteShellScript(hostKey, dbKey, collKey, script string) (result ExecuteShellScriptResult) {
+ if !a.Env.HasMongoShell {
+ result.ErrorTitle = "mongosh not found"
+ result.ErrorDescription = "The mongosh executable is required to run a shell script. Please see https://www.mongodb.com/docs/mongodb-shell/install/"
+ return
+ }
+
+ hosts, err := a.Hosts()
+ if err != nil {
+ result.ErrorTitle = "Could not get hosts"
+ result.ErrorDescription = err.Error()
+ return
+ }
+
+ host, hostFound := hosts[hostKey]
+ if !hostFound {
+ result.ErrorTitle = "The specified host does not seem to exist"
+ return
+ }
+
+ id, err := uuid.NewRandom()
+ if err != nil {
+ runtime.LogErrorf(a.ctx, "Shell: failed to generate a UUID: %s", err.Error())
+ result.ErrorTitle = "Could not generate UUID"
+ result.ErrorDescription = err.Error()
+ return
+ }
+
+ dirname := path.Join(a.Env.DataDirectory, "Shell Scripts")
+ fname := path.Join(dirname, fmt.Sprintf("%s.js", id.String()))
+
+ if err := os.MkdirAll(dirname, os.ModePerm); err != nil {
+ runtime.LogWarningf(a.ctx, "Shell: failed to mkdir %s", err.Error())
+ result.ErrorTitle = "Could not create temporary directory"
+ result.ErrorDescription = err.Error()
+ return
+ }
+
+ script = fmt.Sprintf("db = connect('%s');\n\n%s", host.URI, script)
+
+ if err := os.WriteFile(fname, []byte(script), os.ModePerm); err != nil {
+ runtime.LogWarningf(a.ctx, "Shell: failed to write script to %s", err.Error())
+ result.ErrorTitle = "Could not create temporary script file"
+ result.ErrorDescription = err.Error()
+ return
+ }
+
+ cmd := exec.Command("mongosh", "--file", fname)
+ stdout, err := cmd.Output()
+
+ if exiterr, ok := err.(*exec.ExitError); ok {
+ result.Status = exiterr.ExitCode()
+ } else if err != nil {
+ runtime.LogWarningf(a.ctx, "Shell: failed to execute: mongosh --file %s: %s", fname, err.Error())
+ result.ErrorTitle = "Could not execute script"
+ result.ErrorDescription = err.Error()
+ return
+ } else {
+ result.Status = 0
+ }
+
+ result.Output = string(stdout)
+ return
+}