garethgeorge / backrest

Backrest is a web UI and orchestrator for restic backup.
GNU General Public License v3.0
779 stars 31 forks source link

Windows electron app for Backrest #283

Open stefnotch opened 1 month ago

stefnotch commented 1 month ago

Note: if you have a question or want discussion please post in the discussions area.

Is your feature request related to a problem? Please describe.

I would like to be able to recommend backup programs, like restic/rustic to non-techy family members. This means that a GUI, like what backrest offers, is awesome.

However, it currently seems to not be designed to be a program where I can simply

  1. Download it
  2. Start it - automatically opens a GUI window
  3. Do whatever I need
  4. Close it

Instead, it is

  1. Download it
  2. Start it, and be surprised about a CLI window
  3. Navigate to http://localhost:9898/ in the browser
  4. Do whatever I need
  5. Close the CLI window

Describe the solution you'd like

Would it be reasonable to offer a variant of the GUI which comes with an embedded web browser? (e.g. Tauri or Electron) Then, it'd be possible for backrest to work like

  1. Download it
  2. Start it, backrest and a HTTP server get started in the background, and a normal looking GUI gets opened. That normal looking GUI is actually a fully fledged web browser. In the case of Electron, it's a massive embedded Chrome, in the case of Tauri it elegantly embeds the system's web browser.
  3. I do whatever I need
  4. I close the window, which also gracefully shuts down the HTTP server and backrest.
stefnotch commented 1 month ago

Apparently there are webview libraries for Go, such as https://github.com/webview/webview_go and https://github.com/pojntfx/hydrapp . There are also a few others which either seem to be unmaintained, or which don't support important browsers.

garethgeorge commented 1 month ago

Hey, just to clarify a bit -- is this a problem you're running into on Windows? An open issue is that Backrest's windows operations aren't very streamlined.

I'd happily accept community contributions to improve the state of things here, my windows knowledge isn't particular deep so it's not something I've been working on. Ideally Backrest should include an installer that runs the binary as a background service.

On MacOS / Linux the installer should already be setup to run the binary in the background (no visible window).

On the GUI front, this is another place where I'd happily accept community work to shim an electron app around Backrest as I can definitely understand the utility but probably it isn't a focus for me and isn't something I'm interested to maintain at the moment.

stefnotch commented 1 month ago

Yes, I'm running into this on Windows. If my target audience were tech-y enough to use Linux, then I'd just show them the restic CLI.

I suggested the webview option, because if such a library is mature enough, then using it is relatively simple, see https://github.com/webview/webview_go/blob/master/examples/basic/main.go . Of course that doesn't cover other parts that one might want, like a tray icon.

aep commented 1 month ago

I'm interested in this for commercial reasons so we can definitely help with contributions if there's a consensus.

I don't think an "native app" really is required tho. In my opinion it's completely fine to open the default Webbrowser.

What's more important is a desktop shortcut that does that.

stefnotch commented 1 month ago

@aep I've gotten feedback from multiple people that apps which just open the webbrowser are somewhat disliked. They pretty much need a tray icon to tell the user that the app is still running, and to let the user close the app. It also looks very confusing when the user closes the app, but the website is still open. So one has to get the website to regularly check if the app is still running, and if not, the website has to show some sort of message. And then there's the tidbit about having to support weirder configurations, like a user who has NoScript or an overzealous adblocker installed.

Hence my request to use a webview library. They open an already installed and webbrowser as a separated window, with a separate profile, which makes them very lightweight and shockingly easy to integrate. And they avoid all the annoyances mentioned above.

aep commented 1 month ago

Fully agree on the systray. That's a thing windows users expect.

Not sure about the wrapper app yet, but if someone else wants to pick that up why not. Currently considering if we can help with the installer first.

stefnotch commented 1 month ago

Sounds like a reasonable plan, improving the status quo one step at a time.

I wish I knew enough Go to quickly whip up a prototype, but I currently do not.

garethgeorge commented 1 month ago

