From 610ec13b19cae39a7a91b06d9010ab36ecc02930 Mon Sep 17 00:00:00 2001 From: Poulpe Date: Tue, 28 Apr 2026 22:08:22 +0000 Subject: [PATCH] feat(v0.2.0): config UI panel + folder picker + S3 bucket lister/tester - Panel Reglages dans l'UI (modal) - Folder picker natif Wails pour dossier local - ListS3Buckets via aws-cli (profile-aware) - TestS3Access pour verifier acces bucket - Config persistee user config dir (cross-OS): %APPDATA%/field-sync ou ~/.config/field-sync - Header affiche bucket + dest visibles - Auto-open modal si bucket vide au premier run Co-Authored-By: Claude Sonnet 4.6 --- app.go | 59 ++++++++++- frontend/src/App.svelte | 209 +++++++++++++++++++++++++++++++++++--- internal/config/config.go | 49 ++++++++- 3 files changed, 302 insertions(+), 15 deletions(-) diff --git a/app.go b/app.go index d0bfcfb..284fb50 100644 --- a/app.go +++ b/app.go @@ -3,6 +3,8 @@ package main import ( "context" "fmt" + "os/exec" + "strings" "os" "path/filepath" "sync" @@ -66,12 +68,67 @@ func NewApp() *App { // startup is called by Wails when the app starts func (a *App) startup(ctx context.Context) { a.ctx = ctx - a.cfg = config.Load(".env") + a.cfg = config.LoadFromFile() for i, letter := range []string{"A", "B", "C", "D"} { a.slots[i] = SlotState{Slot: letter, Status: "idle"} } } +// SaveConfig persists config to user config dir and updates in-memory config +func (a *App) SaveConfig(cfg config.Config) error { + if err := config.Save(cfg); err != nil { + return err + } + a.mu.Lock() + a.cfg = cfg + a.mu.Unlock() + return nil +} + +// GetConfigPath returns where the config is stored (for display) +func (a *App) GetConfigPath() string { + return config.Path() +} + +// PickLocalDest opens native folder picker, returns selected path or "" if cancelled +func (a *App) PickLocalDest() (string, error) { + path, err := runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{ + Title: "Dossier de destination locale", + }) + return path, err +} + +// ListS3Buckets runs `aws s3api list-buckets --profile

` returns []string +func (a *App) ListS3Buckets(profile string) ([]string, error) { + args := []string{"s3api", "list-buckets", "--query", "Buckets[].Name", "--output", "text"} + if profile != "" { + args = append(args, "--profile", profile) + } + out, err := exec.Command("aws", args...).CombinedOutput() + if err != nil { + return nil, fmt.Errorf("aws error: %s", strings.TrimSpace(string(out))) + } + raw := strings.TrimSpace(string(out)) + if raw == "" || raw == "None" { + return []string{}, nil + } + parts := strings.Fields(raw) + return parts, nil +} + +// TestS3Access checks bucket reachable: `aws s3 ls s3:/// --profile

` +func (a *App) TestS3Access(bucket, profile string) error { + args := []string{"s3", "ls", "s3://" + bucket + "/"} + if profile != "" { + args = append(args, "--profile", profile) + } + out, err := exec.Command("aws", args...).CombinedOutput() + if err != nil { + return fmt.Errorf("accès refusé: %s", strings.TrimSpace(string(out))) + } + return nil +} + // ScanCards returns up to 4 detected SD cards func (a *App) ScanCards() []detect.SDCard { cards := detect.Detect() diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 29904bf..ce38ed9 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -1,12 +1,25 @@