Skip to content

AlexTrebs/trekka-api

Repository files navigation

Trekka API

A high-performance Go API for serving and managing photos and videos stored in Firebase Storage with automatic HEIC/HEIF to JPEG conversion, intelligent caching, comprehensive metadata extraction, and reverse geocoding.

Features

API Server

  • Image & Video Serving: Fetch and serve media from Firebase Storage via signed URLs
  • HEIC/HEIF Conversion: Automatic conversion of HEIC/HEIF images to JPEG format
  • Intelligent Caching: In-memory LRU cache with configurable TTL to reduce storage API calls
  • Comprehensive Metadata Extraction:
    • Images: EXIF data extraction (GPS coordinates, timestamps, resolution)
    • Videos: MP4 metadata extraction using exiftool (GPS, creation date, dimensions)
    • Reverse Geocoding: Automatic GPS coordinate to location name conversion using OpenStreetMap Nominatim
  • Pagination Support: List images with configurable page size and pagination
  • API Key Authentication: Required for all endpoints except /health
  • Rate Limiting: Per-IP rate limiting (10 req/sec) to prevent abuse and control costs
  • Swagger/OpenAPI Documentation: Interactive API documentation at /swagger/
  • CORS Support: Configurable CORS middleware for cross-origin requests
  • Request Tracking: Request ID middleware for debugging and monitoring
  • Health Checks: Built-in health check endpoint for monitoring
  • Graceful Shutdown: Proper cleanup of resources on server termination
  • Docker Support: Multi-stage Docker build optimized for Cloud Run deployment
  • Cloud Build Caching: Fast rebuilds with Docker layer caching (1-2 min vs 3-5 min)

Google Drive Sync (Optional)

  • Automatic Sync: Monitor Google Drive folder for new images and videos
  • Backfill Support: Sync all existing media from Drive to Firebase
  • HEIC Conversion: Automatically converts HEIC/HEIF files to JPEG during sync
  • Metadata Extraction: Automatic GPS and timestamp extraction during sync
  • Reverse Geocoding: Converts GPS coordinates to location names (city, country)
  • Duplicate Detection: Skips files already synced to prevent duplicates
  • Continuous Monitoring: Watch mode for real-time syncing of new uploads
  • Robust Rate Limiting: Automatic retry with exponential backoff (5 attempts per file)
  • Smart Error Recovery: Detects persistent rate limits and pauses automatically
  • Timeout Protection: 5-minute timeout per download prevents hangs
  • Standalone Tool: Separate CLI tool for flexible deployment options

Metadata Management Tools

  • Metadata Update Utilities: Multiple commands for updating and backfilling metadata
  • Batch Processing: Update all files or only files with missing data
  • Dry Run Mode: Preview changes before applying them
  • Google Drive Backfill: Re-download and re-extract metadata from Google Drive
  • Progress Tracking: Real-time progress updates during batch operations

Tech Stack

  • Language: Go 1.25+
  • Storage: Firebase Cloud Storage
  • Database: Cloud Firestore
  • Image Processing: goheif for HEIC/HEIF conversion
  • Metadata Extraction:
    • goexif for image EXIF data
    • exiftool for MP4 video metadata
  • Geocoding: OpenStreetMap Nominatim API
  • Containerization: Docker & Docker Compose

Prerequisites

  • Go 1.25 or higher
  • Firebase project with Cloud Storage and Firestore enabled
  • Firebase service account credentials JSON file
  • exiftool (for video metadata extraction): sudo apt-get install libimage-exiftool-perl or brew install exiftool

Installation

Clone the repository

git clone <repository-url>
cd trekka-api

Install dependencies

make install-deps

Or manually:

go mod download

Install exiftool (required for video metadata)

Linux:

sudo apt-get update
sudo apt-get install libimage-exiftool-perl

macOS:

brew install exiftool

Verify installation:

exiftool -ver

Configuration

Environment Variables

Create a .env file in the root directory (see .env.example):

# Server Configuration
PORT=8080

# Firebase Configuration
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_BUCKET_NAME=your-project-id.appspot.com
FIREBASE_CREDENTIALS_PATH=firebase-service-account.json
# Or use FIREBASE_CREDENTIALS_JSON for raw JSON (e.g., on Vercel)
FIRESTORE_COLLECTION=images

# Cache Configuration
CACHE_TTL=15m
CACHE_CLEANUP_INTERVAL=10m

# Security
API_KEYS=your-secret-api-key-1,your-secret-api-key-2

# CORS origins (comma-separated, use * for all origins)
ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com

