91 lines
4.3 KiB
Markdown
91 lines
4.3 KiB
Markdown
# Birthday Manager
|
||
|
||
Self-hosted birthday reminder application that automatically e-mails people on their birthday.
|
||
|
||
© 2026 Martinhal IT - Joao Vaz - Version 1.1
|
||
|
||
## Features
|
||
|
||
**Page 1 — Dashboard**
|
||
- Banner showing whose birthday it is today (based on system date/time) with a **Force Send** button to resend the e-mail
|
||
- 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
|
||
- Two template zones: **E-mail Template Male** and **E-Mail Template Female**, with placeholders `{first_name}`, `{last_name}`, `{age}`
|
||
- Image upload — reference an uploaded image inside a template with `<img src="/uploads/filename">` and it is 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` | Hour (0–23) the daily automatic send runs |
|
||
| `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.
|