monorepo/cloud/maplepress-backend/internal/service/page/sync.go

143 lines
4.4 KiB
Go

package page
import (
"context"
"fmt"
"github.com/gocql/gocql"
"go.uber.org/zap"
domainpage "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/page"
pageusecase "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/page"
)
// SyncPagesService handles page synchronization operations
type SyncPagesService interface {
SyncPages(ctx context.Context, tenantID, siteID gocql.UUID, input *pageusecase.SyncPagesInput) (*pageusecase.SyncPagesOutput, error)
}
type syncPagesService struct {
// Focused usecases
validateSiteUC *pageusecase.ValidateSiteUseCase
ensureIndexUC *pageusecase.EnsureSearchIndexUseCase
createPageUC *pageusecase.CreatePageEntityUseCase
upsertPageUC *pageusecase.UpsertPageUseCase
indexPageUC *pageusecase.IndexPageToSearchUseCase
updateUsageUC *pageusecase.UpdateSiteUsageUseCase
logger *zap.Logger
}
// NewSyncPagesService creates a new SyncPagesService
func NewSyncPagesService(
validateSiteUC *pageusecase.ValidateSiteUseCase,
ensureIndexUC *pageusecase.EnsureSearchIndexUseCase,
createPageUC *pageusecase.CreatePageEntityUseCase,
upsertPageUC *pageusecase.UpsertPageUseCase,
indexPageUC *pageusecase.IndexPageToSearchUseCase,
updateUsageUC *pageusecase.UpdateSiteUsageUseCase,
logger *zap.Logger,
) SyncPagesService {
return &syncPagesService{
validateSiteUC: validateSiteUC,
ensureIndexUC: ensureIndexUC,
createPageUC: createPageUC,
upsertPageUC: upsertPageUC,
indexPageUC: indexPageUC,
updateUsageUC: updateUsageUC,
logger: logger.Named("sync-pages-service"),
}
}
// SyncPages orchestrates the page synchronization workflow
func (s *syncPagesService) SyncPages(ctx context.Context, tenantID, siteID gocql.UUID, input *pageusecase.SyncPagesInput) (*pageusecase.SyncPagesOutput, error) {
s.logger.Info("syncing pages",
zap.String("tenant_id", tenantID.String()),
zap.String("site_id", siteID.String()),
zap.Int("page_count", len(input.Pages)))
// Step 1: Validate site (no quota check - usage-based billing)
site, err := s.validateSiteUC.Execute(ctx, tenantID, siteID)
if err != nil {
s.logger.Error("failed to validate site", zap.Error(err))
return nil, err
}
// Step 2: Ensure search index exists
if err := s.ensureIndexUC.Execute(ctx, siteID); err != nil {
s.logger.Error("failed to ensure search index", zap.Error(err))
return nil, err
}
// Step 3: Process pages (create, save, prepare for indexing)
syncedCount, failedPages, pagesToIndex := s.processPages(ctx, siteID, site.TenantID, input.Pages)
// Step 4: Bulk index pages to search
indexedCount, err := s.indexPageUC.Execute(ctx, siteID, pagesToIndex)
if err != nil {
s.logger.Error("failed to index pages", zap.Error(err))
return nil, err
}
// Step 5: Update site usage tracking (for billing)
if indexedCount > 0 {
if err := s.updateUsageUC.Execute(ctx, site, indexedCount); err != nil {
s.logger.Warn("failed to update usage (non-fatal)", zap.Error(err))
// Don't fail the whole operation
}
}
// Step 6: Build output
message := fmt.Sprintf("Successfully synced %d pages, indexed %d pages", syncedCount, indexedCount)
if len(failedPages) > 0 {
message += fmt.Sprintf(", failed %d pages", len(failedPages))
}
s.logger.Info("pages synced successfully",
zap.String("site_id", siteID.String()),
zap.Int("synced", syncedCount),
zap.Int("indexed", indexedCount),
zap.Int("failed", len(failedPages)))
return &pageusecase.SyncPagesOutput{
SyncedCount: syncedCount,
IndexedCount: indexedCount,
FailedPages: failedPages,
Message: message,
}, nil
}
// Helper: Process pages - create entities, save to DB, collect pages to index
func (s *syncPagesService) processPages(
ctx context.Context,
siteID, tenantID gocql.UUID,
pages []pageusecase.SyncPageInput,
) (int, []string, []*domainpage.Page) {
syncedCount := 0
var failedPages []string
var pagesToIndex []*domainpage.Page
for _, pageInput := range pages {
// Create page entity (usecase)
page, err := s.createPageUC.Execute(siteID, tenantID, pageInput)
if err != nil {
failedPages = append(failedPages, pageInput.PageID)
continue
}
// Save to database (usecase)
if err := s.upsertPageUC.Execute(ctx, page); err != nil {
failedPages = append(failedPages, pageInput.PageID)
continue
}
syncedCount++
// Collect pages that should be indexed
if page.ShouldIndex() {
pagesToIndex = append(pagesToIndex, page)
}
}
return syncedCount, failedPages, pagesToIndex
}