| import sys |
| sys.path.insert(0, "/app") |
|
|
| from flask import Flask, request, jsonify, render_template |
| import json, time, os |
|
|
| from shared.config import Config |
| from shared.kafka_utils import create_producer |
|
|
| app = Flask(__name__, template_folder=os.getenv("TEMPLATE_FOLDER", "templates")) |
|
|
| producer = create_producer(component_name="Frontend") |
|
|
| @app.route('/') |
| def index(): |
| return render_template('index.html') |
|
|
| @app.route('/health') |
| def health(): |
| """Health check endpoint.""" |
| import requests |
| status = { |
| "status": "healthy", |
| "service": "frontend", |
| "timestamp": time.time() |
| } |
| |
| try: |
| producer.flush(timeout=1) |
| status["kafka"] = "connected" |
| except Exception as e: |
| status["kafka"] = f"error: {e}" |
| status["status"] = "degraded" |
|
|
| |
| try: |
| r = requests.get(f"{Config.MATCHER_URL}/health", timeout=2) |
| if r.status_code == 200: |
| status["matcher"] = "connected" |
| else: |
| status["matcher"] = f"error: status {r.status_code}" |
| except Exception as e: |
| status["matcher"] = f"error: {e}" |
|
|
| return jsonify(status) |
|
|
| @app.route('/order', methods=['POST']) |
| def send_order(): |
| if request.is_json: |
| data = request.json |
| else: |
| data = { |
| 'order_id': request.form.get('order_id') or str(time.time_ns()), |
| 'symbol': request.form.get('symbol'), |
| 'type': request.form.get('type'), |
| 'quantity': int(request.form.get('quantity')), |
| 'price': float(request.form.get('price')), |
| 'timestamp': time.time() |
| } |
| producer.send(Config.ORDERS_TOPIC, value=data) |
| producer.flush() |
| return jsonify({'status':'sent','data':data}) |
|
|
| @app.route('/securities') |
| def securities(): |
| symbols = [] |
| try: |
| with open(Config.SECURITIES_FILE) as f: |
| for line in f: |
| line = line.strip() |
| if line and not line.startswith('#'): |
| symbols.append(line.split('\t')[0]) |
| except Exception: |
| pass |
| return jsonify(symbols) |
|
|
| @app.route('/book') |
| def book(): |
| import requests |
| symbol = request.args.get('symbol', '') |
| if not symbol: |
| return jsonify({"bids": [], "asks": []}) |
| r = requests.get(f"{Config.MATCHER_URL}/orderbook/{symbol}", timeout=3) |
| return (r.text, r.status_code, {'Content-Type': 'application/json'}) |
|
|
| @app.route('/trades') |
| def trades(): |
| import requests |
| r = requests.get(f"{Config.MATCHER_URL}/trades", timeout=2) |
| return (r.text, r.status_code, {'Content-Type': 'application/json'}) |
|
|
| if __name__ == '__main__': |
| port = int(os.getenv('PORT', '5000')) |
| app.run(host='0.0.0.0', port=port) |
|
|
|
|
| @app.route('/submit', methods=['POST']) |
| def submit_order(): |
| from flask import request |
| data = None |
| try: |
| data = request.get_json(force=True) |
| except Exception as e: |
| return jsonify({'status':'badjson','error': str(e)}), 400 |
| if not data: |
| return jsonify({'status':'no_data'}), 400 |
| try: |
| fut = producer.send(Config.ORDERS_TOPIC, value=data) |
| meta = fut.get(timeout=10) |
| producer.flush() |
| app.logger.info("Produced order %s -> topic=%s partition=%s offset=%s", data.get('order_id'), meta.topic, meta.partition, meta.offset) |
| return jsonify({'status':'sent','data':data}) |
| except Exception as e: |
| app.logger.exception("Failed producing order: %s", e) |
| return jsonify({'status':'failed','error':str(e)}), 500 |
|
|