"""Public "contact the owner" API: a client's own site (a top-level page on its own domain, e.g. barbershop.mivanchenko.de -- not an iframe of this app, unlike the booking widget) posts here directly, cross-origin. See contact_mail.py for why this is a separate path from n8n/lead-intake.json. """ from flask import Blueprint, jsonify, request import booking_db as bdb import contact_mail bp = Blueprint("contact_api", __name__, url_prefix="/api/contact") @bp.after_request def _add_cors_headers(resp): # Client sites live on their own domains (barbershop.mivanchenko.de, a # future client's own domain, ...), never this app's own origin -- a # fixed allowlist would mean editing this file for every new client, so # this mirrors n8n/lead-intake.json's existing allowedOrigins: "*" for # the same public, unauthenticated, abuse-limited-by-content form. # Runs on every response from this blueprint, including Flask's # automatic OPTIONS response to the browser's CORS preflight (a JSON # POST isn't a CORS-simple request) -- no separate OPTIONS route needed. resp.headers["Access-Control-Allow-Origin"] = "*" resp.headers["Access-Control-Allow-Methods"] = "POST, OPTIONS" resp.headers["Access-Control-Allow-Headers"] = "Content-Type" return resp @bp.post("") def create_contact(): body = request.get_json(force=True, silent=True) or {} if (body.get("website") or "").strip(): # Honeypot field, same convention as booking_api.py's create_booking: # real visitors never see or fill it, so a filled value means a bot. # Fake a normal-looking success so a scripted client has no signal. return jsonify({"sent": True}), 201 client_id = body.get("client_id") name = (body.get("name") or "").strip() contact = (body.get("contact") or "").strip() if not (client_id and name and contact): return jsonify({"error": "client_id, name, contact are required"}), 400 if bdb.get_client(client_id) is None: return jsonify({"error": "not found"}), 404 payload = { "name": name, "contact": contact, "service_interest": (body.get("service_interest") or body.get("service") or "").strip(), "message": (body.get("message") or "").strip(), } if not contact_mail.notify_owner_of_contact(client_id, payload): return jsonify({"error": "not found"}), 404 return jsonify({"sent": True}), 201