Router /api/public/termine/v1 (config, tage, zeiten, buchungen, widerruf, Token-Seiten /termin/*, Kalender-Abo) mit
eigenem 16-KB-Parser und Fehlermiddleware, CORS nach Herkunft, Honeypot und Formular-Token, Grenzen je IP (IPv6 je /64),
siteKey und Adresse in limits.js, Mindestantwortzeit (TERMINE_MIN_ANTWORT_MS) und 10-Sekunden-Cache fuer tage/zeiten.
Vor dem globalen express.json eingehaengt; /api/public/ ist vom Kundenansicht-Waechter ausgenommen.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Schalter TERMINE_ENABLED (off|pilot|on) mit TERMINE_PILOT_EMAILS, config.termine
und die vier Collection-IDs, Schema der Termine-Collections als reine Daten
(server/services/termine/schema.js), Setup-Skript scripts/termine-setup.mjs mit
--check und schemaKeys() fuer den Appwrite-Nachbau der Tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- server/config.js: amountCents 2000, lookup_key webklar_hosting_monatlich_2000
- ensurePrice haengt einen neuen Preis an das vorhandene Stripe-Produkt (metadata.webklar),
statt ein zweites "WEBklar Hosting-Abo" anzulegen
- abo-bedingungen.html (Uebersicht, Preise, Leistungsbeschreibung) + PDF, Leitfaden + PDF
- Tests auf 20 € umgestellt (399/399)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
getStripe() nutzt in Tests über STRIPE_API_BASE den Nachbau auf 127.0.0.1;
in Produktion ist der Wert leer und nichts ändert sich.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- SMTP_REPLY_TO (z. B. support@webklar.com) setzt Reply-To auf allen
Kundenmails, damit Antworten nicht im Versandpostfach (noreply@) landen
- Absendername einheitlich "WEBklar" (Kunden) bzw. "WEBklar Kundenbereich"
(interne Hinweise) statt "Webklar"/"Webklar Kundenportal"
- requireTLS auf Port 587: der neue Mailserver (Hetzner) nimmt die
Anmeldung auch unverschluesselt an; ohne STARTTLS wird jetzt nicht gesendet
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bisher endete die Frist rechnerisch 14 x 24 h nach Vertragsschluss, und der spaetere
Beginn lag pauschal 15 Tage danach. Gesetzlich beginnt die Frist am Folgetag und endet
um 24 Uhr des 14. Tages; faellt der auf Sa/So/Feiertag, am naechsten Werktag. Beispiel:
Abschluss Sa 26.09. -> Frist bis Mo 12.10., 24 Uhr. Vorher verschwand "Vertrag widerrufen"
schon Sa 10.10. vormittags, und "Beginn nach Ablauf der Frist" buchte am So 11.10. ab.
- server/services/deadlines.js: Fristende und Leistungsbeginn, deutscher Kalender,
Feiertage aller Bundeslaender (zugunsten des Kunden, § 193 BGB: Erklaerungsort).
- Portal: "Vertrag widerrufen · bis TT.MM." bleibt bis zum echten Fristende sichtbar.
- Beginn nach Ablauf der Frist: Stripe trial_end = 6 Uhr am Tag nach dem spaetesten
moeglichen Fristende (Checkout gilt hoechstens 24 h). Die Bestellseite zeigt genau
dieses Datum; hat es sich um Mitternacht verschoben, kommt "Seite neu laden".
- Team-Mail beim Widerruf nennt das Fristende und ob der Widerruf rechtzeitig kam.
- Datumsanzeige im Portal nach deutscher Zeit (auch bei Kunden im Ausland).
Vertragstexte unveraendert (termsVersion bleibt v3).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Kamerafotos in anderen Formaten (z. B. HEIF von Android-Kameras) werden
wie Dateien angenommen statt abgelehnt
- Dateinamen nach Zeichen kuerzen: ein halbiertes Emoji machte den Anhang
unabrufbar (encodeURIComponent)
- Endet eine Aufnahme von selbst (Headset getrennt, Anruf), bleibt die
Sprachnachricht erhalten statt "zu kurz"
- Kamera-App nur auf iOS/iPadOS/Android; Touch-Laptops bekommen die Kamera
im Browser statt eines Dateidialogs
- Knapp neben dem Chat abgelegte Dateien oeffnet der Browser nicht mehr
selbst (Portal bliebe sonst stehen); Datei-Felder bleiben Ablagen
- Fehler beim Ausliefern kommt als JSON statt mit dem Dateityp
- Server liest hoechstens 4 Uploads gleichzeitig ein (Speicher), weitere
warten; abgebrochene Wartende geben ihren Platz frei
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Kundenbereich und Admin-Chat: Bueroklammer-Menue (Bild, Kamera, Datei),
Mikrofon fuer Sprachnachrichten, Vorschau vor dem Senden mit
Upload-Fortschritt, Einfuegen (Strg+V) und Hineinziehen von Dateien
- Kamera: am Handy/Tablet die Kamera-App, am Rechner die Kamera im Browser
mit Vorschau, Kamerawechsel und "Nochmal"
- Sprachnachricht: Aufnahme mit Zeit und Pegel, vorher anhoerbar oder
direkt senden, hoechstens 5 Minuten
- Verlauf: Bilder (einzeln bzw. Raster, Grossansicht), Player fuer
Sprachnachrichten, Datei-Kacheln mit Download
- Server: jede Datei einzeln roh hochgeladen (max. 25 MB, 10 je Nachricht,
max. 3 gleichzeitig), Typ aus dem Inhalt, nur erlaubte Endungen, keine
Programme; Ablage unter upload-data/chat/<Kunde>/ ausserhalb von public/,
Auslieferung nur mit Login; Bilder, Audio und PDF inline, alles andere
als Download (nosniff, CSP sandbox)
- Nicht gesendete Anhaenge werden nach 24 Stunden geloescht
- Benachrichtigungs-Mail nennt nur Art und Namen der Anhaenge
- Appwrite: neues Attribut portalMessages.attachments
(scripts/setup-chat-collection.mjs, vor dem Deploy ausfuehren)
- Datenschutzerklaerung Abschnitt 6 ergaenzt (Stand 26.09.2026), PDF neu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Admin-Tab "Abo-Angebote" und Button "Angebot" in der Kundenliste: Preis, Monate,
Gueltigkeit, Hinweis fuer den Kunden, interne Notiz, Vorschau vor dem Anlegen
- Je Angebot sofort ein Stripe-Coupon (amount_off, repeating ueber die
Angebotsmonate, max. 1 Einloesung); danach automatisch wieder Normalpreis
- Kunde ohne Abo: Angebot im Portal, Angebotspreis in der Bestelluebersicht,
Coupon im Checkout; Angebot schaltet die Bestellung auch ausserhalb der
Pilotliste frei; Bestellung prueft, dass das angezeigte Angebot noch gilt
- Laufendes Abo: Coupon sofort am Abo fuer die naechsten Abbuchungen
- Testkonten: Testrabatt im Checkout, Angebot per Webhook zusaetzlich am Abo
- Angebot in Abo-Metadaten, Vertragsbestaetigung, Admin-Mail und Abo-Karte
- Zurueckziehen loescht den Coupon; abgelaufene Angebote nicht mehr bestellbar
- Stundenkontingent nach invoice.subtotal: auch voll rabattierte oder per
Guthaben bezahlte Monate bringen die Stunde, die Probezeit nicht
- setup-abo-collections.mjs: aboOffers, subscriptions.offerId/offerText,
Attribut-Abfrage mit Limit (sonst Haenger ab 26 Attributen)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Kunde (Dashboard, nur mit laufendem Abo): Antrag mit Leistung, Anbieter,
Domain, Rechnungsbetrag/-zeitraum, Grund, Rechnungsnachweis (PDF/JPG/PNG)
und E-Mail fuer die Antwort; Eingangsbestaetigung per Mail, Status und
Antwort auf Rueckfragen im Portal
- Admin-Mail mit Link admin.html?gutschrift=<id> (Login leitet zurueck)
- Admin-Tab "Gutschriften": Kunde und Vertrag, Nachweise, Verlauf,
automatische Pruefungen (Duplikate per Datei-Hash/Domain, Abo, Zeitraum,
DNS-Abgleich Nameserver/MX mit Anbieter), Pflicht-Checkliste, Rechner
(tagesgenau anteilig), Genehmigen/Rueckfrage/Ablehnen mit Kundenmail
- Genehmigte Gutschrift als Stripe-Kundenguthaben (idempotent, Nachbuchen
bei Stripe-Stoerung)
- Nachweise in upload-data/ (gitignored), Loeschung abgelehnter nach 6 Monaten
- Stundenkontingent auch fuer Monate, die ein Guthaben komplett deckt
(invoice.total statt amount_paid)
- Datenschutzerklaerung Abschnitt 7 ergaenzt, PDF neu erzeugt
- Collection takeoverCredits in setup-abo-collections.mjs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Bestellseite: Pflichtangaben direkt ueber dem Button, Zahlungsarten,
Wahl des Leistungsbeginns (sofort / nach Widerrufsfrist via 15 Tage
Stripe-Probezeit), keine Vorauswahl
- Zustimmungsnachweis: Zeitpunkt, Version, Wahl, IP, Browser in Stripe-
Metadaten und Appwrite (neue Attribute, Setup-Skript ergaenzt)
- Checkout: Karte + SEPA (Fallback nur Karte), korrigierter Hinweistext
- Vertragsbestaetigung mit PDF der Vertragsunterlagen statt HTML
- Kuendigen/Widerrufen: Formular direkt (keine Zwischenseite), Wunsch-
termin, Druck/PDF der Bestaetigung, Vorbefuellung bei Login
- Widerrufsbutton im Abo waehrend der Frist, 'beginnt am'-Status
- Pilotbetrieb per ABO_PILOT_EMAILS
- Vertragsbedingungen v2, Datenschutzerklaerung, Kalkulator ohne MwSt
und mit 100/80 EUR
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Bestellung auf der Portal-Startseite: Zusammenfassung, AGB- und
Vorab-Leistungs-Zustimmung, Button 'Zahlungspflichtig bestellen',
danach Stripe Checkout (Abo, Kleinunternehmer-Fusszeile)
- Stripe-Webhook /api/abo/webhook spiegelt Abos nach Appwrite
(subscriptions) und schreibt je bezahlter Rechnung 60 min gut
(hourCredits, idempotent ueber Invoice-ID, 12 Monate gueltig)
- Stundenkonto: FIFO-Verbrauch aus Worksheets mit countsToPlan,
15-Minuten-Takt, Mehraufwand separat ausgewiesen
- Stripe-Kundenportal fuer Zahlungsart/Rechnungen/Kuendigung
- Oeffentlicher Kuendigungsbutton (§ 312k) und Widerrufsbutton
(§ 356a) mit Eingangsbestaetigung per Mail
- Abo-Bedingungen inkl. Leistungsbeschreibung und Widerrufsbelehrung,
als Anhang der Vertragsbestaetigung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- previewDeploy.syncPreviewTls(): schreibt Traefik-Router (Host-Regeln) fuer alle
Preview-Subdomains nach preview-hosts.yml, damit Let's Encrypt Zertifikate
ausstellt. Laeuft nach jedem Deploy und beim Serverstart (ersetzt das nie
aufgerufene bash-Script sync-preview-tls.sh).
- createProjectFromTemplate: Subdomain optional. Ohne Subdomain wird nur das
Gitea-Repo angelegt (projectType 'repo', Status 'created'), kein Deploy.
- customerAdmin: Portal-Passwort liegt jetzt im Feld customers.portalPassword
statt als __portalPassword__-Prefix in den Notizen; Legacy-Notizen werden
beim naechsten Update bereinigt. CRM-Stufe (lead/customer) wird beim Setzen
des Portal-Passworts nicht mehr auf 'active' ueberschrieben.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sichert den bisher uncommitteten Produktionsstand des Kundenbereich-Servers,
damit kuenftige Deploys (git reset --hard) nichts mehr verwerfen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Express-Server für Appwrite-Auth, Session, Projekt-Dashboard und Gitea-Webhook; statisches Frontend und Schema-Dokumentation für woms-database.
Co-authored-by: Cursor <cursoragent@cursor.com>