b2in/dev/2026-08-28/preview-deployment.md
Kevin Adametz c8899e2d7d Markenlogos einbinden: Seitenkopf und alle Kacheln
Zulieferung dev/2026-08-28/markenlogos/ (CABINET und Rotpunkt als SVG, Februe
als PNG). Die Dateien liegen unter public/img/assets/marken/ und haengen als
`logo` an der jeweiligen Marke.

- Kopf der Markenseite: Logo anstelle des Schriftzugs, im <h1>, damit die
  Ueberschrift erhalten bleibt – den Text liefert das alt-Attribut. Ohne Logo
  erscheint weiterhin der Schriftzug.
- Logo zusaetzlich auf der Startseiten-Kachel, im Block auf /marken und in den
  „Weitere Marken“-Kacheln am Seitenfuss.
- Feste Box (Hoehe + Maximalbreite + object-contain) statt fester Hoehe: die
  Seitenverhaeltnisse gehen weit auseinander (Februe 2,3:1, Rotpunkt 1,2:1),
  mit reiner Hoehenangabe waere Februe doppelt so breit erschienen. Konvention
  aus der Sponsoring-Sektion.
- Februe-Logo auf seinen Inhalt beschnitten: im Original waren knapp drei
  Viertel der Flaeche leerer Rand, in einer Box haette es winzig gewirkt.
  53 KB → 15 KB.
- BrandLogoSeeder traegt die Logos auf bestehenden Installationen nach –
  CmsBrandSeeder ueberspringt vorhandene Marken. Setzt nur `logo` und nur bei
  leerem Feld, ein im Admin hochgeladenes Logo bleibt stehen.

Tests: 871 gruen (3215 Assertions), Pint sauber, alle Themes gebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 11:16:41 +00:00

