564 lines
22 KiB
Markdown
564 lines
22 KiB
Markdown
# SEREACT - Secure Image Management API
|
|
|
|
SEREACT is a secure API for storing, organizing, and retrieving images with advanced search capabilities.
|
|
|
|
## Features
|
|
|
|
- Secure image storage in Google Cloud Storage
|
|
- Team-based organization and access control
|
|
- API key authentication
|
|
- Semantic search using image embeddings
|
|
- Metadata extraction and storage
|
|
- Image processing capabilities
|
|
- Multi-team support
|
|
|
|
## Architecture
|
|
|
|
```
|
|
sereact/
|
|
├── images/ # Sample images for testing
|
|
├── deployment/ # Deployment configurations
|
|
│ ├── cloud-run/ # Google Cloud Run configuration
|
|
│ └── terraform/ # Infrastructure as code
|
|
├── docs/ # Documentation
|
|
│ └── api/ # API documentation
|
|
├── scripts/ # Utility scripts
|
|
├── src/ # Source code
|
|
│ ├── api/ # API endpoints and routers
|
|
│ │ └── v1/ # API version 1 routes
|
|
│ ├── auth/ # Authentication and authorization
|
|
│ ├── config/ # Configuration management
|
|
│ ├── models/ # Database models
|
|
│ ├── services/ # Business logic services
|
|
│ └── utils/ # Utility functions
|
|
├── tests/ # Test code
|
|
│ ├── api/ # API tests
|
|
│ ├── auth/ # Authentication tests
|
|
│ ├── models/ # Model tests
|
|
│ └── services/ # Service tests
|
|
├── main.py # Application entry point
|
|
├── requirements.txt # Python dependencies
|
|
└── README.md # This file
|
|
```
|
|
|
|
## System Architecture
|
|
|
|
```
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
│ │ │ │ │ │
|
|
│ FastAPI │ ───────▶│ Firestore │◀────────│ Cloud │
|
|
│ Backend │ │ Database │ │ Functions │
|
|
│ │ │ │ │ │
|
|
└─────┬───────┘ └─────────────┘ └──────┬──────┘
|
|
│ │
|
|
│ │
|
|
▼ │
|
|
┌─────────────┐ ┌─────────────┐ │
|
|
│ │ │ │ │
|
|
│ Cloud │ │ Pub/Sub │ │
|
|
│ Storage │────────▶│ Queue │────────────────┘
|
|
│ │ │ │
|
|
└─────────────┘ └─────────────┘
|
|
│
|
|
│
|
|
▼
|
|
┌─────────────┐ ┌─────────────┐
|
|
│ │ │ │
|
|
│ Cloud │ │ Pinecone │
|
|
│ Vision API │────────▶│ Vector DB │
|
|
│ │ │ │
|
|
└─────────────┘ └─────────────┘
|
|
```
|
|
|
|
1. **Image Upload Flow**:
|
|
- Images are uploaded through the FastAPI backend
|
|
- Images are stored in Cloud Storage
|
|
- A message is published to Pub/Sub queue
|
|
|
|
2. **Embedding Generation Flow**:
|
|
- Cloud Function is triggered by Pub/Sub message
|
|
- Function calls Cloud Vision API to generate image embeddings
|
|
- Embeddings are stored in Pinecone Vector DB
|
|
|
|
3. **Search Flow**:
|
|
- Search queries processed by FastAPI backend
|
|
- Vector similarity search performed against Pinecone
|
|
- Results combined with metadata from Firestore
|
|
|
|
## Technology Stack
|
|
|
|
- FastAPI - Web framework
|
|
- Firestore - Database
|
|
- Google Cloud Storage - Image storage
|
|
- Google Pub/Sub - Message queue
|
|
- Google Cloud Functions - Serverless computing
|
|
- Google Cloud Vision API - Image analysis and embedding generation
|
|
- Pinecone - Vector database for semantic search
|
|
- Pydantic - Data validation
|
|
|
|
## Setup and Installation
|
|
|
|
### Prerequisites
|
|
|
|
- Python 3.8+
|
|
- Google Cloud account with Firestore, Storage, Pub/Sub, and Cloud Functions enabled
|
|
- Pinecone account for vector database
|
|
|
|
### Installation
|
|
|
|
1. Clone the repository:
|
|
```bash
|
|
git clone https://github.com/yourusername/sereact.git
|
|
cd sereact
|
|
```
|
|
|
|
2. Create and activate a virtual environment:
|
|
```bash
|
|
python -m venv venv
|
|
source venv/bin/activate # Linux/macOS
|
|
venv\Scripts\activate # Windows
|
|
```
|
|
|
|
3. Install dependencies:
|
|
```bash
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
4. Create a `.env` file with the following environment variables:
|
|
```
|
|
# Firestore
|
|
FIRESTORE_PROJECT_ID=your-gcp-project-id
|
|
FIRESTORE_CREDENTIALS_FILE=path/to/firestore-credentials.json
|
|
|
|
# Google Cloud Storage
|
|
GCS_BUCKET_NAME=your-bucket-name
|
|
GCS_CREDENTIALS_FILE=path/to/credentials.json
|
|
|
|
# Google Pub/Sub
|
|
PUBSUB_TOPIC=image-processing-topic
|
|
|
|
# Google Cloud Vision
|
|
VISION_API_ENABLED=true
|
|
|
|
# Security
|
|
API_KEY_SECRET=your-secret-key
|
|
|
|
# Vector database
|
|
VECTOR_DB_API_KEY=your-pinecone-api-key
|
|
VECTOR_DB_ENVIRONMENT=your-pinecone-environment
|
|
VECTOR_DB_INDEX_NAME=image-embeddings
|
|
```
|
|
|
|
5. Run the application:
|
|
```bash
|
|
uvicorn main:app --reload
|
|
```
|
|
|
|
6. Visit `http://localhost:8000/docs` in your browser to access the API documentation.
|
|
|
|
## API Endpoints
|
|
|
|
The API provides the following main endpoints:
|
|
|
|
- `/api/v1/auth/*` - Authentication and API key management
|
|
- `/api/v1/teams/*` - Team management
|
|
- `/api/v1/users/*` - User management
|
|
- `/api/v1/images/*` - Image upload, download, and management
|
|
- `/api/v1/search/*` - Image search functionality
|
|
|
|
Refer to the Swagger UI documentation at `/docs` for detailed endpoint information.
|
|
|
|
## Development
|
|
|
|
### Running Tests
|
|
|
|
```bash
|
|
pytest
|
|
```
|
|
|
|
### Creating a New API Version
|
|
|
|
1. Create a new package under `src/api/` (e.g., `v2`)
|
|
2. Implement new endpoints
|
|
3. Update the main.py file to include the new routers
|
|
|
|
## Deployment
|
|
|
|
### Google Cloud Run
|
|
|
|
1. Build the Docker image:
|
|
```bash
|
|
docker build -t gcr.io/your-project/sereact .
|
|
```
|
|
|
|
2. Push to Google Container Registry:
|
|
```bash
|
|
docker push gcr.io/your-project/sereact
|
|
```
|
|
|
|
3. Deploy to Cloud Run:
|
|
```bash
|
|
gcloud run deploy sereact --image gcr.io/your-project/sereact --platform managed
|
|
```
|
|
|
|
## Local Development with Docker Compose
|
|
|
|
To run the application locally using Docker Compose:
|
|
|
|
1. Make sure you have Docker and Docker Compose installed
|
|
2. Run the following command in the project root:
|
|
|
|
```bash
|
|
docker compose up
|
|
```
|
|
|
|
This will:
|
|
- Build the API container based on the Dockerfile
|
|
- Mount your local codebase into the container for live reloading
|
|
- Mount your Firestore credentials for authentication
|
|
- Expose the API on http://localhost:8000
|
|
|
|
To stop the containers:
|
|
|
|
```bash
|
|
docker compose down
|
|
```
|
|
|
|
To rebuild containers after making changes to the Dockerfile or requirements:
|
|
|
|
```bash
|
|
docker compose up --build
|
|
```
|
|
|
|
## Additional Information
|
|
|
|
## Design Decisions
|
|
|
|
### Database Selection: Firestore
|
|
|
|
- **Document-oriented model**: Ideal for hierarchical team/user/image data structures with flexible schemas
|
|
- **Real-time capabilities**: Enables live updates for collaborative features
|
|
- **Automatic scaling**: Handles variable workloads without manual intervention
|
|
- **ACID transactions**: Ensures data integrity for critical operations
|
|
- **Security rules**: Granular access control at the document level
|
|
- **Seamless GCP integration**: Works well with other Google Cloud services
|
|
|
|
### Storage Solution: Google Cloud Storage
|
|
|
|
- **Object storage optimized for binary data**: Perfect for image files of varying sizes
|
|
- **Content-delivery capabilities**: Fast global access to images
|
|
- **Lifecycle management**: Automated rules for moving less-accessed images to cheaper storage tiers
|
|
- **Fine-grained access control**: Secure pre-signed URLs for temporary access
|
|
- **Versioning support**: Maintains image history when needed
|
|
- **Cost-effective**: Pay only for what you use with no minimum fees
|
|
|
|
### Decoupled Embedding Generation
|
|
|
|
We deliberately decoupled the image embedding process from the upload flow for several reasons:
|
|
|
|
1. **Upload responsiveness**: Users experience fast upload times since compute-intensive embedding generation happens asynchronously
|
|
2. **System resilience**: Upload service remains available even if embedding service experiences issues
|
|
3. **Independent scaling**: Each component can scale based on its specific resource needs
|
|
4. **Cost optimization**: Cloud Functions only run when needed, avoiding idle compute costs
|
|
5. **Processing flexibility**: Can modify embedding algorithms without affecting the core upload flow
|
|
6. **Batch processing**: Potential to batch embedding generation for further cost optimization
|
|
|
|
### Latency Considerations
|
|
|
|
- **API response times**: FastAPI provides high-performance request handling
|
|
- **Caching strategy**: Frequently accessed images and search results are cached
|
|
- **Edge deployment**: Cloud Run regional deployment optimizes for user location
|
|
- **Async processing**: Non-blocking operations for concurrent request handling
|
|
- **Embedding pre-computation**: All embeddings are generated ahead of time, making searches fast
|
|
- **Search optimization**: Vector database indices are optimized for quick similarity searches
|
|
|
|
### Cost Optimization
|
|
|
|
- **Serverless architecture**: Pay-per-use model eliminates idle infrastructure costs
|
|
- **Storage tiering**: Automatic migration of older images to cheaper storage classes
|
|
- **Compute efficiency**: Cloud Functions minimize compute costs through precise scaling
|
|
- **Caching**: Reduces repeated processing of the same data
|
|
- **Resource throttling**: Rate limits prevent unexpected usage spikes
|
|
- **Embedding dimensions**: Balancing vector size for accuracy vs. storage costs
|
|
- **Query optimization**: Efficient search patterns to minimize vector database operations
|
|
|
|
### Scalability Approach
|
|
|
|
- **Horizontal scaling**: All components can scale out rather than up
|
|
- **Stateless design**: API servers maintain no local state, enabling easy replication
|
|
- **Queue-based workload distribution**: Prevents system overload during traffic spikes
|
|
- **Database sharding capability**: Firestore automatically shards data for growth
|
|
- **Vector database partitioning**: Pinecone handles distributed vector search at scale
|
|
- **Load balancing**: Traffic distributed across multiple service instances
|
|
- **Microservice architecture**: Individual components can scale independently based on demand
|
|
|
|
### Security Architecture
|
|
|
|
- **API key authentication**: Simple but effective access control for machine-to-machine communication
|
|
- **Team-based permissions**: Multi-tenant isolation with hierarchical access controls
|
|
- **Encrypted storage**: All data encrypted at rest and in transit
|
|
- **Secret management**: Sensitive configuration isolated from application code
|
|
- **Minimal attack surface**: Limited public endpoints with appropriate rate limiting
|
|
- **Audit logging**: Comprehensive activity tracking for security analysis
|
|
|
|
### API Key Authentication System
|
|
|
|
SEREACT uses a simple API key authentication system:
|
|
|
|
#### Key Generation and Storage
|
|
|
|
- API keys are generated as cryptographically secure random strings
|
|
- Each team can have multiple API keys
|
|
- Keys are never stored in plaintext - only secure hashes are saved to the database
|
|
|
|
#### Authentication Flow
|
|
|
|
1. Client includes the API key in requests via the `X-API-Key` HTTP header
|
|
2. Auth middleware validates the key by:
|
|
- Hashing the provided key
|
|
- Querying Firestore for a matching hash
|
|
- Verifying the key belongs to the appropriate team
|
|
3. Request is either authorized to proceed or rejected with 401/403 status
|
|
|
|
#### Key Management
|
|
|
|
- API keys can be created through the API:
|
|
- User makes an authenticated request
|
|
- The system generates a new random key and returns it ONCE
|
|
- Only the hash is stored in the database
|
|
- Keys can be viewed and revoked through dedicated endpoints
|
|
- Each API key use is logged for audit purposes
|
|
|
|
#### Design Considerations
|
|
|
|
- No master/global API key exists to eliminate single points of failure
|
|
- All keys are scoped to specific teams to enforce multi-tenant isolation
|
|
- Keys are transmitted only over HTTPS to prevent interception
|
|
|
|
This authentication approach balances security with usability for machine-to-machine API interactions, while maintaining complete isolation between different teams using the system.
|
|
|
|
### Database Structure
|
|
|
|
The Firestore database is organized into collections and documents with the following structure:
|
|
|
|
#### Collections and Documents
|
|
|
|
```
|
|
firestore-root/
|
|
├── teams/ # Teams collection
|
|
│ └── {team_id}/ # Team document
|
|
│ ├── name: string # Team name
|
|
│ ├── created_at: timestamp
|
|
│ ├── updated_at: timestamp
|
|
│ │
|
|
│ ├── users/ # Team users subcollection
|
|
│ │ └── {user_id}/ # User document in team context
|
|
│ │ ├── role: string (admin, member, viewer)
|
|
│ │ ├── joined_at: timestamp
|
|
│ │ └── status: string (active, inactive)
|
|
│ │
|
|
│ ├── api_keys/ # Team API keys subcollection
|
|
│ │ └── {api_key_id}/ # API key document
|
|
│ │ ├── key_hash: string # Hashed API key value
|
|
│ │ ├── name: string # Key name/description
|
|
│ │ ├── created_at: timestamp
|
|
│ │ ├── expires_at: timestamp
|
|
│ │ ├── created_by: user_id
|
|
│ │ └── permissions: array # Specific permissions
|
|
│ │
|
|
│ └── collections/ # Image collections subcollection
|
|
│ └── {collection_id}/ # Collection document
|
|
│ ├── name: string
|
|
│ ├── description: string
|
|
│ ├── created_at: timestamp
|
|
│ ├── created_by: user_id
|
|
│ └── metadata: map # Collection-level metadata
|
|
│
|
|
├── users/ # Global users collection
|
|
│ └── {user_id}/ # User document
|
|
│ ├── email: string
|
|
│ ├── name: string
|
|
│ ├── created_at: timestamp
|
|
│ └── settings: map # User preferences
|
|
│
|
|
└── images/ # Images collection
|
|
└── {image_id}/ # Image document
|
|
├── filename: string
|
|
├── storage_path: string # GCS path
|
|
├── mime_type: string
|
|
├── size_bytes: number
|
|
├── width: number
|
|
├── height: number
|
|
├── uploaded_at: timestamp
|
|
├── uploaded_by: user_id
|
|
├── team_id: string
|
|
├── collection_id: string # Optional parent collection
|
|
├── status: string (processing, ready, error)
|
|
├── embedding_id: string # Reference to vector DB
|
|
├── metadata: map # Extracted and custom metadata
|
|
│ ├── labels: array # AI-generated labels
|
|
│ ├── colors: array # Dominant colors
|
|
│ ├── objects: array # Detected objects
|
|
│ ├── custom: map # User-defined metadata
|
|
│ └── exif: map # Original image EXIF data
|
|
│
|
|
└── processing/ # Processing subcollection
|
|
└── {job_id}/ # Processing job document
|
|
├── type: string # embedding, analysis, etc.
|
|
├── status: string # pending, running, complete, error
|
|
├── created_at: timestamp
|
|
├── updated_at: timestamp
|
|
├── completed_at: timestamp
|
|
└── error: string # Error message if applicable
|
|
```
|
|
|
|
#### Key Relationships and Indexes
|
|
|
|
- **Team-User**: Many-to-many relationship through the team's users subcollection
|
|
- **Team-Image**: One-to-many relationship (images belong to one team)
|
|
- **Collection-Image**: One-to-many relationship (images can belong to one collection)
|
|
- **User-Image**: One-to-many relationship (upload attribution)
|
|
|
|
#### Composite Indexes
|
|
|
|
The following composite indexes are created to support efficient queries:
|
|
|
|
1. `images` collection:
|
|
- `team_id` ASC, `uploaded_at` DESC → List recent images for a team
|
|
- `team_id` ASC, `collection_id` ASC, `uploaded_at` DESC → List recent images in a collection
|
|
- `team_id` ASC, `status` ASC, `uploaded_at` ASC → Find oldest processing images
|
|
- `uploaded_by` ASC, `uploaded_at` DESC → List user's recent uploads
|
|
|
|
2. `users` subcollection (within teams):
|
|
- `role` ASC, `joined_at` DESC → List team members by role
|
|
|
|
#### Security Rules
|
|
|
|
Firestore security rules enforce the following access patterns:
|
|
|
|
- Team admins can read/write all team data
|
|
- Team members can read all team data but can only write to collections and images
|
|
- Team viewers can only read team data
|
|
- Users can only access teams they belong to
|
|
- API keys have scoped access based on their assigned permissions
|
|
|
|
## License
|
|
|
|
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
|
|
## API Modules Architecture
|
|
|
|
The SEREACT API is organized into the following key modules to ensure separation of concerns and maintainable code:
|
|
|
|
```
|
|
src/
|
|
├── api/ # API endpoints and routers
|
|
│ └── v1/ # API version 1 routes
|
|
├── auth/ # Authentication and authorization
|
|
├── config/ # Configuration management
|
|
├── models/ # Database models
|
|
├── services/ # Business logic services
|
|
└── utils/ # Utility functions
|
|
```
|
|
|
|
### Module Responsibilities
|
|
|
|
#### Router Module
|
|
- Defines API endpoints and routes
|
|
- Handles HTTP requests and responses
|
|
- Validates incoming request data
|
|
- Directs requests to appropriate services
|
|
- Implements API versioning
|
|
|
|
#### Auth Module
|
|
- Manages user authentication
|
|
- Handles API key validation and verification
|
|
- Implements role-based access control
|
|
- Provides security middleware
|
|
- Manages user sessions and tokens
|
|
|
|
#### Services Module
|
|
- Contains core business logic
|
|
- Orchestrates operations across multiple resources
|
|
- Implements domain-specific rules and workflows
|
|
- Integrates with external services (Cloud Vision, Storage)
|
|
- Handles image processing and embedding generation
|
|
|
|
#### Models Module
|
|
- Defines data structures and schemas
|
|
- Provides database entity representations
|
|
- Handles data validation and serialization
|
|
- Implements data relationships and constraints
|
|
- Manages database migrations
|
|
|
|
#### Utils Module
|
|
- Provides helper functions and utilities
|
|
- Implements common functionality used across modules
|
|
- Handles error processing and logging
|
|
- Provides formatting and conversion utilities
|
|
- Implements reusable middleware components
|
|
|
|
#### Config Module
|
|
- Manages application configuration
|
|
- Handles environment variable loading
|
|
- Provides centralized settings management
|
|
- Configures service connections and credentials
|
|
- Defines application constants and defaults
|
|
|
|
### Module Interactions
|
|
|
|
```
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
│ │ │ │ │ │
|
|
│ Router │ ───────▶│ Services │ ◀───────│ Config │
|
|
│ Module │ │ Module │ │ Module │
|
|
│ │ │ │ │ │
|
|
└──────┬──────┘ └──────┬──────┘ └─────────────┘
|
|
│ │
|
|
│ │
|
|
▼ ▼
|
|
┌─────────────┐ ┌─────────────┐
|
|
│ │ │ │
|
|
│ Auth │ │ Models │
|
|
│ Module │ │ Module │
|
|
│ │ │ │
|
|
└──────┬──────┘ └──────┬──────┘
|
|
│ │
|
|
│ │
|
|
└───────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────┐
|
|
│ │
|
|
│ Utils │
|
|
│ Module │
|
|
│ │
|
|
└─────────────┘
|
|
```
|
|
|
|
The modules interact in the following ways:
|
|
- **Request Flow**:
|
|
- Client request arrives at the Router Module
|
|
- Auth Module validates the request authentication
|
|
- Router delegates to appropriate Service functions
|
|
- Service uses Models to interact with the database
|
|
- Service returns data to Router which formats the response
|
|
|
|
- **Cross-Cutting Concerns**:
|
|
- Config Module provides settings to all other modules
|
|
- Utils Module provides helper functions across the application
|
|
- Auth Module secures access to routes and services
|
|
|
|
- **Dependency Direction**:
|
|
- Router depends on Services and Auth
|
|
- Services depend on Models and Config
|
|
- Models depend on Utils for helper functions
|
|
- Auth depends on Models for user information
|
|
- All modules may use Utils and Config
|
|
|
|
This modular architecture provides several benefits:
|
|
- **Maintainability**: Changes in one module have minimal impact on others
|
|
- **Testability**: Modules can be tested in isolation with mocked dependencies
|
|
- **Scalability**: New features can be added by extending existing modules
|
|
- **Reusability**: Common functionality is centralized for consistent implementation
|
|
- **Security**: Authentication and authorization are handled consistently |