feat: Add whitelabel service with branding, partner config, and email template management

- Add Service struct with db and logger dependencies and NewService constructor
- Add CreateBranding, GetBranding, GetBrandingByDomain, and UpdateBranding methods for branding configuration management
- Add CreatePartnerConfig and GetPartnerConfig methods for partner-specific settings
- Add CreateEmailTemplate, GetEmailTemplate, and ListEmailTemplates methods for custom email template management
- Add BrandingConfig with
This commit is contained in:
nutcas3
2026-05-03 10:48:26 +03:00
parent 52797e54fc
commit 4d0eb13cee
2 changed files with 184 additions and 0 deletions
@@ -0,0 +1,99 @@
package whitelabel
import (
"context"
"fmt"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
// Service provides whitelabel management operations
type Service struct {
db *gorm.DB
logger *logrus.Logger
}
// NewService creates a new whitelabel service
func NewService(db *gorm.DB, logger *logrus.Logger) *Service {
return &Service{db: db, logger: logger}
}
// CreateBranding creates a new branding configuration
func (s *Service) CreateBranding(ctx context.Context, config *BrandingConfig) error {
if err := s.db.WithContext(ctx).Create(config).Error; err != nil {
s.logger.WithError(err).Error("Failed to create branding config")
return fmt.Errorf("failed to create branding: %w", err)
}
s.logger.WithField("tenant_id", config.TenantID).Info("Branding config created")
return nil
}
// GetBranding retrieves branding by tenant ID
func (s *Service) GetBranding(ctx context.Context, tenantID string) (*BrandingConfig, error) {
var config BrandingConfig
if err := s.db.WithContext(ctx).Where("tenant_id = ?", tenantID).First(&config).Error; err != nil {
return nil, fmt.Errorf("branding not found: %w", err)
}
return &config, nil
}
// GetBrandingByDomain retrieves branding by custom domain
func (s *Service) GetBrandingByDomain(ctx context.Context, domain string) (*BrandingConfig, error) {
var config BrandingConfig
if err := s.db.WithContext(ctx).Where("custom_domain = ? AND is_active = ?", domain, true).First(&config).Error; err != nil {
return nil, fmt.Errorf("branding not found for domain: %w", err)
}
return &config, nil
}
// UpdateBranding updates branding configuration
func (s *Service) UpdateBranding(ctx context.Context, config *BrandingConfig) error {
if err := s.db.WithContext(ctx).Save(config).Error; err != nil {
return fmt.Errorf("failed to update branding: %w", err)
}
return nil
}
// CreatePartnerConfig creates partner configuration
func (s *Service) CreatePartnerConfig(ctx context.Context, config *PartnerConfig) error {
if err := s.db.WithContext(ctx).Create(config).Error; err != nil {
return fmt.Errorf("failed to create partner config: %w", err)
}
return nil
}
// GetPartnerConfig retrieves partner configuration
func (s *Service) GetPartnerConfig(ctx context.Context, tenantID string) (*PartnerConfig, error) {
var config PartnerConfig
if err := s.db.WithContext(ctx).Where("tenant_id = ?", tenantID).First(&config).Error; err != nil {
return nil, fmt.Errorf("partner config not found: %w", err)
}
return &config, nil
}
// CreateEmailTemplate creates a custom email template
func (s *Service) CreateEmailTemplate(ctx context.Context, template *EmailTemplate) error {
if err := s.db.WithContext(ctx).Create(template).Error; err != nil {
return fmt.Errorf("failed to create email template: %w", err)
}
return nil
}
// GetEmailTemplate retrieves an email template
func (s *Service) GetEmailTemplate(ctx context.Context, tenantID, templateKey string) (*EmailTemplate, error) {
var template EmailTemplate
if err := s.db.WithContext(ctx).Where("tenant_id = ? AND template_key = ? AND is_active = ?", tenantID, templateKey, true).First(&template).Error; err != nil {
return nil, fmt.Errorf("email template not found: %w", err)
}
return &template, nil
}
// ListEmailTemplates lists all templates for a tenant
func (s *Service) ListEmailTemplates(ctx context.Context, tenantID string) ([]*EmailTemplate, error) {
var templates []*EmailTemplate
if err := s.db.WithContext(ctx).Where("tenant_id = ?", tenantID).Find(&templates).Error; err != nil {
return nil, fmt.Errorf("failed to list templates: %w", err)
}
return templates, nil
}
@@ -0,0 +1,85 @@
package whitelabel
import "time"
// BrandingConfig defines partner branding configuration
type BrandingConfig struct {
ID string `json:"id" gorm:"primaryKey"`
TenantID string `json:"tenant_id" gorm:"index"`
CompanyName string `json:"company_name"`
LogoURL string `json:"logo_url"`
FaviconURL string `json:"favicon_url"`
PrimaryColor string `json:"primary_color"`
SecondaryColor string `json:"secondary_color"`
AccentColor string `json:"accent_color"`
FontFamily string `json:"font_family"`
CustomCSS string `json:"custom_css"`
CustomDomain string `json:"custom_domain" gorm:"uniqueIndex"`
EmailFromName string `json:"email_from_name"`
EmailFromAddr string `json:"email_from_address"`
SupportEmail string `json:"support_email"`
SupportPhone string `json:"support_phone"`
TermsURL string `json:"terms_url"`
PrivacyURL string `json:"privacy_url"`
FooterText string `json:"footer_text"`
SocialLinks map[string]string `json:"social_links" gorm:"serializer:json"`
Features FeatureFlags `json:"features" gorm:"embedded;embeddedPrefix:feature_"`
IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// FeatureFlags controls which features are enabled for a whitelabel partner
type FeatureFlags struct {
ShowPoweredBy bool `json:"show_powered_by"`
CustomEmailDomain bool `json:"custom_email_domain"`
CustomAPIDomain bool `json:"custom_api_domain"`
AdvancedAnalytics bool `json:"advanced_analytics"`
WhitelabelMobile bool `json:"whitelabel_mobile"`
CustomWebhooks bool `json:"custom_webhooks"`
APIDocsBranding bool `json:"api_docs_branding"`
MultiLanguage bool `json:"multi_language"`
}
// EmailTemplate defines customizable email templates
type EmailTemplate struct {
ID string `json:"id" gorm:"primaryKey"`
TenantID string `json:"tenant_id" gorm:"index"`
TemplateKey string `json:"template_key" gorm:"index"`
Subject string `json:"subject"`
HTMLBody string `json:"html_body"`
TextBody string `json:"text_body"`
Variables []string `json:"variables" gorm:"serializer:json"`
IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// PartnerTier defines partnership levels with different capabilities
type PartnerTier string
const (
PartnerTierBasic PartnerTier = "basic"
PartnerTierProfession PartnerTier = "professional"
PartnerTierEnterprise PartnerTier = "enterprise"
PartnerTierPlatinum PartnerTier = "platinum"
)
// PartnerConfig defines partner-specific configuration
type PartnerConfig struct {
ID string `json:"id" gorm:"primaryKey"`
TenantID string `json:"tenant_id" gorm:"uniqueIndex"`
Tier PartnerTier `json:"tier"`
RevenueSharePct float64 `json:"revenue_share_pct"`
MinMonthlyCommit float64 `json:"min_monthly_commit"`
MaxAPIRequests int64 `json:"max_api_requests"`
MaxProfiles int64 `json:"max_profiles"`
AllowedCountries []string `json:"allowed_countries" gorm:"serializer:json"`
AllowedCarriers []string `json:"allowed_carriers" gorm:"serializer:json"`
CustomPricing map[string]any `json:"custom_pricing" gorm:"serializer:json"`
ContractStartDate time.Time `json:"contract_start_date"`
ContractEndDate time.Time `json:"contract_end_date"`
IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}