← back to jearl4__PhotoGallery

Function bodies 1,000 total

All specs Real LLM only Function bodies
main.main function · go · L22-L154 (133 LOC)
backend/cmd/admin-fix/main.go
func main() {
	userID := flag.String("user-id", "", "User ID to fix (required)")
	stripeCustomerID := flag.String("stripe-customer-id", "", "Stripe customer ID to set (required)")
	sync := flag.Bool("sync", true, "Sync from Stripe after fix")
	flag.Parse()

	if *userID == "" || *stripeCustomerID == "" {
		fmt.Println("Usage: admin-fix -user-id=<user_id> -stripe-customer-id=<cus_xxx>")
		flag.PrintDefaults()
		os.Exit(1)
	}

	ctx := context.Background()

	// Load configuration
	cfg := appConfig.Load()

	// Load AWS config
	awsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(cfg.AWSRegion))
	if err != nil {
		logger.Error("Failed to load AWS config", map[string]interface{}{"error": err.Error()})
		os.Exit(1)
	}

	// Initialize DynamoDB client
	dynamoClient := dynamodb.NewFromConfig(awsCfg)

	// Initialize subscription repository
	subRepo := dynamodbRepo.NewSubscriptionRepository(
		dynamoClient,
		fmt.Sprintf("%s-subscriptions-%s", cfg.DynamoDBTablePrefix, cfg.APIStage),
		fmt
main.main function · go · L54-L62 (9 LOC)
backend/cmd/api/main.go
func main() {
	app, err := initializeApp()
	if err != nil {
		logger.Error("Failed to initialize app", map[string]interface{}{"error": err.Error()})
		os.Exit(1)
	}

	lambda.Start(app.handler)
}
main.initializeApp function · go · L64-L107 (44 LOC)
backend/cmd/api/main.go
func initializeApp() (*App, error) {
	ctx := context.Background()
	cfg := appConfig.Load()

	awsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(cfg.AWSRegion))
	if err != nil {
		return nil, fmt.Errorf("failed to load AWS config: %w", err)
	}

	// Load secrets from Secrets Manager
	secretsARN := os.Getenv("SECRETS_ARN")
	if secretsARN == "" {
		return nil, fmt.Errorf("SECRETS_ARN environment variable is required")
	}

	secretsMgr := secrets.NewManager(awsCfg, secretsARN)
	appSecrets, err := secretsMgr.Load(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to load secrets: %w", err)
	}

	logger.Info("Secrets loaded from Secrets Manager", nil)

	// Initialize infrastructure
	dynamoClient := dynamodb.NewFromConfig(awsCfg)
	s3Client := s3.NewFromConfig(awsCfg)
	sqsClient := sqs.NewFromConfig(awsCfg)

	// Initialize repositories
	repos := initRepositories(dynamoClient, cfg)

	// Initialize services
	services := initServices(s3Client, sqsClient, repos, cfg, appSecrets)

	// 
main.initRepositories function · go · L124-L145 (22 LOC)
backend/cmd/api/main.go
func initRepositories(client *dynamodb.Client, cfg *appConfig.Config) *repositories {
	prefix := cfg.DynamoDBTablePrefix
	stage := cfg.APIStage
	return &repositories{
		gallery:      dynamodbRepo.NewGalleryRepository(client, fmt.Sprintf("%s-galleries-%s", prefix, stage)),
		photo:        dynamodbRepo.NewPhotoRepository(client, fmt.Sprintf("%s-photos-%s", prefix, stage)),
		favorite:     dynamodbRepo.NewFavoriteRepository(client, fmt.Sprintf("%s-favorites-%s", prefix, stage)),
		session:      dynamodbRepo.NewClientSessionRepository(client, fmt.Sprintf("%s-sessions-%s", prefix, stage)),
		photographer: dynamodbRepo.NewPhotographerRepository(client, fmt.Sprintf("%s-photographers-%s", prefix, stage)),
		subscription: dynamodbRepo.NewSubscriptionRepository(
			client,
			fmt.Sprintf("%s-subscriptions-%s", prefix, stage),
			fmt.Sprintf("%s-subscription-history-%s", prefix, stage),
		),
		zipJob:              dynamodbRepo.NewZipJobRepository(client, fmt.Sprintf("%s-zip-jobs-%s", prefix, stag
main.initServices function · go · L166-L293 (128 LOC)
backend/cmd/api/main.go
func initServices(s3Client *s3.Client, sqsClient *sqs.Client, repos *repositories, cfg *appConfig.Config, appSecrets *secrets.AppSecrets) *services {
	storageService := storage.NewService(
		s3Client,
		cfg.S3BucketOriginal,
		cfg.S3BucketOptimized,
		cfg.S3BucketThumbnail,
		time.Duration(cfg.SignedURLExpiration)*time.Hour,
	)

	// JWT secret is loaded from Secrets Manager (validated during load)
	jwtSecret := appSecrets.JWTSecret

	// Get base domain from environment or use default
	baseDomain := os.Getenv("BASE_DOMAIN")
	if baseDomain == "" {
		baseDomain = "framefocal.com"
	}

	// Initialize event bus
	eventBus := pkgEvents.NewEventBus()

	// Initialize analytics service
	photographerRepoAdapter := &analytics.PhotographerRepoAdapter{
		GetByID: func(ctx context.Context, userID string) (*photographer.Photographer, error) {
			return repos.photographer.GetByID(ctx, userID)
		},
	}
	analyticsService := analytics.NewService(
		photographerRepoAdapter,
		repos.gallery,
		repos.photo,
		
main.buildRouter function · go · L295-L482 (188 LOC)
backend/cmd/api/main.go
func buildRouter(svc *services, repos *repositories, cfg *appConfig.Config) *api.Router {
	// Get base domain
	baseDomain := os.Getenv("BASE_DOMAIN")
	if baseDomain == "" {
		baseDomain = "framefocal.com"
	}

	// Initialize tier gate middleware for limit enforcement
	var tierGate *middleware.TierGateMiddleware
	if svc.subscription != nil {
		tierGate = middleware.NewTierGateMiddleware(svc.subscription)
	}

	// Initialize handlers
	authHandler := handlers.NewAuthHandler(repos.photographer)
	galleryHandler := handlers.NewGalleryHandler(svc.gallery, tierGate, repos.photographer, svc.subscription)
	photoHandler := handlers.NewPhotoHandler(svc.photo, tierGate, repos.photographer, repos.gallery)
	clientHandler := handlers.NewClientHandler(svc.gallery, svc.photo, svc.session, svc.zipJob, svc.subscription).
		WithCookieConfig(cfg.CookieDomain, cfg.SessionTTLHours)
	domainHandler := handlers.NewDomainHandler(svc.domain)
	portalHandler := handlers.NewPortalHandler(svc.domain, svc.gallery, repos.
main.App.handler method · go · L484-L509 (26 LOC)
backend/cmd/api/main.go
func (app *App) handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
	logger.Info("Received request", map[string]interface{}{
		"method": req.HTTPMethod,
		"path":   req.Path,
	})

	// Get request origin from headers (case-insensitive)
	requestOrigin := req.Headers["Origin"]
	if requestOrigin == "" {
		requestOrigin = req.Headers["origin"]
	}

	// Parse allowed origins from config (comma-separated string)
	allowedOrigins := middleware.ParseAllowedOrigins(app.config.AllowedOrigins)

	if req.HTTPMethod == "OPTIONS" {
		return middleware.HandlePreflight(requestOrigin, allowedOrigins), nil
	}

	response, err := app.router.HandleLambda(ctx, req)
	if err != nil {
		logger.Error("Request failed", map[string]interface{}{"error": err.Error()})
	}

	return middleware.AddCORSHeaders(response, requestOrigin, allowedOrigins), nil
}
Repobility · severity-and-effort ranking · https://repobility.com
main.wrapHandler function · go · L512-L556 (45 LOC)
backend/cmd/api/main.go
func wrapHandler(fn http.HandlerFunc) api.Handler {
	return func(req *api.Request) (*api.Response, error) {
		rw := &responseCapture{statusCode: http.StatusOK, headers: http.Header{}}

		// Build HTTP request from api.Request
		httpReq, err := http.NewRequestWithContext(req.Context, "GET", req.Path, strings.NewReader(req.Body))
		if err != nil {
			return api.InternalError("failed to create request"), err
		}
		for k, v := range req.Headers {
			httpReq.Header.Set(k, v)
		}
		// Store path params in context
		ctx := httpReq.Context()
		for k, v := range req.PathParams {
			ctx = context.WithValue(ctx, k, v)
		}
		httpReq = httpReq.WithContext(ctx)

		fn(rw, httpReq)

		headers := make(map[string]string)
		for k, vals := range rw.headers {
			if len(vals) > 0 {
				headers[k] = vals[0]
			}
		}

		// Parse the JSON body back into an interface{} to avoid double-encoding
		// when HandleLambda calls json.Marshal on the response body
		var body interface{}
		if rw.body != "" {
			if err :=
main.authMiddlewareWrapper function · go · L558-L572 (15 LOC)
backend/cmd/api/main.go
func authMiddlewareWrapper(m *middleware.AuthMiddleware) api.Middleware {
	return func(next api.Handler) api.Handler {
		return func(req *api.Request) (*api.Response, error) {
			// Simulate auth verification
			ctx, err := m.VerifyPhotographerToken(req.Context, events.APIGatewayProxyRequest{
				Headers: req.Headers,
			})
			if err != nil {
				return api.Unauthorized(err.Error()), nil
			}
			req.Context = ctx
			return next(req)
		}
	}
}
main.sessionMiddlewareWrapper function · go · L574-L587 (14 LOC)
backend/cmd/api/main.go
func sessionMiddlewareWrapper(m *middleware.SessionMiddleware) api.Middleware {
	return func(next api.Handler) api.Handler {
		return func(req *api.Request) (*api.Response, error) {
			ctx, err := m.VerifyClientSession(req.Context, events.APIGatewayProxyRequest{
				Headers: req.Headers,
			})
			if err != nil {
				return api.Unauthorized(err.Error()), nil
			}
			req.Context = ctx
			return next(req)
		}
	}
}
main.domainMiddlewareWrapper function · go · L589-L603 (15 LOC)
backend/cmd/api/main.go
func domainMiddlewareWrapper(m *middleware.DomainMiddleware) api.Middleware {
	return func(next api.Handler) api.Handler {
		return func(req *api.Request) (*api.Response, error) {
			ctx, err := m.ResolvePhotographer(req.Context, events.APIGatewayProxyRequest{
				Headers: req.Headers,
			})
			if err != nil {
				// Domain resolution errors are not fatal - continue without photographer context
				logger.Warn("Domain resolution failed", map[string]interface{}{"error": err.Error()})
			}
			req.Context = ctx
			return next(req)
		}
	}
}
main.main function · go · L51-L189 (139 LOC)
backend/cmd/api/main-local.go
func main() {
	app, err := initializeLocalApp()
	if err != nil {
		logger.Error("Failed to initialize app", map[string]interface{}{"error": err.Error()})
		os.Exit(1)
	}

	// Set up HTTP server
	mux := http.NewServeMux()

	// Health check
	mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		json.NewEncoder(w).Encode(map[string]string{"status": "healthy"})
	})

	// API routes - wrap with CORS middleware
	mux.HandleFunc("/api/v1/", app.corsMiddleware(app.router))

	// Get port from environment or use default
	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}

	logger.Info("Starting local development server", map[string]interface{}{
		"port": port,
		"url":  fmt.Sprintf("http://localhost:%s", port),
	})

	log.Fatal(http.ListenAndServe(":"+port, mux))
}

func initializeLocalApp() (*LocalApp, error) {
	ctx := context.Background()

	// Load configuration
	cfg := appConfig.Load()

	logger.Info("Application initialized", map[str
main.initializeLocalApp function · go · L84-L170 (87 LOC)
backend/cmd/api/main-local.go
func initializeLocalApp() (*LocalApp, error) {
	ctx := context.Background()

	// Load configuration
	cfg := appConfig.Load()

	logger.Info("Application initialized", map[string]interface{}{
		"region": cfg.AWSRegion,
		"stage":  cfg.APIStage,
	})

	// Load AWS config
	awsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(cfg.AWSRegion))
	if err != nil {
		return nil, fmt.Errorf("failed to load AWS config: %w", err)
	}

	// Initialize AWS clients
	dynamoClient := dynamodb.NewFromConfig(awsCfg)
	s3Client := s3.NewFromConfig(awsCfg)

	// Initialize repositories
	galleryRepo := dynamodbRepo.NewGalleryRepository(
		dynamoClient,
		fmt.Sprintf("%s-galleries-%s", cfg.DynamoDBTablePrefix, cfg.APIStage),
	)
	photoRepo := dynamodbRepo.NewPhotoRepository(
		dynamoClient,
		fmt.Sprintf("%s-photos-%s", cfg.DynamoDBTablePrefix, cfg.APIStage),
	)
	favoriteRepo := dynamodbRepo.NewFavoriteRepository(
		dynamoClient,
		fmt.Sprintf("%s-favorites-%s", cfg.DynamoDBTablePrefix, cfg.APIStage),
	)
	s
main.LocalApp.router method · go · L192-L252 (61 LOC)
backend/cmd/api/main-local.go
func (app *LocalApp) router(w http.ResponseWriter, r *http.Request) {
	path := strings.TrimPrefix(r.URL.Path, "/api/v1")

	// Route matching
	switch {
	// Gallery routes (photographer - requires auth)
	case path == "/galleries" && r.Method == "GET":
		app.withAuth(app.galleryHandler.ListGalleries)(w, r)
	case path == "/galleries" && r.Method == "POST":
		app.withAuth(app.galleryHandler.CreateGallery)(w, r)
	case strings.HasPrefix(path, "/galleries/") && !strings.Contains(path, "/photos"):
		galleryID := strings.TrimPrefix(path, "/galleries/")
		if strings.Contains(galleryID, "/") {
			parts := strings.Split(galleryID, "/")
			galleryID = parts[0]
		}
		switch r.Method {
		case "GET":
			app.withAuth(app.galleryHandler.GetGallery)(w, r)
		case "PUT":
			app.withAuth(app.galleryHandler.UpdateGallery)(w, r)
		case "DELETE":
			app.withAuth(app.galleryHandler.DeleteGallery)(w, r)
		}

	// Photo routes (photographer - requires auth)
	case strings.HasPrefix(path, "/galleries/") && strings.Co
main.LocalApp.withAuth method · go · L255-L268 (14 LOC)
backend/cmd/api/main-local.go
func (app *LocalApp) withAuth(handler http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Extract user ID from auth header
		authHeader := r.Header.Get("Authorization")
		if authHeader == "" {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}

		// For local dev, you can bypass actual token validation
		// In production, this would validate the JWT
		handler(w, r)
	}
}
Repobility analyzer · published findings · https://repobility.com
main.LocalApp.withSession method · go · L271-L282 (12 LOC)
backend/cmd/api/main-local.go
func (app *LocalApp) withSession(handler http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Extract session token from auth header
		authHeader := r.Header.Get("Authorization")
		if authHeader == "" {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}

		handler(w, r)
	}
}
main.main function · go · L62-L99 (38 LOC)
backend/cmd/dlq-reprocessor/main.go
func main() {
	// Load configuration from environment variables
	cfg := &Config{
		AWSRegion:           os.Getenv("AWS_REGION_NAME"),
		DynamoDBTablePrefix: os.Getenv("DYNAMODB_TABLE_PREFIX"),
		S3BucketOriginal:    os.Getenv("S3_BUCKET_ORIGINAL"),
		S3BucketOptimized:   os.Getenv("S3_BUCKET_OPTIMIZED"),
		S3BucketThumbnail:   os.Getenv("S3_BUCKET_THUMBNAIL"),
		ProcessingQueueURL:  os.Getenv("PROCESSING_QUEUE_URL"),
		APIStage:            os.Getenv("STAGE"),
	}

	// Initialize AWS SDK
	awsCfg, err := config.LoadDefaultConfig(context.Background(),
		config.WithRegion(cfg.AWSRegion),
	)
	if err != nil {
		log.Fatalf("Failed to load AWS config: %v", err)
	}

	// Initialize clients
	sqsClient := sqs.NewFromConfig(awsCfg)
	dynamoClient := dynamodb.NewFromConfig(awsCfg)

	// Initialize repositories
	photoRepo := dynamodbRepo.NewPhotoRepository(
		dynamoClient,
		fmt.Sprintf("%s-photos-%s", cfg.DynamoDBTablePrefix, cfg.APIStage),
	)

	app := &App{
		cfg:       cfg,
		sqsClient: sqsClient,
		
main.App.handleDLQEvent method · go · L102-L141 (40 LOC)
backend/cmd/dlq-reprocessor/main.go
func (app *App) handleDLQEvent(ctx context.Context, sqsEvent events.SQSEvent) error {
	log.Printf("Processing %d DLQ event records", len(sqsEvent.Records))

	for _, sqsRecord := range sqsEvent.Records {
		// Extract retry metadata from message attributes
		retryMetadata := app.extractRetryMetadata(sqsRecord)

		log.Printf("Processing DLQ message, attempt %d/%d", retryMetadata.AttemptNumber+1, maxRetryAttempts)

		// Check if we've exceeded max retry attempts
		if retryMetadata.AttemptNumber >= maxRetryAttempts {
			log.Printf("Max retry attempts reached for message. Marking as permanently failed.")
			if err := app.markAsPermantlyFailed(ctx, sqsRecord.Body, retryMetadata); err != nil {
				log.Printf("Failed to mark photo as permanently failed: %v", err)
			}
			continue
		}

		// Calculate exponential backoff delay
		delaySeconds := calculateBackoffDelay(retryMetadata.AttemptNumber)
		log.Printf("Scheduling retry with %d second delay (exponential backoff)", delaySeconds)

		// Update 
main.App.extractRetryMetadata method · go · L144-L184 (41 LOC)
backend/cmd/dlq-reprocessor/main.go
func (app *App) extractRetryMetadata(record events.SQSMessage) RetryMetadata {
	metadata := RetryMetadata{
		AttemptNumber: 0,
	}

	// Check for retry attempt count in message attributes
	if attemptAttr, ok := record.MessageAttributes["RetryAttempt"]; ok {
		if attemptAttr.StringValue != nil {
			if attempt, err := strconv.Atoi(*attemptAttr.StringValue); err == nil {
				metadata.AttemptNumber = attempt
			}
		}
	}

	// Check for first failed timestamp
	if firstFailedAttr, ok := record.MessageAttributes["FirstFailedAt"]; ok {
		if firstFailedAttr.StringValue != nil {
			if t, err := time.Parse(time.RFC3339, *firstFailedAttr.StringValue); err == nil {
				metadata.FirstFailedAt = t
			}
		}
	}

	// Check for last retry timestamp
	if lastRetryAttr, ok := record.MessageAttributes["LastRetryAt"]; ok {
		if lastRetryAttr.StringValue != nil {
			if t, err := time.Parse(time.RFC3339, *lastRetryAttr.StringValue); err == nil {
				metadata.LastRetryAt = t
			}
		}
	}

	// Check for error messag
main.calculateBackoffDelay function · go · L188-L202 (15 LOC)
backend/cmd/dlq-reprocessor/main.go
func calculateBackoffDelay(attemptNumber int) int {
	delays := []int{
		60,    // 1 minute
		300,   // 5 minutes
		900,   // 15 minutes
		3600,  // 1 hour
		14400, // 4 hours
	}

	if attemptNumber >= len(delays) {
		return delays[len(delays)-1]
	}

	return delays[attemptNumber]
}
main.App.requeueWithDelay method · go · L205-L238 (34 LOC)
backend/cmd/dlq-reprocessor/main.go
func (app *App) requeueWithDelay(ctx context.Context, messageBody string, metadata RetryMetadata, delaySeconds int) error {
	// Prepare message attributes with retry metadata
	messageAttributes := map[string]types.MessageAttributeValue{
		"RetryAttempt": {
			DataType:    aws.String("Number"),
			StringValue: aws.String(strconv.Itoa(metadata.AttemptNumber)),
		},
		"FirstFailedAt": {
			DataType:    aws.String("String"),
			StringValue: aws.String(metadata.FirstFailedAt.Format(time.RFC3339)),
		},
		"LastRetryAt": {
			DataType:    aws.String("String"),
			StringValue: aws.String(metadata.LastRetryAt.Format(time.RFC3339)),
		},
	}

	if metadata.ErrorMessage != "" {
		messageAttributes["ErrorMessage"] = types.MessageAttributeValue{
			DataType:    aws.String("String"),
			StringValue: aws.String(metadata.ErrorMessage),
		}
	}

	// Send message to processing queue with delay
	_, err := app.sqsClient.SendMessage(ctx, &sqs.SendMessageInput{
		QueueUrl:          aws.String(app.cfg.Processin
main.App.markAsPermantlyFailed method · go · L241-L294 (54 LOC)
backend/cmd/dlq-reprocessor/main.go
func (app *App) markAsPermantlyFailed(ctx context.Context, messageBody string, metadata RetryMetadata) error {
	// Parse S3 event from message body
	var s3Event events.S3Event
	if err := json.Unmarshal([]byte(messageBody), &s3Event); err != nil {
		return fmt.Errorf("failed to unmarshal S3 event: %w", err)
	}

	for _, record := range s3Event.Records {
		objectKey := record.S3.Object.Key

		// Extract photo ID from the object key
		photoID, err := extractPhotoID(objectKey)
		if err != nil {
			log.Printf("Failed to extract photo ID from key %s: %v", objectKey, err)
			continue
		}

		// Get photo from database
		photo, err := app.photoRepo.GetByID(ctx, photoID)
		if err != nil {
			log.Printf("Failed to get photo from database: %v", err)
			continue
		}

		if photo == nil {
			log.Printf("Photo not found in database: %s", photoID)
			continue
		}

		// Update photo status to permanently failed
		photo.ProcessingStatus = "failed_permanent"
		now := time.Now()
		photo.ProcessedAt = &now

main.extractPhotoID function · go · L298-L311 (14 LOC)
backend/cmd/dlq-reprocessor/main.go
func extractPhotoID(objectKey string) (string, error) {
	parts := strings.Split(objectKey, "/")
	if len(parts) < 3 {
		return "", fmt.Errorf("invalid object key format: %s", objectKey)
	}

	// The photoID is the second part: galleryID/photoID/filename
	photoID := parts[1]
	if !strings.HasPrefix(photoID, "photo_") {
		return "", fmt.Errorf("invalid photo ID format in key: %s", objectKey)
	}

	return photoID, nil
}
All rows scored by the Repobility analyzer (https://repobility.com)
main.main function · go · L58-L64 (7 LOC)
backend/cmd/processor/main.go
func main() {
	app, err := initApp()
	if err != nil {
		log.Fatalf("Failed to initialize: %v", err)
	}
	lambda.Start(app.handleS3Event)
}
main.initApp function · go · L66-L87 (22 LOC)
backend/cmd/processor/main.go
func initApp() (*App, error) {
	cfg, err := appconfig.NewProcessorConfigBuilder().FromEnvironment().Build()
	if err != nil {
		return nil, err
	}

	awsCfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(cfg.AWSRegion))
	if err != nil {
		return nil, err
	}

	dynamoClient := dynamodb.NewFromConfig(awsCfg)

	return &App{
		cfg:              cfg,
		s3Client:         s3.NewFromConfig(awsCfg),
		photoRepo:        dynamodbRepo.NewPhotoRepository(dynamoClient, cfg.PhotosTableName()),
		galleryRepo:      dynamodbRepo.NewGalleryRepository(dynamoClient, cfg.GalleriesTableName()),
		photographerRepo: dynamodbRepo.NewPhotographerRepository(dynamoClient, cfg.PhotographersTableName()),
		processor:        image.NewProcessor(),
	}, nil
}
main.App.handleS3Event method · go · L90-L126 (37 LOC)
backend/cmd/processor/main.go
func (app *App) handleS3Event(ctx context.Context, sqsEvent events.SQSEvent) error {
	log.Printf("Processing %d SQS records", len(sqsEvent.Records))

	for _, sqsRecord := range sqsEvent.Records {
		var s3Event events.S3Event
		if err := json.Unmarshal([]byte(sqsRecord.Body), &s3Event); err != nil {
			log.Printf("Failed to unmarshal S3 event: %v", err)
			continue
		}

		for _, record := range s3Event.Records {
			bucket := record.S3.Bucket.Name
			// S3 event keys are URL-encoded (spaces become '+' or '%20')
			// We must decode them to get the actual S3 key
			key, err := url.QueryUnescape(record.S3.Object.Key)
			if err != nil {
				log.Printf("Failed to decode S3 key %s: %v", record.S3.Object.Key, err)
				continue
			}
			log.Printf("Processing: %s/%s", bucket, key)

			parsed, err := s3key.Parse(key)
			if err != nil {
				log.Printf("Invalid key %s: %v", key, err)
				continue
			}

			if err := app.processPhoto(ctx, parsed, bucket, key); err != nil {
				log.Printf("Failed to p
main.App.processPhoto method · go · L129-L228 (100 LOC)
backend/cmd/processor/main.go
func (app *App) processPhoto(ctx context.Context, key *s3key.Key, bucket, objectKey string) error {
	// Download from S3
	imageData, contentLength, err := app.downloadImage(ctx, bucket, objectKey)
	if err != nil {
		return err
	}

	// Get dimensions and metadata
	width, height, _ := app.processor.GetImageDimensions(bytes.NewReader(imageData))
	metadata, _ := app.processor.ExtractEXIF(bytes.NewReader(imageData))
	if metadata == nil {
		metadata = &image.ImageMetadata{}
	}
	if metadata.Width == 0 {
		metadata.Width = width
	}
	if metadata.Height == 0 {
		metadata.Height = height
	}

	// Fetch gallery for watermark settings
	gallery, err := app.galleryRepo.GetByID(ctx, key.GalleryID)
	if err != nil {
		log.Printf("Warning: Failed to fetch gallery %s for watermark settings: %v", key.GalleryID, err)
		// Continue processing without watermark
	}

	// Fetch photographer for watermark text (custom domain/subdomain priority)
	var photographerData *photographer.Photographer
	if gallery != nil {
main.App.downloadImage method · go · L230-L249 (20 LOC)
backend/cmd/processor/main.go
func (app *App) downloadImage(ctx context.Context, bucket, key string) (data []byte, size int64, err error) {
	output, err := app.s3Client.GetObject(ctx, &s3.GetObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(key),
	})
	if err != nil {
		return nil, 0, fmt.Errorf("S3 download failed: %w", err)
	}
	defer output.Body.Close()

	data, err = io.ReadAll(output.Body)
	if err != nil {
		return nil, 0, fmt.Errorf("read failed: %w", err)
	}

	if output.ContentLength != nil {
		size = *output.ContentLength
	}
	return data, size, nil
}
main.App.updatePhotoRecord method · go · L251-L294 (44 LOC)
backend/cmd/processor/main.go
func (app *App) updatePhotoRecord(ctx context.Context, key *s3key.Key, objectKey, thumbnailKey, optimizedKey string, metadata *image.ImageMetadata, size int64) error {
	photo, _ := app.photoRepo.GetByID(ctx, key.PhotoID)
	isNew := photo == nil

	if isNew {
		now := time.Now()
		photo = &repository.Photo{
			PhotoID:          key.PhotoID,
			GalleryID:        key.GalleryID,
			FileName:         key.FileName,
			OriginalKey:      objectKey,
			MimeType:         s3key.GetMimeType(key.Extension),
			Size:             size,
			Width:            metadata.Width,
			Height:           metadata.Height,
			UploadedAt:       now,
			ProcessingStatus: "processing",
			Metadata:         make(map[string]string),
		}
		if err := app.photoRepo.Create(ctx, photo); err != nil {
			return fmt.Errorf("create photo failed: %w", err)
		}
	}

	// Update with processing results
	photo.OptimizedKey = optimizedKey
	photo.ThumbnailKey = thumbnailKey
	photo.Width = metadata.Width
	photo.Height = metadata.Height
	p
main.App.storeEXIFMetadata method · go · L296-L322 (27 LOC)
backend/cmd/processor/main.go
func (app *App) storeEXIFMetadata(photo *repository.Photo, m *image.ImageMetadata) {
	if photo.Metadata == nil {
		photo.Metadata = make(map[string]string)
	}
	if m.CameraModel != "" {
		photo.Metadata["cameraModel"] = m.CameraModel
	}
	if m.DateTaken != "" {
		photo.Metadata["dateTaken"] = m.DateTaken
	}
	if m.ISO > 0 {
		photo.Metadata["iso"] = fmt.Sprintf("%d", m.ISO)
	}
	if m.Aperture != "" {
		photo.Metadata["aperture"] = m.Aperture
	}
	if m.ShutterSpeed != "" {
		photo.Metadata["shutterSpeed"] = m.ShutterSpeed
	}
	if m.FocalLength != "" {
		photo.Metadata["focalLength"] = m.FocalLength
	}
	if m.GPS != nil {
		gpsData, _ := json.Marshal(m.GPS)
		photo.Metadata["gps"] = string(gpsData)
	}
}
main.App.uploadToS3 method · go · L324-L332 (9 LOC)
backend/cmd/processor/main.go
func (app *App) uploadToS3(ctx context.Context, bucket, key string, data []byte, contentType string) error {
	_, err := app.s3Client.PutObject(ctx, &s3.PutObjectInput{
		Bucket:      aws.String(bucket),
		Key:         aws.String(key),
		Body:        bytes.NewReader(data),
		ContentType: aws.String(contentType),
	})
	return err
}
Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
main.App.updatePhotoStatus method · go · L334-L345 (12 LOC)
backend/cmd/processor/main.go
func (app *App) updatePhotoStatus(ctx context.Context, photoID, status string) {
	photo, err := app.photoRepo.GetByID(ctx, photoID)
	if err != nil || photo == nil {
		return
	}
	photo.ProcessingStatus = status
	if status == "completed" || status == "failed" {
		now := time.Now()
		photo.ProcessedAt = &now
	}
	_ = app.photoRepo.Update(ctx, photo)
}
main.App.generateThumbnail method · go · L349-L371 (23 LOC)
backend/cmd/processor/main.go
func (app *App) generateThumbnail(imageData io.Reader, gallery *repository.Gallery) ([]byte, error) {
	// Use the processor's orientation-aware decoder
	img, _, err := app.processor.DecodeWithOrientation(imageData)
	if err != nil {
		return nil, fmt.Errorf("decode failed: %w", err)
	}

	// Create thumbnail with smart cropping (center)
	thumbnail := imaging.Fill(img, image.ThumbnailWidth, image.ThumbnailHeight, imaging.Center, imaging.Lanczos)

	// Apply photographer's custom watermark as a banner if enabled
	var result imageType.Image = thumbnail
	if gallery != nil && gallery.WatermarksEnabled && gallery.WatermarkText != "" {
		result = app.processor.ApplyWatermarkBanner(thumbnail, gallery.WatermarkText)
	}

	// Encode to JPEG
	var buf bytes.Buffer
	if err := imaging.Encode(&buf, result, imaging.JPEG, imaging.JPEGQuality(image.ThumbnailJPEGQuality)); err != nil {
		return nil, fmt.Errorf("encode failed: %w", err)
	}
	return buf.Bytes(), nil
}
main.App.generateOptimized method · go · L373-L406 (34 LOC)
backend/cmd/processor/main.go
func (app *App) generateOptimized(imageData io.Reader, gallery *repository.Gallery) ([]byte, error) {
	// Use the processor's orientation-aware decoder
	img, _, err := app.processor.DecodeWithOrientation(imageData)
	if err != nil {
		return nil, fmt.Errorf("decode failed: %w", err)
	}

	bounds := img.Bounds()
	var result imageType.Image
	if bounds.Dx() > image.OptimizedMaxWidth || bounds.Dy() > image.OptimizedMaxHeight {
		result = imaging.Fit(img, image.OptimizedMaxWidth, image.OptimizedMaxHeight, imaging.Lanczos)
	} else {
		// Ensure consistent NRGBA format even for images that don't need resizing
		result = imaging.Clone(img)
	}

	if gallery != nil && gallery.WatermarksEnabled && gallery.WatermarkText != "" {
		position := gallery.WatermarkPosition
		if position == "" {
			position = "bottom-right"
		}
		result = app.processor.ApplyWatermark(result, image.WatermarkOptions{
			Text:     gallery.WatermarkText,
			Position: position,
		})
	}

	// Encode with configurable quality
	var 
main.App.generateWatermarkedThumbnail method · go · L410-L440 (31 LOC)
backend/cmd/processor/main.go
func (app *App) generateWatermarkedThumbnail(imageData io.Reader, watermarkText string, overlay imageType.Image) ([]byte, error) {
	// Use the processor's orientation-aware decoder
	img, _, err := app.processor.DecodeWithOrientation(imageData)
	if err != nil {
		return nil, fmt.Errorf("decode failed: %w", err)
	}

	// Create thumbnail with smart cropping (center)
	thumbnail := imaging.Fill(img, image.ThumbnailWidth, image.ThumbnailHeight, imaging.Center, imaging.Lanczos)

	// Apply watermark banner
	var watermarked imageType.Image
	switch {
	case overlay != nil:
		// Use the photographer's uploaded image watermark
		watermarked = app.processor.ApplyImageWatermarkBanner(thumbnail, overlay)
	case watermarkText == "FrameFocal":
		// Use the cached FrameFocal brand watermark for efficiency
		watermarked = app.processor.ApplyBrandWatermarkBanner(thumbnail)
	default:
		// Use dynamic text watermark for custom domain/subdomain
		watermarked = app.processor.ApplyTextWatermarkBanner(thumbnail, 
main.App.generateWatermarkedOptimized method · go · L444-L479 (36 LOC)
backend/cmd/processor/main.go
func (app *App) generateWatermarkedOptimized(imageData io.Reader, watermarkText string, overlay imageType.Image) ([]byte, error) {
	// Use the processor's orientation-aware decoder
	img, _, err := app.processor.DecodeWithOrientation(imageData)
	if err != nil {
		return nil, fmt.Errorf("decode failed: %w", err)
	}

	bounds := img.Bounds()
	var result imageType.Image
	if bounds.Dx() > image.OptimizedMaxWidth || bounds.Dy() > image.OptimizedMaxHeight {
		result = imaging.Fit(img, image.OptimizedMaxWidth, image.OptimizedMaxHeight, imaging.Lanczos)
	} else {
		result = imaging.Clone(img)
	}

	// Apply watermark
	var watermarked imageType.Image
	switch {
	case overlay != nil:
		// Use the photographer's uploaded image watermark
		watermarked = app.processor.ApplyImageWatermark(result, overlay, "bottom-right")
	case watermarkText == "FrameFocal":
		// Use the cached FrameFocal brand watermark for efficiency
		watermarked = app.processor.ApplyBrandWatermark(result, "bottom-right")
	default:
		
main.App.fetchWatermarkOverlay method · go · L482-L508 (27 LOC)
backend/cmd/processor/main.go
func (app *App) fetchWatermarkOverlay(ctx context.Context, s3Key string) (imageType.Image, error) {
	const maxWatermarkFileSize = 5 * 1024 * 1024 // 5MB

	output, err := app.s3Client.GetObject(ctx, &s3.GetObjectInput{
		Bucket: aws.String(app.cfg.S3BucketOriginal),
		Key:    aws.String(s3Key),
	})
	if err != nil {
		return nil, fmt.Errorf("failed to download watermark overlay: %w", err)
	}
	defer output.Body.Close()

	data, err := io.ReadAll(io.LimitReader(output.Body, maxWatermarkFileSize+1))
	if err != nil {
		return nil, fmt.Errorf("failed to read watermark overlay: %w", err)
	}
	if len(data) > maxWatermarkFileSize {
		return nil, fmt.Errorf("watermark overlay exceeds maximum size of %d bytes", maxWatermarkFileSize)
	}

	img, _, err := imageType.Decode(bytes.NewReader(data))
	if err != nil {
		return nil, fmt.Errorf("failed to decode watermark overlay: %w", err)
	}

	return img, nil
}
main.main function · go · L16-L108 (93 LOC)
backend/cmd/processor/manual_check.go
func main() {
	if len(os.Args) < 2 {
		fmt.Println("Usage: go run manual_test.go <image_path>")
		fmt.Println("\nThis will generate thumbnail and optimized versions of your image.")
		os.Exit(1)
	}

	imagePath := os.Args[1]
	fmt.Printf("Processing: %s\n\n", imagePath)

	// Read the image
	data, err := os.ReadFile(imagePath)
	if err != nil {
		fmt.Printf("Error reading file: %v\n", err)
		os.Exit(1)
	}
	fmt.Printf("Original size: %d bytes\n", len(data))

	processor := image.NewProcessor()

	// Test DecodeWithOrientation
	img, format, err := processor.DecodeWithOrientation(bytes.NewReader(data))
	if err != nil {
		fmt.Printf("Error decoding: %v\n", err)
		os.Exit(1)
	}
	bounds := img.Bounds()
	fmt.Printf("Format: %s, Dimensions: %dx%d (after orientation correction)\n\n", format, bounds.Dx(), bounds.Dy())

	// Extract EXIF
	metadata, _ := processor.ExtractEXIF(bytes.NewReader(data))
	fmt.Println("EXIF Metadata:")
	if metadata.CameraModel != "" {
		fmt.Printf("  Camera: %s\n", metadata.Cam
main.main function · go · L26-L33 (8 LOC)
backend/cmd/scheduler/main.go
func main() {
	app, err := initializeApp()
	if err != nil {
		log.Fatalf("Failed to initialize app: %v", err)
	}

	lambda.Start(app.handleScheduledEvent)
}
Repobility · severity-and-effort ranking · https://repobility.com
main.initializeApp function · go · L35-L79 (45 LOC)
backend/cmd/scheduler/main.go
func initializeApp() (*SchedulerApp, error) {
	ctx := context.Background()

	// Load AWS config
	cfg, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to load AWS config: %w", err)
	}

	// Initialize AWS clients
	dynamoClient := dynamodb.NewFromConfig(cfg)
	s3Client := s3.NewFromConfig(cfg)

	// Get environment variables
	tablePrefix := os.Getenv("DYNAMODB_TABLE_PREFIX")
	stage := os.Getenv("STAGE")
	originalBucket := os.Getenv("S3_BUCKET_ORIGINAL")
	optimizedBucket := os.Getenv("S3_BUCKET_OPTIMIZED")
	thumbnailBucket := os.Getenv("S3_BUCKET_THUMBNAIL")

	if tablePrefix == "" {
		tablePrefix = "photographer-gallery"
	}
	if stage == "" {
		stage = "dev"
	}

	// Initialize repositories
	galleriesTable := fmt.Sprintf("%s-galleries-%s", tablePrefix, stage)
	photosTable := fmt.Sprintf("%s-photos-%s", tablePrefix, stage)

	galleryRepo := dynamodbRepo.NewGalleryRepository(dynamoClient, galleriesTable)
	photoRepo := dynamodbRepo.NewPhotoRepository(dynamoCl
main.SchedulerApp.handleScheduledEvent method · go · L82-L102 (21 LOC)
backend/cmd/scheduler/main.go
func (app *SchedulerApp) handleScheduledEvent(ctx context.Context, _ interface{}) error {
	startTime := time.Now().UTC()
	log.Printf("=== SCHEDULER START === Time: %v", startTime.Format(time.RFC3339))
	log.Printf("Environment: STAGE=%s, TABLE_PREFIX=%s", os.Getenv("STAGE"), os.Getenv("DYNAMODB_TABLE_PREFIX"))

	// Process scheduled galleries first (publishes galleries where publishAt < now)
	if err := app.galleryService.ProcessScheduledGalleries(ctx, 100); err != nil {
		log.Printf("ERROR: Failed to process scheduled galleries: %v", err)
		// Continue to process expired galleries even if scheduled processing fails
	}

	// Process expired galleries (deletes galleries where expiresAt < now)
	if err := app.galleryService.ProcessExpiredGalleries(ctx, 100); err != nil {
		log.Printf("ERROR: Failed to process expired galleries: %v", err)
		return fmt.Errorf("failed to process expired galleries: %w", err)
	}

	duration := time.Since(startTime)
	log.Printf("=== SCHEDULER END === Duration: %v",
main.main function · go · L50-L56 (7 LOC)
backend/cmd/zip-processor/main.go
func main() {
	app, err := initApp()
	if err != nil {
		log.Fatalf("Failed to initialize: %v", err)
	}
	lambda.Start(app.handleSQSEvent)
}
main.initApp function · go · L58-L79 (22 LOC)
backend/cmd/zip-processor/main.go
func initApp() (*App, error) {
	cfg, err := appconfig.NewZipProcessorConfigBuilder().FromEnvironment().Build()
	if err != nil {
		return nil, err
	}

	awsCfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(cfg.AWSRegion))
	if err != nil {
		return nil, err
	}

	s3Client := s3.NewFromConfig(awsCfg)
	dynamoClient := dynamodb.NewFromConfig(awsCfg)

	return &App{
		cfg:           cfg,
		s3Client:      s3Client,
		presignClient: s3.NewPresignClient(s3Client),
		jobRepo:       dynamodbRepo.NewZipJobRepository(dynamoClient, cfg.ZipJobsTableName()),
		photoRepo:     dynamodbRepo.NewPhotoRepository(dynamoClient, cfg.PhotosTableName()),
	}, nil
}
main.App.handleSQSEvent method · go · L82-L109 (28 LOC)
backend/cmd/zip-processor/main.go
func (app *App) handleSQSEvent(ctx context.Context, sqsEvent events.SQSEvent) error {
	log.Printf("Processing %d SQS records", len(sqsEvent.Records))

	for _, record := range sqsEvent.Records {
		var msg ZipJobMessage
		if err := json.Unmarshal([]byte(record.Body), &msg); err != nil {
			log.Printf("Failed to unmarshal message: %v", err)
			continue
		}

		log.Printf("Processing ZIP job: %s for gallery: %s", msg.JobID, msg.GalleryID)

		if err := app.processZipJob(ctx, &msg); err != nil {
			log.Printf("Failed to process ZIP job %s: %v", msg.JobID, err)
			// Mark job as failed
			if job, _ := app.jobRepo.GetByID(ctx, msg.JobID); job != nil {
				job.Status = repository.ZipJobStatusFailed
				job.ErrorMsg = err.Error()
				_ = app.jobRepo.Update(ctx, job)
			}
			continue
		}

		log.Printf("Successfully processed ZIP job: %s", msg.JobID)
	}

	return nil
}
main.App.processZipJob method · go · L111-L247 (137 LOC)
backend/cmd/zip-processor/main.go
func (app *App) processZipJob(ctx context.Context, msg *ZipJobMessage) error {
	// Get and update job status to processing
	job, err := app.jobRepo.GetByID(ctx, msg.JobID)
	if err != nil {
		return fmt.Errorf("failed to get job: %w", err)
	}
	if job == nil {
		return fmt.Errorf("job not found: %s", msg.JobID)
	}

	job.Status = repository.ZipJobStatusProcessing
	if updateErr := app.jobRepo.Update(ctx, job); updateErr != nil {
		return fmt.Errorf("failed to update job status: %w", updateErr)
	}

	// Fetch all photos for the gallery
	var allPhotos []*repository.Photo
	var lastKey map[string]interface{}
	const pageSize = 500

	for {
		photos, nextKey, listErr := app.photoRepo.ListByGallery(ctx, msg.GalleryID, pageSize, lastKey)
		if listErr != nil {
			return fmt.Errorf("failed to list photos: %w", listErr)
		}
		allPhotos = append(allPhotos, photos...)

		if nextKey == nil || len(photos) < pageSize {
			break
		}
		lastKey = nextKey
	}

	if len(allPhotos) == 0 {
		return fmt.Errorf("no ph
main.App.downloadPhoto method · go · L249-L258 (10 LOC)
backend/cmd/zip-processor/main.go
func (app *App) downloadPhoto(ctx context.Context, key string) (io.ReadCloser, error) {
	output, err := app.s3Client.GetObject(ctx, &s3.GetObjectInput{
		Bucket: aws.String(app.cfg.S3BucketOptimized),
		Key:    aws.String(key),
	})
	if err != nil {
		return nil, err
	}
	return output.Body, nil
}
main.App.uploadZip method · go · L260-L268 (9 LOC)
backend/cmd/zip-processor/main.go
func (app *App) uploadZip(ctx context.Context, key string, data []byte) error {
	_, err := app.s3Client.PutObject(ctx, &s3.PutObjectInput{
		Bucket:      aws.String(app.cfg.S3BucketOptimized),
		Key:         aws.String(key),
		Body:        bytes.NewReader(data),
		ContentType: aws.String("application/zip"),
	})
	return err
}
Repobility analyzer · published findings · https://repobility.com
main.App.generateDownloadURL method · go · L270-L282 (13 LOC)
backend/cmd/zip-processor/main.go
func (app *App) generateDownloadURL(ctx context.Context, key, filename string) (string, error) {
	presignedReq, err := app.presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
		Bucket:                     aws.String(app.cfg.S3BucketOptimized),
		Key:                        aws.String(key),
		ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", filename)),
	}, func(opts *s3.PresignOptions) {
		opts.Expires = 24 * time.Hour
	})
	if err != nil {
		return "", err
	}
	return presignedReq.URL, nil
}
adapters.S3Adapter.Download method · go · L31-L43 (13 LOC)
backend/internal/adapters/s3_adapter.go
func (a *S3Adapter) Download(ctx context.Context, bucket, key string) (io.ReadCloser, error) {
	log.Printf("[S3Adapter] Downloading from %s/%s", bucket, key)

	output, err := a.client.GetObject(ctx, &s3.GetObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(key),
	})
	if err != nil {
		return nil, err
	}

	return output.Body, nil
}
adapters.S3Adapter.DownloadBytes method · go · L46-L54 (9 LOC)
backend/internal/adapters/s3_adapter.go
func (a *S3Adapter) DownloadBytes(ctx context.Context, bucket, key string) ([]byte, error) {
	reader, err := a.Download(ctx, bucket, key)
	if err != nil {
		return nil, err
	}
	defer reader.Close()

	return io.ReadAll(reader)
}
page 1 / 20next ›