package service import ( "dal-license-server/internal/model" "dal-license-server/internal/repository" "database/sql" "encoding/json" "fmt" "time" ) type LicenseService struct { repo *repository.LicenseRepo audit *repository.AuditRepo } func NewLicenseService(repo *repository.LicenseRepo, audit *repository.AuditRepo) *LicenseService { return &LicenseService{repo: repo, audit: audit} } func (s *LicenseService) Create(req *model.CreateLicenseRequest, ip string) (*model.License, error) { product, err := s.repo.GetProductByID(req.ProductID) if err != nil { return nil, fmt.Errorf("product not found: %w", err) } key, err := GenerateKey(product.KeyPrefix) if err != nil { return nil, fmt.Errorf("generate key: %w", err) } limits := req.Limits if len(limits) == 0 || string(limits) == "" || string(limits) == "null" { limits = product.DefaultLimits } features := req.Features if len(features) == 0 || string(features) == "" || string(features) == "null" { features = product.AvailableFeatures } graceDays := req.GraceDays if graceDays == 0 { graceDays = 30 } l := &model.License{ ProductID: req.ProductID, LicenseKey: key, LicenseType: req.LicenseType, CustomerName: req.CustomerName, CustomerPIB: req.CustomerPIB, CustomerEmail: req.CustomerEmail, Limits: limits, Features: features, GraceDays: graceDays, Notes: req.Notes, } // Set expiry switch req.LicenseType { case "TRIAL": exp := time.Now().AddDate(0, 0, 30) l.ExpiresAt = sql.NullTime{Time: exp, Valid: true} case "MONTHLY": exp := time.Now().AddDate(0, 1, 0) l.ExpiresAt = sql.NullTime{Time: exp, Valid: true} case "ANNUAL": exp := time.Now().AddDate(1, 0, 0) l.ExpiresAt = sql.NullTime{Time: exp, Valid: true} case "PERPETUAL": l.ExpiresAt = sql.NullTime{Valid: false} } id, err := s.repo.Create(l) if err != nil { return nil, err } s.audit.Log(&id, "CREATE", ip, map[string]interface{}{ "product": product.Code, "type": req.LicenseType, "customer": req.CustomerName, }) return s.repo.GetByID(id) } func (s *LicenseService) GetByID(id int64) (*model.License, error) { return s.repo.GetByID(id) } func (s *LicenseService) GetByKey(key string) (*model.License, error) { return s.repo.GetByKey(key) } func (s *LicenseService) List(productCode, status, search string) ([]model.LicenseWithActivation, error) { return s.repo.List(productCode, status, search) } func (s *LicenseService) Update(id int64, req *model.UpdateLicenseRequest, ip string) error { l, err := s.repo.GetByID(id) if err != nil { return err } l.CustomerName = req.CustomerName l.CustomerPIB = req.CustomerPIB l.CustomerEmail = req.CustomerEmail l.Notes = req.Notes if len(req.Limits) > 0 { l.Limits = req.Limits } if len(req.Features) > 0 { l.Features = req.Features } if req.GraceDays > 0 { l.GraceDays = req.GraceDays } if req.ExpiresAt != "" { t, err := time.Parse("2006-01-02", req.ExpiresAt) if err == nil { l.ExpiresAt = sql.NullTime{Time: t, Valid: true} } } s.audit.Log(&id, "UPDATE", ip, map[string]interface{}{"customer": req.CustomerName}) return s.repo.Update(l) } func (s *LicenseService) Revoke(id int64, reason, ip string) error { s.audit.Log(&id, "REVOKE", ip, map[string]interface{}{"reason": reason}) return s.repo.Revoke(id, reason) } func (s *LicenseService) GetProducts() ([]model.Product, error) { return s.repo.GetProducts() } func (s *LicenseService) GetStats() (*model.StatsResponse, error) { stats := &model.StatsResponse{} byProduct, err := s.repo.CountByProduct() if err != nil { return nil, err } stats.ByProduct = byProduct for _, p := range byProduct { stats.TotalLicenses += p.Total stats.ActiveLicenses += p.Active stats.ExpiredLicenses += p.Expired stats.RevokedLicenses += p.Trial // count trials separately stats.ActiveActivations += p.ActiveActivations } // Fix: count revoked properly var revoked int for _, p := range byProduct { _ = p // need a separate query for revoked } _ = revoked licenses, _ := s.repo.List("", "revoked", "") stats.RevokedLicenses = len(licenses) return stats, nil } func (s *LicenseService) ExpiringIn(days int) ([]model.License, error) { return s.repo.ExpiringIn(days) } func (s *LicenseService) BuildLicenseData(l *model.License, fingerprint string) ([]byte, error) { expiresAt := "" if l.ExpiresAt.Valid { expiresAt = l.ExpiresAt.Time.Format(time.RFC3339) } ld := model.LicenseData{ LicenseKey: l.LicenseKey, Product: l.ProductCode, LicenseType: l.LicenseType, IssuedAt: l.IssuedAt.Format(time.RFC3339), ExpiresAt: expiresAt, ActivatedAt: time.Now().Format(time.RFC3339), MachineFingerprint: fingerprint, GraceDays: l.GraceDays, Limits: l.Limits, Features: l.Features, Customer: model.CustomerData{ Name: l.CustomerName, Email: l.CustomerEmail, }, } return json.Marshal(ld) }