A self-hosted cloud file management platform designed for secure storage, organization, sharing, and retrieval of large files. The system separates file metadata from object storage and uses direct S3 transfers to minimize backend bandwidth and improve scalability.
Traditional file management applications often route uploaded files through the application server before storing them in object storage. This introduces unnecessary network and memory overhead, makes large-file transfers expensive, and can turn the backend into a bottleneck as concurrent users increase.
This project addresses these limitations by separating:
- Metadata management -> users, folders, file information
- Object storage -> actual file contents
- File transfer -> direct browser-to-object-storage communication
It is built with React, Express, PostgreSQL, and Amazon S3. PostgreSQL stores users and file metadata; S3 stores the file contents. The browser uploads and downloads file bytes directly through short-lived S3 presigned URLs, so the Node.js API does not proxy the file contents.
- Authentication & Authorization: Secure email/password login using JWT tokens (
localStoragepersistence), parameterized PostgreSQL queries, and strict resource ownership checks. - File & Folder Management: Full file system capabilities including nested folder creation, directory browsing, filename/foldername search, renaming, and deletion.
- S3 File Transfers & Storage Tracking: Direct file uploads via S3
PutObject, secure downloads using temporary S3GetObjectpresigned URLs, SHA-256 integrity verification. - Expiring Share Links: API endpoints to generate, manage, and revoke temporary file-sharing links with optional expiration limits.
- React Frontend: Modern web dashboard supporting core user workflows including authentication, folder navigation, file uploads/downloads, search, and storage management.
fms.1.1.mp4
Security is enforced at both the application and storage layers.
Users authenticate using JWT-based sessions.
Every file operation verifies ownership or the appropriate access permission before generating an S3 URL.
For example:
User A
│
├── File 101 ✓
└── File 202 ✗
A user cannot obtain a presigned URL for another user's private object simply by knowing its file ID.
The S3 bucket remains private. Files are accessed through short-lived presigned URLs rather than publicly exposed object URLs.
The browser calculates a SHA-256 checksum for each selected file and sends it when the upload is completed. The API stores the checksum with the file metadata. Downloads, including downloads through a public share link, are fetched through the temporary S3 URL and checked against that stored checksum before the browser saves the file. A mismatch stops the download and reports an integrity failure.
All endpoints require Authorization: Bearer <token> unless marked public. Request and response bodies are JSON.
POST /api/auth/register
POST /api/auth/login
GET /api/auth/mePOST /api/files/upload-url
POST /api/files/complete
GET /api/files
GET /api/files/:id
GET /api/files/:id/download
PATCH /api/files/:id
DELETE /api/files/:idGET /api/folders
POST /api/folders
GET /api/folders/:id
PATCH /api/folders/:id
DELETE /api/folders/:idPOST /api/files/:id/share
GET /api/share/:token # public read-only access
DELETE /api/share/:token # authenticated file ownerself-hosted-file-system/
│
├── backend/
│ ├── migrations/
│ │
│ ├── src/
│ │ ├── config/
│ │ │ ├── db.js
│ │ │ └── s3.js
│ │ │
│ │ ├── controllers/
│ │ ├── middleware/
│ │ ├── routes/
│ │ ├── services/
│ │ ├── utils/
│ │ │
│ │ ├── app.js
│ │ └── server.js
│ │
│ ├── .env
│ ├── .env.example
│ ├── .gitignore
│ ├── eslint.config.js
│ ├── package.json
│ └── package-lock.json
│
├── frontend/
│ ├── src/
│ │ ├── assets/
│ │ │ ├── hero.png
│ │ │ ├── react.svg
│ │ │ └── vite.svg
│ │ │
│ │ ├── api.js
│ │ ├── App.css
│ │ ├── App.jsx
│ │ ├── index.css
│ │ └── main.jsx
│ │
│ ├── .env.example
│ ├── .gitignore
│ ├── eslint.config.js
│ ├── index.html
│ ├── package.json
│ ├── package-lock.json
│ ├── README.md
│ └── vite.config.js
│
├── package-lock.json
└── README.md
The application can be deployed on AWS using:
Internet
│
▼
CloudFront
/ \
/ \
▼ ▼
Frontend Node.js API
S3 EC2
│
┌─────┴─────┐
▼ ▼
RDS S3
PostgreSQL File Storage
The architecture can be adapted for several real-world use cases:
A private alternative to cloud drive applications for storing:
- Documents
- Photos
- Videos
- Backups
- Personal projects
The architecture can be extended with:
- Fine-grained permissions
- Audit logs
- File versioning
- Retention policies
- Encryption
- Compliance controls
The same architecture can serve as the storage layer for applications requiring:
- User-generated content
- Media uploads
- PDF/document storage
- Dataset storage
- Backup systems
The system can be designed so that file-transfer traffic and API traffic can scale independently.
API workload
│
▼
Load Balancer
│
┌──┴──┐
▼ ▼
API API
│ │
└──┬──┘
│
▼
PostgreSQL
File workload
│
▼
Browser ───────────────► S3
As file traffic grows, the backend does not need to process every byte of every uploaded file.
Future scaling improvements include:
- Horizontal Node.js instances
- Redis caching
- Database read replicas
- CDN-based downloads
- S3 lifecycle policies
- Asynchronous background processing
- Distributed job queues
- Advanced search using OpenSearch
Backend → Node.js, Express.js
Database → PostgreSQL
Object Store → AWS S3
Authentication→ JWT
Frontend → React
Deployment → AWS EC2, RDS, S3, CloudFront
- Resumable uploads
- Redis-based caching
- File versioning
- Trash and automatic retention
- Duplicate-file detection
- Advanced full-text search
- End-to-end encryption
- Audit logging
- Background virus scanning
- Storage lifecycle management
- Horizontal API scaling