@@ -0,0 +1,167 @@
|
||||
// Commande api : point d'entrée du control-plane Omnex.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/omnex/control-plane/api/internal/auth"
|
||||
"github.com/omnex/control-plane/api/internal/config"
|
||||
"github.com/omnex/control-plane/api/internal/db"
|
||||
"github.com/omnex/control-plane/api/internal/demos"
|
||||
"github.com/omnex/control-plane/api/internal/k8s"
|
||||
"github.com/omnex/control-plane/api/internal/leads"
|
||||
"github.com/omnex/control-plane/api/internal/router"
|
||||
"github.com/omnex/control-plane/api/internal/sav"
|
||||
"github.com/omnex/control-plane/api/internal/session"
|
||||
"github.com/omnex/control-plane/api/internal/sub"
|
||||
)
|
||||
|
||||
// memUsers : store utilisateurs en mémoire (fallback dev sans base).
|
||||
type memUsers map[string]auth.User
|
||||
|
||||
func (m memUsers) ByUsername(username string) (auth.User, bool) {
|
||||
u, ok := m[strings.ToLower(strings.TrimSpace(username))]
|
||||
return u, ok
|
||||
}
|
||||
|
||||
func (m memUsers) Create(username, passwordHash string, role auth.Role) (auth.User, error) {
|
||||
key := strings.ToLower(strings.TrimSpace(username))
|
||||
u := auth.User{ID: uuid.NewString(), Username: key, PasswordHash: passwordHash, Role: role}
|
||||
m[key] = u
|
||||
return u, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
log.Fatalf("config: %v", err)
|
||||
}
|
||||
|
||||
iss := auth.NewIssuer(cfg.JWTSecret, cfg.SessionTTL)
|
||||
|
||||
// Sessions : Redis en prod/dev configuré, mémoire en fallback dev.
|
||||
var sessions session.Manager
|
||||
if cfg.RedisURL != "" {
|
||||
opt, err := redis.ParseURL(cfg.RedisURL)
|
||||
if err != nil {
|
||||
log.Fatalf("redis url: %v", err)
|
||||
}
|
||||
rdb := redis.NewClient(opt)
|
||||
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
||||
log.Fatalf("redis ping: %v", err)
|
||||
}
|
||||
sessions = session.NewRedisManager(rdb, cfg.SessionTTL)
|
||||
log.Printf("sessions: Redis")
|
||||
} else {
|
||||
sessions = session.NewMemManager(cfg.SessionTTL)
|
||||
log.Printf("sessions: mémoire (dev — définir OMNEX_REDIS_URL pour Redis)")
|
||||
}
|
||||
|
||||
var userStore auth.UserStore
|
||||
var leadStore leads.Store
|
||||
var demoStore demos.Store
|
||||
var demoPool demos.Pool
|
||||
var codeStore sub.Store
|
||||
var contactStore sav.Store
|
||||
if cfg.DatabaseURL != "" {
|
||||
gdb, err := db.Open(cfg.DatabaseURL)
|
||||
if err != nil {
|
||||
log.Fatalf("db: %v", err)
|
||||
}
|
||||
if err := db.AutoMigrate(gdb); err != nil {
|
||||
log.Fatalf("migrate: %v", err)
|
||||
}
|
||||
if err := db.SeedExternalPool(gdb); err != nil {
|
||||
log.Fatalf("seed pool: %v", err)
|
||||
}
|
||||
users := auth.NewGormStore(gdb)
|
||||
seedAdmin(users)
|
||||
userStore = users
|
||||
codeStore = sub.NewGormStore(gdb)
|
||||
leadStore = leads.NewGormStore(gdb)
|
||||
demoStore = demos.NewGormStore(gdb)
|
||||
demoPool = demos.NewGormPool(gdb)
|
||||
contactStore = sav.NewGormStore(gdb)
|
||||
log.Printf("persistance: PostgreSQL (GORM)")
|
||||
} else {
|
||||
userStore = seedMemUsers()
|
||||
codeStore = sub.NewMemStore()
|
||||
leadStore = leads.NewMemStore()
|
||||
demoStore = demos.NewMemStore()
|
||||
demoPool = demos.NewMemPool()
|
||||
contactStore = sav.NewMemContactStore()
|
||||
log.Printf("persistance: mémoire (dev — définir OMNEX_DATABASE_URL pour PostgreSQL)")
|
||||
}
|
||||
|
||||
// Créer le client Kubernetes
|
||||
k8sClient, err := k8s.NewClient(&cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("k8s client: %v", err)
|
||||
}
|
||||
|
||||
helmProv, err := demos.NewHelmProvisioner(&cfg, k8sClient, "/charts", "helm")
|
||||
if err != nil {
|
||||
log.Fatalf("helm provisioner: %v", err)
|
||||
}
|
||||
|
||||
// Service de démos avec le provisioner Helm
|
||||
demoSvc := demos.NewService(demoStore, demoPool, helmProv, demos.Config{
|
||||
BaseDomain: cfg.DemoDomain,
|
||||
TTL: demos.TTL,
|
||||
MaxDemos: demos.MaxConcurrentDemos,
|
||||
})
|
||||
|
||||
deps := router.Deps{
|
||||
Cfg: cfg,
|
||||
Issuer: iss,
|
||||
Sessions: sessions,
|
||||
AuthH: auth.NewHandler(userStore, sessions, iss, cfg.Secure()),
|
||||
LeadsH: leads.NewHandler(leadStore),
|
||||
DemosH: demos.NewHandler(demoSvc),
|
||||
SubH: sub.NewHandler(codeStore),
|
||||
ContactH: sav.NewHandler(contactStore),
|
||||
}
|
||||
|
||||
r := router.New(deps)
|
||||
log.Printf("Omnex API sur %s (env=%s)", cfg.Addr, cfg.Env)
|
||||
if err := r.Run(cfg.Addr); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// seedAdmin crée l'utilisateur admin de départ depuis l'env (idempotent).
|
||||
func seedAdmin(users *auth.GormStore) {
|
||||
username, pass := os.Getenv("OMNEX_SEED_USERNAME"), os.Getenv("OMNEX_SEED_PASSWORD")
|
||||
if username == "" || pass == "" {
|
||||
return
|
||||
}
|
||||
hash, err := auth.HashPassword(pass)
|
||||
if err != nil {
|
||||
log.Fatalf("seed: %v", err)
|
||||
}
|
||||
if err := users.EnsureUser(username, hash, auth.RoleAdmin); err != nil {
|
||||
log.Fatalf("seed: %v", err)
|
||||
}
|
||||
log.Printf("utilisateur admin seedé: %s", username)
|
||||
}
|
||||
|
||||
// seedMemUsers construit un store mémoire depuis l'env (dev uniquement).
|
||||
func seedMemUsers() memUsers {
|
||||
users := memUsers{}
|
||||
if username, pass := os.Getenv("OMNEX_SEED_USERNAME"), os.Getenv("OMNEX_SEED_PASSWORD"); username != "" && pass != "" {
|
||||
hash, err := auth.HashPassword(pass)
|
||||
if err != nil {
|
||||
log.Fatalf("seed: %v", err)
|
||||
}
|
||||
key := strings.ToLower(strings.TrimSpace(username))
|
||||
users[key] = auth.User{ID: "seed-admin", Username: key, PasswordHash: hash, Role: auth.RoleAdmin}
|
||||
log.Printf("utilisateur admin seedé (mémoire): %s", key)
|
||||
}
|
||||
return users
|
||||
}
|
||||
Reference in New Issue
Block a user