Skip to content

Commit 9b58c80

Browse files
committed
docs: add detailed configuration and deployment guides
- Add `configuration.md` with environment variable documentation - Add `deployment.md` with production deployment instructions and tips - Include detailed security, performance, storage, and scaling checklists - Document rate limit configurations and logging best practices - Provide S3/MinIO configuration examples and FAQs for common scenarios
1 parent 239ca47 commit 9b58c80

File tree

12 files changed

+1121
-192
lines changed

12 files changed

+1121
-192
lines changed

README.md

Lines changed: 52 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,184 +1,72 @@
1-
# DevBin – The Bin for all your Pasting needs
1+
# DevBin
22

3-
A lightweight and modern pastebin service built with FastAPI (Python) for the backend and Svelte for the frontend. Share
4-
code snippets, text, and more with ease.
3+
A lightweight pastebin service built with FastAPI and Svelte.
54

6-
## Features
7-
8-
- **Simple and Clean Interface** – Intuitive UI for quick paste creation and sharing
9-
- **Syntax Highlighting** – Support for multiple programming languages
10-
- **Paste Expiration** – Automatic cleanup of expired pastes
11-
- **Rate Limiting** – Built-in protection with configurable limits and bypass tokens
12-
- **Caching Layer** – LRU cache for improved performance
13-
- **Legacy Paste Support** – Backward compatibility with older paste formats
14-
- **Health Monitoring** – Built-in health check endpoint and storage monitoring
15-
- **Docker Ready** – Easy deployment with Docker Compose
16-
- **Configurable Storage** – Customizable file storage and size limits
17-
- **Trusted Hosts** – IP-based access control
18-
19-
## Tech Stack
20-
21-
- **Backend**: FastAPI, SQLAlchemy, PostgreSQL, Alembic (migrations)
22-
- **Frontend**: Svelte
23-
- **Deployment**: Docker, Docker Compose
24-
- **Caching**: LRU Memory Cache with configurable TTL
25-
26-
## Requirements
27-
28-
- Docker Engine
29-
- Docker Compose (or `docker-compose` for older versions)
30-
31-
## Installation
32-
33-
> **Note**: Depending on your Docker version, you may need to use `docker-compose` instead of `docker compose`
34-
35-
### Quick Start
36-
37-
DevBin can be deployed in two ways:
38-
39-
#### Option 1: Development Setup
40-
41-
1. **Clone the repository**
42-
```bash
43-
git clone <repository-url>
44-
cd DevBin
45-
```
46-
47-
2. **Configure environment variables**
48-
```bash
49-
cp .env.example .env
50-
```
51-
Edit `.env` and update the values according to your needs. Key configurations include:
52-
- Database credentials
53-
- API port and host
54-
- Rate limiting settings
55-
- CORS domains
56-
- Storage paths and limits
57-
- Trusted hosts
58-
59-
3. **Start the services**
60-
```bash
61-
docker compose up -d
62-
```
63-
64-
4. **Run database migrations**
65-
```bash
66-
docker compose run --rm app uv run alembic upgrade head
67-
```
68-
69-
5. **Access the application**
70-
- Frontend: http://localhost:3000
71-
- API Documentation (Swagger): http://localhost:8000/docs
72-
- Health Check: http://localhost:8000/health
73-
74-
#### Option 2: Production Setup
75-
76-
For production deployment, you only need the production compose file and environment configuration:
77-
78-
1. **Clone the repository**
79-
```bash
80-
git clone <repository-url>
81-
cd DevBin
82-
```
83-
84-
2. **Configure environment variables**
85-
```bash
86-
cp .env.example .env
87-
```
88-
Edit `.env` with production-ready values (strong passwords, proper domains, etc.)
89-
90-
3. **Start the services**
91-
```bash
92-
docker compose -f docker-compose.prod.yml up -d
93-
```
94-
95-
4. **Run database migrations**
96-
```bash
97-
docker compose -f docker-compose.prod.yml run --rm app uv run alembic upgrade head
98-
```
99-
100-
### Stopping the Service
101-
102-
Development:
5+
## Quick Start
1036

