Storage¶
Strategy¶
Storage is opt-in via FACE_API_STORAGE_ENABLED=true. When enabled:
- Every uploaded image is stored in S3 under
originals/{image_id}/{filename} - Every processing result creates a
ProcessingJobrow in PostgreSQL - Masked images (from
/maskendpoint) are stored underprocessed/{image_id}/{job_id}.{format} - Storage failures are logged but never fail API responses (non-blocking)
When disabled, the API operates statelessly with zero persistence overhead.
Database¶
Engine: PostgreSQL 18 via SQLAlchemy 2.0 async + asyncpg
Schema¶
images table¶
| Column | Type | Description |
|---|---|---|
| id | UUID (PK) | Auto-generated |
| filename | VARCHAR(255) | Original upload filename |
| content_type | VARCHAR(100) | MIME type |
| size_bytes | BIGINT | File size |
| width | INT | Image width in pixels |
| height | INT | Image height in pixels |
| s3_key | VARCHAR(500) | S3 object key for original |
| created_at | TIMESTAMPTZ | Upload timestamp |
processing_jobs table¶
| Column | Type | Description |
|---|---|---|
| id | UUID (PK) | Auto-generated |
| image_id | UUID (FK) | References images.id ON DELETE CASCADE |
| job_type | VARCHAR(20) | detect or mask |
| status | VARCHAR(50) | completed (currently only value) |
| confidence_threshold | FLOAT | Detection threshold used |
| nms_threshold | FLOAT | NMS threshold used |
| blur_method | VARCHAR(50) | Null for detect jobs |
| blur_strength | INT | Null for detect jobs |
| padding | FLOAT | Null for detect jobs |
| output_format | VARCHAR(20) | jpeg/png/webp, null for detect |
| output_quality | INT | 1-100, null for detect |
| face_count | INT | Number of faces detected |
| detections | JSONB | Full detection results |
| processed_s3_key | VARCHAR(500) | S3 key for masked image |
| processed_size_bytes | BIGINT | Masked image file size |
| inference_ms | FLOAT | SCRFD inference time |
| total_ms | FLOAT | Total request processing time |
| created_at | TIMESTAMPTZ | Job creation timestamp |
Relationships¶
Image→ProcessingJob(1:N, cascade delete)- Deleting an image removes all associated jobs and S3 objects
S3 Object Layout¶
{bucket}/
├── originals/
│ └── {image_id}/
│ └── {original_filename}
└── processed/
└── {image_id}/
└── {job_id}.{format}
Key Format Examples¶
originals/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg
processed/a1b2c3d4-e5f6-7890-abcd-ef1234567890/f0e1d2c3-b4a5-6789-0abc-def123456789.jpeg
Configuration¶
| Variable | Default | Description |
|---|---|---|
FACE_API_STORAGE_ENABLED |
false |
Enable/disable all storage |
FACE_API_DATABASE_URL |
postgresql+asyncpg://postgres:postgres@localhost:5432/faceapi |
PostgreSQL connection |
FACE_API_S3_ENDPOINT |
http://localhost:9000 |
S3 endpoint (MinIO for dev) |
FACE_API_S3_ACCESS_KEY |
minioadmin |
S3 access key |
FACE_API_S3_SECRET_KEY |
minioadmin |
S3 secret key |
FACE_API_S3_BUCKET |
face-api |
S3 bucket name |
FACE_API_S3_REGION |
us-east-1 |
S3 region |