Files
2026-09-13 20:03:24 +01:00

117 lines
7.4 KiB
Markdown

# ApprovalFlow
A self-hosted approval-workflow app. Requests are created on the site (login required), move through each workflow's ordered chain of approvers by email, and once approved or denied, **everyone involved up to that point** is notified. Every request keeps a full audit log of everything that happened to it.
Outgoing mail is preconfigured for **Purelymail** — you only provide the account's login and password.
## What it does
- **Sequential multi-step approval.** Every workflow has an ordered chain of approvers. The request goes to the first person; each approval sends it on to the next; the last approval completes it. **A denial at any step stops the workflow immediately** — nobody later in the chain is contacted.
- **Decisions notify everyone.** On the final outcome (approved or denied), the requester and every approver involved **up to that point** receive the outcome email.
- **Full log.** Every request records: received, each approval request sent, each step's decision, notifications, and any mail errors — each with a timestamp.
- **Attachments.** A request can include one or more uploaded files (15 MB combined). They are attached to **every approval mail** sent along the chain, and admins can download each from the request page. Files are stored next to the database in `data/attachments/`.
- **Knowledge chain.** Next to each workflow's approval chain there is a knowledge chain: addresses that never approve anything but are informed by e-mail when a request is created and on every step approval, denial, or final approval. They are not counted as "involved" and don't receive the participant outcome notice — only their own FYI messages, which include the request's attached files just like the approval mails.
- **Configure profile.** Every user has a profile page (top-right corner): first name, last name, unit location, e-mail, change password, and MFA. The profile e-mail pre-fills the mandatory "Request's e-mail" field when submitting a request.
- **MFA.** Any user can enable two-factor authentication from their Configure profile page (TOTP — Google Authenticator, Authy, 1Password, …). Sign-in then requires password + 6-digit code. Admins can disable MFA for a user who lost their device.
- **Light and dark mode.** A toggle in the top bar (and on the login page) switches themes; the choice is remembered per browser and defaults to the system preference.
- **Images in emails.** Admins upload PNG/JPG/GIF images (up to 2 MB each) in the library shown below the Email wording section of any workflow. Placing an image's `{image:name}` tag in the approval request body, approved notice body, or denied notice body embeds the image at that spot (HTML email with a plain-text fallback).
- **Mail status page (admin only).** Every outgoing email is recorded with its delivery result and whether the Sent-folder copy succeeded, so you can confirm mail is going out and spot errors at a glance.
- **Multiple named workflows**, each personalized: its own approval chain and the wording of all three emails (approval request, approved notice, denied notice) with `{placeholders}`.
- **Main page (login required):** users pick a workflow they have access to and submit a request from the browser.
- **Admin pages:** create/edit/pause workflows, monitor all requests and open their full logs, decide directly from the UI, manage users, and set which users can access which workflows.
## Quick start (Docker)
```bash
cp .env.example .env # edit it: SECRET_KEY, BASE_URL, MAIL_USER, MAIL_PASSWORD
docker compose up -d
```
Open http://localhost:8000 and sign in with `ADMIN_USERNAME` / `ADMIN_PASSWORD` from `.env` (defaults `admin` / `changeme` — change them). The SQLite database lives in `./data/`.
> **If a value in `.env` contains a `$`** (common in passwords), escape it as `$$` — e.g. `MAIL_PASSWORD=xK9$$vh4pQ2`. Docker Compose otherwise treats `$word` as a variable and silently replaces it with a blank string, printing a warning like `The "word" variable is not set`.
## Quick start (bare Python)
```bash
pip install -r requirements.txt
cp .env.example .env
set -a; source .env; set +a # or export the variables another way
python app.py
```
Leaving `MAIL_USER` empty prints outgoing mail to the console instead of sending it — handy for trying the app before entering the account.
## Mail server
The app is preconfigured for Purelymail's servers — you never enter host or port settings:
| | |
|---|---|
| Outgoing (SMTP) | `smtp.purelymail.com`, port `465`, SSL/TLS |
| Sent-folder copy (IMAP) | `imap.purelymail.com`, port `993`, SSL/TLS |
Every email the app sends is also saved as a copy to the account's **Sent** folder over IMAP, so the mailbox keeps a complete record of what went out.
Only two settings are asked for in `.env`: `MAIL_USER` (the email address) and `MAIL_PASSWORD`. If the account uses Two Factor Authentication, use an **App Password** instead of the real password.
## The request lifecycle
```
web form (login required)
│ received + logged
approval request → approver 1 ── deny ──► STOP: DENIED notice to
│ approve everyone involved so far
approval request → approver 2 ── deny ──► STOP: DENIED notice ...
│ approve
⋮ (… every approver in the chain, in order)
last approver approves ──► APPROVED notice to everyone involved
(requester + all approvers)
```
Each step gets a fresh secret link, so an earlier approver's link cannot decide later steps.
## Personalization placeholders
Usable in every workflow's subject and body templates:
`{workflow}`, `{requester}`, `{subject}`, `{body}`, `{request_id}`, `{step}`, `{total_steps}`, `{decided_by}`, `{summary}` (creation + each approval with timestamps, for the approved notice), `{deny_reason}`, `{approve_url}`, `{deny_url}`.
## Upgrading an existing instance (no data loss)
All schema changes are applied automatically at boot by the built-in migrations —
the SQLite database and uploaded files live in `./data/`, which is a volume and is
never touched by rebuilding the image.
```bash
cd /path/to/approvalflow
docker compose down # stop the app (data stays in ./data)
cp -r data data.backup-$(date +%F) # safety backup of DB + attachments
# replace the code with the new version, KEEPING .env and data/:
# unzip -o approvalflow.zip -d .. (or copy the new files over the old ones)
docker compose up -d --build # rebuild image, start; migrations run at boot
docker compose logs | grep migrate # optional: see what was migrated
```
Roll back = stop, restore `data.backup-…` to `data/`, start the previous image.
## Security notes
- Set a strong `SECRET_KEY` and put the app behind HTTPS (a reverse proxy such as Caddy or nginx) — the approve/deny links are secrets.
- Decision links are unguessable random tokens and work only once; a second click shows "already decided."
- Passwords are stored hashed (Werkzeug PBKDF2). Change the bootstrap admin password immediately.
- Non-admin users only ever see and use the workflows they've been granted.
## Project layout
```
app.py Flask routes, auth, admin pages
db.py SQLite schema + audit/mail-log helpers
pipeline.py approval chain + decision/notification logic
mailer.py Purelymail SMTP sending, Sent-folder copy, mail log
templates/ pages static/style.css styling
```