Hey all, just chiming in to say that community contributions re: a windows installer would be very welcome.

I think the right place to put the code for any contributions would be in a build subfolder (following the rough example here https://github.com/golang-standards/project-layout/tree/master/build) and I should probably consolidate the Dockerfiles and other release related code in there.

There's a bug tracking an installer https://github.com/garethgeorge/backrest/issues/163 but I don't think any recent progress has been made. Ideally we are able to offer a simple binary that we can package with the release (given a PR adding a codegen for a windows installer I can easily add it to the release workflow). Feel free to chime in on that bug if it's something you're interested to take up.

Beyond that, I think I can see a system tray release as something worth supporting in this repo, I found https://github.com/getlantern/systray as a good option here. I think the way to go here would be a separate binary e.g. backrestmon which is installed along with backrest and which forks and manages the backrest process (e.g. perhaps supporting a start / stop button? Backrest could be configured to write it's PID into a tmp file to support this) and provides a tray icon with a link that opens the backrest UI. In this model the backrestmon binary is largely independent / doesn't need to share code with backrest. It's just an optional extra binary that can be included with the macOS and Windows releases along side the installer.

aep commented 1 month ago

@ThaUnknown

ThaUnknown commented 1 month ago

@garethgeorge by far the easiest way to solve this is with nsis, I created a simple NSIS script for this, I think it's the best solution because it also can be compiled cross platform, as there are nsis compilers for linux.

However there are some key issues with this software on windows: it needs an always open CMD window to run, from a sysadmin perspective it's annoying, because if the user closes it, it kills the software, it would be nice if we could simply launch the software with some --as-service flag which simply runs it in background.

The script needs 2 dynamic changes, the build directory [where the files included in the build dist are stored], and the out dir [where the installer is outputted], those are configured in the first 2 lines. Additionally it also needs an icon in the build directory, which I also made based on the WebUI logo.

The NSIS script:

!define BUILD_DIR "D:\change_me"
!define OUT_DIR "D:\change_me_2"
!define APP_NAME "Backrest"
!define COMP_NAME "garethgeorge"
!define WEB_SITE "https://github.com/garethgeorge/backrest"
!define VERSION "00.00.00.00"
!define COPYRIGHT "garethgeorge   2024"
!define DESCRIPTION "Application"
!define LICENSE_TXT "${BUILD_DIR}\LICENSE.txt"
!define INSTALLER_NAME "${OUT_DIR}\Backrest-setup.exe"
!define MAIN_APP_EXE "backrest.exe"
!define INSTALL_TYPE "SetShellVarContext current"
!define REG_ROOT "HKCU"
!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${MAIN_APP_EXE}"
!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}"

!define REG_START_MENU "Start Menu Folder"

var SM_Folder

######################################################################

VIProductVersion  "${VERSION}"
VIAddVersionKey "ProductName"  "${APP_NAME}"
VIAddVersionKey "CompanyName"  "${COMP_NAME}"
VIAddVersionKey "LegalCopyright"  "${COPYRIGHT}"
VIAddVersionKey "FileDescription"  "${DESCRIPTION}"
VIAddVersionKey "FileVersion"  "${VERSION}"

######################################################################

SetCompressor ZLIB
Name "${APP_NAME}"
Caption "${APP_NAME}"
OutFile "${INSTALLER_NAME}"
BrandingText "${APP_NAME}"
XPStyle on
InstallDirRegKey "${REG_ROOT}" "${REG_APP_PATH}" ""
InstallDir "$PROGRAMFILES\Backrest"

######################################################################

!include "MUI.nsh"

!define MUI_ABORTWARNING
!define MUI_UNABORTWARNING

!insertmacro MUI_PAGE_WELCOME

!ifdef LICENSE_TXT
!insertmacro MUI_PAGE_LICENSE "${LICENSE_TXT}"
!endif

!insertmacro MUI_PAGE_DIRECTORY

