docs: replace nginx with Caddy across all documentation and installer

This commit is contained in:
2026-07-26 21:10:45 +02:00
parent 4dd7b6991d
commit 9457d3f8cc
8 changed files with 239 additions and 180 deletions
+30 -12
View File
@@ -7,7 +7,7 @@
```mermaid
graph TD
Browser[Browser / Frontend]:::accent6
Nginx[nginx reverse proxy]:::accent6
Caddy[Caddy reverse proxy]:::accent6
Server[cmd/server/main.go]:::accent0
Config[internal/config/]:::accent7
Handler[internal/handler/]:::accent0
@@ -20,8 +20,8 @@ graph TD
SMTP[(SMTP server)]:::accent2
JSONL[(data_dir/newsletter-*.jsonl)]:::accent2
Browser -->|POST + Origin| Nginx
Nginx -->|X-Forwarded-For| Server
Browser -->|POST + Origin| Caddy
Caddy -->|X-Forwarded-For| Server
Server --> Config
Server --> Handler
Handler --> RateLimit
@@ -32,8 +32,8 @@ graph TD
Handler --> Storage
Sender -->|SMTP PLAIN over net/smtp| SMTP
Storage -->|append JSONL| JSONL
Handler -->|200 / 4xx / 5xx| Nginx
Nginx --> Browser
Handler -->|200 / 4xx / 5xx| Caddy
Caddy --> Browser
classDef accent0 fill:#3B82F6,stroke:#2563EB,color:#fff
classDef accent1 fill:#22C55E,stroke:#16A34A,color:#fff
@@ -74,14 +74,28 @@ The `Form` type mirrors the TOML shape one-to-one. `ValidFormTypes` is the autho
### 3. HTTP handler — `internal/handler/`
`ContactHandler` stores its dependencies behind small interfaces so the handler pipeline can be tested without a real SMTP server or filesystem:
```go
type formSender interface {
Send(req contactform.Request) error
}
type subscriberStorer interface {
Append(sub storage.Subscriber) error
}
```
`email.FormSender` and `storage.NewsletterStore` satisfy these interfaces in production. Tests use mock implementations (`mockSender`, `mockStore`) that record calls and simulate errors.
`ContactHandler` is a single struct with four maps keyed by URL path:
```go
type ContactHandler struct {
forms map[string]*config.Form
senders map[string]*email.FormSender
senders map[string]formSender
rateLimits map[string]*rateLimiter
stores map[string]*storage.NewsletterStore
stores map[string]subscriberStorer
}
```
@@ -142,6 +156,10 @@ sequenceDiagram
end
```
### Rate limiter cleanup
The per-form `rateLimiter` type starts a background goroutine (`startCleanup`) that ticks once per hour and deletes IP entries older than twice the refill window. This prevents unbounded memory growth from one-off IPs. The cleanup goroutine is covered by `TestRateLimiterCleanup` in `contact_test.go`.
Notable behaviors:
- **CORS is path-aware** — Each form has its own `allowed_origins` list; preflights and actual requests are checked against the form being hit, not the host.
@@ -182,13 +200,13 @@ There is **no rotation**. Add a `logrotate` unit if the file grows beyond what y
```mermaid
sequenceDiagram
participant Browser
participant Nginx
participant Caddy
participant Nuntius as nuntius
participant SMTP
participant Disk
Browser->>Nginx: POST /api/nuntius/contact
Nginx->>Nuntius: forward (X-Forwarded-For, X-Real-IP)
Browser->>Caddy: POST /api/nuntius/contact
Caddy->>Nuntius: forward (X-Forwarded-For, X-Real-IP)
Nuntius->>Nuntius: CORS check
Nuntius->>Nuntius: rate-limit per IP
Nuntius->>Nuntius: parse JSON
@@ -199,8 +217,8 @@ sequenceDiagram
opt newsletter form
Nuntius->>Disk: append one JSONL line
end
Nuntius-->>Nginx: 200 {"ok": true}
Nginx-->>Browser: 200 {"ok": true}
Nuntius-->>Caddy: 200 {"ok": true}
Caddy-->>Browser: 200 {"ok": true}
```
## Config Merging