← back to jearl4__PhotoGallery

Function bodies 1,000 total

All specs Real LLM only Function bodies
handlers.SubscriptionHandler.CheckFeature method · go · L338-L365 (28 LOC)
backend/internal/api/handlers/subscription.go
func (h *SubscriptionHandler) CheckFeature(w http.ResponseWriter, r *http.Request) {
	userID, ok := r.Context().Value("userID").(string)
	if !ok || userID == "" {
		respondWithError(w, http.StatusUnauthorized, "Unauthorized")
		return
	}

	feature := r.URL.Query().Get("feature")
	if feature == "" {
		respondWithError(w, http.StatusBadRequest, "Feature parameter is required")
		return
	}

	hasFeature, err := h.subscriptionService.HasFeature(r.Context(), userID, feature)
	if err != nil {
		logger.Error("Failed to check feature", map[string]interface{}{
			"userID":  userID,
			"feature": feature,
			"error":   err.Error(),
		})
		respondWithError(w, http.StatusInternalServerError, "Failed to check feature access")
		return
	}

	respondWithJSON(w, http.StatusOK, map[string]bool{
		"hasAccess": hasFeature,
	})
}
handlers.NewWatermarkHandler function · go · L37-L44 (8 LOC)
backend/internal/api/handlers/watermark.go
func NewWatermarkHandler(storageSvc WatermarkStorageService, photographerSvc PhotographerUpdater, subChecker SubscriptionFeatureChecker, originalBucket string) *WatermarkHandler {
	return &WatermarkHandler{
		storageSvc:      storageSvc,
		photographerSvc: photographerSvc,
		subChecker:      subChecker,
		originalBucket:  originalBucket,
	}
}
handlers.WatermarkHandler.requireFullWatermarkFeature method · go · L53-L90 (38 LOC)
backend/internal/api/handlers/watermark.go
func (h *WatermarkHandler) requireFullWatermarkFeature(ctx context.Context, w http.ResponseWriter, userID string) bool {
	if h.subChecker == nil {
		logger.Error("subscriptionChecker is nil, denying feature access", map[string]interface{}{
			"photographerId": userID,
			"feature":        "full_watermark",
		})
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusServiceUnavailable)
		json.NewEncoder(w).Encode(map[string]interface{}{
			"error":   "service_unavailable",
			"message": "Unable to verify feature access. Please try again later.",
		})
		return false
	}

	hasFeature, err := h.subChecker.HasFeature(ctx, userID, "full_watermark")
	if err != nil {
		logger.Error("Failed to check full_watermark feature", map[string]interface{}{
			"photographerId": userID,
			"error":          err.Error(),
		})
		respondError(w, errors.NewInternalServer("Failed to check feature access"))
		return false
	}

	if !hasFeature {
		w.Header().Set("Content-Type", "applicatio
handlers.WatermarkHandler.GetStatus method · go · L94-L138 (45 LOC)
backend/internal/api/handlers/watermark.go
func (h *WatermarkHandler) GetStatus(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	userID, ok := ctx.Value("userID").(string)
	if !ok || userID == "" {
		respondError(w, errors.NewUnauthorized("User ID not found"))
		return
	}

	if !h.requireFullWatermarkFeature(ctx, w, userID) {
		return
	}

	p, err := h.photographerSvc.GetByID(ctx, userID)
	if err != nil {
		logger.Error("Failed to get photographer for watermark status", map[string]interface{}{
			"photographerId": userID,
			"error":          err.Error(),
		})
		respondError(w, errors.NewInternalServer("Failed to get watermark status"))
		return
	}

	key := ""
	viewURL := ""
	if p != nil && p.WatermarkImageKey != "" {
		key = p.WatermarkImageKey
		url, err := h.storageSvc.GenerateDownloadURL(ctx, key, h.originalBucket, "watermark.png")
		if err != nil {
			logger.Error("Failed to generate watermark view URL", map[string]interface{}{
				"photographerId": userID,
				"key":            key,
				"error":          err.
handlers.WatermarkHandler.GetUploadURL method · go · L143-L176 (34 LOC)
backend/internal/api/handlers/watermark.go
func (h *WatermarkHandler) GetUploadURL(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	userID, ok := ctx.Value("userID").(string)
	if !ok || userID == "" {
		respondError(w, errors.NewUnauthorized("User ID not found"))
		return
	}

	if !h.requireFullWatermarkFeature(ctx, w, userID) {
		return
	}

	key := watermarkKey(userID)
	resp, err := h.storageSvc.GenerateUploadURL(ctx, storage.UploadURLRequest{
		GalleryID: "watermarks",
		PhotoID:   userID,
		FileName:  "watermark.png",
		MimeType:  "image/png",
	})
	if err != nil {
		logger.Error("Failed to generate watermark upload URL", map[string]interface{}{
			"photographerId": userID,
			"error":          err.Error(),
		})
		respondError(w, errors.NewInternalServer("Failed to generate upload URL"))
		return
	}

	respondJSON(w, http.StatusOK, map[string]string{
		"uploadUrl": resp.URL,
		"key":       key,
	})
}
handlers.WatermarkHandler.ConfirmUpload method · go · L180-L211 (32 LOC)
backend/internal/api/handlers/watermark.go
func (h *WatermarkHandler) ConfirmUpload(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	userID, ok := ctx.Value("userID").(string)
	if !ok || userID == "" {
		respondError(w, errors.NewUnauthorized("User ID not found"))
		return
	}

	if !h.requireFullWatermarkFeature(ctx, w, userID) {
		return
	}

	key := watermarkKey(userID)
	if err := h.photographerSvc.UpdateWatermarkImageKey(ctx, userID, key); err != nil {
		logger.Error("Failed to confirm watermark upload", map[string]interface{}{
			"photographerId": userID,
			"error":          err.Error(),
		})
		respondError(w, errors.NewInternalServer("Failed to confirm watermark upload"))
		return
	}

	logger.Info("Watermark image confirmed", map[string]interface{}{
		"photographerId": userID,
		"key":            key,
	})

	respondJSON(w, http.StatusOK, map[string]string{
		"key": key,
	})
}
handlers.WatermarkHandler.Delete method · go · L215-L268 (54 LOC)
backend/internal/api/handlers/watermark.go
func (h *WatermarkHandler) Delete(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	userID, ok := ctx.Value("userID").(string)
	if !ok || userID == "" {
		respondError(w, errors.NewUnauthorized("User ID not found"))
		return
	}

	if !h.requireFullWatermarkFeature(ctx, w, userID) {
		return
	}

	// Check that photographer has a watermark image
	p, err := h.photographerSvc.GetByID(ctx, userID)
	if err != nil {
		logger.Error("Failed to get photographer for watermark deletion", map[string]interface{}{
			"photographerId": userID,
			"error":          err.Error(),
		})
		respondError(w, errors.NewInternalServer("Failed to get photographer"))
		return
	}
	if p == nil || p.WatermarkImageKey == "" {
		respondError(w, errors.NewNotFound("Watermark image"))
		return
	}

	// Delete from S3
	if err := h.storageSvc.DeleteObject(ctx, h.originalBucket, p.WatermarkImageKey); err != nil {
		logger.Error("Failed to delete watermark image from S3", map[string]interface{}{
			"photographerI
Repobility · open methodology · https://repobility.com/research/
middleware.AuthMiddleware.VerifyPhotographerToken method · go · L28-L63 (36 LOC)
backend/internal/api/middleware/auth.go
func (m *AuthMiddleware) VerifyPhotographerToken(ctx context.Context, req events.APIGatewayProxyRequest) (context.Context, error) {
	// Get authorization header
	authHeader := req.Headers["Authorization"]
	if authHeader == "" {
		authHeader = req.Headers["authorization"]
	}

	if authHeader == "" {
		logger.Warn("Missing authorization header", nil)
		return ctx, errors.NewUnauthorized("Missing authorization header")
	}

	// Extract token
	token, err := cognitoAuth.ExtractToken(authHeader)
	if err != nil {
		return ctx, err
	}

	// Verify token
	claims, err := m.authService.VerifyToken(ctx, token)
	if err != nil {
		return ctx, err
	}

	// Add user info to context
	ctx = context.WithValue(ctx, "userID", claims.CognitoUsername)
	ctx = context.WithValue(ctx, "email", claims.Email)
	ctx = context.WithValue(ctx, "name", claims.Name)

	// SECURITY FIX: Don't log PII (email) - only log anonymized identifiers
	logger.Info("Photographer authenticated", map[string]interface{}{
		"userId": claims.
middleware.SessionMiddleware.VerifyClientSession method · go · L80-L132 (53 LOC)
backend/internal/api/middleware/auth.go
func (m *SessionMiddleware) VerifyClientSession(ctx context.Context, req events.APIGatewayProxyRequest) (context.Context, error) {
	var token string

	// First, try to get token from httpOnly cookie (more secure)
	cookieHeader := req.Headers["Cookie"]
	if cookieHeader == "" {
		cookieHeader = req.Headers["cookie"]
	}

	if cookieHeader != "" {
		if cookieToken, ok := security.GetCookieValue(cookieHeader, security.SessionCookieName); ok && cookieToken != "" {
			token = cookieToken
			logger.Info("Using session token from cookie", nil)
		}
	}

	// Fall back to Authorization header for backwards compatibility
	if token == "" {
		authHeader := req.Headers["Authorization"]
		if authHeader == "" {
			authHeader = req.Headers["authorization"]
		}

		if authHeader == "" {
			logger.Warn("Missing session token", nil)
			return ctx, errors.NewUnauthorized("Missing session token")
		}

		// Extract token (format: "Bearer <token>")
		var err error
		token, err = cognitoAuth.ExtractToken(authHeader
middleware.ValidateOrigin function · go · L12-L32 (21 LOC)
backend/internal/api/middleware/cors.go
func ValidateOrigin(requestOrigin string, allowedOrigins []string) string {
	if requestOrigin == "" {
		return ""
	}

	for _, allowed := range allowedOrigins {
		// Exact match
		if allowed == requestOrigin {
			return requestOrigin
		}
		// Wildcard subdomain match (e.g., "*.example.com")
		if strings.HasPrefix(allowed, "*.") {
			suffix := allowed[1:] // Remove the "*" to get ".example.com"
			if strings.HasSuffix(requestOrigin, suffix) {
				return requestOrigin
			}
		}
	}

	return ""
}
middleware.ParseAllowedOrigins function · go · L36-L50 (15 LOC)
backend/internal/api/middleware/cors.go
func ParseAllowedOrigins(allowedOriginsStr string) []string {
	if allowedOriginsStr == "" {
		return []string{}
	}

	origins := strings.Split(allowedOriginsStr, ",")
	result := make([]string, 0, len(origins))
	for _, origin := range origins {
		trimmed := strings.TrimSpace(origin)
		if trimmed != "" {
			result = append(result, trimmed)
		}
	}
	return result
}
middleware.AddCORSHeaders function · go · L55-L75 (21 LOC)
backend/internal/api/middleware/cors.go
func AddCORSHeaders(response events.APIGatewayProxyResponse, requestOrigin string, allowedOrigins []string) events.APIGatewayProxyResponse {
	if response.Headers == nil {
		response.Headers = make(map[string]string)
	}

	// Validate the origin against the whitelist
	validatedOrigin := ValidateOrigin(requestOrigin, allowedOrigins)

	if validatedOrigin != "" {
		response.Headers["Access-Control-Allow-Origin"] = validatedOrigin
		response.Headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
		response.Headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization, X-Requested-With"
		response.Headers["Access-Control-Max-Age"] = "3600"
		response.Headers["Access-Control-Allow-Credentials"] = "true"
		// Vary header is important when the response depends on the Origin header
		response.Headers["Vary"] = "Origin"
	}
	// If origin is not valid, don't set CORS headers - browser will block the request

	return response
}
middleware.AddCORSHeadersLegacy function · go · L80-L91 (12 LOC)
backend/internal/api/middleware/cors.go
func AddCORSHeadersLegacy(response events.APIGatewayProxyResponse, allowedOrigins string) events.APIGatewayProxyResponse {
	if response.Headers == nil {
		response.Headers = make(map[string]string)
	}

	response.Headers["Access-Control-Allow-Origin"] = allowedOrigins
	response.Headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
	response.Headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization, X-Requested-With"
	response.Headers["Access-Control-Max-Age"] = "3600"

	return response
}
middleware.HandlePreflight function · go · L95-L115 (21 LOC)
backend/internal/api/middleware/cors.go
func HandlePreflight(requestOrigin string, allowedOrigins []string) events.APIGatewayProxyResponse {
	validatedOrigin := ValidateOrigin(requestOrigin, allowedOrigins)

	headers := map[string]string{
		"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
		"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
		"Access-Control-Max-Age":       "3600",
		"Vary":                         "Origin",
	}

	if validatedOrigin != "" {
		headers["Access-Control-Allow-Origin"] = validatedOrigin
		headers["Access-Control-Allow-Credentials"] = "true"
	}

	return events.APIGatewayProxyResponse{
		StatusCode: 200,
		Headers:    headers,
		Body:       "",
	}
}
middleware.HandlePreflightLegacy function · go · L119-L130 (12 LOC)
backend/internal/api/middleware/cors.go
func HandlePreflightLegacy(allowedOrigins string) events.APIGatewayProxyResponse {
	return events.APIGatewayProxyResponse{
		StatusCode: 200,
		Headers: map[string]string{
			"Access-Control-Allow-Origin":  allowedOrigins,
			"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
			"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
			"Access-Control-Max-Age":       "3600",
		},
		Body: "",
	}
}
Repobility · code-quality intelligence platform · https://repobility.com
middleware.NewDomainMiddleware function · go · L21-L26 (6 LOC)
backend/internal/api/middleware/domain.go
func NewDomainMiddleware(domainService *customdomain.Service, baseDomain string) *DomainMiddleware {
	return &DomainMiddleware{
		domainService: domainService,
		baseDomain:    baseDomain,
	}
}
middleware.DomainMiddleware.ResolvePhotographer method · go · L31-L79 (49 LOC)
backend/internal/api/middleware/domain.go
func (m *DomainMiddleware) ResolvePhotographer(ctx context.Context, req events.APIGatewayProxyRequest) (context.Context, error) {
	// Get host from headers (try multiple header names for compatibility)
	host := m.getHost(req)
	if host == "" {
		logger.Debug("No host header found", nil)
		return ctx, nil
	}

	// Clean up the host
	host = strings.ToLower(strings.TrimSpace(host))
	host = strings.Split(host, ":")[0] // Remove port if present

	logger.Debug("Resolving photographer from host", map[string]interface{}{
		"host":       host,
		"baseDomain": m.baseDomain,
	})

	// Check if this is the main domain (not a custom domain)
	if m.isMainDomain(host) {
		logger.Debug("Main domain detected, skipping photographer resolution", nil)
		return ctx, nil
	}

	// Try to resolve photographer from the host
	p, err := m.domainService.ResolvePhotographerByHost(ctx, host)
	if err != nil {
		if err == photographer.ErrNotFound {
			logger.Debug("No photographer found for host", map[string]interface{}{"
middleware.DomainMiddleware.getHost method · go · L82-L100 (19 LOC)
backend/internal/api/middleware/domain.go
func (m *DomainMiddleware) getHost(req events.APIGatewayProxyRequest) string {
	// Check X-Forwarded-Host first (set by CloudFront/Lambda@Edge)
	if host := req.Headers["X-Forwarded-Host"]; host != "" {
		return host
	}
	if host := req.Headers["x-forwarded-host"]; host != "" {
		return host
	}

	// Fall back to Host header
	if host := req.Headers["Host"]; host != "" {
		return host
	}
	if host := req.Headers["host"]; host != "" {
		return host
	}

	return ""
}
middleware.DomainMiddleware.isMainDomain method · go · L103-L130 (28 LOC)
backend/internal/api/middleware/domain.go
func (m *DomainMiddleware) isMainDomain(host string) bool {
	// Exact match
	if host == m.baseDomain {
		return true
	}

	// www subdomain
	if host == "www."+m.baseDomain {
		return true
	}

	// Localhost for development
	if strings.HasPrefix(host, "localhost") {
		return true
	}

	// API Gateway domains
	if strings.Contains(host, ".execute-api.") {
		return true
	}

	// CloudFront domains
	if strings.HasSuffix(host, ".cloudfront.net") {
		return true
	}

	return false
}
api.NewMiddlewareFactory function · go · L23-L28 (6 LOC)
backend/internal/api/middleware_factory.go
func NewMiddlewareFactory(allowedOrigins []string, jwtValidator JWTValidator) *MiddlewareFactory {
	return &MiddlewareFactory{
		allowedOrigins: allowedOrigins,
		jwtValidator:   jwtValidator,
	}
}
api.MiddlewareFactory.CORS method · go · L31-L82 (52 LOC)
backend/internal/api/middleware_factory.go
func (f *MiddlewareFactory) CORS() Middleware {
	return func(next Handler) Handler {
		return func(req *Request) (*Response, error) {
			origin := req.Headers["origin"]
			if origin == "" {
				origin = req.Headers["Origin"]
			}

			// Check if origin is allowed
			allowed := false
			for _, o := range f.allowedOrigins {
				if o == "*" || o == origin {
					allowed = true
					break
				}
			}

			// Handle preflight
			if req.Method == http.MethodOptions {
				headers := map[string]string{
					"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
					"Access-Control-Allow-Headers": "Content-Type, Authorization",
					"Access-Control-Max-Age":       "86400",
				}
				if allowed {
					headers["Access-Control-Allow-Origin"] = origin
					headers["Access-Control-Allow-Credentials"] = "true"
				}
				return &Response{
					StatusCode: http.StatusNoContent,
					Headers:    headers,
				}, nil
			}

			// Process request and add CORS headers to response
			resp, err := n
api.MiddlewareFactory.Auth method · go · L85-L122 (38 LOC)
backend/internal/api/middleware_factory.go
func (f *MiddlewareFactory) Auth() Middleware {
	return func(next Handler) Handler {
		return func(req *Request) (*Response, error) {
			authHeader := req.Headers["authorization"]
			if authHeader == "" {
				authHeader = req.Headers["Authorization"]
			}

			if authHeader == "" {
				return Unauthorized("Authorization header required"), nil
			}

			parts := strings.SplitN(authHeader, " ", 2)
			if len(parts) != 2 || strings.ToLower(parts[0]) != "bearer" {
				return Unauthorized("Invalid authorization header format"), nil
			}

			token := parts[1]
			claims, err := f.jwtValidator.ValidateToken(token)
			if err != nil {
				return Unauthorized("Invalid or expired token"), nil
			}

			// Add claims to request context
			if req.PathParams == nil {
				req.PathParams = make(map[string]string)
			}
			if userID, ok := claims["sub"].(string); ok {
				req.PathParams["__userId"] = userID
			}
			if email, ok := claims["email"].(string); ok {
				req.PathParams["__email"] = email
			}

			
api.MiddlewareFactory.Logging method · go · L125-L149 (25 LOC)
backend/internal/api/middleware_factory.go
func (f *MiddlewareFactory) Logging() Middleware {
	return func(next Handler) Handler {
		return func(req *Request) (*Response, error) {
			start := time.Now()

			resp, err := next(req)

			duration := time.Since(start)
			status := 0
			if resp != nil {
				status = resp.StatusCode
			}

			log.Printf("[%s] %s %s - %d (%v)",
				req.Method,
				req.Path,
				getClientIP(req),
				status,
				duration,
			)

			return resp, err
		}
	}
}
All rows above produced by Repobility · https://repobility.com
api.MiddlewareFactory.Recovery method · go · L152-L165 (14 LOC)
backend/internal/api/middleware_factory.go
func (f *MiddlewareFactory) Recovery() Middleware {
	return func(next Handler) Handler {
		return func(req *Request) (resp *Response, err error) {
			defer func() {
				if r := recover(); r != nil {
					log.Printf("[PANIC] %s %s: %v", req.Method, req.Path, r)
					resp = InternalError("Internal server error")
					err = nil
				}
			}()
			return next(req)
		}
	}
}
api.MiddlewareFactory.RateLimit method · go · L168-L177 (10 LOC)
backend/internal/api/middleware_factory.go
func (f *MiddlewareFactory) RateLimit(requestsPerMinute int) Middleware {
	// Note: In production, use a distributed rate limiter with Redis
	return func(next Handler) Handler {
		return func(req *Request) (*Response, error) {
			// For Lambda, rate limiting should be done at API Gateway level
			// This is a placeholder for local development
			return next(req)
		}
	}
}
api.getClientIP function · go · L180-L195 (16 LOC)
backend/internal/api/middleware_factory.go
func getClientIP(req *Request) string {
	// Check X-Forwarded-For first (common for proxied requests)
	if xff := req.Headers["X-Forwarded-For"]; xff != "" {
		parts := strings.Split(xff, ",")
		if len(parts) > 0 {
			return strings.TrimSpace(parts[0])
		}
	}
	if xff := req.Headers["x-forwarded-for"]; xff != "" {
		parts := strings.Split(xff, ",")
		if len(parts) > 0 {
			return strings.TrimSpace(parts[0])
		}
	}
	return "unknown"
}
api.Chain function · go · L198-L205 (8 LOC)
backend/internal/api/middleware_factory.go
func Chain(middlewares ...Middleware) Middleware {
	return func(final Handler) Handler {
		for i := len(middlewares) - 1; i >= 0; i-- {
			final = middlewares[i](final)
		}
		return final
	}
}
api.ConditionalMiddleware function · go · L208-L217 (10 LOC)
backend/internal/api/middleware_factory.go
func ConditionalMiddleware(condition func(*Request) bool, middleware Middleware) Middleware {
	return func(next Handler) Handler {
		return func(req *Request) (*Response, error) {
			if condition(req) {
				return middleware(next)(req)
			}
			return next(req)
		}
	}
}
api.ExcludePaths function · go · L227-L238 (12 LOC)
backend/internal/api/middleware_factory.go
func ExcludePaths(middleware Middleware, paths ...string) Middleware {
	return func(next Handler) Handler {
		return func(req *Request) (*Response, error) {
			for _, p := range paths {
				if req.Path == p {
					return next(req)
				}
			}
			return middleware(next)(req)
		}
	}
}
middleware.TierGateMiddleware.RequireTier method · go · L43-L79 (37 LOC)
backend/internal/api/middleware/tier_gate.go
func (m *TierGateMiddleware) RequireTier(minTier subscription.Tier) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			userID, ok := r.Context().Value("userID").(string)
			if !ok || userID == "" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}

			meets, err := m.subscriptionService.MeetsTierRequirement(r.Context(), userID, minTier)
			if err != nil {
				http.Error(w, "Failed to check subscription", http.StatusInternalServerError)
				return
			}

			if !meets {
				sub, _ := m.subscriptionService.GetSubscription(r.Context(), userID)
				currentTier := subscription.TierFree
				if sub != nil {
					currentTier = sub.Tier
				}

				w.Header().Set("Content-Type", "application/json")
				w.WriteHeader(http.StatusPaymentRequired)
				json.NewEncoder(w).Encode(TierGateResponse{
					Error:        "upgrade_required",
					Message:      "This feature require
middleware.TierGateMiddleware.RequireFeature method · go · L82-L111 (30 LOC)
backend/internal/api/middleware/tier_gate.go
func (m *TierGateMiddleware) RequireFeature(feature string) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			userID, ok := r.Context().Value("userID").(string)
			if !ok || userID == "" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}

			hasFeature, err := m.subscriptionService.HasFeature(r.Context(), userID, feature)
			if err != nil {
				http.Error(w, "Failed to check feature access", http.StatusInternalServerError)
				return
			}

			if !hasFeature {
				w.Header().Set("Content-Type", "application/json")
				w.WriteHeader(http.StatusPaymentRequired)
				json.NewEncoder(w).Encode(map[string]interface{}{
					"error":   "feature_not_available",
					"message": "Your subscription does not include this feature",
					"feature": feature,
				})
				return
			}

			next.ServeHTTP(w, r)
		})
	}
}
Open data scored by Repobility · https://repobility.com
middleware.TierGateMiddleware.CheckStorageLimit method · go · L124-L150 (27 LOC)
backend/internal/api/middleware/tier_gate.go
func (m *TierGateMiddleware) CheckStorageLimit(ctx context.Context, userID string, currentStorageUsed int64) (bool, *StorageLimitResponse) {
	withinLimit, limits, err := m.subscriptionService.CheckStorageLimit(ctx, userID, currentStorageUsed)
	if err != nil {
		return false, &StorageLimitResponse{
			Error:   "check_failed",
			Message: "Failed to check storage limit",
		}
	}

	if !withinLimit && limits != nil {
		sub, _ := m.subscriptionService.GetSubscription(ctx, userID)
		currentTier := "free"
		if sub != nil {
			currentTier = string(sub.Tier)
		}

		return false, &StorageLimitResponse{
			Error:        "storage_limit_exceeded",
			Message:      "You have reached your storage limit. Please upgrade your plan.",
			StorageUsed:  currentStorageUsed,
			StorageLimit: limits.StorageBytes,
			CurrentTier:  currentTier,
		}
	}

	return true, nil
}
middleware.TierGateMiddleware.CheckStorageLimitWithUpload method · go · L154-L180 (27 LOC)
backend/internal/api/middleware/tier_gate.go
func (m *TierGateMiddleware) CheckStorageLimitWithUpload(ctx context.Context, userID string, currentStorageUsed int64, uploadSizeBytes int64) (bool, *StorageLimitResponse) {
	withinLimit, limits, err := m.subscriptionService.CheckStorageLimitWithUpload(ctx, userID, currentStorageUsed, uploadSizeBytes)
	if err != nil {
		return false, &StorageLimitResponse{
			Error:   "check_failed",
			Message: "Failed to check storage limit",
		}
	}

	if !withinLimit && limits != nil {
		sub, _ := m.subscriptionService.GetSubscription(ctx, userID)
		currentTier := "free"
		if sub != nil {
			currentTier = string(sub.Tier)
		}

		return false, &StorageLimitResponse{
			Error:        "storage_limit_exceeded",
			Message:      "This upload would exceed your storage limit. Please upgrade your plan.",
			StorageUsed:  currentStorageUsed,
			StorageLimit: limits.StorageBytes,
			CurrentTier:  currentTier,
		}
	}

	return true, nil
}
middleware.TierGateMiddleware.CheckGalleryLimit method · go · L193-L219 (27 LOC)
backend/internal/api/middleware/tier_gate.go
func (m *TierGateMiddleware) CheckGalleryLimit(ctx context.Context, userID string, currentGalleryCount int) (bool, *GalleryLimitResponse) {
	withinLimit, limits, err := m.subscriptionService.CheckGalleryLimit(ctx, userID, currentGalleryCount)
	if err != nil {
		return false, &GalleryLimitResponse{
			Error:   "check_failed",
			Message: "Failed to check gallery limit",
		}
	}

	if !withinLimit && limits != nil {
		sub, _ := m.subscriptionService.GetSubscription(ctx, userID)
		currentTier := "free"
		if sub != nil {
			currentTier = string(sub.Tier)
		}

		return false, &GalleryLimitResponse{
			Error:        "gallery_limit_exceeded",
			Message:      "You have reached your gallery limit. Please upgrade your plan.",
			GalleryCount: currentGalleryCount,
			GalleryLimit: limits.MaxGalleries,
			CurrentTier:  currentTier,
		}
	}

	return true, nil
}
middleware.TierGateMiddleware.CheckPhotosPerGalleryLimit method · go · L232-L258 (27 LOC)
backend/internal/api/middleware/tier_gate.go
func (m *TierGateMiddleware) CheckPhotosPerGalleryLimit(ctx context.Context, userID string, currentPhotoCount int) (bool, *PhotosPerGalleryLimitResponse) {
	withinLimit, limits, err := m.subscriptionService.CheckPhotosPerGalleryLimit(ctx, userID, currentPhotoCount)
	if err != nil {
		return false, &PhotosPerGalleryLimitResponse{
			Error:   "check_failed",
			Message: "Failed to check photos per gallery limit",
		}
	}

	if !withinLimit && limits != nil {
		sub, _ := m.subscriptionService.GetSubscription(ctx, userID)
		currentTier := "free"
		if sub != nil {
			currentTier = string(sub.Tier)
		}

		return false, &PhotosPerGalleryLimitResponse{
			Error:       "photo_limit_exceeded",
			Message:     "You have reached your photo limit for this gallery. Please upgrade your plan.",
			PhotoCount:  currentPhotoCount,
			PhotoLimit:  limits.MaxPhotosPerGallery,
			CurrentTier: currentTier,
		}
	}

	return true, nil
}
api.NewRouter function · go · L52-L63 (12 LOC)
backend/internal/api/router.go
func NewRouter() *Router {
	return &Router{
		routes:      make([]Route, 0),
		middlewares: make([]Middleware, 0),
		notFound: func(req *Request) (*Response, error) {
			return &Response{
				StatusCode: http.StatusNotFound,
				Body:       map[string]string{"message": "Not found"},
			}, nil
		},
	}
}
api.Router.Handle method · go · L72-L79 (8 LOC)
backend/internal/api/router.go
func (r *Router) Handle(method, pattern string, handler Handler) *Router {
	r.routes = append(r.routes, Route{
		Method:  method,
		Pattern: pattern,
		Handler: handler,
	})
	return r
}
api.Router.matchRoute method · go · L108-L127 (20 LOC)
backend/internal/api/router.go
func (r *Router) matchRoute(pattern, path string) (bool, map[string]string) {
	patternParts := strings.Split(strings.Trim(pattern, "/"), "/")
	pathParts := strings.Split(strings.Trim(path, "/"), "/")

	if len(patternParts) != len(pathParts) {
		return false, nil
	}

	params := make(map[string]string)
	for i, part := range patternParts {
		if strings.HasPrefix(part, "{") && strings.HasSuffix(part, "}") {
			paramName := part[1 : len(part)-1]
			params[paramName] = pathParts[i]
		} else if part != pathParts[i] {
			return false, nil
		}
	}

	return true, params
}
api.Router.Route method · go · L130-L144 (15 LOC)
backend/internal/api/router.go
func (r *Router) Route(req *Request) (*Response, error) {
	for _, route := range r.routes {
		if route.Method != req.Method {
			continue
		}

		if match, params := r.matchRoute(route.Pattern, req.Path); match {
			req.PathParams = params
			handler := r.applyMiddleware(route.Handler)
			return handler(req)
		}
	}

	return r.notFound(req)
}
Repobility · open methodology · https://repobility.com/research/
api.Router.applyMiddleware method · go · L147-L152 (6 LOC)
backend/internal/api/router.go
func (r *Router) applyMiddleware(handler Handler) Handler {
	for i := len(r.middlewares) - 1; i >= 0; i-- {
		handler = r.middlewares[i](handler)
	}
	return handler
}
api.Router.HandleLambda method · go · L155-L200 (46 LOC)
backend/internal/api/router.go
func (r *Router) HandleLambda(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	req := &Request{
		Method:      event.HTTPMethod,
		Path:        event.Path,
		PathParams:  event.PathParameters,
		QueryParams: event.QueryStringParameters,
		Headers:     event.Headers,
		Body:        event.Body,
		Context:     ctx,
	}

	resp, err := r.Route(req)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusInternalServerError,
			Body:       `{"message": "Internal server error"}`,
			Headers: map[string]string{
				"Content-Type": "application/json",
			},
		}, nil
	}

	body, err := json.Marshal(resp.Body)
	if err != nil {
		return events.APIGatewayProxyResponse{
			StatusCode: http.StatusInternalServerError,
			Body:       `{"message": "Failed to serialize response"}`,
			Headers: map[string]string{
				"Content-Type": "application/json",
			},
		}, nil
	}

	headers := map[string]string{
		"Content-Type": "applicat
api.JSON function · go · L203-L211 (9 LOC)
backend/internal/api/router.go
func JSON(status int, body interface{}) *Response {
	return &Response{
		StatusCode: status,
		Body:       body,
		Headers: map[string]string{
			"Content-Type": "application/json",
		},
	}
}
config.Load function · go · L59-L96 (38 LOC)
backend/internal/config/config.go
func Load() *Config {
	return &Config{
		AWSRegion:           getEnv("AWS_REGION", "us-east-1"),
		DynamoDBTablePrefix: getEnv("DYNAMODB_TABLE_PREFIX", "photographer-gallery"),
		S3BucketOriginal:    getEnv("S3_BUCKET_ORIGINAL", ""),
		S3BucketOptimized:   getEnv("S3_BUCKET_OPTIMIZED", ""),
		S3BucketThumbnail:   getEnv("S3_BUCKET_THUMBNAIL", ""),
		CloudFrontDomain:    getEnv("CLOUDFRONT_DOMAIN", ""),
		CloudFrontKeyPairID: getEnv("CLOUDFRONT_KEY_PAIR_ID", ""),
		CloudFrontKeyPath:   getEnv("CLOUDFRONT_KEY_PATH", ""),
		CognitoUserPoolID:   getEnv("COGNITO_USER_POOL_ID", ""),
		CognitoClientID:     getEnv("COGNITO_CLIENT_ID", ""),
		CognitoRegion:       getEnv("COGNITO_REGION", "us-east-1"),
		APIStage:            getEnv("API_STAGE", "dev"),
		AllowedOrigins:      getEnv("ALLOWED_ORIGINS", "*"),
		SQSQueueURL:         getEnv("SQS_QUEUE_URL", ""),
		ZipSQSQueueURL:      getEnv("ZIP_SQS_QUEUE_URL", ""),
		SessionTTLHours:     getEnvAsInt("SESSION_TTL_HOURS", 24),
		CookieDomain:        
config.Config.GetPriceToTierMap method · go · L99-L122 (24 LOC)
backend/internal/config/config.go
func (c *Config) GetPriceToTierMap() map[string]string {
	m := make(map[string]string)
	// Monthly prices (3-tier system: Starter, Pro, Studio)
	if c.StripePriceStarterMonthly != "" {
		m[c.StripePriceStarterMonthly] = "starter"
	}
	if c.StripePriceProMonthly != "" {
		m[c.StripePriceProMonthly] = "pro"
	}
	if c.StripePriceStudioMonthly != "" {
		m[c.StripePriceStudioMonthly] = "studio"
	}
	// Yearly prices
	if c.StripePriceStarterYearly != "" {
		m[c.StripePriceStarterYearly] = "starter"
	}
	if c.StripePriceProYearly != "" {
		m[c.StripePriceProYearly] = "pro"
	}
	if c.StripePriceStudioYearly != "" {
		m[c.StripePriceStudioYearly] = "studio"
	}
	return m
}
config.getEnv function · go · L124-L130 (7 LOC)
backend/internal/config/config.go
func getEnv(key, defaultValue string) string {
	value := os.Getenv(key)
	if value == "" {
		return defaultValue
	}
	return value
}
config.getEnvAsInt function · go · L132-L141 (10 LOC)
backend/internal/config/config.go
func getEnvAsInt(key string, defaultValue int) int {
	valueStr := os.Getenv(key)
	if valueStr == "" {
		return defaultValue
	}
	// Simple conversion, in production use strconv.Atoi with error handling
	value := defaultValue
	_, _ = fmt.Sscanf(valueStr, "%d", &value)
	return value
}
config.NewProcessorConfigBuilder function · go · L26-L31 (6 LOC)
backend/internal/config/processor_config.go
func NewProcessorConfigBuilder() *ProcessorConfigBuilder {
	return &ProcessorConfigBuilder{
		config: &ProcessorConfig{},
		errors: []string{},
	}
}
Repobility · code-quality intelligence platform · https://repobility.com
config.ProcessorConfigBuilder.FromEnvironment method · go · L34-L42 (9 LOC)
backend/internal/config/processor_config.go
func (b *ProcessorConfigBuilder) FromEnvironment() *ProcessorConfigBuilder {
	b.config.AWSRegion = os.Getenv("AWS_REGION_NAME")
	b.config.DynamoDBTablePrefix = os.Getenv("DYNAMODB_TABLE_PREFIX")
	b.config.S3BucketOriginal = os.Getenv("S3_BUCKET_ORIGINAL")
	b.config.S3BucketOptimized = os.Getenv("S3_BUCKET_OPTIMIZED")
	b.config.S3BucketThumbnail = os.Getenv("S3_BUCKET_THUMBNAIL")
	b.config.APIStage = os.Getenv("STAGE")
	return b
}
config.ProcessorConfigBuilder.WithS3Buckets method · go · L57-L62 (6 LOC)
backend/internal/config/processor_config.go
func (b *ProcessorConfigBuilder) WithS3Buckets(original, optimized, thumbnail string) *ProcessorConfigBuilder {
	b.config.S3BucketOriginal = original
	b.config.S3BucketOptimized = optimized
	b.config.S3BucketThumbnail = thumbnail
	return b
}
config.ProcessorConfigBuilder.Build method · go · L71-L79 (9 LOC)
backend/internal/config/processor_config.go
func (b *ProcessorConfigBuilder) Build() (*ProcessorConfig, error) {
	b.validate()

	if len(b.errors) > 0 {
		return nil, fmt.Errorf("configuration errors: %v", b.errors)
	}

	return b.config, nil
}
‹ prevpage 4 / 20next ›