# dms-ui · docker-mailserver web management UI

A small FastAPI + vanilla-JS web console for managing a remote
[docker-mailserver](https://github.com/docker-mailserver/docker-mailserver)
container over SSH. Lives next to the existing `sharekit` / `miniquota` /
`mcp-server` projects on this host and re-uses `/home/hermes/venv`.

UI labels are bilingual (English + 中文) since the operator wears both hats.

## Features

- **Dashboard** — container status, postqueue depth, account count, disk usage, last 30 mail.log lines.
- **Accounts** — list / add / update password / restrict / delete, optional quota on creation, live password strength meter.
- **Aliases** — list / add / delete.
- **Quotas** — per-account set / delete (shown next to the current quota).
- **Postfix queue** — `postqueue -p` viewer plus a "flush" button (`postqueue -f`).
- **Logs** — live `tail -F /var/log/mail.log` over Server-Sent Events, with pause + search.
- **Multi-host** — `config.json` lists as many DMS hosts as you like; the top selector switches between them.

## Architecture

- `app.py` — FastAPI app, all `/api/*` endpoints, session-cookie auth.
- `ssh_client.py` — thin wrapper around `paramiko` (preferred) or `subprocess + ssh` with `SSH_ASKPASS` fallback. Each request opens a fresh connection; **no long-lived SSH state** is kept between requests.
- `static/` — single-page Tailwind-CDN UI.

All API responses are JSON of the shape `{"ok": bool, "stdout": str, "stderr": str, "exit_code": int, "data": any, "error": str}`.

## Configuration

`config.json` lives at `/home/hermes/dms-ui/config.json` (copy from
`config.example.json`). The example already points at the production host
(`smail.icu` via the `smail-server` SSH alias). Passwords are read from this
file; alternatively the UI will fall back to `/home/hermes/.ssh-askpass.sh`
when the field is left blank.

## Auth

- Set `DMS_UI_ADMIN_PASSWORD` in the environment (default: `admin`).
- Login posts to `/api/login` and receives a `dms_ui_session` cookie. All
  other endpoints require it.

## Run

```bash
/home/hermes/venv/bin/python /home/hermes/dms-ui/app.py --host 127.0.0.1 --port 8792
```

Binds to `127.0.0.1` by default; expose via your existing reverse proxy
(Caddy / nginx) — the sharekit project has a known pattern for this.

## Install (automated)

```bash
bash /home/hermes/dms-ui/install.sh
```

This will:

1. `pip install paramiko` into the shared venv (idempotent).
2. Copy `config.example.json` to `config.json` if missing.
3. Install `systemd/dms-ui.service` to `/etc/systemd/system/`.
4. `systemctl enable --now dms-ui`.

## Files

```
/home/hermes/dms-ui/
├── BRIEF.md
├── README.md
├── app.py
├── ssh_client.py
├── config.example.json
├── install.sh
├── systemd/dms-ui.service
└── static/
    ├── app.js
    ├── index.html
    └── style.css
```

## Operator notes

- The production DMS container is named `mailserver`; all setup commands are
  invoked as `docker exec -i mailserver setup <sub> ...`.
- `setup email update` is the supported way to set/change a password.
- The first creation also calls `setup email update` to set the password,
  since `setup email add` does not always propagate the password on every
  DMS version.
- `setup email restrict` / `unrestrict` toggles login without deleting mail.
- `setup quota set <addr> <size>` — size format is `<number><G|M|K>`, e.g. `5G`.

## TODOs / known limitations

- The log tail endpoint requires `paramiko` (the streaming channel can't be
  expressed in `subprocess.run`). The non-streaming backend is still used for
  every other call.
- `setup quota get <addr>` is slow on hosts with many accounts because we
  shell out per account. A bulk query via `doveadm quota get -A` would be
  faster; left as a follow-up.
- TLS is terminated by the shared reverse proxy; the app binds plain HTTP.
- No CSRF token on the session cookie — acceptable because we only accept
  JSON `Content-Type: application/json` and the cookie is `SameSite=Lax`.
