🚀 Initial commit: BSN Chatbot — Multi-Channel Intake System
WhatsApp webhook, admin dashboard, public frontend, media handling, LLM auto-summarize, hall/stand detection, iOS-style backend
This commit is contained in:
+124
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
BSN Intake Supervisor — starts Flask app + Cloudflare tunnel.
|
||||
Keeps both alive, restarts on crash, prints the public URL.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
BASE_DIR = "/home/hermes/workspace/bsn-chatbot"
|
||||
VENV_PYTHON = "/home/hermes/venv/bin/python3"
|
||||
CLOUDFLARED = "/home/hermes/.local/bin/cloudflared"
|
||||
|
||||
# Load .env
|
||||
env = os.environ.copy()
|
||||
env_file = os.path.join(BASE_DIR, ".env")
|
||||
if os.path.exists(env_file):
|
||||
with open(env_file) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith("#") and "=" in line:
|
||||
key, _, val = line.partition("=")
|
||||
key = key.strip()
|
||||
val = val.strip().strip('"').strip("'")
|
||||
env[key] = val
|
||||
|
||||
def start_flask():
|
||||
"""Start the Flask app as a subprocess."""
|
||||
proc = subprocess.Popen(
|
||||
[VENV_PYTHON, os.path.join(BASE_DIR, "app.py")],
|
||||
env=env,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
)
|
||||
return proc
|
||||
|
||||
def start_cloudflared():
|
||||
"""Start the Cloudflare tunnel."""
|
||||
proc = subprocess.Popen(
|
||||
[CLOUDFLARED, "tunnel", "--url", "http://127.0.0.1:5002"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
)
|
||||
return proc
|
||||
|
||||
|
||||
def main():
|
||||
LOG = open("/tmp/bsn_supervisor.log", "w")
|
||||
def log(msg):
|
||||
LOG.write(msg + "\n")
|
||||
LOG.flush()
|
||||
|
||||
log("🚀 BSN Intake Supervisor starting...")
|
||||
|
||||
# Start Flask
|
||||
flask = start_flask()
|
||||
log("[flask] started")
|
||||
time.sleep(2)
|
||||
|
||||
# Verify Flask is up
|
||||
import urllib.request
|
||||
try:
|
||||
urllib.request.urlopen("http://127.0.0.1:5002/health", timeout=5)
|
||||
log("[flask] health check: OK")
|
||||
except Exception as e:
|
||||
log(f"[flask] health check FAILED: {e}")
|
||||
|
||||
# Start Cloudflared
|
||||
cf = start_cloudflared()
|
||||
log("[cloudflared] started")
|
||||
|
||||
# Wait for tunnel URL to appear in output
|
||||
tunnel_url = None
|
||||
deadline = time.time() + 20
|
||||
buffer = ""
|
||||
while time.time() < deadline:
|
||||
line = cf.stdout.readline() if cf.stdout else ""
|
||||
if line:
|
||||
buffer += line
|
||||
m = re.search(r"(https://[a-z-]+\.trycloudflare\.com)", buffer)
|
||||
if m:
|
||||
tunnel_url = m.group(1)
|
||||
break
|
||||
if flask.poll() is not None:
|
||||
log(f"[flask] CRASHED! exit={flask.returncode}")
|
||||
break
|
||||
if cf.poll() is not None:
|
||||
log(f"[cloudflared] CRASHED! exit={cf.returncode}")
|
||||
break
|
||||
|
||||
if tunnel_url:
|
||||
log(f"\n✅ TUNNEL: {tunnel_url}")
|
||||
log(f" Webhook: {tunnel_url}/whatsapp/webhook")
|
||||
log(f" Admin: {tunnel_url}/admin")
|
||||
else:
|
||||
log("❌ Failed to get tunnel URL")
|
||||
|
||||
log("\n[supervisor] Running. Press Ctrl+C to stop.\n")
|
||||
|
||||
# Keep running, restart children if they die
|
||||
try:
|
||||
while True:
|
||||
if flask.poll() is not None:
|
||||
log("[flask] died, restarting...")
|
||||
flask = start_flask()
|
||||
if cf.poll() is not None:
|
||||
log("[cloudflared] died, restarting...")
|
||||
cf = start_cloudflared()
|
||||
time.sleep(5)
|
||||
except KeyboardInterrupt:
|
||||
log("\n[supervisor] Shutting down...")
|
||||
flask.terminate()
|
||||
cf.terminate()
|
||||
flask.wait()
|
||||
cf.wait()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user