chore: update
This commit is contained in:
@@ -11,7 +11,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCapacityReached = errors.New("capacité maximale de démos atteinte")
|
||||
ErrNotFound = errors.New("démo introuvable")
|
||||
ErrNotExtendable = errors.New("démo non prolongeable dans son état actuel")
|
||||
ErrUserHasActiveDemo = errors.New("ce client a déjà une démo active")
|
||||
@@ -22,7 +21,6 @@ type Config struct {
|
||||
BaseDomain string // ex. "demo.omnex.app"
|
||||
HTTPSPort string // port public HTTPS pour l'URL affichée ("443" = omis, sinon ex. NodePort)
|
||||
TTL time.Duration // durée de vie
|
||||
MaxDemos int // capacité
|
||||
}
|
||||
|
||||
// Service : logique métier des démos (indépendante du transport HTTP).
|
||||
@@ -38,9 +36,6 @@ func NewService(store Store, pool Pool, prov Provisioner, cfg Config) *Service {
|
||||
if cfg.TTL == 0 {
|
||||
cfg.TTL = TTL
|
||||
}
|
||||
if cfg.MaxDemos == 0 {
|
||||
cfg.MaxDemos = MaxConcurrentDemos
|
||||
}
|
||||
if cfg.BaseDomain == "" {
|
||||
cfg.BaseDomain = "demo.omnex.app"
|
||||
}
|
||||
@@ -52,8 +47,12 @@ func NewService(store Store, pool Pool, prov Provisioner, cfg Config) *Service {
|
||||
|
||||
// Create réserve la capacité + le pool, persiste la démo et déclenche le worker.
|
||||
// username (optionnel) rattache la démo à un client existant. Un client n'a
|
||||
// droit qu'à une seule démo active à la fois.
|
||||
func (s *Service) Create(leadID, username string) (Demo, error) {
|
||||
// droit qu'à une seule démo active à la fois. cfg (bot Telegram, stockage)
|
||||
// n'est jamais persisté : transmis tel quel au provisioning.
|
||||
func (s *Service) Create(leadID, username string, cfg ProvisionConfig) (Demo, error) {
|
||||
if err := cfg.Validate(); err != nil {
|
||||
return Demo{}, err
|
||||
}
|
||||
normalizedUsername := auth.NormalizeUsername(username)
|
||||
if normalizedUsername != "" {
|
||||
existing, err := s.store.ListByUsername(normalizedUsername)
|
||||
@@ -67,14 +66,6 @@ func (s *Service) Create(leadID, username string) (Demo, error) {
|
||||
}
|
||||
}
|
||||
|
||||
active, err := s.store.CountActive()
|
||||
if err != nil {
|
||||
return Demo{}, err
|
||||
}
|
||||
if active >= s.cfg.MaxDemos {
|
||||
return Demo{}, ErrCapacityReached
|
||||
}
|
||||
|
||||
id := uuid.NewString()
|
||||
demo := Demo{
|
||||
ID: id,
|
||||
@@ -95,12 +86,11 @@ func (s *Service) Create(leadID, username string) (Demo, error) {
|
||||
}
|
||||
demo.URL = "https://" + demo.Namespace + "." + s.cfg.BaseDomain + portSuffix
|
||||
|
||||
// Réserve les ressources externes ; ErrPoolExhausted => capacité atteinte.
|
||||
// Réserve les ressources externes (slots Telegram/NowPayments pré-
|
||||
// provisionnés) — contrainte réelle indépendante du nombre de démos,
|
||||
// non concernée par la suppression du quota.
|
||||
resources, err := s.pool.Borrow(id)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrPoolExhausted) {
|
||||
return Demo{}, ErrCapacityReached
|
||||
}
|
||||
return Demo{}, err
|
||||
}
|
||||
|
||||
@@ -113,7 +103,7 @@ func (s *Service) Create(leadID, username string) (Demo, error) {
|
||||
|
||||
// Lance le déploiement de manière asynchrone (worker inline via goroutine).
|
||||
go func() {
|
||||
if err := s.prov.Provision(created, resources); err != nil {
|
||||
if err := s.prov.Provision(created, resources, cfg); err != nil {
|
||||
log.Printf("Provisioning échoué pour %s: %v", created.ID, err)
|
||||
created.Status = StatusFailed
|
||||
_, _ = s.store.Update(created)
|
||||
@@ -144,6 +134,45 @@ func (s *Service) ListForUser(username string) ([]Demo, error) {
|
||||
return s.store.ListByUsername(auth.NormalizeUsername(username))
|
||||
}
|
||||
|
||||
// TransferToPaid marque la démo active du client comme abonnement payant
|
||||
// (n'expire plus) et déclenche la bascule en stockage persistant (migration
|
||||
// des données postgres incluse, voir Provisioner.UpgradeToPaid). 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). L'upgrade infra est lent (dump/
|
||||
// restore/helm) : il 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
var active *Demo
|
||||
for i := range existing {
|
||||
if existing[i].Status.Active() {
|
||||
active = &existing[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if active == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
active.TypeAbo = "premium"
|
||||
active.ExpiresAt = s.now().UTC().AddDate(10, 0, 0) // n'expire plus, en pratique
|
||||
updated, err := s.store.Update(*active)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := s.prov.UpgradeToPaid(updated); err != nil {
|
||||
log.Printf("Transfert payant échoué pour %s: %v", updated.Namespace, err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete déclenche le teardown et libère le pool.
|
||||
func (s *Service) Delete(id string) (Demo, error) {
|
||||
d, ok := s.store.Get(id)
|
||||
|
||||
Reference in New Issue
Block a user