A professional Flask-based CRUD application for managing primary/secondary email record pairs. Exposes a versioned RESTful API backed by SQLite, ships a modern single-page web interface, and includes rate limiting, pagination, bulk operations, CSV export/import, and a full pytest test suite.
- Versioned REST API — all endpoints live under
/api/v1/; legacy/api/routes are retained for backward compatibility - Complete CRUD — Create, Read, Update, Delete individual records
- Paginated listing —
GET /api/v1/email-records/listwith configurable page size (max 100) - Bulk create — insert multiple records in a single JSON request
- CSV export / import — download all records as CSV or upload a CSV to bulk-import
- Rate limiting — per-IP limits via Flask-Limiter (configurable)
- Advanced email validation — RFC-5321-inspired regex on both server and client
- Health check endpoint —
/api/v1/healthfor load-balancer probes - Docker support — production-ready
Dockerfileanddocker-compose.yml - Test suite — pytest integration tests covering every endpoint and edge case
- Structured logging — timestamped, levelled log output to stdout
| Layer | Technology |
|---|---|
| Web framework | Flask 2.3 |
| Database | SQLite (via stdlib sqlite3) |
| CORS | Flask-CORS |
| Rate limiting | Flask-Limiter |
| Frontend | Vanilla JavaScript (ES6+), CSS3, HTML5 |
| Containerisation | Docker / Docker Compose |
| Testing | pytest, pytest-flask, pytest-cov |
email-management-system/
├── app.py # Application entry point and all route handlers
├── templates/
│ └── email_management.html # Single-page web interface
├── tests/
│ ├── __init__.py
│ └── test_app.py # pytest integration tests
├── docs/
│ └── API.md # Full API reference
├── Dockerfile # Production container image
├── docker-compose.yml # Compose service definition
├── requirements.txt # Python dependencies (runtime + dev)
├── .env.example # Environment variable template
├── .gitignore
└── README.md
- Python 3.8 or higher
- pip
# 1. Clone the repository
git clone https://github.com/Rosomak-002/email-management-system.git
cd email-management-system
# 2. Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 3. Install dependencies
pip install -r requirements.txt
# 4. (Optional) configure environment
cp .env.example .env
# 5. Run the development server
python app.pyThe application will be available at http://localhost:5002.
# Build and start
docker compose up --build
# Run in the background
docker compose up -d --build
# Stop
docker compose downThe SQLite database is persisted in a named Docker volume (db_data).
Full reference: docs/API.md
Base URL: http://localhost:5002/api/v1
| Method | Path | Description |
|---|---|---|
GET |
/health |
Service health check |
POST |
/email-records |
Create a single record |
GET |
/email-records?primary_email=… |
Retrieve a single record |
PUT |
/email-records |
Update a record's secondary email |
DELETE |
/email-records |
Delete a record |
GET |
/email-records/list |
Paginated list of all records |
POST |
/email-records/bulk |
Bulk create from a JSON array |
GET |
/email-records/export |
Download all records as CSV |
POST |
/email-records/import |
Import records from a CSV file |
# Create
curl -X POST http://localhost:5002/api/v1/email-records \
-d "primary_email=alice@example.com&secondary_email=bob@example.com"
# Read
curl "http://localhost:5002/api/v1/email-records?primary_email=alice@example.com"
# Update (JSON body)
curl -X PUT http://localhost:5002/api/v1/email-records \
-H "Content-Type: application/json" \
-d '{"primary_email":"alice@example.com","secondary_email":"new@example.com"}'
# Delete (JSON body)
curl -X DELETE http://localhost:5002/api/v1/email-records \
-H "Content-Type: application/json" \
-d '{"primary_email":"alice@example.com"}'
# Paginated list
curl "http://localhost:5002/api/v1/email-records/list?page=1&per_page=10"
# Bulk create
curl -X POST http://localhost:5002/api/v1/email-records/bulk \
-H "Content-Type: application/json" \
-d '[{"primary_email":"u1@example.com","secondary_email":"s@example.com"}]'
# Export CSV
curl -o records.csv http://localhost:5002/api/v1/email-records/export
# Import CSV
curl -X POST http://localhost:5002/api/v1/email-records/import \
-F "file=@records.csv"CREATE TABLE email_records (
primary_email TEXT PRIMARY KEY,
secondary_email TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);The database file is created automatically on first run. The path defaults to email_management.db in the project root and can be overridden via the DATABASE_PATH environment variable.
| Variable | Default | Description |
|---|---|---|
DATABASE_PATH |
<project_root>/email_management.db |
Absolute or relative path to the SQLite file |
FLASK_ENV |
production |
Set to development to enable the Flask debugger |
Copy .env.example to .env — python-dotenv will load it automatically.
# Run the full suite with coverage
pytest tests/ -v --cov=app --cov-report=term-missing
# Run a specific test file
pytest tests/test_app.py -vTests use an isolated temporary SQLite database and never touch the production file.
| Scope | Limit |
|---|---|
| Global (per IP) | 300 / day, 60 / hour |
| Create / Update / Delete | 30 / minute |
| Read / List | 60 / minute |
| Bulk create | 10 / minute |
| Export / Import | 10 / minute (export), 5 / minute (import) |
Exceeding a limit returns 429 Too Many Requests.
- All queries use parameterised statements — no SQL injection risk
- Input validation applied on both client (regex) and server (regex + length checks)
- CORS is enabled globally; restrict
originsinCORS(app, origins=[…])for production - No authentication is implemented — add an auth layer (e.g. JWT) before exposing this publicly
- Run behind a reverse proxy (nginx / Caddy) with TLS in production
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit your changes:
git commit -m 'Add my feature' - Push the branch:
git push origin feature/my-feature - Open a Pull Request
Please ensure pytest passes and flake8 reports no errors before submitting.
This project is licensed under the MIT License. See the LICENSE file for details.
rosomak — github.com/Rosomak-002