- Implemented email settings configuration in the admin panel, allowing for SMTP settings and notification preferences. - Created a new template for email settings with fields for SMTP host, port, username, password, sender address, and recipients. - Added JavaScript functionality to handle loading, saving, and validating email settings. - Introduced email templates management, enabling the listing, editing, and saving of email templates. - Updated navigation to include links to email settings and templates. - Added tests for email settings and templates to ensure proper functionality and validation.
Server README
Backend service for a static website. The app accepts contact and newsletter submissions, persists them, applies rate limiting and origin checks, and sends notification emails when SMTP is configured. Includes admin authentication for accessing application settings and managing dynamic configuration.
Table of Contents
- Server README
Overview
- Flask application exposed through
app.py(development) and Gunicorn (container runtime) - Service and blueprint architecture under
server/for HTTP routes, business logic, and observability - SQLite by default with optional PostgreSQL support and Redis-backed distributed rate limiting
- Docker- and docker-compose-friendly with health checks and environment-driven configuration
Architecture
app.py: simple shim that imports the Flask app for local development.server/: application package.factory.py: application factory that wires logging, middleware, routes, and database initialisation.routes/: Flask blueprints forcontact,newsletter,monitoring,auth, andadminendpoints.services/: reusable business logic for persisting submissions and sending notifications.database.py: SQLite/PostgreSQL helpers and connection management.middleware.py,rate_limit.py,metrics.py: request guards, throttling, and Prometheus-style instrumentation.settings.py: environment-driven configuration loader.auth.py: authentication utilities and login required decorator.
templates/: Jinja2 templates for HTML pages (login, admin dashboard, newsletter management, settings).entrypoint.sh: container entrypoint that tunes Gunicorn worker count and timeout before booting the WSGI app.Dockerfile: multi-stage build that installs requirements in a builder image and runs the app behind Gunicorn.docker-compose.yml: local stack for the API with bind-mounted SQLite data.docker-compose.redis.yml: optional Redis sidecar for distributed rate limiting tests.tests/: pytest suite covering API behaviour, services, metrics, and SMTP integration (opt-in).
Quick Start
-
Create a virtual environment and install dependencies:
python -m venv .venv .\.venv\Scripts\Activate.ps1 pip install -r requirements.txt -
Copy the sample environment file and adjust values:
Copy-Item .env.example .env -
Run the development server:
python app.py
The development server listens on http://127.0.0.1:5002 by default.
Admin Access
Access the admin interface at http://127.0.0.1:5002/auth/login using the configured ADMIN_USERNAME and ADMIN_PASSWORD (defaults: admin/admin). The admin interface provides a dashboard overview, newsletter subscriber management with search and pagination, newsletter creation and sending capabilities, and dynamic application settings management. The settings page displays current application configuration and allows dynamic management of application settings, while the submissions page allows viewing and managing contact form submissions.
API Surface
POST /api/contact: acceptsname,email,message, and optionalcompany,timeline.GET /api/contact: retrieves contact form submissions (admin only, requires authentication). Supports pagination (page,per_page), filtering (email,date_from,date_to), and sorting (sort_by,sort_order).GET /api/contact/<id>: retrieves a specific contact submission by ID (admin only).DELETE /api/contact/<id>: deletes a contact submission by ID (admin only).POST /api/newsletter: subscribes an address and optional metadata to the newsletter list. Sends a confirmation email if SMTP is configured.DELETE /api/newsletter: unsubscribes an email address from the newsletter list.PUT /api/newsletter: updates a subscriber's email address (requiresold_emailandnew_email).GET /api/newsletter/manage: displays HTML form for newsletter subscription management.POST /api/newsletter/manage: processes subscription management actions (subscribe, unsubscribe, update).GET /newsletter/unsubscribe/confirmation: displays unsubscription confirmation page.GET /health: lightweight database connectivity check used for container health monitoring.GET /metrics: Prometheus-compatible metrics endpoint (requiresENABLE_REQUEST_LOGSfor detailed tracing).GET /admin/api/settings: retrieves all application settings (admin only).PUT /admin/api/settings/<key>: updates a specific application setting (admin only).DELETE /admin/api/settings/<key>: deletes a specific application setting (admin only).GET /admin/api/newsletter: retrieves newsletter subscribers with pagination, filtering, and sorting (admin only).POST /admin/api/newsletters: creates a new newsletter (admin only).GET /admin/api/newsletters: retrieves newsletters with pagination and filtering (admin only).POST /admin/api/newsletters/<id>/send: sends a newsletter to all subscribers (admin only).GET /admin/api/contact: retrieves contact form submissions with pagination, filtering, and sorting (admin only).DELETE /admin/api/contact/<id>: deletes a contact submission by ID (admin only).GET /embed/contact: serves an HTML page with a contact form that can be embedded in an iframe on external sites.GET /embed/newsletter: serves an HTML page with a newsletter subscription form that can be embedded in an iframe on external sites.
Embeddable Forms
The application provides embeddable contact and newsletter subscription forms that can be integrated into other websites via iframe. CORS (Cross-Origin Resource Sharing) headers are automatically added to API endpoints to allow forms embedded on external sites to submit data successfully.
GET /embed/contact: serves an HTML page with a contact form that can be embedded in an iframe on external sites.GET /embed/newsletter: serves an HTML page with a newsletter subscription form that can be embedded in an iframe on external sites.
To embed the contact form on another website, use the following HTML code:
<iframe
src="https://your-server-domain/embed/contact"
width="600"
height="400"
frameborder="0"
allowfullscreen
></iframe>
To embed the newsletter subscription form:
<iframe
src="https://your-server-domain/embed/newsletter"
width="600"
height="300"
frameborder="0"
allowfullscreen
></iframe>
Replace https://your-server-domain with your actual server URL. The iframe codes are available in the admin settings page for easy copying.
Running With Docker
Build manually
docker build -t backend-server -f Dockerfile .
Run with explicit environment variables
docker run --rm -p 5002:5002 `
-e FLASK_SECRET_KEY=change-me `
-e SMTP_HOST=smtp.example.com `
-e SMTP_PORT=587 `
-e SMTP_USERNAME=api@example.com `
-e SMTP_PASSWORD=secret `
-e SMTP_RECIPIENTS=hello@example.com `
backend-server
Run using docker-compose
docker-compose up --build
- Mounts
./datainto the container for SQLite persistence. - Exposes port
5002and wires environment variables from your.envfile.
To experiment with Redis-backed throttling:
docker-compose -f docker-compose.redis.yml up --build
Environment Variables
See .env.example for a complete reference. The most relevant groups are captured below.
Core runtime
| Variable | Description | Default |
|---|---|---|
FLASK_SECRET_KEY |
Secret used by Flask for session signing; set to a strong random value in production. | dev |
ENABLE_JSON_LOGS |
Emit logs in JSON format when true. |
false |
ENABLE_REQUEST_LOGS |
Enable request start/end logging. | true |
Admin authentication
| Variable | Description | Default |
|---|---|---|
ADMIN_USERNAME |
Username for admin login. | admin |
ADMIN_PASSWORD |
Password for admin login. | admin |
Database configuration
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
SQLite URL or filesystem path. Examples: sqlite:///./forms.db, ./data/forms.db. |
sqlite:///./data/forms.db |
POSTGRES_URL |
PostgreSQL connection URI, e.g. postgresql://user:pass@host:5432/dbname. Requires psycopg2-binary installed. |
(none) |
When POSTGRES_URL is set and psycopg2-binary is available, the app prefers PostgreSQL. Otherwise it falls back to SQLite. A custom DATABASE_URL always wins over POSTGRES_URL to simplify local development.
Email delivery
| Variable | Description | Default |
|---|---|---|
SMTP_HOST |
SMTP server hostname or IP. Leave empty to disable email notifications. | (none) |
SMTP_PORT |
SMTP server port. | 587 |
SMTP_USERNAME |
Username for SMTP authentication. | (none) |
SMTP_PASSWORD |
Password or token for SMTP authentication. | (none) |
SMTP_SENDER |
Sender email address; defaults to SMTP_USERNAME when unset. |
(none) |
SMTP_RECIPIENTS |
Comma-separated recipient list for notifications. | (none) |
SMTP_USE_TLS |
Enables STARTTLS when true. |
true |
EMAIL_NOTIFY_CONTACT_FORM |
Default toggle for sending emails when new contact submissions arrive. Can be overridden in the admin UI. | true |
EMAIL_NOTIFY_NEWSLETTER_SIGNUPS |
Default toggle for sending notification/confirmation emails when new newsletter signups occur. Admin UI can override per deployment. | false |
Rate limiting and caching
| Variable | Description | Default |
|---|---|---|
RATE_LIMIT_MAX |
Maximum submissions allowed per window from a single IP. Set to 0 to disable throttling. |
10 |
RATE_LIMIT_WINDOW |
Sliding window size (seconds) for rate limiting. | 60 |
REDIS_URL |
Redis connection string for distributed rate limiting; when empty, use in-memory storage. | (none) |
Request hardening
| Variable | Description | Default |
|---|---|---|
STRICT_ORIGIN_CHECK |
Enforce Origin/Referer validation when true. |
false |
ALLOWED_ORIGIN |
Expected site origin (e.g. https://example.com) used by strict origin checks. |
(none) |
Observability
| Variable | Description | Default |
|---|---|---|
SENTRY_DSN |
Sentry project DSN for error reporting. | (none) |
SENTRY_TRACES_SAMPLE_RATE |
Sampling rate for Sentry performance tracing (0-1). | 0.0 |
Docker / Gunicorn runtime
| Variable | Description | Default |
|---|---|---|
GUNICORN_WORKERS |
Overrides auto-calculated worker count; leave blank to use (2 × CPU) + 1 capped at 8 workers. |
(auto) |
GUNICORN_TIMEOUT |
Worker timeout in seconds used by Gunicorn. | 30 |
Health Checks and Monitoring
/healthand Docker health checks verify that the API can reach the configured database./metricssurfaces request counters, latency histograms, rate limit metrics, and SMTP success/error counts.- Enable Sentry by setting
SENTRY_DSNto forward exceptions and performance traces.
Testing
pytest -q tests
SMTP integration tests are skipped unless RUN_SMTP_INTEGRATION_TEST=1 and valid SMTP credentials are set. Run pytest -q tests/test_integration_smtp.py to target them explicitly.
Deployment Notes
- A single Gitea Actions workflow (
.github/workflows/ci.yml) exercises pytest on each push, pull request, or manual dispatch, then conditionally builds the Docker image. - When the default branch (
main) runs and registry secrets (REGISTRY_URL,REGISTRY_USERNAME,REGISTRY_PASSWORD) are configured in Gitea, the workflow logs in and pushes bothlatestand commit-specific image tags. - Import or mirror the required reusable actions (
actions/checkout,actions/setup-python, and the Docker actions) into your Gitea instance so that the workflow can resolve them. - For production use, deploy the container behind a load balancer or reverse proxy and supply the appropriate environment variables.
Admin Email Settings
- Navigate to
/admin/email-settingsafter authenticating to manage SMTP host, credentials, sender identity, notification recipients, and feature toggles. - Values saved through the form are persisted in the
app_settingstable and override any.envdefaults until updated again. - The page surfaces validation feedback in-line and falls back to environment-sourced defaults when a field is left blank.
- Toggle
Email notifications for new contact submissionsandConfirmation emails for newsletter signupsto control runtime behaviour. These switches seed fromEMAIL_NOTIFY_CONTACT_FORMandEMAIL_NOTIFY_NEWSLETTER_SIGNUPSrespectively.
Email Templates
The application supports customizable email templates for newsletter confirmations. Administrators can manage templates from the dedicated Email Templates page (/admin/email-templates), which provides a list/detail editor for each available template. Changes are persisted to dynamic settings, allowing runtime updates without redeploying the application. Templates use HTML format and should include an unsubscribe link for compliance with email regulations.
Default confirmation email includes:
- Welcome message
- Unsubscribe instructions with link to
/newsletter/manage - Customizable HTML content