FileBud is an Express, MongoDB, and Cloudinary backend for a cloud storage application with authenticated file and folder management. It models user storage as a hierarchical directory tree, stores file metadata in MongoDB, stores binary assets in Cloudinary, and coordinates uploads, downloads, signed media access, recursive deletion, and per-user quota accounting.
Status: The backend MVP implements its current file-management scope. Production hardening, automated testing, and operational cleanup tooling remain in progress.
- Frontend: https://file-bud-frontend.vercel.app/
- Backend API: Deployed on Render
- JWT authentication with HTTP-only cookie and bearer-token support
- Automatic root-folder provisioning during registration
- User-scoped file and folder ownership checks
- Nested folder hierarchy with parent and child references
- Recursive folder deletion with nested storage accounting
- Per-user storage usage tracking and a default 1 GiB quota
- Authenticated Cloudinary uploads with metadata persisted in MongoDB
- Signed Cloudinary URLs for access, download, thumbnails, and HLS video delivery
- MongoDB transactions for multi-document write workflows
- Compensating cleanup after partial upload failures
- Filename conflict handling within a folder
- Basic liveness endpoint for hosted deployments
- Register users with email, full name, and password
- Hash passwords with bcrypt before persistence
- Create a root folder as part of account registration
- Issue access and refresh JWTs on login
- Accept access tokens from cookies or
Authorization: Bearer <token> - Remove the stored refresh token and clear cookies on logout
- Upload one multipart file at a time through Multer
- Temporarily write uploads to
public/temp - Resolve duplicate names within a folder using numeric suffixes
- Persist Cloudinary
public_id, resource type, format, byte size, and optional duration - Return signed access and attachment-download URLs
- Redirect supported thumbnail requests to signed transformed Cloudinary URLs
- Delete file metadata, folder references, quota usage, and Cloudinary assets
- Create nested folders below an owned parent folder
- Resolve duplicate child-folder titles using numeric suffixes
- Fetch a folder with its direct files and subfolders populated
- Delete nested folders recursively
- Protect each user's root folder from deletion
- Store
storageUsedandmaxStorageon each user - Default each account to
1073741824bytes (1 GiB) - Reject uploads that exceed the currently available quota
- Increment usage after successful uploads
- Decrement usage during file and recursive folder deletion
- Store uploaded assets as authenticated Cloudinary resources
- Request asynchronous eager HLS generation for video uploads
- Return signed HLS (
m3u8) URLs for video streaming - Generate signed JPG thumbnails for supported images and videos
- Group recursive remote deletions by Cloudinary resource type
- Verify resource ownership before file and folder operations
- Use MongoDB transactions for registration, folder creation, upload metadata updates, and deletions
- Remove temporary files after upload processing
- Attempt Cloudinary cleanup if a database step fails after remote upload
- Log failed Cloudinary deletions for later handling
- Limit JSON and URL-encoded request bodies to 16 KB
- Limit individual uploads to 100 MB
| Area | Technology |
|---|---|
| Runtime | Node.js with ECMAScript modules |
| API framework | Express 4 |
| Database | MongoDB |
| ODM and transactions | Mongoose 7 |
| File storage and delivery | Cloudinary |
| Authentication | JSON Web Tokens (jsonwebtoken) |
| Password hashing | bcrypt |
| Multipart uploads | Multer |
| Cookies | cookie-parser |
| Cross-origin access | cors |
| Environment configuration | dotenv |
| Development runner | Nodemon |
The implementation keeps orchestration in controllers. There is currently no separate service layer.
Client
-> Express app and middleware
-> /api/v1 routes
-> optional JWT verification
-> controllers
-> Mongoose models and Cloudinary utilities
-> MongoDB and Cloudinary
Authenticated upload
-> Multer temporary file
-> quota and folder-ownership checks
-> authenticated Cloudinary upload
-> MongoDB transaction:
create File metadata
update Folder.files
increment User.storageUsed
-> remove temporary file
Authenticated delete
-> ownership and permission checks
-> MongoDB transaction:
remove metadata and parent references
decrement User.storageUsed
-> commit database transaction
-> attempt Cloudinary deletion
-> log failed remote deletions
Cloudinary calls cannot participate in MongoDB transactions. The backend uses cleanup attempts and a local deletion log to reduce cross-system inconsistency, but it does not yet include an automated retry worker.
.
|-- .env.sample Example environment variables
|-- package.json Dependencies and npm scripts
|-- public/
| `-- temp/ Temporary Multer uploads
`-- src/
|-- app.js Express middleware and route mounting
|-- constants.js Shared database name
|-- index.js Database connection and HTTP startup
|-- controllers/
| |-- file.controller.js Upload, delivery, streaming, and deletion
| |-- folder.controller.js Folder fetch, create, and recursive deletion
| `-- user.controller.js Registration, login, logout, and current user
|-- db/
| `-- index.js MongoDB connection helper
|-- middlewares/
| |-- auth.middleware.js JWT verification and user loading
| `-- multer.middleware.js Temporary storage and upload-size limit
|-- models/
| |-- file.model.js Cloudinary-backed file metadata
| |-- folder.model.js Directory-tree metadata
| `-- user.model.js Account, token, and quota metadata
|-- routes/ Express route definitions
`-- utils/ API response, error, async, and Cloudinary helpers
All routes use the /api/v1 prefix.
| Area | Method | Route | Purpose |
|---|---|---|---|
| Users | POST |
/api/v1/users/register |
Register a user and create a root folder |
| Users | POST |
/api/v1/users/login |
Validate credentials and issue tokens |
| Users | POST |
/api/v1/users/logout |
Clear stored refresh token and cookies |
| Users | GET |
/api/v1/users/getUser |
Return the authenticated user |
| Folders | POST |
/api/v1/folders/create |
Create a child folder |
| Folders | GET |
/api/v1/folders/fetch/:folderId |
Fetch direct folder contents |
| Folders | DELETE |
/api/v1/folders/delete |
Delete a folder recursively |
| Files | POST |
/api/v1/files/upload |
Upload multipart field file into body folderId |
| Files | GET |
/api/v1/files/fetch?fileId=... |
Return a signed file-access URL |
| Files | GET |
/api/v1/files/download?fileId=... |
Return a signed attachment-download URL |
| Files | DELETE |
/api/v1/files/delete |
Delete the body fileId |
| Files | GET |
/api/v1/files/thumbnail/:id |
Redirect to a signed thumbnail URL |
| Files | GET |
/api/v1/files/stream?fileId=... |
Return a signed HLS URL for a video |
| Operations | GET |
/api/v1/files/awake |
Return a basic liveness response |
Except for registration, login, and /files/awake, routes require a valid access token.
Stores account details, password hash, latest refresh token, root-folder reference, quota state, and an access-limitation flag. email is unique and indexed. storageUsed defaults to 0; maxStorage defaults to 1 GiB.
Represents a directory node. Each folder stores its owner, parent-folder reference, direct child-folder references, and direct file references. A root folder has no parent.
Stores Cloudinary-backed asset metadata: publicId, display title, owner, parent folder, byte size, resource type, optional format, optional duration, and optional URL-expiry metadata.
Files and folders carry owner references so controllers can enforce user scoping before access or mutation. The current implementation does not include soft-delete or archive behavior.
- Store binary assets in Cloudinary while keeping ownership, hierarchy, and file metadata in MongoDB.
- Represent folders with both parent references and direct child arrays for directory-style navigation.
- Provision a root folder during registration within the same MongoDB transaction as user creation.
- Maintain
User.storageUsedas derived account state and update it during upload and deletion flows. - Check ownership in controllers before returning or modifying files and folders.
- Resolve duplicate file and folder names locally within their parent folder.
- Upload to Cloudinary before starting the metadata transaction to avoid holding a database transaction open during remote transfer.
- Use compensating Cloudinary deletion when an upload succeeds remotely but later database work fails.
- Commit metadata deletion before attempting remote Cloudinary cleanup, then log failed remote deletions.
- Use authenticated Cloudinary resources and signed delivery URLs instead of exposing public asset URLs.
- Node.js and npm
- A transaction-capable MongoDB deployment, such as a replica set
- A Cloudinary account
git clone <your-repository-url>
cd file-bud-backend
npm installCreate a .env file from .env.sample, fill in the required values, then run:
npm run devThe API defaults to http://localhost:8000. The server appends /filebud to MONGODB_URI.
public/temp is already included for temporary uploads. The application also expects a writable logs/ directory when recording failed Cloudinary deletions, but it does not currently create that directory automatically.
| Variable | Purpose |
|---|---|
PORT |
HTTP port; defaults to 8000 |
CORS_ORIGIN |
Allowed frontend origin for credentialed requests |
MONGODB_URI |
MongoDB base URI; /filebud is appended by the code |
ACCESS_TOKEN_SECRET |
Access-token signing and verification secret |
ACCESS_TOKEN_EXPIRY |
Access-token lifetime |
REFRESH_TOKEN_SECRET |
Refresh-token signing secret |
REFRESH_TOKEN_EXPIRY |
Refresh-token lifetime |
CLOUDINARY_CLOUD_NAME |
Cloudinary cloud name |
CLOUDINARY_API_KEY |
Cloudinary API key |
CLOUDINARY_API_SECRET |
Cloudinary API secret |
| Command | Purpose |
|---|---|
npm run dev |
Run the API with Nodemon and dotenv preloading |
The repository does not currently define production start, test, lint, or build scripts.
- Use a MongoDB replica set or sharded cluster because core workflows use transactions.
- Store JWT and Cloudinary credentials as deployment secrets.
- Set
CORS_ORIGINto the deployed frontend origin. - Add a production
startscript and configure the chosen hosting platform or process manager. - Ensure temporary uploads use writable storage appropriate for the hosting platform.
- Create a durable cleanup strategy for failed Cloudinary deletions; local logs are insufficient on ephemeral hosts.
- Add structured logs, monitoring, readiness checks, and alerting.
- Review console logging before deployment because controllers currently print request and asset details.
- Keep
.envfiles out of version control.
- No automated test suite is included.
- No rename, move, sharing, search, admin, or bulk-operation endpoints are present.
- No background worker or scheduled cleanup job processes failed Cloudinary deletions.
- No virus scanning, resumable upload, or chunked upload support is present.
- No rate limiting, Helmet middleware, CSRF protection, audit logging, or observability stack is included.
- Quota checks can race during concurrent uploads because they are not enforced with an atomic conditional update.
- Temporary uploads use original filenames under the statically served
publicdirectory. - The expected
logs/directory is not created automatically. - Cloudinary storage is integrated directly; there is no storage-provider abstraction.
This repository uses the ISC license. The project is developed for educational and portfolio purposes.