chore: update
ci-api / test (push) Successful in 22m17s
ci-web / test (push) Successful in 13m44s

This commit is contained in:
Xor290
2026-08-02 18:34:59 +02:00
parent 6992e505ae
commit c5ee5636ef
34 changed files with 2660 additions and 498 deletions
+42 -15
View File
@@ -75,15 +75,7 @@ func (s *Service) Create(username string, cfg ProvisionConfig) (Demo, error) {
CreatedAt: s.now().UTC(),
ExpiresAt: s.now().UTC().Add(s.cfg.TTL),
}
// Port omis dans l'URL s'il s'agit du 443 standard ; sinon affiché
// explicitement (ex. NodePort HTTPS sans LoadBalancer). Ne concerne que
// l'URL affichée : le Host() de l'IngressRoute reste sans port (voir
// buildIngressRouteValues), le port n'y a pas sa place pour le matching.
portSuffix := ""
if s.cfg.HTTPSPort != "" && s.cfg.HTTPSPort != "443" {
portSuffix = ":" + s.cfg.HTTPSPort
}
demo.URL = "https://" + demo.Namespace + "." + s.cfg.BaseDomain + portSuffix
demo.URL = s.urlFor(demo.Namespace)
// Réserve les ressources externes (slots Telegram/NowPayments pré-
// provisionnés) — contrainte réelle indépendante du nombre de démos,
@@ -134,11 +126,16 @@ func (s *Service) ListForUser(username string) ([]Demo, error) {
}
// TransferToPaid marque la démo active du client comme abonnement payant
// (n'expire plus). Le stockage (PVC postgres/redis) est déjà en place depuis
// la création de la démo (voir HelmProvisioner.Provision) : rien à basculer
// côté infra, seul le statut change. No-op si le client n'a pas de démo
// active — ce n'est pas une erreur (ex: compte créé sans jamais avoir
// demandé de démo).
// (n'expire plus) et déclenche la migration de son namespace vers
// "premium-<client>" (Kubernetes ne permet pas de renommer un namespace,
// voir HelmProvisioner.MigrateToPremiumNamespace) — no-op si déjà migrée
// (ex. renouvellement d'un client déjà premium). Le stockage (PVC
// postgres/redis) est déjà en place depuis la création de la démo, seules
// les données postgres sont recopiées vers le nouveau namespace. No-op
// total si le client n'a pas de démo active — ce n'est pas une erreur (ex:
// compte créé sans jamais avoir demandé de démo). La migration est lente
// (dump/restore/helm) : elle tourne en tâche de fond, cette méthode ne
// bloque pas.
func (s *Service) TransferToPaid(username string) error {
existing, err := s.store.ListByUsername(auth.NormalizeUsername(username))
if err != nil {
@@ -158,13 +155,43 @@ func (s *Service) TransferToPaid(username string) error {
active.TypeAbo = "premium"
active.ExpiresAt = s.now().UTC().AddDate(10, 0, 0) // n'expire plus, en pratique
if _, err := s.store.Update(*active); err != nil {
updated, err := s.store.Update(*active)
if err != nil {
return err
}
newNamespace := premiumNamespace(updated.Username)
if newNamespace != updated.Namespace {
newURL := s.urlFor(newNamespace)
go func() {
if err := s.prov.MigrateToPremiumNamespace(updated, newNamespace, newURL); err != nil {
log.Printf("Migration namespace premium échouée pour %s: %v", updated.Namespace, err)
return
}
updated.Namespace = newNamespace
updated.URL = newURL
if _, err := s.store.Update(updated); err != nil {
log.Printf("Mise à jour namespace après migration premium échouée pour %s: %v", newNamespace, err)
}
}()
}
return nil
}
// urlFor construit l'URL publique d'une démo pour un namespace donné. Port
// omis s'il s'agit du 443 standard ; sinon affiché explicitement (ex.
// NodePort HTTPS sans LoadBalancer). Ne concerne que l'URL affichée : le
// Host() de l'IngressRoute reste sans port (voir buildIngressRouteValues),
// le port n'y a pas sa place pour le matching.
func (s *Service) urlFor(namespace string) string {
portSuffix := ""
if s.cfg.HTTPSPort != "" && s.cfg.HTTPSPort != "443" {
portSuffix = ":" + s.cfg.HTTPSPort
}
return "https://" + namespace + "." + s.cfg.BaseDomain + portSuffix
}
// Delete déclenche le teardown et libère le pool.
func (s *Service) Delete(id string) (Demo, error) {
d, ok := s.store.Get(id)