From 8c902876e7935af3b7b06c7b8f6c252a91749638 Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Tue, 17 Mar 2026 07:04:15 -0400 Subject: [PATCH] feat: wire SSH, SFTP, and credential services into Wails app Add SSHService, SFTPService, and CredentialService to the WraithApp struct. SSH service uses a no-op output handler (Wails event emission will be wired at runtime). CredentialService is created lazily after vault unlock. Both SSH and SFTP services are registered with Wails in main.go. Co-Authored-By: Claude Opus 4.6 (1M context) --- internal/app/app.go | 28 ++++++++++++++++++++++++++++ main.go | 2 ++ 2 files changed, 30 insertions(+) diff --git a/internal/app/app.go b/internal/app/app.go index aac1439..8b4e183 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -9,10 +9,13 @@ import ( "path/filepath" "github.com/vstockwell/wraith/internal/connections" + "github.com/vstockwell/wraith/internal/credentials" "github.com/vstockwell/wraith/internal/db" "github.com/vstockwell/wraith/internal/plugin" "github.com/vstockwell/wraith/internal/session" "github.com/vstockwell/wraith/internal/settings" + "github.com/vstockwell/wraith/internal/sftp" + "github.com/vstockwell/wraith/internal/ssh" "github.com/vstockwell/wraith/internal/theme" "github.com/vstockwell/wraith/internal/vault" ) @@ -27,6 +30,9 @@ type WraithApp struct { Themes *theme.ThemeService Sessions *session.Manager Plugins *plugin.Registry + SSH *ssh.SSHService + SFTP *sftp.SFTPService + Credentials *credentials.CredentialService unlocked bool } @@ -52,6 +58,17 @@ func New() (*WraithApp, error) { sessionMgr := session.NewManager() pluginReg := plugin.NewRegistry() + // No-op output handler — Wails event emission will be wired at runtime. + sshSvc := ssh.NewSSHService(database, func(sessionID string, data []byte) { + // TODO: Emit Wails event "ssh:output" with sessionID + data + _ = sessionID + _ = data + }) + sftpSvc := sftp.NewSFTPService() + + // CredentialService requires the vault to be unlocked, so it starts nil. + // It is created lazily after the vault is unlocked via initCredentials(). + // Seed built-in themes on every startup (INSERT OR IGNORE keeps it idempotent) if err := themeSvc.SeedBuiltins(); err != nil { slog.Warn("failed to seed themes", "error", err) @@ -64,6 +81,8 @@ func New() (*WraithApp, error) { Themes: themeSvc, Sessions: sessionMgr, Plugins: pluginReg, + SSH: sshSvc, + SFTP: sftpSvc, }, nil } @@ -121,6 +140,7 @@ func (a *WraithApp) CreateVault(password string) error { } a.unlocked = true + a.initCredentials() slog.Info("vault created successfully") return nil } @@ -157,6 +177,7 @@ func (a *WraithApp) Unlock(password string) error { a.Vault = vs a.unlocked = true + a.initCredentials() slog.Info("vault unlocked successfully") return nil } @@ -165,3 +186,10 @@ func (a *WraithApp) Unlock(password string) error { func (a *WraithApp) IsUnlocked() bool { return a.unlocked } + +// initCredentials creates the CredentialService after the vault is unlocked. +func (a *WraithApp) initCredentials() { + if a.Vault != nil { + a.Credentials = credentials.NewCredentialService(a.db, a.Vault) + } +} diff --git a/main.go b/main.go index 9e46232..f81e181 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,8 @@ func main() { application.NewService(wraith.Connections), application.NewService(wraith.Themes), application.NewService(wraith.Settings), + application.NewService(wraith.SSH), + application.NewService(wraith.SFTP), }, Assets: application.AssetOptions{ Handler: application.BundledAssetFileServer(assets),