diff --git a/server/.env.example b/.env.example similarity index 79% rename from server/.env.example rename to .env.example index e61fe68..597ba24 100644 --- a/server/.env.example +++ b/.env.example @@ -1,5 +1,5 @@ DATABASE_URL= -# Generate a 32-byte (256-bit) key for AES-256 encryption using `openssl rand -base64 32` +# Generate a 32-byte (256-bit) key for AES-256 encryption using `openssl rand -hex 32` ENCRYPTION_KEY="" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..74a8090 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +build/bin +node_modules +tmp/ +.env +/data.db* +Vaulterm-res.syso diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..76054d9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "go.buildTags": "gui" +} \ No newline at end of file diff --git a/app.go b/app.go new file mode 100644 index 0000000..aef2daa --- /dev/null +++ b/app.go @@ -0,0 +1,34 @@ +//go:build gui +// +build gui + +package main + +import ( + "context" + "fmt" +) + +// App struct +type App struct { + ctx context.Context + localPort int +} + +// NewApp creates a new App application struct +func NewApp() *App { + return &App{} +} + +// startup is called when the app starts. The context is saved +// so we can call the runtime methods +func (a *App) startup(ctx context.Context) { + a.ctx = ctx +} + +// Returns local server address +func (a *App) GetLocalServer() string { + if a.localPort == 0 { + return "" + } + return fmt.Sprintf("http://localhost:%d", a.localPort) +} diff --git a/build/README.md b/build/README.md new file mode 100644 index 0000000..1ae2f67 --- /dev/null +++ b/build/README.md @@ -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. \ No newline at end of file diff --git a/build/appicon.png b/build/appicon.png new file mode 100644 index 0000000..63617fe Binary files /dev/null and b/build/appicon.png differ diff --git a/build/darwin/Info.dev.plist b/build/darwin/Info.dev.plist new file mode 100644 index 0000000..04727c2 --- /dev/null +++ b/build/darwin/Info.dev.plist @@ -0,0 +1,68 @@ + + + + CFBundlePackageType + APPL + CFBundleName + {{.Info.ProductName}} + CFBundleExecutable + {{.Name}} + CFBundleIdentifier + com.wails.{{.Name}} + CFBundleVersion + {{.Info.ProductVersion}} + CFBundleGetInfoString + {{.Info.Comments}} + CFBundleShortVersionString + {{.Info.ProductVersion}} + CFBundleIconFile + iconfile + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + {{.Info.Copyright}} + {{if .Info.FileAssociations}} + CFBundleDocumentTypes + + {{range .Info.FileAssociations}} + + CFBundleTypeExtensions + + {{.Ext}} + + CFBundleTypeName + {{.Name}} + CFBundleTypeRole + {{.Role}} + CFBundleTypeIconFile + {{.IconName}} + + {{end}} + + {{end}} + {{if .Info.Protocols}} + CFBundleURLTypes + + {{range .Info.Protocols}} + + CFBundleURLName + com.wails.{{.Scheme}} + CFBundleURLSchemes + + {{.Scheme}} + + CFBundleTypeRole + {{.Role}} + + {{end}} + + {{end}} + NSAppTransportSecurity + + NSAllowsLocalNetworking + + + + diff --git a/build/darwin/Info.plist b/build/darwin/Info.plist new file mode 100644 index 0000000..19cc937 --- /dev/null +++ b/build/darwin/Info.plist @@ -0,0 +1,63 @@ + + + + CFBundlePackageType + APPL + CFBundleName + {{.Info.ProductName}} + CFBundleExecutable + {{.Name}} + CFBundleIdentifier + com.wails.{{.Name}} + CFBundleVersion + {{.Info.ProductVersion}} + CFBundleGetInfoString + {{.Info.Comments}} + CFBundleShortVersionString + {{.Info.ProductVersion}} + CFBundleIconFile + iconfile + LSMinimumSystemVersion + 10.13.0 + NSHighResolutionCapable + true + NSHumanReadableCopyright + {{.Info.Copyright}} + {{if .Info.FileAssociations}} + CFBundleDocumentTypes + + {{range .Info.FileAssociations}} + + CFBundleTypeExtensions + + {{.Ext}} + + CFBundleTypeName + {{.Name}} + CFBundleTypeRole + {{.Role}} + CFBundleTypeIconFile + {{.IconName}} + + {{end}} + + {{end}} + {{if .Info.Protocols}} + CFBundleURLTypes + + {{range .Info.Protocols}} + + CFBundleURLName + com.wails.{{.Scheme}} + CFBundleURLSchemes + + {{.Scheme}} + + CFBundleTypeRole + {{.Role}} + + {{end}} + + {{end}} + + diff --git a/build/windows/icon.ico b/build/windows/icon.ico new file mode 100644 index 0000000..f334798 Binary files /dev/null and b/build/windows/icon.ico differ diff --git a/build/windows/info.json b/build/windows/info.json new file mode 100644 index 0000000..9727946 --- /dev/null +++ b/build/windows/info.json @@ -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}}" + } + } +} \ No newline at end of file diff --git a/build/windows/installer/project.nsi b/build/windows/installer/project.nsi new file mode 100644 index 0000000..654ae2e --- /dev/null +++ b/build/windows/installer/project.nsi @@ -0,0 +1,114 @@ +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}" + +# Enable HiDPI support. https://nsis.sourceforge.io/Reference/ManifestDPIAware +ManifestDPIAware true + +!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.setShellContext + + !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.associateFiles + !insertmacro wails.associateCustomProtocols + + !insertmacro wails.writeUninstaller +SectionEnd + +Section "uninstall" + !insertmacro wails.setShellContext + + RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath + + RMDir /r $INSTDIR + + Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" + Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk" + + !insertmacro wails.unassociateFiles + !insertmacro wails.unassociateCustomProtocols + + !insertmacro wails.deleteUninstaller +SectionEnd diff --git a/build/windows/installer/wails_tools.nsh b/build/windows/installer/wails_tools.nsh new file mode 100644 index 0000000..f9c0f88 --- /dev/null +++ b/build/windows/installer/wails_tools.nsh @@ -0,0 +1,249 @@ +# 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 + +!macro wails.setShellContext + ${If} ${REQUEST_EXECUTION_LEVEL} == "admin" + SetShellVarContext all + ${else} + SetShellVarContext current + ${EndIf} +!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 + +# Copy of APP_ASSOCIATE and APP_UNASSOCIATE macros from here https://gist.github.com/nikku/281d0ef126dbc215dd58bfd5b3a5cd5b +!macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND + ; Backup the previously associated file class + ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" "" + WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "${FILECLASS}_backup" "$R0" + + WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "${FILECLASS}" + + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}" "" `${DESCRIPTION}` + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\DefaultIcon" "" `${ICON}` + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell" "" "open" + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open" "" `${COMMANDTEXT}` + WriteRegStr SHELL_CONTEXT "Software\Classes\${FILECLASS}\shell\open\command" "" `${COMMAND}` +!macroend + +!macro APP_UNASSOCIATE EXT FILECLASS + ; Backup the previously associated file class + ReadRegStr $R0 SHELL_CONTEXT "Software\Classes\.${EXT}" `${FILECLASS}_backup` + WriteRegStr SHELL_CONTEXT "Software\Classes\.${EXT}" "" "$R0" + + DeleteRegKey SHELL_CONTEXT `Software\Classes\${FILECLASS}` +!macroend + +!macro wails.associateFiles + ; Create file associations + {{range .Info.FileAssociations}} + !insertmacro APP_ASSOCIATE "{{.Ext}}" "{{.Name}}" "{{.Description}}" "$INSTDIR\{{.IconName}}.ico" "Open with ${INFO_PRODUCTNAME}" "$INSTDIR\${PRODUCT_EXECUTABLE} $\"%1$\"" + + File "..\{{.IconName}}.ico" + {{end}} +!macroend + +!macro wails.unassociateFiles + ; Delete app associations + {{range .Info.FileAssociations}} + !insertmacro APP_UNASSOCIATE "{{.Ext}}" "{{.Name}}" + + Delete "$INSTDIR\{{.IconName}}.ico" + {{end}} +!macroend + +!macro CUSTOM_PROTOCOL_ASSOCIATE PROTOCOL DESCRIPTION ICON COMMAND + DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "" "${DESCRIPTION}" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}" "URL Protocol" "" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\DefaultIcon" "" "${ICON}" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell" "" "" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open" "" "" + WriteRegStr SHELL_CONTEXT "Software\Classes\${PROTOCOL}\shell\open\command" "" "${COMMAND}" +!macroend + +!macro CUSTOM_PROTOCOL_UNASSOCIATE PROTOCOL + DeleteRegKey SHELL_CONTEXT "Software\Classes\${PROTOCOL}" +!macroend + +!macro wails.associateCustomProtocols + ; Create custom protocols associations + {{range .Info.Protocols}} + !insertmacro CUSTOM_PROTOCOL_ASSOCIATE "{{.Scheme}}" "{{.Description}}" "$INSTDIR\${PRODUCT_EXECUTABLE},0" "$INSTDIR\${PRODUCT_EXECUTABLE} $\"%1$\"" + + {{end}} +!macroend + +!macro wails.unassociateCustomProtocols + ; Delete app custom protocol associations + {{range .Info.Protocols}} + !insertmacro CUSTOM_PROTOCOL_UNASSOCIATE "{{.Scheme}}" + {{end}} +!macroend diff --git a/build/windows/wails.exe.manifest b/build/windows/wails.exe.manifest new file mode 100644 index 0000000..17e1a23 --- /dev/null +++ b/build/windows/wails.exe.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + true/pm + permonitorv2,permonitor + + + \ No newline at end of file diff --git a/frontend/app.config.ts b/frontend/app.config.ts index ccece38..ba89900 100644 --- a/frontend/app.config.ts +++ b/frontend/app.config.ts @@ -27,7 +27,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ }, web: { bundler: "metro", - output: "static", + output: "single", favicon: "./assets/images/favicon.png", }, plugins: ["expo-router"], diff --git a/frontend/app/index.tsx b/frontend/app/index.tsx index 0814aba..bf94b9c 100644 --- a/frontend/app/index.tsx +++ b/frontend/app/index.tsx @@ -1,12 +1,36 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { Redirect } from "expo-router"; import { useTermSession } from "@/stores/terminal-sessions"; -import { useServer } from "@/stores/app"; +import { setLocalServer, useServer } from "@/stores/app"; +import { GetLocalServer } from "@/lib/wailsjs/go/main/App"; +import { Spinner, View } from "tamagui"; export default function index() { const { sessions, curSession } = useTermSession(); + const [isPending, setPending] = useState(true); const curServer = useServer(); + useEffect(() => { + // load local server + (async function () { + try { + const url = await GetLocalServer(); + if (url) { + setLocalServer(url); + } + } catch (_) {} + setPending(false); + })(); + }, []); + + if (isPending) { + return ( + + + + ); + } + if (!curServer) { return ; } diff --git a/frontend/components/containers/user-menu-button.tsx b/frontend/components/containers/user-menu-button.tsx index abc69db..05ca444 100644 --- a/frontend/components/containers/user-menu-button.tsx +++ b/frontend/components/containers/user-menu-button.tsx @@ -38,9 +38,9 @@ const UserMenuButton = () => { - - {user?.name} - + + {user?.name} + {team ? `${team.icon} ${team.name}` : "Personal"} diff --git a/frontend/hooks/useWebsocket.ts b/frontend/hooks/useWebsocket.ts index 877ee69..1f8beb4 100644 --- a/frontend/hooks/useWebsocket.ts +++ b/frontend/hooks/useWebsocket.ts @@ -1,6 +1,5 @@ import { useServer } from "@/stores/app"; - -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; type UseWebsocketOptions = { onMessage?: (message: string) => void; @@ -50,7 +49,8 @@ export const useWebSocket = (url: string, opt?: UseWebsocketOptions) => { export const useWebsocketUrl = (initParams: any = {}) => { const server = useServer(); - const baseUrl = server?.url.replace("http://", "ws://") || ""; + const baseUrl = + server?.replace("http://", "ws://").replace("https://", "wss://") || ""; return (url: string, params: any = {}) => { const query = new URLSearchParams({ ...initParams, ...params }); diff --git a/frontend/lib/api.ts b/frontend/lib/api.ts index ceabdc1..4cde527 100644 --- a/frontend/lib/api.ts +++ b/frontend/lib/api.ts @@ -10,7 +10,7 @@ const api = ofetch.create({ } // set server url - config.options.baseURL = server.url; + config.options.baseURL = server; const { token, teamId } = authStore.getState(); diff --git a/frontend/lib/wailsjs/go/main/App.d.ts b/frontend/lib/wailsjs/go/main/App.d.ts new file mode 100644 index 0000000..483dfe7 --- /dev/null +++ b/frontend/lib/wailsjs/go/main/App.d.ts @@ -0,0 +1,4 @@ +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export function GetLocalServer():Promise; diff --git a/frontend/lib/wailsjs/go/main/App.js b/frontend/lib/wailsjs/go/main/App.js new file mode 100644 index 0000000..7dadce6 --- /dev/null +++ b/frontend/lib/wailsjs/go/main/App.js @@ -0,0 +1,7 @@ +// @ts-check +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL +// This file is automatically generated. DO NOT EDIT + +export function GetLocalServer() { + return window['go']['main']['App']['GetLocalServer'](); +} diff --git a/frontend/lib/wailsjs/runtime/package.json b/frontend/lib/wailsjs/runtime/package.json new file mode 100644 index 0000000..1e7c8a5 --- /dev/null +++ b/frontend/lib/wailsjs/runtime/package.json @@ -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 ", + "license": "MIT", + "bugs": { + "url": "https://github.com/wailsapp/wails/issues" + }, + "homepage": "https://github.com/wailsapp/wails#readme" +} diff --git a/frontend/lib/wailsjs/runtime/runtime.d.ts b/frontend/lib/wailsjs/runtime/runtime.d.ts new file mode 100644 index 0000000..94778df --- /dev/null +++ b/frontend/lib/wailsjs/runtime/runtime.d.ts @@ -0,0 +1,249 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +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; + +// [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; + +// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize) +// Gets the width and height of the window. +export function WindowGetSize(): Promise; + +// [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; + +// [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; + +// [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; + +// [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; + +// [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; + +// [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; + +// [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; + +// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext) +// Returns the current text stored on clipboard +export function ClipboardGetText(): Promise; + +// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext) +// Sets a text on the clipboard +export function ClipboardSetText(text: string): Promise; + +// [OnFileDrop](https://wails.io/docs/reference/runtime/draganddrop#onfiledrop) +// OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings. +export function OnFileDrop(callback: (x: number, y: number ,paths: string[]) => void, useDropTarget: boolean) :void + +// [OnFileDropOff](https://wails.io/docs/reference/runtime/draganddrop#dragandddropoff) +// OnFileDropOff removes the drag and drop listeners and handlers. +export function OnFileDropOff() :void + +// Check if the file path resolver is available +export function CanResolveFilePaths(): boolean; + +// Resolves file paths for an array of files +export function ResolveFilePaths(files: File[]): void \ No newline at end of file diff --git a/frontend/lib/wailsjs/runtime/runtime.js b/frontend/lib/wailsjs/runtime/runtime.js new file mode 100644 index 0000000..623397b --- /dev/null +++ b/frontend/lib/wailsjs/runtime/runtime.js @@ -0,0 +1,238 @@ +/* + _ __ _ __ +| | / /___ _(_) /____ +| | /| / / __ `/ / / ___/ +| |/ |/ / /_/ / / (__ ) +|__/|__/\__,_/_/_/____/ +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) { + return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks); +} + +export function EventsOn(eventName, callback) { + return EventsOnMultiple(eventName, callback, -1); +} + +export function EventsOff(eventName, ...additionalEventNames) { + return window.runtime.EventsOff(eventName, ...additionalEventNames); +} + +export function EventsOnce(eventName, callback) { + return 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(); +} + +export function ClipboardGetText() { + return window.runtime.ClipboardGetText(); +} + +export function ClipboardSetText(text) { + return window.runtime.ClipboardSetText(text); +} + +/** + * Callback for OnFileDrop returns a slice of file path strings when a drop is finished. + * + * @export + * @callback OnFileDropCallback + * @param {number} x - x coordinate of the drop + * @param {number} y - y coordinate of the drop + * @param {string[]} paths - A list of file paths. + */ + +/** + * OnFileDrop listens to drag and drop events and calls the callback with the coordinates of the drop and an array of path strings. + * + * @export + * @param {OnFileDropCallback} callback - Callback for OnFileDrop returns a slice of file path strings when a drop is finished. + * @param {boolean} [useDropTarget=true] - Only call the callback when the drop finished on an element that has the drop target style. (--wails-drop-target) + */ +export function OnFileDrop(callback, useDropTarget) { + return window.runtime.OnFileDrop(callback, useDropTarget); +} + +/** + * OnFileDropOff removes the drag and drop listeners and handlers. + */ +export function OnFileDropOff() { + return window.runtime.OnFileDropOff(); +} + +export function CanResolveFilePaths() { + return window.runtime.CanResolveFilePaths(); +} + +export function ResolveFilePaths(files) { + return window.runtime.ResolveFilePaths(files); +} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 0cc62d2..e83f839 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,8 @@ "test": "jest --watchAll", "lint": "expo lint", "build:preview": "eas build -p android --profile preview --local", - "build:android": "eas build -p android --local" + "build:android": "eas build -p android --local", + "build:web": "expo export -p web" }, "jest": { "preset": "jest-expo" diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 new file mode 100644 index 0000000..bf93129 --- /dev/null +++ b/frontend/package.json.md5 @@ -0,0 +1 @@ +9084e4692d068564abc2554891e6cd45 \ No newline at end of file diff --git a/frontend/pages/server/page.tsx b/frontend/pages/server/page.tsx index 22f52fd..11e4453 100644 --- a/frontend/pages/server/page.tsx +++ b/frontend/pages/server/page.tsx @@ -11,11 +11,14 @@ import { useMutation } from "@tanstack/react-query"; import { ofetch } from "ofetch"; import { z } from "zod"; import { ErrorAlert } from "@/components/ui/alert"; -import { addServer } from "@/stores/app"; +import appStore, { addServer, setCurrentServer } from "@/stores/app"; import tamaguiConfig from "@/tamagui.config"; +import { useStore } from "zustand"; +import Icons from "@/components/ui/icons"; export default function ServerPage() { const form = useZForm(serverSchema); + const localServer = useStore(appStore, (i) => i.localServer); const serverConnect = useMutation({ mutationFn: async (body: z.infer) => { @@ -36,6 +39,13 @@ export default function ServerPage() { serverConnect.mutate(values); }); + const onUseLocalServer = () => { + if (localServer) { + setCurrentServer(localServer); + router.replace("/auth/login"); + } + }; + return ( <> - + + {localServer != null && ( + <> + or + + + )} diff --git a/frontend/stores/app.ts b/frontend/stores/app.ts index 2177025..2cb6669 100644 --- a/frontend/stores/app.ts +++ b/frontend/stores/app.ts @@ -8,15 +8,17 @@ export type AppServer = { }; type AppStore = { + localServer?: string | null; servers: AppServer[]; - curServerIdx?: number | null; + curServer?: string | null; }; const appStore = createStore( persist( () => ({ + localServer: null, servers: [], - curServerIdx: null, + curServer: null, }), { name: "vaulterm:app", @@ -30,36 +32,42 @@ export function addServer(srv: AppServer, setActive?: boolean) { const isExist = curServers.findIndex((s) => s.url === srv.url); if (isExist >= 0) { - setActiveServer(isExist); + setCurrentServer(srv.url); return; } appStore.setState((state) => ({ servers: [...state.servers, srv], - curServerIdx: setActive ? state.servers.length : state.curServerIdx, + curServer: setActive ? srv.url : state.curServer, })); } export function removeServer(idx: number) { appStore.setState((state) => ({ servers: state.servers.filter((_, i) => i !== idx), - curServerIdx: state.curServerIdx === idx ? null : state.curServerIdx, })); } -export function setActiveServer(idx: number) { - appStore.setState({ curServerIdx: idx }); +export function setCurrentServer(url: string) { + appStore.setState({ curServer: url }); } export function getCurrentServer() { - const state = appStore.getState(); - return state.curServerIdx != null ? state.servers[state.curServerIdx] : null; + return appStore.getState().curServer; +} + +export function setLocalServer(url: string) { + appStore.setState((state) => ({ + localServer: url, + curServer: + !state.curServer || state.curServer === state.localServer + ? url + : state.curServer, + })); } export const useServer = () => { - const servers = useStore(appStore, (i) => i.servers); - const idx = useStore(appStore, (i) => i.curServerIdx); - return servers.length > 0 && idx != null ? servers[idx] : null; + return useStore(appStore, (i) => i.curServer); }; export default appStore; diff --git a/server/go.mod b/go.mod similarity index 64% rename from server/go.mod rename to go.mod index cb5429d..c148092 100644 --- a/server/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/joho/godotenv v1.5.1 github.com/oklog/ulid/v2 v2.1.0 github.com/stretchr/testify v1.9.0 + github.com/wailsapp/wails/v2 v2.9.2 golang.org/x/crypto v0.28.0 gorm.io/datatypes v1.2.4 gorm.io/driver/postgres v1.5.9 @@ -25,7 +26,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.55.0 // indirect @@ -35,18 +36,36 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/bep/debounce v1.2.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/kr/text v0.2.0 // indirect + github.com/labstack/echo/v4 v4.10.2 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/leaanthony/go-ansi-parser v1.6.0 // indirect + github.com/leaanthony/gosod v1.0.3 // indirect + github.com/leaanthony/slicer v1.6.0 // indirect + github.com/leaanthony/u v1.1.0 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/samber/lo v1.38.1 // indirect + github.com/tkrajina/go-reflector v0.5.6 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/wailsapp/go-webview2 v1.0.16 // indirect + github.com/wailsapp/mimetype v1.4.1 // indirect + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect diff --git a/server/go.sum b/go.sum similarity index 62% rename from server/go.sum rename to go.sum index 9979d05..421acfe 100644 --- a/server/go.sum +++ b/go.sum @@ -2,15 +2,21 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/fasthttp/websocket v1.5.10 h1:bc7NIGyrg1L6sd5pRzCIbXpro54SZLEluZCu0rOpcN4= github.com/fasthttp/websocket v1.5.10/go.mod h1:BwHeuXGWzCW1/BIKUKD3+qfCl+cTdsHu/f243NcAI/Q= +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/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofiber/contrib/websocket v1.3.2 h1:AUq5PYeKwK50s0nQrnluuINYeep1c4nRCJ0NWsV3cvg= github.com/gofiber/contrib/websocket v1.3.2/go.mod h1:07u6QGMsvX+sx7iGNCl5xhzuUVArWwLQ3tBIH24i+S8= github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= @@ -29,6 +35,8 @@ github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +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/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -41,8 +49,27 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M= +github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/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.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg= +github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU= +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/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY= +github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js= +github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8= +github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI= +github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI= +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/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -55,12 +82,19 @@ github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOa github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc= github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -68,30 +102,56 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE= +github.com/tkrajina/go-reflector v0.5.6/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/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/wailsapp/go-webview2 v1.0.16 h1:wffnvnkkLvhRex/aOrA3R7FP7rkvOqL/bir1br7BekU= +github.com/wailsapp/go-webview2 v1.0.16/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo= +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.9.2 h1:Xb5YRTos1w5N7DTMyYegWaGukCP2fIaX9WF21kPPF2k= +github.com/wailsapp/wails/v2 v2.9.2/go.mod h1:uehvlCwJSFcBq7rMCGfk4rxca67QQGsbg5Nm4m9UnBs= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +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-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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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= gorm.io/datatypes v1.2.4 h1:uZmGAcK/QZ0uyfCuVg0VQY1ZmV9h1fuG0tMwKByO1z4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..88f5e1a --- /dev/null +++ b/main.go @@ -0,0 +1,26 @@ +//go:build !gui +// +build !gui + +package main + +import ( + "log" + "os" + + srv "rul.sh/vaulterm/server/app" +) + +func main() { + server := srv.NewApp() + + port := os.Getenv("PORT") + if port == "" { + port = "3000" + } + + log.Printf("Starting server on port %s\n", port) + + if err := server.Listen(":" + port); err != nil { + log.Printf("Server error: %s\n", err) + } +} diff --git a/main_gui.go b/main_gui.go new file mode 100644 index 0000000..76a1e3b --- /dev/null +++ b/main_gui.go @@ -0,0 +1,63 @@ +//go:build gui +// +build gui + +package main + +import ( + "context" + "embed" + "log" + "net" + + "github.com/wailsapp/wails/v2/pkg/application" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" + srv "rul.sh/vaulterm/server/app" +) + +//go:embed all:frontend/dist +var assets embed.FS + +func main() { + server := srv.NewApp() + defer server.Shutdown() + + // Create wails app + appCtx := NewApp() + app := application.NewWithOptions(&options.App{ + Title: "Vaulterm", + Width: 1150, + Height: 720, + AssetServer: &assetserver.Options{ + Assets: assets, + }, + BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, + OnStartup: func(ctx context.Context) { + appCtx.startup(ctx) + }, + Bind: []interface{}{appCtx}, + }) + + // Run the local server + go func() { + defer app.Quit() + + listener, err := net.Listen("tcp", ":0") + if err != nil { + log.Fatal(err) + } + + port := listener.Addr().(*net.TCPAddr).Port + appCtx.localPort = port + + log.Printf("Starting server on http://localhost:%d\n", port) + + if err := server.Listener(listener); err != nil { + log.Printf("Server error: %s\n", err) + } + }() + + if err := app.Run(); err != nil { + println("Error:", err.Error()) + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..53a0f78 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "vaulterm", + "version": "1.0.0", + "author": "Khairul Hidayat", + "license": "MIT", + "description": "Your Unified Vault for Remote Access", + "scripts": { + "test": "go test -count 1 -p 1 -v rul.sh/vaulterm/server/tests", + "dev": "wails dev -s -tags \"gui\"", + "build": "cross-env CGO_ENABLED=1 wails build -tags \"gui\" -m -skipbindings -s", + "build:frontend": "cd frontend && npm run build:web", + "build:server": "cross-env CGO_ENABLED=1 go build -o vaulterm ." + }, + "keywords": [ + "vaulterm", + "ssh", + "vnc", + "incus", + "remote-access", + "terminal" + ], + "devDependencies": { + "cross-env": "^7.0.3" + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..17ae51c --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,70 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + +packages: + + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + + cross-spawn@7.0.5: + resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} + engines: {node: '>= 8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + +snapshots: + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.5 + + cross-spawn@7.0.5: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + isexe@2.0.0: {} + + path-key@3.1.1: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 diff --git a/server/.gitignore b/server/.gitignore deleted file mode 100644 index 33ca294..0000000 --- a/server/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -tmp/ -.env -/data.db* diff --git a/server/Makefile b/server/Makefile deleted file mode 100644 index 3a6a6b9..0000000 --- a/server/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# Makefile - -.PHONY: test build run - -# Run tests sequentially with verbose output -test: - go test -count 1 -p 1 -v rul.sh/vaulterm/tests - -# Build the Go application -build: - go build -o myapp . - -# Run the built application -run: build - ./myapp diff --git a/server/app/app.go b/server/app/app.go index 3924f2d..ecc4f34 100644 --- a/server/app/app.go +++ b/server/app/app.go @@ -4,13 +4,15 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/joho/godotenv" - "rul.sh/vaulterm/app/auth" - "rul.sh/vaulterm/db" - "rul.sh/vaulterm/middleware" + "rul.sh/vaulterm/server/app/auth" + "rul.sh/vaulterm/server/db" + "rul.sh/vaulterm/server/middleware" + "rul.sh/vaulterm/server/utils" ) func NewApp() *fiber.App { // Load deps + utils.CheckAndCreateEnvFile() godotenv.Load() db.Init() diff --git a/server/app/auth/repository.go b/server/app/auth/repository.go index 79cb050..72c612d 100644 --- a/server/app/auth/repository.go +++ b/server/app/auth/repository.go @@ -2,9 +2,9 @@ package auth import ( "gorm.io/gorm" - "rul.sh/vaulterm/db" - "rul.sh/vaulterm/lib" - "rul.sh/vaulterm/models" + "rul.sh/vaulterm/server/db" + "rul.sh/vaulterm/server/lib" + "rul.sh/vaulterm/server/models" ) type Auth struct{ db *gorm.DB } diff --git a/server/app/auth/router.go b/server/app/auth/router.go index ec97d01..5f8a667 100644 --- a/server/app/auth/router.go +++ b/server/app/auth/router.go @@ -2,10 +2,10 @@ package auth import ( "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/lib" - "rul.sh/vaulterm/middleware" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/lib" + "rul.sh/vaulterm/server/middleware" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) func Router(app *fiber.App) { diff --git a/server/app/auth/schema.go b/server/app/auth/schema.go index ae7cfaf..2dbc849 100644 --- a/server/app/auth/schema.go +++ b/server/app/auth/schema.go @@ -1,6 +1,6 @@ package auth -import "rul.sh/vaulterm/middleware" +import "rul.sh/vaulterm/server/middleware" type LoginSchema struct { Username string `json:"username"` diff --git a/server/app/hosts/repository.go b/server/app/hosts/repository.go index ce45af9..c65bb18 100644 --- a/server/app/hosts/repository.go +++ b/server/app/hosts/repository.go @@ -2,9 +2,9 @@ package hosts import ( "gorm.io/gorm" - "rul.sh/vaulterm/db" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/db" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) type Hosts struct { diff --git a/server/app/hosts/router.go b/server/app/hosts/router.go index 0bb51ac..7e1f563 100644 --- a/server/app/hosts/router.go +++ b/server/app/hosts/router.go @@ -6,8 +6,8 @@ import ( "net/http" "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) func Router(app fiber.Router) { diff --git a/server/app/hosts/utils.go b/server/app/hosts/utils.go index 2a20139..cf12fdd 100644 --- a/server/app/hosts/utils.go +++ b/server/app/hosts/utils.go @@ -2,13 +2,12 @@ package hosts import ( "fmt" - "log" "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/app/keychains" - "rul.sh/vaulterm/lib" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/app/keychains" + "rul.sh/vaulterm/server/lib" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) func tryConnect(c *fiber.Ctx, host *models.Host) (string, error) { @@ -46,8 +45,6 @@ func tryConnect(c *fiber.Ctx, host *models.Host) (string, error) { } defer c.Close() - log.Println("Test", c.Conn) - os, err := c.GetOS(c) if err != nil { return "", err diff --git a/server/app/keychains/repository.go b/server/app/keychains/repository.go index 8b6ffa9..23cc079 100644 --- a/server/app/keychains/repository.go +++ b/server/app/keychains/repository.go @@ -2,9 +2,9 @@ package keychains import ( "gorm.io/gorm" - "rul.sh/vaulterm/db" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/db" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) type Keychains struct { diff --git a/server/app/keychains/router.go b/server/app/keychains/router.go index fe0a577..95ccbfc 100644 --- a/server/app/keychains/router.go +++ b/server/app/keychains/router.go @@ -5,8 +5,8 @@ import ( "net/http" "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) func Router(app fiber.Router) { diff --git a/server/app/router.go b/server/app/router.go index 76f2b3c..51f9d45 100644 --- a/server/app/router.go +++ b/server/app/router.go @@ -2,11 +2,11 @@ package app import ( "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/app/hosts" - "rul.sh/vaulterm/app/keychains" - "rul.sh/vaulterm/app/teams" - "rul.sh/vaulterm/app/teams/members" - "rul.sh/vaulterm/app/ws" + "rul.sh/vaulterm/server/app/hosts" + "rul.sh/vaulterm/server/app/keychains" + "rul.sh/vaulterm/server/app/teams" + "rul.sh/vaulterm/server/app/teams/members" + "rul.sh/vaulterm/server/app/ws" ) func InitRouter(app *fiber.App) { diff --git a/server/app/teams/members/repository.go b/server/app/teams/members/repository.go index a1092af..ffc319b 100644 --- a/server/app/teams/members/repository.go +++ b/server/app/teams/members/repository.go @@ -3,9 +3,9 @@ package members import ( "gorm.io/gorm" "gorm.io/gorm/clause" - "rul.sh/vaulterm/db" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/db" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) type TeamMembers struct { diff --git a/server/app/teams/members/router.go b/server/app/teams/members/router.go index 5e33e83..95732f1 100644 --- a/server/app/teams/members/router.go +++ b/server/app/teams/members/router.go @@ -4,10 +4,10 @@ import ( "errors" "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/app/teams" - "rul.sh/vaulterm/app/users" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/app/teams" + "rul.sh/vaulterm/server/app/users" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) func Router(app fiber.Router) { diff --git a/server/app/teams/repository.go b/server/app/teams/repository.go index cc024f5..b3e06e6 100644 --- a/server/app/teams/repository.go +++ b/server/app/teams/repository.go @@ -2,9 +2,9 @@ package teams import ( "gorm.io/gorm" - "rul.sh/vaulterm/db" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/db" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) type Teams struct { diff --git a/server/app/teams/router.go b/server/app/teams/router.go index 45d9284..82491b8 100644 --- a/server/app/teams/router.go +++ b/server/app/teams/router.go @@ -5,8 +5,8 @@ import ( "net/http" "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) func Router(app fiber.Router) fiber.Router { diff --git a/server/app/users/repository.go b/server/app/users/repository.go index c13ad39..7ef95aa 100644 --- a/server/app/users/repository.go +++ b/server/app/users/repository.go @@ -2,9 +2,9 @@ package users import ( "gorm.io/gorm" - "rul.sh/vaulterm/db" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/db" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) type Users struct { diff --git a/server/app/ws/router.go b/server/app/ws/router.go index a293eb8..52d7ebf 100644 --- a/server/app/ws/router.go +++ b/server/app/ws/router.go @@ -3,8 +3,8 @@ package ws import ( "github.com/gofiber/contrib/websocket" "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/app/ws/stats" - "rul.sh/vaulterm/app/ws/term" + "rul.sh/vaulterm/server/app/ws/stats" + "rul.sh/vaulterm/server/app/ws/term" ) func Router(app fiber.Router) { diff --git a/server/app/ws/stats/ssh.go b/server/app/ws/stats/ssh.go index 9a383fa..4bdf653 100644 --- a/server/app/ws/stats/ssh.go +++ b/server/app/ws/stats/ssh.go @@ -10,7 +10,7 @@ import ( "time" "github.com/gofiber/contrib/websocket" - "rul.sh/vaulterm/lib" + "rul.sh/vaulterm/server/lib" ) func HandleSSHStats(c *websocket.Conn, client *lib.SSHClient) error { diff --git a/server/app/ws/stats/stats.go b/server/app/ws/stats/stats.go index 4ff697c..d0899e6 100644 --- a/server/app/ws/stats/stats.go +++ b/server/app/ws/stats/stats.go @@ -2,10 +2,10 @@ package stats import ( "github.com/gofiber/contrib/websocket" - "rul.sh/vaulterm/app/hosts" - "rul.sh/vaulterm/lib" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/app/hosts" + "rul.sh/vaulterm/server/lib" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) func HandleStats(c *websocket.Conn) { diff --git a/server/app/ws/term/incus.go b/server/app/ws/term/incus.go index 3cfdbcc..8c37914 100644 --- a/server/app/ws/term/incus.go +++ b/server/app/ws/term/incus.go @@ -9,7 +9,7 @@ import ( fastWs "github.com/fasthttp/websocket" "github.com/gofiber/contrib/websocket" - "rul.sh/vaulterm/lib" + "rul.sh/vaulterm/server/lib" ) type IncusWebsocketSession struct { diff --git a/server/app/ws/term/pve.go b/server/app/ws/term/pve.go index d6060df..0e0be74 100644 --- a/server/app/ws/term/pve.go +++ b/server/app/ws/term/pve.go @@ -11,7 +11,7 @@ import ( fastWs "github.com/fasthttp/websocket" "github.com/gofiber/contrib/websocket" - "rul.sh/vaulterm/lib" + "rul.sh/vaulterm/server/lib" ) // https://github.com/proxmox/pve-xtermjs/blob/master/README diff --git a/server/app/ws/term/ssh.go b/server/app/ws/term/ssh.go index 6c47c78..fba7e51 100644 --- a/server/app/ws/term/ssh.go +++ b/server/app/ws/term/ssh.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/gofiber/contrib/websocket" - "rul.sh/vaulterm/lib" + "rul.sh/vaulterm/server/lib" ) func NewSSHWebsocketSession(c *websocket.Conn, client *lib.SSHClient) ([]byte, error) { diff --git a/server/app/ws/term/term.go b/server/app/ws/term/term.go index c3c012a..8f313a1 100644 --- a/server/app/ws/term/term.go +++ b/server/app/ws/term/term.go @@ -5,11 +5,11 @@ import ( "time" "github.com/gofiber/contrib/websocket" - "rul.sh/vaulterm/app/hosts" - "rul.sh/vaulterm/db" - "rul.sh/vaulterm/lib" - "rul.sh/vaulterm/models" - "rul.sh/vaulterm/utils" + "rul.sh/vaulterm/server/app/hosts" + "rul.sh/vaulterm/server/db" + "rul.sh/vaulterm/server/lib" + "rul.sh/vaulterm/server/models" + "rul.sh/vaulterm/server/utils" ) func HandleTerm(c *websocket.Conn) { diff --git a/server/db/database.go b/server/db/database.go index a44e976..babe18a 100644 --- a/server/db/database.go +++ b/server/db/database.go @@ -24,7 +24,8 @@ func Init() { dsn := os.Getenv("DATABASE_URL") if dsn == "" { - dsn = "file:data.db?cache=shared&mode=rwc&_journal_mode=WAL" + // WAL: _journal_mode=WAL + dsn = "file:data.db?cache=shared&mode=rwc" } // Open db connection diff --git a/server/db/models.go b/server/db/models.go index 9c2766e..dd936d3 100644 --- a/server/db/models.go +++ b/server/db/models.go @@ -1,7 +1,7 @@ package db import ( - "rul.sh/vaulterm/models" + "rul.sh/vaulterm/server/models" ) var Models = []interface{}{ diff --git a/server/db/seeders.go b/server/db/seeders.go index 11c466f..3daa9d5 100644 --- a/server/db/seeders.go +++ b/server/db/seeders.go @@ -2,8 +2,8 @@ package db import ( "gorm.io/gorm" - "rul.sh/vaulterm/lib" - "rul.sh/vaulterm/models" + "rul.sh/vaulterm/server/lib" + "rul.sh/vaulterm/server/models" ) type SeedFn func(*gorm.DB) error diff --git a/server/lib/crypto.go b/server/lib/crypto.go index 39ecae3..39d4920 100644 --- a/server/lib/crypto.go +++ b/server/lib/crypto.go @@ -138,3 +138,13 @@ func Decrypt(encrypted string) (string, error) { return string(res), nil } + +// Function to generate a 32-byte random key for AES-256 +func GenerateRandomKey() (string, error) { + key := make([]byte, 32) // 32 bytes = 256 bits + _, err := rand.Read(key) + if err != nil { + return "", err + } + return hex.EncodeToString(key), nil +} diff --git a/server/main.go b/server/main.go deleted file mode 100644 index fda0744..0000000 --- a/server/main.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "os" - - "rul.sh/vaulterm/app" -) - -func main() { - app := app.NewApp() - - port := os.Getenv("PORT") - if port == "" { - port = "3000" - } - - app.Listen(":" + port) -} diff --git a/server/middleware/auth.go b/server/middleware/auth.go index 0b05af3..3bd6669 100644 --- a/server/middleware/auth.go +++ b/server/middleware/auth.go @@ -1,11 +1,12 @@ package middleware import ( + "errors" "strings" "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/db" - "rul.sh/vaulterm/models" + "rul.sh/vaulterm/server/db" + "rul.sh/vaulterm/server/models" ) func Auth(c *fiber.Ctx) error { @@ -36,6 +37,10 @@ type AuthUser struct { } func GetUserSession(sessionId string) (*AuthUser, error) { + if sessionId == "" { + return nil, errors.New("sessionid is empty") + } + var session AuthUser res := db.Get(). diff --git a/server/models/keychain.go b/server/models/keychain.go index c779d39..776037c 100644 --- a/server/models/keychain.go +++ b/server/models/keychain.go @@ -3,7 +3,7 @@ package models import ( "encoding/json" - "rul.sh/vaulterm/lib" + "rul.sh/vaulterm/server/lib" ) const ( diff --git a/server/tests/setup_test.go b/server/tests/setup_test.go index 8d61144..77e90fe 100644 --- a/server/tests/setup_test.go +++ b/server/tests/setup_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - "rul.sh/vaulterm/db" + "rul.sh/vaulterm/server/db" ) func TestMain(m *testing.M) { diff --git a/server/tests/utils.go b/server/tests/utils.go index d5b6ba6..dd43c11 100644 --- a/server/tests/utils.go +++ b/server/tests/utils.go @@ -13,7 +13,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/stretchr/testify/assert" - "rul.sh/vaulterm/app" + "rul.sh/vaulterm/server/app" ) type HTTPTest struct { diff --git a/server/utils/config.go b/server/utils/config.go new file mode 100644 index 0000000..600ad05 --- /dev/null +++ b/server/utils/config.go @@ -0,0 +1,31 @@ +package utils + +import ( + "fmt" + "os" + + "rul.sh/vaulterm/server/lib" +) + +func CheckAndCreateEnvFile() error { + // Check if .env file exists + if _, err := os.Stat(".env"); !os.IsNotExist(err) { + return nil + } + + // File doesn't exist, so create it + randomKey, err := lib.GenerateRandomKey() + if err != nil { + return err + } + + // Write the random key to the .env file + envContent := fmt.Sprintf("ENCRYPTION_KEY=%s\n", randomKey) + err = os.WriteFile(".env", []byte(envContent), 0644) + if err != nil { + return err + } + fmt.Println(".env file created with ENCRYPTION_KEY.") + + return nil +} diff --git a/server/utils/context.go b/server/utils/context.go index 859021f..18bbc77 100644 --- a/server/utils/context.go +++ b/server/utils/context.go @@ -3,7 +3,7 @@ package utils import ( "github.com/gofiber/contrib/websocket" "github.com/gofiber/fiber/v2" - "rul.sh/vaulterm/middleware" + "rul.sh/vaulterm/server/middleware" ) type UserContext = middleware.AuthUser diff --git a/wails.json b/wails.json new file mode 100644 index 0000000..bbe13ad --- /dev/null +++ b/wails.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://wails.io/schemas/config.v2.json", + "name": "Vaulterm", + "author": { + "name": "Khairul Hidayat", + "email": "khai@rul.sh" + }, + "outputfilename": "vaulterm", + "frontend:install": "pnpm install", + "frontend:build": "pnpm run build:web", + "frontend:dev:watcher": "pnpm run start", + "frontend:dev:serverUrl": "http://localhost:8081", + "wailsjsdir": "frontend/lib" +} \ No newline at end of file