1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-01-18 13:07:58 +00:00

Initial commit

This commit is contained in:
Romein van Buren 2023-01-10 17:28:27 +01:00
commit 101d892139
Signed by: romein
GPG Key ID: 0EFF8478ADDF6C49
45 changed files with 7976 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/bin
node_modules
frontend/dist

18
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
"editor.tabSize": 2,
"[go]": {
"editor.insertSpaces": false,
"editor.tabSize": 4,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"editor.suggest.snippetsPreventQuickSuggestions": false
},
"[css]": {
"editor.suggest.insertMode": "replace",
"editor.tabSize": 2
}
}

35
build/README.md Normal file
View File

@ -0,0 +1,35 @@
# Build Directory
The build directory is used to house all the build files and assets for your application.
The structure is:
* bin - Output directory
* darwin - macOS specific files
* windows - Windows specific files
## Mac
The `darwin` directory holds files specific to Mac builds.
These may be customised and used as part of the build. To return these files to the default state, simply delete them
and
build with `wails build`.
The directory contains the following files:
- `Info.plist` - the main plist file used for Mac builds. It is used when building using `wails build`.
- `Info.dev.plist` - same as the main plist file but used when building using `wails dev`.
## Windows
The `windows` directory contains the manifest and rc files used when building with `wails build`.
These may be customised for your application. To return these files to the default state, simply delete them and
build with `wails build`.
- `icon.ico` - The icon used for the application. This is used when building using `wails build`. If you wish to
use a different icon, simply replace this file with your own. If it is missing, a new `icon.ico` file
will be created using the `appicon.png` file in the build directory.
- `installer/*` - The files used to create the Windows installer. These are used when building using `wails build`.
- `info.json` - Application details used for Windows builds. The data here will be used by the Windows installer,
as well as the application itself (right click the exe -> properties -> details)
- `wails.exe.manifest` - The main application manifest file.

BIN
build/appicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

View File

@ -0,0 +1,32 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>{{.Info.ProductName}}</string>
<key>CFBundleExecutable</key>
<string>{{.Name}}</string>
<key>CFBundleIdentifier</key>
<string>com.wails.{{.Name}}</string>
<key>CFBundleVersion</key>
<string>{{.Info.ProductVersion}}</string>
<key>CFBundleGetInfoString</key>
<string>{{.Info.Comments}}</string>
<key>CFBundleShortVersionString</key>
<string>{{.Info.ProductVersion}}</string>
<key>CFBundleIconFile</key>
<string>iconfile</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.0</string>
<key>NSHighResolutionCapable</key>
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>{{.Info.Copyright}}</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsLocalNetworking</key>
<true/>
</dict>
</dict>
</plist>

27
build/darwin/Info.plist Normal file
View File

@ -0,0 +1,27 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>{{.Info.ProductName}}</string>
<key>CFBundleExecutable</key>
<string>{{.Name}}</string>
<key>CFBundleIdentifier</key>
<string>com.wails.{{.Name}}</string>
<key>CFBundleVersion</key>
<string>{{.Info.ProductVersion}}</string>
<key>CFBundleGetInfoString</key>
<string>{{.Info.Comments}}</string>
<key>CFBundleShortVersionString</key>
<string>{{.Info.ProductVersion}}</string>
<key>CFBundleIconFile</key>
<string>iconfile</string>
<key>LSMinimumSystemVersion</key>
<string>10.13.0</string>
<key>NSHighResolutionCapable</key>
<string>true</string>
<key>NSHumanReadableCopyright</key>
<string>{{.Info.Copyright}}</string>
</dict>
</plist>

BIN
build/windows/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

15
build/windows/info.json Normal file
View File

@ -0,0 +1,15 @@
{
"fixed": {
"file_version": "{{.Info.ProductVersion}}"
},
"info": {
"0000": {
"ProductVersion": "{{.Info.ProductVersion}}",
"CompanyName": "{{.Info.CompanyName}}",
"FileDescription": "{{.Info.ProductName}}",
"LegalCopyright": "{{.Info.Copyright}}",
"ProductName": "{{.Info.ProductName}}",
"Comments": "{{.Info.Comments}}"
}
}
}

View File

@ -0,0 +1,101 @@
Unicode true
####
## Please note: Template replacements don't work in this file. They are provided with default defines like
## mentioned underneath.
## If the keyword is not defined, "wails_tools.nsh" will populate them with the values from ProjectInfo.
## If they are defined here, "wails_tools.nsh" will not touch them. This allows to use this project.nsi manually
## from outside of Wails for debugging and development of the installer.
##
## For development first make a wails nsis build to populate the "wails_tools.nsh":
## > wails build --target windows/amd64 --nsis
## Then you can call makensis on this file with specifying the path to your binary:
## For a AMD64 only installer:
## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app.exe
## For a ARM64 only installer:
## > makensis -DARG_WAILS_ARM64_BINARY=..\..\bin\app.exe
## For a installer with both architectures:
## > makensis -DARG_WAILS_AMD64_BINARY=..\..\bin\app-amd64.exe -DARG_WAILS_ARM64_BINARY=..\..\bin\app-arm64.exe
####
## The following information is taken from the ProjectInfo file, but they can be overwritten here.
####
## !define INFO_PROJECTNAME "MyProject" # Default "{{.Name}}"
## !define INFO_COMPANYNAME "MyCompany" # Default "{{.Info.CompanyName}}"
## !define INFO_PRODUCTNAME "MyProduct" # Default "{{.Info.ProductName}}"
## !define INFO_PRODUCTVERSION "1.0.0" # Default "{{.Info.ProductVersion}}"
## !define INFO_COPYRIGHT "Copyright" # Default "{{.Info.Copyright}}"
###
## !define PRODUCT_EXECUTABLE "Application.exe" # Default "${INFO_PROJECTNAME}.exe"
## !define UNINST_KEY_NAME "UninstKeyInRegistry" # Default "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
####
## !define REQUEST_EXECUTION_LEVEL "admin" # Default "admin" see also https://nsis.sourceforge.io/Docs/Chapter4.html
####
## Include the wails tools
####
!include "wails_tools.nsh"
# The version information for this two must consist of 4 parts
VIProductVersion "${INFO_PRODUCTVERSION}.0"
VIFileVersion "${INFO_PRODUCTVERSION}.0"
VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}"
VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer"
VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}"
VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}"
VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}"
VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}"
!include "MUI.nsh"
!define MUI_ICON "..\icon.ico"
!define MUI_UNICON "..\icon.ico"
# !define MUI_WELCOMEFINISHPAGE_BITMAP "resources\leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314
!define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps
!define MUI_ABORTWARNING # This will warn the user if they exit from the installer.
!insertmacro MUI_PAGE_WELCOME # Welcome to the installer page.
# !insertmacro MUI_PAGE_LICENSE "resources\eula.txt" # Adds a EULA page to the installer
!insertmacro MUI_PAGE_DIRECTORY # In which folder install page.
!insertmacro MUI_PAGE_INSTFILES # Installing page.
!insertmacro MUI_PAGE_FINISH # Finished installation page.
!insertmacro MUI_UNPAGE_INSTFILES # Uinstalling page
!insertmacro MUI_LANGUAGE "English" # Set the Language of the installer
## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1
#!uninstfinalize 'signtool --file "%1"'
#!finalize 'signtool --file "%1"'
Name "${INFO_PRODUCTNAME}"
OutFile "..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file.
InstallDir "$PROGRAMFILES64\${INFO_COMPANYNAME}\${INFO_PRODUCTNAME}" # Default installing folder ($PROGRAMFILES is Program Files folder).
ShowInstDetails show # This will always show the installation details.
Function .onInit
!insertmacro wails.checkArchitecture
FunctionEnd
Section
!insertmacro wails.webview2runtime
SetOutPath $INSTDIR
!insertmacro wails.files
CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
!insertmacro wails.writeUninstaller
SectionEnd
Section "uninstall"
RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath
RMDir /r $INSTDIR
Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk"
Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"
!insertmacro wails.deleteUninstaller
SectionEnd

