diff options
| -rw-r--r-- | internal/cli/serve.go | 78 | ||||
| -rw-r--r-- | internal/storage/db.go | 20 |
2 files changed, 36 insertions, 62 deletions
diff --git a/internal/cli/serve.go b/internal/cli/serve.go index 1753a64..efac719 100644 --- a/internal/cli/serve.go +++ b/internal/cli/serve.go @@ -1,7 +1,6 @@ package cli import ( - "bytes" "context" "fmt" "net/http" @@ -11,7 +10,6 @@ import ( "syscall" "time" - "github.com/BurntSushi/toml" "github.com/thepeterstone/claudomator/internal/api" "github.com/thepeterstone/claudomator/internal/executor" "github.com/thepeterstone/claudomator/internal/notify" @@ -46,29 +44,28 @@ func serve(addr string) error { return fmt.Errorf("creating dirs: %w", err) } - // Auto-generate VAPID keys if not configured. - if cfg.VAPIDPublicKey == "" || cfg.VAPIDPrivateKey == "" { - pub, priv, err := notify.GenerateVAPIDKeys() - if err != nil { - return fmt.Errorf("generating VAPID keys: %w", err) - } - cfg.VAPIDPublicKey = pub - cfg.VAPIDPrivateKey = priv - // Write new keys back to config file. - if cfgFile != "" { - if err := saveVAPIDToConfig(cfgFile, pub, priv); err != nil { - // Non-fatal: log but continue. - fmt.Fprintf(os.Stderr, "warning: failed to persist VAPID keys to %s: %v\n", cfgFile, err) - } - } - } - store, err := storage.Open(cfg.DBPath) if err != nil { return fmt.Errorf("opening db: %w", err) } defer store.Close() + // Load VAPID keys from DB; generate and persist if missing. + if cfg.VAPIDPublicKey == "" || cfg.VAPIDPrivateKey == "" { + pub, _ := store.GetSetting("vapid_public_key") + priv, _ := store.GetSetting("vapid_private_key") + if pub == "" || priv == "" { + pub, priv, err = notify.GenerateVAPIDKeys() + if err != nil { + return fmt.Errorf("generating VAPID keys: %w", err) + } + _ = store.SetSetting("vapid_public_key", pub) + _ = store.SetSetting("vapid_private_key", priv) + } + cfg.VAPIDPublicKey = pub + cfg.VAPIDPrivateKey = priv + } + logger := newLogger(verbose) apiURL := "http://localhost" + addr @@ -164,46 +161,3 @@ func serve(addr string) error { return nil } -// saveVAPIDToConfig appends VAPID key assignments to the config file. -// It reads the existing file (if any), then writes a complete TOML file with -// the new keys merged in. Uses toml encoder for correctness. -func saveVAPIDToConfig(path, pub, priv string) error { - existing := cfg // already loaded - - // Marshal the full config back including the new VAPID keys. - // We use a struct alias to only encode fields we want persisted. - type persistedConfig struct { - DataDir string `toml:"data_dir,omitempty"` - ClaudeBinaryPath string `toml:"claude_binary_path,omitempty"` - GeminiBinaryPath string `toml:"gemini_binary_path,omitempty"` - MaxConcurrent int `toml:"max_concurrent,omitempty"` - DefaultTimeout string `toml:"default_timeout,omitempty"` - ServerAddr string `toml:"server_addr,omitempty"` - WebhookURL string `toml:"webhook_url,omitempty"` - WorkspaceRoot string `toml:"workspace_root,omitempty"` - WebhookSecret string `toml:"webhook_secret,omitempty"` - VAPIDPublicKey string `toml:"vapid_public_key,omitempty"` - VAPIDPrivateKey string `toml:"vapid_private_key,omitempty"` - VAPIDEmail string `toml:"vapid_email,omitempty"` - } - pc := persistedConfig{ - DataDir: existing.DataDir, - ClaudeBinaryPath: existing.ClaudeBinaryPath, - GeminiBinaryPath: existing.GeminiBinaryPath, - MaxConcurrent: existing.MaxConcurrent, - DefaultTimeout: existing.DefaultTimeout, - ServerAddr: existing.ServerAddr, - WebhookURL: existing.WebhookURL, - WorkspaceRoot: existing.WorkspaceRoot, - WebhookSecret: existing.WebhookSecret, - VAPIDPublicKey: pub, - VAPIDPrivateKey: priv, - VAPIDEmail: existing.VAPIDEmail, - } - - var buf bytes.Buffer - if err := toml.NewEncoder(&buf).Encode(pc); err != nil { - return fmt.Errorf("encoding config: %w", err) - } - return os.WriteFile(path, buf.Bytes(), 0600) -} diff --git a/internal/storage/db.go b/internal/storage/db.go index 51121e1..25801b2 100644 --- a/internal/storage/db.go +++ b/internal/storage/db.go @@ -94,6 +94,10 @@ func (s *DB) migrate() error { auth_key TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )`, + `CREATE TABLE IF NOT EXISTS settings ( + key TEXT PRIMARY KEY, + value TEXT NOT NULL + )`, } for _, m := range migrations { if _, err := s.db.Exec(m); err != nil { @@ -835,3 +839,19 @@ func (s *DB) ListPushSubscriptions() ([]PushSubscription, error) { } return subs, rows.Err() } + +// GetSetting returns the value for a key, or ("", nil) if not found. +func (s *DB) GetSetting(key string) (string, error) { + var value string + err := s.db.QueryRow(`SELECT value FROM settings WHERE key = ?`, key).Scan(&value) + if err == sql.ErrNoRows { + return "", nil + } + return value, err +} + +// SetSetting upserts a key/value pair in the settings table. +func (s *DB) SetSetting(key, value string) error { + _, err := s.db.Exec(`INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`, key, value) + return err +} |
