@
Skripte nach bin/ und lib/ umstrukturiert + WSL-Log-Analyse Alle .bat-Skripte liegen jetzt in bin/, alle .py in lib/, .env(.example) in bin/. setenv.bat setzt IWZ_* (inkl. IWZ_ENV) und PYTHONPATH; die Launcher rufen setenv, aktivieren die .venv falls vorhanden und starten das zugehoerige lib/-Skript. Die Python-Skripte lesen die .env ueber IWZ_ENV (Fallback bin/.env). Neu: Nutzungsanalyse per GoAccess (analyze.py/.bat) - laedt die IONOS-Access-Logs per SFTP (paramiko) und erzeugt einen HTML-Report. GoAccess laeuft ueber WSL (GOACCESS_MODE=wsl, Pfad-Uebersetzung nach /mnt/c). install_py.bat prueft WSL+GoAccess und zeigt sonst den Installationsweg. Doku in doc/analyse.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
This commit is contained in:
@@ -1,16 +0,0 @@
|
|||||||
# Kopieren nach .env und mit echten Werten befuellen.
|
|
||||||
# .env ist in .gitignore eingetragen und wird NICHT eingecheckt.
|
|
||||||
|
|
||||||
# Storage Zone -> Access-Tab im Bunny.net Dashboard
|
|
||||||
BUNNY_STORAGE_ZONE=iwz-ferienwohnungen
|
|
||||||
BUNNY_STORAGE_HOST=storage.bunnycdn.com
|
|
||||||
BUNNY_STORAGE_PASSWORD=
|
|
||||||
|
|
||||||
# Pull Zone -> CDN -> Security-Tab im Bunny.net Dashboard (Token Authentication)
|
|
||||||
BUNNY_PULL_ZONE_URL=https://iwz-ferienwohnungen.b-cdn.net
|
|
||||||
BUNNY_AUTH_KEY=
|
|
||||||
|
|
||||||
# Account-API-Key fuer den CDN-Cache-Purge nach dem Deploy.
|
|
||||||
# Dashboard -> oben rechts auf den Namen -> Account -> API -> "API Key".
|
|
||||||
# ACHTUNG: nicht der BUNNY_AUTH_KEY oben, sondern der Account-weite API-Key!
|
|
||||||
BUNNY_API_KEY=
|
|
||||||
@@ -4,3 +4,10 @@ resources/
|
|||||||
.env
|
.env
|
||||||
.deploy_manifest.json
|
.deploy_manifest.json
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|
||||||
|
# WordPress-Export der alten Seite (nur temporär, enthält DB-Zugangsdaten im Klartext)
|
||||||
|
app816341166/
|
||||||
|
|
||||||
|
# Nutzungsanalyse (analyze.py): heruntergeladene Rohlogs + erzeugte Reports
|
||||||
|
logs/
|
||||||
|
results/
|
||||||
|
|||||||
@@ -14,14 +14,35 @@ Drei Ferienwohnungen in Kaufbeuren-Hirschzell / Allgäu.
|
|||||||
- **Buchungskalender**: webplanner.de (externer Dienst, per Link eingebunden)
|
- **Buchungskalender**: webplanner.de (externer Dienst, per Link eingebunden)
|
||||||
- **Git**: eigene Gitea-Instanz (kein GitHub/GitLab, kein CI/CD-Workflow)
|
- **Git**: eigene Gitea-Instanz (kein GitHub/GitLab, kein CI/CD-Workflow)
|
||||||
- **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)
|
- **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)
|
||||||
|
- **Nutzungsanalyse**: Server-Log-Auswertung mit GoAccess (kein JS-Tracking, keine Cookies, kein Consent-Banner). Skript `analyze.bat` / `analyze.py` lädt die Access-Logs per SFTP und erzeugt einen HTML-Report. Siehe `doc/analyse.md`.
|
||||||
|
|
||||||
|
## Hosting-Roadmap (Stand aktuell)
|
||||||
|
|
||||||
|
- **Jetzt**: Deploy läuft auf **Bunny.net** – für das erste Ausrollen der Instanz zur Kontrolle und Absprache. Die vorhandene Bunny-Konfiguration (Storage Zone, Pull Zone, `.env`) ist in Ordnung und bleibt vorerst so. `deploy.bat` / `deploy.py` deployen weiterhin nach Bunny.
|
||||||
|
- **Später**: Die Instanz wird auf **IONOS-Webspace** ausgerollt (Deploy-Ziel wird dann umgestellt).
|
||||||
|
- **Nutzungsanalyse jetzt schon erprobbar**: Die Log-Analyse (`analyze.py`) wird bereits mit den **IONOS-Access-Logs** getestet (SFTP-Zugang zum IONOS `/logs`-Ordner), unabhängig davon, dass das Deploy-Ziel noch Bunny ist. Nach dem IONOS-Umzug bleibt der Analyse-Workflow gleich.
|
||||||
|
|
||||||
## Projektstruktur
|
## Projektstruktur
|
||||||
|
|
||||||
```
|
```
|
||||||
iwz/
|
iwz/
|
||||||
├── hugo.toml # Konfiguration: baseURL, Menü, Kontaktdaten (Params)
|
├── hugo.toml # Konfiguration: baseURL, Menü, Kontaktdaten (Params)
|
||||||
├── deploy.bat # Windows: hugo build + wrangler deploy
|
├── requirements.txt # Python-Abhängigkeiten (paramiko für analyze)
|
||||||
├── deploy.py # Python-Alternative zum deploy.bat
|
├── bin/ # Alle .bat-Skripte (Einstiegspunkte)
|
||||||
|
│ ├── setenv.bat # setzt IWZ, IWZ_BIN, IWZ_LIB, IWZ_ENV ... + PYTHONPATH
|
||||||
|
│ ├── install_py.bat # legt .venv an + pip install -r requirements.txt
|
||||||
|
│ ├── get_cmd.bat # öffnet Shell mit gesetzter Umgebung
|
||||||
|
│ ├── deploy.bat # → lib/deploy.py (Vollupload nach Bunny)
|
||||||
|
│ ├── deploy-lastdiff.bat# → lib/deploy_lastdiff.py (nur Änderungen)
|
||||||
|
│ ├── analyze.bat # → lib/analyze.py (IONOS-Logs + GoAccess)
|
||||||
|
│ ├── provide_previewlink.bat # → lib/preview_link.py
|
||||||
|
│ ├── .env # Secrets (NICHT im Git; von setenv als IWZ_ENV gesetzt)
|
||||||
|
│ └── .env.example # Vorlage zum Kopieren nach bin/.env
|
||||||
|
├── lib/ # Alle .py-Skripte (von den .bat aufgerufen)
|
||||||
|
│ ├── deploy.py # Hugo-Build + FTP-Upload nach Bunny Storage Zone
|
||||||
|
│ ├── deploy_lastdiff.py # nur geänderte Dateien (Hash-Manifest)
|
||||||
|
│ ├── analyze.py # SFTP-Download der Logs + GoAccess-Report
|
||||||
|
│ └── preview_link.py # signierter Bunny-Vorschau-Link
|
||||||
├── content/
|
├── content/
|
||||||
│ ├── _index.md # Startseite
|
│ ├── _index.md # Startseite
|
||||||
│ ├── preise.md # Preistabelle + Buchungsbedingungen
|
│ ├── preise.md # Preistabelle + Buchungsbedingungen
|
||||||
@@ -94,8 +115,15 @@ hugo --gc --minify
|
|||||||
# danach: Upload von ./public zur Bunny.net Storage Zone
|
# danach: Upload von ./public zur Bunny.net Storage Zone
|
||||||
# (Storage Zone Name + API-Key/Storage-Passwort als Secrets/env, nicht im Git)
|
# (Storage Zone Name + API-Key/Storage-Passwort als Secrets/env, nicht im Git)
|
||||||
|
|
||||||
# Bei jeder Änderung:
|
# Bei jeder Änderung (Skripte liegen jetzt in bin/, Python in lib/):
|
||||||
deploy.bat
|
bin\deploy.bat # Vollupload
|
||||||
# oder:
|
# oder nur geänderte Dateien:
|
||||||
python deploy.py
|
bin\deploy-lastdiff.bat
|
||||||
|
# oder direkt:
|
||||||
|
python lib\deploy.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Die `.bat`-Skripte in `bin/` rufen `setenv.bat` auf (setzt `IWZ_*` und
|
||||||
|
`PYTHONPATH`), wechseln ins Projekt-Root und starten das zugehörige Skript in
|
||||||
|
`lib/`. Die Secrets liegen in `bin/.env` (von `setenv.bat` als `IWZ_ENV`
|
||||||
|
gesetzt; die Python-Skripte lesen diesen Pfad, sonst Fallback auf `bin/.env`).
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Kopieren nach .env und mit echten Werten befuellen.
|
||||||
|
# .env ist in .gitignore eingetragen und wird NICHT eingecheckt.
|
||||||
|
|
||||||
|
# Storage Zone -> Access-Tab im Bunny.net Dashboard
|
||||||
|
BUNNY_STORAGE_ZONE=iwz-ferienwohnungen
|
||||||
|
BUNNY_STORAGE_HOST=storage.bunnycdn.com
|
||||||
|
BUNNY_STORAGE_PASSWORD=
|
||||||
|
|
||||||
|
# Pull Zone -> CDN -> Security-Tab im Bunny.net Dashboard (Token Authentication)
|
||||||
|
BUNNY_PULL_ZONE_URL=https://iwz-ferienwohnungen.b-cdn.net
|
||||||
|
BUNNY_AUTH_KEY=
|
||||||
|
|
||||||
|
# Account-API-Key fuer den CDN-Cache-Purge nach dem Deploy.
|
||||||
|
# Dashboard -> oben rechts auf den Namen -> Account -> API -> "API Key".
|
||||||
|
# ACHTUNG: nicht der BUNNY_AUTH_KEY oben, sondern der Account-weite API-Key!
|
||||||
|
BUNNY_API_KEY=
|
||||||
|
|
||||||
|
# --- Nutzungsanalyse (analyze.py) ------------------------------------------
|
||||||
|
# SFTP-Zugang zu IONOS (gleiche Zugangsdaten wie im IONOS-Kundenbereich / SFTP).
|
||||||
|
IONOS_SFTP_HOST=
|
||||||
|
IONOS_SFTP_PORT=22
|
||||||
|
IONOS_SFTP_USER=
|
||||||
|
IONOS_SFTP_PASSWORD=
|
||||||
|
# Ordner mit den Access-Logs auf dem Server (im Screenshot: /logs).
|
||||||
|
IONOS_LOGS_DIR=/logs
|
||||||
|
|
||||||
|
# GoAccess-Aufruf:
|
||||||
|
# GOACCESS_MODE=wsl -> GoAccess laeuft in der WSL (empfohlen, Standard).
|
||||||
|
# Installation: bin\install_py.bat prueft WSL+GoAccess
|
||||||
|
# und zeigt sonst die Installationsbefehle.
|
||||||
|
# GOACCESS_MODE=exe -> nativer Windows-Build; dann GOACCESS_EXE auf die
|
||||||
|
# goaccess.exe setzen (Download: https://goaccess.io/download).
|
||||||
|
GOACCESS_MODE=wsl
|
||||||
|
GOACCESS_EXE=goaccess
|
||||||
|
|
||||||
|
# Apache-Logformat. IONOS nutzt i.d.R. das Combined Log Format.
|
||||||
|
# Falls der Report leer bleibt -> siehe doc/analyse.md (Format anpassen).
|
||||||
|
LOG_FORMAT=COMBINED
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
@echo off
|
||||||
|
REM ================================================================
|
||||||
|
REM SPS_SKEL - Python Virtual Environment aktivieren
|
||||||
|
REM ================================================================
|
||||||
|
|
||||||
|
call "%~dp0setenv.bat"
|
||||||
|
|
||||||
|
if not exist "%IWZ%\.venv" (
|
||||||
|
echo FEHLER: Virtual environment nicht gefunden.
|
||||||
|
echo Bitte zuerst bin\install_py.bat ausfuehren.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
call "%IWZ%\.venv\Scripts\activate.bat"
|
||||||
|
echo Virtuelle Umgebung aktiviert.
|
||||||
|
echo Python-Version:
|
||||||
|
python --version
|
||||||
|
echo.
|
||||||
|
echo Installierte Pakete:
|
||||||
|
pip list
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
@echo off
|
||||||
|
REM ================================================================
|
||||||
|
REM IWZ Ferienwohnungen - Nutzungsanalyse (IONOS-Logs + GoAccess)
|
||||||
|
REM ================================================================
|
||||||
|
REM Aufruf:
|
||||||
|
REM bin\analyze.bat Logs laden + Report bauen + oeffnen
|
||||||
|
REM bin\analyze.bat --no-download nur neu auswerten (schon geladene Logs)
|
||||||
|
REM ================================================================
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
call "%~dp0setenv.bat" >nul
|
||||||
|
|
||||||
|
REM Virtuelle Umgebung nutzen, falls vorhanden (paramiko liegt dort)
|
||||||
|
if exist "%IWZ%\.venv\Scripts\activate.bat" (
|
||||||
|
call "%IWZ%\.venv\Scripts\activate.bat"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PY=py"
|
||||||
|
py --version >nul 2>&1 || set "PY=python"
|
||||||
|
|
||||||
|
cd /d "%IWZ%"
|
||||||
|
"%PY%" "%IWZ_LIB%\analyze.py" %*
|
||||||
|
set "RC=%ERRORLEVEL%"
|
||||||
|
|
||||||
|
if not "%RC%"=="0" pause
|
||||||
|
endlocal & exit /b %RC%
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
@echo off
|
||||||
|
REM ================================================================
|
||||||
|
REM IWZ Ferienwohnungen - Deploy (nur geaenderte Dateien)
|
||||||
|
REM ================================================================
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
call "%~dp0setenv.bat" >nul
|
||||||
|
|
||||||
|
REM Virtuelle Umgebung nutzen, falls vorhanden (paramiko etc. liegen dort)
|
||||||
|
if exist "%IWZ%\.venv\Scripts\activate.bat" (
|
||||||
|
call "%IWZ%\.venv\Scripts\activate.bat"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PY=py"
|
||||||
|
py --version >nul 2>&1 || set "PY=python"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo === IWZ Ferienwohnungen - Deploy (nur Aenderungen) ===
|
||||||
|
echo.
|
||||||
|
|
||||||
|
cd /d "%IWZ%"
|
||||||
|
"%PY%" "%IWZ_LIB%\deploy_lastdiff.py" %*
|
||||||
|
set "RC=%ERRORLEVEL%"
|
||||||
|
|
||||||
|
if not "%RC%"=="0" pause
|
||||||
|
endlocal & exit /b %RC%
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
@echo off
|
||||||
|
REM ================================================================
|
||||||
|
REM IWZ Ferienwohnungen - Deploy (Vollupload nach Bunny.net)
|
||||||
|
REM ================================================================
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
REM Umgebung setzen (IWZ, IWZ_LIB, IWZ_ENV, PYTHONPATH ...)
|
||||||
|
call "%~dp0setenv.bat" >nul
|
||||||
|
|
||||||
|
REM Virtuelle Umgebung nutzen, falls vorhanden (paramiko etc. liegen dort)
|
||||||
|
if exist "%IWZ%\.venv\Scripts\activate.bat" (
|
||||||
|
call "%IWZ%\.venv\Scripts\activate.bat"
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Python-Interpreter bestimmen (py-Launcher bevorzugt, sonst python)
|
||||||
|
set "PY=py"
|
||||||
|
py --version >nul 2>&1 || set "PY=python"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo === IWZ Ferienwohnungen - Deploy ===
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Aus dem Projekt-Root heraus bauen (hugo + public liegen dort)
|
||||||
|
cd /d "%IWZ%"
|
||||||
|
"%PY%" "%IWZ_LIB%\deploy.py" %*
|
||||||
|
set "RC=%ERRORLEVEL%"
|
||||||
|
|
||||||
|
if not "%RC%"=="0" pause
|
||||||
|
endlocal & exit /b %RC%
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
@echo off
|
||||||
|
REM ================================================================
|
||||||
|
REM IWZ Ferienwohnungen - Shell mit gesetzten Umgebungsvariablen oeffnen
|
||||||
|
REM ================================================================
|
||||||
|
|
||||||
|
call "%~dp0setenv.bat"
|
||||||
|
start cmd /k "echo IWZ Umgebung aktiv. && echo IWZ=%IWZ%"
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
@echo off
|
||||||
|
REM ================================================================
|
||||||
|
REM IWZ - Python Virtual Environment einrichten
|
||||||
|
REM ================================================================
|
||||||
|
|
||||||
|
call "%~dp0setenv.bat"
|
||||||
|
|
||||||
|
if not exist "%IWZ%\.venv" (
|
||||||
|
echo Initialisiere Python virtual environment...
|
||||||
|
py -m venv "%IWZ%\.venv" --upgrade-deps
|
||||||
|
echo Erfolgreich.
|
||||||
|
|
||||||
|
call "%IWZ%\.venv\Scripts\activate.bat"
|
||||||
|
echo Installiere erforderliche Python Packages...
|
||||||
|
pip install -r "%IWZ%\requirements.txt" -q
|
||||||
|
echo Erfolgreich.
|
||||||
|
deactivate
|
||||||
|
) else (
|
||||||
|
echo Erforderliche Python Packages bereits installiert!
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ================================================================
|
||||||
|
echo Pruefe Nutzungsanalyse-Voraussetzungen (WSL + GoAccess)
|
||||||
|
echo ================================================================
|
||||||
|
|
||||||
|
REM 1) Ist WSL verfuegbar?
|
||||||
|
where wsl >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [FEHLT] WSL ist nicht installiert.
|
||||||
|
echo So einrichten ^(einmalig, PowerShell als Administrator^):
|
||||||
|
echo wsl --install
|
||||||
|
echo Danach Windows neu starten und die Ubuntu-Ersteinrichtung
|
||||||
|
echo ^(Benutzername/Passwort^) durchlaufen. Anschliessend GoAccess
|
||||||
|
echo wie unten installieren.
|
||||||
|
goto :wsl_done
|
||||||
|
)
|
||||||
|
echo [OK] WSL gefunden.
|
||||||
|
|
||||||
|
REM 2) Ist GoAccess in der WSL installiert?
|
||||||
|
wsl -e bash -lc "command -v goaccess >/dev/null 2>&1"
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [FEHLT] GoAccess ist in der WSL nicht installiert.
|
||||||
|
echo So installieren:
|
||||||
|
echo wsl -e sudo apt update
|
||||||
|
echo wsl -e sudo apt install -y goaccess
|
||||||
|
echo ^(fragt einmalig nach dem WSL-sudo-Passwort^)
|
||||||
|
) else (
|
||||||
|
for /f "delims=" %%V in ('wsl -e bash -lc "goaccess --version ^| head -1"') do set "GA_VER=%%V"
|
||||||
|
echo [OK] GoAccess in der WSL: %GA_VER%
|
||||||
|
)
|
||||||
|
|
||||||
|
:wsl_done
|
||||||
|
echo ================================================================
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
@echo off
|
||||||
|
REM ================================================================
|
||||||
|
REM Ferienwohnungen Wolframm - Vorschau-Link erzeugen
|
||||||
|
REM ================================================================
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
call "%~dp0setenv.bat" >nul
|
||||||
|
|
||||||
|
REM Virtuelle Umgebung nutzen, falls vorhanden
|
||||||
|
if exist "%IWZ%\.venv\Scripts\activate.bat" (
|
||||||
|
call "%IWZ%\.venv\Scripts\activate.bat"
|
||||||
|
)
|
||||||
|
|
||||||
|
set "PY=py"
|
||||||
|
py --version >nul 2>&1 || set "PY=python"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo === Ferienwohnungen Wolframm - Vorschau-Link erzeugen ===
|
||||||
|
|
||||||
|
cd /d "%IWZ%"
|
||||||
|
"%PY%" "%IWZ_LIB%\preview_link.py" %*
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo Link oben markieren und mit Strg+C kopieren.
|
||||||
|
echo.
|
||||||
|
endlocal
|
||||||
|
cmd /k
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
REM ================================================================
|
||||||
|
REM IWZ Ferienwohnungen - Umgebungsvariablen Setup
|
||||||
|
REM ================================================================
|
||||||
|
|
||||||
|
if defined IWZ_BIN (
|
||||||
|
echo IWZ Umgebung bereits gesetzt.
|
||||||
|
goto :show
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Setting up environment variables for IWZ ...
|
||||||
|
|
||||||
|
REM Basis-Projektpfad (Ordner ueber bin\)
|
||||||
|
set "IWZ=%~dp0.."
|
||||||
|
if "%IWZ:~-6%"=="bin\.." set "IWZ=%IWZ:~0,-6%"
|
||||||
|
if "%IWZ:~-1%"=="\" set "IWZ=%IWZ:~0,-1%"
|
||||||
|
|
||||||
|
REM Pfade fuer verschiedene Komponenten
|
||||||
|
set "IWZ_BIN=%IWZ%\bin"
|
||||||
|
set "IWZ_LIB=%IWZ%\lib"
|
||||||
|
set "IWZ_DOC=%IWZ%\doc"
|
||||||
|
set "IWZ_CONTENT=%IWZ%\content"
|
||||||
|
set "IWZ_LAYOUTS=%IWZ%\layouts"
|
||||||
|
set "IWZ_STATIC=%IWZ%\static"
|
||||||
|
set "IWZ_PUBLIC=%IWZ%\public"
|
||||||
|
set "IWZ_LOGS=%IWZ%\logs"
|
||||||
|
set "IWZ_RESULTS=%IWZ%\results"
|
||||||
|
|
||||||
|
REM Pfad zur .env (liegt jetzt in bin\). Die Python-Skripte lesen IWZ_ENV.
|
||||||
|
set "IWZ_ENV=%IWZ_BIN%\.env"
|
||||||
|
|
||||||
|
REM Python-Pfad erweitern (nur wenn noch nicht vorhanden)
|
||||||
|
echo %PYTHONPATH% | find /i "%IWZ_LIB%" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
set "PYTHONPATH=%IWZ_LIB%;%PYTHONPATH%"
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Ordner erstellen falls sie nicht existieren
|
||||||
|
if not exist "%IWZ_BIN%" mkdir "%IWZ_BIN%"
|
||||||
|
if not exist "%IWZ_LIB%" mkdir "%IWZ_LIB%"
|
||||||
|
if not exist "%IWZ_LOGS%" mkdir "%IWZ_LOGS%"
|
||||||
|
if not exist "%IWZ_RESULTS%" mkdir "%IWZ_RESULTS%"
|
||||||
|
|
||||||
|
REM Optional dauerhaft in der Registry setzen: bin\setenv.bat --persist
|
||||||
|
if /i "%1"=="--persist" (
|
||||||
|
echo Setze Umgebungsvariablen dauerhaft in der Registry ^(setx^)...
|
||||||
|
setx IWZ "%IWZ%" >nul
|
||||||
|
setx IWZ_BIN "%IWZ_BIN%" >nul
|
||||||
|
setx IWZ_LIB "%IWZ_LIB%" >nul
|
||||||
|
setx IWZ_ENV "%IWZ_ENV%" >nul
|
||||||
|
echo Dauerhaft gesetzt. Gilt fuer alle neuen Prozesse.
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Umgebungsvariablen anzeigen
|
||||||
|
:show
|
||||||
|
echo.
|
||||||
|
echo ================================================================
|
||||||
|
echo IWZ ENVIRONMENT SETUP COMPLETE
|
||||||
|
echo ================================================================
|
||||||
|
echo IWZ = %IWZ%
|
||||||
|
echo IWZ_BIN = %IWZ_BIN%
|
||||||
|
echo IWZ_LIB = %IWZ_LIB%
|
||||||
|
echo IWZ_DOC = %IWZ_DOC%
|
||||||
|
echo IWZ_CONTENT = %IWZ_CONTENT%
|
||||||
|
echo IWZ_LAYOUTS = %IWZ_LAYOUTS%
|
||||||
|
echo IWZ_STATIC = %IWZ_STATIC%
|
||||||
|
echo IWZ_PUBLIC = %IWZ_PUBLIC%
|
||||||
|
echo IWZ_LOGS = %IWZ_LOGS%
|
||||||
|
echo IWZ_RESULTS = %IWZ_RESULTS%
|
||||||
|
echo IWZ_ENV = %IWZ_ENV%
|
||||||
|
echo PYTHONPATH = %PYTHONPATH%
|
||||||
|
echo ================================================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM Optionally keep window open
|
||||||
|
if "%1"=="--keep-open" pause
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo.
|
|
||||||
echo === IWZ Ferienwohnungen - Deploy (nur Änderungen) ===
|
|
||||||
echo.
|
|
||||||
python deploy_lastdiff.py
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo.
|
|
||||||
echo === IWZ Ferienwohnungen - Deploy ===
|
|
||||||
echo.
|
|
||||||
python deploy.py
|
|
||||||
if %errorlevel% neq 0 (
|
|
||||||
pause
|
|
||||||
exit /b 1
|
|
||||||
)
|
|
||||||
pause
|
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
# Nutzungsanalyse der Website (Server-Logs + GoAccess)
|
||||||
|
|
||||||
|
Die neue Hugo-Seite verzichtet bewusst auf JavaScript-Tracking (kein Google
|
||||||
|
Analytics, keine Cookies, kein Consent-Banner nötig). Stattdessen werten wir
|
||||||
|
die **Apache-Access-Logs** aus, die IONOS ohnehin auf dem Server schreibt.
|
||||||
|
|
||||||
|
Ablauf des Skripts `analyze.py`:
|
||||||
|
|
||||||
|
1. Verbindet sich per **SFTP** mit IONOS und lädt alle `access.log*`-Dateien
|
||||||
|
aus dem `/logs`-Ordner nach `./logs/` herunter.
|
||||||
|
2. Entpackt die rotierten `.gz`-Logs und fügt alles zu einer Datei zusammen.
|
||||||
|
3. Lässt **GoAccess** darüberlaufen und erzeugt einen HTML-Report unter
|
||||||
|
`./results/report_JJJJ-MM-TT.html`, der automatisch im Browser geöffnet wird.
|
||||||
|
|
||||||
|
`logs/` und `results/` stehen in `.gitignore` und werden nicht eingecheckt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Einmalige Einrichtung
|
||||||
|
|
||||||
|
### 1. Python-Abhängigkeit (paramiko)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. GoAccess (über WSL – empfohlen)
|
||||||
|
|
||||||
|
GoAccess wird offiziell nur für Linux/macOS gebaut und ist in keinem
|
||||||
|
Windows-Paketmanager verfügbar. Unter Windows ist der zuverlässigste Weg die
|
||||||
|
**WSL** (Windows Subsystem for Linux). `bin\install_py.bat` prüft automatisch,
|
||||||
|
ob WSL und GoAccess vorhanden sind, und zeigt sonst die passenden Befehle.
|
||||||
|
|
||||||
|
**WSL installieren** (falls noch nicht vorhanden, PowerShell als Administrator):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
wsl --install
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach Windows neu starten und die Ubuntu-Ersteinrichtung (Benutzer/Passwort)
|
||||||
|
durchlaufen.
|
||||||
|
|
||||||
|
**GoAccess in der WSL installieren:**
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
wsl -e sudo apt update
|
||||||
|
wsl -e sudo apt install -y goaccess
|
||||||
|
```
|
||||||
|
|
||||||
|
In der `.env` steht dafür (Standard):
|
||||||
|
|
||||||
|
```text
|
||||||
|
GOACCESS_MODE=wsl
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Skript ruft GoAccess dann per `wsl goaccess ...` auf und übersetzt die
|
||||||
|
Windows-Pfade automatisch nach `/mnt/c/...`.
|
||||||
|
|
||||||
|
> **Alternative – nativer Windows-Build:** Falls du kein WSL nutzen willst,
|
||||||
|
> lade den Cygwin-Build von <https://goaccess.io/download>, entpacke ihn (z. B.
|
||||||
|
> nach `C:\tools\goaccess\`) und setze in der `.env`:
|
||||||
|
>
|
||||||
|
> ```text
|
||||||
|
> GOACCESS_MODE=exe
|
||||||
|
> GOACCESS_EXE=C:\tools\goaccess\goaccess.exe
|
||||||
|
> ```
|
||||||
|
|
||||||
|
### 3. IONOS-SFTP-Zugang in `.env` eintragen
|
||||||
|
|
||||||
|
Die Zugangsdaten stehen im IONOS-Kundenbereich unter **Hosting → SFTP/SSH**.
|
||||||
|
Es sind dieselben, mit denen der `/logs`-Ordner erreichbar ist.
|
||||||
|
|
||||||
|
```text
|
||||||
|
IONOS_SFTP_HOST=<z.B. home123456789.1and1-data.host oder access-5000000000.webspace-host.com>
|
||||||
|
IONOS_SFTP_PORT=22
|
||||||
|
IONOS_SFTP_USER=<SFTP-Benutzer>
|
||||||
|
IONOS_SFTP_PASSWORD=<SFTP-Passwort>
|
||||||
|
IONOS_LOGS_DIR=/logs
|
||||||
|
```
|
||||||
|
|
||||||
|
> Die `.env` steht in `.gitignore` und wird **nicht** eingecheckt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nutzung
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Logs neu laden, Report bauen und im Browser öffnen:
|
||||||
|
analyze.bat
|
||||||
|
# oder:
|
||||||
|
python analyze.py
|
||||||
|
|
||||||
|
# Nur neu auswerten (ohne erneuten Download der schon geladenen Logs):
|
||||||
|
python analyze.py --no-download
|
||||||
|
```
|
||||||
|
|
||||||
|
Beim wiederholten Lauf werden bereits geladene, rotierte Logs übersprungen
|
||||||
|
(gleiche Dateigröße) – nur das aktuell wachsende Log wird neu geholt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Was im Report steht
|
||||||
|
|
||||||
|
GoAccess zeigt u. a.:
|
||||||
|
|
||||||
|
- **Eindeutige Besucher** und Seitenaufrufe pro Tag
|
||||||
|
- **Meistbesuchte Seiten** (welche Ferienwohnung interessiert am meisten?)
|
||||||
|
- **Referrer** (woher kommen die Besucher – Google, direkte Eingabe, Links)
|
||||||
|
- **Länder / Sprachen**, Browser, Betriebssysteme, Geräte
|
||||||
|
- **HTTP-Statuscodes** (404-Fehler = tote Links finden)
|
||||||
|
- **Traffic-Volumen**
|
||||||
|
|
||||||
|
Aufrufe von Suchmaschinen-Bots werden herausgefiltert (`--ignore-crawlers`),
|
||||||
|
IP-Adressen im Report anonymisiert (`--anonymize-ip`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DSGVO-Hinweis
|
||||||
|
|
||||||
|
Diese Auswertung passiert **nur lokal auf deinem Rechner** aus den ohnehin
|
||||||
|
anfallenden Server-Logs. Es wird nichts an Dritte übertragen und kein
|
||||||
|
Tracking-Code beim Besucher ausgeführt. Server-Logs mit IP-Adressen dürfen zu
|
||||||
|
Sicherheits-/Betriebszwecken vorübergehend gespeichert werden; im GoAccess-
|
||||||
|
Report sind die IPs zusätzlich anonymisiert. Ein Cookie-Consent-Banner ist für
|
||||||
|
diese Art der Auswertung **nicht** erforderlich.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fehlerbehebung
|
||||||
|
|
||||||
|
**Report bleibt leer / „No data".**
|
||||||
|
Dann passt das Log-Format nicht. IONOS nutzt meist das *Combined Log Format*
|
||||||
|
(`LOG_FORMAT=COMBINED`). Wirf einen Blick in die erste Zeile von
|
||||||
|
`logs/access.log.current`:
|
||||||
|
|
||||||
|
- Beginnt sie mit einer IP und enthält am Ende `"...Referrer" "...User-Agent"`
|
||||||
|
→ `COMBINED` ist korrekt.
|
||||||
|
- Fehlen Referrer/User-Agent → `LOG_FORMAT=COMMON`.
|
||||||
|
- Steht der Vhost-Name vor der IP → `LOG_FORMAT=VCOMBINED`.
|
||||||
|
|
||||||
|
Alternativ ein eigenes Format als GoAccess-`--log-format`-String in `.env`
|
||||||
|
hinterlegen (siehe <https://goaccess.io/man#custom-log>).
|
||||||
|
|
||||||
|
**`paramiko`-Authentifizierungsfehler.**
|
||||||
|
SFTP-Host/-User/-Passwort im IONOS-Kundenbereich gegenprüfen. Der SFTP-Benutzer
|
||||||
|
ist nicht zwingend identisch mit dem Login des Kundenbereichs.
|
||||||
+226
@@ -0,0 +1,226 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
IWZ Ferienwohnungen - Nutzungsanalyse
|
||||||
|
|
||||||
|
Laedt die Apache-Access-Logs per SFTP von IONOS herunter, entpackt die
|
||||||
|
rotierten .gz-Dateien und erzeugt mit GoAccess einen HTML-Report.
|
||||||
|
|
||||||
|
Kein JavaScript-Tracking, keine Cookies beim Besucher -> DSGVO-freundlich.
|
||||||
|
Die Auswertung passiert rein aus den Server-Logs.
|
||||||
|
|
||||||
|
Voraussetzungen:
|
||||||
|
- pip install -r requirements.txt (paramiko)
|
||||||
|
- GoAccess Windows-Build (Pfad in .env: GOACCESS_EXE)
|
||||||
|
- .env mit IONOS_SFTP_* befuellt (siehe .env.example)
|
||||||
|
|
||||||
|
Aufruf:
|
||||||
|
python analyze.py # Logs laden + Report bauen + oeffnen
|
||||||
|
python analyze.py --no-download # nur neu auswerten (schon geladene Logs)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import gzip
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import stat
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import webbrowser
|
||||||
|
from datetime import date
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
try:
|
||||||
|
import paramiko
|
||||||
|
except ImportError:
|
||||||
|
print("FEHLER: paramiko fehlt. Bitte 'pip install -r requirements.txt' ausfuehren.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent # Skript liegt in lib/
|
||||||
|
LOGS_DIR = PROJECT_ROOT / "logs" # heruntergeladene Rohlogs (in .gitignore)
|
||||||
|
RESULTS_DIR = PROJECT_ROOT / "results" # erzeugte Reports (in .gitignore)
|
||||||
|
SFTP_PORT = 22
|
||||||
|
|
||||||
|
|
||||||
|
def env_file():
|
||||||
|
"""Pfad zur .env: bevorzugt IWZ_ENV (von setenv.bat), sonst bin/.env."""
|
||||||
|
from_var = os.environ.get("IWZ_ENV")
|
||||||
|
if from_var:
|
||||||
|
return Path(from_var)
|
||||||
|
return PROJECT_ROOT / "bin" / ".env"
|
||||||
|
|
||||||
|
# Nur diese Dateien aus dem IONOS /logs-Ordner interessieren uns.
|
||||||
|
# Alles andere (sftp.log, traffic.db, forensic/, info.*) wird ignoriert.
|
||||||
|
ACCESS_LOG_PREFIX = "access.log"
|
||||||
|
|
||||||
|
|
||||||
|
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 download_access_logs(host, port, user, password, remote_dir):
|
||||||
|
"""Laedt alle access.log*-Dateien aus dem IONOS /logs-Ordner nach LOGS_DIR."""
|
||||||
|
print(f"\n-> Verbinde per SFTP mit {host}:{port} ...")
|
||||||
|
transport = paramiko.Transport((host, port))
|
||||||
|
try:
|
||||||
|
transport.connect(username=user, password=password)
|
||||||
|
sftp = paramiko.SFTPClient.from_transport(transport)
|
||||||
|
sftp.chdir(remote_dir)
|
||||||
|
|
||||||
|
LOGS_DIR.mkdir(exist_ok=True)
|
||||||
|
count = 0
|
||||||
|
for attr in sftp.listdir_attr("."):
|
||||||
|
name = attr.filename
|
||||||
|
if not name.startswith(ACCESS_LOG_PREFIX):
|
||||||
|
continue
|
||||||
|
if stat.S_ISDIR(attr.st_mode):
|
||||||
|
continue
|
||||||
|
local_path = LOGS_DIR / name
|
||||||
|
# Schon vorhanden und gleich gross? -> ueberspringen (rotierte Logs
|
||||||
|
# aendern sich nicht mehr; nur .current / aktueller Tag waechst).
|
||||||
|
if (local_path.exists()
|
||||||
|
and local_path.stat().st_size == attr.st_size
|
||||||
|
and not name.endswith(".current")):
|
||||||
|
continue
|
||||||
|
print(f" lade {name} ({attr.st_size} Bytes)")
|
||||||
|
sftp.get(name, str(local_path))
|
||||||
|
count += 1
|
||||||
|
sftp.close()
|
||||||
|
print(f" {count} Datei(en) geladen/aktualisiert.")
|
||||||
|
finally:
|
||||||
|
transport.close()
|
||||||
|
|
||||||
|
|
||||||
|
def collect_log_lines():
|
||||||
|
"""Fuehrt alle access.log*-Dateien (auch .gz) zu einem kombinierten Log zusammen.
|
||||||
|
|
||||||
|
Gibt den Pfad zur kombinierten Klartext-Logdatei zurueck.
|
||||||
|
"""
|
||||||
|
combined = LOGS_DIR / "_combined.log"
|
||||||
|
sources = sorted(
|
||||||
|
p for p in LOGS_DIR.iterdir()
|
||||||
|
if p.is_file()
|
||||||
|
and p.name.startswith(ACCESS_LOG_PREFIX)
|
||||||
|
and p.name != combined.name
|
||||||
|
)
|
||||||
|
if not sources:
|
||||||
|
print("\nFEHLER: keine access.log*-Dateien in ./logs/ gefunden.")
|
||||||
|
print("Erst 'python analyze.py' (ohne --no-download) ausfuehren.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"\n-> Fuehre {len(sources)} Logdatei(en) zusammen ...")
|
||||||
|
with open(combined, "wb") as out:
|
||||||
|
for src in sources:
|
||||||
|
if src.suffix == ".gz":
|
||||||
|
with gzip.open(src, "rb") as f:
|
||||||
|
shutil.copyfileobj(f, out)
|
||||||
|
else:
|
||||||
|
with open(src, "rb") as f:
|
||||||
|
shutil.copyfileobj(f, out)
|
||||||
|
return combined
|
||||||
|
|
||||||
|
|
||||||
|
def to_wsl_path(win_path):
|
||||||
|
"""Uebersetzt einen Windows-Pfad (C:\\...) in einen WSL-Pfad (/mnt/c/...)."""
|
||||||
|
p = Path(win_path).resolve()
|
||||||
|
drive = p.drive.rstrip(":").lower() # "C:" -> "c"
|
||||||
|
rest = p.as_posix()[len(p.drive):] # Rest ohne Laufwerk, mit /
|
||||||
|
return f"/mnt/{drive}{rest}"
|
||||||
|
|
||||||
|
|
||||||
|
def goaccess_args(log_format):
|
||||||
|
"""Gemeinsame GoAccess-Optionen (ohne Ein-/Ausgabedateien)."""
|
||||||
|
return [
|
||||||
|
"--log-format=" + log_format,
|
||||||
|
"--anonymize-ip", # DSGVO: IPs im Report anonymisieren
|
||||||
|
"--ignore-crawlers", # Bots aus den Besucherzahlen filtern
|
||||||
|
"--html-report-title=IWZ Ferienwohnungen - Zugriffsstatistik",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def run_goaccess(mode, goaccess_exe, log_file, report_file, log_format):
|
||||||
|
"""Ruft GoAccess auf und erzeugt einen eigenstaendigen HTML-Report.
|
||||||
|
|
||||||
|
mode="wsl": GoAccess laeuft in der WSL (Standard), Pfade werden nach
|
||||||
|
/mnt/c/... uebersetzt.
|
||||||
|
mode="exe": nativer Windows-Build unter GOACCESS_EXE.
|
||||||
|
"""
|
||||||
|
print(f"\n-> GoAccess ({mode}) -> {report_file.name}")
|
||||||
|
|
||||||
|
if mode == "wsl":
|
||||||
|
cmd = [
|
||||||
|
"wsl", "goaccess",
|
||||||
|
to_wsl_path(log_file),
|
||||||
|
"-o", to_wsl_path(report_file),
|
||||||
|
] + goaccess_args(log_format)
|
||||||
|
not_found_hint = (
|
||||||
|
"GoAccess ist in der WSL nicht installiert (oder WSL fehlt).\n"
|
||||||
|
" In der WSL installieren: wsl -e sudo apt update ^&^& "
|
||||||
|
"sudo apt install -y goaccess\n"
|
||||||
|
"Siehe auch bin/install_py.bat (prueft WSL + GoAccess) und doc/analyse.md."
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
cmd = [
|
||||||
|
goaccess_exe,
|
||||||
|
str(log_file),
|
||||||
|
"-o", str(report_file),
|
||||||
|
] + goaccess_args(log_format)
|
||||||
|
not_found_hint = (
|
||||||
|
f"GoAccess-EXE nicht gefunden ('{goaccess_exe}').\n"
|
||||||
|
" Windows-Build laden: https://goaccess.io/download, entpacken,\n"
|
||||||
|
" dann GOACCESS_EXE in bin/.env auf die goaccess.exe setzen."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("\nFEHLER: GoAccess konnte nicht gestartet werden.")
|
||||||
|
print(not_found_hint)
|
||||||
|
sys.exit(1)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print("\nFEHLER: GoAccess fehlgeschlagen.")
|
||||||
|
print("Pruefe die GoAccess-Installation und das Log-Format (LOG_FORMAT).")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("=== IWZ Nutzungsanalyse ===")
|
||||||
|
no_download = "--no-download" in sys.argv
|
||||||
|
|
||||||
|
env = {**load_env(env_file()), **os.environ}
|
||||||
|
host = env.get("IONOS_SFTP_HOST")
|
||||||
|
port = int(env.get("IONOS_SFTP_PORT", SFTP_PORT))
|
||||||
|
user = env.get("IONOS_SFTP_USER")
|
||||||
|
password = env.get("IONOS_SFTP_PASSWORD")
|
||||||
|
remote_dir = env.get("IONOS_LOGS_DIR", "/logs")
|
||||||
|
# GoAccess-Aufruf: "wsl" (Standard) oder "exe" (nativer Windows-Build).
|
||||||
|
goaccess_mode = env.get("GOACCESS_MODE", "wsl").strip().lower()
|
||||||
|
goaccess_exe = env.get("GOACCESS_EXE", "goaccess")
|
||||||
|
# IONOS liefert i.d.R. Apache "Combined Log Format". Bei anderen
|
||||||
|
# Formaten hier anpassen (siehe doc/analyse.md).
|
||||||
|
log_format = env.get("LOG_FORMAT", "COMBINED")
|
||||||
|
|
||||||
|
if not no_download:
|
||||||
|
if not host or not user or not password:
|
||||||
|
print("\nFEHLER: IONOS_SFTP_HOST / _USER / _PASSWORD fehlen in bin/.env.")
|
||||||
|
print("Siehe bin/.env.example. Oder mit --no-download nur neu auswerten.")
|
||||||
|
sys.exit(1)
|
||||||
|
download_access_logs(host, port, user, password, remote_dir)
|
||||||
|
else:
|
||||||
|
print("\n-> Download uebersprungen (--no-download).")
|
||||||
|
|
||||||
|
combined = collect_log_lines()
|
||||||
|
|
||||||
|
RESULTS_DIR.mkdir(exist_ok=True)
|
||||||
|
report_file = RESULTS_DIR / f"report_{date.today().isoformat()}.html"
|
||||||
|
run_goaccess(goaccess_mode, goaccess_exe, combined, report_file, log_format)
|
||||||
|
|
||||||
|
print(f"\nFertig! Report: {report_file}")
|
||||||
|
webbrowser.open(report_file.as_uri())
|
||||||
@@ -13,11 +13,19 @@ import urllib.parse
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parent
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent # Skript liegt in lib/
|
||||||
PUBLIC_DIR = PROJECT_ROOT / "public"
|
PUBLIC_DIR = PROJECT_ROOT / "public"
|
||||||
FTP_PORT = 21
|
FTP_PORT = 21
|
||||||
|
|
||||||
|
|
||||||
|
def env_file():
|
||||||
|
"""Pfad zur .env: bevorzugt IWZ_ENV (von setenv.bat), sonst bin/.env."""
|
||||||
|
from_var = os.environ.get("IWZ_ENV")
|
||||||
|
if from_var:
|
||||||
|
return Path(from_var)
|
||||||
|
return PROJECT_ROOT / "bin" / ".env"
|
||||||
|
|
||||||
|
|
||||||
def load_env(path):
|
def load_env(path):
|
||||||
env = {}
|
env = {}
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
@@ -99,7 +107,7 @@ def purge_cdn_cache(pull_zone_url, api_key):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("=== IWZ Deploy ===")
|
print("=== IWZ Deploy ===")
|
||||||
|
|
||||||
env = {**load_env(PROJECT_ROOT / ".env"), **os.environ}
|
env = {**load_env(env_file()), **os.environ}
|
||||||
zone = env.get("BUNNY_STORAGE_ZONE")
|
zone = env.get("BUNNY_STORAGE_ZONE")
|
||||||
host = env.get("BUNNY_STORAGE_HOST", "storage.bunnycdn.com")
|
host = env.get("BUNNY_STORAGE_HOST", "storage.bunnycdn.com")
|
||||||
password = env.get("BUNNY_STORAGE_PASSWORD")
|
password = env.get("BUNNY_STORAGE_PASSWORD")
|
||||||
@@ -108,7 +116,7 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
if not zone or not password:
|
if not zone or not password:
|
||||||
print("\nFEHLER: BUNNY_STORAGE_ZONE / BUNNY_STORAGE_PASSWORD fehlen.")
|
print("\nFEHLER: BUNNY_STORAGE_ZONE / BUNNY_STORAGE_PASSWORD fehlen.")
|
||||||
print("Siehe .env.example -> .env kopieren und befuellen.")
|
print("Siehe bin/.env.example -> bin/.env kopieren und befuellen.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
hugo_build()
|
hugo_build()
|
||||||
@@ -14,12 +14,20 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parent
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent # Skript liegt in lib/
|
||||||
PUBLIC_DIR = PROJECT_ROOT / "public"
|
PUBLIC_DIR = PROJECT_ROOT / "public"
|
||||||
MANIFEST_PATH = PROJECT_ROOT / ".deploy_manifest.json"
|
MANIFEST_PATH = PROJECT_ROOT / ".deploy_manifest.json"
|
||||||
FTP_PORT = 21
|
FTP_PORT = 21
|
||||||
|
|
||||||
|
|
||||||
|
def env_file():
|
||||||
|
"""Pfad zur .env: bevorzugt IWZ_ENV (von setenv.bat), sonst bin/.env."""
|
||||||
|
from_var = os.environ.get("IWZ_ENV")
|
||||||
|
if from_var:
|
||||||
|
return Path(from_var)
|
||||||
|
return PROJECT_ROOT / "bin" / ".env"
|
||||||
|
|
||||||
|
|
||||||
def load_env(path):
|
def load_env(path):
|
||||||
env = {}
|
env = {}
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
@@ -124,14 +132,14 @@ def deploy_to_bunny(host, zone, password, changed, deleted):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("=== IWZ Deploy (nur Änderungen) ===")
|
print("=== IWZ Deploy (nur Änderungen) ===")
|
||||||
|
|
||||||
env = {**load_env(PROJECT_ROOT / ".env"), **os.environ}
|
env = {**load_env(env_file()), **os.environ}
|
||||||
zone = env.get("BUNNY_STORAGE_ZONE")
|
zone = env.get("BUNNY_STORAGE_ZONE")
|
||||||
host = env.get("BUNNY_STORAGE_HOST", "storage.bunnycdn.com")
|
host = env.get("BUNNY_STORAGE_HOST", "storage.bunnycdn.com")
|
||||||
password = env.get("BUNNY_STORAGE_PASSWORD")
|
password = env.get("BUNNY_STORAGE_PASSWORD")
|
||||||
|
|
||||||
if not zone or not password:
|
if not zone or not password:
|
||||||
print("\nFEHLER: BUNNY_STORAGE_ZONE / BUNNY_STORAGE_PASSWORD fehlen.")
|
print("\nFEHLER: BUNNY_STORAGE_ZONE / BUNNY_STORAGE_PASSWORD fehlen.")
|
||||||
print("Siehe .env.example -> .env kopieren und befuellen.")
|
print("Siehe bin/.env.example -> bin/.env kopieren und befuellen.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
hugo_build()
|
hugo_build()
|
||||||
@@ -6,15 +6,24 @@ fuer die per Bunny Token Authentication geschuetzte Pull Zone.
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
PROJECT_ROOT = Path(__file__).resolve().parent
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent # Skript liegt in lib/
|
||||||
GUELTIGKEIT_SEKUNDEN = 7 * 24 * 60 * 60 # 7 Tage
|
GUELTIGKEIT_SEKUNDEN = 7 * 24 * 60 * 60 # 7 Tage
|
||||||
URL_PATH = "/"
|
URL_PATH = "/"
|
||||||
|
|
||||||
|
|
||||||
|
def env_file():
|
||||||
|
"""Pfad zur .env: bevorzugt IWZ_ENV (von setenv.bat), sonst bin/.env."""
|
||||||
|
from_var = os.environ.get("IWZ_ENV")
|
||||||
|
if from_var:
|
||||||
|
return Path(from_var)
|
||||||
|
return PROJECT_ROOT / "bin" / ".env"
|
||||||
|
|
||||||
|
|
||||||
def load_env(path):
|
def load_env(path):
|
||||||
env = {}
|
env = {}
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
@@ -36,12 +45,12 @@ def build_signed_url(pull_zone_url, auth_key, url_path, expires):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
env = load_env(PROJECT_ROOT / ".env")
|
env = load_env(env_file())
|
||||||
pull_zone_url = env.get("BUNNY_PULL_ZONE_URL", "").rstrip("/")
|
pull_zone_url = env.get("BUNNY_PULL_ZONE_URL", "").rstrip("/")
|
||||||
auth_key = env.get("BUNNY_AUTH_KEY")
|
auth_key = env.get("BUNNY_AUTH_KEY")
|
||||||
|
|
||||||
if not pull_zone_url or not auth_key:
|
if not pull_zone_url or not auth_key:
|
||||||
print("\nFEHLER: BUNNY_PULL_ZONE_URL / BUNNY_AUTH_KEY fehlen in .env.")
|
print("\nFEHLER: BUNNY_PULL_ZONE_URL / BUNNY_AUTH_KEY fehlen in bin/.env.")
|
||||||
print("Siehe .env.example. Auth Key: Pull Zone -> Security -> Token Authentication.")
|
print("Siehe .env.example. Auth Key: Pull Zone -> Security -> Token Authentication.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
@echo off
|
|
||||||
echo.
|
|
||||||
echo === Ferienwohnungen Wolframm - Vorschau-Link erzeugen ===
|
|
||||||
python preview_link.py
|
|
||||||
echo.
|
|
||||||
echo Link oben markieren und mit Strg+C kopieren.
|
|
||||||
echo.
|
|
||||||
cmd /k
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Python-Abhaengigkeiten fuer die lib/-Skripte.
|
||||||
|
#
|
||||||
|
# deploy.py / deploy_lastdiff.py / preview_link.py:
|
||||||
|
# nutzen ausschliesslich die Standardbibliothek (ftplib, urllib, hashlib,
|
||||||
|
# json, subprocess ...) -> keine externen Pakete noetig.
|
||||||
|
#
|
||||||
|
# analyze.py:
|
||||||
|
# paramiko fuer den SFTP-Download der IONOS-Access-Logs.
|
||||||
|
paramiko
|
||||||
Reference in New Issue
Block a user