Add files via upload

This commit is contained in:
nathaninline authored and GitHub committed 2026-06-08 12:54:49 +02:00
1 parent 5d35ff0484
commit 6802962120
20 files changed
+3186 -1

No files matched your search

+180
View File
@@ -0,0 +1,180 @@
package main
import (
"bufio"
"crypto/sha1"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
"time"
)
type Preset struct {
Name string
Path string
Active bool
}
// ListPresets returns all configs/*.env files sorted by name, marking the one
// whose contents match the current config.env (by SHA-1).
func ListPresets() ([]Preset, error) {
dir := presetsDir()
_ = os.MkdirAll(dir, 0o755)
cur := ""
if b, err := os.ReadFile(confPath()); err == nil {
h := sha1.Sum(b)
cur = hex.EncodeToString(h[:])
}
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
out := []Preset{}
for _, e := range entries {
if e.IsDir() || !strings.HasSuffix(e.Name(), ".env") {
continue
}
p := filepath.Join(dir, e.Name())
b, err := os.ReadFile(p)
if err != nil {
continue
}
h := sha1.Sum(b)
out = append(out, Preset{
Name: strings.TrimSuffix(e.Name(), ".env"),
Path: p,
Active: hex.EncodeToString(h[:]) == cur,
})
}
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
return out, nil
}
var presetNameRe = regexp.MustCompile(`^[A-Za-z0-9._-]+$`)
// safePresetPath validates name and returns its resolved path inside presetsDir.
func safePresetPath(name string) (string, error) {
if !presetNameRe.MatchString(name) {
return "", fmt.Errorf("nom invalide (alphanum, ._-)")
}
root, err := filepath.Abs(presetsDir())
if err != nil {
return "", err
}
p := filepath.Join(root, name+".env")
abs, err := filepath.Abs(p)
if err != nil {
return "", err
}
if !strings.HasPrefix(abs, root+string(filepath.Separator)) {
return "", fmt.Errorf("path invalide")
}
return abs, nil
}
// SwitchToPreset backs up the current config and copies the target into place,
// then restarts the service.
func SwitchToPreset(target string) error {
src, err := os.ReadFile(target)
if err != nil {
return err
}
ts := time.Now().Format("20060102-150405")
if cur, err := os.ReadFile(confPath()); err == nil {
_ = os.WriteFile(confPath()+".bak.switch-"+ts, cur, 0o644)
}
if err := os.WriteFile(confPath(), src, 0o644); err != nil {
return err
}
fmt.Printf("%s config.env <- %s (backup .bak.switch-%s)\n", green("[ok]"), filepath.Base(target), ts)
fmt.Println(dim("[info] redémarrage du service..."))
return serviceAction("restart")
}
func cmdSwitch(args []string) error {
list, err := ListPresets()
if err != nil {
return err
}
if len(list) == 0 {
return fmt.Errorf("aucun preset dans %s", presetsDir())
}
fmt.Printf("\n %s (%s)\n\n", cyan("Presets disponibles"), presetsDir())
for i, p := range list {
mark := " "
if p.Active {
mark = green("●") + " actif"
}
fmt.Printf(" %2d) %-30s %s\n", i+1, p.Name, mark)
}
fmt.Println()
choice := ""
if len(args) > 0 {
choice = args[0]
} else {
fmt.Print("Numéro à activer (vide = annuler) : ")
sc := bufio.NewScanner(os.Stdin)
if sc.Scan() {
choice = strings.TrimSpace(sc.Text())
}
}
if choice == "" {
fmt.Println(dim("[info] annulé"))
return nil
}
n, err := strconv.Atoi(choice)
if err != nil || n < 1 || n > len(list) {
return fmt.Errorf("choix invalide")
}
return SwitchToPreset(list[n-1].Path)
}
// SavePreset creates or overwrites a preset, handling rename when old != "".
func SavePreset(name, old, content string) error {
if old != "" && old != name {
if of, err := safePresetPath(old); err == nil {
_ = os.Remove(of)
}
}
p, err := safePresetPath(name)
if err != nil {
return err
}
_ = os.MkdirAll(filepath.Dir(p), 0o755)
return os.WriteFile(p, []byte(content), 0o644)
}
// DeletePreset removes a preset; refuses if it is the active config.
func DeletePreset(name string) error {
p, err := safePresetPath(name)
if err != nil {
return err
}
cur, _ := os.ReadFile(confPath())
target, err := os.ReadFile(p)
if err != nil {
return fmt.Errorf("introuvable")
}
if sha1.Sum(cur) == sha1.Sum(target) {
return fmt.Errorf("preset actif, switche d'abord")
}
return os.Remove(p)
}
// ReadPreset returns the contents of a preset by name.
func ReadPreset(name string) (string, error) {
p, err := safePresetPath(name)
if err != nil {
return "", err
}
b, err := os.ReadFile(p)
if err != nil {
return "", err
}
return string(b), nil
}