Files
quote-of-the-day/README.md
patrickbeane ddf5e983e2 Initial commit: Quote of the Day API
Secure, rate‑limited Flask + Gunicorn microservice with SQLite persistence, delivery tracking, and systemd deployment config. Includes setup script, HTML template, and production‑ready README.
2025-08-30 13:24:33 -04:00

138 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Quote of the Day API
**A secure, ratelimited microservice** serving curated jokes and quotes from a persistent SQLite database. Built for fun, engineered for production.
---
## 📖 Overview
Quote of the Day is a small but production-minded API designed to demonstrate:
- **Rate limiting** `flask-limiter` enforces perIP request caps to prevent abuse.
- **Persistent storage** SQLite replaces JSON to ensure atomic reads/writes and avoid race conditions under concurrent access.
- **Proxy awareness** `ProxyFix` middleware ensures correct client IP logging behind reverse proxies.
- **Organic usage** The live API has been discovered and now serves hundreds of requests daily.
---
## ✨ Features
- Randomized quote delivery with delivery count tracking
- Plain text and JSON endpoints
- Rate-limited access to prevent abuse
- Systemd-ready deployment with Gunicorn
- SQLite-backed persistence for safe concurrent access
---
## 🗂 Architecture
```
[ Client ]
|
v
[ Flask + Gunicorn ]
|
v
[ SQLite DB (quotes.db) ]
```
---
## 🚀 Endpoints
| Method | Path | Description | Rate Limit |
|--------|--------------|--------------------------------------------|----------------|
| GET | `/` | HTML frontend (quotes.html) | N/A |
| GET | `/quote` | Returns a random quote as plain text | 20/minute |
| GET | `/api/quote` | Returns a random quote as JSON | 50/minute |
---
## 📦 Running Locally
```bash
# Clone the repo
git clone https://github.com/patrickbeane/quote-of-the-day.git
cd quote-of-the-day
# (Optional) Create a virtual environment
python3 -m venv venv && source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Edit `add-quotes.py` with your favorite quotes
nano add-quotes.py
# Run `add-quotes.py` to generate the DB
python add-quotes.py
# Run the API
python quotes.py
```
Visit `http://<your-server-ip>:5051` for the HTML frontend or `http://<your-server-ip>:5051/api/quote` for JSON output.
---
## 📝 Example Output
```json
{
"id": 1,
"quote": "Strive not to be a success, but rather to be of value.",
"author": "Albert Einstein",
"delivered": 3
}
```
## 🛠 Deployment with systemd
For production use, this service can be run behind Gunicorn and managed via systemd.
A sample unit file is included in [`deploy/quotes.service`](deploy/quotes.service):
```bash
sudo cp deploy/quotes.service /etc/systemd/system/
sudo systemctl daemon-reexec
sudo systemctl enable quotes
sudo systemctl start quotes
```
---
## 🔒 Production Considerations
- **Rate limiting** prevents abuse and keeps the service responsive.
- **SQLite** ensures atomic writes and safe concurrent access.
- **ProxyFix** ensures accurate IP logging behind reverse proxies.
---
## 🏗 Production Deployment (on my infra)
```
[ Client ]
|
v
[ Hermes (Docker + Caddy) ]
|
v
[ Hades (Flask + Gunicorn) ]
|
v
[ SQLite DB (quotes.db) ]
```
---
## 📊 Fun Fact
The live API has been hit by automated clients and bots, resulting in some quotes being "delivered" hundreds of times - a realworld example of why rate limiting and logging matter.
---
## 📜 License
MIT License free to use, modify, and share.