from flask import Flask, request, jsonify, Response import requests import os from flask_cors import CORS # Initialize Flask app app = Flask(__name__) # Enhanced CORS configuration CORS(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True, allow_headers=["Content-Type", "Authorization", "Access-Control-Allow-Credentials"], methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"] ) # Configuration - point to your main backend MAIN_BACKEND_URL = "http://127.0.0.1:5000" # MAIN_BACKEND_URL = "https://iotbackend.rootxwire.com" # Change this to your main backend URL # Add OPTIONS handler for preflight requests @app.before_request def handle_preflight(): if request.method == "OPTIONS": response = Response() response.headers.add("Access-Control-Allow-Origin", "*") response.headers.add('Access-Control-Allow-Headers', "*") response.headers.add('Access-Control-Allow-Methods', "*") return response # Routes @app.route('/login', methods=['POST']) def login(): """Handle machine login""" try: data = request.json print(f"Login request received: {data}") # Forward the login request to the main backend response = requests.post(f"{MAIN_BACKEND_URL}/login", json=data) print(f"Login response status: {response.status_code}") # Pass through the response return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Login error: {e}") return jsonify({"error": "Login service unavailable"}), 500 @app.route('/uploads/', methods=['GET']) def get_image(filename): """Forward image requests to the main backend""" try: response = requests.get(f"{MAIN_BACKEND_URL}/uploads/{filename}", stream=True) return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'image/jpeg') ) except Exception as e: print(f"Image fetch error: {e}") return jsonify({"error": "Image not found"}), 404 @app.route('/machine-slots/', methods=['GET']) def get_machine_slots(machine_id): """Get slots for a specific machine""" try: print(f"Fetching slots for machine: {machine_id}") # Simply forward the request to the main backend response = requests.get(f"{MAIN_BACKEND_URL}/machine-slots/{machine_id}") print(f"Slots response status: {response.status_code}") # Pass through the response return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Machine slots error: {e}") return jsonify({"error": "Could not fetch machine slots"}), 500 @app.route('/machine-slots/update-inventory', methods=['POST']) def update_inventory(): """Forward inventory update request to main backend""" try: print("=== Inventory Update Request ===") data = request.json if not data: return jsonify({"error": "No data received"}), 400 machine_id = data.get('machineId') updates = data.get('inventoryUpdates', []) print(f"Machine ID: {machine_id}") print(f"Inventory updates: {len(updates)}") response = requests.post( f"{MAIN_BACKEND_URL}/machine-slots/update-inventory", json=data, headers={'Content-Type': 'application/json'} ) print(f"Inventory update response: {response.status_code}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Inventory update error: {e}") return jsonify({"error": "Could not update inventory"}), 500 @app.route('/vending-state/', methods=['GET']) def get_vending_state(machine_id): """Get the vending state for a machine""" try: # Forward the request to the main backend response = requests.get(f"{MAIN_BACKEND_URL}/machines/{machine_id}") # Pass through the response return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Vending state error: {e}") return jsonify({"error": "Could not fetch vending state"}), 500 @app.route('/create-payu-order', methods=['POST', 'OPTIONS']) def create_payu_order(): """Create PayU payment order - forward to main backend""" try: print("=== PayU Order Creation Request ===") data = request.json print(f"Request data: {data}") if not data: print("No data received in request") return jsonify({"error": "No data received"}), 400 # Forward to main backend print(f"Forwarding to: {MAIN_BACKEND_URL}/create-payu-order") response = requests.post( f"{MAIN_BACKEND_URL}/create-payu-order", json=data, headers={'Content-Type': 'application/json'} ) print(f"Main backend response status: {response.status_code}") print(f"Main backend response: {response.text}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except requests.exceptions.ConnectionError as e: print(f"Connection error to main backend: {e}") return jsonify({"error": "Payment service unavailable"}), 503 except Exception as e: print(f"PayU order creation error: {e}") return jsonify({"error": "Could not create payment order"}), 500 @app.route('/vending-state/', methods=['PUT']) def update_vending_state(machine_id): """Update the vending state for a machine""" try: # Forward the request to the main backend headers = {k: v for k, v in request.headers.items() if k not in ['Host', 'Content-Length']} response = requests.put( f"{MAIN_BACKEND_URL}/machines/{machine_id}/vending-state", json=request.json, headers=headers ) # Pass through the response return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Update vending state error: {e}") return jsonify({"error": "Could not update vending state"}), 500 # NEW: Serial Communication Routes @app.route('/machine/connect', methods=['POST']) def connect_machine(): """Forward machine connection request to main backend""" try: print("=== Machine Connection Request ===") data = request.json or {} print(f"Connection data: {data}") response = requests.post( f"{MAIN_BACKEND_URL}/machine/connect", json=data, headers={'Content-Type': 'application/json'} ) print(f"Connection response: {response.status_code}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Machine connection error: {e}") return jsonify({"error": "Could not connect to machine"}), 500 @app.route('/machine/status', methods=['GET']) def get_machine_status(): """Forward machine status request to main backend""" try: print("=== Machine Status Request ===") response = requests.get(f"{MAIN_BACKEND_URL}/machine/status") print(f"Status response: {response.status_code}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Machine status error: {e}") return jsonify({"error": "Could not get machine status"}), 500 @app.route('/machine/dispense', methods=['POST']) def dispense_product(): """Forward single product dispensing to main backend""" try: print("=== Single Product Dispensing Request ===") data = request.json if not data: return jsonify({"error": "No data received"}), 400 print(f"Dispense data: {data}") response = requests.post( f"{MAIN_BACKEND_URL}/machine/dispense", json=data, headers={'Content-Type': 'application/json'} ) print(f"Dispense response: {response.status_code}") print(f"Dispense result: {response.text}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Product dispensing error: {e}") return jsonify({"error": "Could not dispense product"}), 500 @app.route('/machine/dispense-multiple', methods=['POST']) def dispense_multiple_products(): """Forward multiple product dispensing to main backend""" try: print("=== Multiple Product Dispensing Request ===") data = request.json if not data: return jsonify({"error": "No data received"}), 400 machine_id = data.get('machineId') items = data.get('items', []) print(f"Machine ID: {machine_id}") print(f"Items to dispense: {len(items)}") for item in items: print(f" - Slot {item.get('slotId')}: {item.get('quantity')}x {item.get('productName')}") response = requests.post( f"{MAIN_BACKEND_URL}/machine/dispense-multiple", json=data, headers={'Content-Type': 'application/json'} ) print(f"Multiple dispense response: {response.status_code}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Multiple product dispensing error: {e}") return jsonify({"error": "Could not dispense multiple products"}), 500 @app.route('/machine/reset', methods=['POST']) def reset_machine(): """Forward machine reset request to main backend""" try: print("=== Machine Reset Request ===") response = requests.post(f"{MAIN_BACKEND_URL}/machine/reset") print(f"Reset response: {response.status_code}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Machine reset error: {e}") return jsonify({"error": "Could not reset machine"}), 500 @app.route('/machine/disconnect', methods=['POST']) def disconnect_machine(): """Forward machine disconnect request to main backend""" try: print("=== Machine Disconnect Request ===") response = requests.post(f"{MAIN_BACKEND_URL}/machine/disconnect") print(f"Disconnect response: {response.status_code}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Machine disconnect error: {e}") return jsonify({"error": "Could not disconnect machine"}), 500 @app.route('/payment-confirmed', methods=['POST']) def payment_confirmed(): """Forward payment confirmation to main backend for dispensing""" try: print("=== Payment Confirmation Request ===") data = request.json if not data: return jsonify({"error": "No data received"}), 400 machine_id = data.get('machineId') cart_items = data.get('cartItems', []) print(f"Payment confirmed for machine: {machine_id}") print(f"Cart items: {len(cart_items)}") response = requests.post( f"{MAIN_BACKEND_URL}/payment-confirmed", json=data, headers={'Content-Type': 'application/json'} ) print(f"Payment confirmation response: {response.status_code}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Payment confirmation error: {e}") return jsonify({"error": "Could not process payment confirmation"}), 500 @app.route('/transactions/bulk-create', methods=['POST']) def bulk_create_transactions(): """Forward bulk transaction creation to main backend""" try: print("=== Bulk Transaction Creation Request ===") data = request.json if not data: return jsonify({"error": "No data received"}), 400 transactions = data.get('transactions', []) print(f"Creating {len(transactions)} transactions") for trans in transactions: print(f" - {trans.get('product_name')} x {trans.get('quantity')} = ₹{trans.get('amount')}") response = requests.post( f"{MAIN_BACKEND_URL}/transactions/bulk-create", json=data, headers={'Content-Type': 'application/json'} ) print(f"Bulk transaction response: {response.status_code}") return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: print(f"Bulk transaction creation error: {e}") return jsonify({"error": "Could not create transactions"}), 500 # LEGACY: Keep existing dispense-products route for backward compatibility @app.route('/dispense-products', methods=['POST']) def dispense_products(): """Legacy route - redirect to new dispensing system""" print("=== Legacy Dispense Products Request ===") print("Redirecting to new dispensing system...") # Get dispensing instructions from the request data = request.json if not data: print("No JSON data received") return jsonify({"error": "No data received"}), 400 machine_id = data.get('machineId') dispensing_instructions = data.get('dispensingInstructions', []) if not machine_id or not dispensing_instructions: print("Missing machine ID or dispensing instructions") return jsonify({"error": "Missing machine ID or dispensing instructions"}), 400 # Convert to new format and forward to new dispensing endpoint try: new_format = { 'machineId': machine_id, 'items': [ { 'slotId': instruction.get('slotId'), 'quantity': instruction.get('quantity', 1), 'productName': instruction.get('productName', 'Unknown') } for instruction in dispensing_instructions ] } return dispense_multiple_products_internal(new_format) except Exception as e: print(f"Legacy conversion error: {e}") return jsonify({"error": "Could not process legacy request"}), 500 def dispense_multiple_products_internal(data): """Internal function to handle dispensing without duplicate logging""" try: response = requests.post( f"{MAIN_BACKEND_URL}/machine/dispense-multiple", json=data, headers={'Content-Type': 'application/json'} ) return Response( response.content, status=response.status_code, content_type=response.headers.get('Content-Type', 'application/json') ) except Exception as e: return jsonify({"error": "Could not dispense products"}), 500 @app.route('/payment-success', methods=['POST', 'GET']) def payment_success(): data = request.form.to_dict() or request.args.to_dict() print("✅ Payment Success:", data) return jsonify({ "success": True, "message": "Payment successful", "data": data }), 200 @app.route('/payment-failure', methods=['POST', 'GET']) def payment_failure(): data = request.form.to_dict() or request.args.to_dict() print("❌ Payment Failure:", data) return jsonify({ "success": False, "message": "Payment failed", "data": data }), 200 # Add a test route to verify the server is running @app.route('/', methods=['GET']) def test_route(): return jsonify({"status": "Flask server is running"}), 200 # Add error handlers @app.errorhandler(404) def not_found(error): return jsonify({"error": "Endpoint not found"}), 404 @app.errorhandler(500) def internal_error(error): return jsonify({"error": "Internal server error"}), 500 if __name__ == '__main__': # Print startup message print("Starting Flask server on port 5001...") print("Available routes:") print(" - /") print(" - /login [POST]") print(" - /uploads/ [GET]") print(" - /machine-slots/ [GET]") print(" - /vending-state/ [GET/PUT]") print(" - /create-payu-order [POST]") print(" - /dispense-products [POST] (legacy)") print(" - /machine/connect [POST]") print(" - /machine/status [GET]") print(" - /machine/dispense [POST]") print(" - /machine/dispense-multiple [POST]") print(" - /machine/reset [POST]") print(" - /machine/disconnect [POST]") print(" - /payment-confirmed [POST]") print(f"Main backend URL: {MAIN_BACKEND_URL}") # Run the server app.run(host='0.0.0.0', debug=True, port=5001)