This commit is contained in:
jpmvaz
2026-09-13 20:09:20 +01:00
commit 9b5cc30fb4
482 changed files with 57569 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
# AlertHub
**Version 1.5 — Martinhal IT (Joao Vaz)**
A Dockerized alert/reminder web application with email notifications, MFA-capable authentication, and role-based access control. Designed for tracking expiration dates (licenses, renewals, certificates, contracts) with live countdown timers and configurable email reminders.
## Features
- **First-run admin setup** — On the very first launch (no accounts yet) you're taken to a "Create administrator" screen to set the admin username & password yourself. No seeded/default admin credentials.
- **Single-port architecture** — Everything runs on one port (`:8080`). The frontoffice (alerts + management) is at the root; the admin backoffice (users, mail, settings, audit log, mail log) is served on the **same port** under `/backoffice` and is restricted to admins.
- **Backoffice shortcut** — Admins get a **Backoffice** link in the top bar (and a **Frontoffice** link back) that switches between the two areas. Only visible to admins.
- **Authentication** — Password (bcrypt, 12 rounds) with optional TOTP MFA. **Any user** can self-enable MFA from *Account → MFA*; it is never mandatory.
- **User avatars** — Any user can upload a profile picture (PNG/JPG/GIF/WebP). Images are auto-squared and resized to 256×256 and stored in the database (survives upgrades). Shown in the top bar.
- **RBAC** — Two roles: `user` (view-only) and `admin` (full access)
- **Live countdown timers** — Per-alert ticking timers (days/hours/minutes/seconds) with state-based color coding (warning, critical, expired)
- **Email reminders** — Configurable SMTP + IMAP editable from the backoffice. Per-alert override of reminder lead times.
- **Mail log** — Every message the platform sends is recorded in the backoffice **Mail Log** (recipients, subject, delivery status, whether the Sent-folder copy was saved, and who triggered it).
- **Sent-folder copy (IMAP)** — After each successful send, a copy of the message is appended to the mailbox's **Sent** folder over IMAP, so it appears in your normal mail client.
- **Log export & email** — Both the Audit log and Mail log can be exported to CSV or emailed (as a CSV attachment) to any destination address.
- **Account security** — Lockout after 5 failed attempts (15-minute window), audit log of sensitive actions, security headers (CSP, X-Frame-Options, HSTS-ready)
- **IT-terminal aesthetic** — JetBrains Mono + IBM Plex Sans, signal-green accents, light + dark mode (auto-detect + manual toggle)
- **Consistent footer** — `© 2026 Martinhal IT - Joao Vaz - Version 1.5` on every page except the login screen.
- **Hardened container** — Non-root user, all capabilities dropped, `no-new-privileges`, healthcheck
## Stack
Python 3.13 · Flask 3.1 · SQLAlchemy 2 · Flask-Login · Flask-WTF · APScheduler · pyotp · bcrypt · gunicorn · SQLite
## Quick start
```bash
# 1. Get the code, then:
cp .env.example .env
# 2. Edit .env — at minimum, change:
# - SECRET_KEY (use: python -c "import secrets; print(secrets.token_hex(32))")
# - SMTP_* / IMAP_* (your Purelymail credentials)
# 3. Build and start:
docker compose up -d --build
# 4. Open:
# App: http://localhost:8080
# Backoffice: http://localhost:8080/backoffice (admins only)
# 5. First visit → you'll be sent to the "Create administrator" screen.
# Set the admin username, email, and password there.
```
### First-run checklist
1. Open the app — you'll land on **Create administrator**. Set the admin username, email and password.
2. Sign in with the credentials you just created.
3. (Optional) Go to **Account****Set up MFA** (scan the QR code with Google Authenticator, Authy, 1Password, etc.). Any user can do this.
4. (Optional) **Account****Profile avatar** → upload a picture.
5. Open the backoffice → **Mail** → confirm the Purelymail SMTP/IMAP settings, fill in the mailbox username/password, and send a test email. Check the **Mail Log** to confirm it sent and that a copy was saved to the mailbox's Sent folder.
6. Backoffice → **Settings** → set the default reminder lead times (e.g. `30,14,7,1` days before expiry).
7. Frontoffice → **Manage Alerts** → add your first alert.
## Configuration
All configuration lives in `.env`. The `docker-compose.yml` only references the file via `env_file` — no secrets are baked into the image or compose file.
| Variable | Purpose |
|---|---|
| `SECRET_KEY` | Flask session signing key (required, generate a new one) |
| `FRONTOFFICE_PORT` | The single host port (default 8080). `BACKOFFICE_PORT` is no longer used. |
| `GUNICORN_WORKERS` / `GUNICORN_THREADS` | Web server sizing. Keep workers at 1 so the reminder scheduler runs once (default 1 worker / 4 threads). |
| `DATABASE_PATH` | SQLite path inside the container (default `/data/alerthub.db`) |
| `SESSION_LIFETIME_HOURS` | Login session length |
| `MAX_LOGIN_ATTEMPTS` / `LOCKOUT_MINUTES` | Brute-force protection |
| `SCHEDULER_INTERVAL_MINUTES` | How often the reminder job runs (default 60) |
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_USERNAME` / `SMTP_PASSWORD` / `SMTP_ENCRYPTION` / `SMTP_FROM_ADDRESS` / `SMTP_FROM_NAME` | Initial outgoing-mail config (Purelymail defaults; seeded into DB on first run; admins edit live values via backoffice afterwards) |
| `IMAP_HOST` / `IMAP_PORT` / `IMAP_USERNAME` / `IMAP_PASSWORD` / `IMAP_SENT_FOLDER` | IMAP settings for saving a copy of sent mail to the Sent folder (Purelymail defaults). Leave user/pass blank to reuse the SMTP credentials. |
| `DEFAULT_REMINDER_DAYS` | Default reminder thresholds, comma-separated (e.g. `30,14,7,1`) |
> **Admin account:** There is no seeded admin. The first launch presents a one-time **Create administrator** screen. The old `INITIAL_ADMIN_*` variables are no longer used.
> **Note:** `SMTP_*` / `IMAP_*` values seed the database on first launch. After that, the backoffice **Mail** page is the source of truth — edits there persist independently of `.env`.
### Purelymail mail settings
Defaults ship pre-configured for Purelymail:
- **Outgoing (SMTP):** `smtp.purelymail.com`, port **465**, SSL/TLS. (If your client only supports STARTTLS, use port **587** with `SMTP_ENCRYPTION=starttls`.)
- **Incoming (IMAP):** `imap.purelymail.com`, port **993**, SSL/TLS — used to append sent messages to the **Sent** folder.
Just fill in your mailbox username and password (in `.env` or the backoffice **Mail** page).
## Upgrading in place (no data loss)
This build is designed to be **unzipped over the previous version**. On startup the app runs additive migrations: it creates any new tables (e.g. the mail log) and adds any new columns (avatars) to the existing SQLite database in the `/data` volume, without touching your existing users, alerts, settings, or audit history.
```bash
# from the folder containing your existing docker-compose.yml / .env / app:
unzip -o alerthub.zip # overwrite code in place (keeps .env and the /data volume)
docker compose up -d --build # rebuild & restart; migrations run automatically
```
Your customised SMTP settings are preserved on upgrade; the new IMAP keys are seeded with the Purelymail defaults (adjust them in the backoffice **Mail** page).
## Architecture
```
┌──────────── Docker container ────────────┐
│ │
│ entrypoint.sh │
│ └── gunicorn wsgi:application :8080 │
│ ├── frontoffice (/) │
│ ├── backoffice (/backoffice) │
│ └── scheduler (APScheduler) │
│ │
│ /data/alerthub.db (named volume) │
└──────────────────────────────────────────┘
```
Both processes share the same SQLite database (mounted as a Docker named volume). The reminder scheduler runs only inside the frontoffice process to avoid duplicate emails.
## Security notes
- Passwords stored as bcrypt hashes (cost 12).
- TOTP secrets stored encrypted at rest is NOT implemented — the secret is stored as-is. If your threat model requires it, mount the DB on an encrypted volume.
- CSP is strict (no inline scripts except the early theme bootstrap which is `nonce`-able if you tighten further).
- Container runs as uid `10001`, drops all Linux capabilities, and uses `no-new-privileges`.
- Brute-force lockout is per-username, not per-IP.
## Operational tips
- **Trigger a reminder check manually:** Backoffice → Settings → "Run check now".
- **Reset a user's MFA:** Backoffice → Users → row menu → Reset MFA.
- **Unlock a locked-out user:** Backoffice → Users → row menu → Unlock.
- **Inspect activity:** Backoffice → Audit log (paginated).
- **Reset reminder history for an alert** (e.g. after extending the date): Manage Alerts → row → Reset reminders.
## Uninstall / data reset
```bash
docker compose down # stop containers, keep data
docker compose down -v # also remove the alerthub_data volume (deletes DB)
```
## License
Provided as-is for internal use. No warranty.