import os import smtplib from email.message import EmailMessage from functools import wraps import requests as http_requests from flask import Flask, render_template, request, redirect, url_for, abort, Response from database import SessionLocal, AgencySetting, Tour, LeadForm app = Flask(__name__, static_folder="static", template_folder="templates") app.secret_key = os.environ.get("SECRET_KEY", "gtravel-secret-key-change-me") # ---------- helpers ---------- def get_db(): return SessionLocal() def set_setting(db, key, value): setting = db.query(AgencySetting).filter(AgencySetting.setting_key == key).first() if setting: setting.setting_value = value else: db.add(AgencySetting(setting_key=key, setting_value=value)) db.commit() def check_auth(username, password): return username == "admin" and password == "admin" def require_admin(f): @wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): return Response( "Unauthorized", 401, {"WWW-Authenticate": 'Basic realm="Login Required"'} ) return f(*args, **kwargs) return decorated def send_email_mailtrap(settings, lead): host = settings.get("mailtrap_host") port = settings.get("mailtrap_port") user = settings.get("mailtrap_user") password = settings.get("mailtrap_password") if not all([host, port, user, password]): return False msg = EmailMessage() msg.set_content( f"New Lead: {lead.name}\nEmail: {lead.email}\nPhone: {lead.phone}" f"\nMessage: {lead.message}\nInterested in: {lead.interested_in}" f"\nWants Retell AI: {lead.wants_retell_ai}" ) msg["Subject"] = "New Inquiry from GTravel" msg["From"] = settings.get("agency_email", "no-reply@gtravel.com") msg["To"] = settings.get("agency_email", "admin@gtravel.com") try: with smtplib.SMTP(host, int(port)) as server: server.login(user, password) server.send_message(msg) return True except Exception as e: print(f"Error sending email: {e}") return False def save_upload(file_storage, subfolder="images"): """Save a werkzeug FileStorage and return the URL path.""" dest_dir = os.path.join("static", subfolder) os.makedirs(dest_dir, exist_ok=True) file_path = os.path.join(dest_dir, file_storage.filename) file_storage.save(file_path) return f"/{file_path}" # ---------- PUBLIC ROUTES ---------- @app.route("/") def home(): lang = request.args.get("lang", "en") db = get_db() try: tours = db.query(Tour).filter(Tour.category == "tour").all() surfs = db.query(Tour).filter(Tour.category == "surf").all() volcanoes = db.query(Tour).filter(Tour.category == "volcano").all() settings = {s.setting_key: s.setting_value for s in db.query(AgencySetting).all()} return render_template("index.html", lang=lang, tours=tours, surfs=surfs, volcanoes=volcanoes, settings=settings, turnstile_site_key=settings.get("turnstile_site_key", "") ) finally: db.close() @app.route("/tour/") def view_tour(tour_id): lang = request.args.get("lang", "en") db = get_db() try: tour = db.query(Tour).filter(Tour.id == tour_id).first() if not tour: abort(404) settings = {s.setting_key: s.setting_value for s in db.query(AgencySetting).all()} return render_template("detail.html", lang=lang, tour=tour, settings=settings, turnstile_site_key=settings.get("turnstile_site_key", "") ) finally: db.close() @app.route("/submit", methods=["POST"]) def submit_form(): lang = request.args.get("lang", "en") db = get_db() try: settings_dict = {s.setting_key: s.setting_value for s in db.query(AgencySetting).all()} secret_key = settings_dict.get("turnstile_secret_key") cf_turnstile_response = request.form.get("cf-turnstile-response") if secret_key and cf_turnstile_response: verify_url = "https://challenges.cloudflare.com/turnstile/v0/siteverify" verify_data = {"secret": secret_key, "response": cf_turnstile_response} resp = http_requests.post(verify_url, data=verify_data) if not resp.json().get("success"): print("Turnstile verification failed") wants_retell = request.form.get("wants_retell_ai") == "true" lead = LeadForm( name=request.form["name"], email=request.form["email"], phone=request.form["phone"], message=request.form.get("message", ""), interested_in=request.form["interested_in"], wants_retell_ai=wants_retell ) db.add(lead) db.commit() db.refresh(lead) send_email_mailtrap(settings_dict, lead) if wants_retell: retell_api = settings_dict.get("retell_api_key") retell_agent_id = settings_dict.get("retell_agent_id") if retell_api and retell_agent_id: try: headers = {"Authorization": f"Bearer {retell_api}"} payload = {"agent_id": retell_agent_id, "to_number": lead.phone} # http_requests.post("https://api.retellai.com/create-phone-call", json=payload, headers=headers) print(f"Initiating Retell AI call to {lead.phone}") except Exception as e: print(f"Retell error: {e}") return redirect(f"/?lang={lang}&submitted=true") finally: db.close() # ---------- ADMIN ROUTES ---------- @app.route("/admin") @require_admin def admin_dashboard(): db = get_db() try: tours = db.query(Tour).all() leads = db.query(LeadForm).order_by(LeadForm.created_at.desc()).all() settings = {s.setting_key: s.setting_value for s in db.query(AgencySetting).all()} return render_template("dashboard.html", tours=tours, leads=leads, settings=settings ) finally: db.close() @app.route("/admin/settings", methods=["POST"]) @require_admin def admin_settings(): db = get_db() try: updates = { "agency_name": request.form.get("agency_name", ""), "agency_email": request.form.get("agency_email", ""), "logo_url": request.form.get("logo_url", ""), "hero_video_url": request.form.get("hero_video_url", ""), "mailtrap_host": request.form.get("mailtrap_host", ""), "mailtrap_port": request.form.get("mailtrap_port", ""), "mailtrap_user": request.form.get("mailtrap_user", ""), "mailtrap_password": request.form.get("mailtrap_password", ""), "turnstile_site_key": request.form.get("turnstile_site_key", ""), "turnstile_secret_key": request.form.get("turnstile_secret_key", ""), "retell_api_key": request.form.get("retell_api_key", ""), "retell_number": request.form.get("retell_number", ""), "retell_agent_id": request.form.get("retell_agent_id", ""), "retell_enabled": "true" if request.form.get("retell_enabled") == "on" else "false" } logo_file = request.files.get("logo_upload") if logo_file and logo_file.filename: updates["logo_url"] = save_upload(logo_file, "images") video_file = request.files.get("hero_video_upload") if video_file and video_file.filename: updates["hero_video_url"] = save_upload(video_file, "videos") for k, v in updates.items(): set_setting(db, k, v) return redirect(url_for("admin_dashboard")) finally: db.close() @app.route("/admin/tours", methods=["POST"]) @require_admin def admin_add_tour(): db = get_db() try: final_image_url = request.form.get("image_url", "") image_file = request.files.get("image_upload") if image_file and image_file.filename: final_image_url = save_upload(image_file, "images") tour = Tour( category=request.form["category"], title_en=request.form["title_en"], title_es=request.form["title_es"], desc_en=request.form["desc_en"], desc_es=request.form["desc_es"], price=request.form["price"], image_url=final_image_url ) db.add(tour) db.commit() return redirect(url_for("admin_dashboard")) finally: db.close() @app.route("/admin/tours/update/", methods=["POST"]) @require_admin def admin_update_tour(tour_id): db = get_db() try: tour = db.query(Tour).filter(Tour.id == tour_id).first() if tour: final_image_url = request.form.get("image_url", "") image_file = request.files.get("image_upload") if image_file and image_file.filename: final_image_url = save_upload(image_file, "images") elif final_image_url == "" and tour.image_url: final_image_url = tour.image_url tour.category = request.form["category"] tour.title_en = request.form["title_en"] tour.title_es = request.form["title_es"] tour.desc_en = request.form["desc_en"] tour.desc_es = request.form["desc_es"] tour.price = request.form["price"] tour.image_url = final_image_url db.commit() return redirect(url_for("admin_dashboard")) finally: db.close() @app.route("/admin/tours/delete/", methods=["POST"]) @require_admin def admin_delete_tour(tour_id): db = get_db() try: tour = db.query(Tour).filter(Tour.id == tour_id).first() if tour: db.delete(tour) db.commit() return redirect(url_for("admin_dashboard")) finally: db.close() # ---------- RUN ---------- if __name__ == "__main__": app.run(host="0.0.0.0", port=8000, debug=True)