feat: migrate desktop app from Electron to Wails v3#6
Open
gustavosbarreto wants to merge 3 commits into
Open
Conversation
Replace the Electron shell with a Wails v3 (Go backend + Vue 3/Vuetify frontend) app, producing a single self-contained binary that uses the OS native webview instead of bundling Chromium. Core architecture (Wails has no nested <webview>): - internal/proxy: dynamic-target reverse proxy on localhost that strips X-Frame-Options/CSP and proxies the SSH-terminal websocket, so the active instance's web UI can be embedded in an <iframe>. - internal/services: InstanceService (server-side /info validation, instance switching) and SSHService (ssh-agent-style ListPublicKeys/Sign; private keys never leave Go, signing gated by a native consent dialog). - frontend/src/services/bridge.ts: postMessage relay so the embedded (cross -origin) web UI can invoke whitelisted native capabilities. Protocol in docs/native-bridge.md. Frontend: moved src/renderer -> frontend, replaced <webview> with an iframe fed by the proxy, window controls/drag via @wailsio/runtime, /info validation via Go bindings. Removed Electron main/preload, electron-vite/builder configs, dead template assets and unused Login/router code. Packaging: Wails build scaffolding with ShellHub branding (io.shellhub.app); CI reworked to build AppImage/.deb/.rpm (Linux), NSIS (Windows), .app (macOS). See docs/packaging.md for the GTK4/GTK3 + AppImage libcroco caveats.
The SSHService (ListPublicKeys/Sign + consent dialog) and the wired postMessage relay were only a proof-of-concept that the native bridge is feasible. The real ShellHub web UI does not call anything in the desktop shell yet, so: - delete internal/services/ssh.go and its SSHService registration in main.go - unwire the bridge from AppLayout (no installNativeBridge call) - keep frontend/src/services/bridge.ts as a documented example/template (not installed) for when native capabilities are needed later - trim docs/native-bridge.md to reflect the dormant state; SSH signing stays as a worked example of a sensitive, consent-gated capability - regenerate bindings (InstanceService only) InstanceService (server-side /info validation + proxy target switching) is unchanged and remains the only Go service the chrome calls.
- Per-instance reverse proxies (proxy.Manager): each instance gets its own loopback port/origin, so cookies/storage are isolated per instance and a proxy's target never changes (in-flight requests can't be routed to the wrong instance after a switch). Replaces the single mutable-target proxy. - Validate instance URLs in Ensure (require http/https + non-empty host) instead of letting a schemeless URL fail with an opaque 502. - Strip the Domain attribute from Set-Cookie so instance session cookies bind to the loopback origin and persist (were dropped on domain mismatch). - Inject a script into proxied HTML that routes target=_blank / window.open to a *different* origin through the chrome, which opens them in the system browser (Browser.OpenURL, http/https only) — Wails has no new-window hook, so external links otherwise failed or navigated the iframe away. - Frontend: tear the old iframe document down (about:blank) before swapping instance; back the loading spinner with a 20s timeout + @error so it can't stick on; validate instances in parallel instead of serially; handle SetActiveInstance errors; guard removeInstance index. - Remove dead ProxyURL binding; add proxy unit tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Migrates ShellHub Desktop from Electron to Wails v3 (Go backend + Vue 3/Vuetify frontend),
producing a single self-contained binary that uses the OS-native webview instead of bundling Chromium
(~18 MB vs ~150 MB).
Why
Lighter runtime, native webview, and a Go backend that unifies the stack with the rest of ShellHub
and unlocks native capabilities (local SSH keys, and later an in-process agent).
How it works
Wails has no nested
<webview>, so the active instance's web UI is embedded in an<iframe>fed by asmall Go reverse proxy:
internal/proxy— dynamic-target reverse proxy onlocalhostthat stripsX-Frame-Options/CSP andproxies the SSH-terminal websocket (which the Wails asset server can't do on Linux).
internal/services:InstanceService— server-side/infovalidation (no CORS) + instance switching.SSHService— ssh-agent-styleListPublicKeys/Sign; private keys never leave Go, signinggated by a native consent dialog.
frontend/src/services/bridge.ts—postMessagerelay so the embedded (cross-origin) web UI caninvoke whitelisted native capabilities. Protocol in
docs/native-bridge.md.Frontend: moved
src/renderer→frontend,<webview>→ iframe (proxy-fed), window controls/dragvia
@wailsio/runtime,/infovalidation via Go bindings. Removed Electron main/preload,electron-vite/electron-builder configs, dead template assets, and unused Login/router code.
Packaging / CI
wails3 task package) on native runners..deb+.rpm; Windows: NSIS; macOS:.app.nfpm.yaml).libcroco; the CI installs it onthe build host so it gets bundled into the AppImage (stays self-contained). See
docs/packaging.md.Test plan
wails3 build→bin/shellhub; app launches and loads cloud.shellhub.io in the iframego vet,go buildall green.debbuilds via nfpm with correct branding/deps.app) jobs — couldn't run GitHubActions locally
Follow-ups (out of scope)
package main, not importable). Wired later viago.work.