A lightweight, secure file upload and access API built with Next.js 15 and Tailwind CSS. Think of it as your private, temporary "vault" for screenshots, quick notes, and files you want to access easily β but only your own.
- Secure File Storage: Upload files up to 50MB with automatic encryption and access tokens
- User Authentication: Secure user registration and login with JWT sessions
- Temporary Links: Files can auto-expire after specified time periods
- File Management: Upload, download, and manage your files through a clean interface
- Image Optimization: Automatic image compression and thumbnail generation
- Drag & Drop Upload: Modern file upload interface with progress tracking
- Private by Default: Each user can only access their own files
- Supported File Types: JPG, PNG, GIF, SVG images and TXT, MD, CSV files
- Frontend: Next.js 15 (App Router), React 18, Tailwind CSS 4
- Backend: Next.js API Routes, SQLite database
- Authentication: JWT with secure HTTP-only cookies
- File Processing: Sharp for image optimization
- Security: bcrypt for password hashing, secure file access tokens
- Node.js 18+
- npm or yarn
-
Clone the repository
git clone <repository-url> cd tanstack-snapvault
-
Install dependencies
npm install
-
Set up environment variables
cp .env.example .env.local
Edit
.env.local
and set your JWT secret:JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
-
Create required directories
mkdir -p data uploads
-
Run the development server
npm run dev
-
Open your browser Navigate to http://localhost:3000
- Register an Account: Create a new account with username, email, and password
- Upload Files: Drag and drop files or click to browse and upload
- Manage Files: View, download, and share your uploaded files
- Set Expiration: Choose when your files should automatically expire
- Share Securely: Copy secure access links to share specific files
- Public/Private: Choose whether files can be accessed without authentication
- Expiration: Set files to expire after 1 hour, 24 hours, 1 week, or 1 month
- Description: Add optional descriptions to your files
- Multiple Files: Upload multiple files at once
Currently supported file formats:
- Images: JPG/JPEG, PNG, GIF, SVG
- Text Files: TXT (plain text), MD (Markdown), CSV (comma-separated values)
POST /api/auth/register
Content-Type: application/json
{
"username": "johndoe",
"email": "[email protected]",
"password": "securepassword123"
}
POST /api/auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "securepassword123"
}
GET /api/auth/me
Authorization: Bearer <token>
POST /api/auth/logout
Authorization: Bearer <token>
POST /api/files/upload
Content-Type: multipart/form-data
Authorization: Bearer <token>
Form data:
- file: (file) The file to upload
- description: (string, optional) File description
- isPublic: (boolean, optional) Whether file is publicly accessible
- expiresIn: (number, optional) Hours until expiration
GET /api/files/{accessToken}
GET /api/files/{accessToken}?info=true
GET /api/files/my
Authorization: Bearer <token>
Query parameters:
- limit: (number, optional) Maximum files to return (default: 50)
- offset: (number, optional) Number of files to skip (default: 0)
DELETE /api/files/delete
Authorization: Bearer <token>
Content-Type: application/json
{
"fileId": "file-uuid"
}
POST /api/files/delete
Authorization: Bearer <token>
Content-Type: application/json
{
"fileIds": ["file-uuid-1", "file-uuid-2", "file-uuid-3"]
}
POST /api/files/generate-url
Authorization: Bearer <token>
Content-Type: application/json
{
"fileId": "file-uuid",
"expiresIn": 900,
"action": "download",
"restrictToIP": true,
"restrictToUserAgent": false
}
GET /api/files/access-logs
Authorization: Bearer <token>
Query parameters:
- fileId: (string, optional) Filter logs for specific file
- timeRange: (string, optional) Time range: "1h", "24h", "7d", "30d" (default: "24h")
- limit: (number, optional) Maximum logs to return (default: 50)
- offset: (number, optional) Number of logs to skip (default: 0)
{
"success": true,
"message": "Operation completed successfully",
"data": { ... }
}
{
"error": "Error message describing what went wrong"
}
- Password Hashing: bcrypt with 12 rounds
- JWT Tokens: Secure session management with HTTP-only cookies
- File Access Tokens: Unique, secure tokens for each file
- CORS Configuration: Configurable cross-origin resource sharing
- File Validation: Strict file type and size validation
- SQL Injection Protection: Parameterized database queries
- XSS Protection: Content Security Policy headers
Variable | Description | Default |
---|---|---|
JWT_SECRET |
Secret key for JWT tokens | (required) |
MAX_FILE_SIZE |
Maximum file size in bytes | 52428800 (50MB) |
UPLOAD_DIR |
Directory for file storage | ./uploads |
SESSION_DURATION |
Session duration in milliseconds | 604800000 (7 days) |
NODE_ENV |
Environment mode | development |
Files are stored locally in the uploads
directory by default. Each file gets:
- A unique filename with timestamp and UUID
- Secure access token for retrieval (legacy support)
- Time-limited signed URLs for secure access
- Metadata stored in SQLite database with audit trails
- Optional expiration time for automatic cleanup
- IP and user agent restrictions for enhanced security
- Comprehensive access logging for monitoring
- Optional automatic expiration
The application uses SQLite with four main tables:
users
: User accounts and authenticationfiles
: File metadata and access tokenssessions
: Active user sessionsfile_access_logs
: Comprehensive audit trail of all file access attempts
tanstack-snapvault/
βββ app/ # Next.js app directory
β βββ api/ # API routes
β βββ vault/ # Main application pages
β βββ globals.css # Global styles
β βββ layout.tsx # Root layout
βββ components/ # Reusable UI components
βββ contexts/ # React contexts
βββ lib/ # Utility libraries
β βββ auth.ts # Authentication logic
β βββ database.ts # Database operations
β βββ file-utils.ts # File handling utilities
βββ uploads/ # File storage directory
βββ data/ # SQLite database
npm run dev # Start development server
npm run build # Build for production
npm run start # Start production server
npm run lint # Run ESLint
The SQLite database is automatically created and initialized on first run. Tables and indexes are set up automatically.
Expired files are cleaned up automatically. You can also run cleanup manually:
import { cleanupExpiredFiles } from "@/lib/file-utils";
await cleanupExpiredFiles();
-
Set production environment variables
NODE_ENV=production JWT_SECRET=your-strong-production-secret DOMAIN=yourdomain.com
-
Build the application
npm run build
-
Start the production server
npm start
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
client_max_body_size 50M;
}
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature
- Commit your changes:
git commit -m 'Add amazing feature'
- Push to the branch:
git push origin feature/amazing-feature
- Open a Pull Request
- Cloud storage integration (AWS S3, Google Cloud)
- Email notifications for file shares
- Advanced file preview capabilities
- API rate limiting and abuse prevention
- File sharing with password protection
- Bulk file operations
- File versioning system
- Analytics and usage statistics
Database connection errors
- Ensure the
data
directory exists and is writable - Check file permissions on the SQLite database file
File upload failures
- Verify the
uploads
directory exists and is writable - Check file size limits and allowed file types (JPG, PNG, GIF, SVG, TXT, MD, CSV)
- Ensure sufficient disk space is available
Authentication issues
- Verify JWT_SECRET is set in environment variables
- Check if cookies are being blocked by browser settings
- Ensure HTTPS is used in production for secure cookies
Memory issues with large files
- Consider reducing MAX_FILE_SIZE for resource-constrained environments
- Implement streaming uploads for very large files
This project is licensed under the MIT License - see the LICENSE file for details.
If you encounter any issues or have questions:
- Check the troubleshooting section
- Search existing GitHub issues
- Create a new issue with detailed information about your problem
SnapVault - Your secure, private file vault. Built with β€οΈ using Next.js and Tailwind CSS.