Seite auf bunny laufen lassen. Konzept umgestellt
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
public/
|
public/
|
||||||
resources/
|
resources/
|
||||||
.hugo_build.lock
|
.hugo_build.lock
|
||||||
|
.env
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ Drei Ferienwohnungen in Kaufbeuren-Hirschzell / Allgäu.
|
|||||||
## Stack
|
## Stack
|
||||||
|
|
||||||
- **Static Site Generator**: Hugo (keine externe Abhängigkeit, einzelne .exe)
|
- **Static Site Generator**: Hugo (keine externe Abhängigkeit, einzelne .exe)
|
||||||
- **Hosting**: Cloudflare Pages (kostenlos, automatisches HTTPS)
|
- **Hosting**: Bunny.net (Bunny CDN / Storage Zone, kostenpflichtig nach Traffic)
|
||||||
- **DNS**: Cloudflare (Nameserver-Wechsel von IONOS zu Cloudflare)
|
- **DNS**: Bunny.net DNS oder weiterhin IONOS (siehe Umzug-Notizen, ggf. anzupassen)
|
||||||
- **Domain-Registrar**: IONOS (bleibt so, nur DNS wandert zu Cloudflare)
|
- **Domain-Registrar**: IONOS (bleibt so)
|
||||||
- **Buchungskalender**: webplanner.de (externer Dienst, per Link eingebunden)
|
- **Buchungskalender**: webplanner.de (externer Dienst, per Link eingebunden)
|
||||||
- **Git**: eigene Gitea-Instanz (kein GitHub/GitLab)
|
- **Git**: eigene Gitea-Instanz (kein GitHub/GitLab, kein CI/CD-Workflow)
|
||||||
- **Deploy**: manuell per `deploy.bat` oder `deploy.py` → `wrangler pages deploy`
|
- **Deploy**: manuell per `deploy.bat` oder `deploy.py` → `hugo --gc --minify` + Upload nach Bunny.net Storage Zone (per Bunny-API/FTP, analog zum Ansatz in `.github/workflows/deploy.yml`-Vorlage, aber lokal statt als GitHub Action)
|
||||||
|
|
||||||
## Projektstruktur
|
## Projektstruktur
|
||||||
|
|
||||||
@@ -72,8 +72,11 @@ bilder:
|
|||||||
- [x] Telefon, E-Mail, Adresse in `hugo.toml` unter `[params]` eintragen
|
- [x] Telefon, E-Mail, Adresse in `hugo.toml` unter `[params]` eintragen
|
||||||
- [x] `content/impressum.md` mit echten Pflichtangaben füllen
|
- [x] `content/impressum.md` mit echten Pflichtangaben füllen
|
||||||
- [x] `content/datenschutz.md` durch rechtskonforme Version ersetzen
|
- [x] `content/datenschutz.md` durch rechtskonforme Version ersetzen
|
||||||
- [ ] Cloudflare-Account anlegen + Wrangler einrichten (`wrangler login`)
|
- [x] Bunny.net-Account anlegen + Storage Zone für die Seite erstellen
|
||||||
- [ ] Nameserver bei IONOS auf Cloudflare umstellen
|
- [x] Bunny Storage-Passwort besorgen, sicher hinterlegen (`.env`, nicht ins Git)
|
||||||
|
- [x] `deploy.bat` / `deploy.py` um Upload zur Bunny Storage Zone erweitern (FTP, Port 21, passiv)
|
||||||
|
- [ ] Bunny Pull Zone / CDN vor die Storage Zone schalten (Auslieferung + HTTPS)
|
||||||
|
- [ ] Domain-Anbindung an Bunny klären (DNS bei IONOS auf Bunny zeigen lassen, oder DNS zu Bunny umziehen)
|
||||||
|
|
||||||
## Lokaler Dev-Server
|
## Lokaler Dev-Server
|
||||||
|
|
||||||
@@ -84,9 +87,12 @@ hugo server
|
|||||||
|
|
||||||
## Deploy
|
## Deploy
|
||||||
|
|
||||||
|
Deploy läuft lokal per Skript (kein CI/CD, da Repo bei Gitea liegt). Referenz für den Bunny-Upload-Schritt ist die Vorlage `.github/workflows/deploy.yml` (GitHub-Actions-Syntax), deren Build- und Deploy-Logik 1:1 in `deploy.bat`/`deploy.py` nachgebaut wird:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# Einmalig:
|
hugo --gc --minify
|
||||||
wrangler login
|
# danach: Upload von ./public zur Bunny.net Storage Zone
|
||||||
|
# (Storage Zone Name + API-Key/Storage-Passwort als Secrets/env, nicht im Git)
|
||||||
|
|
||||||
# Bei jeder Änderung:
|
# Bei jeder Änderung:
|
||||||
deploy.bat
|
deploy.bat
|
||||||
|
|||||||
@@ -1,16 +1,166 @@
|
|||||||
# SSL/HTTPS auf Cloudflare Pages
|
# Bunny.net – Ersteinrichtung und Vorab-Veröffentlichung
|
||||||
|
|
||||||
|
Ziel: `iwz-ferienwohnungen` bei Bunny.net anlegen, eine erste Fassung der Hugo-Seite
|
||||||
|
online stellen und den Zugriff darauf auf dich + den Kunden beschränken (Vorschau,
|
||||||
|
bevor die Seite öffentlich unter der echten Domain läuft).
|
||||||
|
|
||||||
|
> Hinweis: Dieser Weg ersetzt den früher geplanten Cloudflare-Pages-Ansatz.
|
||||||
|
> Details zum alten Cloudflare/IONOS-Plan siehe weiter unten im Abschnitt
|
||||||
|
> "Alt: Cloudflare-Variante (nicht mehr aktuell)".
|
||||||
|
|
||||||
|
## 1. Bunny.net-Account anlegen
|
||||||
|
|
||||||
|
1. Auf [bunny.net](https://bunny.net) registrieren (E-Mail + Passwort, keine
|
||||||
|
Kreditkarte für den Start nötig, Abrechnung erfolgt nach Traffic/Storage).
|
||||||
|
2. E-Mail-Adresse bestätigen und einloggen ins Dashboard.
|
||||||
|
|
||||||
|
## 2. Storage Zone anlegen (Ablage für die gebauten Hugo-Dateien)
|
||||||
|
|
||||||
|
1. Im Dashboard-Menü **Storage** → **Add Storage Zone**.
|
||||||
|
2. Name vergeben, z. B. `iwz-ferienwohnungen`.
|
||||||
|
3. **Main Region**: Region wählen, die am nächsten an euch/den Besuchern liegt
|
||||||
|
(z. B. Frankfurt/Falkenstein, Europa).
|
||||||
|
4. Replikations-Regionen optional dazuwählen, falls später Besucher aus anderen
|
||||||
|
Kontinenten erwartet werden (für die Vorschau-Phase nicht nötig).
|
||||||
|
5. Zone erstellen.
|
||||||
|
|
||||||
|
## 3. Zugangsdaten der Storage Zone notieren
|
||||||
|
|
||||||
|
1. Storage Zone öffnen → Reiter **FTP & API Access**.
|
||||||
|
2. **Hostname**, **Username** und **Password** (nicht das Read-only-Passwort!)
|
||||||
|
notieren – diese werden später für den Upload per `deploy.bat`/`deploy.py`
|
||||||
|
gebraucht (als `BUNNY_STORAGE_ZONE` und `BUNNY_API_KEY`, siehe CLAUDE.md).
|
||||||
|
3. Zugangsdaten **nicht** ins Git-Repo einchecken (z. B. in `.env` oder als
|
||||||
|
Windows-Umgebungsvariable ablegen).
|
||||||
|
|
||||||
|
## 4. Erste Fassung hochladen
|
||||||
|
|
||||||
|
Lokal die Seite bauen und die `public/`-Inhalte in die Storage Zone hochladen:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
hugo --gc --minify
|
||||||
|
```
|
||||||
|
|
||||||
|
Für den allerersten Test kann der Upload manuell per **File Manager** im Bunny-
|
||||||
|
Dashboard (Storage Zone → Files → Drag & Drop von `public/`) erfolgen. Sobald
|
||||||
|
`deploy.bat`/`deploy.py` entsprechend erweitert ist, übernimmt das Skript diesen
|
||||||
|
Schritt automatisch (Ordnerinhalt von `public/` 1:1 in die Storage Zone spiegeln).
|
||||||
|
|
||||||
|
## 5. Pull Zone anlegen (macht die Storage Zone über HTTPS erreichbar)
|
||||||
|
|
||||||
|
1. In der Storage Zone → **Connected Pull Zones** → **Connect Pull Zone**
|
||||||
|
(oder im Menü **CDN → Add Pull Zone** und als Origin die Storage Zone wählen).
|
||||||
|
2. Namen vergeben, z. B. `iwz-ferienwohnungen`.
|
||||||
|
3. Zonen für die Auslieferung wählen (Europa reicht für die Vorschau).
|
||||||
|
4. Pull Zone erstellen → Bunny vergibt automatisch eine Test-URL:
|
||||||
|
`https://iwz-ferienwohnungen.b-cdn.net`
|
||||||
|
5. HTTPS ist auf dieser `b-cdn.net`-URL automatisch aktiv, kein eigenes
|
||||||
|
Zertifikat nötig.
|
||||||
|
|
||||||
|
Damit ist die Seite technisch live – als nächstes wird sie geschützt, bevor der
|
||||||
|
Link an den Kunden geht.
|
||||||
|
|
||||||
|
## 6. Zugriff beschränken: nur du + der Kunde
|
||||||
|
|
||||||
|
Bunny.net Pull Zones bieten dafür **Token Authentication**: Requests an die
|
||||||
|
Pull Zone werden nur ausgeliefert, wenn die URL einen gültigen, signierten Token
|
||||||
|
enthält. Es gibt keinen klassischen Basic-Auth-Login-Dialog im Browser –
|
||||||
|
stattdessen bekommt der Kunde einen fertigen Link mit Token, der bis zu einem
|
||||||
|
festgelegten Ablaufzeitpunkt gültig ist.
|
||||||
|
|
||||||
|
### 6.1 Token Authentication aktivieren
|
||||||
|
|
||||||
|
1. Pull Zone öffnen → Reiter **Security**.
|
||||||
|
2. **Token Authentication** aktivieren.
|
||||||
|
3. Den angezeigten **Authentication Key** kopieren und sicher aufbewahren
|
||||||
|
(nicht ins Git-Repo, nicht per unverschlüsselter Mail verschicken) – wer
|
||||||
|
diesen Key kennt, kann selbst gültige Links erzeugen.
|
||||||
|
|
||||||
|
Ab jetzt liefert die Pull Zone **nichts mehr aus**, ohne gültigen Token in der
|
||||||
|
URL – das ist der eigentliche Zugriffsschutz.
|
||||||
|
|
||||||
|
### 6.2 Signierten Link erzeugen
|
||||||
|
|
||||||
|
Ein signierter Link besteht aus einem Hash über `Authentication Key + Pfad +
|
||||||
|
Ablaufzeitpunkt`. Das lässt sich einfach per kleinem Python-Snippet erzeugen,
|
||||||
|
z. B. lokal ausführen, wenn ein neuer Vorschau-Link für den Kunden gebraucht
|
||||||
|
wird:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import hashlib
|
||||||
|
import base64
|
||||||
|
import time
|
||||||
|
|
||||||
|
security_key = "DEIN_AUTHENTICATION_KEY"
|
||||||
|
url_path = "/index.html" # z.B. Startseite, oder "/" je nach Setup
|
||||||
|
expires = int(time.time()) + 7 * 24 * 60 * 60 # 7 Tage gültig
|
||||||
|
|
||||||
|
hashable = f"{security_key}{url_path}{expires}"
|
||||||
|
token_hash = hashlib.sha256(hashable.encode()).digest()
|
||||||
|
token = base64.urlsafe_b64encode(token_hash).decode().rstrip("=")
|
||||||
|
|
||||||
|
print(f"https://iwz-ferienwohnungen.b-cdn.net{url_path}?token={token}&expires={expires}")
|
||||||
|
```
|
||||||
|
|
||||||
|
Den ausgegebenen Link schickst du dem Kunden. Läuft die Gültigkeit ab, einfach
|
||||||
|
ein neues Snippet mit neuem `expires`-Wert ausführen und den frischen Link
|
||||||
|
verschicken.
|
||||||
|
|
||||||
|
### 6.3 Praktisch für Vorschau-Zwecke
|
||||||
|
|
||||||
|
- Länge der Gültigkeit (`expires`) nach Bedarf wählen – z. B. 7 Tage für eine
|
||||||
|
kurze Abstimmungsrunde, oder wenige Stunden für einen einzelnen Termin.
|
||||||
|
- Ohne gültigen Token bekommt jeder andere Besucher einen Fehler von Bunny –
|
||||||
|
die Seite ist damit faktisch nur über den geteilten Link erreichbar.
|
||||||
|
- Sobald die Seite live gehen soll (echte Domain, öffentlich), wird Token
|
||||||
|
Authentication in der Pull Zone einfach wieder deaktiviert.
|
||||||
|
|
||||||
|
## 7. Domain-Anbindung (später, für den echten Umzug)
|
||||||
|
|
||||||
|
Noch offen und erst relevant, wenn die Seite final ist:
|
||||||
|
|
||||||
|
- Eigene Domain (`iwz-ferienwohnungen.de`) als Custom Hostname in der Pull Zone
|
||||||
|
hinterlegen.
|
||||||
|
- Entweder DNS bei IONOS per CNAME auf die Bunny-Pull-Zone zeigen lassen, oder
|
||||||
|
DNS komplett zu Bunny.net DNS umziehen (Nameserver-Wechsel bei IONOS,
|
||||||
|
ähnlich wie ursprünglich für Cloudflare geplant).
|
||||||
|
- SSL-Zertifikat für die eigene Domain wird von Bunny automatisch ausgestellt,
|
||||||
|
sobald der Hostname verifiziert ist.
|
||||||
|
- Domain-Registrar bleibt in jedem Fall IONOS.
|
||||||
|
|
||||||
|
## Zusammenfassung des Stacks (aktuell)
|
||||||
|
|
||||||
|
```
|
||||||
|
Domain-Registrar: IONOS (bleibt so)
|
||||||
|
DNS: noch offen (IONOS bleibt vorerst / später ggf. Bunny.net DNS)
|
||||||
|
Hosting: Bunny.net (Storage Zone + Pull Zone/CDN)
|
||||||
|
Vorschau-Schutz: Bunny Token Authentication (signierte, befristete Links)
|
||||||
|
Code/Content: Gitea (eigene Instanz, kein CI/CD-Workflow)
|
||||||
|
Deploy: manuell per deploy.bat / deploy.py (hugo build + Upload zur Storage Zone)
|
||||||
|
Buchungskalender: webplanner.de (bleibt so, extern)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Alt: Cloudflare-Variante (nicht mehr aktuell)
|
||||||
|
|
||||||
|
Der folgende Abschnitt beschreibt den ursprünglich geplanten Umzug auf
|
||||||
|
Cloudflare Pages und wird nicht mehr weiterverfolgt (Entscheidung: Umstieg auf
|
||||||
|
Bunny.net, siehe oben). Als Referenz stehen gelassen.
|
||||||
|
|
||||||
|
## SSL/HTTPS auf Cloudflare Pages
|
||||||
|
|
||||||
Ja, vollautomatisch. Es gibt keine Tokens oder Zertifikate, die du selbst beantragen oder verlängern musst. Das läuft komplett im Hintergrund über Let's Encrypt, gesteuert von Cloudflare. Du siehst davon nichts.
|
Ja, vollautomatisch. Es gibt keine Tokens oder Zertifikate, die du selbst beantragen oder verlängern musst. Das läuft komplett im Hintergrund über Let's Encrypt, gesteuert von Cloudflare. Du siehst davon nichts.
|
||||||
|
|
||||||
# DNS und IP-Adresse — das ist der entscheidende Teil
|
## DNS und IP-Adresse — das ist der entscheidende Teil
|
||||||
|
|
||||||
Hier gibt es zwei verschiedene Szenarien:
|
Hier gibt es zwei verschiedene Szenarien:
|
||||||
|
|
||||||
## Szenario A: Domain bei einem anderen Registrar (z.B. IONOS, Strato, Hetzner)
|
### Szenario A: Domain bei einem anderen Registrar (z.B. IONOS, Strato, Hetzner)
|
||||||
|
|
||||||
Du hast zwei Möglichkeiten:
|
Du hast zwei Möglichkeiten:
|
||||||
|
|
||||||
### Option 1 — Nameserver umziehen (empfohlen):
|
#### Option 1 — Nameserver umziehen (empfohlen):
|
||||||
Beim Registrar trägst du statt dessen Nameserver die Cloudflare-Nameserver ein:
|
Beim Registrar trägst du statt dessen Nameserver die Cloudflare-Nameserver ein:
|
||||||
|
|
||||||
aria.ns.cloudflare.com
|
aria.ns.cloudflare.com
|
||||||
@@ -20,10 +170,10 @@ Danach verwaltest du alle DNS-Einträge bei Cloudflare. Cloudflare kennt dann de
|
|||||||
|
|
||||||
www → dein-projekt.pages.dev
|
www → dein-projekt.pages.dev
|
||||||
|
|
||||||
### Option 2 — Nur einen CNAME/A-Record setzen (ohne Nameserver-Wechsel):
|
#### Option 2 — Nur einen CNAME/A-Record setzen (ohne Nameserver-Wechsel):
|
||||||
Beim Registrar trägst du einen CNAME auf dein-projekt.pages.dev ein. Cloudflare Pages erkennt die Domain und stellt trotzdem ein Zertifikat aus — dauert aber etwas länger zur Verifizierung.
|
Beim Registrar trägst du einen CNAME auf dein-projekt.pages.dev ein. Cloudflare Pages erkennt die Domain und stellt trotzdem ein Zertifikat aus — dauert aber etwas länger zur Verifizierung.
|
||||||
|
|
||||||
## Szenario B: Domain direkt bei Cloudflare registriert
|
### Szenario B: Domain direkt bei Cloudflare registriert
|
||||||
|
|
||||||
Dann ist alles von Anfang an in einem Dashboard, kein Umzug nötig.
|
Dann ist alles von Anfang an in einem Dashboard, kein Umzug nötig.
|
||||||
|
|
||||||
@@ -45,8 +195,6 @@ Danach läuft alles von selbst — das Zertifikat verlängert sich alle 90 Tage
|
|||||||
|
|
||||||
# Wo ist die Domain iwz-ferienwohnungen.de aktuell registriert?
|
# Wo ist die Domain iwz-ferienwohnungen.de aktuell registriert?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Ablauf IONOS
|
## Ablauf IONOS
|
||||||
|
|
||||||
Nameserver-Wechsel bei IONOS zu Cloudflare
|
Nameserver-Wechsel bei IONOS zu Cloudflare
|
||||||
@@ -76,7 +224,7 @@ IONOS bleibt weiterhin dein Domain-Registrar — das ist wichtig zu verstehen. D
|
|||||||
|
|
||||||
Falls über IONOS auch E-Mail-Adressen laufen (z.B. info@iwz-ferienwohnungen.de), dann müssen die MX-Einträge beim Import in Cloudflare korrekt übernommen werden. Cloudflare importiert die normalerweise automatisch, aber das solltest du einmal kontrollieren bevor du den Nameserver-Wechsel machst. Die E-Mail funktioniert dann wie bisher — Cloudflare leitet den Mail-Verkehr nur weiter, hostet ihn nicht selbst.
|
Falls über IONOS auch E-Mail-Adressen laufen (z.B. info@iwz-ferienwohnungen.de), dann müssen die MX-Einträge beim Import in Cloudflare korrekt übernommen werden. Cloudflare importiert die normalerweise automatisch, aber das solltest du einmal kontrollieren bevor du den Nameserver-Wechsel machst. Die E-Mail funktioniert dann wie bisher — Cloudflare leitet den Mail-Verkehr nur weiter, hostet ihn nicht selbst.
|
||||||
|
|
||||||
# Zusammenfassung des gesamten Stacks
|
# Zusammenfassung des gesamten Stacks (alt, Cloudflare)
|
||||||
Domain-Registrar: IONOS (bleibt so)
|
Domain-Registrar: IONOS (bleibt so)
|
||||||
DNS + SSL: Cloudflare (kostenlos)
|
DNS + SSL: Cloudflare (kostenlos)
|
||||||
Hosting: Cloudflare Pages (kostenlos)
|
Hosting: Cloudflare Pages (kostenlos)
|
||||||
|
|||||||
+2
-14
@@ -1,22 +1,10 @@
|
|||||||
@echo off
|
@echo off
|
||||||
echo.
|
echo.
|
||||||
echo === IWZ Ferienwohnungen – Deploy ===
|
echo === IWZ Ferienwohnungen - Deploy ===
|
||||||
echo.
|
echo.
|
||||||
echo Hugo Build...
|
python deploy.py
|
||||||
hugo
|
|
||||||
if %errorlevel% neq 0 (
|
if %errorlevel% neq 0 (
|
||||||
echo FEHLER: Hugo Build fehlgeschlagen!
|
|
||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
echo.
|
|
||||||
echo Deploy zu Cloudflare Pages...
|
|
||||||
wrangler pages deploy public/ --project-name iwz-ferienwohnungen
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
echo FEHLER: Deploy fehlgeschlagen!
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
echo Fertig! Seite ist live.
|
|
||||||
pause
|
pause
|
||||||
|
|||||||
@@ -1,25 +1,79 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
IWZ Ferienwohnungen – Deploy Script
|
IWZ Ferienwohnungen - Deploy Script
|
||||||
Baut die Hugo-Seite und deployt sie zu Cloudflare Pages.
|
Baut die Hugo-Seite und deployt sie per FTP zur Bunny.net Storage Zone.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import ftplib
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
PROJECT = "iwz-ferienwohnungen"
|
PROJECT_ROOT = Path(__file__).resolve().parent
|
||||||
|
PUBLIC_DIR = PROJECT_ROOT / "public"
|
||||||
|
FTP_PORT = 21
|
||||||
|
|
||||||
|
|
||||||
def run(cmd, desc):
|
def load_env(path):
|
||||||
print(f"\n→ {desc}...")
|
env = {}
|
||||||
result = subprocess.run(cmd, shell=True)
|
if not path.exists():
|
||||||
|
return env
|
||||||
|
for line in path.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#") or "=" not in line:
|
||||||
|
continue
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
env[key.strip()] = value.strip()
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def hugo_build():
|
||||||
|
print("\n-> Hugo Build...")
|
||||||
|
result = subprocess.run("hugo --gc --minify", shell=True)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
print(f"\nFEHLER bei: {desc}")
|
print("\nFEHLER: Hugo Build fehlgeschlagen!")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def ftp_upload_dir(ftp, local_dir, remote_dir=""):
|
||||||
|
for entry in sorted(local_dir.iterdir()):
|
||||||
|
remote_path = f"{remote_dir}/{entry.name}" if remote_dir else entry.name
|
||||||
|
if entry.is_dir():
|
||||||
|
try:
|
||||||
|
ftp.mkd(remote_path)
|
||||||
|
except ftplib.error_perm:
|
||||||
|
pass # existiert bereits
|
||||||
|
ftp_upload_dir(ftp, entry, remote_path)
|
||||||
|
else:
|
||||||
|
with open(entry, "rb") as f:
|
||||||
|
ftp.storbinary(f"STOR {remote_path}", f)
|
||||||
|
print(f" {remote_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def deploy_to_bunny(host, zone, password):
|
||||||
|
print(f"\n-> Deploy zu Bunny.net Storage Zone '{zone}'...")
|
||||||
|
with ftplib.FTP() as ftp:
|
||||||
|
ftp.connect(host, FTP_PORT)
|
||||||
|
ftp.login(zone, password)
|
||||||
|
ftp.set_pasv(True)
|
||||||
|
ftp_upload_dir(ftp, PUBLIC_DIR)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("=== IWZ Deploy ===")
|
print("=== IWZ Deploy ===")
|
||||||
run("hugo", "Hugo Build")
|
|
||||||
run(f'wrangler pages deploy public/ --project-name {PROJECT}', "Cloudflare Pages Deploy")
|
env = {**load_env(PROJECT_ROOT / ".env"), **os.environ}
|
||||||
print("\n✓ Fertig! Seite ist live.")
|
zone = env.get("BUNNY_STORAGE_ZONE")
|
||||||
|
host = env.get("BUNNY_STORAGE_HOST", "storage.bunnycdn.com")
|
||||||
|
password = env.get("BUNNY_STORAGE_PASSWORD")
|
||||||
|
|
||||||
|
if not zone or not password:
|
||||||
|
print("\nFEHLER: BUNNY_STORAGE_ZONE / BUNNY_STORAGE_PASSWORD fehlen.")
|
||||||
|
print("Siehe .env.example -> .env kopieren und befuellen.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
hugo_build()
|
||||||
|
deploy_to_bunny(host, zone, password)
|
||||||
|
|
||||||
|
print("\nFertig! Seite ist live.")
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
IWZ Ferienwohnungen - erzeugt einen befristeten, signierten Vorschau-Link
|
||||||
|
fuer die per Bunny Token Authentication geschuetzte Pull Zone.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parent
|
||||||
|
GUELTIGKEIT_SEKUNDEN = 7 * 24 * 60 * 60 # 7 Tage
|
||||||
|
URL_PATH = "/"
|
||||||
|
|
||||||
|
|
||||||
|
def load_env(path):
|
||||||
|
env = {}
|
||||||
|
if not path.exists():
|
||||||
|
return env
|
||||||
|
for line in path.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#") or "=" not in line:
|
||||||
|
continue
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
env[key.strip()] = value.strip()
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def build_signed_url(pull_zone_url, auth_key, url_path, expires):
|
||||||
|
hashable = f"{auth_key}{url_path}{expires}"
|
||||||
|
token_hash = hashlib.sha256(hashable.encode()).digest()
|
||||||
|
token = base64.urlsafe_b64encode(token_hash).decode().rstrip("=")
|
||||||
|
return f"{pull_zone_url}{url_path}?token={token}&expires={expires}"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
env = load_env(PROJECT_ROOT / ".env")
|
||||||
|
pull_zone_url = env.get("BUNNY_PULL_ZONE_URL", "").rstrip("/")
|
||||||
|
auth_key = env.get("BUNNY_AUTH_KEY")
|
||||||
|
|
||||||
|
if not pull_zone_url or not auth_key:
|
||||||
|
print("\nFEHLER: BUNNY_PULL_ZONE_URL / BUNNY_AUTH_KEY fehlen in .env.")
|
||||||
|
print("Siehe .env.example. Auth Key: Pull Zone -> Security -> Token Authentication.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
expires = int(time.time()) + GUELTIGKEIT_SEKUNDEN
|
||||||
|
link = build_signed_url(pull_zone_url, auth_key, URL_PATH, expires)
|
||||||
|
|
||||||
|
gueltig_bis = time.strftime("%d.%m.%Y %H:%M", time.localtime(expires))
|
||||||
|
print("\n=== Vorschau-Link (Bunny Token Authentication) ===\n")
|
||||||
|
print(link)
|
||||||
|
print(f"\nGueltig bis: {gueltig_bis}\n")
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
@echo off
|
||||||
|
echo.
|
||||||
|
echo === IWZ Ferienwohnungen - Vorschau-Link erzeugen ===
|
||||||
|
python preview_link.py
|
||||||
|
echo.
|
||||||
|
echo Link oben markieren und mit Strg+C kopieren.
|
||||||
|
echo.
|
||||||
|
cmd /k
|
||||||
Reference in New Issue
Block a user