diff --git a/internal/app/global_proxy.go b/internal/app/global_proxy.go index 016fb26..ffb5b35 100644 --- a/internal/app/global_proxy.go +++ b/internal/app/global_proxy.go @@ -123,11 +123,26 @@ func proxyConfigEqual(a, b connection.ProxyConfig) bool { a.Password == b.Password } +func currentGlobalProxyView() connection.GlobalProxyView { + snapshot := currentGlobalProxyConfig() + if !snapshot.Enabled { + return connection.GlobalProxyView{Enabled: false} + } + return connection.GlobalProxyView{ + Enabled: true, + Type: snapshot.Proxy.Type, + Host: snapshot.Proxy.Host, + Port: snapshot.Proxy.Port, + User: snapshot.Proxy.User, + HasPassword: strings.TrimSpace(snapshot.Proxy.Password) != "", + } +} + func (a *App) GetGlobalProxyConfig() connection.QueryResult { return connection.QueryResult{ Success: true, Message: "OK", - Data: currentGlobalProxyConfig(), + Data: currentGlobalProxyView(), } } @@ -312,3 +327,4 @@ func buildProxyURLFromConfig(proxyConfig connection.ProxyConfig) (*url.URL, erro } return proxyURL, nil } + diff --git a/internal/app/methods_saved_connections.go b/internal/app/methods_saved_connections.go new file mode 100644 index 0000000..d8d916d --- /dev/null +++ b/internal/app/methods_saved_connections.go @@ -0,0 +1,44 @@ +package app + +import "GoNavi-Wails/internal/connection" + +func (a *App) savedConnectionRepository() *savedConnectionRepository { + return newSavedConnectionRepository(a.configDir, a.secretStore) +} + +func (a *App) GetSavedConnections() ([]connection.SavedConnectionView, error) { + return a.savedConnectionRepository().List() +} + +func (a *App) SaveConnection(input connection.SavedConnectionInput) (connection.SavedConnectionView, error) { + return a.savedConnectionRepository().Save(input) +} + +func (a *App) DeleteConnection(id string) error { + return a.savedConnectionRepository().Delete(id) +} + +func (a *App) DuplicateConnection(id string) (connection.SavedConnectionView, error) { + return a.savedConnectionRepository().Duplicate(id) +} + +func (a *App) ImportLegacyConnections(items []connection.LegacySavedConnection) ([]connection.SavedConnectionView, error) { + result := make([]connection.SavedConnectionView, 0, len(items)) + repo := a.savedConnectionRepository() + for _, item := range items { + view, err := repo.Save(connection.SavedConnectionInput(item)) + if err != nil { + return nil, err + } + result = append(result, view) + } + return result, nil +} + +func (a *App) SaveGlobalProxy(input connection.SaveGlobalProxyInput) (connection.GlobalProxyView, error) { + return a.saveGlobalProxy(input) +} + +func (a *App) ImportLegacyGlobalProxy(input connection.LegacyGlobalProxyInput) (connection.GlobalProxyView, error) { + return a.saveGlobalProxy(connection.SaveGlobalProxyInput(input)) +} diff --git a/internal/app/methods_saved_connections_test.go b/internal/app/methods_saved_connections_test.go new file mode 100644 index 0000000..4b117cc --- /dev/null +++ b/internal/app/methods_saved_connections_test.go @@ -0,0 +1,94 @@ +package app + +import ( + "testing" + + "GoNavi-Wails/internal/connection" +) + +func TestSaveConnectionMethodReturnsSecretlessView(t *testing.T) { + app := NewAppWithSecretStore(newFakeAppSecretStore()) + app.configDir = t.TempDir() + + result, err := app.SaveConnection(connection.SavedConnectionInput{ + ID: "conn-1", + Name: "Primary", + Config: connection.ConnectionConfig{ + ID: "conn-1", + Type: "postgres", + Host: "db.local", + Port: 5432, + User: "postgres", + Password: "postgres-secret", + }, + }) + if err != nil { + t.Fatal(err) + } + if result.Config.Password != "" { + t.Fatal("SaveConnection must not return plaintext password") + } + if !result.HasPrimaryPassword { + t.Fatal("expected HasPrimaryPassword=true") + } +} + +func TestDuplicateConnectionClonesSecretBundle(t *testing.T) { + app := NewAppWithSecretStore(newFakeAppSecretStore()) + app.configDir = t.TempDir() + + _, err := app.SaveConnection(connection.SavedConnectionInput{ + ID: "conn-1", + Name: "Primary", + Config: connection.ConnectionConfig{ + ID: "conn-1", + Type: "postgres", + Host: "db.local", + Port: 5432, + User: "postgres", + Password: "postgres-secret", + }, + }) + if err != nil { + t.Fatal(err) + } + + duplicate, err := app.DuplicateConnection("conn-1") + if err != nil { + t.Fatal(err) + } + if duplicate.ID == "conn-1" { + t.Fatal("duplicate should have a new id") + } + + resolved, err := app.resolveConnectionSecrets(duplicate.Config) + if err != nil { + t.Fatal(err) + } + if resolved.Password != "postgres-secret" { + t.Fatalf("expected duplicated secret bundle, got %q", resolved.Password) + } +} + +func TestSaveGlobalProxyReturnsSecretlessView(t *testing.T) { + app := NewAppWithSecretStore(newFakeAppSecretStore()) + app.configDir = t.TempDir() + + view, err := app.SaveGlobalProxy(connection.SaveGlobalProxyInput{ + Enabled: true, + Type: "http", + Host: "127.0.0.1", + Port: 8080, + User: "ops", + Password: "proxy-secret", + }) + if err != nil { + t.Fatal(err) + } + if view.Password != "" { + t.Fatal("global proxy view must not expose plaintext password") + } + if !view.HasPassword { + t.Fatal("expected hasPassword=true") + } +}