require client-key for uploads

This commit is contained in:
2025-04-15 07:44:20 -04:00
parent 350a8aaab7
commit e0c89b4f21
7 changed files with 110 additions and 13 deletions

7
.env.example Normal file
View File

@@ -0,0 +1,7 @@
# Required for file uploads
CLIENT_KEY=your-secret-key-here
# Optional configurations
UPLOAD_FOLDER=tmp/uploads
DEBUG=False
MAX_CONTENT_LENGTH=16777216 # 16MB in bytes

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
tmp/ tmp/
uploads/ uploads/
file_metadata.json file_metadata.json
.env
# Python # Python
venv/ venv/

View File

@@ -4,4 +4,10 @@ COPY ./src /app
COPY requirements.txt /app/requirements.txt COPY requirements.txt /app/requirements.txt
RUN pip install -r /app/requirements.txt RUN pip install -r /app/requirements.txt
EXPOSE 7777 EXPOSE 7777
# The actual values should be provided when running the container
ENV CLIENT_KEY=default-secret-key \
UPLOAD_FOLDER=tmp/uploads \
DEBUG=False
CMD ["gunicorn", "wsgi:app", "--bind", "0.0.0.0:7777"] CMD ["gunicorn", "wsgi:app", "--bind", "0.0.0.0:7777"]

View File

@@ -4,6 +4,7 @@ A lightweight Flask-based file upload and download server with a modern UI. Perf
## Features ## Features
- Clean, modern UI for file downloads - Clean, modern UI for file downloads
- Secure API authentication for uploads
- Mobile-friendly upload endpoint - Mobile-friendly upload endpoint
- Secure 6-digit alphanumeric code system - Secure 6-digit alphanumeric code system
- Automatic file cleanup - Automatic file cleanup
@@ -20,25 +21,40 @@ Built using the MVC design pattern:
## Getting Started ## Getting Started
### Local Development ### Local Development
1. Clone the repository
2. Create and activate a virtual environment:
```bash ```bash
# Create and activate virtual environment
python -m venv venv python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate source venv/bin/activate # On Windows: venv\Scripts\activate
```
# Install dependencies 3. Install dependencies:
```bash
pip install -r requirements.txt pip install -r requirements.txt
```
# Run the server 4. Set up environment variables:
- Copy `.env.example` to `.env`
- Set your `CLIENT_KEY` in the `.env` file
- Adjust other settings as needed
5. Run the server:
```bash
python src/app.py python src/app.py
``` ```
### Docker Deployment ### Docker Deployment
1. Build the image:
```bash ```bash
# Build the image
docker build -t file-upload-server . docker build -t file-upload-server .
```
# Run the container 2. Run the container:
docker run -p 7777:7777 -v $(pwd)/tmp:/app/tmp file-upload-server ```bash
docker run -p 7777:7777 \
-e CLIENT_KEY=your-secret-key \
-v $(pwd)/tmp:/app/tmp \
file-upload-server
``` ```
## API Endpoints ## API Endpoints
@@ -46,10 +62,13 @@ docker run -p 7777:7777 -v $(pwd)/tmp:/app/tmp file-upload-server
### POST /upload ### POST /upload
Upload a file and receive a download code. Upload a file and receive a download code.
**Authentication:**
- Required: `X-Client-Key` header with your API key
**Form Fields:** **Form Fields:**
- `file`: File to upload (required) - `file`: File to upload (required)
- `max_downloads`: Maximum download count (optional, default: 1) - `max_downloads`: Maximum download count (optional, default: 1)
- `expiry_hours`: Hours until expiry (optional, default: 24, max: 24) - `expiry_hours`: Hours until expiry (optional, default: 24)
**Response:** **Response:**
```json ```json
@@ -58,34 +77,75 @@ Upload a file and receive a download code.
} }
``` ```
**Error Responses:**
- 401: Invalid or missing client key
- 400: Missing file or invalid request
```json
{
"error": "Error message here"
}
```
### GET /download/{code} ### GET /download/{code}
Download a file using a 6-character code. Download a file using a 6-character code.
**Responses:** **Responses:**
- 200: File download starts - 200: File download starts
- 404: File not found with specific error page for: - 404: File not found with specific error pages for:
- Invalid code - Invalid code
- Expired file - Expired file
- Download limit reached - Download limit reached
- File no longer exists - File no longer exists
## Security Notes ### DELETE /delete/{code}
- Files are automatically deleted after: Delete a file using its code.
**Responses:**
- 200: File deleted successfully
- 404: File not found or expired
## Security Features
- **API Authentication**: Client key required for file uploads
- **Secure Downloads**: 6-digit alphanumeric codes
- **Auto Cleanup**: Files are automatically deleted after:
- Reaching download limit - Reaching download limit
- Exceeding expiry time - Exceeding expiry time
- Server restart (temporary storage) - Server restart (temporary storage)
- No file execution permissions in upload directory - **Upload Security**:
- Secure filename handling - Secure filename handling
- No file execution permissions
- File size limits
## Configuration ## Configuration
All configuration is handled through environment variables: All configuration is handled through environment variables:
### Required Settings
- `CLIENT_KEY`: API key for file uploads (required for production)
### Optional Settings
- `UPLOAD_FOLDER`: Upload directory path (default: tmp/uploads) - `UPLOAD_FOLDER`: Upload directory path (default: tmp/uploads)
- `MAX_CONTENT_LENGTH`: Maximum file size (default: 16MB) - `MAX_CONTENT_LENGTH`: Maximum file size (default: 16MB)
- `DEBUG`: Enable debug mode (default: False) - `DEBUG`: Enable debug mode (default: False)
### Environment File
The application supports `.env` file for configuration:
```env
CLIENT_KEY=your-secret-key-here
UPLOAD_FOLDER=tmp/uploads
DEBUG=False
MAX_CONTENT_LENGTH=16777216
```
## Contributing ## Contributing
1. Fork the repository 1. Fork the repository
2. Create a feature branch 2. Create a feature branch
3. Commit your changes 3. Commit your changes
4. Push to the branch 4. Push to the branch
5. Create a Pull Request 5. Create a Pull Request
## Security Best Practices
- Always change the default client key
- Use HTTPS in production
- Regularly monitor disk usage
- Set appropriate file size limits
- Keep dependencies updated

