Files
tsv08kulmbach-website/config/config.example.php
fs f1ba364922 config.example: transiente SMTP-Auth-Fehler und Rückfallweg dokumentieren
Auf dem Server scheiterte die Anmeldung mit "Could not authenticate", lief
Minuten später ohne jede Änderung fehlerfrei und wurde parallel vom
Entwicklungsrechner durchgehend angenommen. Es war also vorübergehend, nicht
falsch konfiguriert — vermutlich der Brute-Force-Schutz des Mailservers.

Notiert, damit das nicht als Konfigurationsfehler behandelt wird, plus der
nachgewiesene Rückfallweg (localhost:25 ohne Anmeldung, der lokale Relay).
Bewusst nicht der Standard: mit localhost liefen Dev und Produktion auseinander.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 17:05:02 +02:00

177 lines
9.8 KiB
PHP
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.
<?php
/**
* Vorlage für config/config.php — kopieren und Werte eintragen:
* cp config/config.example.php config/config.php
*
* config.php ist gitignored und liegt außerhalb des Webroots (zusätzlich per .htaccess gesperrt).
*
* ---------------------------------------------------------------------------
* TAKT DER SYNC-SKRIPTE — zwei Wege, je nach Tarif
* ---------------------------------------------------------------------------
* A) Der Tarif hat Cronjobs → Shell-Cronjobs unten verwenden. Sauberster Weg,
* kein Endpoint, kein Dritter im Spiel.
* B) Der Tarif hat KEINE Cronjobs (Stand 27.07.2026 bei uns der Fall, KAS meldet
* „in deinem Tarif nicht verfügbar“) → public/cron.php + externer Cron-Dienst.
* Dann `cron.key` unten setzen und beim Dienst diese URLs im gewünschten Takt
* aufrufen lassen (Schlüssel besser als Header X-Cron-Key als in der URL):
*
* https://www.tsv08kulmbach.de/cron.php?job=matchcenter
* https://www.tsv08kulmbach.de/cron.php?job=instagram
*
* Takt wie unten bei den Cronjobs. Der Dienst muss keine Antwort auswerten:
* 204 = angenommen oder wegen Mindestabstand übersprungen, 403 = Schlüssel oder
* Job falsch (dann ist der Dienst falsch eingerichtet und es syncte nichts).
* Erwarteter Fehlerfall: Instagram sperrt Rechenzentrums-IPs. Einmal von Hand
* prüfen, ob der Scraper auf dem Server überhaupt durchkommt.
*
* ---------------------------------------------------------------------------
* CRONJOBS (im KAS anlegen, Typ „eigener Cronjob“ / Shell) — nur Weg A
* ---------------------------------------------------------------------------
* SITE=/www/htdocs/<KAS-User>/tsv08kulmbach (echten Pfad einsetzen: pwd auf dem Server)
* PHP=/usr/bin/php (All-Inkl ggf. /usr/local/bin/php8.2 — `which php8.2` prüft es)
*
* # Instagram-Feed: 2×/Tag (versetzte Minuten, damit die Läufe sich nicht überlappen)
* 17 6,18 * * * cd $SITE && $PHP bin/instagram-sync.php >> storage/logs/instagram.log 2>&1
*
* # Matchcenter: 2×/Tag als Grundtakt
* 37 7,19 * * * cd $SITE && $PHP bin/matchcenter-sync.php >> storage/logs/matchcenter.log 2>&1
*
* # Matchcenter am Spieltag-Nachmittag/Abend ½-stündlich (Sa+So 1321 Uhr),
* # damit Ergebnisse und Tabelle zeitnah stehen.
* 7,37 13-21 * * 6,0 cd $SITE && $PHP bin/matchcenter-sync.php >> storage/logs/matchcenter.log 2>&1
*
* # Logpflege: sonntags nachts. Rotiert Dateien über 2 MB und löscht Einträge älter als 90 Tage.
* 23 3 * * 0 cd $SITE && $PHP bin/log-rotate.php > /dev/null 2>&1
*
* Alle Skripte sind CLI-only; die Syncs sperren sich per Lock gegen Parallelläufe und lassen
* bei jedem Fehler den letzten guten Cache stehen — ein fehlgeschlagener Lauf schadet nie.
* Erstlauf nach dem Deploy einmal von Hand anstoßen (füllt Cache + Bilder/Wappen).
*
* ---------------------------------------------------------------------------
* NACH DEM DEPLOY
* ---------------------------------------------------------------------------
* php bin/preflight.php Config, Secrets, Schreibrechte, vendor/, PHP-Version prüfen
* php bin/smtp-test.php SMTP-Login testen (ohne Mailversand)
* danach je eine Testmail über /kontakt, /mitglied-werden und /sportheimbuchung
*
* IM BETRIEB
* php bin/logs.php Läuft alles? Fehler der letzten 7 Tage + Stand aller Kanäle
*
* Für die Dateiablage /dateien zusätzlich:
* storage/dateien/uploads und storage/dateien/system müssen beschreibbar sein
* php bin/filesgallery-update.php --check (Patch + self-hosted Assets prüfen)
* php bin/dateien-init.php Ordnerstruktur Jahr → Abteilung anlegen
*/
return [
// Kanonische Basis-URL ohne trailing slash (für Canonical, OG, Sitemap).
'base_url' => 'https://www.tsv08kulmbach.de',
// 'production' oder 'development' (steuert Fehleranzeige).
'env' => 'production',
// Geheimer Schlüssel für die HMAC-Time-Trap der Formulare und den IP-Hash im Spam-Log.
// Generieren mit: php -r "echo bin2hex(random_bytes(32));"
// Für Produktion einen EIGENEN Wert erzeugen — nie den aus der lokalen Dev-Config
// übernehmen. Solange hier CHANGE_ME steht, weisen die Formular-Actions jeden
// Versand ab (config_problems()), damit die Time-Trap nie mit einem bekannten
// Schlüssel läuft.
'app_secret' => 'CHANGE_ME',
// All-Inkl SMTP (Postfach im KAS anlegen; Host = <KAS-Login>.kasserver.com).
// Verbindung testen ohne Mailversand: php bin/smtp-test.php
//
// „SMTP Error: Could not authenticate" auf dem Server, obwohl dieselben Daten vom
// Entwicklungsrechner angenommen werden? Am 31.07.2026 war das VORÜBERGEHEND:
// derselbe Weg lief Minuten später fehlerfrei, ohne Änderung an der Config
// (vermutlich der Brute-Force-Schutz des Mailservers). Erst `php bin/smtp-test.php --probe`
// laufen lassen, bevor irgendetwas hier geändert wird — und nicht in Serie
// wiederholen, jeder Fehlversuch ist ein fehlgeschlagener Login.
//
// Bekannter RÜCKFALLWEG auf diesem Webspace, falls die Anmeldung öfter kippt:
// host 'localhost', port 25, ohne Verschlüsselung und OHNE Anmeldung — der lokale
// Relay des Hosters nimmt uns an (am 31.07.2026 nachgewiesen). Dann kann eine
// Anmeldung gar nicht mehr fehlschlagen. Bewusst NICHT der Standard: mit
// 'localhost' liefen Dev und Produktion auseinander (lokal gibt es keinen
// Mailserver), und ein Codepfad ist leichter zu pflegen als zwei.
'smtp' => [
'host' => 'w01ca75d.kasserver.com',
'port' => 587, // STARTTLS
'username' => 'noreply@tsv08kulmbach.de', // volle Postfach-Adresse
'password' => 'POSTFACH_PASSWORT',
// Absender: das authentifizierte Postfach (gleiche Domain, sonst lehnt All-Inkl ab).
'from' => 'noreply@tsv08kulmbach.de',
'from_name' => 'TSV 08 Kulmbach',
// Empfänger der Formular-Mails.
'to' => 'info@tsv08kulmbach.de',
],
// Cron-Endpoint public/cron.php — nur nötig, wenn der Tarif keine Cronjobs hat
// (Weg B oben). Leer oder 'CHANGE_ME' lassen heißt: der Endpoint ist zu und
// antwortet auf jeden Aufruf mit 403.
'cron' => [
// Langes Zufallsgeheimnis, NICHT app_secret wiederverwenden — der Schlüssel
// reist bei einem externen Dienst mit und darf dort nichts anderes aufschließen.
// Erzeugen: php -r 'echo bin2hex(random_bytes(24)), "\n";'
'key' => 'CHANGE_ME',
// Mindestabstand zwischen zwei Läufen desselben Jobs in Sekunden. Schützt bei
// geleaktem Link davor, dass BFV oder Instagram unsere Server-IP sperren.
// Muss kleiner sein als der engste gewünschte Takt (½-stündlich = 1800 s).
'min_interval' => 240,
],
// Instagram-Scraper (bin/instagram-sync.php).
'instagram' => [
'username' => 'tsv08kulmbach',
'max_posts' => 9,
],
// Zentrale News-Seite (app/pages/news.php) — mischt data/news.json,
// Instagram-Cache und Matchcenter-Ergebnisse; hier nur die Obergrenze für
// die Gesamtliste (News allein könnte sonst unbegrenzt wachsen).
'news' => [
'max_items' => 20,
],
// Dateiablage /dateien (Files Gallery, vendored unter public/dateien/) — der Verein
// lädt dort Fotos und Videos hoch, eingearbeitet werden sie danach von Hand.
// Konfiguriert wird die App in public/dateien/_filesconfig.php; hier stehen nur die
// Geheimnisse. Passwort-Hash erzeugen mit:
// php -r "echo password_hash('DAS-PASSWORT', PASSWORD_DEFAULT), PHP_EOL;"
// Klartext-Passwörter würden auch funktionieren (index.php:507) — aber nicht in einer Datei.
// Solange hier CHANGE_ME steht, ist der Zugang gesperrt (kein Passwort = kein Login).
'dateien' => [
'license_key' => 'CHANGE_ME', // Vollversion-Schlüssel von files.gallery
'users' => [
// Gemeinsames Konto zum Hochladen — Passwort im Verein weitergeben.
'verein' => ['password_hash' => 'CHANGE_ME'],
// Verwaltung (Aufräumen, Sortieren, Löschen).
'admin' => ['password_hash' => 'CHANGE_ME'],
],
],
// Matchcenter-Scraper (bin/matchcenter-sync.php) — Spielplan/Ergebnisse/Tabelle
// aus der öffentlichen BFV-Widget-JSON-API. Die IDs sind keine Geheimnisse
// (sie steckten schon im alten BFV-Widget-Embed) und dürfen hier stehen.
'matchcenter' => [
// Referer für die BFV-Requests (gegen HTTP 418).
'referer' => 'https://widget-prod.bfv.de/',
'max_upcoming' => 5, // anstehende Spiele pro Team + im globalen Slider
'max_results' => 5, // letzte Ergebnisse pro Team
// key = Schlüssel in data/teams.json (für kuratierten Namen/Liga/Slug),
// team_id = BFV permanentId (saison-stabil). compound_id = Wettbewerbs-ID
// der Tabelle — nur Fallback: der Scraper leitet die aktuelle compoundId
// automatisch aus der Matches-API ab, sodass eine neue Saison von selbst
// übernommen wird. compound_id nur pflegen, falls die Auto-Erkennung mal fehlt.
'teams' => [
['key' => 'erste-mannschaft', 'team_id' => '016PKHIPIO000000VV0AG811VUDIC8D7', 'compound_id' => '02TBKEVREG000006VS5489BTVVQ0O654-G'],
// Zweite = Spielgemeinschaft „(SG1) TSV Melkendorf III/ TSV 08 Kulmbach II".
// team_id ist die Saison-26/27-ID (reaktiviert 29.07.2026 — die alte ID
// 016PCRVRJC… lieferte ab 26/27 nur noch den TSV Melkendorf ohne uns; bei
// SG-Umbauten zur neuen Saison die ID von der BFV-Mannschaftsseite prüfen).
['key' => 'zweite-mannschaft', 'team_id' => '030RNM1V6K000000VS5489BRVVPSQ1LR'],
['key' => 'damen', 'team_id' => '01HLQ7QVES000000VV0AG80NVVKQLCU9', 'compound_id' => '02TK5AGFE0000004VS5489BUVVPGA9RV-G'],
],
],
];