// monorepo/cloud/backend/internal/maplefile/interface/http/collection/sync.go package collection import ( "encoding/json" "net/http" "strconv" "go.uber.org/zap" "github.com/gocql/gocql" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config/constants" dom_sync "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/interface/http/middleware" svc_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/service/collection" "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror" ) type CollectionSyncHTTPHandler struct { config *config.Configuration logger *zap.Logger service svc_collection.GetCollectionSyncDataService middleware middleware.Middleware } func NewCollectionSyncHTTPHandler( config *config.Configuration, logger *zap.Logger, service svc_collection.GetCollectionSyncDataService, middleware middleware.Middleware, ) *CollectionSyncHTTPHandler { logger = logger.Named("CollectionSyncHTTPHandler") return &CollectionSyncHTTPHandler{ config: config, logger: logger, service: service, middleware: middleware, } } func (*CollectionSyncHTTPHandler) Pattern() string { return "POST /api/v1/collections/sync" } func (h *CollectionSyncHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Apply middleware before handling the request h.middleware.Attach(h.Execute)(w, req) } func (h *CollectionSyncHTTPHandler) Execute(w http.ResponseWriter, r *http.Request) { // Set response content type w.Header().Set("Content-Type", "application/json") ctx := r.Context() // Get user ID from context userID, ok := ctx.Value(constants.SessionUserID).(gocql.UUID) if !ok { h.logger.Error("Failed getting user ID from context") httperror.RespondWithError(w, r, httperror.NewForInternalServerErrorWithSingleField("message", "Authentication context error")) return } // Parse query parameters queryParams := r.URL.Query() // Parse limit parameter (default: 1000, max: 5000) limit := int64(1000) if limitStr := queryParams.Get("limit"); limitStr != "" { if parsedLimit, err := strconv.ParseInt(limitStr, 10, 64); err == nil { if parsedLimit > 0 && parsedLimit <= 5000 { limit = parsedLimit } else { h.logger.Warn("Invalid limit parameter, using default", zap.String("limit", limitStr), zap.Int64("default", limit)) } } else { h.logger.Warn("Failed to parse limit parameter, using default", zap.String("limit", limitStr), zap.Error(err)) } } // Parse cursor parameter var cursor *dom_sync.CollectionSyncCursor if cursorStr := queryParams.Get("cursor"); cursorStr != "" { var parsedCursor dom_sync.CollectionSyncCursor if err := json.Unmarshal([]byte(cursorStr), &parsedCursor); err != nil { h.logger.Error("Failed to parse cursor parameter", zap.String("cursor", cursorStr), zap.Error(err)) httperror.RespondWithError(w, r, httperror.NewForBadRequestWithSingleField("cursor", "Invalid cursor format")) return } cursor = &parsedCursor } h.logger.Debug("Processing collection sync request", zap.Any("user_id", userID), zap.Int64("limit", limit), zap.Any("cursor", cursor)) // Call service to get sync data response, err := h.service.Execute(ctx, userID, cursor, limit, "all") if err != nil { h.logger.Error("Failed to get collection sync data", zap.Any("user_id", userID), zap.Error(err)) httperror.RespondWithError(w, r, err) return } // Encode and return response if err := json.NewEncoder(w).Encode(response); err != nil { h.logger.Error("Failed to encode collection sync response", zap.Error(err)) httperror.RespondWithError(w, r, err) return } h.logger.Info("Successfully served collection sync data", zap.Any("user_id", userID), zap.Int("collections_count", len(response.Collections)), zap.Bool("has_more", response.HasMore)) }