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
+14
View File
@@ -24,6 +24,13 @@ bp = Blueprint("booking_api", __name__, url_prefix="/api/booking")
TOKEN_SECRET = os.environ.get("BOOKING_TOKEN_SECRET", "")
TOKEN_TTL_DAYS = 30
# Per-contact rate limit on the public booking endpoint: stops one phone
# number/email from filling every slot on every resource. Deliberately not
# applied to owner_booking.py's manual-entry path (source="owner") -- it
# calls create_booking_row directly, never through this route.
CONTACT_BOOKING_LIMIT = 5
CONTACT_BOOKING_WINDOW_HOURS = 24
def _mint_manage_token(client_id, booking_id):
payload = {
@@ -290,6 +297,13 @@ def create_booking():
and body.get("customer_name") and body.get("customer_contact")):
return jsonify({"error": "client_id, resource_id, service_id, start_time, "
"customer_name, customer_contact are required"}), 400
since = datetime.now(timezone.utc) - timedelta(hours=CONTACT_BOOKING_WINDOW_HOURS)
recent = bdb.count_recent_bookings_by_contact(
client_id, body["customer_contact"], since)
if recent >= CONTACT_BOOKING_LIMIT:
return jsonify({"error": "too many recent bookings for this contact"}), 429
try:
booking, token = create_booking_row(
client_id, resource_id, service_id, start_time,