docs: replace nginx with Caddy across all documentation and installer
This commit is contained in:
+30
-12
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user