feat: rebuild xiteng.site homelab platform

This commit is contained in:
2026-08-12 09:48:25 +08:00
parent 557b0eca33
commit 5b84988789
128 changed files with 14979 additions and 292 deletions
+66
View File
@@ -0,0 +1,66 @@
# Label-managed edge cache
Services opt paths into the shared edge cache with Docker labels. The controller discovers the
labels, writes higher-priority Traefik routers, and maintains a single Cloudflare Cache Rule that
makes the declared host/path pairs eligible for edge caching.
```yaml
labels:
- "xiteng.site.cache.frontend.enabled=true"
- "xiteng.site.cache.frontend.routers=example,example-http"
- "xiteng.site.cache.frontend.paths=/static/,/assets/"
- "xiteng.site.cache.frontend.edge-ttl=604800"
- "xiteng.site.cache.frontend.browser-ttl=0"
- "xiteng.site.cache.frontend.stale-while-revalidate=86400"
```
The policy id (`frontend`) only needs to be unique inside one Compose service. `routers` refers to
Traefik Docker router names declared on the same service. When omitted, all explicit HTTP routers on
the service are used. Each router must declare its entrypoints and service so it can be referenced
from the file provider. Existing router middlewares are inherited, so request headers and origin-side
authentication are not silently removed.
| Field | Default | Behavior |
|---|---:|---|
| `enabled` | required | Only the exact value `true` enables a policy. |
| `routers` | auto | Comma-separated Traefik router names on the same container. |
| `paths` | required | Comma-separated directory prefixes. Each must start and end with `/`; `/`, traversal and encoded paths are rejected. |
| `hosts` | from `Host()` | Optional exact hostnames for complex router rules; hosts must be inside `xiteng.site`. |
| `edge-ttl` | `604800` | Cloudflare TTL in seconds, bounded to 60 seconds365 days. |
| `browser-ttl` | `0` | When non-zero, also overrides the browser `Cache-Control` TTL. |
| `stale-while-revalidate` | `86400` | Stale revalidation window in seconds. |
The generated router only matches `GET` and `HEAD`, has priority 1000 above its base router, and adds
`Cloudflare-CDN-Cache-Control`. Cloudflare's managed rule makes extensionless files eligible for cache,
respects the TTL header for successful responses, and gives 3xx5xx responses a zero edge TTL.
Declaring a path static is a security decision: every response below the prefix must be public and
independent of users, cookies, authorization headers, and request-specific data. Never label HTML,
API, callback, admin, download-token, or user-content paths. A Cloudflare cache hit is served before
Traefik/ForwardAuth, even though cache misses still inherit the base router's middlewares.
Removing a policy does not revoke copies already held by browsers or edge locations; choose bounded
TTLs and purge Cloudflare explicitly when previously public content must be withdrawn immediately.
## Cloudflare token
Place an untracked API token at `secrets/cloudflare_cache_api_token`. It needs access to the
`xiteng.site` zone and the permissions required to edit Cache Rules. The existing DNS-01 token is
intentionally not reused. The controller remains healthy without this file and reports
`waiting-for-token`; in that state Traefik headers and default-extension caching work, but arbitrary
extensionless URLs are not guaranteed to be cached by Cloudflare.
Create the token in Cloudflare, then install it without exposing it in shell history or chat:
```sh
./edge-cache/configure-token
```
Inspect the generated rule without contacting Cloudflare:
```sh
docker compose -f edge-cache/compose.yml exec controller \
node /app/cloudflare.mjs plan
```
The live controller automatically creates or updates only the rule whose description is
`Xiteng label-managed static paths`; it preserves all unrelated Cache Rules.