"""Flask app factory.""" import subprocess from flask import Flask, request, jsonify from . import config, db, forwarder from .auth import get_device_serial, get_api_token, require_auth from .routes import landmarks, gnss, status, frames def create_app(): """Create and configure the Flask app.""" app = Flask(__name__) # Load config and init DB config.load() db.init() # Register blueprints app.register_blueprint(landmarks.bp) app.register_blueprint(gnss.bp) app.register_blueprint(status.bp) app.register_blueprint(frames.bp) # ── PAIRING ENDPOINT ───────────────────────────────────────────────────── @app.route('/pair') @app.route('/api/1/pair') def pair(): """Pairing info for Varroa app.""" serial = get_device_serial() return jsonify({ 'serial': serial, 'version': '1.0', 'ap_ip': '10.77.0.1', 'api_port': 5000 }) # ── DEVICE INFO ────────────────────────────────────────────────────────── @app.route('/api/1/info') def device_info(): """Device identity (unauthenticated).""" serial = get_device_serial() return jsonify({ 'device_id': serial, 'version': '1.0', 'firmware': 'adacam', 'ap_ip': '10.77.0.1' }) # ── WIFI STATUS/CONNECT ────────────────────────────────────────────────── @app.route('/api/1/wifi/status') def wifi_status(): """Get WiFi client interface status (unauthenticated).""" try: result = subprocess.run( ['wpa_cli', '-i', 'wlp1s0f1', 'status'], capture_output=True, text=True, timeout=5 ) lines = dict( l.split('=', 1) for l in result.stdout.strip().split('\n') if '=' in l ) return jsonify({ 'ssid': lines.get('ssid', ''), 'ip': lines.get('ip_address', ''), 'state': lines.get('wpa_state', 'DISCONNECTED'), 'connected': lines.get('wpa_state') == 'COMPLETED' }) except Exception as e: return jsonify({'error': str(e), 'connected': False}) @app.route('/api/1/wifi/connect', methods=['POST']) @require_auth def wifi_connect(): """Connect to a WiFi network (authenticated).""" data = request.get_json() ssid = data.get('ssid', '').strip() password = data.get('password', '').strip() if not ssid or not password: return jsonify({'error': 'ssid and password required'}), 400 try: result = subprocess.run( ['/usr/local/bin/adacam-wifi-connect', ssid, password], capture_output=True, text=True, timeout=15 ) if result.returncode == 0: return jsonify({'ok': True, 'message': f'Connecting to {ssid}'}) else: return jsonify({'error': result.stderr or 'Failed'}), 500 except Exception as e: return jsonify({'error': str(e)}), 500 # ── SSH CONTROL ────────────────────────────────────────────────────────── @app.route('/api/1/ssh/status') def ssh_status(): """Get SSH daemon status (unauthenticated).""" result = subprocess.run( ['systemctl', 'is-active', 'sshd'], capture_output=True, text=True ) return jsonify({'active': result.stdout.strip() == 'active'}) @app.route('/api/1/ssh/toggle', methods=['POST']) @require_auth def ssh_toggle(): """Enable/disable SSH (authenticated).""" data = request.get_json() enable = data.get('enable', True) subprocess.run( ['systemctl', 'start' if enable else 'stop', 'sshd'], timeout=5 ) return jsonify({'ok': True, 'ssh_enabled': enable}) # Start background forwarder thread forwarder.start_retry_thread() return app