mirror of
https://github.com/Warky-Devs/vecna.git
synced 2026-05-05 01:26:58 +00:00
138 lines
3.7 KiB
Go
138 lines
3.7 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// SaveTarget adds or replaces a named ForwardTarget in the config file at path.
|
|
// Only JSON config files are written in-place. For other formats an error is
|
|
// returned describing what to add manually.
|
|
func SaveTarget(path, name string, target ForwardTarget) error {
|
|
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(path), "."))
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return fmt.Errorf("read config %s: %w", path, err)
|
|
}
|
|
|
|
switch ext {
|
|
case "json":
|
|
return saveTargetJSON(path, data, name, target)
|
|
default:
|
|
snippet, _ := json.MarshalIndent(map[string]ForwardTarget{name: target}, "", " ")
|
|
return fmt.Errorf(
|
|
"auto-update not supported for .%s files\n"+
|
|
"Add the following to the 'forward.targets' section of %s:\n\n%s",
|
|
ext, path, snippet,
|
|
)
|
|
}
|
|
}
|
|
|
|
// RemoveBrokenEndpoints removes failing endpoints from the config file.
|
|
// broken maps target name → set of failing endpoint URLs.
|
|
// If all endpoints of a target are removed, the target itself is deleted.
|
|
// Returns the list of removed items as human-readable strings.
|
|
func RemoveBrokenEndpoints(path string, broken map[string][]string) ([]string, error) {
|
|
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(path), "."))
|
|
if ext != "json" {
|
|
return nil, fmt.Errorf("auto-update not supported for .%s files; edit %s manually", ext, path)
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read config %s: %w", path, err)
|
|
}
|
|
|
|
var cfg Config
|
|
if err := json.Unmarshal(data, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parse %s: %w", path, err)
|
|
}
|
|
|
|
var removed []string
|
|
|
|
for targetName, failedURLs := range broken {
|
|
target, ok := cfg.Forward.Targets[targetName]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
failSet := make(map[string]bool, len(failedURLs))
|
|
for _, u := range failedURLs {
|
|
failSet[u] = true
|
|
}
|
|
|
|
kept := target.Endpoints[:0]
|
|
for _, ep := range target.Endpoints {
|
|
if failSet[ep.URL] {
|
|
removed = append(removed, fmt.Sprintf("endpoint %s (target %q)", ep.URL, targetName))
|
|
} else {
|
|
kept = append(kept, ep)
|
|
}
|
|
}
|
|
|
|
if len(kept) == 0 {
|
|
delete(cfg.Forward.Targets, targetName)
|
|
removed = append(removed, fmt.Sprintf("target %q (all endpoints failed)", targetName))
|
|
if cfg.Forward.Default == targetName {
|
|
cfg.Forward.Default = ""
|
|
}
|
|
} else {
|
|
target.Endpoints = kept
|
|
cfg.Forward.Targets[targetName] = target
|
|
}
|
|
}
|
|
|
|
if len(removed) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
out, err := json.MarshalIndent(cfg, "", " ")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal config: %w", err)
|
|
}
|
|
if err := os.WriteFile(path, append(out, '\n'), 0o600); err != nil {
|
|
return nil, fmt.Errorf("write %s: %w", path, err)
|
|
}
|
|
return removed, nil
|
|
}
|
|
|
|
// WriteConfig serialises cfg as indented JSON and atomically overwrites path.
|
|
func WriteConfig(path string, cfg Config) error {
|
|
out, err := json.MarshalIndent(cfg, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("marshal config: %w", err)
|
|
}
|
|
if err := os.WriteFile(path, append(out, '\n'), 0o600); err != nil {
|
|
return fmt.Errorf("write %s: %w", path, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func saveTargetJSON(path string, data []byte, name string, target ForwardTarget) error {
|
|
var cfg Config
|
|
if err := json.Unmarshal(data, &cfg); err != nil {
|
|
return fmt.Errorf("parse %s: %w", path, err)
|
|
}
|
|
|
|
if cfg.Forward.Targets == nil {
|
|
cfg.Forward.Targets = make(map[string]ForwardTarget)
|
|
}
|
|
cfg.Forward.Targets[name] = target
|
|
if cfg.Forward.Default == "" {
|
|
cfg.Forward.Default = name
|
|
}
|
|
|
|
out, err := json.MarshalIndent(cfg, "", " ")
|
|
if err != nil {
|
|
return fmt.Errorf("marshal config: %w", err)
|
|
}
|
|
if err := os.WriteFile(path, append(out, '\n'), 0o600); err != nil {
|
|
return fmt.Errorf("write %s: %w", path, err)
|
|
}
|
|
return nil
|
|
}
|