This commit is contained in:
jpmvaz
2026-09-13 20:15:10 +01:00
commit 0072054fc7
39 changed files with 4414 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
# Birthday Manager
Self-hosted birthday reminder application that automatically e-mails people on their birthday.
© 2026 Martinhal IT - Joao Vaz - Version 1.2
## Features
**Page 1 — Dashboard**
- Three panels at the top: **Yesterday's Birthdays**, **Today's Birthdays** (with the **Force Send** button to resend the e-mail) and **Tomorrow's Birthdays**
- Add / remove birthday entries: First Name, Last Name, Date of Birth, Gender, E-mail address
- Yearly calendar overview of all entries (hover a marked day to see the names; browse other years)
**Page 2 — Administration**
- Add / remove application users
- Every user can enable/disable MFA (TOTP — Google Authenticator, Authy, etc.) and change their own password
- Full e-mail log: who received mail, at what date & time, and whether it succeeded
- Setting for the time of day the automatic birthday e-mails are sent (applies immediately, no restart needed)
- Two template zones: **E-mail Template Male** and **E-Mail Template Female**, with placeholders `{first_name}`, `{last_name}`, `{age}`
- Image upload — select multiple files at once (15 MB total per upload); each image shows its ready-to-copy `<img src="/uploads/…">` command, and images are embedded inline in the outgoing e-mail
Automatic sending: a scheduler runs every day at `SEND_HOUR` (default 08:00) and e-mails everyone whose birthday it is, using the template matching their gender. Duplicates are avoided (one automatic mail per person per day); **Force Send** always resends. February 29 birthdays are celebrated on February 28 in non-leap years.
Every successfully sent e-mail is also copied to the mailbox's **Sent** folder via IMAP, so it shows up in your normal mail client.
## Mail provider
The app comes pre-configured for **Purelymail**:
| | Server | Port | Security |
|---|---|---|---|
| Outgoing (SMTP) | `smtp.purelymail.com` | `465` | SSL/TLS |
| Incoming (IMAP, Sent-folder copy) | `imap.purelymail.com` | `993` | SSL/TLS |
You only need to fill in `SMTP_USER`, `SMTP_PASS` and `SMTP_FROM` with your Purelymail mailbox credentials. If your network only supports STARTTLS for SMTP, set `SMTP_PORT=587` and `SMTP_SECURITY=starttls` instead. Any other provider works too — just override the host/port variables.
## Quick start (Docker — recommended)
1. Edit `docker-compose.yml` and fill in your Purelymail credentials (`SMTP_USER`, `SMTP_PASS`, `SMTP_FROM`) plus a random `SECRET_KEY` and your timezone.
2. Run:
```bash
docker compose up -d --build
```
3. Open http://localhost:8080 and log in with **admin / admin** — then change the password on the Administration page immediately.
All data (SQLite database + uploaded images) lives in `./data`, so back up that folder.
## Quick start (plain Python)
```bash
pip install -r requirements.txt
# Purelymail servers are the built-in defaults - only credentials are needed
export SMTP_USER=you@yourdomain.com
export SMTP_PASS=your-purelymail-password
export SMTP_FROM=you@yourdomain.com
export SEND_HOUR=8
export SECRET_KEY=$(python -c "import secrets;print(secrets.token_hex(32))")
python app.py
```
The app listens on port 8080 (`PORT` env var to change). Data is stored in `./data` (`DATA_DIR` env var to change).
## Environment variables
| Variable | Default | Purpose |
|---|---|---|
| `SMTP_HOST` | `smtp.purelymail.com` | SMTP server |
| `SMTP_PORT` | `465` | SMTP port |
| `SMTP_USER` | | SMTP username / mailbox |
| `SMTP_PASS` | | SMTP password |
| `SMTP_FROM` | `SMTP_USER` | From address |
| `SMTP_SECURITY` | `ssl` | `ssl`, `starttls` or `none` |
| `IMAP_HOST` | `imap.purelymail.com` | IMAP server for the Sent-folder copy |
| `IMAP_PORT` | `993` | IMAP port (SSL/TLS) |
| `IMAP_USER` | `SMTP_USER` | IMAP username |
| `IMAP_PASS` | `SMTP_PASS` | IMAP password |
| `IMAP_SENT_FOLDER` | `Sent` | Mailbox folder sent mail is stored in |
| `SEND_HOUR` | `8` | Default hour for the daily send — the time picked on the Administration page overrides this |
| `SECRET_KEY` | random | Flask session key — set it so logins survive restarts |
| `PORT` | `8080` | HTTP port |
| `DATA_DIR` | `./data` | Where the database and uploads are stored |
## Notes
- The e-mail address field on entries is required because it is where the birthday greeting is sent.
- Failed sends (e.g. SMTP misconfigured) are also recorded in the e-mail log with the error as tooltip on the "Failed" status. If a mail is delivered but the IMAP copy to the Sent folder fails, the mail is still logged as Sent and the IMAP error appears as a tooltip.
- Run behind a reverse proxy (nginx / Caddy / Traefik) with HTTPS if you expose it beyond your LAN.