View File

@ -0,0 +1,171 @@
# DO NOT EDIT - Generated automatically by `wails build`
!include "x64.nsh"
!include "WinVer.nsh"
!include "FileFunc.nsh"
!ifndef INFO_PROJECTNAME
!define INFO_PROJECTNAME "{{.Name}}"
!endif
!ifndef INFO_COMPANYNAME
!define INFO_COMPANYNAME "{{.Info.CompanyName}}"
!endif
!ifndef INFO_PRODUCTNAME
!define INFO_PRODUCTNAME "{{.Info.ProductName}}"
!endif
!ifndef INFO_PRODUCTVERSION
!define INFO_PRODUCTVERSION "{{.Info.ProductVersion}}"
!endif
!ifndef INFO_COPYRIGHT
!define INFO_COPYRIGHT "{{.Info.Copyright}}"
!endif
!ifndef PRODUCT_EXECUTABLE
!define PRODUCT_EXECUTABLE "${INFO_PROJECTNAME}.exe"
!endif
!ifndef UNINST_KEY_NAME
!define UNINST_KEY_NAME "${INFO_COMPANYNAME}${INFO_PRODUCTNAME}"
!endif
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINST_KEY_NAME}"
!ifndef REQUEST_EXECUTION_LEVEL
!define REQUEST_EXECUTION_LEVEL "admin"
!endif
RequestExecutionLevel "${REQUEST_EXECUTION_LEVEL}"
!ifdef ARG_WAILS_AMD64_BINARY
!define SUPPORTS_AMD64
!endif
!ifdef ARG_WAILS_ARM64_BINARY
!define SUPPORTS_ARM64
!endif
!ifdef SUPPORTS_AMD64
!ifdef SUPPORTS_ARM64
!define ARCH "amd64_arm64"
!else
!define ARCH "amd64"
!endif
!else
!ifdef SUPPORTS_ARM64
!define ARCH "arm64"
!else
!error "Wails: Undefined ARCH, please provide at least one of ARG_WAILS_AMD64_BINARY or ARG_WAILS_ARM64_BINARY"
!endif
!endif
!macro wails.checkArchitecture
!ifndef WAILS_WIN10_REQUIRED
!define WAILS_WIN10_REQUIRED "This product is only supported on Windows 10 (Server 2016) and later."
!endif
!ifndef WAILS_ARCHITECTURE_NOT_SUPPORTED
!define WAILS_ARCHITECTURE_NOT_SUPPORTED "This product can't be installed on the current Windows architecture. Supports: ${ARCH}"
!endif
${If} ${AtLeastWin10}
!ifdef SUPPORTS_AMD64
${if} ${IsNativeAMD64}
Goto ok
${EndIf}
!endif
!ifdef SUPPORTS_ARM64
${if} ${IsNativeARM64}
Goto ok
${EndIf}
!endif
IfSilent silentArch notSilentArch
silentArch:
SetErrorLevel 65
Abort
notSilentArch:
MessageBox MB_OK "${WAILS_ARCHITECTURE_NOT_SUPPORTED}"
Quit
${else}
IfSilent silentWin notSilentWin
silentWin:
SetErrorLevel 64
Abort
notSilentWin:
MessageBox MB_OK "${WAILS_WIN10_REQUIRED}"
Quit
${EndIf}
ok:
!macroend
!macro wails.files
!ifdef SUPPORTS_AMD64
${if} ${IsNativeAMD64}
File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_AMD64_BINARY}"
${EndIf}
!endif
!ifdef SUPPORTS_ARM64
${if} ${IsNativeARM64}
File "/oname=${PRODUCT_EXECUTABLE}" "${ARG_WAILS_ARM64_BINARY}"
${EndIf}
!endif
!macroend
!macro wails.writeUninstaller
WriteUninstaller "$INSTDIR\uninstall.exe"
SetRegView 64
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}"
WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKLM "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD HKLM "${UNINST_KEY}" "EstimatedSize" "$0"
!macroend
!macro wails.deleteUninstaller
Delete "$INSTDIR\uninstall.exe"
SetRegView 64
DeleteRegKey HKLM "${UNINST_KEY}"
!macroend
# Install webview2 by launching the bootstrapper
# See https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/distribution#online-only-deployment
!macro wails.webview2runtime
!ifndef WAILS_INSTALL_WEBVIEW_DETAILPRINT
!define WAILS_INSTALL_WEBVIEW_DETAILPRINT "Installing: WebView2 Runtime"
!endif
SetRegView 64
# If the admin key exists and is not empty then webview2 is already installed
ReadRegStr $0 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${If} $0 != ""
Goto ok
${EndIf}
${If} ${REQUEST_EXECUTION_LEVEL} == "user"
# If the installer is run in user level, check the user specific key exists and is not empty then webview2 is already installed
ReadRegStr $0 HKCU "Software\Microsoft\EdgeUpdate\Clients{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" "pv"
${If} $0 != ""
Goto ok
${EndIf}
${EndIf}
SetDetailsPrint both
DetailPrint "${WAILS_INSTALL_WEBVIEW_DETAILPRINT}"
SetDetailsPrint listonly
InitPluginsDir
CreateDirectory "$pluginsdir\webview2bootstrapper"
SetOutPath "$pluginsdir\webview2bootstrapper"
File "tmp\MicrosoftEdgeWebview2Setup.exe"
ExecWait '"$pluginsdir\webview2bootstrapper\MicrosoftEdgeWebview2Setup.exe" /silent /install'
SetDetailsPrint both
ok:
!macroend

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity type="win32" name="com.wails.{{.Name}}" version="{{.Info.ProductVersion}}.0" processorArchitecture="*"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
</dependentAssembly>
</dependency>
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- fallback for Windows 7 and 8 -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported -->
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

12
frontend/index.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>mongodup</title>
</head>
<body>
<div id="app"></div>
<script src="./src/main.js" type="module"></script>
</body>
</html>

38
frontend/jsconfig.json Normal file
View File

