Files
llama-link/internal/config/config.go
T
darroyo 4d34c6d31a
CI / test (push) Failing after 12m45s
Replace token-based admin auth with JWT session authentication
- Add AdminUser model (bcrypt hashed passwords) and admin_users table
- Add AdminJWTService for HS256 JWT sessions (24h TTL)
- Add AdminSessionAuth middleware for /api/v1/admin/* routes
- Add admin handlers: login, logout, me, change-password, users CRUD
- Keys and model management routes now require admin JWT session
- Remove ADMIN_TOKEN, add ADMIN_USERNAME, ADMIN_PASSWORD env vars
- Update frontend: username/password login, admin_session storage, AdminUsers CRUD view
2026-07-31 17:15:29 -04:00

150 lines
4.0 KiB
Go

package config
import (
"log/slog"
"os"
"strconv"
"time"
)
type Config struct {
// Server
LlamalinkEnv string
LlamalinkHost string
LlamalinkPort int
// Database
DatabaseURL string
DatabaseMaxOpenConns int
DatabaseMaxIdleConns int
DatabaseConnMaxLifetime int // seconds
// Llama server management
ManageLlamaServer bool
LlamaServerBin string
LlamaServerHost string
LlamaServerPort int
LlamaServerStartupTimeout int // seconds
LlamaServerStopTimeout int // seconds
ModelSwapCooldown int // seconds
// Rate limiting
RateLimitPerMinute int
RateLimitStorage string // "memory" or "redis"
// Auth
AdminUsername string
AdminPassword string
JWTSecret []byte
AdminSessionTTL int // seconds
// Logging
LogLevel string
LogFormat string // "json" or "text"
}
func Load() *Config {
c := &Config{
LlamalinkEnv: getEnv("LLAMALINK_ENV", "development"),
LlamalinkHost: getEnv("LLAMALINK_HOST", "0.0.0.0"),
LlamalinkPort: intEnv("LLAMALINK_PORT", 8000),
DatabaseURL: getEnv("DATABASE_URL", "sqlite:///./llamalink.db"),
DatabaseMaxOpenConns: intEnv("DATABASE_MAX_OPEN_CONNS", 25),
DatabaseMaxIdleConns: intEnv("DATABASE_MAX_IDLE_CONNS", 5),
DatabaseConnMaxLifetime: intEnv("DATABASE_CONN_MAX_LIFETIME", 300),
ManageLlamaServer: boolEnv("MANAGE_LLAMA_SERVER", true),
LlamaServerBin: getEnv("LLAMA_SERVER_BIN", "/usr/local/bin/llama-server"),
LlamaServerHost: getEnv("LLAMA_SERVER_HOST", "127.0.0.1"),
LlamaServerPort: intEnv("LLAMA_SERVER_PORT", 8080),
LlamaServerStartupTimeout: intEnv("LLAMA_SERVER_STARTUP_TIMEOUT", 120),
LlamaServerStopTimeout: intEnv("LLAMA_SERVER_STOP_TIMEOUT", 10),
ModelSwapCooldown: intEnv("MODEL_SWAP_COOLDOWN", 2),
RateLimitPerMinute: intEnv("RATE_LIMIT_PER_MINUTE", 60),
RateLimitStorage: getEnv("RATE_LIMIT_STORAGE", "memory"),
AdminUsername: getEnv("ADMIN_USERNAME", "admin"),
AdminPassword: getEnv("ADMIN_PASSWORD", ""),
AdminSessionTTL: intEnv("ADMIN_SESSION_TTL", 86400),
LogLevel: getEnv("LOG_LEVEL", "info"),
LogFormat: getEnv("LOG_FORMAT", "json"),
}
secretKey := getEnv("LLAMALINK_SECRET_KEY", "change-me-in-production")
if secretKey == "change-me-in-production" {
slog.Warn("LLAMALINK_SECRET_KEY is using the default value — set a secure random string in production")
}
c.JWTSecret = []byte(secretKey)
return c
}
func (c *Config) LlamaServerURL() string {
return "http://" + c.LlamaServerHost + ":" + strconv.Itoa(c.LlamaServerPort)
}
func (c *Config) LlamaServerStartupTimeoutDuration() time.Duration {
return time.Duration(c.LlamaServerStartupTimeout) * time.Second
}
func (c *Config) LlamaServerStopTimeoutDuration() time.Duration {
return time.Duration(c.LlamaServerStopTimeout) * time.Second
}
func (c *Config) ModelSwapCooldownDuration() time.Duration {
return time.Duration(c.ModelSwapCooldown) * time.Second
}
func (c *Config) Logger() *slog.Logger {
var level slog.Level
switch c.LogLevel {
case "debug":
level = slog.LevelDebug
case "warn":
level = slog.LevelWarn
case "error":
level = slog.LevelError
default:
level = slog.LevelInfo
}
opts := &slog.HandlerOptions{
Level: level,
}
var handler slog.Handler
if c.LogFormat == "text" {
handler = slog.NewTextHandler(os.Stdout, opts)
} else {
handler = slog.NewJSONHandler(os.Stdout, opts)
}
return slog.New(handler)
}
func getEnv(key, defaultValue string) string {
if v := os.Getenv(key); v != "" {
return v
}
return defaultValue
}
func intEnv(key string, defaultValue int) int {
if v := os.Getenv(key); v != "" {
if i, err := strconv.Atoi(v); err == nil {
return i
}
}
return defaultValue
}
func boolEnv(key string, defaultValue bool) bool {
if v := os.Getenv(key); v != "" {
if v == "true" || v == "1" || v == "yes" {
return true
}
if v == "false" || v == "0" || v == "no" {
return false
}
}
return defaultValue
}