package sslsvc import ( "context" "log" "time" "github.com/panelhosting/panel/internal/config" "github.com/panelhosting/panel/internal/repository" "github.com/panelhosting/panel/internal/ssl" ) type Service struct { repo *repository.SSLRepository issuer *ssl.Issuer cfg *config.Config } func NewService(repo *repository.SSLRepository, issuer *ssl.Issuer, cfg *config.Config) *Service { return &Service{repo: repo, issuer: issuer, cfg: cfg} } func (s *Service) IssueAsync(sslID, domainID int64, domain, webroot string) { go func() { ctx := context.Background() if err := s.Issue(ctx, sslID, domainID, domain, webroot); err != nil { log.Printf("ssl issue %s: %v", domain, err) } }() } func (s *Service) Issue(ctx context.Context, sslID, domainID int64, domain, webroot string) error { if s.cfg.ACMEEmail == "" { return s.repo.MarkError(ctx, sslID, "ACME_EMAIL not configured") } res, err := s.issuer.Obtain(ctx, domain, webroot) if err != nil { _ = s.repo.MarkError(ctx, sslID, err.Error()) return err } expiresAt, err := ssl.ParseCertExpiry(res.Certificate) if err != nil { expiresAt = time.Now().Add(90 * 24 * time.Hour) } issuerName := "Let's Encrypt" if s.cfg.ACMEStaging { issuerName = "Let's Encrypt (staging)" } return s.repo.MarkActive(ctx, sslID, issuerName, string(res.Certificate), string(res.PrivateKey), string(res.IssuerCertificate), time.Now(), expiresAt) } func (s *Service) IssueForSite(ctx context.Context, siteID int64) error { domainID, err := s.repo.GetDomainIDBySite(ctx, siteID) if err != nil { return err } domain, webroot, err := s.repo.GetDomainInfo(ctx, domainID) if err != nil { return err } cert, err := s.repo.GetByDomainID(ctx, domainID) if err != nil { return err } var sslID int64 if cert == nil { sslID, err = s.repo.CreatePending(ctx, domainID) } else { sslID = cert.ID } if err != nil { return err } go func() { if err := s.Issue(context.Background(), sslID, domainID, domain, webroot); err != nil { log.Printf("ssl reissue %s: %v", domain, err) } }() return nil }