my-homelab-configs/apps/website/README.md

109 lines
3.5 KiB
Markdown

# Website Runtime
The portfolio website keeps Apache at the core while moving PHP execution to
PHP-FPM.
## Runtime Shape
- Base image: `php:8.5-fpm-alpine`
- Web server: Alpine Apache, listening on container port `8080`
- PHP runtime: PHP-FPM on `127.0.0.1:9000`
- PHP cache: OPcache enabled, with timestamp validation disabled for immutable
image deploys
- Dynamic storage: `/var/www/localhost/htdocs/db` mounted from the retained
`website-db` PVC
Apache routes `*.php` through `proxy_fcgi` to PHP-FPM. The startup script starts
PHP-FPM in the background and then runs Apache in the foreground.
## Static Render Path
`render-static-pages.sh` runs during the image build and renders static HTML
for the read-heavy pages:
- `/`
- `/cv.php`
- `/blog.php`
- `/demos.php`
- `/homelab-tree.php`
Static output is generated for the committed static languages under
`/static-pages/<lang>/`. Apache rewrites supported static-language requests to
those generated HTML files. Requests for runtime-generated languages still fall
through to PHP so the translation workflow continues to work.
`blog.php` no longer renders visitor ideas directly into the HTML. Recent ideas
are served by `visitor_ideas.php` and loaded by `visitor-ideas.js`, which keeps
the blog page cacheable while preserving the write path through `save_idea.php`.
## Cache Path
The website sets cache headers for pre-rendered HTML:
```text
Cache-Control: public, max-age=300, s-maxage=3600
```
Dynamic write and translation endpoints are marked `no-store`:
- `save_idea.php`
- `visitor_ideas.php`
- `translate.php`
- `save_lang.php`
The OCI edge nginx layer handles gzip, cache HITs, HTTP/2, TLS session reuse,
client keepalive, and upstream keepalive to the Traefik backend. CSS and
JavaScript keep the edge's longer immutable asset policy.
## Translation Flow
Unsupported browser languages call the same-origin `translate.php` endpoint.
The endpoint sanitizes and batches text, checks Redis per string, calls the
custom `website-translator` Ollama model only for cache misses, writes fresh
translations back to Redis with a TTL, then returns results in the original
order. The browser never calls the private Ollama address directly.
n8n is deployed separately at `https://n8n.lab2025.duckdns.org` and receives
`OLLAMA_BASE_URL=http://192.168.100.73:11434` so workflows can prewarm
translations or run batch jobs against the Debian-host Ollama API.
## Translation Observability
`translate.php` emits one structured JSON log event for each valid translation
request. The event name is `website_translation` and the payload does not include
source text or translated text.
Useful fields:
- `cache_hits`
- `cache_misses`
- `ollama_latency_ms`
- `failed_json_parse_count`
- `timeout_count`
- `status`
- `reason`
In Loki, filter website logs for `"event":"website_translation"` to inspect
cache behavior, Ollama latency, JSON parse failures, and timeout failures.
## Validation
Do not build images locally. The image is built and deployed by the Debian
homelab path and Gitea Actions.
Local source validation that does not build the image:
```bash
shellcheck apps/website/start-website.sh apps/website/render-static-pages.sh
find apps/website -name "*.php" -print0 | xargs -0 -n1 php -l
node --check apps/website/visitor-ideas.js
kubectl kustomize apps/website >/tmp/website-kustomize.yaml
```
Static rendering can be checked without Docker by copying the app to a temporary
directory and running:
```bash
sh render-static-pages.sh /path/to/copied/apps/website
```