monorepo/cloud/maplepress-backend/internal/service/site/create.go

112 lines
3.8 KiB
Go

package site
import (
"context"
"fmt"
"github.com/gocql/gocql"
"go.uber.org/zap"
siteusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/site"
)
// CreateSiteService handles site creation operations
type CreateSiteService interface {
CreateSite(ctx context.Context, tenantID gocql.UUID, input *siteusecase.CreateSiteInput) (*siteusecase.CreateSiteOutput, error)
}
type createSiteService struct {
// Focused usecases
validateDomainUC *siteusecase.ValidateDomainUseCase
generateAPIKeyUC *siteusecase.GenerateAPIKeyUseCase
generateVerifyTokenUC *siteusecase.GenerateVerificationTokenUseCase
createSiteEntityUC *siteusecase.CreateSiteEntityUseCase
saveSiteToRepoUC *siteusecase.SaveSiteToRepoUseCase
logger *zap.Logger
}
// NewCreateSiteService creates a new CreateSiteService
func NewCreateSiteService(
validateDomainUC *siteusecase.ValidateDomainUseCase,
generateAPIKeyUC *siteusecase.GenerateAPIKeyUseCase,
generateVerifyTokenUC *siteusecase.GenerateVerificationTokenUseCase,
createSiteEntityUC *siteusecase.CreateSiteEntityUseCase,
saveSiteToRepoUC *siteusecase.SaveSiteToRepoUseCase,
logger *zap.Logger,
) CreateSiteService {
return &createSiteService{
validateDomainUC: validateDomainUC,
generateAPIKeyUC: generateAPIKeyUC,
generateVerifyTokenUC: generateVerifyTokenUC,
createSiteEntityUC: createSiteEntityUC,
saveSiteToRepoUC: saveSiteToRepoUC,
logger: logger.Named("create-site-service"),
}
}
// CreateSite orchestrates the site creation workflow
func (s *createSiteService) CreateSite(ctx context.Context, tenantID gocql.UUID, input *siteusecase.CreateSiteInput) (*siteusecase.CreateSiteOutput, error) {
s.logger.Info("creating site",
zap.String("tenant_id", tenantID.String()),
zap.String("domain", input.Domain))
// Step 1: Validate domain availability
if err := s.validateDomainUC.Execute(ctx, input.Domain); err != nil {
s.logger.Error("domain validation failed",
zap.String("domain", input.Domain),
zap.Error(err))
return nil, err
}
// Step 2: Generate API key
apiKeyResult, err := s.generateAPIKeyUC.Execute(input.TestMode)
if err != nil {
s.logger.Error("API key generation failed", zap.Error(err))
return nil, fmt.Errorf("failed to generate API key: %w", err)
}
// Step 3: Generate verification token
verificationToken, err := s.generateVerifyTokenUC.Execute()
if err != nil {
s.logger.Error("verification token generation failed", zap.Error(err))
return nil, fmt.Errorf("failed to generate verification token: %w", err)
}
// Step 4: Create site entity (no plan tier - usage-based billing)
site, err := s.createSiteEntityUC.Execute(&siteusecase.CreateSiteEntityInput{
TenantID: tenantID,
Domain: input.Domain,
SiteURL: input.SiteURL,
APIKeyHash: apiKeyResult.HashedKey,
APIKeyPrefix: apiKeyResult.Prefix,
APIKeyLastFour: apiKeyResult.LastFour,
VerificationToken: verificationToken,
IPAddress: input.IPAddress,
})
if err != nil {
s.logger.Error("failed to create site entity", zap.Error(err))
return nil, err
}
// Step 5: Save site to repository
if err := s.saveSiteToRepoUC.Execute(ctx, site); err != nil {
s.logger.Error("failed to save site", zap.Error(err))
return nil, err
}
s.logger.Info("site created successfully",
zap.String("site_id", site.ID.String()),
zap.String("domain", site.Domain))
// Step 6: Build output
return &siteusecase.CreateSiteOutput{
ID: site.ID.String(),
Domain: site.Domain,
SiteURL: site.SiteURL,
APIKey: apiKeyResult.PlaintextKey, // PLAINTEXT - only shown once!
VerificationToken: verificationToken,
Status: site.Status,
SearchIndexName: site.SearchIndexName,
}, nil
}