343 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Vorschau-Instanz auf dem Live-Server einrichten
**Ziel:** `https://preview.b2in.eu` zeigt den neuen Stand zur Abstimmung. **Die Live-Seite bleibt
unangetastet** – es wird keine einzige Datei im Live-Verzeichnis überschrieben und keine Zeile in
der Live-Datenbank geändert.
**Prinzip:** zweites, eigenständiges Verzeichnis · eigene `.env` · eigene Datenbank.
```
/var/www/b2in/ ← Live, bleibt wie es ist
/var/www/b2in-preview/ ← Kopie davon, darauf das Update
```
> Pfade unten sind Beispiele. Ersetze `/var/www/b2in` durch das echte Live-Verzeichnis und
> `b2in_live` / `b2in_preview` durch die echten Datenbanknamen.
---
## 0. Warum nicht einfach die Live-Dateien tauschen
Zwei Gründe, die diesen Aufwand rechtfertigen:
1. **Der Relaunch-Seeder überschreibt CMS-Sektionen.** `InteriorRelaunchCmsSeeder` schreibt 23
Sektionen mit den neuen Texten. Auf der Live-Datenbank ausgeführt wäre der alte Stand weg.
2. **Migrationen sind nicht umkehrbar.** `cms_brands` und die beiden Spalten `summary` /
`highlights` kommen neu dazu.
Mit einer eigenen Datenbank kann beim Testen nichts passieren, was die Live-Seite betrifft.
---
## 1. Voraussetzungen
- [ ] DNS-Eintrag `preview.b2in.eu` → dieselbe Server-IP wie `b2in.eu`
- [ ] SSL-Zertifikat für die Subdomain (Let's Encrypt genügt)
- [ ] vhost mit `DocumentRoot /var/www/b2in-preview/public`
- [ ] Zweite MySQL-Datenbank plus Benutzer
- [ ] Genug Plattenplatz für eine Kopie des Live-Verzeichnisses (vor allem `storage/`)
---
## 2. Sicherung ziehen
Auch wenn wir live nichts anfassen – der Dump ist zugleich die Grundlage für die Vorschau-DB.
```bash
cd /var/www/b2in
mysqldump -u USER -p b2in_live > ~/b2in_live_$(date +%F_%H%M).sql
ls -lh ~/b2in_live_*.sql # Größe prüfen, darf nicht 0 sein
```
---
## 3. Vorschau-Verzeichnis anlegen
Kopie des Live-Standes, **inklusive `vendor/`**. Das erspart ein `composer install` auf dem
Server – für `livewire/flux-pro` bräuchte es sonst die Zugangsdaten aus `auth.json`.
```bash
cp -a /var/www/b2in /var/www/b2in-preview
cd /var/www/b2in-preview
# Laufzeit-Reste der Kopie entfernen
rm -rf bootstrap/cache/*.php
rm -f public/storage # Symlink wird in Schritt 7 neu gesetzt
```
**Zu `storage/app/public` (Medienbibliothek, Display-Videos):** Durch `cp -a` liegt eine echte
Kopie in der Vorschau – sauber getrennt, kostet aber den Speicherplatz. Wenn das zu groß ist:
```bash
rm -rf storage/app/public
ln -s /var/www/b2in/storage/app/public storage/app/public
```
⚠️ Mit dem Symlink landen **Uploads aus der Vorschau in den Live-Dateien**. Zum Anschauen in
Ordnung, zum Testen von Uploads nicht. Im Zweifel kopieren.
---
## 4. `.env` der Vorschau anpassen
Der wichtigste Schritt. Der `ThemeServiceProvider` sucht den aufgerufenen Host in
`config/domains.php` und setzt `URL::forceRootUrl()` samt Asset-Origin auf die dort hinterlegte
URL. Fehlt `DOMAIN_B2IN_URL`, greift der Vorgabewert **`https://b2in.test`** – eine Domain, die es
im Internet nicht gibt. Dann laden Bilder, Favicon und `livewire.min.js` nicht mehr.
```bash
cd /var/www/b2in-preview
nano .env
```
Diese Werte ändern:
```dotenv
APP_ENV=preview
APP_DEBUG=false
APP_URL=https://preview.b2in.eu
APP_DOMAIN_NAME=preview.b2in.eu
# Vorschau-Domain, damit Routing, Links und Assets stimmen
DOMAIN_B2IN=preview.b2in.eu
DOMAIN_B2IN_URL=https://preview.b2in.eu
# Assets ebenfalls auf die Vorschau-Domain, sonst laedt der Browser sie von woanders
ASSET_URL=https://preview.b2in.eu
# Eigene Datenbank
DB_DATABASE=b2in_preview
DB_USERNAME=…
DB_PASSWORD=…
# Eigener Cache-/Session-Namensraum, damit sich Live und Vorschau nicht ins Gehege kommen
CACHE_PREFIX=b2in_preview
SESSION_COOKIE=b2in_preview_session
# Keine Mails aus der Vorschau verschicken
MAIL_MAILER=log
```
`APP_ENV=preview` sorgt automatisch für `noindex, nofollow` in jeder Seite
(`config/app.php` → `app.indexable`). Wer ganz sichergehen will, setzt zusätzlich
`SITE_INDEXABLE=false`.
**Nicht ändern:** `APP_KEY` – sonst sind verschlüsselte Werte in der kopierten Datenbank
nicht mehr lesbar.
---
## 5. Datenbank der Vorschau befüllen
```bash
mysql -u USER -p -e "CREATE DATABASE b2in_preview CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mysql -u USER -p b2in_preview < ~/b2in_live_JJJJ-MM-TT_HHMM.sql
```
Gegenprobe, dass die Vorschau wirklich auf ihrer eigenen DB steht:
```bash
cd /var/www/b2in-preview
php artisan tinker --execute="echo config('database.connections.mysql.database');"
# muss b2in_preview ausgeben – wenn hier b2in_live steht: STOPP
```
---
## 6. Update einspielen
Das Archiv `dev/2026-08-28/deploy/b2in-interior-update.tar.gz` enthält genau die geänderten und
neuen Dateien plus die gebauten Assets (`public/build/b2in`, `public/build/portal`). **Node wird
auf dem Server nicht gebraucht.** Die enthaltene Dateiliste steht in `deploy/dateiliste.txt`.
```bash
# Archiv hochladen, dann:
cd /var/www/b2in-preview
tar xzf ~/b2in-interior-update.tar.gz
```
Das Archiv enthält bewusst **keine** `.env`, kein `vendor/` und nichts aus `storage/`.
---
## 7. Artisan-Schritte
```bash
cd /var/www/b2in-preview
php artisan config:clear
php artisan storage:link
php artisan migrate --force # legt cms_brands an, ergänzt summary/highlights
# Kernmarken anlegen – überschreibt nichts, was schon gepflegt ist
php artisan db:seed --class=CmsBrandSeeder --force
# Die neuen Texte ins CMS: genau die 23 umgetexteten Sektionen
php artisan db:seed --class=InteriorRelaunchCmsSeeder --force
# Magazin: die zwei neuen Beiträge plus Reihenfolge
php artisan db:seed --class=CmsArticleSeeder --force
# Markenseiten CABINET, Rotpunkt und Febrü – nur Textfelder, Bilder und Galerie bleiben
php artisan db:seed --class=BrandPageTextSeeder --force
# Entfernt den Vermerk „BEISPIEL – “ aus vorhandenen Bildunterschriften (Nachtrag 5).
# Nur nötig, wenn die Demogalerien schon eingespielt sind. Mehrfach ausführbar.
php artisan db:seed --class=BrandGalleryCaptionNormalizerSeeder --force
# Markenlogos nachtragen. Setzt `logo` nur, wenn das Feld leer ist.
php artisan db:seed --class=BrandLogoSeeder --force
# Display-Branding auf das Interior-Logo
php artisan db:seed --class=DisplayInteriorBrandingSeeder --force
# Medien-URLs host-unabhängig (sonst zeigen vier Playlists auf b2in.test)
php artisan db:seed --class=DisplayMediaUrlNormalizerSeeder --force
php artisan config:cache
php artisan route:cache
php artisan view:clear
```
> **Assets:** Das Archiv bringt `public/build/b2in` fertig mit. Wird auf dem Server selbst
> gebaut, gilt: `npm run build:web` baut **nur** die vier Frontend-Themes, nicht das Portal.
> Ohne `npm run build:portal` liefert das Admin-Portal danach 500.
**Optional, nur für die Abstimmung:** Beispielbilder in die Markengalerien, damit Galerie,
Referenzen-Seite und Lightbox nicht leer sind.
> ⚠️ Es sind Stockmotive und KI-Renderings, kein einziges echtes Projekt. Der frühere Vermerk
> „BEISPIEL" in der Bildunterschrift ist auf Kundenwunsch entfallen – die Bilder der Rubrik
> „Realisiertes Projekt" geben sich damit als echte Referenzen aus. Für die Abstimmung in
> Ordnung, **vor dem Livegang zwingend ersetzen**.
```bash
php artisan db:seed --class=DemoBrandGallerySeeder --force
```
Wieder entfernen:
```bash
php artisan tinker --execute="App\Models\CmsBrand::query()->update(['gallery' => null]);"
```
---
## 8. Vorschau abschirmen
**Suchmaschinen** – `noindex` kommt schon aus `APP_ENV=preview`, die `robots.txt` gehört
zusätzlich umgestellt:
```bash
printf 'User-agent: *\nDisallow: /\n' > /var/www/b2in-preview/public/robots.txt
```
**Passwortschutz** (empfohlen, damit die Vorschau nicht zufällig gefunden wird):
```bash
htpasswd -c /etc/apache2/.htpasswd-preview vorschau
```
Apache:
```apache
<Directory /var/www/b2in-preview/public>
AuthType Basic
AuthName "B2in Vorschau"
AuthUserFile /etc/apache2/.htpasswd-preview
Require valid-user
</Directory>
```
Nginx:
```nginx
auth_basic "B2in Vorschau";
auth_basic_user_file /etc/nginx/.htpasswd-preview;
```
---
## 9. Prüfen
| Seite | Erwartung |
|---|---|
| `/` | Hero → Marken-Teaser → Vision → Sponsoring → Showroom-Display → Immobilien → CTA |
| `/marken` | Grauer Seitenkopf mit Bild, drei gerahmte Markenblöcke |
| `/marken/cabinet` | Detailseite, Galerie mit den zwei Rubriken (falls Demo-Seeder lief) |
| `/marken/rotpunkt` | Neun Abschnitte, vier davon mit aufklappbarer Vertiefung; Button unten rechts heißt „Persönliche Küchenberatung vereinbaren" |
| `/marken/februe` | Elf Abschnitte, Zitat von Marcel; Button heißt „Persönliche Arbeitsplatzberatung vereinbaren" |
| `/referenzen` | Realisierte Projekte, darunter das Showroom-Display im Hochformat |
| `/magazin` | Die zwei Interior-Beiträge stehen oben |
| `/faq` | Rubriken „Für Ihr Zuhause" und „Für Projekte und Objekte" |
| `/about` | Werte: Beratung statt Verkauf, Maßarbeit, … |
| `/contact` | Betreffliste beginnt mit Beratungstermin |
| `/immobilien` | unverändert |
Und diese vier Punkte:
- [ ] Terminanfrage-Widget unten rechts öffnet sich, Absenden zeigt die Bestätigung
- [ ] Alle Links bleiben auf `preview.b2in.eu` – **springt einer auf `b2in.eu`, stimmt Schritt 4 nicht**
- [ ] Seitenquelltext enthält `<meta name="robots" content="noindex, nofollow">`
- [ ] Die Live-Seite `https://b2in.eu` sieht unverändert aus
---
## 10. Aufräumen
Vorschau wieder abbauen:
```bash
rm -rf /var/www/b2in-preview
mysql -u USER -p -e "DROP DATABASE b2in_preview;"
# vhost und DNS-Eintrag entfernen
```
Beim späteren echten Livegang gilt **Abschnitt 8 des Entwicklungsplans**, nicht dieses Dokument.
Vorher offen: Rechtstexte, OG-Bild, Seeder-Passwort rotieren.
---
## Wenn etwas nicht stimmt
| Symptom | Ursache |
|---|---|
| **„Kein Server mit dem angegebenen Hostnamen gefunden" für Bilder, Favicon, `livewire.min.js`** | `DOMAIN_B2IN_URL` fehlt oder ist falsch geschrieben → Vorgabewert `https://b2in.test`. Siehe Diagnose unten. |
| Links springen auf `b2in.eu` | `DOMAIN_B2IN` / `DOMAIN_B2IN_URL` in Schritt 4 nicht gesetzt, oder `config:cache` fehlt |
| Seite ohne Styles | `public/build/b2in` fehlt – Archiv nicht vollständig entpackt |
| „Vite manifest not found" | dasselbe, oder `build/portal` fehlt für das Admin-Panel |
| Bilder fehlen | `php artisan storage:link` vergessen, oder `storage/app/public` nicht mitkopiert |
| Display-Kachel bleibt leer | Kein aktives Display mit veröffentlichter Playlist – dann wird die Sektion bewusst gar nicht angezeigt |
| Änderungen wirken nicht | `php artisan config:clear && php artisan config:cache` |
### Diagnose: Assets zeigen auf den falschen Host
```bash
cd /var/www/b2in-preview
# 1. Was steht in der .env?
grep -E '^(APP_ENV|APP_URL|APP_DOMAIN_NAME|ASSET_URL|DOMAIN_B2IN|DOMAIN_B2IN_URL)=' .env
# 2. Was macht die Anwendung daraus? (zeigt den zwischengespeicherten Stand)
php artisan tinker --execute="echo config('domains.domains.b2in.domain_name').' -> '.config('domains.domains.b2in.url').PHP_EOL;"
# 3. Liegt ein alter Config-Cache herum?
ls -l bootstrap/cache/config.php 2>/dev/null
```
Steht in Zeile 2 etwas mit `.test`, fehlen die beiden `DOMAIN_B2IN*`-Zeilen. Beheben:
```bash
cd /var/www/b2in-preview
php artisan config:clear # zuerst den Cache weg, sonst wirkt die .env nicht
nano .env # DOMAIN_B2IN, DOMAIN_B2IN_URL, ASSET_URL, APP_URL setzen
php artisan config:cache
php artisan view:clear
```
**Sicherheitsnetz seit dieser Runde:** Findet der `ThemeServiceProvider` den aufgerufenen Host
nicht in der Konfiguration, verwendet er außerhalb von `APP_ENV=production` den tatsächlich
aufgerufenen Origin – die Vorschau verlinkt sich dann auf sich selbst statt auf eine fremde
Domain. In `production` bleibt es bei der konfigurierten URL, damit ein gefälschter Host-Header
keine Links auf fremde Domains erzeugen kann. Die `.env` sollte trotzdem stimmen.