mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Add files via upload
This commit is contained in:
1 parent
5d35ff0484
commit
6802962120
20 files changed
+3186
-1
No files matched your search
+180
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user