docs: align operator and as-built docs with the code
Fix setup URL shape, import encryption UI, architecture layering/routes, and stale plan/roadmap pointers so the prose matches what the tree does. Co-Authored-By: Composer <noreply@cursor.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -51,6 +51,17 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Docs aligned with the code: setup URL is `/setup/<token>` (README and
|
||||||
|
guide; local trial rewrites the printed `https://<hostname>/…` link to
|
||||||
|
`http://127.0.0.1:8080/…`); domain import uses the file extension / magic
|
||||||
|
bytes for the password field, not an "encrypted" checkbox; architecture
|
||||||
|
layering and route table match `web`→`store` and `POST /domains/import`;
|
||||||
|
OpenDKIM drops to `opendkim` via `UserID`; guide drops the archived
|
||||||
|
"spec 7.5" pointer, clarifies `POSTFIX_SENDER_LOGIN_MAPS` vs panel writes,
|
||||||
|
and states logrotate keeps 14 daily files. Intermediate CHANGELOG cuts vs
|
||||||
|
the still-pinned compose `0.1.0` image are called out in the guide and
|
||||||
|
`development.md`. Roadmap / implementation-plan point at CHANGELOG
|
||||||
|
`[0.5.0]` Security and refreshed `internal/web` size / symbol links.
|
||||||
- Full backups no longer carry `/data/log`. It is Postfix's raw log plus its
|
- Full backups no longer carry `/data/log`. It is Postfix's raw log plus its
|
||||||
fourteen rotated copies — diagnostic output rather than state to restore, and
|
fourteen rotated copies — diagnostic output rather than state to restore, and
|
||||||
otherwise by far the largest thing in the archive.
|
otherwise by far the largest thing in the archive.
|
||||||
|
|||||||
@@ -105,7 +105,10 @@ docker logs selfpost-try 2>&1 | grep -m1 'http'
|
|||||||
docker exec selfpost-try cat /data/setup-token
|
docker exec selfpost-try cat /data/setup-token
|
||||||
```
|
```
|
||||||
|
|
||||||
Open the printed `http://…/setup?token=…` link before it expires.
|
The printed URL is `https://mail.local.test/setup/<token>`. For this local
|
||||||
|
trial rewrite it to `http://127.0.0.1:8080/setup/<token>` (same path token;
|
||||||
|
`PANEL_COOKIE_SECURE=false` so the cookie works over plain HTTP). Open it
|
||||||
|
before it expires.
|
||||||
|
|
||||||
When finished:
|
When finished:
|
||||||
|
|
||||||
|
|||||||
+14
-8
@@ -26,7 +26,7 @@ Managed programs ([build/supervisord.conf](../build/supervisord.conf)):
|
|||||||
|
|
||||||
| Program | User | Priority | Role |
|
| Program | User | Priority | Role |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `opendkim` | opendkim | 100 | DKIM signing milter |
|
| `opendkim` | root → `opendkim` (`UserID` in opendkim.conf) | 100 | DKIM signing milter |
|
||||||
| `panel` | panel | 200 | HTTP UI + journal-milter + log-tailer goroutine |
|
| `panel` | panel | 200 | HTTP UI + journal-milter + log-tailer goroutine |
|
||||||
| `postfix` | root (wrapper) | 300 | MTA — started only after both milter sockets exist |
|
| `postfix` | root (wrapper) | 300 | MTA — started only after both milter sockets exist |
|
||||||
| `postfix-reload` | root | — | On-demand `postfix reload` (autostart off) |
|
| `postfix-reload` | root | — | On-demand `postfix reload` (autostart off) |
|
||||||
@@ -151,8 +151,10 @@ state for an older message and the page reports it as such, not as a failure.
|
|||||||
|
|
||||||
## Panel HTTP surface
|
## Panel HTTP surface
|
||||||
|
|
||||||
Route table: [internal/web/web.go](../internal/web/web.go). Authenticated
|
Canonical routes: [internal/web/web.go](../internal/web/web.go). Authenticated
|
||||||
unless noted.
|
unless noted. The table below is a summary — HTMX fragment endpoints
|
||||||
|
(`/status/fragment`, `/deliveries/rows`, `/mail-queue/body`,
|
||||||
|
`/system-log/body`, …) and every POST variant live in `web.go`.
|
||||||
|
|
||||||
| Route | Purpose |
|
| Route | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -161,12 +163,13 @@ unless noted.
|
|||||||
| `/login`, `/logout` | Session auth |
|
| `/login`, `/logout` | Session auth |
|
||||||
| `/status` | Process, cert, socket, PTR checks; machine CPU/memory/network |
|
| `/status` | Process, cert, socket, PTR checks; machine CPU/memory/network |
|
||||||
| `/domains`, `/domains/*` | Domain and application CRUD, DKIM, L2 limits |
|
| `/domains`, `/domains/*` | Domain and application CRUD, DKIM, L2 limits |
|
||||||
|
| `/domains/import` | Domain import (`POST`; form on the Backup page) |
|
||||||
| `/deliveries` | Send log with filters |
|
| `/deliveries` | Send log with filters |
|
||||||
| `/deliveries/{id}` | One send-log row in full, with its `mail.log` lines |
|
| `/deliveries/{id}` | One send-log row in full, with its `mail.log` lines |
|
||||||
| `/mail-queue` | Postfix queue view |
|
| `/mail-queue` | Postfix queue view |
|
||||||
| `/system-log` | `mail.log` tail |
|
| `/system-log` | `mail.log` tail |
|
||||||
| `/reload` | Reload OpenDKIM + Postfix maps |
|
| `/reload` | Reload OpenDKIM + Postfix maps |
|
||||||
| `/backup` | Full backup download, domain import |
|
| `/backup` | Full backup download (page also hosts the import form) |
|
||||||
| `/account` | Admin username/password |
|
| `/account` | Admin username/password |
|
||||||
|
|
||||||
HTMX polling refreshes monitoring fragments (5 s while the operator is active on
|
HTMX polling refreshes monitoring fragments (5 s while the operator is active on
|
||||||
@@ -198,10 +201,12 @@ cookie and idle timeout has not expired.
|
|||||||
|
|
||||||
## Code layers
|
## Code layers
|
||||||
|
|
||||||
Handlers never touch SQLite or the filesystem directly; every write that has to
|
Multi-store writes that must land in more than one place (SQLite row,
|
||||||
land in more than one place (SQLite row, `sasldb2` entry, Postfix map, OpenDKIM
|
`sasldb2` entry, Postfix map, OpenDKIM table) go through a service, which is
|
||||||
table) goes through a service, which is also where the rollback of a partial
|
also where the rollback of a partial failure lives. Handlers may call
|
||||||
failure lives. The adapters below the services are the only code that knows
|
`store` directly for single-table reads and simple writes (sessions, admin,
|
||||||
|
send-log queries); the first-run setup-token file is read and written in
|
||||||
|
`web` itself. The adapters below the services are the only code that knows
|
||||||
about Postfix, OpenDKIM, DNS or the log file, which is what makes them
|
about Postfix, OpenDKIM, DNS or the log file, which is what makes them
|
||||||
substitutable in tests — `milter.Store`, `app.SenderMaps` and
|
substitutable in tests — `milter.Store`, `app.SenderMaps` and
|
||||||
`logtail.StatusStore` are the seams the unit tests replace with fakes.
|
`logtail.StatusStore` are the seams the unit tests replace with fakes.
|
||||||
@@ -236,6 +241,7 @@ flowchart TB
|
|||||||
panel --> logtail
|
panel --> logtail
|
||||||
backupcli --> backupPkg
|
backupcli --> backupPkg
|
||||||
backupcli --> secretfile
|
backupcli --> secretfile
|
||||||
|
web --> store
|
||||||
web --> domainSvc
|
web --> domainSvc
|
||||||
web --> appSvc
|
web --> appSvc
|
||||||
web --> backupPkg
|
web --> backupPkg
|
||||||
|
|||||||
+8
-4
@@ -44,7 +44,7 @@ mechanics → Haiku. Reviewers must not be the author of the code under review.
|
|||||||
|---|---|
|
|---|---|
|
||||||
| **Go** | 1.26+ (`go.mod`); `CGO_ENABLED=0` — pure Go, static linking |
|
| **Go** | 1.26+ (`go.mod`); `CGO_ENABLED=0` — pure Go, static linking |
|
||||||
| **SQLite** | `modernc.org/sqlite` (pure Go, no cgo) |
|
| **SQLite** | `modernc.org/sqlite` (pure Go, no cgo) |
|
||||||
| **Build** | [Makefile](../Makefile): `vet`, `test`, `build`, `e2e` |
|
| **Build** | [Makefile](../Makefile): primary targets `vet`, `test`, `build`, `e2e` (also `all`, `clean`) |
|
||||||
| **Container** | Docker + Compose v2 on the dev host and in CI |
|
| **Container** | Docker + Compose v2 on the dev host and in CI |
|
||||||
| **Image (build stage)** | `golang:1.26-bookworm` — [build/Dockerfile](../build/Dockerfile) |
|
| **Image (build stage)** | `golang:1.26-bookworm` — [build/Dockerfile](../build/Dockerfile) |
|
||||||
| **Image (runtime)** | `debian:bookworm-slim` + Postfix, OpenDKIM, supervisord, SASL, logrotate |
|
| **Image (runtime)** | `debian:bookworm-slim` + Postfix, OpenDKIM, supervisord, SASL, logrotate |
|
||||||
@@ -163,7 +163,11 @@ The release image is published **only on tag** `vX.Y.Z` (not on every push to
|
|||||||
commit as the tag (see [roadmap.md](roadmap.md) § «v1.x — documentation and
|
commit as the tag (see [roadmap.md](roadmap.md) § «v1.x — documentation and
|
||||||
deploy tail»).
|
deploy tail»).
|
||||||
|
|
||||||
Ordinary commits **do not** publish an image.
|
Ordinary commits **do not** publish an image. Intermediate CHANGELOG version
|
||||||
|
cuts (`0.2.0`…`0.6.0`) document history on `main`; the compose pin and the only
|
||||||
|
git release tag may lag until an explicit image publish (today: compose
|
||||||
|
`0.1.0`, tag `v0.0.1` — see [roadmap.md](roadmap.md) § «v1.x — documentation
|
||||||
|
and deploy tail»).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -200,8 +204,8 @@ go test ./...
|
|||||||
|
|
||||||
### Env documentation regression
|
### Env documentation regression
|
||||||
|
|
||||||
`go test ./cmd/panel -run TestLoadConfig` — every new `loadConfig` key must
|
`go test ./cmd/panel -run 'TestLoadConfigKeysDocumented|TestBuildScriptKeysDocumented|TestDocumentedKeysAreRead'`
|
||||||
appear in the env lists in [guide.md](guide.md)
|
— every new `loadConfig` key must appear in the env lists in [guide.md](guide.md)
|
||||||
([cmd/panel/envdoc_test.go](../cmd/panel/envdoc_test.go)).
|
([cmd/panel/envdoc_test.go](../cmd/panel/envdoc_test.go)).
|
||||||
|
|
||||||
### End-to-end (container suite)
|
### End-to-end (container suite)
|
||||||
|
|||||||
+33
-18
@@ -59,6 +59,12 @@ reverse proxy, no `./certs` bind mount — Postfix still starts, but the Status
|
|||||||
page will report missing TLS material until you mount PEM files at
|
page will report missing TLS material until you mount PEM files at
|
||||||
`/etc/postfix/tls/fullchain.pem` and `privkey.pem`.
|
`/etc/postfix/tls/fullchain.pem` and `privkey.pem`.
|
||||||
|
|
||||||
|
The one-time setup link is always printed as
|
||||||
|
`https://<SELFPOST_HOSTNAME>/setup/<token>` (and written the same way to
|
||||||
|
`/data/setup-token`). For a local trial that means rewriting the host and
|
||||||
|
scheme to `http://127.0.0.1:8080/setup/<token>` — the path token is what
|
||||||
|
matters; the hostname in the printed URL is not reachable as written.
|
||||||
|
|
||||||
**What works:** the full panel — setup, domains, applications, deliveries view,
|
**What works:** the full panel — setup, domains, applications, deliveries view,
|
||||||
mail queue, system log. **What does not:** reliable outbound delivery to the
|
mail queue, system log. **What does not:** reliable outbound delivery to the
|
||||||
public internet (no PTR, no real DNS for your domains, port 25 may be blocked
|
public internet (no PTR, no real DNS for your domains, port 25 may be blocked
|
||||||
@@ -116,15 +122,17 @@ supported configuration:
|
|||||||
`DKIM_SELECTOR_DEFAULT` (`selfpost`), `SASL_DB_PATH`
|
`DKIM_SELECTOR_DEFAULT` (`selfpost`), `SASL_DB_PATH`
|
||||||
(`/data/sasl/sasldb2`), `SASL_REALM` (defaults to `SELFPOST_HOSTNAME`),
|
(`/data/sasl/sasldb2`), `SASL_REALM` (defaults to `SELFPOST_HOSTNAME`),
|
||||||
`POSTFIX_DIR` (`/data/postfix`), `POSTFIX_SENDER_LOGIN_MAPS`
|
`POSTFIX_DIR` (`/data/postfix`), `POSTFIX_SENDER_LOGIN_MAPS`
|
||||||
(`/data/postfix/sender_login_maps`).
|
(`/data/postfix/sender_login_maps` — read by Postfix config only; the panel
|
||||||
|
always writes `<POSTFIX_DIR>/sender_login_maps`, so overriding this env alone
|
||||||
|
desyncs the map Postfix reads from the file the panel maintains).
|
||||||
- **Milter and Postfix startup:** `MILTER_CONNECT_TIMEOUT` (`15s`),
|
- **Milter and Postfix startup:** `MILTER_CONNECT_TIMEOUT` (`15s`),
|
||||||
`MILTER_COMMAND_TIMEOUT` (`15s`), `MILTER_CONTENT_TIMEOUT` (`30s`),
|
`MILTER_COMMAND_TIMEOUT` (`15s`), `MILTER_CONTENT_TIMEOUT` (`30s`),
|
||||||
`MILTER_WAIT_TIMEOUT` (`30` seconds).
|
`MILTER_WAIT_TIMEOUT` (`30` seconds).
|
||||||
- **Background maintenance:** `TLS_RELOAD_INTERVAL_SECONDS` (`86400` — daily
|
- **Background maintenance:** `TLS_RELOAD_INTERVAL_SECONDS` (`86400` — daily
|
||||||
`postfix reload` to pick up renewed certificates),
|
`postfix reload` to pick up renewed certificates),
|
||||||
`LOGROTATE_INTERVAL_SECONDS` (`21600` — check `mail.log` rotation every six
|
`LOGROTATE_INTERVAL_SECONDS` (`21600` — check `mail.log` rotation every six
|
||||||
hours; rotated logs are kept 14 days and each rotation triggers
|
hours; logrotate keeps 14 rotated files on a daily schedule, and each
|
||||||
`postfix reload`).
|
rotation triggers `postfix reload`).
|
||||||
|
|
||||||
## DNS setup
|
## DNS setup
|
||||||
|
|
||||||
@@ -212,8 +220,9 @@ service healthy and will mail be accepted?"
|
|||||||
hours. It lives in the data volume, so it survives a container recreate along
|
hours. It lives in the data volume, so it survives a container recreate along
|
||||||
with the rest of the state — `./data/log/` on the host — but it is *not*
|
with the rest of the state — `./data/log/` on the host — but it is *not*
|
||||||
included in backups: it is diagnostics, not state.
|
included in backups: it is diagnostics, not state.
|
||||||
- **Backup** (`/backup`) — download a full-server backup or import a
|
- **Backup** (`/backup`) — download a full-server backup; the same page hosts
|
||||||
single-domain export. See [Backup, restore, and moving a single domain](#backup-restore-and-moving-a-single-domain).
|
the domain-import form (`POST /domains/import`). See
|
||||||
|
[Backup, restore, and moving a single domain](#backup-restore-and-moving-a-single-domain).
|
||||||
- **Account** (`/account`) — change the administrator username and/or password.
|
- **Account** (`/account`) — change the administrator username and/or password.
|
||||||
Application SASL logins are separate and are not changed here.
|
Application SASL logins are separate and are not changed here.
|
||||||
|
|
||||||
@@ -243,9 +252,10 @@ or poll `docker inspect` health state on the host.
|
|||||||
**First-time setup link.** On first start the one-time setup URL is printed in
|
**First-time setup link.** On first start the one-time setup URL is printed in
|
||||||
the container log (`docker compose logs -f`) and written to `/data/setup-token`
|
the container log (`docker compose logs -f`) and written to `/data/setup-token`
|
||||||
inside the container — `./data/setup-token` on the host, mode `0600` — then
|
inside the container — `./data/setup-token` on the host, mode `0600` — then
|
||||||
deleted when setup completes. The link is a bearer token valid for ten minutes.
|
deleted when setup completes. The link is
|
||||||
If this host ships container logs to a central aggregator, prefer reading the
|
`https://<SELFPOST_HOSTNAME>/setup/<token>` (path token, not a query string),
|
||||||
file:
|
valid for ten minutes. If this host ships container logs to a central
|
||||||
|
aggregator, prefer reading the file:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker compose exec selfpost cat /data/setup-token
|
docker compose exec selfpost cat /data/setup-token
|
||||||
@@ -273,10 +283,11 @@ exceeded, Postfix returns a 4xx and the refusal is recorded in Deliveries as
|
|||||||
|
|
||||||
## Backup, restore, and moving a single domain
|
## Backup, restore, and moving a single domain
|
||||||
|
|
||||||
Two related but distinct operations — spec 7.5:
|
Two related but distinct operations
|
||||||
|
([architecture.md](architecture.md) § Persistence):
|
||||||
|
|
||||||
- **Full backup** (whole `/data`: SQLite, all domains' DKIM keys, all
|
- **Full backup** (whole `/data` except `log/`: SQLite, all domains' DKIM keys,
|
||||||
applications' SASL credentials, `manifest.json` with the version that
|
all applications' SASL credentials, `manifest.json` with the version that
|
||||||
created it): panel button (*Backup* → *Full backup*), or from the
|
created it): panel button (*Backup* → *Full backup*), or from the
|
||||||
host:
|
host:
|
||||||
```sh
|
```sh
|
||||||
@@ -329,8 +340,10 @@ with AES-256-GCM, in chunks, so a truncated or altered file fails to open rather
|
|||||||
than restoring quietly. **SelfPost does not store the password** — lose it and
|
than restoring quietly. **SelfPost does not store the password** — lose it and
|
||||||
the file is unrecoverable, which is the entire point.
|
the file is unrecoverable, which is the entire point.
|
||||||
|
|
||||||
*Import a domain* takes an encrypted export directly: tick **The file is
|
*Import a domain* takes an encrypted export directly: choose a `.spde` file and
|
||||||
encrypted** and give the password.
|
the password field appears (driven by the file extension in the browser; the
|
||||||
|
server also detects the envelope by its magic bytes). A plain `.json` export
|
||||||
|
needs no password.
|
||||||
|
|
||||||
A full backup has to be turned back into a plain archive before it can be
|
A full backup has to be turned back into a plain archive before it can be
|
||||||
unpacked into `/data`, which the CLI does with the same password:
|
unpacked into `/data`, which the CLI does with the same password:
|
||||||
@@ -359,10 +372,12 @@ but it can look like an open port in external scans.
|
|||||||
## Fixed image tag
|
## Fixed image tag
|
||||||
|
|
||||||
`deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`),
|
`deploy/docker-compose.yml` pins an explicit version (`ghcr.io/mixeme/selfpost:X.Y.Z`),
|
||||||
deliberately never `:latest`. This is a direct consequence of the backup
|
deliberately never `:latest`. Until the `v1.0.0` cut the shipped pin is still
|
||||||
version check above: the panel binary's embedded version and the image tag
|
`0.1.0` — intermediate CHANGELOG sections (`0.2.0`…`0.6.0`) record development
|
||||||
that produced it are the same value by construction (the release CI stamps
|
cuts and do not imply a published image of that tag. Pinning matters because of
|
||||||
both from one git tag — see `.github/workflows/release.yml`), so pinning the
|
the backup version check above: the panel binary's embedded version and the
|
||||||
tag is what makes "restore into the same version" a checkable fact rather than
|
image tag that produced it are the same value by construction (the release CI
|
||||||
|
stamps both from one git tag — see `.github/workflows/release.yml`), so the
|
||||||
|
pin is what makes "restore into the same version" a checkable fact rather than
|
||||||
a guess. Upgrade by bumping the tag deliberately, not by riding a moving
|
a guess. Upgrade by bumping the tag deliberately, not by riding a moving
|
||||||
target.
|
target.
|
||||||
|
|||||||
@@ -27,5 +27,6 @@ e2e/CI в [development.md](development.md), принятые риски в
|
|||||||
whitelist'ом), не могло быть разобрано getopt как флаг
|
whitelist'ом), не могло быть разобрано getopt как флаг
|
||||||
([internal/app/sasl.go](../internal/app/sasl.go)). Принятые риски в
|
([internal/app/sasl.go](../internal/app/sasl.go)). Принятые риски в
|
||||||
[security.md](security.md) не пополнились — существующие записи (origin-check
|
[security.md](security.md) не пополнились — существующие записи (origin-check
|
||||||
fallback, отсутствие CSRF-токенов, send-log gap) покрывают всё найденное.
|
fallback, отсутствие CSRF-токенов, ложно-отрицательный `bounced` при сверке
|
||||||
Сводка ревизии — в записи `Security` CHANGELOG `[Unreleased]`.
|
с `postqueue`) покрывают всё найденное.
|
||||||
|
Сводка ревизии — в записи `Security` CHANGELOG `[0.5.0]`.
|
||||||
|
|||||||
+5
-4
@@ -38,7 +38,7 @@ compose неделю ссылается на несуществующий обр
|
|||||||
|
|
||||||
**Убрать `implementation-plan.md` — в релизном коммите.** Документ закрыт:
|
**Убрать `implementation-plan.md` — в релизном коммите.** Документ закрыт:
|
||||||
уникального содержания в нём нет, § D (предрелизная ревизия безопасности)
|
уникального содержания в нём нет, § D (предрелизная ревизия безопасности)
|
||||||
продублирован в [security.md](security.md) и CHANGELOG `[Unreleased]/Security`,
|
продублирован в [security.md](security.md) и CHANGELOG `[0.5.0]/Security`,
|
||||||
а разделы B.1–B.3 и C.4 вырезаны ещё в `22f86d1`. Держится до тега только
|
а разделы B.1–B.3 и C.4 вырезаны ещё в `22f86d1`. Держится до тега только
|
||||||
потому, что описывает релизный гейт, пока тот формально не закрыт. При резке
|
потому, что описывает релизный гейт, пока тот формально не закрыт. При резке
|
||||||
версии:
|
версии:
|
||||||
@@ -137,7 +137,7 @@ git-тег `vX.Y.Z`; `implementation-plan.md` и `v1.x-closure-plan.md` удал
|
|||||||
|
|
||||||
## Роль администратора домена — кандидат на 2.x
|
## Роль администратора домена — кандидат на 2.x
|
||||||
|
|
||||||
**Что это.** Сейчас в панели ровно один субъект: `requireAuth` — булев гейт, а не роль ([web.go:182](../internal/web/web.go:182)), сессия не несёт ничего, кроме факта входа. Роль выдаёт доступ к одному домену и только к нему: приложения этого домена (создание, режим отправителя, перегенерация пароля, удаление, свой L2-лимит), DKIM/DNS-статус домена и журнал отправки, отфильтрованный по домену — фильтр в журнале уже есть ([handlers_monitor.go:49](../internal/web/handlers_monitor.go:49)). Вне роли остаётся то, что глобально по своей природе: добавление и удаление доменов, `/reload`, полный бэкап (это весь `/data` вместе с `sasldb2`, то есть все домены сразу), очередь и хвост `mail.log` — они серверные и к домену не привязаны.
|
**Что это.** Сейчас в панели ровно один субъект: `requireAuth` — булев гейт, а не роль ([web.go](../internal/web/web.go) — обёртка `mux.Handle("/", s.requireAuth(authed))`), сессия не несёт ничего, кроме факта входа. Роль выдаёт доступ к одному домену и только к нему: приложения этого домена (создание, режим отправителя, перегенерация пароля, удаление, свой L2-лимит), DKIM/DNS-статус домена и журнал отправки, отфильтрованный по домену — фильтр в журнале уже есть ([sendLogData](../internal/web/handlers_monitor.go)). Вне роли остаётся то, что глобально по своей природе: добавление и удаление доменов, `/reload`, полный бэкап (это весь `/data` вместе с `sasldb2`, то есть все домены сразу), очередь и хвост `mail.log` — они серверные и к домену не привязаны.
|
||||||
|
|
||||||
**Почему 2.x, а не v1.x.** [product.md](product.md) относит «несколько пользователей
|
**Почему 2.x, а не v1.x.** [product.md](product.md) относит «несколько пользователей
|
||||||
панели, роли» к out of scope (один администратор), поэтому появление второго
|
панели, роли» к out of scope (один администратор), поэтому появление второго
|
||||||
@@ -172,8 +172,9 @@ README, `architecture.md`; [roadmap.md](roadmap.md) — внутренний т
|
|||||||
|
|
||||||
## Разбиение `internal/web` на подпакеты — кандидат на 2.x
|
## Разбиение `internal/web` на подпакеты — кандидат на 2.x
|
||||||
|
|
||||||
**Что это.** `internal/web` — самый крупный пакет проекта: 47 файлов, ~3030
|
**Что это.** `internal/web` — самый крупный пакет проекта: ~50 файлов
|
||||||
строк, в одной плоскости лежат хендлеры всех разделов панели, сессии,
|
(включая шаблоны и static), ~25 `.go` / ~4000 строк Go, в одной плоскости
|
||||||
|
лежат хендлеры всех разделов панели, сессии,
|
||||||
security-заголовки, проверка Origin, валидация форм и рендер шаблонов.
|
security-заголовки, проверка Origin, валидация форм и рендер шаблонов.
|
||||||
Кандидаты на выделение — `web/handlers` и `web/auth`, либо разрез по доменам
|
Кандидаты на выделение — `web/handlers` и `web/auth`, либо разрез по доменам
|
||||||
панели.
|
панели.
|
||||||
|
|||||||
Reference in New Issue
Block a user