2360→ except Exception as e: 2361→ return jsonify({'status': 'error', 'message': str(e)}), 500 2362→ 2363→@app.route('/api/cc/health', methods=['GET']) 2364→def cc_health(): 2365→ """Health check for all gates""" 2366→ try: 2367→ conn = sqlite3.connect(DB_PATH) 2368→ c = conn.cursor() 2369→ 2370→ # Budget health 2371→ c.execute('SELECT daily_limit_usd, alert_threshold_pct FROM budget_config WHERE name = "default"') 2372→ budget_row = c.fetchone() 2373→ daily_limit = budget_row[0] if budget_row else 10.00 2374→ alert_pct = budget_row[1] if budget_row else 80 2375→ 2376→ today = datetime.now().strftime('%Y-%m-%d') 2377→ c.execute('SELECT COALESCE(SUM(cost_usd), 0) FROM cc_runs WHERE DATE(started_at) = ?', (today,)) 2378→ spent_today = c.fetchone()[0] 2379→ budget_pct = (spent_today / daily_limit * 100) if daily_limit > 0 else 0 2380→ 2381→ # Guardrails health 2382→ c.execute('SELECT COUNT(*) FROM guardrails WHERE is_active = 1') 2383→ active_guardrails = c.fetchone()[0] 2384→ 2385→ # Active runs (heartbeat check) 2386→ c.execute("SELECT COUNT(*) FROM cc_runs WHERE status = 'running'") 2387→ active_runs = c.fetchone()[0] 2388→ 2389→ conn.close() 2390→ 2391→ health = { 2392→ 'guardrails': { 2393→ 'status': 'ok' if active_guardrails > 0 else 'warning', 2394→ 'active_rules': active_guardrails 2395→ }, 2396→ 'budget': { 2397→ 'status': 'critical' if budget_pct >= 100 else 'warning' if budget_pct >= alert_pct else 'ok', 2398→ 'spent_usd': round(spent_today, 4), 2399→ 'limit_usd': daily_limit, 2400→ 'used_pct': round(budget_pct, 1) 2401→ }, 2402→ 'heartbeat': { 2403→ 'status': 'ok', 2404→ 'active_runs': active_runs 2405→ } 2406→ } 2407→ 2408→ overall = 'ok' 2409→ if health['budget']['status'] == 'critical': 2410→ overall = 'critical' 2411→ elif health['budget']['status'] == 'warning' or health['guardrails']['status'] == 'warning': 2412→ overall = 'warning' 2413→ 2414→ return jsonify({ 2415→ 'overall': overall, 2416→ 'gates': health, 2417→ 'timestamp': datetime.now().isoformat() 2418→ }) 2419→ 2420→ except Exception as e: 2421→ return jsonify({'status': 'error', 'message': str(e)}), 500 2422→ 2423→# Analytics endpoints 2424→@app.route('/api/analytics', methods=['POST']) 2425→def receive_analytics(): 2426→ """Receive analytics data from clients""" 2427→ try: 2428→ analytics_data = request.json or {} 2429→ 2430→ conn = sqlite3.connect(DB_PATH) 2431→ c = conn.cursor() 2432→ 2433→ # Store analytics data 2434→ c.execute('''INSERT INTO logs 2435→ (timestamp, level, source, message, data, device_ip, user_agent, url) 2436→ VALUES (?, ?, ?, ?, ?, ?, ?, ?)''', 2437→ (datetime.utcnow().isoformat(), 2438→ 'info', 2439→ 'analytics_data', 2440→ f"Analytics data received from session", 2441→ json.dumps(analytics_data), 2442→ request.headers.get('X-Forwarded-For', request.remote_addr), 2443→ request.headers.get('User-Agent', 'Unknown'), 2444→ '')) 2445→ 2446→ log_id = c.lastrowid 2447→ conn.commit() 2448→ conn.close() 2449→ 2450→ return jsonify({'status': 'ok', 'id': log_id}) 2451→ 2452→ except Exception as e: 2453→ print(f"Error receiving analytics: {e}") 2454→ return jsonify({'status': 'error', 'message': str(e)}), 500 2455→ 2456→@app.route('/api/session-end', methods=['POST']) 2457→def session_end(): 2458→ """Receive session end data""" 2459→ try: 2460→ session_data = request.json or {} 2461→ 2462→ conn = sqlite3.connect(DB_PATH) 2463→ c = conn.cursor() 2464→ 2465→ duration = session_data.get('duration', 0) / 1000 if session_data.get('duration') else 0 2466→ 2467→ c.execute('''INSERT INTO logs 2468→ (timestamp, level, source, message, data, device_ip, user_agent, url) 2469→ VALUES (?, ?, ?, ?, ?, ?, ?, ?)''', 2470→ (datetime.utcnow().isoformat(), 2471→ 'info', 2472→ 'session_end', 2473→ f"Session ended: {duration:.1f}s duration", 2474→ json.dumps(session_data), 2475→ request.headers.get('X-Forwarded-For', request.remote_addr), 2476→ request.headers.get('User-Agent', 'Unknown'), 2477→ '')) 2478→ 2479→ conn.commit() 2480→ conn.close() 2481→ 2482→ return jsonify({'status': 'ok'}) 2483→ 2484→ except Exception as e: 2485→ print(f"Error receiving session end: {e}") 2486→ return jsonify({'status': 'error', 'message': str(e)}), 500 2487→ 2488→@app.route('/analytics') 2489→def analytics_dashboard(): 2490→ """Serve Analytics Dashboard""" 2491→ try: 2492→ # Generate analytics dashboard HTML 2493→ html_content = generate_analytics_dashboard() 2494→ return html_content 2495→ except Exception as e: 2496→ return f"Error generating analytics dashboard: {e}", 500 2497→ 2498→def generate_analytics_dashboard(): 2499→ """Generate analytics dashboard HTML""" 2500→ html = f""" 2501→ 2502→ 2503→
2504→ 2505→ 2506→