1047
```bash
105-
docker compose down
8+
git clone <repository-url>
9+
cd DevBin
10+
cp .env.example .env # Configure as needed
11+
task dev:up # Start all services
12+
task db:migrate # Run migrations
10613
```
10714

108-
Production:
109-
110-
```bash
111-
docker compose -f docker-compose.prod.yml down
112-
```
113-
114-
## Configuration
115-
116-
Key environment variables in `.env`:
117-
118-
| Variable | Description | Default |
119-
|--------------------------|-----------------------------------------|---------------------------|
120-
| `APP_PORT` | Backend API port | 8000 |
121-
| `APP_MAX_CONTENT_LENGTH` | Maximum paste size | 10000 |
122-
| `APP_WORKERS` | Number of worker processes | 1 |
123-
| `APP_BYPASS_TOKEN` | Token to bypass rate limits | - |
124-
| `APP_CACHE_TTL` | Cache time-to-live (seconds) | 300 |
125-
| `APP_MIN_STORAGE_MB` | Minimum required storage (MB) | 1024 |
126-
| `TRUSTED_HOSTS` | Array of trusted IP addresses/hostnames | ["127.0.0.1"] |
127-
| `APP_CORS_DOMAINS` | Allowed CORS origins | ["http://localhost:3000"] |
128-
129-
See `.env.example` for a complete list of configuration options.
15+
**Access:**
16+
- Frontend: http://localhost:3000
17+
- API Docs: http://localhost:8000/docs
18+
- Health: http://localhost:8000/health
13019

131-
## Development
20+
## Commands
13221

133-
### Running in Development Mode
22+
Run `task --list` for all available commands.
13423

135-
Set these variables in `.env`:
24+
| Command | Description |
25+
|---------|-------------|
26+
| `task dev:up` | Start all dev services |
27+
| `task dev:down` | Stop all dev services |
28+
| `task dev:logs` | Tail logs |
29+
| `task dev:reset` | Reset with fresh volumes |
30+
| `task db:migrate` | Run migrations |
31+
| `task db:shell` | PostgreSQL shell |
32+
| `task test` | Run all tests |
33+
| `task prod:up` | Start production |
34+
| `task prod:down` | Stop production |
35+
| `task clean` | Remove containers and volumes |
13636

137-
```env
138-
APP_DEBUG=true
139-
APP_RELOAD=true
140-
APP_SQLALCHEMY_ECHO=true
141-
```
142-
143-
### Tests
144-
145-
### Backend Tests
146-
147-
Run tests with:
148-
149-
```bash
150-
cd backend
151-
uv run pytest
152-
```
37+
## Configuration
15338

154-
> If you modified any of the core API endpoints, make sure to run the tests to ensure they still work as expected. Or if
155-
> you are committing breaking changes, please adjust them and add a note why this breaking change is necessary.
39+
See [`.env.example`](.env.example) for all options. Key settings:
15640

157-
See [Testing](/backend/TESTING.md)
41+
| Variable | Description |
42+
|----------|-------------|
43+
| `APP_PORT` | API port (default: 8000) |
44+
| `APP_MAX_CONTENT_LENGTH` | Max paste size |
45+
| `APP_STORAGE_TYPE` | `local`, `s3`, or `minio` |
46+
| `APP_CACHE_TYPE` | `memory` or `redis` |
15847

159-
### API Endpoints
48+
Full reference: [`docs/configuration.md`](docs/configuration.md)
16049

161-
- `GET /health` – Health check
162-
- `GET /pastes/{paste_id}` – Retrieve a paste
163-
- `GET /pastes/legacy/{paste_id}` – Retrieve a legacy paste
164-
- `POST /pastes` – Create a new paste
50+
## API
16551