@ -0,0 +1,38 @@
{
"compilerOptions": {
"moduleResolution": "Node",
"target": "ESNext",
"module": "ESNext",
/**
* svelte-preprocess cannot figure out whether you have
* a value or a type, so tell TypeScript to enforce using
* `import type` instead of `import` for Types.
*/
"importsNotUsedAsValues": "error",
"isolatedModules": true,
"resolveJsonModule": true,
/**
* To have warnings / errors of the Svelte compiler at the
* correct position, enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable this if you'd like to use dynamic types.
*/
"checkJs": true
},
/**
* Use global.d.ts instead of compilerOptions.types
* to avoid limiting type declarations.
*/
"include": [
"src/**/*.d.ts",
"src/**/*.js",
"src/**/*.svelte"
]
}

5430
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

22
frontend/package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.1",
"eslint": "^8.31.0",
"eslint-config-nodejs": "github:johbog/eslint-config-nodejs",
"eslint-config-svelte3": "github:johbog/eslint-config-svelte3",
"svelte": "^3.49.0",
"vite": "^3.0.0"
},
"eslintConfig": {
"extends": ["nodejs", "svelte3"]
}
}

1
frontend/package.json.md5 Executable file
View File

@ -0,0 +1 @@
2c80c12f4d36310f15d586e535d30d27

27
frontend/src/actions.js Normal file
View File

@ -0,0 +1,27 @@
export function input(node, { json } = { json: false }) {
const handleInput = () => {
if (json) {
try {
JSON.parse(node.value);
node.classList.remove('invalid');
}
catch {
node.classList.add('invalid');
}
}
};
const handleFocus = () => {
node.select();
};
node.addEventListener('focus', handleFocus);
node.addEventListener('input', handleInput);
return {
destroy: () => {
node.removeEventListener('focus', handleFocus);
node.removeEventListener('input', handleInput);
},
};
}

120
frontend/src/app.svelte Normal file
View File

