Annehmen war bisher nur ein Etikett: Man stellte den Status um, und es
passierte nichts. Kunde, Projekt, Aufgabenliste, Termin und Zugang musste
man danach von Hand in fünf Schritten nachbauen. Der alte Code gab das im
Kommentar selbst zu -- schlug der zweite von zwei Aufrufen fehl, stand der
Kunde schon in der Datenbank und man durfte es nicht noch einmal
versuchen.
Jetzt macht das EIN Aufruf, ganz oder gar nicht:
Kunde finden oder anlegen, Projekt anlegen, die komplette Aufgabenliste
des Pakets einsetzen, Liefertermin berechnen, Einladungslink erzeugen.
Der Kunde verfolgt ab diesem Moment alles in seinem Portal.
Die Zeit läuft wirklich:
- Eigenes Datumsfeld (termin_am) neben dem freien Text. Aus 'Mitte
Oktober' kann man keine verbleibenden Tage rechnen.
- Werktage statt Kalendertage, inklusive luxemburgischer Feiertage. Die
beweglichen werden über die Osterformel berechnet statt gepflegt --
eine Liste ist im übernächsten Jahr lautlos falsch.
- Vorschlag je Paket (Onepager 10, Website 20, Shop 30 Werktage),
überschreibbar vor dem Bestätigen.
- Verbleibende Zeit wird SERVERSEITIG gerechnet. Der Browser kennt die
Feiertage nicht; zwei verschiedene Zahlen für denselben Termin wären
schlimmer als gar keine.
Ablehnen mit Grund und vorbereiteter Absage in fünf Sprachen, Text
serverseitig erzeugt und vor dem Abschicken lesbar. Ein laufendes
Projekt lässt sich nicht nachträglich als Anfrage ablehnen.
Sechs Fehler dabei gefunden:
- Ein unbekanntes Paket hätte GAR KEINEN Termin bekommen statt des
Ersatzwerts. Zwei Funktionen lasen dieselbe Tabelle, nur eine hatte
einen Rückfallwert. Eine fehlende Zahl fällt nirgends auf.
- wd_anfragen hat keine Spalte 'firma' -- better-sqlite3 weist undefined
ab, die ganze Annahme wäre gescheitert.
- Migrationen stehen in einer ausdrücklichen Liste; 0015 fehlte darin.
- datumKurz() wurde aufgerufen, gab es aber nicht. Der Fehler wäre erst
NACH dem Anlegen aufgetreten.
- Der Installations-Hinweis lag fest über dem Annehmen-Knopf und machte
ihn auf dem Handy untreffbar. Die Seite reserviert jetzt Platz dafür.
- 'richttermin' ist freier Text, lief aber durch einen Datumsformatierer:
Der Kunde las 'Invalid Date' an der wichtigsten Stelle seines Projekts.
Geprüft: 49 Prüfungen der Terminrechnung gegen nachschlagbare Osterdaten
und Wochentage, 70 gegen eine echte Datenbank (darunter: zweiter Klick
legt nichts doppelt an, Abbruch mittendrin lässt NICHTS zurück, gesperrter
Kunde wird nicht still entsperrt), 42 im Browser auf Computer und Handy,
40 im Portal. Alle bestehenden Prüfungen weiter grün.
Co-Authored-By: Claude Opus 5 <[email protected]>
Widerrufsbelehrung und Datenschutz
RECHERCHIERT, NICHT AUS DEM GEDAECHTNIS
Der Webdesign-Bereich hatte KEINE eigene Rechtsseite -- obwohl dort
Vertraege geschlossen und Zahlungen ausgeloest werden. Die Fusszeile
verwies auf die Kontaktseite des Universe, die ein Content-Projekt
beschreibt, kein Dienstleistungsgeschaeft.
Die Web-Recherche hat eine Pflicht zutage gefoerdert, die seit zwei
Monaten gilt und die die Seite nicht erfuellt hat:
§ 356a BGB -- elektronische Widerrufsfunktion, in Kraft seit
19.06.2026. Sie gilt fuer ALLE Fernabsatzvertraege, die ueber eine
Online-Benutzeroberflaeche geschlossen werden, nicht nur fuer
Finanzdienstleistungen.
WARUM DAS HIER GILT: Im Kundenportal nimmt der Kunde Zusatzangebote per
Klick VERBINDLICH an ("Damit nimmst du das Zusatzangebot verbindlich
an"). Das ist ein Vertragsschluss ueber eine Online-Benutzeroberflaeche.
Fuer die geplanten PayPal-Zahlungen gilt dasselbe.
Der Anbieter sitzt in Luxemburg. Fuer Verbraucher mit gewoehnlichem
Aufenthalt in Deutschland gilt nach Art. 6 Rom-I-VO trotzdem das
deutsche zwingende Verbraucherrecht, weil die Seite sich erkennbar an den
deutschsprachigen Markt richtet. Deshalb wurde der STRENGERE Standard
umgesetzt, nicht der bequemere.
WAS GEBAUT WURDE
webdesign/widerruf.html -- die Widerrufsfunktion, genau nach dem
Wortlaut der Vorschrift:
* Beschriftung "Vertrag widerrufen" (gesetzlich vorgegeben)
* ZWEITE Schaltflaeche "Widerruf bestaetigen" (ebenfalls vorgegeben --
ein einstufiges Formular wuerde die Vorschrift nicht erfuellen)
* unverzuegliche Eingangsbestaetigung mit Nummer, ZEITPUNKT (nicht nur
Datum), Empfaenger und dem Wortlaut der Erklaerung
* Link in der Fusszeile JEDER Seite -- "staendig verfuegbar,
hervorgehoben platziert, leicht zugaenglich" heisst nicht "in den AGB
versteckt"
Drei Entscheidungen, die unmittelbar aus dem Sinn der Vorschrift folgen:
KEINE ANMELDUNG. Die Seite ist von der Zugangswand ausgenommen. Wer
seinen Code verlegt hat, wuerde sonst seine Frist verlieren --
Fristverlust durch eine selbstgebaute Huerde ist der schlimmste
denkbare Fall.
KEINE MENGENBEGRENZUNG. Ueberall sonst richtig, hier ein
Rechtsverlust: Wer in der letzten Stunde seiner Frist wegen eines
hakenden Netzes dreimal klickt, darf nicht abgewiesen werden.
KEIN ZWISCHENSPEICHER. Beim Anfrageformular ein Segen, hier eine
Falle: Auf einem geteilten Rechner laege der halb ausgefuellte Widerruf
des einen im Browser des naechsten.
webdesign/rechtliches.html -- Impressum, AGB, Widerrufsbelehrung samt
Muster-Widerrufsformular, Datenschutzerklaerung. Alle Betreiberangaben
sind aus den bestehenden Seiten UEBERNOMMEN, nichts erfunden: Anschrift
in Mondercange, Kleinunternehmerregelung nach Art. 57 TVA-Gesetz LU und
Richtlinie (EU) 2020/285, netcup/Cloudflare, PayPal Luxemburg.
Migration 0014: wd_widerrufe (der dauerhafte Datentraeger und der
Nachweis, wird nie geleert) und wd_zustimmungen. Letztere speichert nicht
nur den Haken, sondern den WORTLAUT, den der Kunde gesehen hat -- die
Beweislast fuer die Zustimmung zum vorzeitigen Leistungsbeginn liegt beim
Unternehmer (§ 356 Abs. 4 BGB), und im Streit zaehlt, WAS bestaetigt
wurde. Bewusst OHNE Fremdschluessel: CASCADE wuerde den Nachweis mit dem
Kunden mitloeschen, RESTRICT wuerde eine DSGVO-Loeschung blockieren.
Druckansicht: Die Eingangsbestaetigung ist ein Rechtsnachweis. Auf Papier
schwarz auf weiss, ohne Navigation und Knoepfe -- ein Nachweis, den
niemand ausdruckt, weil er eine halbe Patrone kostet, erfuellt seinen
Zweck nicht.
ZWEI ECHTE FEHLER GEFUNDEN
1. Die Pflicht-Sternchen verschwanden nach der Uebersetzung. Ursache:
data-i18n stand auf dem <label> selbst und ersetzte dessen ganzen
Inhalt -- samt <span class="wd-pflicht">. Das Anfrageformular macht es
laengst richtig (data-i18n auf einem INNEREN span); die neue Seite
hatte das Muster nicht uebernommen. Aufgefallen ist es nur, weil der
Test die Pflichtfelder GEZAEHLT hat statt sie vorauszusetzen.
2. window.WD.sprache() gibt es nicht, die Funktion heisst getSprache().
Dadurch brach der Uebergang zur zweiten Stufe stumm ab.
GEPRUEFT: 58 Pruefungen, alle bestanden. Darunter die gesetzlich
vorgegebenen Beschriftungen in allen FUENF Sprachen, "kein Grund noetig",
genau drei Pflichtfelder, Datum UND Uhrzeit in der Bestaetigung, und die
Druckansicht.
WAS FILIPE NOCH PRUEFEN LASSEN MUSS: Diese Texte sind sorgfaeltig
recherchiert, aber ich bin keine Rechtsanwaeltin. Vor dem oeffentlichen
Start gehoert das Ganze einmal zu einer im luxemburgischen Recht
qualifizierten Fachperson -- besonders die grenzueberschreitende Lage
(Sitz LU, Kunden in DE/CH/FR/PT) und die Frage, ob die Seite in
Franzoesisch und Portugiesisch aktiv verkaufen soll. Dann muessen die
Rechtstexte auch dorthin uebersetzt werden, und zwar fachlich.
Versionsstempel und Cache-Name auf v7.
Co-Authored-By: Claude Opus 5 <[email protected]>
Erster von vier Schritten fuer zwei Wuensche vom 22.08.2026:
"wenn sie drauf druecken sollen sie auch sehen was ich schon gemacht habe
von dem was im plan war" und "die kunden sollen mir auch nachrichten
hinterlassen koennen".
DATENBANK (3 neue Tabellen, jetzt 18 insgesamt, Fremdschluessel geprueft)
wd_aufgaben -- die Punkte je Projekt. Drei Entscheidungen darin:
* "wer_dran" ist eine eigene Spalte, kein Text. Manche Punkte warten
auf den KUNDEN ("Texte liefern"), und genau die muessen ihm ins Auge
springen. Ohne diese Unterscheidung liest er die Liste als reinen
Fortschrittsbericht und uebersieht seinen eigenen Teil -- der
haeufigste Grund fuer Verzoegerungen ueberhaupt.
* "nicht_enthalten" bildet ab, was NICHT zum Umfang gehoert. Das ist
die haeufigste Streitfrage in jedem Projekt ("ich dachte, das ist
dabei"). Vorher sichtbar aufgeschrieben kostet es nichts, hinterher
kostet es Geld oder den Kunden.
* "kategorie" nutzt dieselben vier Abschnitte wie die Farben im Portal.
Damit bedeutet eine Farbe ueberall dasselbe statt nur huebsch zu sein.
wd_postfach -- Nachrichten OHNE Projektbezug. Bewusst eine EIGENE Tabelle:
wd_nachrichten hat projekt_id als NOT NULL, und SQLite kann das nicht
lockern, ohne die ganze Tabelle zu kopieren -- ein unnoetiges Risiko bei
echten Kundennachrichten. Die neue Tabelle hat ohnehin andere
Anforderungen (Anhaenge, Lesestatus in BEIDE Richtungen, optionaler Bezug
auf eine Aufgabe). Zwei klar getrennte Tabellen sind ehrlicher als eine,
die beides halb kann.
VORLAGEN (56 Punkte ueber 5 Pakete, davon 20 beim Kunden)
Bei jedem Projekt fuenfzehn Punkte von Hand einzutippen fuehrt
zuverlaessig dazu, dass es irgendwann niemand mehr macht -- und dann
steht der Kunde wieder vor der leeren Liste, die der Ausloeser war.
Die Punkte sind in der Sprache formuliert, in der ein KUNDE denkt:
"Aufbau der Seiten festlegen" statt "Informationsarchitektur", "Auf dem
Handy durchtesten" statt "Responsive QA". Er liest diese Liste -- sie
muss ihm etwas sagen, nicht mir.
Die Vorlagen wandern beim ersten Start in die Datenbank und sind ab dann
dort aenderbar. Bereits vorhandene werden NIE ueberschrieben: sonst waeren
von Hand angepasste Vorlagen nach dem naechsten Neustart weg, und niemand
kaeme auf die Idee, dass der Neustart schuld war.
Naechste Schritte: Server-Routen, Verwaltung, Portal.
Co-Authored-By: Claude Opus 5 <[email protected]>
Umsetzung des "Website Masterplan" (16 Seiten) unter /webdesign.
Zugangsschutz mit EIGENER Schranke (server/webdesign-gate.js) statt gate.js:
gate.js laesst seit dem oeffentlichen Start am 21.08.2026 jeden durch, weil
die Pruefung auf SITE_PUBLIC_LAUNCH_AT als allererste Zeile steht. Haette man
/webdesign dahintergehaengt, waere der ausdruecklich nicht-oeffentliche Bereich
inklusive Preisen und spaeteren Kundendaten ab der ersten Sekunde fuer jeden
lesbar gewesen. Eigenes Sitzungs-Cookie, bereich="webdesign" im Token, damit
ein gueltiges Universe-Cookie hier NICHT gilt. 37/37 Tests.
Sieben oeffentliche Seiten in fuenf Sprachen (de, de-CH mit echtem Dialekt, en,
fr, pt). Preise, Zeitrahmen, Paketnamen und die 30-%-Regel stehen an genau
EINER Stelle in wd-core.js -- der Masterplan verlangt "ueberall
widerspruchsfrei", und vier Kopien laufen bei der ersten Preisaenderung
auseinander.
Als App installierbar auf Handy und PC. Der Service Worker speichert bewusst
KEINE HTML-Seite zwischen: nach dem Abmelden wuerden sonst geschuetzte Seiten
weiter ausgeliefert, ohne dass der Server je gefragt wird. 18/18 Tests.
Handy-Abnahme ueber alle Seiten in fuenf Breiten (320-1440) und fuenf Sprachen:
40/40. Der Test fand 35 echte Fehler (Touch-Ziele unter 44px), behoben im
Designsystem statt einzeln pro Seite.
PayPal (Wunsch 22.08.2026 "sofort auf meinem paypal"): Orders API mit
intent=CAPTURE, also sofortiger Einzug statt blosser Reservierung. Gebuehr und
Nettobetrag getrennt gespeichert. Betraege durchgehend als Ganzzahl in Cent.
Gefaelschte Webhooks werden abgewiesen. Fail closed solange Zugangsdaten
fehlen. 28/28 Tests gegen einen nachgebauten PayPal-Server.
Datenbank: 15 Tabellen mit Praefix wd_, fachlich vollstaendig vom Universe
getrennt.
Co-Authored-By: Claude Opus 5 <[email protected]>
Diese Änderungen liefen bereits live auf dem Server, lagen dort aber
ausschließlich als nicht eingecheckte Arbeitskopie — bei jedem Deploy
(stash/pull/pop) und bei jedem Serverproblem wären sie verloren gewesen.
Deshalb hier unverändert in git übernommen, bevor darauf aufgebaut wird.
Enthalten (nicht von mir gebaut, nur gesichert):
- Supporter: eigener fester Zugangscode statt Einmalcode-Login
(Migration 0009, lib/crypto.js scrypt-Hash, routes/supporter.js,
abonnieren.html, supporter.html, i18n-abonnieren/-supporter)
- Stimmen: Profilbilder (Migration 0010, routes/testimonials.js)
- Event-Bild-Upload: voller Pfad statt relativem (routes/events.js)
- Neue/überarbeitete Hintergrundbilder für viele Seiten
- Teilen-Funktion (streamplan.js, i18n-index/-streamplan, main.css)
Co-Authored-By: Claude Opus 5 <[email protected]>
Nutzer-Wunsch 21.08.2026: "wenn ich eine änderung mache ist es auch für
jeden für die die schon da sind und die neuen" + "ich will ich titel
und namen von kategorien und seiten ändern können in der verwaltung".
Backend (braucht Filipes manuellen Deploy):
- Migration 0008: team_members bekommt intro/bioHtml/extraCta-Spalten
-- volle Feld-Parität mit den von Hand gepflegten Profilen (Diene/
Patrick/Bananenstift/Marina nutzen diese Felder).
- routes/team.js: neue importLegacyMember()-Funktion -- übernimmt ein
bestehendes Profil 1:1 in die Datenbank, OHNE erneut zu übersetzen
(die vorhandenen, von Hand geschriebenen Übersetzungen bleiben
erhalten). Idempotent: mehrfacher Import erzeugt keine Duplikate.
- routes/site-texts.js (neu): admin-editierbare Kategorie-Namen und
Seitentitel, generischer key->{de,...}-Override über app_settings
(wie "Event des Jahres"), fester Schlüssel-Katalog aus
Sicherheitsgründen. Leeres Feld setzt auf den Standardtext zurück.
- 27 Tests in isolierter Testumgebung geprüft (kein better-sqlite3
lokal kompilierbar).
Frontend:
- window.dogiTeamZusammenfuehren() (main.js): admin-Mitglieder
ÜBERSCHREIBEN jetzt gleichnamige statische Einträge (per slug) statt
sie zu duplizieren -- eine Bearbeitung wirkt dadurch für alle.
- window.dogiSiteTexteLaden() + applyTranslations() erweitert um
data-site-text-key -- Kategorie-Seitentitel (team-modis.html/
team-scouts.html/creator.html/manager.html) sind jetzt live editierbar.
- Verwaltung: "📥 Bestehende Mitglieder importieren"-Knopf (holt VanVan/
Diene/Funny/Miss/Marina/Ghost/Patrick/Bananenstift aus den data-*.js-
Dateien, VanVan bewusst ausgenommen -- eigene Sonderkarte + Seite),
erweitertes Formular (Intro/ausführliche Vorstellung/zweiter Button,
eingeklappt unter "Erweitert"), neue Sektion "Kategorien &
Seitentitel" (4 Karten, sofort wirksam auf Website UND Verwaltung).
Beim Testen einen echten UX-Bug gefunden und gefixt: die "Gespeichert"-
Meldung nach dem Speichern eines Mitglieds wurde von der direkt
anschließenden Formular-Zurücksetzung sofort wieder überschrieben und
war nie sichtbar.
Cache-Busting-Version auf 20260821k erhöht.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Nutzer-Wunsch 21.08.2026: "überall wo Modis sind oder Scouts oder
Manager, oder Creator, will ich dass ich die easy über meine
Verwaltungsseite hinzufüge und die automatisch in der Website
hinzugefügt werden ... auch mit Fotos und TikTok Link."
Backend (server-internal, braucht Filipes manuellen Deploy):
- Neue Tabelle team_members (Migration 0007) für alle vier Kategorien
gemeinsam -- ergänzt, überschreibt NIE die von Hand gepflegten
Einträge in data-modis.js/data-scouts.js/data-creator.js.
- routes/team.js: öffentliches Lesen (nur aktive Mitglieder, optional
nach Kategorie gefiltert), TEAM_MANAGE-geschütztes Anlegen/Bearbeiten/
Löschen, Foto-Upload (gleiches Muster wie "Event des Jahres"),
automatische Übersetzung von Rolle/Bio/Zitat wie bei anderen
admin-gepflegten Texten. Slug global eindeutig (mit automatischer
Kollisionsauflösung), TikTok-Kurzname wird zu voller URL ergänzt.
27 Tests in isolierter Testumgebung geprüft (kein better-sqlite3
lokal kompilierbar).
Frontend:
- window.dogiTeamLaden() in main.js: holt admin-gepflegte Mitglieder
einer (oder aller) Kategorien und bringt sie in exakt dieselbe Form
wie die bestehenden data-*.js-Einträge.
- team-scouts.html/team-modis.html/creator.html hängen das Ergebnis
einfach an ihre bestehenden Arrays an und rendern erneut -- exakt
derselbe Look wie die schon bestehenden Profile (Foto/Avatar-Initiale,
Rolle, Zitat, TikTok-Button). creator.html hatte bisher ein "return"
bei leerem CREATORS-Array, wodurch admin-Creator NIE geladen worden
wären -- gefixt. team-modis.html berücksichtigt Rang/Rang-Bezeichnung
für die Pyramide.
- Neue Sektion "Weitere Manager" auf manager.html, komplett unsichtbar
bis der erste Manager angelegt wird (data-manager.js neu, wie
data-creator.js aktuell leer).
- profil.html sucht jetzt kategorieübergreifend auch in admin-gepflegten
Profilen, inkl. korrektem Theme/Zurück-Link auch für Manager.
Verwaltung: neue Sektion "Team verwalten" (TEAM_MANAGE-Berechtigung) --
ein Formular für alle vier Kategorien mit Foto-Sofort-Upload,
TikTok-Feld, Bio/Zitat, bedingten Rang-Feldern (nur Modi), Kategorie-
Filterleiste und Bearbeiten/Löschen pro Eintrag.
Beim Testen mit Playwright einen echten Syntaxfehler gefunden und
gefixt (ASCII-Anführungszeichen statt schließendem „" in einem
Statustext), der das GESAMTE Verwaltungs-Skript und damit die komplette
Seite lahmgelegt hätte.
Cache-Busting-Version auf 20260821j erhöht.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Nutzer-Wunsch 20.08.2026: "eine richtig geile kachel ... wo die leute mit
namen und tiktok namen mir eine nachricht schreiben können die ich in die
verwaltung kriege, und dann kann ich die ausgewählten über die
verwaltungsseite auf die website hinzufügen ... das wird mega persoenlich
zu den fans, bitte wirklich krass geil speziell."
- Neue, augenschonend gestaltete Einreich-Kachel auf stimmen.html: Name,
TikTok-Name (optional), Nachricht -- landet NIE automatisch oeffentlich,
sondern immer erst als Entwurf in der Verwaltung.
Warmer Babyblau/Rosé-Farbverlauf, wandernder Lichtschein, pulsierendes
Herz -- ersetzt die 3 ewigen "Hier steht bald..."-Platzhalterkarten.
- Neue Verwaltungs-Sektion "💬 Stimmen verwalten": Warteschlange (offene
zuerst), Freigeben/Ablehnen/Löschen pro Eintrag.
- Freigegebene Stimmen erscheinen automatisch in einer neuen, spezielleren
Zitat-Kartenoptik (großes Anführungszeichen, Name + TikTok-Chip) --
Abschnitt bleibt komplett unsichtbar, solange keine einzige freigegeben
wurde.
- Backend: neue Tabelle `testimonials` (Migration 0006), routes/
testimonials.js (oeffentliches Einreichen + Lesen freigegebener,
admin-Warteschlange + Status/Loeschen mit neuer TESTIMONIALS_MANAGE-
Berechtigung). 18 automatisierte Tests gegen eine Fake-DB bestanden.
- Alles per Playwright visuell durchgespielt: leerer Zustand, Einreichen
-> Erfolgsmeldung, freigegebene Stimmen-Anzeige, Verwaltungs-Warteschlange
mit allen Aktionen.
Backend-Teil (server-internal/, cloudflare-worker/migrations/) noch ohne
Deploy-Zugriff -- Dogi muss ihn manuell auf dogiintern ausrollen.
Co-Authored-By: Claude Opus 5 <[email protected]>
Naechste "Ausbaustufe" aus Dogfather_VanVan_Supporter_Abo.odt Abschnitt 20
(siehe Supporter-Abo-System.md), auf Nutzerwunsch "perfektioniere meine
Verwaltungsseite": Dogi/VanVan koennen in verwaltung.html eine Frage mit
2-6 Antwortoptionen auf Deutsch erstellen, automatische Uebersetzung beim
Speichern (gleiches Muster wie "Event des Jahres"). Jede aktive DogiCrew-
Person sieht die Abstimmung in ihrem Supporter-Bereich, stimmt genau einmal
ab (UNIQUE-Constraint in der DB, nicht nur Anwendungslogik), sieht danach
die Live-Ergebnisse. Admin-Seite zeigt Ergebnisbalken live, kann schliessen/
wiedereroeffnen/loeschen.
- Neue Migration 0005_supporter_polls.sql (supporter_polls,
supporter_poll_votes), neue Berechtigung POLLS_MANAGE.
- server-internal/routes/polls.js: 30 End-to-End-Tests gegen eine
Fake-DB bestanden (better-sqlite3 laesst sich lokal nicht kompilieren).
- verwaltung.html: neue "Abstimmungen"-Kiste im bestehenden
vw-overview-box-Stil (violett/pink Ergebnisbalken).
- supporter.html: neue "Aktuelle Abstimmung"-Karte im bestehenden
Gold-Look, Optionen -> Stimme -> Ergebnisbalken, alle 5 Sprachen.
Backend-Teil (server-internal/, cloudflare-worker/migrations/) noch ohne
Deploy-Zugriff -- Dogi muss ihn manuell auf dogiintern ausrollen.
Co-Authored-By: Claude Opus 5 <[email protected]>
- streamer.html: kompletter Inhalt ersetzt durch Dogis ausführliche
persönliche Vorstellung ("Wer ist DogFather? - Mehr als nur ein Name").
Enthält erstmals öffentlich: Filipe, 34, Portugiese, seit Februar 2026
in Luxemburg, Partnerin Franzi. Abschnitte: Der Mensch hinter DogFather,
DogFather als Streamer, Casper, Team Dogi/VanVan, DogFather als Manager
(Scouts Patrick & Bananenstift verlinkt), Was macht DogFather aus,
Schluss-Statement mit Signatur. Schlanke Buttonzeile am Ende erhalten
fuer Navigation (Videos/Galerie/Team Dogi).
- Neue Standing Rule: "DogFather" wird IMMER mit großem F geschrieben
(nicht "Dogfather"). Site-weit korrigiert in 29 Dateien (Anzeige-Text,
Alt-Texte, Meta-Beschreibungen, Kommentare) - technische Bezeichner
(Dateinamen, data-theme-Werte, CSS-Klassen, @dogfather0804-Handle)
bleiben bewusst unangetastet, da alle klein geschrieben sind und somit
von der Ersetzung gar nicht erst betroffen waren.
Ticketanizers Inbound-Ticket-API erzeugt bei API-Tickets nachweislich
keinen echten Discord-Kanal (nur Web-Inbox + Rollen-Ping im Log-Kanal,
auch nach manuellem Uebernehmen nicht) - das wurde heute gemeinsam mit
Dogi live in der Ticketanizer-Inbox und in Discord verifiziert. Deshalb
zusaetzlich ein normaler Discord-Webhook pro Zielkanal, der die komplette
Bewerbung garantiert sichtbar direkt postet.
Kanal-Zuordnung (von Dogi aus Discord kopiert):
modi -> #modi-bewerbung (DISCORD_WEBHOOK_MODI)
kooperation -> #kooperations-bewerbung (DISCORD_WEBHOOK_KOOPERATION)
scout + creator -> #scout-bewerbung gemeinsam (DISCORD_WEBHOOK_SCOUT_MANAGER)
- Neu: cloudflare-worker/src/lib/discord-webhooks.js
- applications.js: postet jetzt parallel zu Ticketanizer (Promise.all,
weiterhin ueber ctx.waitUntil im Hintergrund, blockiert nie die Antwort)
- Alle 3 Secrets bereits live gesetzt und deployed
- Getestet mit allen 4 Bewerbungstypen (modi/kooperation/scout/creator) -
laut Log alle erfolgreich gepostet, Testdaten aus D1 wieder entfernt
- README.md: Einschraenkung von Ticketanizer dokumentiert + neue
Webhook-Einrichtungsanleitung
Fremde Bewerber koennen theoretisch kurz hintereinander absenden (z.B.
wenn Dogi live "bewerbt euch jetzt" sagt) und dabei dasselbe Rate-Limit
treffen, das beim Testen aufgefallen ist. Jetzt wird ein 429 automatisch
bis zu 2x mit kurzer Pause wiederholt (Retry-After-Header wird beachtet,
sonst 2s/5s Standard-Wartezeit), bevor endgueltig aufgegeben wird - laeuft
komplett im Hintergrund ueber ctx.waitUntil, blockiert nie die Antwort an
den Bewerber. Die Bewerbung selbst landet ohnehin immer sicher in der
eigenen Datenbank, unabhaengig vom Ticketanizer-Ergebnis.
Getestet mit 3 gleichzeitigen Bewerbungen - alle liefen sauber durch.
Bisher wurden Fehler beim Ticket-Anlegen komplett stillschweigend
verschluckt (bewusst, damit es die Bewerbung nie blockiert) - das machte
es aber unmöglich zu sehen, WARUM ein Ticket nicht ankam. Jetzt wird
jeder Versuch (Erfolg wie Fehler, inkl. HTTP-Status und Antworttext)
über console.log/console.error protokolliert, sichtbar per
`wrangler tail`.
Umgestellt von der ursprünglichen Discord-Webhook-Idee auf die offizielle
Ticketanizer-Inbound-Ticket-API (api.ticketanizer.com/v1/tickets) - erzeugt
echte Tickets statt nur Nachrichten, landen dadurch direkt im richtigen
Ticketanizer-Bereich in Discord.
- Neu: cloudflare-worker/src/lib/ticketanizer.js. panel_id pro Bewerbungs-
bereich fest hinterlegt (aus dem Ticketanizer-Dashboard abgelesen):
Modi=4, Kooperation=5, Scout=6, Creator=7 (dort "Manager-Bewerbung")
- Alte discord.js entfernt (Ansatz verworfen)
- applications.js: Aufruf entsprechend umbenannt/angepasst
- README.md: Einrichtungsanleitung aktualisiert (TICKETANIZER_API_KEY
statt 4x DISCORD_WEBHOOK_*)
- Ticketanizer bleibt rein informativ, kein Ausfallpunkt - Bewerbung wird
immer in der eigenen Datenbank gespeichert, unabhängig vom Ticket-Erfolg
- Secret TICKETANIZER_API_KEY bereits live gesetzt und deployed; End-to-
End mit 3 Test-Bewerbungen geprüft (HTTP Ok geloggt), Testeinträge aus
der DB wieder entfernt
Jede eingehende Bewerbung landet weiterhin wie bisher in der D1-Datenbank,
wird zusätzlich aber (falls eine Webhook-URL hinterlegt ist) als formatierte
Discord-Nachricht in den passenden Kanal gepostet - pro Bewerbungsbereich
(Modi/Scout/Creator/Kooperation) eine eigene Webhook-URL, exakt wie auf der
Website getrennt. Läuft über normale Discord-Webhooks, kompatibel mit
Ticketanizer, falls dessen Kanäle eigene Ziel-URLs im selben Format anbieten.
- Neu: cloudflare-worker/src/lib/discord.js (Embed-Aufbau, Feld-Labels
pro Formular, Farbe je Bewerbungsart)
- applications.js: submitApplication ruft den Versand nach dem Speichern
auf, über ctx.waitUntil() damit die Antwort an den Nutzer nicht wartet
- worker.js: ctx wird jetzt an fetch() durchgereicht
- Discord ist rein informativ - fehlt die Webhook-URL oder ist Discord
kurz nicht erreichbar, wird die Bewerbung trotzdem ganz normal
gespeichert
- README.md: Einrichtungsanleitung (wrangler secret put DISCORD_WEBHOOK_*)
- bewerben.html: neuer Reiter "Modi" mit eigenem Formular (Name, E-Mail,
Discord/TikTok-Handle, Community-Erfahrung, Verfuegbarkeit, Motivation)
- Reiter wechselt jetzt nicht nur das Hintergrundbild, sondern auch die
Farbwelt: Creator/Scout/Kooperation bleiben Spicy-Media-rot (Agentur),
Modi wird babyblau (Team Dogi) -- passend zur bestehenden Regel
"Rot ist NUR die Agentur"
- Direkt-Link bewerben.html#modi oeffnet die Bewerbung gleich auf dem
richtigen Reiter; team-modis.html hat jetzt einen "Als Modi bewerben"
Knopf, der genau dahin fuehrt
- Worker: "modi" als vierter erlaubter Bewerbungstyp, laeuft ueber
dieselbe Sichtbarkeits-Berechtigung wie Scout/Kooperation (der feste
Rechte-Katalog aus dem Anforderungsdokument kennt nur zwei Bewerbungs-
Sichtbereiche -- es wurde bewusst KEIN neuer Permission-Key erfunden)
Co-Authored-By: Claude Opus 5 <[email protected]>
- Neues Modul lib/tiktok-live.js liest den Live-Status direkt von der
oeffentlichen TikTok-Live-Seite (SIGI_STATE -> liveRoom.status,
2 = live, alles andere = offline; bestaetigt durch yt-dlp)
- Cron-Trigger im Worker prueft jede Minute und schreibt das Ergebnis
nach app_settings; schlaegt ein Abruf fehl, bleibt der letzte gute
Stand stehen statt faelschlich auf offline zu springen
- /live-status liefert jetzt Quelle, Titel und Zeitpunkt der Pruefung
- Punkt ist rot wenn offline, gruen+pulsierend wenn live, grau solange
der Status laedt; Startseite aktualisiert alle 30 s ohne Neuladen
- Handschaltung bleibt als Notfall-Vorrang mit 3-Stunden-Ablauf, plus
Knopf "Zurueck zur Automatik" im Postfach
Co-Authored-By: Claude Opus 5 <[email protected]>
- Footer hat jetzt einen eigenen Block "Intern" mit Links zum
Bewerbungs-Postfach und zu Zugaenge & Rollen (vorher nur ein
winziger Link ganz unten)
- Postfach-Login verlinkt auf die Zugaenge-Seite
- Neue Tabelle app_settings + Endpunkt /admin/owner/set-code:
der Hauptadministrator kann seinen Code selbst aendern, mit
Bestaetigung des aktuellen Codes; gespeichert wird nur der Hash
- Neuer Tab "Mein Zugang" in zugaenge.html (nur fuer den Owner)
Co-Authored-By: Claude Opus 5 <[email protected]>