166-
Full API documentation available at `/docs` when running.
52+
| Endpoint | Description |
53+
|----------|-------------|
54+
| `GET /health` | Health check |
55+
| `GET /metrics` | Prometheus metrics |
56+
| `POST /pastes` | Create paste |
57+
| `GET /pastes/{id}` | Get paste |
58+
| `DELETE /pastes/{id}` | Delete paste |
16759

168-
## Production Deployment
60+
Interactive docs at `/docs` when running.
16961

170-
See **[Option 2: Production Setup](#option-2-production-setup)** in the Quick Start section above for deployment
171-
instructions.
62+
## Documentation
17263

173-
### Production Checklist
64+
- [Configuration Reference](docs/configuration.md)
65+
- [Production Deployment](docs/deployment.md)
66+
- [Testing Guide](backend/TESTING.md)
17467

175-
Make sure to configure the following in your `.env` file:
68+
## Tech Stack
17669

177-
- ✅ Set strong database credentials (`POSTGRES_USER`, `POSTGRES_PASSWORD`)
178-
- ✅ Configure appropriate rate limits
179-
- ✅ Set `APP_DEBUG=false` and `APP_RELOAD=false`
180-
- ✅ Configure trusted hosts properly (`TRUSTED_HOSTS`)
181-
- ✅ Set up proper CORS domains (`APP_CORS_DOMAINS`)
182-
- ✅ Use a secure bypass token (`APP_BYPASS_TOKEN`)
183-
- ✅ Configure minimum storage requirements (`APP_MIN_STORAGE_MB`)
184-
- ✅ Set appropriate worker count (`APP_WORKERS`)
70+
- **Backend**: FastAPI, SQLAlchemy, PostgreSQL
71+
- **Frontend**: Svelte
72+
- **Deployment**: Docker Compose

Taskfile.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ tasks:
2222
cmds:
2323
- "{{.COMPOSE_DEV}} --profile dev_backend up -d --build"
2424

25+
dev:db:
26+
desc: Start backend only (with database)
27+
env:
28+
APP_DEBUG: "true"
29+
cmds:
30+
- "{{.COMPOSE_DEV}} up -d"
31+
2532
dev:frontend:
2633
desc: Start frontend only
2734
env:

backend/.env.example

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,26 @@ APP_LOCK_TYPE=file
8989
APP_SAVE_USER_AGENT=false
9090
APP_SAVE_IP_ADDRESS=false
9191

92-
# Optional: Rate limit bypass token for trusted apps
93-
# APP_BYPASS_TOKEN=your_secret_bypass_token_here
92+
# Rate Limiting Configuration
93+
# Enable/disable rate limiting globally
94+
APP_RATELIMIT_ENABLED=true
95+
# Storage backend: memory (default) or redis
96+
APP_RATELIMIT_BACKEND=memory
97+
# Default rate limit for endpoints without explicit limits
98+
APP_RATELIMIT_DEFAULT=60/minute
99+
100+
# Per-endpoint rate limits (optional, these are defaults)
101+
# Format: <number>/<second|minute|hour|day>
102+
# APP_RATELIMIT_HEALTH=60/minute
103+
# APP_RATELIMIT_GET_PASTE=10/minute
104+
# APP_RATELIMIT_GET_PASTE_LEGACY=10/minute
105+
# APP_RATELIMIT_CREATE_PASTE=4/minute
106+
# APP_RATELIMIT_EDIT_PASTE=4/minute
107+
# APP_RATELIMIT_DELETE_PASTE=4/minute
108+
109+
# Bypass tokens for trusted services (JSON list format)
110+
# Requests with matching Authorization header bypass rate limiting
111+
# APP_RATELIMIT_BYPASS_TOKENS=["service-token-1", "internal-api-key"]
94112

95113
# Metrics Authentication
96114
# Optional: Secure the Prometheus /metrics endpoint with Bearer token authentication

backend/app/api/routes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from app.config import config
1313
from app.containers import Container
1414
from app.exceptions import UnauthorizedError
15-
from app.ratelimit import limiter
15+
from app.ratelimit import create_limit_resolver, limiter
1616
from app.services.health_service import HealthService
1717

1818
router = APIRouter()
@@ -51,7 +51,7 @@ def verify_metrics_token(credentials: HTTPAuthorizationCredentials | None = Depe
5151

5252

5353
@router.get("/health")
54-
@limiter.limit("60/minute")
54+
@limiter.limit(create_limit_resolver(config, "health"))
5555
@inject
5656
async def health(
5757
request: Request,
@@ -61,7 +61,7 @@ async def health(
6161

6262

6363
@router.get("/ready")
64-
@limiter.limit("60/minute")
64+
@limiter.limit(create_limit_resolver(config, "health"))
6565
@inject
6666
async def ready(
6767
request: Request,

backend/app/api/subroutes/pastes.py

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import logging
22
from typing import TYPE_CHECKING
3-
from uuid import uuid4
43

54
from dependency_injector.wiring import Provide, inject
65
from fastapi import APIRouter, Depends, HTTPException
@@ -20,7 +19,7 @@
2019
)
2120
from app.config import config
2221
from app.containers import Container
23-
from app.ratelimit import get_ip_address, limiter
22+
from app.ratelimit import create_limit_resolver, get_exempt_key, limiter
2423
from app.services.paste_service import PasteService
2524
from app.utils.LRUMemoryCache import LRUMemoryCache
2625

@@ -46,19 +45,11 @@ def set_cache(cache_instance: "RedisCache | LRUMemoryCache"):
4645
delete_token_key_header = APIKeyHeader(name="Authorization", scheme_name="Delete Token")
4746

4847

49-
def get_exempt_key(request: Request) -> str:
50-
auth_header = request.headers.get("Authorization")
51-
if not auth_header or auth_header != config.BYPASS_TOKEN:
52-
return get_ip_address(request)
53-
54-
return str(uuid4()) # To simulate a new request if it is the BYPASS_TOKEN
55-
56-
5748
@pastes_route.get(
5849
"/legacy/{paste_id}",
5950
responses={404: {"model": ErrorResponse}, 200: {"model": LegacyPasteResponse}},
6051
)
61-
@limiter.limit("10/minute", key_func=get_exempt_key)
52+
@limiter.limit(create_limit_resolver(config, "get_paste_legacy"), key_func=get_exempt_key)
6253
@inject
6354
async def get_paste(
6455
request: Request,
@@ -105,7 +96,7 @@ async def get_paste(
10596
"/{paste_id}",
10697
responses={404: {"model": ErrorResponse}, 200: {"model": PasteResponse}},
10798
)
108-
@limiter.limit("10/minute", key_func=get_exempt_key)
99+
@limiter.limit(create_limit_resolver(config, "get_paste"), key_func=get_exempt_key)
109100
@inject
110101
async def get_paste(
111102
request: Request,
@@ -148,7 +139,7 @@ async def get_paste(
148139

149140

150141
@pastes_route.post("", response_model=CreatePasteResponse)
151-
@limiter.limit("4/minute", key_func=get_exempt_key)
142+
@limiter.limit(create_limit_resolver(config, "create_paste"), key_func=get_exempt_key)
152143
@inject
153144
async def create_paste(
154145
request: Request,
@@ -159,7 +150,7 @@ async def create_paste(
159150

160151

161152
@pastes_route.put("/{paste_id}")
162-
@limiter.limit("4/minute", key_func=get_exempt_key)
153+
@limiter.limit(create_limit_resolver(config, "edit_paste"), key_func=get_exempt_key)
163154
@inject
164155
async def edit_paste(
165156
request: Request,
@@ -183,7 +174,7 @@ async def edit_paste(
183174

184175

185176
@pastes_route.delete("/{paste_id}")
186-
@limiter.limit("4/minute", key_func=get_exempt_key)
177+
@limiter.limit(create_limit_resolver(config, "delete_paste"), key_func=get_exempt_key)
187178
@inject
188179
async def delete_paste(
189180
request: Request,

0 commit comments

Comments
 (0)