View File

@@ -7,6 +7,7 @@
- [x] Implement download tracking and limits - [x] Implement download tracking and limits
- [x] Add file expiry system - [x] Add file expiry system
- [x] Create modern UI with error handling - [x] Create modern UI with error handling
- [x] Add API authentication for uploads
## 🎯 Core Features Implemented ## 🎯 Core Features Implemented
@@ -24,6 +25,7 @@
- [x] Metadata tracking - [x] Metadata tracking
- [x] Configurable limits - [x] Configurable limits
- [x] API response format - [x] API response format
- [x] API key authentication
### Download System ### Download System
- [x] Code validation - [x] Code validation
@@ -47,6 +49,9 @@
- [ ] Implement file type restrictions - [ ] Implement file type restrictions
- [ ] Add optional password protection - [ ] Add optional password protection
- [ ] CSRF protection - [ ] CSRF protection
- [ ] Add key rotation support
- [ ] Implement IP whitelisting
- [ ] Add audit logging
### Features ### Features
- [ ] Multi-file upload support - [ ] Multi-file upload support
@@ -61,6 +66,7 @@
- [ ] Implement persistent storage - [ ] Implement persistent storage
- [ ] Add monitoring/metrics - [ ] Add monitoring/metrics
- [ ] Create backup system - [ ] Create backup system
- [ ] Add health check endpoints
### UI/UX ### UI/UX
- [ ] Dark mode support - [ ] Dark mode support

View File

@@ -11,6 +11,7 @@ itsdangerous==2.2.0
Jinja2==3.1.4 Jinja2==3.1.4
MarkupSafe==3.0.0 MarkupSafe==3.0.0
packaging==24.1 packaging==24.1
python-dotenv==1.0.1
requests==2.32.3 requests==2.32.3
urllib3==2.2.3 urllib3==2.2.3
Werkzeug==3.0.4 Werkzeug==3.0.4

View File

@@ -1,20 +1,36 @@
import os import os
import uuid import uuid
from functools import wraps
from flask import Flask, request, jsonify, send_from_directory from flask import Flask, request, jsonify, send_from_directory
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
import requests import requests
from dotenv import load_dotenv
import utils.mLogger as log import utils.mLogger as log
from controllers.file_controller import upload_file, download_file, delete_file from controllers.file_controller import upload_file, download_file, delete_file
from views import views from views import views
# Load environment variables
load_dotenv()
app = Flask(__name__) app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'tmp/uploads' app.config['UPLOAD_FOLDER'] = 'tmp/uploads'
app.config['CLIENT_KEY'] = os.getenv('CLIENT_KEY', 'default-secret-key')
if not os.path.exists(app.config['UPLOAD_FOLDER']): if not os.path.exists(app.config['UPLOAD_FOLDER']):
os.makedirs(app.config['UPLOAD_FOLDER']) os.makedirs(app.config['UPLOAD_FOLDER'])
def require_client_key(f):
@wraps(f)
def decorated_function(*args, **kwargs):
client_key = request.headers.get('X-Client-Key')
if not client_key or client_key != app.config['CLIENT_KEY']:
log.e("Invalid or missing client key")
return jsonify({'error': 'Invalid or missing client key'}), 401
return f(*args, **kwargs)
return decorated_function
# Route for file uploads # Route for file uploads
app.route('/upload', methods=['POST'])(upload_file) app.route('/upload', methods=['POST'])(require_client_key(upload_file))
# Route for downloading the file # Route for downloading the file
app.route('/download/<code>', methods=['GET'])(download_file) app.route('/download/<code>', methods=['GET'])(download_file)