# Google Drive Sync (Optional)
GOOGLE_DRIVE_FOLDER_ID=your-drive-folder-id
DRIVE_SYNC_INTERVAL=5m
DRIVE_BACKFILL_ON_STARTUP=false

Firebase Setup

  1. Create a Firebase project at Firebase Console
  2. Enable Cloud Storage and Firestore
  3. Generate a service account key:
    • Go to Project Settings → Service Accounts
    • Click "Generate New Private Key"
    • Save the JSON file as firebase-service-account.json in the project root
  4. Update the environment variables with your Firebase project details

Usage

Development

Run the application locally:

make run

Or with live reload (requires air):

make dev

Build

Build the binary:

make build

The binaries will be created in bin/:

  • bin/server - API server
  • bin/update-metadata - Metadata update utility

Docker

Build and run with Docker Compose:

docker-compose up --build

Or manually with Docker:

docker build -t trekka-api .
docker run -p 8080:8080 \
  -e FIREBASE_BUCKET_NAME=your-bucket \
  -e API_KEYS=your-api-key \
  -v $(pwd)/firebase-service-account.json:/root/firebase-service-account.json:ro \
  trekka-api

API Endpoints

Health Check

GET /health

Returns the health status of the API. No authentication required.

Response:

{
  "status": "healthy"
}

Get Image

GET /image?fileName=<filename>

Retrieves and serves an image or video from Firebase Storage. Automatically converts HEIC/HEIF images to JPEG.

Authentication: Required (API key in X-API-Key header)

Query Parameters:

  • fileName (required): Name of the media file

Response:

  • Returns a redirect (302) to the signed URL for direct download from Firebase Storage
  • Headers include:
    • X-Geo-Location: Geographic location metadata (if available)
    • X-Content-Type: Media MIME type
    • Cache-Control: public, max-age=900 (15 minutes)
    • CDN-Cache-Control: public, max-age=86400 (24 hours for edge caching)

Example:

curl -H "X-API-Key: your-api-key" \
  http://localhost:8080/image?fileName=photo.heic

List Images

GET /images/list?limit=<limit>&page=<page>

Retrieves a paginated list of image metadata from Firestore.

Authentication: Required (API key in X-API-Key header)

Query Parameters:

  • limit (optional): Number of items per page (max 1000, default: 1000)
  • page (optional): Page number (0-indexed, default: 0)

Response:

[
  {
    "id": "doc-id",
    "fileName": "photo.jpg",
    "contentType": "image/jpeg",
    "coordinates": {
      "lat": "37.774900",
      "lng": "-122.419400"
    },
    "storagePath": "photo.jpg",
    "geoLocation": "San Francisco, United States",
    "formattedDate": "Wednesday, 15 January 2025, 14:30",
    "resolution": [4032, 3024],
    "takenAt": "2025-01-15T14:30:45Z",
    "createdAt": "2025-01-15T10:30:00Z",
    "updatedAt": "2025-01-15T10:30:00Z"
  }
]

Example:

curl -H "X-API-Key: your-api-key" \
  "http://localhost:8080/images/list?limit=20&page=0"

Project Structure

trekka-api/
├── cmd/
│   ├── server/
│   │   └── main.go              # API server entry point
│   └── update-metadata/
│       ├── main.go              # Metadata update tool
│       └── update-dates.go      # Date/time metadata updater
├── internal/
│   ├── config/
│   │   └── config.go            # Configuration loading
│   ├── handlers/
│   │   ├── handler.go           # Handler initialization
│   │   ├── health.go            # Health check handler
│   │   └── image.go             # Image/video handlers
│   ├── middleware/
│   │   ├── auth.go              # API key authentication
│   │   ├── cors.go              # CORS middleware
│   │   ├── logger.go            # Request logging
│   │   ├── ratelimit.go         # Rate limiting
│   │   └── requestid.go         # Request ID tracking
│   ├── models/
│   │   └── image.go             # Data models
│   ├── router/
│   │   └── router.go            # Route definitions
│   ├── server/
│   │   └── init.go              # Server initialization
│   ├── services/
│   │   ├── cache.go             # In-memory cache service
│   │   ├── driveClient.go       # Google Drive API client
│   │   ├── driveService.go      # Google Drive sync service
│   │   ├── firestore.go         # Firestore operations
│   │   ├── geocoding.go         # Reverse geocoding service
│   │   ├── image.go             # Image processing service
│   │   ├── metadata.go          # Metadata extraction orchestration
│   │   └── storage.go           # Firebase Storage operations
│   ├── utils/
│   │   ├── drive.go             # Drive utility functions
│   │   ├── exif.go              # EXIF data extraction
│   │   ├── heicFunctions.go     # HEIC/HEIF conversion
│   │   └── mp4.go               # MP4 video metadata extraction
│   └── errors/
│       └── errors.go            # Custom error types
├── docs/
│   ├── docs.go                  # Swagger documentation
│   ├── swagger.json             # OpenAPI spec (JSON)
│   └── swagger.yaml             # OpenAPI spec (YAML)
├── .env.example                 # Example environment variables
├── docker-compose.yml           # Docker Compose configuration
├── Dockerfile                   # Multi-stage Docker build
├── go.mod                       # Go module dependencies
├── go.sum                       # Dependency checksums
├── Makefile                     # Build automation
└── README.md                    # This file

