from flask import Flask, request, jsonify from flask_cors import CORS import joblib import pandas as pd import os import psycopg2 from datetime import datetime import uuid import jwt # ================= CONFIG ================= DATABASE_URL = os.environ.get("DATABASE_URL") JWT_SECRET = os.environ.get("JWT_SECRET") if not DATABASE_URL: raise RuntimeError("DATABASE_URL is not set in environment variables") if not JWT_SECRET: raise RuntimeError("JWT_SECRET is not set in environment variables") # ================= LOAD MODEL ================= if not os.path.exists("diabetes_model_tuned2.pkl"): import gdown url = "https://drive.google.com/uc?id=1Zo-xI1y3Y64HU6aiwXVUIHXfu84bBphx" gdown.download(url, "diabetes_model_tuned2.pkl", quiet=False) model = joblib.load("diabetes_model_tuned2.pkl") # ================= APP ================= app = Flask(__name__) # 🔹 Konfigurasi CORS yang lebih lengkap CORS(app, resources={r"/*": { "origins": [ "http://localhost:5173", "http://localhost:3000", "https://capstone-dbs-react.vercel.app", r"https://.*\.ngrok-free\.app" ], "methods": ["GET", "POST", "OPTIONS"], "allow_headers": ["Content-Type", "Authorization"], "supports_credentials": True }}) def get_db_connection(): try: return psycopg2.connect(DATABASE_URL) except Exception as e: print(f"Database connection error: {e}") return None # ================= JWT HELPER ================= def get_user_id_from_token(request): auth_header = request.headers.get("Authorization") if not auth_header: return None try: scheme, token = auth_header.split() if scheme.lower() != "bearer": return None decoded = jwt.decode(token, JWT_SECRET, algorithms=["HS256"]) return decoded.get("userId") or decoded.get("id") except Exception as e: print("JWT decode error:", e) return None # ================= ROUTES ================= @app.route('/') def home(): return "API is running" @app.route('/predict-history', methods=['POST']) def predict_history(): try: data = request.get_json() if not data: return jsonify({'error': 'No data provided'}), 400 required_fields = [ 'hypertension', 'heart_disease', 'bmi', 'blood_glucose_level', 'HbA1c_level', 'smoking_history', 'gender', 'age' ] for field in required_fields: if field not in data: return jsonify({'error': f'Missing field: {field}'}), 400 # 🔹 Ambil userId dari JWT (boleh None) current_user_id = get_user_id_from_token(request) input_data = pd.DataFrame([{ 'hypertension': int(data['hypertension']), 'heart_disease': int(data['heart_disease']), 'bmi': float(data['bmi']), 'blood_glucose_level': float(data['blood_glucose_level']), 'HbA1c_level': float(data['HbA1c_level']), 'smoking_history': data['smoking_history'], 'gender': data['gender'], 'age': float(data['age']) }]) prediction = model.predict(input_data)[0] proba = model.predict_proba(input_data)[0] confidence = float(proba[1]) if prediction == 1 else float(proba[0]) prediction_result = "positive" if prediction == 1 else "negative" conn = get_db_connection() if not conn: return jsonify({'error': 'Database connection failed'}), 500 try: cursor = conn.cursor() insert_query = """ INSERT INTO form_check_history (id, "userId", hypertension, "heartDisease", bmi, "bloodGlucoseLevel", "hba1cLevel", "smokingHistory", gender, age, "predictionResult", "createdAt", "updatedAt") VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id """ record_id = str(uuid.uuid4()) current_time = datetime.now() cursor.execute(insert_query, ( record_id, current_user_id, bool(int(data['hypertension'])), bool(int(data['heart_disease'])), float(data['bmi']), float(data['blood_glucose_level']), float(data['HbA1c_level']), data['smoking_history'], data['gender'], float(data['age']), prediction_result, current_time, current_time )) conn.commit() cursor.close() conn.close() return jsonify({ 'success': True, 'message': 'Prediction completed and saved successfully', 'recordId': record_id, 'prediction': { 'result': int(prediction), 'resultText': prediction_result, 'confidence': round(confidence, 4) }, 'inputData': data, 'timestamp': current_time.isoformat(), 'userId': current_user_id }), 200 except Exception as db_error: conn.rollback() cursor.close() conn.close() return jsonify({'error': f'Database error: {str(db_error)}'}), 500 except Exception as e: return jsonify({'error': f'Prediction error: {str(e)}'}), 500 # ================= RUN ================= if __name__ == "__main__": app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)), debug=False)