Multi-tenant image management system built with NestJS 10, Prisma ORM, and PostgreSQL.
✅ Multi-tenant Architecture: Complete isolation between clients with API key authentication
✅ Image Management: Upload, store, and serve images with automatic WebP conversion
✅ Avatar System: Single avatar per user with automatic replacement
✅ Album Support: Organize images in public or private albums
✅ Automatic Optimization: Scheduled jobs for compression and EXIF removal
✅ On-Demand Resizing: Generate custom sizes and formats on the fly
✅ Secure Access: Token-based access for private albums
✅ Rate Limiting: Built-in throttling protection
✅ API Documentation: Auto-generated Swagger/OpenAPI docs
- Framework: NestJS 10 (Node.js/TypeScript)
- Database: PostgreSQL with Prisma ORM
- Image Processing: Sharp library
- Authentication: JWT tokens, API Keys
- Package Manager: pnpm (enforced via preinstall script)
- Testing: Jest
- API Documentation: Swagger/OpenAPI
- Caching: HTTP ETag & Cache-Control headers
- Storage: Local file system (extensible to S3/Cloud)
# Install dependencies (pnpm only)
pnpm install
# Setup database
cp .env.example .env
# Edit .env with your database credentials
# Generate Prisma client
pnpm run prisma:generate
# Run migrations
pnpm run prisma:migrate
# Seed database with demo data (optional)
pnpm run prisma:seed# Development mode with hot reload
pnpm run start:dev
# Production build
pnpm run build
pnpm run start:prodThe API will be available at http://localhost:3000
Complete API documentation with interactive testing interface:
- Swagger UI:
http://localhost:3000/docs
All endpoints, request/response schemas, and examples are available in the Swagger documentation.
- Client sends request with
X-API-Keyheader (validates and retrieves client - REQUIRED) ClientInterceptorvalidates and attaches client info to request- All queries are scoped to
clientId - User isolation via
X-User-Idheader for user-specific operations
Security Note: Only API Key authentication is supported.
storage/
├── {domain}/
│ ├── images/
│ │ └── {imageId}/
│ │ ├── original.webp
│ │ └── thumb.webp
│ └── avatars/
│ └── {userId}/
│ ├── avatar.webp
│ └── thumb.webp
X-API-Key: {your-api-key} # Required for authentication
X-User-Id: {externalUserId} # Required for user-specific operations
The system runs hourly jobs to:
- Convert images to WebP format
- Remove EXIF metadata
- Compress images
- Generate optimized thumbnails
Jobs run automatically via NestJS Schedule module.
Key environment variables in .env:
# Server
APP_PORT=3000
APP_URL=http://localhost:3000
API_PREFIX=v2
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/fileharbor?schema=public"
# Storage
STORAGE_PATH=./storage
MAX_FILE_SIZE=10485760 # 10MB
# Image Processing
WEBP_QUALITY=90
JPEG_QUALITY=85
THUMBNAIL_SIZE=300
# Rate Limiting
THROTTLE_TTL=60 # seconds
THROTTLE_LIMIT=10 # requests per TTL
# Caching
CACHE_TTL=60 # seconds
# Logging (optional)
LOGS_TOKEN=your-betterstack-token- Client: Multi-tenant clients with API keys
- User: Users within each client
- Image: Image metadata and storage paths
- Avatar: Single avatar per user
- Album: Image collections (public/private)
- AlbumImage: Many-to-many relation
- AlbumToken: Access tokens for private albums
- API key authentication for clients
- User-level authorization
- Token-based album access
- Rate limiting on all endpoints
- EXIF data sanitization
- File type validation
- Size limits on uploads
- ETag caching for static assets
- Cache-Control headers
- On-demand image generation
- Batch processing for optimization jobs
- Database indexing on frequently queried fields
# Watch mode
pnpm run start:dev
# Run tests
pnpm run test
pnpm run test:watch
pnpm run test:cov
# E2E tests
pnpm run test:e2e
# Linting and formatting
pnpm run lint
pnpm run format
# Prisma Studio (DB GUI)
pnpm run prisma:studio
# Generate migration
pnpm run prisma:migrate- Set
NODE_ENV=production - Configure PostgreSQL connection
- Set up file storage (local or cloud)
- Configure reverse proxy (Nginx)
- Enable SSL/TLS
- Set up monitoring and logging
- Configure
API_PREFIXif different from default (v2)
- Advanced Image Transformations: Watermarks, filters, blur, grayscale, sepia effects
- Batch Upload API: Multiple image upload in single request with progress tracking
- Image Collections: Extended album functionality with sorting, filtering, and bulk operations
- Enhanced Analytics Dashboard: Real-time usage metrics, popular images, client statistics
- Performance Metrics: API response times, optimization job statistics, storage usage trends
- Client Usage Reports: Bandwidth consumption, storage quotas, API call analytics
- API Rate Limiting per Client: Individual throttling limits based on client subscription
- Audit Logging: Complete action history for compliance and security monitoring
- AI-Powered Features: Auto-tagging, content moderation, duplicate detection
- Dynamic Watermarking: Configurable watermarks per client with position/opacity controls
- Format-Specific Optimizations: AVIF support, progressive JPEG, animated WebP
- Face Detection & Cropping: Smart avatar cropping and face-based image optimization
- WebSocket Support: Real-time notifications for upload progress, optimization status
- Webhook System: Client notifications for image events (upload, optimization, deletion)
- CDN Integration: CloudFlare, AWS CloudFront integration for global distribution
- Image Search & Filtering: Advanced search by tags, date ranges, formats, sizes
- Bulk Operations Dashboard: Mass deletion, optimization, and metadata editing
- Caching Layer: Redis caching for frequently accessed images
- SSO Integration: SAML/OAuth2 integration for enterprise client authentication
- Compliance Features: GDPR data export/deletion, audit trails, data encryption
- White-Label Solution: Custom branding and domain configuration per client
- Smart Cleanup: Auto-deletion of unused images based on configurable policies
- Predictive Optimization: Machine learning for optimal compression settings
- Content Insights: Usage analytics and recommendations for image management
MIT
For issues and questions, please open a GitHub issue.
