Spaces:
Sleeping
Document Management System
Overview
Universal document management system with polymorphic ownership, automatic storage routing, and version control.
Features
β
Universal & Polymorphic - One system for all entities (users, tickets, projects, etc.)
β
Automatic Storage Routing - Images/videos β Cloudinary, Documents β Supabase
β
Version Control - Track document versions with history
β
Metadata & Tagging - Rich metadata, tags, and search capabilities
β
Access Control - Public/private documents with uploader tracking
β
Audit Logging - Complete audit trail of all document operations
Architecture
User uploads file
β
FastAPI receives file
β
StorageService determines provider based on MIME type
β
βββ Images/Videos β Cloudinary (CDN-optimized)
βββ Documents (PDF, DOCX) β Supabase Storage
β
Receive URL from storage provider
β
Create document record in database
β
Return document metadata to user
Storage Routing Rules
The system automatically routes files based on optimization, not capability:
| File Type | Default Provider | Reason | Alternative |
|---|---|---|---|
image/* |
Cloudinary | CDN delivery, auto-optimization, transformations | Supabase can also store images |
video/* |
Cloudinary | Streaming, transcoding, adaptive bitrate | Supabase can also store videos |
application/pdf |
Supabase | Cost-effective, simple storage | N/A |
application/* |
Supabase | General documents (DOCX, XLSX, etc.) | N/A |
| Other | Supabase | Fallback for all other file types | N/A |
Note: Both providers can technically store any file type. The routing is based on optimization:
- Cloudinary: Best for media that needs CDN delivery and transformations
- Supabase: Best for documents and general file storage
Database Schema
CREATE TABLE documents (
id UUID PRIMARY KEY,
-- Polymorphic ownership
entity_type TEXT NOT NULL, -- 'user', 'project', 'ticket', etc.
entity_id UUID NOT NULL,
-- File details
file_name TEXT NOT NULL,
file_type TEXT, -- MIME type
file_size BIGINT,
file_url TEXT NOT NULL,
storage_provider TEXT DEFAULT 'supabase', -- 'cloudinary', 'supabase'
-- Classification
document_type TEXT, -- 'profile_photo', 'identity_card', etc.
document_category TEXT, -- 'legal', 'financial', 'operational'
-- Version control
version INTEGER DEFAULT 1,
is_latest_version BOOLEAN DEFAULT TRUE,
previous_version_id UUID REFERENCES documents(id),
-- Metadata
description TEXT,
tags JSONB DEFAULT '[]',
additional_metadata JSONB DEFAULT '{}',
-- Access control
uploaded_by_user_id UUID REFERENCES users(id),
is_public BOOLEAN DEFAULT FALSE,
-- Timestamps
created_at TIMESTAMP WITH TIME ZONE,
updated_at TIMESTAMP WITH TIME ZONE,
deleted_at TIMESTAMP WITH TIME ZONE
);
API Endpoints
Universal Endpoints
POST /api/v1/documents/upload
GET /api/v1/documents/{entity_type}/{entity_id}
GET /api/v1/documents/id/{document_id}
PUT /api/v1/documents/id/{document_id}
DELETE /api/v1/documents/id/{document_id}
Convenience Endpoints (Shortcuts)
POST /api/v1/documents/users/{user_id}/upload
GET /api/v1/documents/users/{user_id}
Usage Examples
Upload User Profile Photo
curl -X POST "https://api.example.com/api/v1/documents/upload" \
-H "Authorization: Bearer {token}" \
-F "file=@profile.jpg" \
-F "entity_type=user" \
-F "entity_id=123e4567-e89b-12d3-a456-426614174000" \
-F "document_type=profile_photo" \
-F "document_category=personal" \
-F "description=User profile photo" \
-F "tags=[\"profile\", \"avatar\"]" \
-F "is_public=true"
Response:
{
"id": "doc-uuid",
"entity_type": "user",
"entity_id": "user-uuid",
"file_name": "profile.jpg",
"file_type": "image/jpeg",
"file_size": 245678,
"file_url": "https://res.cloudinary.com/...",
"storage_provider": "cloudinary",
"document_type": "profile_photo",
"version": 1,
"uploader": {
"id": "uploader-uuid",
"name": "John Doe",
"email": "john@example.com"
},
"created_at": "2025-11-16T10:30:00Z"
}
Upload Ticket Photo
curl -X POST "https://api.example.com/api/v1/documents/upload" \
-H "Authorization: Bearer {token}" \
-F "file=@site_photo.jpg" \
-F "entity_type=ticket" \
-F "entity_id=ticket-uuid" \
-F "document_type=ticket_image" \
-F "document_category=evidence" \
-F "description=Before installation photo"
Upload User ID Document (PDF)
curl -X POST "https://api.example.com/api/v1/documents/users/{user_id}/upload" \
-H "Authorization: Bearer {token}" \
-F "file=@national_id.pdf" \
-F "document_type=identity_card" \
-F "document_category=legal" \
-F "description=National ID card"
Get All User Documents
curl "https://api.example.com/api/v1/documents/user/{user_id}" \
-H "Authorization: Bearer {token}"
Get Specific Document Type
curl "https://api.example.com/api/v1/documents/user/{user_id}?document_type=profile_photo" \
-H "Authorization: Bearer {token}"
Update Document Metadata
curl -X PUT "https://api.example.com/api/v1/documents/id/{document_id}" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"description": "Updated description",
"tags": ["updated", "new-tag"],
"is_public": true
}'
Delete Document
curl -X DELETE "https://api.example.com/api/v1/documents/id/{document_id}" \
-H "Authorization: Bearer {token}"
Document Types
User Documents
profile_photo- User avatar/profile pictureidentity_card- National ID, passport, etc.driver_license- Driver's licensecontract- Employment contractcertification- Professional certifications
Ticket Documents
ticket_image- Site photos, equipment photosbefore_photo- Before installation/repairafter_photo- After installation/repairwork_report- PDF work reports
Expense Documents
receipt- Payment receiptsinvoice- Invoicesexpense_proof- Expense documentation
Project Documents
project_plan- Project planning documentsblueprint- Technical blueprintscontract- Project contracts
Document Categories
legal- Legal documents (contracts, IDs, licenses)financial- Financial documents (receipts, invoices)operational- Operational documents (reports, plans)evidence- Evidence documents (photos, proofs)personal- Personal documents (profile photos)
Storage Providers
Cloudinary
- Used for: Images and videos
- Benefits: CDN delivery, automatic optimization, on-the-fly transformations
- Folder structure:
/swiftops/users/,/swiftops/tickets/,/swiftops/receipts/ - Metadata stored:
public_id,format,width,height,bytes,resource_type
Supabase Storage
- Used for: Documents (PDF, DOCX, etc.)
- Benefits: Integrated with database, simple API, cost-effective
- Bucket structure:
documents-users,documents-tickets,documents-general - Metadata stored:
bucket,path,size,content_type
Version Control
Documents support versioning:
- Upload new version of existing document
- System creates new document record with incremented version
- Previous version marked as
is_latest_version = false previous_version_idlinks to old version- Both versions remain accessible
Security
Authentication
- All endpoints require valid JWT token
- User must be authenticated to upload/view documents
Authorization
- Users can upload documents for entities they have access to
- Public documents (
is_public=true) can be viewed by anyone with the link - Private documents require proper permissions
File Validation
- File size limits enforced
- File type validation
- Malicious file detection (future enhancement)
Error Handling
Upload Failures
- If storage provider fails, no database record is created
- User receives clear error message
- Failed uploads are logged for debugging
Database Failures
- If database insert fails after successful upload, file is deleted from storage
- Prevents orphaned files
- Transaction-like behavior
Monitoring
Metrics to Track
- Upload success/failure rates
- Average upload times
- Storage usage by provider
- Most common document types
- Failed uploads requiring retry
Logs
- All uploads logged with user, entity, file details
- Audit trail in
audit_logstable - Error logs for failed operations
Future Enhancements
- Document preview/thumbnail generation
- OCR for text extraction from images
- Virus scanning for uploaded files
- Bulk upload support
- Document expiry/retention policies
- Advanced search with full-text search
- Document sharing with external users
- E-signature integration
Testing
Run integration tests:
node tests/integration/test_document_upload.js
Configuration
Required environment variables:
# Cloudinary (for images/videos)
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Supabase (for documents)
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_KEY=your_service_key
Troubleshooting
"Cloudinary not configured" error
- Check that all Cloudinary environment variables are set
- Verify credentials are correct in Cloudinary dashboard
"Failed to upload to Supabase Storage" error
- Check Supabase service key is valid
- Verify storage buckets exist in Supabase dashboard
- Check bucket permissions
Files not appearing in Cloudinary Media Library
- Check
asset_folderparameter in upload - Verify account is using dynamic folder mode (post-June 2024)
Large file uploads failing
- Check file size limits (default 10MB for most endpoints)
- For files >100MB, use chunked upload (future enhancement)
Support
For issues or questions:
- Check logs in
docs/hflogs/runtimeerror.txt - Review audit logs in database
- Check Cloudinary/Supabase dashboards for storage issues