zwitschi 3a24e86c45
Some checks failed
CI / test (3.11) (push) Has been cancelled
CI / build-image (push) Has been cancelled
fix: optimize Dockerfile by refining build dependencies and improving caching strategy
2025-10-23 09:23:57 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00
v1
2025-10-22 16:48:55 +02:00

Server README

Backend service for the contact 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.

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 for contact, newsletter, monitoring, auth, and admin endpoints.
    • 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

  1. Create a virtual environment and install dependencies:

    python -m venv .venv
    .\.venv\Scripts\Activate.ps1
    pip install -r requirements.txt
    
  2. Copy the sample environment file and adjust values:

    Copy-Item .env.example .env
    
  3. 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: accepts name, email, message, and optional company, 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.
  • DELETE /api/newsletter: unsubscribes an email address from the newsletter list.
  • PUT /api/newsletter: updates a subscriber's email address (requires old_email and new_email).
  • GET /api/newsletter/manage: displays HTML form for newsletter subscription management.
  • POST /api/newsletter/manage: processes subscription management actions (subscribe, unsubscribe, update).
  • GET /health: lightweight database connectivity check used for container health monitoring.
  • GET /metrics: Prometheus-compatible metrics endpoint (requires ENABLE_REQUEST_LOGS for 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).

Running With Docker

Build manually

docker build -t contact.allucanget.biz -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 `
  contact.allucanget.biz

Run using docker-compose

docker-compose up --build
  • Mounts ./data into the container for SQLite persistence.
  • Exposes port 5002 and wires environment variables from your .env file.

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

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

  • /health and Docker health checks verify that the API can reach the configured database.
  • /metrics surfaces request counters, latency histograms, rate limit metrics, and SMTP success/error counts.
  • Enable Sentry by setting SENTRY_DSN to 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 both latest and 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.
Description
No description provided
Readme 229 KiB
Languages
Python 63.8%
JavaScript 16.5%
HTML 13.3%
CSS 5%
Dockerfile 1%
Other 0.4%