@ -0,0 +1,120 @@
<script>
import { onMount } from 'svelte';
import { Hosts, OpenCollection, OpenConnection, OpenDatabase } from '../wailsjs/go/main/App';
import AddressBar from './organisms/addressbar/index.svelte';
import Grid from './components/grid.svelte';
import CollectionDetail from './organisms/collection-detail/index.svelte';
import { busy } from './stores';
const connections = {};
let hosts = {};
let activeHostKey = '';
let activeDbKey = '';
let activeCollKey = '';
let addressBarModalOpen = false;
$: host = hosts[activeHostKey];
$: connection = connections[activeHostKey];
$: database = connection?.databases[activeDbKey];
$: collection = database?.collections?.[activeCollKey];
async function openConnection(hostKey) {
$busy = true;
const databases = await OpenConnection(hostKey);
if (databases) {
connections[hostKey] = { databases: {} };
databases.forEach(dbKey => {
connections[hostKey].databases[dbKey] = { collections: {} };
});
activeHostKey = hostKey;
addressBarModalOpen = false;
window.runtime.WindowSetTitle(`${host.name} - Mongodup`);
}
$busy = false;
}
async function openDatabase(dbKey) {
$busy = true;
const collections = await OpenDatabase(activeHostKey, dbKey);
for (const collKey of collections || []) {
connections[activeHostKey].databases[dbKey].collections[collKey] = {};
}
$busy = false;
}
async function openCollection(collKey) {
$busy = true;
const stats = await OpenCollection(activeHostKey, activeDbKey, collKey);
connections[activeHostKey].databases[activeDbKey].collections[collKey].stats = stats;
$busy = false;
}
onMount(() => {
Hosts().then(h => hosts = h);
});
</script>
<main>
<AddressBar {hosts} bind:activeHostKey on:select={e => openConnection(e.detail)} bind:modalOpen={addressBarModalOpen} />
<div class="columns">
{#if host && connection}
<div class="hostlist">
<Grid
columns={[ { key: 'id' }, { key: 'collCount', right: true } ]}
items={Object.keys(connection.databases).map(id => ({
id,
collCount: Object.keys(connection.databases[id].collections || {}).length,
children: connection.databases[id].collections || [],
}))}
bind:activeKey={activeDbKey}
bind:activeChildKey={activeCollKey}
on:select={e => openDatabase(e.detail)}
on:selectChild={e => openCollection(e.detail)}
/>
</div>
<div class="collection">
<CollectionDetail
{collection}
hostKey={activeHostKey}
dbKey={activeDbKey}
collectionKey={activeCollKey}
/>
</div>
{/if}
</div>
</main>
<style>
main {
height: 100vh;
max-height: 100vh;
display: flex;
flex-direction: column;
}
.columns {
display: flex;
gap: 1rem;
flex: 1;
}
.columns > :global(*) {
height: 100%;
display: flex;
flex-direction: column;
}
.hostlist {
flex: 0 0 250px;
overflow-y: scroll;
}
.collection {
flex: 1;
width: auto;
}
</style>

View File

@ -0,0 +1,28 @@
<script>
export let code = '';
</script>
<div class="examplecode">
<strong>CLI command</strong>
<code>{code}</code>
</div>
<style>
.examplecode {
border: 1px solid #ccc;
border-radius: 10px;
padding: 0.5rem;
opacity: 0.6;
margin-bottom: 0.5rem;
}
strong {
display: inline-block;
margin-right: 1rem;
font-weight: 700;
}
code {
user-select: all;
}
</style>

View File

@ -0,0 +1,167 @@
<script>
import { createEventDispatcher } from 'svelte';
import Icon from './icon.svelte';
export let columns = [];
export let items = [];
export let key = 'id';
export let activeKey = '';
export let activeChildKey = '';
export let showHeaders = true;
export let level = 0;
export let contained = false;
const dispatch = createEventDispatcher();
const childrenOpen = {};
$: _items = objectToArray(items).map(item => {
item.children = objectToArray(item.children);
return item;
});
function objectToArray(obj) {
if (Array.isArray(obj)) {
return obj;
}
else if (typeof obj === 'object') {
return Object.entries(obj).map(([ k, item ]) => ({ ...item, [key]: k }));
}
else {
return obj;
}
}
function select(itemKey) {
activeKey = itemKey;
activeChildKey = '';
dispatch('select', itemKey);
}
function selectChild(itemKey, childKey) {
select(itemKey);
activeChildKey = childKey;
dispatch('selectChild', childKey);
}
function toggleChildren(itemKey) {
childrenOpen[itemKey] = !childrenOpen[itemKey];
}
</script>
<div class:grid={level === 0} class:subgrid={level > 0} class:contained>
<table>
{#if showHeaders && columns.some(col => col.title)}
<thead>
<tr>
<th class="has-toggle"></th>
{#each columns as column}
<th scope="col">{column.title || ''}</th>
{/each}
</tr>
</thead>
{/if}
<tbody>
{#each _items as item (item[key])}
<tr on:click={() => select(item[key])} class:selected={activeKey === item[key] && !activeChildKey}>
<td class="has-toggle">
{#if item.children}
<button class="toggle" on:click={() => toggleChildren(item[key])}>
<Icon name={childrenOpen[item[key]] ? 'chev-d' : 'chev-r'} />
</button>
{/if}
</td>
{#each columns as column}
{@const value = item[column.key]}
<td class:right={column.right}>
{#if typeof value !== 'object'}
{value || ''}
{/if}
</td>
{/each}
</tr>
{#if item.children && childrenOpen[item[key]]}
<tr>
<td></td>
<td colspan={columns.length + 1} class="subgrid-parent">
<svelte:self
{columns}
{key}
bind:activeKey={activeChildKey}
showHeaders={false}
items={item.children}
level={level + 1}
on:select={e => selectChild(item[key], e.detail)}
/>
</td>
</tr>
{/if}
{/each}
</tbody>
</table>
</div>
<style>
.grid {
background-color: #fff;
height: 100%;
width: 100%;
overflow: scroll;
}
.grid.contained {
border: 1px solid #ccc;
}
.subgrid {
width: 100%;
}
table {
border-collapse: collapse;
width: 100%;
}
table thead {
border-bottom: 2px solid #ccc;
}
table th {
font-weight: 600;
text-align: left;
}
tr {
cursor: pointer;
}
td, th {
padding: 0.3rem;
height: 100%;
}
td.has-toggle {
width: calc(20px + 0.3rem);
}
td.subgrid-parent {
padding: 0;
}
table tbody tr.selected td {
background-color: #00008b;
color: #fff;
}
button.toggle {
color: inherit;
padding: 0;
}
button.toggle :global(svg) {
width: 15px;
height: 15px;
vertical-align: top;
}
.right {
text-align: right;
}
</style>

View File

@ -0,0 +1,15 @@
<script>
export let name;
</script>
{#if name === 'radio'}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-radio"><circle cx="12" cy="12" r="2"></circle><path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"></path></svg>
{:else if name === 'chev-r'}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>
{:else if name === 'chev-d'}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>
{:else if name === 'db'}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-database"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path></svg>
{:else if name === 'x'}
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-x"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
{/if}

View File

@ -0,0 +1,105 @@
<script>
import { fade, fly } from 'svelte/transition';
import Icon from './icon.svelte';
export let show = false;
export let title = undefined;
export let contentPadding = true;
</script>
{#if show}
<div class="modal outer" on:mousedown|self={close} transition:fade>
<div class="inner" transition:fly={{ y: 100 }}>
<header>
{#if title}
<div class="title">{title}</div>
{/if}
<button class="btn close" on:click={() => show = false} title="close">
<Icon name="x" />
</button>
</header>
<div class="slot content" class:p-0={!contentPadding}> <slot /> </div>
{#if $$slots.footerLeft || $$slots.footerRight}
<footer>
<slot name="footer" />
</footer>
{/if}
</div>
</div>
{/if}
<style>
.outer {
position: fixed;
display: flex;
z-index: 100;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
margin: 0;
padding-top: 1rem;
cursor: pointer;
}
.inner {
max-width: 80vw;
max-height: 80vh;
background-color: #fff;
margin-left: auto;
margin-right: auto;
margin-bottom: auto;
width: 100%;
border-radius: 10px;
display: flex;
flex-direction: column;
cursor: auto;
}
.inner > :global(*:first-child) {
margin-top: 0;
}
.inner > :global(*:last-child) {
margin-bottom: 0;
}
header {
border-bottom: 1px solid #ccc;
display: flex;
align-items: center;
padding: 1rem;
}
header .title {
font-size: 1.5rem;
}
.close {
margin-left: auto;
}
.content {
padding: 1rem;
overflow-y: auto;
max-height: 100%;
}
footer {
padding: 1rem;
border-top: 1px solid #ccc;
}
@media (max-width: 600px) {
.outer {
padding: 0;
}
.inner {
max-width: 100%;
max-height: 100%;
width: 100%;
margin-top: auto;
margin-bottom: 0;
}
}
</style>

View File

@ -0,0 +1,64 @@
<script>
import Grid from './grid.svelte';
export let data = [];
export let key = '_id';
export let showHeaders = false;
export let contained = true;
console.log(data);
let items = [];
$: if (data) {
items = [];
if (Array.isArray(data)) {
for (const item of data) {
const _item = {};
_item.key = item[key];
_item.children = dissectObject(item);
items = [ ...items, _item ];
}
}
else {
items = dissectObject(data);
console.log(items);
}
}
function getType(value) {
if (Array.isArray(value)) {
return `array (${value.length} item${value.length === 1 ? '' : 's'})`;
}
else if (new Date(value).toString() !== 'Invalid Date') {
return 'date';
}
else if (typeof value === 'object') {
const keys = Object.keys(value);
return `object (${keys.length} item${keys.length === 1 ? '' : 's'})`;
}
else {
return typeof value;
}
}
function dissectObject(object) {
return Object.entries(object).map(([ key, value ]) => {
const type = getType(value);
const child = { key, value, type };
if (type.startsWith('object')) {
child.children = dissectObject(value);
}
return child;
});
}
</script>
<Grid columns={[
{ key: 'key', label: 'Key' },
{ key: 'value', label: 'Value' },
{ key: 'type', label: 'Type' },
]} {items} {showHeaders} {contained} key="key" />

View File

@ -0,0 +1,54 @@
<script>
import { createEventDispatcher } from 'svelte';
export let tabs = [];
export let selectedKey = {};
const dispatch = createEventDispatcher();
function select(tabKey) {
selectedKey = tabKey;
dispatch('select', tabKey);
}
</script>
<nav class="tabs">
<ul>
{#each tabs as tab (tab.key)}
<li class="tab" class:active={tab.key === selectedKey}>
<button on:click={() => select(tab.key)}>{tab.title}</button>
</li>
{/each}
</ul>
</nav>
<style>
.tabs {
border-bottom: 1px solid #ccc;
padding: 0 0.5rem;
}
.tabs ul {
overflow-x: scroll;
display: flex;
gap: 0.5rem;
list-style: none;
}
.tabs li {
display: inline-block;
flex-grow: 1;
}
.tabs li button {
width: 100%;
padding: 0.7rem 1rem;
border: 1px solid #ccc;
border-bottom: none;
border-radius: 5px 5px 0 0;
cursor: pointer;
}
.tabs li.active button {
color: #fff;
background-color: #00008b;
border-color: #00008b;
cursor: not-allowed;
}
</style>

7
frontend/src/main.js Normal file
View File

@ -0,0 +1,7 @@
import './reset.css';
import './style.css';
import App from './app.svelte';
const app = new App({ target: document.getElementById('app') });
export default app;

View File

@ -0,0 +1,78 @@
<script>
import Modal from '../../components/modal.svelte';
import Icon from '../../components/icon.svelte';
import { createEventDispatcher } from 'svelte';
export let hosts = {};
export let activeHostKey = '';
export let modalOpen = false;
const dispatch = createEventDispatcher();
$: host = hosts?.[activeHostKey];
function select(hostKey) {
activeHostKey = hostKey;
dispatch('select', hostKey);
}
</script>
<div class="addressbar">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="address" class:empty={!host?.uri} on:click={() => modalOpen = true}>
{host?.uri || 'No host selected'}
</div>
<div class="actions">
<button class="btn" on:click={() => modalOpen = true}>
<Icon name="db" />
</button>
</div>
</div>
<Modal bind:show={modalOpen} title="Hosts">
{#if Object.keys(hosts).length}
<ul class="hosts">
{#each Object.entries(hosts) as [hostKey, host]}
<li>
<button on:click={() => select(hostKey)}>
{host.name}
</button>
</li>
{/each}
</ul>
{/if}
</Modal>
<style>
.addressbar {
display: flex;
justify-content: space-between;
align-items: center;
margin: 1rem;
padding: 0.5rem 0.5rem 0.5rem 1rem;
border: 1px solid #ccc;
border-radius: 10px;
}
.address.empty {
font-style: italic;
opacity: 0.6;
}
.hosts {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
}
.hosts li button {
display: block;
width: 100%;
height: 100%;
padding: 1rem;
background-color: #ddd;
border-radius: 10px;
}
.hosts li button:hover {
background-color: #ccc;
}
</style>

View File

@ -0,0 +1,93 @@
<script>
import { PerformFind } from '../../../wailsjs/go/main/App';
import CodeExample from '../../components/code-example.svelte';
import { onMount } from 'svelte';
import { input } from '../../actions';
import ObjectGrid from '../../components/objectgrid.svelte';
export let collection;
const defaults = {
query: '{}',
sort: '{ "_id": 1 }',
fields: '{}',
skip: 0,
limit: 30,
};
const form = {
query: '{}',
sort: '{ "_id": 1 }',
fields: '{}',
skip: 0,
limit: 30,
};
let result = [];
let queryField;
$: code = `db.${collection.key}.find(${form.query || '{}'}${form.fields && form.fields !== '{}' ? `, ${form.fields}` : ''}).sort(${form.sort})${form.skip ? `.skip(${form.skip})` : ''}${form.limit ? `.limit(${form.limit})` : ''};`;
$: if (collection) {
result = [];
}
async function submitQuery() {
result = await PerformFind(collection.hostKey, collection.dbKey, collection.key, JSON.stringify(form));
}
onMount(() => {
queryField?.focus();
queryField?.select();
});
</script>
<form on:submit|preventDefault={submitQuery}>
<div class="row-one">
<label class="field">
<span class="label">Query or id</span>
<input type="text" class="code" bind:this={queryField} bind:value={form.query} use:input={{ json: true }} placeholder={defaults.query} />
</label>
<label class="field">
<span class="label">Sort</span>
<input type="text" class="code" bind:value={form.sort} use:input={{ json: true }} placeholder={defaults.sort} />
</label>
</div>
<div class="row-two">
<label class="field">
<span class="label">Fields</span>
<input type="text" class="code" bind:value={form.fields} use:input={{ json: true }} placeholder={defaults.fields} />
</label>
<label class="field">
<span class="label">Skip</span>
<input type="number" min="0" bind:value={form.skip} use:input placeholder={defaults.skip} />
</label>
<label class="field">
<span class="label">Limit</span>
<input type="number" min="0" bind:value={form.limit} use:input placeholder={defaults.limit} />
</label>
<button type="submit" class="btn">Run</button>
</div>
</form>
<CodeExample {code} />
<ObjectGrid data={result} />
<style>
.row-one {
display: grid;
gap: 0.5rem;
grid-template-columns: 3fr 2fr;
margin-bottom: 0.5rem;
}
.row-two {
display: grid;
gap: 0.5rem;
grid-template-columns: 5fr 1fr 1fr 1fr;
margin-bottom: 0.5rem;
}
</style>

View File

@ -0,0 +1,68 @@
<script>
import ObjectGrid from '../../components/objectgrid.svelte';
import CodeExample from '../../components/code-example.svelte';
import TabBar from '../../components/tabbar.svelte';
import Find from './find.svelte';
import Indexes from './indexes.svelte';
import Insert from './insert.svelte';
import Remove from './remove.svelte';
export let collection;
export let hostKey;
export let dbKey;
export let collectionKey;
let tab = 'find';
$: if (collection) {
collection.hostKey = hostKey;
collection.dbKey = dbKey;
collection.key = collectionKey;
}
</script>
<div class="collection" class:empty={!collection}>
{#if collection}
<TabBar tabs={[
{ key: 'stats', title: 'Stats' },
{ key: 'find', title: 'Find' },
{ key: 'insert', title: 'Insert' },
{ key: 'update', title: 'Update' },
{ key: 'remove', title: 'Remove' },
{ key: 'indexes', title: 'Indexes' },
]} bind:selectedKey={tab} />
<div class="container">
{#if tab === 'stats'}
<CodeExample code="db.stats()" />
<ObjectGrid data={collection.stats} />
{:else if tab === 'find'}
<Find {collection} />
{:else if tab === 'insert'}
<Insert {collection} />
{:else if tab === 'remove'}
<Remove {collection} />
{:else if tab === 'indexes'}
<Indexes {collection} />
{/if}
</div>
{:else}
No collection selected
{/if}
</div>
<style>
.collection {
margin: 1rem 1rem 1rem 0;
height: 100%;
display: flex;
flex-direction: column;
}
.container {
padding: 0.5rem 0.5rem 0;
flex: 1;
display: flex;
flex-direction: column;
}
</style>

View File

@ -0,0 +1,13 @@
<script>
export let collection;
const indexes = [];
function getIndexes() {}
</script>
<div class="buttons">
<button class="btn">Get indexes</button>
<button class="btn" disabled={!indexes?.length}>Drop selected</button>
<button class="btn">Create&hellip;</button>
</div>

View File

@ -0,0 +1,39 @@
<script>
import { PerformInsert } from '../../../wailsjs/go/main/App';
export let collection;
let input = '';
let insertedIds;
$: if (collection) {
insertedIds = undefined;
}
async function insert() {
insertedIds = await PerformInsert(collection.hostKey, collection.dbKey, collection.key, input);
}
</script>
<form on:submit|preventDefault={insert}>
<label class="field">
<textarea cols="30" rows="10" bind:value={input} placeholder="[]" class="code"></textarea>
</label>
<div class="flex">
<div>
{#if insertedIds}
Success! {insertedIds.length} document{insertedIds.length > 1 ? 's' : ''} inserted
{/if}
</div>
<button type="submit" class="btn">Insert</button>
</div>
</form>
<style>
.flex {
display: flex;
justify-content: space-between;
margin-top: 0.5rem;
}
</style>

View File

@ -0,0 +1,31 @@
<script>
import CodeExample from '../../components/code-example.svelte';
export let collection;
let remove = '';
$: code = `db.${collection.key}.remove(${remove});`;
function insert() {}
</script>
<form on:submit|preventDefault={insert}>
<CodeExample {code} />
<label class="field">
<textarea cols="30" rows="10" bind:value={remove} placeholder={'{}'} class="code"></textarea>
</label>
<div class="flex">
<div></div>
<button type="submit" class="btn">Remove</button>
</div>
</form>
<style>
.flex {
display: flex;
justify-content: space-between;
margin-top: 0.5rem;
}
</style>

66
frontend/src/reset.css Normal file
View File

@ -0,0 +1,66 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* Buttons */
button {
font: inherit;
border: none;
background: none;
margin: 0;
padding: 0;
box-shadow: none;
cursor: pointer;
}
/* Inputs */
input {
font: inherit;
min-width: 100px;
width: 100%;
}

11
frontend/src/stores.js Normal file
View File

@ -0,0 +1,11 @@
import { writable } from 'svelte/store';
export const busy = writable(false);
busy.subscribe(isBusy => {
if (isBusy) {
document.body.classList.add('busy');
}
else {
document.body.classList.remove('busy');
}
});

93
frontend/src/style.css Normal file
View File

@ -0,0 +1,93 @@
html,
body {
height: 100vh;
max-height: 100vh;
overflow: hidden;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
margin: 0;
padding: 0;
-webkit-user-select: none;
user-select: none;
cursor: default;
font-size: 15px;
line-height: 15px;
}
* {
vertical-align: middle;
box-sizing: border-box;
}
p {
margin: 0 0 0.7rem 0;
}
.loading {
cursor: wait;
}
.field {
display: flex;
white-space: nowrap;
align-items: stretch;
}
.field > * {
padding: 0.5rem;
border: 1px solid #ccc;
border-right: none;
display: inline-block;
margin: 0;
}
.field .label {
background-color: #eee;
display: flex;
align-items: center;
}
.field > :first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
.field > :last-child {
border-right: 1px solid #ccc;
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.field > input,
.field > textarea {
flex: 1;
}
.field > textarea:focus,
.field > input:focus {
outline: none;
border-color: #00008b;
box-shadow: 0 0 0 3px rgba(0, 0, 139, 0.2);
}
.field > input.invalid,
.field > textarea.invalid {
background-color: rgba(255, 80, 80, 0.3);
border-color: rgb(255, 80, 80);
}
.btn {
background-color: #00008b;
padding: 0.5rem;
border-radius: 10px;
color: #fff;
}
.btn:focus {
box-shadow: 0 0 0 3px rgba(0, 0, 139, 0.2);
outline: none;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn svg {
height: 15px;
width: auto;
vertical-align: bottom;
}
code, .code {
font-family: Menlo, monospace;
}

2
frontend/src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference types="svelte" />
/// <reference types="vite/client" />

7
frontend/vite.config.js Normal file
View File

@ -0,0 +1,7 @@
import {defineConfig} from 'vite'
import {svelte} from '@sveltejs/vite-plugin-svelte'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte()]
})

16
frontend/wailsjs/go/main/App.d.ts vendored Executable file
View File

@ -0,0 +1,16 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {map[string]main} from '../models';
import {primitive} from '../models';
export function Hosts():Promise<map[string]main.Host>;
export function OpenCollection(arg1:string,arg2:string,arg3:string):Promise<primitive.M>;
export function OpenConnection(arg1:string):Promise<Array<string>>;
export function OpenDatabase(arg1:string,arg2:string):Promise<Array<string>>;
export function PerformFind(arg1:string,arg2:string,arg3:string,arg4:string):Promise<any>;
export function PerformInsert(arg1:string,arg2:string,arg3:string,arg4:string):Promise<any>;

27
frontend/wailsjs/go/main/App.js Executable file
View File

@ -0,0 +1,27 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function Hosts() {
return window['go']['main']['App']['Hosts']();
}
export function OpenCollection(arg1, arg2, arg3) {
return window['go']['main']['App']['OpenCollection'](arg1, arg2, arg3);
}
export function OpenConnection(arg1) {
return window['go']['main']['App']['OpenConnection'](arg1);
}
export function OpenDatabase(arg1, arg2) {
return window['go']['main']['App']['OpenDatabase'](arg1, arg2);
}
export function PerformFind(arg1, arg2, arg3, arg4) {
return window['go']['main']['App']['PerformFind'](arg1, arg2, arg3, arg4);
}
export function PerformInsert(arg1, arg2, arg3, arg4) {
return window['go']['main']['App']['PerformInsert'](arg1, arg2, arg3, arg4);
}

View File

@ -0,0 +1,24 @@
{
"name": "@wailsapp/runtime",
"version": "2.0.0",
"description": "Wails Javascript runtime library",
"main": "runtime.js",
"types": "runtime.d.ts",
"scripts": {
},
"repository": {
"type": "git",
"url": "git+https://github.com/wailsapp/wails.git"
},
"keywords": [
"Wails",
"Javascript",
"Go"
],
"author": "Lea Anthony <lea.anthony@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/wailsapp/wails/issues"
},
"homepage": "https://github.com/wailsapp/wails#readme"
}

227
frontend/wailsjs/runtime/runtime.d.ts vendored Normal file
View File

@ -0,0 +1,227 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export interface Position {
x: number;
y: number;
}
export interface Size {
w: number;
h: number;
}
export interface Screen {
isCurrent: boolean;
isPrimary: boolean;
width : number
height : number
}
// Environment information such as platform, buildtype, ...
export interface EnvironmentInfo {
buildType: string;
platform: string;
arch: string;
}
// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
// emits the given event. Optional data may be passed with the event.
// This will trigger any event listeners.
export function EventsEmit(eventName: string, ...data: any): void;
// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
export function EventsOn(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
// sets up a listener for the given event name, but will only trigger a given number times.
export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void;
// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
// sets up a listener for the given event name, but will only trigger once.
export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
// unregisters the listener for the given event name.
export function EventsOff(eventName: string, ...additionalEventNames: string[]): void;
// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
// unregisters all listeners.
export function EventsOffAll(): void;
// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
// logs the given message as a raw message
export function LogPrint(message: string): void;
// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
// logs the given message at the `trace` log level.
export function LogTrace(message: string): void;
// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
// logs the given message at the `debug` log level.
export function LogDebug(message: string): void;
// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
// logs the given message at the `error` log level.
export function LogError(message: string): void;
// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
// logs the given message at the `fatal` log level.
// The application will quit after calling this method.
export function LogFatal(message: string): void;
// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
// logs the given message at the `info` log level.
export function LogInfo(message: string): void;
// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
// logs the given message at the `warning` log level.
export function LogWarning(message: string): void;
// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
// Forces a reload by the main application as well as connected browsers.
export function WindowReload(): void;
// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
// Reloads the application frontend.
export function WindowReloadApp(): void;
// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
// Sets the window AlwaysOnTop or not on top.
export function WindowSetAlwaysOnTop(b: boolean): void;
// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
// *Windows only*
// Sets window theme to system default (dark/light).
export function WindowSetSystemDefaultTheme(): void;
// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
// *Windows only*
// Sets window to light theme.
export function WindowSetLightTheme(): void;
// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
// *Windows only*
// Sets window to dark theme.
export function WindowSetDarkTheme(): void;
// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
// Centers the window on the monitor the window is currently on.
export function WindowCenter(): void;
// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
// Sets the text in the window title bar.
export function WindowSetTitle(title: string): void;
// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
// Makes the window full screen.
export function WindowFullscreen(): void;
// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
// Restores the previous window dimensions and position prior to full screen.
export function WindowUnfullscreen(): void;
// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
// Returns the state of the window, i.e. whether the window is in full screen mode or not.
export function WindowIsFullscreen(): Promise<boolean>;
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
// Sets the width and height of the window.
export function WindowSetSize(width: number, height: number): Promise<Size>;
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
// Gets the width and height of the window.
export function WindowGetSize(): Promise<Size>;
// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMaxSize(width: number, height: number): void;
// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMinSize(width: number, height: number): void;
// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
// Sets the window position relative to the monitor the window is currently on.
export function WindowSetPosition(x: number, y: number): void;
// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
// Gets the window position relative to the monitor the window is currently on.
export function WindowGetPosition(): Promise<Position>;
// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
// Hides the window.
export function WindowHide(): void;
// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
// Shows the window, if it is currently hidden.
export function WindowShow(): void;
// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
// Maximises the window to fill the screen.
export function WindowMaximise(): void;
// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
// Toggles between Maximised and UnMaximised.
export function WindowToggleMaximise(): void;
// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
// Restores the window to the dimensions and position prior to maximising.
export function WindowUnmaximise(): void;
// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
// Returns the state of the window, i.e. whether the window is maximised or not.
export function WindowIsMaximised(): Promise<boolean>;
// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
// Minimises the window.
export function WindowMinimise(): void;
// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
// Restores the window to the dimensions and position prior to minimising.
export function WindowUnminimise(): void;
// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
// Returns the state of the window, i.e. whether the window is minimised or not.
export function WindowIsMinimised(): Promise<boolean>;
// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
// Returns the state of the window, i.e. whether the window is normal or not.
export function WindowIsNormal(): Promise<boolean>;
// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
export function ScreenGetAll(): Promise<Screen[]>;
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
// Opens the given URL in the system browser.
export function BrowserOpenURL(url: string): void;
// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
// Returns information about the environment
export function Environment(): Promise<EnvironmentInfo>;
// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
// Quits the application.
export function Quit(): void;
// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
// Hides the application.
export function Hide(): void;
// [Show](https://wails.io/docs/reference/runtime/intro#show)
// Shows the application.
export function Show(): void;

View File

@ -0,0 +1,194 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export function LogPrint(message) {
window.runtime.LogPrint(message);
}
export function LogTrace(message) {
window.runtime.LogTrace(message);
}
export function LogDebug(message) {
window.runtime.LogDebug(message);
}
export function LogInfo(message) {
window.runtime.LogInfo(message);
}
export function LogWarning(message) {
window.runtime.LogWarning(message);
}
export function LogError(message) {
window.runtime.LogError(message);
}
export function LogFatal(message) {
window.runtime.LogFatal(message);
}
export function EventsOnMultiple(eventName, callback, maxCallbacks) {
window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks);
}
export function EventsOn(eventName, callback) {
EventsOnMultiple(eventName, callback, -1);
}
export function EventsOff(eventName, ...additionalEventNames) {
return window.runtime.EventsOff(eventName, ...additionalEventNames);
}
export function EventsOnce(eventName, callback) {
EventsOnMultiple(eventName, callback, 1);
}
export function EventsEmit(eventName) {
let args = [eventName].slice.call(arguments);
return window.runtime.EventsEmit.apply(null, args);
}
export function WindowReload() {
window.runtime.WindowReload();
}
export function WindowReloadApp() {
window.runtime.WindowReloadApp();
}
export function WindowSetAlwaysOnTop(b) {
window.runtime.WindowSetAlwaysOnTop(b);
}
export function WindowSetSystemDefaultTheme() {
window.runtime.WindowSetSystemDefaultTheme();
}
export function WindowSetLightTheme() {
window.runtime.WindowSetLightTheme();
}
export function WindowSetDarkTheme() {
window.runtime.WindowSetDarkTheme();
}
export function WindowCenter() {
window.runtime.WindowCenter();
}
export function WindowSetTitle(title) {
window.runtime.WindowSetTitle(title);
}
export function WindowFullscreen() {
window.runtime.WindowFullscreen();
}
export function WindowUnfullscreen() {
window.runtime.WindowUnfullscreen();
}
export function WindowIsFullscreen() {
return window.runtime.WindowIsFullscreen();
}
export function WindowGetSize() {
return window.runtime.WindowGetSize();
}
export function WindowSetSize(width, height) {
window.runtime.WindowSetSize(width, height);
}
export function WindowSetMaxSize(width, height) {
window.runtime.WindowSetMaxSize(width, height);
}
export function WindowSetMinSize(width, height) {
window.runtime.WindowSetMinSize(width, height);
}
export function WindowSetPosition(x, y) {
window.runtime.WindowSetPosition(x, y);
}
export function WindowGetPosition() {
return window.runtime.WindowGetPosition();
}
export function WindowHide() {
window.runtime.WindowHide();
}
export function WindowShow() {
window.runtime.WindowShow();
}
export function WindowMaximise() {
window.runtime.WindowMaximise();
}
export function WindowToggleMaximise() {
window.runtime.WindowToggleMaximise();
}
export function WindowUnmaximise() {
window.runtime.WindowUnmaximise();
}
export function WindowIsMaximised() {
return window.runtime.WindowIsMaximised();
}
export function WindowMinimise() {
window.runtime.WindowMinimise();
}
export function WindowUnminimise() {
window.runtime.WindowUnminimise();
}
export function WindowSetBackgroundColour(R, G, B, A) {
window.runtime.WindowSetBackgroundColour(R, G, B, A);
}
export function ScreenGetAll() {
return window.runtime.ScreenGetAll();
}
export function WindowIsMinimised() {
return window.runtime.WindowIsMinimised();
}
export function WindowIsNormal() {
return window.runtime.WindowIsNormal();
}
export function BrowserOpenURL(url) {
window.runtime.BrowserOpenURL(url);
}
export function Environment() {
return window.runtime.Environment();
}
export function Quit() {
window.runtime.Quit();
}
export function Hide() {
window.runtime.Hide();
}
export function Show() {
window.runtime.Show();
}

47
go.mod Normal file
View File

@ -0,0 +1,47 @@
module github.com/garraflavatra/mongodup
go 1.18
require github.com/wailsapp/wails/v2 v2.3.1
require (
github.com/golang/snappy v0.0.1 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
)
require (
github.com/bep/debounce v1.2.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/labstack/echo/v4 v4.9.0 // indirect
github.com/labstack/gommon v0.3.1 // indirect
github.com/leaanthony/go-ansi-parser v1.0.1 // indirect
github.com/leaanthony/gosod v1.0.3 // indirect
github.com/leaanthony/slicer v1.5.0 // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/samber/lo v1.27.1 // indirect
github.com/tkrajina/go-reflector v0.5.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
go.mongodb.org/mongo-driver v1.11.1
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.3.7 // indirect
)
// replace github.com/wailsapp/wails/v2 v2.3.1 => /Users/romeinvanburen/go/pkg/mod

117
go.sum Normal file
View File

@ -0,0 +1,117 @@
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/labstack/echo/v4 v4.9.0 h1:wPOF1CE6gvt/kmbMR4dGzWvHMPT+sAEUJOwOTtvITVY=
github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2 h1:acNfDZXmm28D2Yg/c3ALnZStzNaZMSagpbr96vY6Zjc=
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/samber/lo v1.27.1 h1:sTXwkRiIFIQG+G0HeAvOEnGjqWeWtI9cg5/n51KrxPg=
github.com/samber/lo v1.27.1/go.mod h1:it33p9UtPMS7z72fP4gw/EIfQB2eI8ke7GR2wc6+Rhg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
github.com/tkrajina/go-reflector v0.5.5 h1:gwoQFNye30Kk7NrExj8zm3zFtrGPqOkzFMLuQZg1DtQ=
github.com/tkrajina/go-reflector v0.5.5/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/wailsapp/wails/v2 v2.3.1 h1:ZJz+pyIBKyASkgO8JO31NuHO1gTTHmvwiHYHwei1CqM=
github.com/wailsapp/wails/v2 v2.3.1/go.mod h1:zlNLI0E2c2qA6miiuAHtp0Bac8FaGH0tlhA19OssR/8=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
go.mongodb.org/mongo-driver v1.11.1 h1:QP0znIRTuL0jf1oBQoAoM0C6ZJfBK4kx0Uumtv1A7w8=
go.mongodb.org/mongo-driver v1.11.1/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM=
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

303
main.go Normal file
View File

@ -0,0 +1,303 @@
package main
import (
"context"
"embed"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
"github.com/wailsapp/wails/v2/pkg/runtime"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
mongoOptions "go.mongodb.org/mongo-driver/mongo/options"
)
//go:embed all:frontend/dist
var assets embed.FS
type Host struct {
Name string `json:"name"`
URI string `json:"uri"`
}
var hosts = map[string]Host{
"localhost": {Name: "Localhost", URI: "mongodb://localhost:27017"},
"tig": {Name: "cmdb.myinfra.nl"},
"vbt": {Name: "vbtverhuurmakelaars.nl"},
}
type App struct {
ctx context.Context
}
func NewApp() *App {
return &App{}
}
func (a *App) Startup(ctx context.Context) {
a.ctx = ctx
}
func (a *App) Hosts() map[string]Host {
return hosts
}
func (a *App) connectToHost(hostKey string) (*mongo.Client, context.Context, func(), error) {
h := hosts[hostKey]
if len(h.URI) == 0 {
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Invalid uri",
Message: "You haven't specified a valid uri for the selected host.",
})
return nil, nil, nil, errors.New("invalid uri")
}
client, err := mongo.NewClient(mongoOptions.Client().ApplyURI(h.URI))
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Could not connect",
Message: "Failed to establish a connection with " + h.Name,
})
return nil, nil, nil, errors.New("could not establish a connection with " + h.Name)
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
client.Connect(ctx)
return client, ctx, func() {
client.Disconnect(ctx)
cancel()
}, nil
}
func (a *App) OpenConnection(hostKey string) (databases []string) {
client, ctx, close, err := a.connectToHost(hostKey)
if err != nil {
fmt.Println(err.Error())
return nil
}
databases, err = client.ListDatabaseNames(ctx, bson.M{})
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Could not retrieve database list",
Message: err.Error(),
})
return nil
}
defer close()
return databases
}
func (a *App) OpenDatabase(hostKey, dbKey string) (collections []string) {
client, ctx, close, err := a.connectToHost(hostKey)
if err != nil {
fmt.Println(err.Error())
return nil
}
collections, err = client.Database(dbKey).ListCollectionNames(ctx, bson.D{})
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Could not retrieve collection list for " + dbKey,
Message: err.Error(),
})
return nil
}
defer close()
return collections
}
func (a *App) OpenCollection(hostKey, dbKey, collKey string) (result bson.M) {
client, ctx, close, err := a.connectToHost(hostKey)
if err != nil {
fmt.Println(err.Error())
return nil
}
command := bson.M{"collStats": collKey}
err = client.Database(dbKey).RunCommand(ctx, command).Decode(&result)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Could not retrieve collection list for " + dbKey,
Message: err.Error(),
})
return nil
}
defer close()
return result
}
func (a *App) PerformFind(hostKey, dbKey, collKey string, formJson string) interface{} {
var form struct {
Fields string `json:"fields"`
Limit int64 `json:"limit"`
Query string `json:"query"`
Skip int64 `json:"skip"`
Sort string `json:"sort"`
}
err := json.Unmarshal([]byte(formJson), &form)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Couldn't parse form",
Message: err.Error(),
})
return nil
}
client, ctx, close, err := a.connectToHost(hostKey)
if err != nil {
fmt.Println(err.Error())
return nil
}
defer close()
var query bson.M
var projection bson.M
var sort bson.M
err = json.Unmarshal([]byte(form.Query), &query)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Invalid query",
Message: err.Error(),
})
return nil
}
err = json.Unmarshal([]byte(form.Fields), &projection)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Invalid projection",
Message: err.Error(),
})
return nil
}
err = json.Unmarshal([]byte(form.Sort), &sort)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Invalid sort",
Message: err.Error(),
})
return nil
}
opt := mongoOptions.FindOptions{
Limit: &form.Limit,
Projection: projection,
Skip: &form.Skip,
Sort: sort,
}
cur, err := client.Database(dbKey).Collection(collKey).Find(ctx, bson.D{}, &opt)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Encountered an error while performing query",
Message: err.Error(),
})
return nil
}
defer cur.Close(ctx)
var results []bson.M
err = cur.All(ctx, &results)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Encountered an error while performing query",
Message: err.Error(),
})
return nil
}
return results
}
func (a *App) PerformInsert(hostKey, dbKey, collKey, jsonData string) interface{} {
var data []interface{}
jsonData = strings.TrimSpace(jsonData)
if strings.HasPrefix(jsonData, "{") {
jsonData = "[" + jsonData + "]"
}
err := json.Unmarshal([]byte(jsonData), &data)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Couldn't parse JSON",
Message: err.Error(),
})
return nil
}
client, ctx, close, err := a.connectToHost(hostKey)
if err != nil {
fmt.Println(err.Error())
return nil
}
defer close()
res, err := client.Database(dbKey).Collection(collKey).InsertMany(ctx, data)
if err != nil {
fmt.Println(err.Error())
runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.ErrorDialog,
Title: "Encountered an error while performing query",
Message: err.Error(),
})
return nil
}
return res.InsertedIDs
}
func main() {
app := NewApp()
err := wails.Run(&options.App{
Title: "Mongodup",
Width: 1000,
Height: 600,
AssetServer: &assetserver.Options{
Assets: assets,
},
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
OnStartup: app.Startup,
Bind: []interface{}{
app,
},
})
if err != nil {
println("Error:", err.Error())
}
}

13
wails.json Normal file
View File

@ -0,0 +1,13 @@
{
"$schema": "https://wails.io/schemas/config.v2.json",
"name": "Mongodup",
"outputfilename": "mongodup",
"frontend:install": "npm install",
"frontend:build": "npm run build",
"frontend:dev:watcher": "npm run dev",
"frontend:dev:serverUrl": "auto",
"author": {
"name": "Romein van Buren",
"email": "romein@vburen.nl"
}
}