!ifdef REG_START_MENU
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Backrest"
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${REG_ROOT}"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "${UNINSTALL_PATH}"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${REG_START_MENU}"
!insertmacro MUI_PAGE_STARTMENU Application $SM_Folder
!endif

!insertmacro MUI_PAGE_INSTFILES

!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAIN_APP_EXE}"
!insertmacro MUI_PAGE_FINISH

!insertmacro MUI_UNPAGE_CONFIRM

!insertmacro MUI_UNPAGE_INSTFILES

!insertmacro MUI_UNPAGE_FINISH

!insertmacro MUI_LANGUAGE "English"

######################################################################

Section -MainProgram
${INSTALL_TYPE}
SetOverwrite ifnewer
SetOutPath "$INSTDIR"
File "${BUILD_DIR}\backrest.exe"
File "${BUILD_DIR}\CHANGELOG.md"
File "${BUILD_DIR}\LICENSE.txt"
File "${BUILD_DIR}\README.md"
SectionEnd

######################################################################

Section "Run at startup"
CreateDirectory $SMSTARTUP
CreateShortcut "$SMSTARTUP\$(^Name).lnk" "$INSTDIR\${MAIN_APP_EXE}" "" "${BUILD_DIR}\icon.ico" 0
SectionEnd

Section -Icons_Reg
SetOutPath "$INSTDIR"
WriteUninstaller "$INSTDIR\uninstall.exe"

!ifdef REG_START_MENU
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
CreateDirectory "$SMPROGRAMS\$SM_Folder"
CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}" "" "${BUILD_DIR}\icon.ico" 0
CreateShortCut "$DESKTOP\${APP_NAME} Console.lnk" "$INSTDIR\${MAIN_APP_EXE}" "" "${BUILD_DIR}\icon.ico" 0
CreateShortCut "$DESKTOP\${APP_NAME} UI.lnk" "http://localhost:9898/" "" "${BUILD_DIR}\icon.ico" 0
CreateShortCut "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe" "" "${BUILD_DIR}\icon.ico" 0

!ifdef WEB_SITE
WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}"
CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME} Website.lnk" "$INSTDIR\${APP_NAME} website.url" "" "${BUILD_DIR}\icon.ico" 0
!endif
!insertmacro MUI_STARTMENU_WRITE_END
!endif

!ifndef REG_START_MENU
CreateDirectory "$SMPROGRAMS\Backrest"
CreateShortCut "$SMPROGRAMS\Backrest\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}" "" "${BUILD_DIR}\icon.ico" 0
CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}" "" "${BUILD_DIR}\icon.ico" 0
CreateShortCut "$SMPROGRAMS\Backrest\Uninstall ${APP_NAME}.lnk" "$INSTDIR\uninstall.exe" "" "${BUILD_DIR}\icon.ico" 0

!ifdef WEB_SITE
WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}"
CreateShortCut "$SMPROGRAMS\Backrest\${APP_NAME} Website.lnk" "$INSTDIR\${APP_NAME} website.url" "" "${BUILD_DIR}\icon.ico" 0
!endif
!endif

WriteRegStr ${REG_ROOT} "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}"  "DisplayName" "${APP_NAME}"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}"  "UninstallString" "$INSTDIR\uninstall.exe"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}"  "DisplayIcon" "$INSTDIR\${MAIN_APP_EXE}"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}"  "DisplayVersion" "${VERSION}"
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}"  "Publisher" "${COMP_NAME}"

!ifdef WEB_SITE
WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}"  "URLInfoAbout" "${WEB_SITE}"
!endif
SectionEnd

######################################################################

Section Uninstall
${INSTALL_TYPE}
Delete "$INSTDIR\backrest.exe"
Delete "$INSTDIR\CHANGELOG.md"
Delete "$INSTDIR\install.sh"
Delete "$INSTDIR\LICENSE.txt"
Delete "$INSTDIR\README.md"
Delete "$INSTDIR\uninstall.sh"
Delete "$INSTDIR\uninstall.exe"
Delete "$SMSTARTUP\$(^Name).lnk"
!ifdef WEB_SITE
Delete "$INSTDIR\${APP_NAME} website.url"
!endif

