v_9.3
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user