mirror of
https://github.com/patrickbeane/quote-of-the-day.git
synced 2026-04-04 06:55:33 +00:00
Secure, rate‑limited Flask + Gunicorn microservice with SQLite persistence, delivery tracking, and systemd deployment config. Includes setup script, HTML template, and production‑ready README.
138 lines
3.4 KiB
Markdown
138 lines
3.4 KiB
Markdown
## Quote of the Day API
|
||
|
||
**A secure, rate‑limited 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 per‑IP 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 real‑world example of why rate limiting and logging matter.
|
||
|
||
---
|
||
|
||
## 📜 License
|
||
|
||
MIT License – free to use, modify, and share. |