panel: shared nav, account settings, backup page, connection settings

Phase 12 (UI/UX). The navigation bar now renders once from layout.html
instead of being copied into each content template, so it is present on
every authenticated page — including the domain page and its delete
confirmation, which had no links at all — and the current page is
highlighted via .Active rather than quietly dropping out of the list.

New /account page changes the administrator's username and/or password:
the current password is required and the attempt is throttled on the same
limiter as the login form, so this route cannot be used to brute-force
past that limit. A password change invalidates every other session while
keeping the one performing it; a rename carries that session over.

Backup and domain import move from a card in the middle of the domain
list to their own /backup page, one card each; the handlers themselves
are unchanged, only the page the import form renders its errors on.

The domain page gains a "Sending server settings" card (server, port,
encryption) so a client can be configured without reading the docs; 587
is listed only when SUBMISSION_ENABLE is true for this deployment, which
is a deploy-time flag the panel cannot verify at runtime.

Client-side (static/panel.js, no libraries): Copy buttons on the values
that get carried elsewhere (DKIM record, new application credentials,
server name), and the Addresses field is hidden while the address mode is
wildcard, where the server ignores it.

Verified in a container on the dev server: setup, login, every page's
nav and active item, domain and application creation, all account-form
paths including cross-session invalidation, import errors, full backup
download. gofmt/vet/test/docker build green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-01 21:34:59 +03:00
parent 558c059341
commit fc53ae1314
28 changed files with 720 additions and 282 deletions
+6 -4
View File
@@ -39,7 +39,9 @@ docker compose up -d
This starts SelfPost alone; it assumes Apache is already installed on the host
as the reverse proxy (see below) and expects certificates at `./certs`. The
first log line (`docker compose logs -f`) prints the one-time setup link —
open it to create the admin account.
open it to create the admin account. That username and password can be changed
later from the panel's *Account* page (changing the password signs out every
other session).
## Reverse proxy (mandatory)
@@ -101,7 +103,7 @@ Two related but distinct operations — spec 7.5:
- **Full backup** (whole `/data`: SQLite, all domains' DKIM keys, all
applications' SASL credentials, `manifest.json` with the version that
created it): panel button (dashboard → *Backup & migration*), or from the
created it): panel button (*Backup* → *Full backup*), or from the
host:
```sh
docker exec <container> selfpost-backup > selfpost-backup.tar.gz
@@ -113,8 +115,8 @@ Two related but distinct operations — spec 7.5:
known version, there'd be no way to tell which image restoring a given
backup actually requires.
- **Export/import a single domain** (dashboard → domain page → *Export
domain*): moves one domain — its DKIM key and its applications' **working**
- **Export/import a single domain** (domain page → *Export domain* to write the
file, *Backup* → *Import a domain* to read it back in): moves one domain — its DKIM key and its applications' **working**
SASL passwords — to a different SelfPost instance without regenerating
anything, so DNS (the DKIM TXT record) doesn't need to change. Unlike a full
restore, this works across different hostnames/instances.