4020ad62c5
- 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.
193 lines
7.3 KiB
Markdown
193 lines
7.3 KiB
Markdown
---
|
||
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 |
|