Available Make Commands

Application Commands

make help                         # Show all available commands
make build                        # Build the application binaries (server + update-metadata)
make run                          # Run the API server
make dev                          # Run with live reload (requires air)
make test                         # Run tests
make test-coverage                # Run tests with coverage report
make clean                        # Clean build artifacts
make install-deps                 # Install dependencies
make tidy                         # Tidy go.mod
make fmt                          # Format code
make lint                         # Run linter (requires golangci-lint)

Metadata Management Commands

Update Metadata from Storage/Drive

# Update ALL files in Firestore
make sync-update-metadata

# Update ONLY files missing GPS/location data
make sync-update-metadata-empty

# Force re-download from Drive for all files (slower but most accurate)
make sync-update-metadata-backfill

# Force re-download from Drive for files missing GPS/location only
make sync-update-metadata-backfill-empty

Dry Run (Preview Changes)

# Preview all updates without making changes
make sync-update-metadata-dry-run

# Preview updates for empty fields only
make sync-update-metadata-empty-dry-run

# Preview backfill updates
make sync-update-metadata-backfill-dry-run

Background Sync (Recommended): Enable automatic syncing when the API server starts:

# .env configuration
DRIVE_SYNC_INTERVAL=5m
DRIVE_BACKFILL_ON_STARTUP=true  # First run only

# Start server with sync
make run

Metadata Extraction Features

Image Metadata (EXIF)

Automatically extracts from JPEG, PNG, HEIC/HEIF images:

  • GPS Coordinates: Latitude and longitude
  • Timestamps: Photo capture date and time with timezone
  • Resolution: Image width and height in pixels
  • Location Names: Reverse geocoding converts GPS to "City, Country" format

Video Metadata (MP4)

Automatically extracts from MP4 videos using exiftool:

  • GPS Coordinates: Latitude and longitude (if embedded)
  • Creation Date: Video recording timestamp
  • Resolution: Video dimensions (width × height)
  • Location Names: Reverse geocoding for videos with GPS data

Reverse Geocoding

  • Uses OpenStreetMap Nominatim API (free, no API key required)
  • Converts GPS coordinates to human-readable locations
  • In-memory caching to minimize API calls
  • Automatic rate limiting (1 request/sec as per Nominatim policy)
  • Gracefully handles missing or invalid coordinates

Deployment

Google Cloud Run (Recommended)

This project is optimized for Google Cloud Run deployment with Docker containers.

Prerequisites

  • Google Cloud account with billing enabled
  • Firebase service account JSON credentials
  • Docker installed locally (for testing)

Deployment Steps

  1. Build and deploy:

    # Build and push to Google Container Registry
    gcloud builds submit --config cloudbuild.yaml
    
    # Deploy to Cloud Run
    gcloud run deploy trekka-api \
      --image gcr.io/YOUR_PROJECT_ID/trekka-api:latest \
      --platform managed \
      --region us-central1 \
      --allow-unauthenticated \
      --min-instances 0 \
      --max-instances 10 \
      --set-env-vars FIREBASE_PROJECT_ID=your-project-id \
      --set-env-vars FIREBASE_BUCKET_NAME=your-bucket \
      --set-env-vars FIRESTORE_COLLECTION=images \
      --set-secrets FIREBASE_CREDENTIALS_JSON=firebase-credentials:latest \
      --set-secrets API_KEYS=api-keys:latest
  2. Configure secrets (one-time setup):

    # Store Firebase credentials in Secret Manager
    gcloud secrets create firebase-credentials \
      --data-file=firebase-service-account.json
    
    # Store API keys in Secret Manager (comma-separated)
    echo -n "your-api-key-1,your-api-key-2" | \
      gcloud secrets create api-keys --data-file=-

Cloud Build Configuration

The cloudbuild.yaml file enables Docker layer caching for faster builds:

  • First build: 3-5 minutes
  • Subsequent builds: 1-2 minutes (with caching)
  • Cost: ~$0.01/month for image storage

Support

For issues and questions, please open an issue in the GitHub repository.

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors