feat: enhance medialib image handling and add asset URL resolution

- Implemented `resolveApiAssetUrl` function to normalize asset URLs based on API base.
- Updated `MedialibImage` component to utilize new asset URL resolution and added support for alt text and class properties.
- Enhanced image loading behavior with improved width measurement and focal point handling.
- Added placeholder image handling and improved accessibility with alt text.
- Introduced new test script for auditing broken links in skill documentation.
- Expanded seeded test content to include medialib entries and updated related tests for pagebuilder previews.
- Improved global setup and teardown logging for clarity on seeded content management.
This commit is contained in:
2026-05-17 00:52:41 +00:00
parent 958b45272d
commit 4020ad62c5
44 changed files with 4276 additions and 867 deletions
+192
View File
@@ -0,0 +1,192 @@
---
name: deployment
description: Production deployment setup for tibi-projects Basispanel subdomain, .env, CI-Pipeline, Makefile. Use when deploying a new project to production or setting up a staging environment.
---
# Deployment
## Überblick
Ein tibi-Projekt wird per Gitea Actions CI gebaut und via rsync auf den Produktionsserver (dock4) deployed. Davor muss die Subdomain im Basispanel angelegt und der Kunde korrekt konfiguriert sein.
## 1. Basispanel Subdomain anlegen
### Kunde prüfen
```bash
# Domain des Kunden suchen
mcp_call(server="basispanel", tool="bp_list_domains", args={"search": "<kunde>"})
# → liefert Customer-ID, Domain-ID, Company, Username
```
### Subdomain anlegen
```bash
# 1. Config holen (verfügbare Webserver + Storages sehen)
mcp_call(server="basispanel", tool="bp_get_config")
# 2. Subdomain erstellen (ohne Webserver)
mcp_call(server="basispanel", tool="bp_create_subdomain", args={
"domainId": <domain-id>,
"name": "<subdomain>", # oder leer für bare domain
})
# 3. Löschen + neu mit Webserver (wenn Update nicht klappt)
mcp_call(server="basispanel", tool="bp_delete_subdomain", args={"id": <subdomain-id>})
mcp_call(server="basispanel", tool="bp_create_subdomain", args={
"domainId": <domain-id>,
"name": "<subdomain>",
"webserverKey": "dock4_lamp2",
"webserverStorage": "dock4_webroots2",
"webserverSettings": {
"redirectType": "docroot",
"docroot": "/<subdomain>.<domain>/frontend",
"gitbaseRepository": "<org>/<repo>",
"deployRoot": "./..",
"defaultAlias": "wwwAlias",
"defaultSubdomain": "defaultSubdomain",
"wwwRedirect": "wwwRedirect",
"php": "phpDisabled", # tibi-SPA kein PHP
"https": "noHttps", # erstmal aus, später aktivieren
"certbot": "noCertbot",
},
})
```
Wichtige Keys (aus `bp_get_config`):
| Server | Key | Storage |
|--------|-----|---------|
| dock4 | `dock4_lamp2` | `dock4_webroots2` |
| dock1 | `dock1_...` | `dock1_webroots...` |
### Status prüfen
```bash
mcp_call(server="basispanel", tool="bp_get_subdomain_status", args={"id": <subdomain-id>})
```
Achtung: Health-Check zeigt DNS-Warnungen (externe Nameserver) das ist normal solange der Kunde sein DNS selbst verwaltet.
## 2. `.env` konfigurieren
```env
# Basis
PROJECT_NAME=<project>
TIBI_PREFIX=tibi
TIBI_NAMESPACE=<project>
# RSYNC für Deploy
RSYNC_HOST=ftp1.webmakers.de
RSYNC_PORT=22223
PRODUCTION_RSYNC_UID=100<customer-id>00 # z.B. 10051300
PRODUCTION_RSYNC_GID=33
# Production Server
PRODUCTION_SERVER=dock4.basehosts.de
PRODUCTION_TIBI_PREFIX=tibi
PRODUCTION_PATH=/webroots2/customers/<customer-id>/htdocs
# Staging
STAGING_PATH=/staging/<org>/<project>/dev
# URLs
LIVE_URL=http://<subdomain>.<domain>.dock4.basispanel.de # Preview-URL
STAGING_URL=https://dev-<project>.staging.testversion.online
CODING_URL=https://<project>.code.testversion.online
```
## 3. CI-Pipeline (`.gitea/workflows/deploy.yml`)
```yaml
name: deploy to production
on: "push"
jobs:
deploy:
steps:
- checkout + git fetch --tags
- node 22 + yarn install
- yarn validate
- ./scripts/ci-modify-config.sh # injiziert LIVE_URL, release, preview
- yarn build # frontend
- yarn build:server # SSR
- sourcemaps → sentry
- if dev-branch: ./scripts/ci-staging.sh
- if master-branch: ./scripts/ci-deploy.sh
```
**Wichtig:** Das aktuelle Workflow-File führt `yarn validate`, `yarn build` und `yarn build:server` im CI aus. Wenn `validate` dort scheitert, behebe den eigentlichen Typ- oder Pfadfehler statt den Schritt stillschweigend zu entfernen.
## 4. Deploy-Skripte
### `scripts/ci-deploy.sh` (Production)
- Liest `.env` und `api/config.yml.env`
- rsynct `frontend/`, `api/`, `media/` via SSH zu `RSYNC_HOST`
- deshalb muessen Collection-Dateiuploads auf den Repo-Root `media/` zeigen, typischerweise via `uploadPath: ../media/<collection>` in `api/collections/*.yml`
- `api/media` ist in diesem Setup nicht der persistente Deploy-Zielpfad fuer Uploads
- Nutzt `RSYNC_USER` + `RSYNC_PASS` (aus Gitea Secrets)
- Auf master: excludiert `src/` und `*.map`
- Reloadt den projektlokalen Proxy-Endpunkt via `LIVE_URL/api/_/admin/reload` mit `Authorization: Bearer ${ADMIN_TOKEN}`
- Cleared SSR cache via `LIVE_URL/api/ssr?clear=1`
### `scripts/ci-staging.sh` (Dev/Staging)
- rsynct `api/`, `frontend/dist`, und `frontend/assets` nach `/data/${{ github.repository }}/${{ github.ref_name }}`
- Startet `docker-compose-staging.yml`
- Reloadt den projektlokalen Proxy-Endpunkt via `STAGING_URL/api/_/admin/reload` mit `Authorization: Bearer ${ADMIN_TOKEN}`
### `scripts/ci-modify-config.sh`
- Injiziert `LIVE_URL` als `originURL` in `api/hooks/config-client.js`
- Injiziert `LIVE_URL` als `PREVIEW_URL` in `api/config.yml.env`
- Setzt `release` + `buildTime` für Sentry
- Kopiert `frontend/spa.html``api/templates/spa.html` (SSR-Template)
- Ersetzt `__TIMESTAMP__` in spa.html (Cache-Busting)
## 5. Makefile
Wichtige Targets:
```makefile
# Media von Production syncen
media-sync-master-to-local:
rsync -v -e "ssh ... -l $(PRODUCTION_RSYNC_UID),$(PRODUCTION_RSYNC_GID),$(PRODUCTION_PATH)/media" \
-az $(RSYNC_HOST):/ media/
# MongoDB von Production syncen (via Chisel-Tunnel)
mongo-sync-master-to-local:
chisel client --auth coder:$$PASSWORD http://$(PRODUCTION_SERVER):10987 27017:mongo:27017 &
mongodump ... | mongorestore ...
```
## 6. DNS
Der Kunde verwaltet sein DNS selbst (externe Nameserver). Für die Subdomain muss ein A-Record gesetzt werden:
```
<subdomain>.<domain> IN A 45.129.180.102 (IP von dock4)
```
Die Preview-URL `http://<subdomain>.<domain>.dock4.basispanel.de` funktioniert ohne DNS (wird von Basispanel intern aufgelöst).
## 7. HTTPS nachträglich aktivieren
Sobald das Projekt live geht:
1. Im Basispanel Subdomain updaten:
- `https`: `"https"` (statt `"noHttps"`)
- `certbot`: `"certbot"` (automatisches Letsencrypt)
- `httpsRedirect`: `"httpsRedirect"` (HTTP→HTTPS)
2. `.env`: `LIVE_URL` auf `https://www.<domain>` ändern
3. `api/hooks/config-client.js`: `originURL` entsprechend setzen (wird von CI überschrieben)
## 8. Typische Fehler
| Problem | Ursache | Fix |
| ------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------ |
| `invalid webserverKey: dock4` | Falscher Key | Mit `bp_get_config` prüfen → `dock4_lamp2` |
| `subdomain exists` | Doppelt angelegt | Mit `bp_delete_subdomain` löschen, neu anlegen |
| `yarn validate` scheitert in CI | Typen/Submodule/Pfade nicht sauber eingecheckt | Checkout-, Submodule- und Include-Pfade korrigieren; `validate` im Workflow belassen |
| Rsync "Permission denied" | Falscher RSYNC_USER | In Gitea Secrets prüfen |
| 404 auf Subdomain | DNS nicht gesetzt | A-Record beim Kunden-DNS-Provider eintragen |