RmDir "$INSTDIR"

!ifdef REG_START_MENU
!insertmacro MUI_STARTMENU_GETFOLDER "Application" $SM_Folder
Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk"
Delete "$SMPROGRAMS\$SM_Folder\Uninstall ${APP_NAME}.lnk"
!ifdef WEB_SITE
Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME} Website.lnk"
!endif
Delete "$DESKTOP\${APP_NAME}.lnk"

RmDir "$SMPROGRAMS\$SM_Folder"
!endif

!ifndef REG_START_MENU
Delete "$SMPROGRAMS\Backrest\${APP_NAME}.lnk"
Delete "$SMPROGRAMS\Backrest\Uninstall ${APP_NAME}.lnk"
!ifdef WEB_SITE
Delete "$SMPROGRAMS\Backrest\${APP_NAME} Website.lnk"
!endif
Delete "$DESKTOP\${APP_NAME}.lnk"

RmDir "$SMPROGRAMS\Backrest"
!endif

DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}"
DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}"
SectionEnd

the icon: icon.zip

sebcej commented 1 month ago

Another great possibility can be Wails. I'm thinking about making a try in the near future

garethgeorge commented 1 month ago

@garethgeorge by far the easiest way to solve this is with nsis, I created a simple NSIS script for this, I think it's the best solution because it also can be compiled cross platform, as there are nsis compilers for linux.

This looks great, thanks for putting that install script (and icon) together! I can run forward with this to setup an installer for windows. I agree that I need to create a new windows build that doesn't open a console. I'll take a look at using https://github.com/getlantern/systray to provide a separate skew of the binary for windows that both opens a system tray icon to make it visible that it's running (and additionally hides it's console window).

Another great possibility can be Wails. I'm thinking about making a try in the near future

I'd be very interested to hear how that goes. I think Backrest should operate as a daemon irrespective of the GUI but a Wails GUI as a client for the daemon makes sense to me.

garethgeorge commented 1 month ago

Started on integrating the installer and setting up a tray application in https://github.com/garethgeorge/backrest/pull/294/files. Using the tray app to fork backrest with the right options solves the console window problem.

garethgeorge commented 1 month ago

Merged with a working first pass, the solution I landed on is

Tested the x86 binary and working nicely on my windows machine (I don't have an arm64 machine to test the other builds), preview binaries available on the CI/CD runs for the merged run:

ThaUnknown commented 1 month ago

@garethgeorge I noticed there's an issue in the build script i provided, all the icon shortcuts are wrong, instead of "${BUILD_DIR}\icon.ico" they should be "$INSTDIR\icon.ico", I haven't checked if you fixed it in your binaries, didn't have time, sorry

garethgeorge commented 1 month ago

No problem, it was a really good start point & worked with a few edits. I found and fixed that, thanks for calling it out. I did do some debugging / wrangling of the script to make it compatible with the setup the ci/cd pipeline needs.

PovilasID commented 1 month ago

1st I really like this project but to recommend it to mostly windows user there a couple considerations.

  1. Single .exe installer. It can be a web wrapper like suggested that is fine.
  2. No open ports. I would prefer user who do not know much to not have an open port on their device. I get that it is not accessible form the local machine only but I would feel much better if client machine for backup was not exposing the UI to the ethernet protocol at all.
  3. Reasonable defaults. Now in new Plan UI retention defaults are set to 0 and there are no planned backups. I just do not want to give user more opportunities to fail to setup basics.
  4. Launch on startup. This ties in the installer but it should really add it to startup queue, so user do not forget to launch it.

EDIT: Here is MSI (installer file) generating action scrip since you have .exe files for running https://github.com/AliceOh/ExampleUseWindowsInstallerGithubAction In the config it has shortcuts section, so if you add a shortcut to tray.exe to C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp it would launch automatically on every restart.