Files
Loki/internal/ajean/backend_config.go
T
nathaninline 2455d9f402 Purge du code de compatibilite jean, base bbolt et arborescence ajean
- suppression des migrations d'agencement, d'unites et de dossier de donnees
- store bbolt unique (ajean.db) : config, prefs, conversation, cles, drapeaux
- arborescence backends/ bin/ presets/ memory/ models/ workspace/
- plus aucune variable, unite ou asset nomme jean
2026-08-07 16:51:36 +02:00

148 lines
4.6 KiB
Go

package ajean
import (
"bufio"
"crypto/rand"
"encoding/hex"
"fmt"
"net/http"
"os"
"sort"
"strconv"
"strings"
)
// readAPIKey renvoie la clé Bearer de llama-server, ou "" si aucune n'est
// définie. Elle est rangée hors de la configuration pour survivre aux
// changements de preset, qui remplacent la configuration en bloc.
func readAPIKey() string { return getStr(bkState, "api_key") }
// authHeader sets the Authorization: Bearer header on req when an API key is
// configured, so AJEAN's own internal calls (chat/web/bench/test) authenticate
// against a protected llama-server. No-op when no key is set.
func authHeader(req *http.Request) {
if k := readAPIKey(); k != "" {
req.Header.Set("Authorization", "Bearer "+k)
}
}
// genAPIKey returns a fresh random OpenAI-style completion key.
func genAPIKey() string {
buf := make([]byte, 24)
_, _ = rand.Read(buf)
return "sk-ajean-" + hex.EncodeToString(buf)
}
// writeAPIKey enregistre (key != "") ou supprime (key == "") la clé Bearer.
// Ne redémarre PAS le service : llama-server ne lit --api-key qu'au lancement,
// c'est à l'appelant de choisir quand appliquer.
func writeAPIKey(key string) error {
_ = SetConfigKey("API_KEY", "") // aucune ambiguïté avec une valeur résiduelle
return putStr(bkState, "api_key", key)
}
// maskAPIKey renders a key for display: keep the "sk-ajean-" prefix and last 4
// chars, elide the middle. Empty in → empty out.
func maskAPIKey(k string) string {
if k == "" {
return ""
}
if len(k) <= 13 {
return "…" + k[len(k)-2:]
}
return k[:9] + "…" + k[len(k)-4:]
}
// cmdSetAPIKey définit (ou supprime) la clé Bearer de llama-server. Exposé sur
// internet, le serveur exige alors « Authorization: Bearer <clé> » à chaque
// appel.
//
// ajean set-api-key <clé> définit la clé
// ajean set-api-key génère une clé aléatoire
// ajean set-api-key "" supprime la protection
func cmdSetAPIKey(args []string) error {
var key string
switch {
case len(args) == 0:
key = genAPIKey()
fmt.Printf("%s clé générée : %s\n", green("[ok]"), bold(key))
case args[0] == "" || args[0] == "off" || args[0] == "none":
key = ""
default:
key = strings.TrimSpace(args[0])
}
if err := writeAPIKey(key); err != nil {
return err
}
if key == "" {
fmt.Printf("%s API_KEY supprimée — serveur ouvert (pas d'authentification)\n", yellow("[info]"))
} else {
fmt.Printf("%s API_KEY enregistrée\n", green("[ok]"))
fmt.Printf(" les clients doivent envoyer : %s\n", dim("Authorization: Bearer "+key))
}
fmt.Print(dim("[info] redémarrer le service pour appliquer ? [Y/n] "))
sc := bufio.NewScanner(os.Stdin)
if sc.Scan() && strings.HasPrefix(strings.ToLower(strings.TrimSpace(sc.Text())), "n") {
fmt.Println(dim("[info] pense à lancer 'ajean restart'"))
return nil
}
return serviceAction("restart")
}
// ReadConfig renvoie la configuration active de llama-server.
func ReadConfig() map[string]string { return allKV(bkConfig) }
// SetConfigKey définit une clé de configuration. Une valeur vide la supprime.
func SetConfigKey(key, value string) error { return putStr(bkConfig, key, value) }
// WriteConfig remplace TOUTE la configuration en une transaction. C'est ce
// qu'exige l'application d'un preset : jamais un état mi-ancien mi-nouveau.
func WriteConfig(m map[string]string) error { return replaceKV(bkConfig, m) }
// parseEnv lit un fichier au format clé=valeur (les presets). Les lignes vides
// et les commentaires sont ignorés, les guillemets retirés.
func parseEnv(text string) map[string]string {
m := map[string]string{}
for _, line := range strings.Split(text, "\n") {
s := strings.TrimSpace(line)
if s == "" || strings.HasPrefix(s, "#") {
continue
}
k, v, ok := strings.Cut(strings.TrimPrefix(s, "export "), "=")
if !ok {
continue
}
m[strings.TrimSpace(k)] = strings.Trim(strings.TrimSpace(v), "\"")
}
return m
}
// formatEnv rend une configuration au format des presets, clés triées pour que
// deux écritures du même contenu donnent le même fichier.
func formatEnv(m map[string]string) string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
var b strings.Builder
for _, k := range keys {
fmt.Fprintf(&b, "%s=%s\n", k, m[k])
}
return b.String()
}
// Le plafond d'appels d'outils par tour (TOOL_LIMIT) et l'anti-boucle ont été
// retirés : ils coupaient surtout des tours légitimes. Le bouton stop est le
// seul frein.
// LLMPort renvoie le port du serveur (clé PORT), 8080 par défaut.
func LLMPort() int {
if p, ok := ReadConfig()["PORT"]; ok {
if n, err := strconv.Atoi(p); err == nil && n > 0 {
return n
}
}
return 8080
}