Rate-limit public bookings per contact; add direct owner contact endpoint
Test backoffice (smb-crm) / test (push) Successful in 1m43s

Public booking API now rejects a 6th active booking from the same
customer_contact within 24h (429), stopping one contact from filling
every slot on every resource, while owner-entered manual bookings stay
unaffected.

Add POST /api/contact: client sites can reach their own owner's inbox
directly (via their existing login email) for general inquiries,
separate from the agency's leads/Telegram pipeline (n8n/lead-intake.json),
which stays reserved for actual prospects contacting the agency itself.
Paris Barber Shop's contact form and Rückruf widget now point here; the
Rückruf floating widget itself has been removed from the site.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-12 04:47:06 +02:00
parent bcb2672d9f
commit c3e520aabb
9 changed files with 299 additions and 108 deletions
+46
View File
@@ -0,0 +1,46 @@
"""Direct customer-to-owner contact email: a client's own site visitor asking
a question or requesting a callback (contact_api.py) reaches the business
owner's inbox directly. This is deliberately NOT the agency's leads pipeline
(n8n/lead-intake.json, the leads table, the agency's Telegram) -- that path
still exists separately for prospects contacting the agency itself (the
"DEMO" client_id on the marketing/demo pages), which are genuine sales leads
for the agency, not a client's own customers.
Recipient is resolved via booking_db.list_users(client_id) (the owner's own
login email, e.g. inhaber@<slug>.mivanchenko.de) -- every provisioned client
already has at least one owner account by the time their site can receive
contact requests. Fire-and-forget via mailer.py, same as booking_mail.py/
owner_mail.py.
"""
import booking_db as bdb
import booking_mail
import mailer
from flask import render_template
def notify_owner_of_contact(client_id, payload):
"""Returns True if at least one owner account was found and an email was
queued, False if this client has no owner account yet (nothing to notify
-- the caller turns that into a clean 404, since there is no one to
receive the message)."""
users = bdb.list_users(client_id)
if not users:
return False
client = bdb.get_client(client_id)
business_name = (client or {}).get("business_name") or "Ihre Website"
html = render_template(
"emails/contact_notification.html",
business_name=business_name,
name=payload.get("name") or "",
contact=payload.get("contact") or "",
service_interest=payload.get("service_interest") or "",
message=payload.get("message") or "",
)
from_addr = booking_mail._sender_for(client)
for user in users:
mailer.send_email(
user["email"],
f"Neue Anfrage über Ihre Website {business_name}",
html,
from_addr=from_addr)
return True