177 lines
4.6 KiB
Go
177 lines
4.6 KiB
Go
package sub
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/omnex/control-plane/api/internal/auth"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Store définit les opérations de persistance pour les codes de souscription.
|
|
type Store interface {
|
|
// CreateCodeForSub crée un nouveau code pour un utilisateur.
|
|
CreateCodeForSub(username, code string) (CodeBuySub, error)
|
|
// GetCodeForSub récupère un code pour un utilisateur.
|
|
GetCodeForSub(username, code string) (CodeBuySub, error)
|
|
// ListCodes liste tous les codes.
|
|
ListCodes() ([]CodeBuySub, error)
|
|
AddCodeForSub(username, code string) (auth.User, error)
|
|
}
|
|
|
|
// GormStore implémente Store avec PostgreSQL via GORM.
|
|
type GormStore struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
// NewGormStore crée un nouveau GormStore.
|
|
func NewGormStore(db *gorm.DB) *GormStore {
|
|
return &GormStore{db: db}
|
|
}
|
|
|
|
// CreateCodeForSub crée un nouveau code pour un utilisateur.
|
|
func (s *GormStore) CreateCodeForSub(username, code string) (CodeBuySub, error) {
|
|
c := CodeBuySub{
|
|
ID: uuid.NewString(),
|
|
Username: auth.NormalizeUsername(username),
|
|
CodeBuy: code,
|
|
}
|
|
if err := s.db.Create(&c).Error; err != nil {
|
|
return CodeBuySub{}, err
|
|
}
|
|
|
|
// S'assurer que CreatedAt est bien rempli
|
|
if c.CreatedAt.IsZero() {
|
|
c.CreatedAt = time.Now().UTC()
|
|
if err := s.db.Save(&c).Error; err != nil {
|
|
return CodeBuySub{}, err
|
|
}
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// GetCodeForSub récupère un code pour un utilisateur.
|
|
func (s *GormStore) GetCodeForSub(username, code string) (CodeBuySub, error) {
|
|
var c CodeBuySub
|
|
if err := s.db.Where("username = ? AND code_buy = ?", username, code).First(&c).Error; err != nil {
|
|
return CodeBuySub{}, err
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func (s *GormStore) AddCodeForSub(username, code string) (auth.User, error) {
|
|
var c CodeBuySub
|
|
err := s.db.Where("username = ? AND code_buy = ?", username, code).First(&c).Error
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return auth.User{}, errors.New("code invalide")
|
|
}
|
|
return auth.User{}, err
|
|
}
|
|
|
|
var u auth.User
|
|
if err := s.db.Where("username = ?", auth.NormalizeUsername(username)).First(&u).Error; err != nil {
|
|
return auth.User{}, err
|
|
}
|
|
|
|
u.TypeAbo = "premium"
|
|
u.ExpiredAt = time.Now().UTC().AddDate(0, 0, 30)
|
|
if err := s.db.Model(&u).Updates(map[string]interface{}{
|
|
"type_abo": u.TypeAbo,
|
|
"expired_at": u.ExpiredAt,
|
|
}).Error; err != nil {
|
|
return auth.User{}, err
|
|
}
|
|
|
|
if err := s.db.Delete(&c).Error; err != nil {
|
|
return auth.User{}, err
|
|
}
|
|
|
|
return u, nil
|
|
}
|
|
|
|
// ListCodes liste tous les codes, triés par date de création.
|
|
func (s *GormStore) ListCodes() ([]CodeBuySub, error) {
|
|
var codes []CodeBuySub
|
|
// Essayer avec tri par date, sinon sans tri pour compatibilité
|
|
if err := s.db.Order("created_at desc").Find(&codes).Error; err != nil {
|
|
// Si colonne manquante, on liste sans tri
|
|
if orderErr := s.db.Find(&codes).Error; orderErr != nil {
|
|
return nil, orderErr
|
|
}
|
|
}
|
|
return codes, nil
|
|
}
|
|
|
|
// MemStore implémente Store en mémoire (pour dev/tests).
|
|
type MemStore struct {
|
|
mu sync.RWMutex
|
|
items map[string]CodeBuySub
|
|
byCode map[string]CodeBuySub
|
|
}
|
|
|
|
// NewMemStore crée un nouveau MemStore.
|
|
func NewMemStore() *MemStore {
|
|
return &MemStore{
|
|
items: make(map[string]CodeBuySub),
|
|
byCode: make(map[string]CodeBuySub),
|
|
}
|
|
}
|
|
|
|
// CreateCodeForSub crée un nouveau code pour un utilisateur.
|
|
func (m *MemStore) CreateCodeForSub(username, code string) (CodeBuySub, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
c := CodeBuySub{
|
|
ID: uuid.NewString(),
|
|
Username: username,
|
|
CodeBuy: code,
|
|
CreatedAt: time.Now().UTC(),
|
|
UpdatedAt: time.Now().UTC(),
|
|
}
|
|
m.items[c.ID] = c
|
|
m.byCode[code] = c
|
|
return c, nil
|
|
}
|
|
|
|
// GetCodeForSub récupère un code pour un utilisateur.
|
|
func (m *MemStore) GetCodeForSub(username, code string) (CodeBuySub, error) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
c, ok := m.byCode[code]
|
|
if !ok {
|
|
return CodeBuySub{}, nil
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
// ListCodes liste tous les codes.
|
|
func (m *MemStore) ListCodes() ([]CodeBuySub, error) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
out := make([]CodeBuySub, 0, len(m.items))
|
|
for _, c := range m.items {
|
|
out = append(out, c)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (m *MemStore) AddCodeForSub(username, code string) (auth.User, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
c, ok := m.byCode[code]
|
|
if !ok || c.Username != username {
|
|
return auth.User{}, errors.New("code invalide")
|
|
}
|
|
delete(m.byCode, code)
|
|
delete(m.items, c.ID)
|
|
|
|
// Le MemStore ne connaît pas les utilisateurs (pas de référence à memUsers ici) :
|
|
// il ne peut donc pas mettre à jour TypeAbo lui-même. Cf. point ci-dessous.
|
|
return auth.User{Username: username, TypeAbo: "premium"}, nil
|
|
}
|