Spaces:
Sleeping
Sleeping
Complete session fix for Hugging Face deployment
Browse files
app.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
| 1 |
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify
|
| 2 |
-
from flask_session import Session
|
| 3 |
from werkzeug.middleware.proxy_fix import ProxyFix
|
| 4 |
import os
|
| 5 |
import gc
|
|
@@ -43,19 +42,20 @@ load_dotenv()
|
|
| 43 |
# Initialize Flask app
|
| 44 |
app = Flask(__name__, static_folder='app/static', template_folder='app/templates')
|
| 45 |
|
| 46 |
-
# CRITICAL FIX:
|
| 47 |
-
app.secret_key = os.environ.get('SECRET_KEY', '
|
| 48 |
|
| 49 |
-
# CRITICAL FIX:
|
| 50 |
app.config.update({
|
| 51 |
'PERMANENT_SESSION_LIFETIME': timedelta(hours=2),
|
| 52 |
-
'SESSION_COOKIE_NAME': '
|
| 53 |
'SESSION_COOKIE_HTTPONLY': True,
|
| 54 |
-
'SESSION_COOKIE_SECURE': False,
|
| 55 |
-
'SESSION_COOKIE_SAMESITE':
|
| 56 |
-
'SESSION_REFRESH_EACH_REQUEST':
|
| 57 |
'SESSION_COOKIE_DOMAIN': None,
|
| 58 |
-
'SESSION_COOKIE_PATH': '/'
|
|
|
|
| 59 |
})
|
| 60 |
|
| 61 |
# CRITICAL FIX: Add ProxyFix middleware for Hugging Face reverse proxy
|
|
@@ -69,7 +69,6 @@ app.wsgi_app = ProxyFix(
|
|
| 69 |
|
| 70 |
print("Flask app initialized with ProxyFix middleware and built-in cookie sessions")
|
| 71 |
|
| 72 |
-
|
| 73 |
# Create temporary directory for image processing
|
| 74 |
TEMP_DIR = tempfile.mkdtemp()
|
| 75 |
|
|
@@ -561,6 +560,8 @@ def login():
|
|
| 561 |
|
| 562 |
print(f"=== LOGIN DEBUG ===")
|
| 563 |
print(f"Student ID: {student_id}")
|
|
|
|
|
|
|
| 564 |
|
| 565 |
if not student_id or not password:
|
| 566 |
flash('Student ID and password are required.', 'danger')
|
|
@@ -570,18 +571,34 @@ def login():
|
|
| 570 |
print(f"Student found: {bool(student)}")
|
| 571 |
|
| 572 |
if student and student.get('password') == password:
|
| 573 |
-
# CRITICAL FIX:
|
| 574 |
session.clear()
|
|
|
|
|
|
|
| 575 |
session['logged_in'] = True
|
| 576 |
session['user_type'] = 'student'
|
| 577 |
session['student_id'] = student_id
|
| 578 |
session['name'] = student.get('name', 'Unknown')
|
|
|
|
| 579 |
|
| 580 |
print(f"Session after setting: {dict(session)}")
|
|
|
|
|
|
|
|
|
|
| 581 |
flash('Login successful!', 'success')
|
| 582 |
|
| 583 |
-
#
|
| 584 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
else:
|
| 586 |
print("Invalid credentials")
|
| 587 |
flash('Invalid credentials. Please try again.', 'danger')
|
|
@@ -592,7 +609,6 @@ def login():
|
|
| 592 |
flash(f'Login failed: {str(e)}', 'danger')
|
| 593 |
return redirect(url_for('login_page'))
|
| 594 |
|
| 595 |
-
|
| 596 |
@app.route('/face-login', methods=['POST'])
|
| 597 |
def face_login():
|
| 598 |
try:
|
|
@@ -647,10 +663,12 @@ def face_login():
|
|
| 647 |
if result["verified"]:
|
| 648 |
# CRITICAL FIX: Simplified session setting
|
| 649 |
session.clear()
|
|
|
|
| 650 |
session['logged_in'] = True
|
| 651 |
session['user_type'] = face_role
|
| 652 |
session[id_field] = user[id_field]
|
| 653 |
session['name'] = user.get('name', 'Unknown')
|
|
|
|
| 654 |
|
| 655 |
print(f"Face login successful for {user.get('name')}, Session: {dict(session)}")
|
| 656 |
flash('Face login successful!', 'success')
|
|
@@ -689,7 +707,6 @@ def face_login():
|
|
| 689 |
flash(f'Face login failed: {str(e)}', 'danger')
|
| 690 |
return redirect(url_for('login_page'))
|
| 691 |
|
| 692 |
-
|
| 693 |
@app.route('/auto-face-login', methods=['POST'])
|
| 694 |
def auto_face_login():
|
| 695 |
"""Enhanced auto face login with role support"""
|
|
@@ -740,14 +757,12 @@ def auto_face_login():
|
|
| 740 |
if result["verified"]:
|
| 741 |
# Clear any existing session
|
| 742 |
session.clear()
|
| 743 |
-
|
| 744 |
-
# Set session variables with Flask-Session
|
| 745 |
session['logged_in'] = True
|
| 746 |
session['user_type'] = face_role
|
| 747 |
session[id_field] = user[id_field]
|
| 748 |
session['name'] = user.get('name', 'Unknown')
|
| 749 |
-
session
|
| 750 |
-
session.modified = True
|
| 751 |
|
| 752 |
print(f"Auto face login successful for {user.get('name')}, Session: {dict(session)}")
|
| 753 |
|
|
@@ -797,25 +812,33 @@ def attendance_page():
|
|
| 797 |
@app.route('/dashboard')
|
| 798 |
def dashboard():
|
| 799 |
print(f"=== DASHBOARD DEBUG ===")
|
|
|
|
|
|
|
| 800 |
print(f"Session data: {dict(session)}")
|
| 801 |
print(f"Session keys: {list(session.keys())}")
|
|
|
|
|
|
|
|
|
|
| 802 |
|
| 803 |
-
# Check session
|
| 804 |
logged_in = session.get('logged_in')
|
| 805 |
user_type = session.get('user_type')
|
|
|
|
| 806 |
|
| 807 |
print(f"logged_in: {logged_in} (type: {type(logged_in)})")
|
| 808 |
print(f"user_type: {user_type} (type: {type(user_type)})")
|
|
|
|
| 809 |
|
| 810 |
if not logged_in or user_type != 'student':
|
| 811 |
print("=== SESSION CHECK FAILED ===")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 812 |
flash('Please log in to access the dashboard.', 'info')
|
| 813 |
return redirect(url_for('login_page'))
|
| 814 |
|
| 815 |
try:
|
| 816 |
-
student_id = session.get('student_id')
|
| 817 |
-
print(f"Loading dashboard for student: {student_id}")
|
| 818 |
-
|
| 819 |
student = students_collection.find_one({'student_id': student_id})
|
| 820 |
if not student:
|
| 821 |
print("Student not found in database")
|
|
@@ -840,24 +863,6 @@ def dashboard():
|
|
| 840 |
flash(f'Error loading dashboard: {str(e)}', 'danger')
|
| 841 |
return redirect(url_for('login_page'))
|
| 842 |
|
| 843 |
-
|
| 844 |
-
# Process face image if exists
|
| 845 |
-
if student and 'face_image' in student and student['face_image']:
|
| 846 |
-
face_image_base64 = base64.b64encode(student['face_image']).decode('utf-8')
|
| 847 |
-
mime_type = student.get('face_image_type', 'image/jpeg')
|
| 848 |
-
student['face_image_url'] = f"data:{mime_type};base64,{face_image_base64}"
|
| 849 |
-
|
| 850 |
-
# Get attendance records
|
| 851 |
-
attendance_records = list(attendance_collection.find({'student_id': student_id}).sort('date', -1))
|
| 852 |
-
|
| 853 |
-
print(f"Dashboard loaded successfully for {student.get('name')}")
|
| 854 |
-
return render_template('dashboard.html', student=student, attendance_records=attendance_records)
|
| 855 |
-
|
| 856 |
-
except Exception as e:
|
| 857 |
-
print(f"Dashboard error: {e}")
|
| 858 |
-
flash(f'Error loading dashboard: {str(e)}', 'danger')
|
| 859 |
-
return redirect(url_for('login_page'))
|
| 860 |
-
|
| 861 |
@app.route('/mark-attendance', methods=['POST'])
|
| 862 |
def mark_attendance():
|
| 863 |
if 'logged_in' not in session or session.get('user_type') != 'student':
|
|
@@ -1170,14 +1175,12 @@ def teacher_login():
|
|
| 1170 |
if teacher and teacher.get('password') == password:
|
| 1171 |
# Clear any existing session
|
| 1172 |
session.clear()
|
| 1173 |
-
|
| 1174 |
-
# Set session variables with Flask-Session
|
| 1175 |
session['logged_in'] = True
|
| 1176 |
session['user_type'] = 'teacher'
|
| 1177 |
session['teacher_id'] = teacher_id
|
| 1178 |
session['name'] = teacher.get('name', 'Unknown')
|
| 1179 |
-
session
|
| 1180 |
-
session.modified = True
|
| 1181 |
|
| 1182 |
print(f"Teacher session set: {dict(session)}")
|
| 1183 |
flash('Login successful!', 'success')
|
|
@@ -1331,7 +1334,7 @@ def health_check():
|
|
| 1331 |
'status': 'healthy',
|
| 1332 |
'platform': 'hugging_face',
|
| 1333 |
'session_working': bool(session.get('logged_in')),
|
| 1334 |
-
'session_storage':
|
| 1335 |
'proxy_fix': 'enabled',
|
| 1336 |
'session_keys': list(session.keys()),
|
| 1337 |
'timestamp': datetime.now().isoformat()
|
|
@@ -1344,13 +1347,28 @@ def debug_session():
|
|
| 1344 |
'session_keys': list(session.keys()),
|
| 1345 |
'cookies': dict(request.cookies),
|
| 1346 |
'headers': dict(request.headers),
|
| 1347 |
-
'
|
| 1348 |
-
'session_dir': app.config['SESSION_FILE_DIR'],
|
| 1349 |
'proxy_fix': 'enabled',
|
| 1350 |
'session_modified': getattr(session, 'modified', 'N/A'),
|
| 1351 |
'session_permanent': getattr(session, 'permanent', 'N/A')
|
| 1352 |
})
|
| 1353 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1354 |
@app.route('/test-session')
|
| 1355 |
def test_session():
|
| 1356 |
"""Test session functionality"""
|
|
@@ -1358,7 +1376,6 @@ def test_session():
|
|
| 1358 |
session['test_data'] = 'working'
|
| 1359 |
session['timestamp'] = datetime.now().isoformat()
|
| 1360 |
session.permanent = True
|
| 1361 |
-
session.modified = True
|
| 1362 |
|
| 1363 |
return jsonify({
|
| 1364 |
'message': 'Session test completed',
|
|
@@ -1378,5 +1395,5 @@ def manual_cleanup():
|
|
| 1378 |
# MAIN APPLICATION ENTRY POINT
|
| 1379 |
if __name__ == '__main__':
|
| 1380 |
port = int(os.environ.get('PORT', 7860)) # Hugging Face uses port 7860
|
| 1381 |
-
print(f"Starting Flask app on port {port} with ProxyFix middleware and
|
| 1382 |
app.run(host='0.0.0.0', port=port, debug=False)
|
|
|
|
| 1 |
from flask import Flask, render_template, request, redirect, url_for, flash, session, jsonify
|
|
|
|
| 2 |
from werkzeug.middleware.proxy_fix import ProxyFix
|
| 3 |
import os
|
| 4 |
import gc
|
|
|
|
| 42 |
# Initialize Flask app
|
| 43 |
app = Flask(__name__, static_folder='app/static', template_folder='app/templates')
|
| 44 |
|
| 45 |
+
# CRITICAL FIX: Enhanced secret key and session config for Hugging Face
|
| 46 |
+
app.secret_key = os.environ.get('SECRET_KEY', 'huggingface-face-recognition-super-secret-key-2025')
|
| 47 |
|
| 48 |
+
# CRITICAL FIX: Optimized session config for Hugging Face Spaces
|
| 49 |
app.config.update({
|
| 50 |
'PERMANENT_SESSION_LIFETIME': timedelta(hours=2),
|
| 51 |
+
'SESSION_COOKIE_NAME': 'face_app_session',
|
| 52 |
'SESSION_COOKIE_HTTPONLY': True,
|
| 53 |
+
'SESSION_COOKIE_SECURE': False, # Hugging Face uses HTTP internally
|
| 54 |
+
'SESSION_COOKIE_SAMESITE': None, # Critical for Hugging Face iframe
|
| 55 |
+
'SESSION_REFRESH_EACH_REQUEST': False, # Prevent session reset
|
| 56 |
'SESSION_COOKIE_DOMAIN': None,
|
| 57 |
+
'SESSION_COOKIE_PATH': '/',
|
| 58 |
+
'SEND_FILE_MAX_AGE_DEFAULT': 0 # Disable caching
|
| 59 |
})
|
| 60 |
|
| 61 |
# CRITICAL FIX: Add ProxyFix middleware for Hugging Face reverse proxy
|
|
|
|
| 69 |
|
| 70 |
print("Flask app initialized with ProxyFix middleware and built-in cookie sessions")
|
| 71 |
|
|
|
|
| 72 |
# Create temporary directory for image processing
|
| 73 |
TEMP_DIR = tempfile.mkdtemp()
|
| 74 |
|
|
|
|
| 560 |
|
| 561 |
print(f"=== LOGIN DEBUG ===")
|
| 562 |
print(f"Student ID: {student_id}")
|
| 563 |
+
print(f"Request headers: {dict(request.headers)}")
|
| 564 |
+
print(f"User-Agent: {request.headers.get('User-Agent', 'N/A')}")
|
| 565 |
|
| 566 |
if not student_id or not password:
|
| 567 |
flash('Student ID and password are required.', 'danger')
|
|
|
|
| 571 |
print(f"Student found: {bool(student)}")
|
| 572 |
|
| 573 |
if student and student.get('password') == password:
|
| 574 |
+
# CRITICAL FIX: Clear and set session with permanent flag
|
| 575 |
session.clear()
|
| 576 |
+
session.permanent = True # Make session permanent FIRST
|
| 577 |
+
|
| 578 |
session['logged_in'] = True
|
| 579 |
session['user_type'] = 'student'
|
| 580 |
session['student_id'] = student_id
|
| 581 |
session['name'] = student.get('name', 'Unknown')
|
| 582 |
+
session['login_time'] = datetime.now().isoformat()
|
| 583 |
|
| 584 |
print(f"Session after setting: {dict(session)}")
|
| 585 |
+
print(f"Session permanent: {session.permanent}")
|
| 586 |
+
print(f"Session modified: {session.modified}")
|
| 587 |
+
|
| 588 |
flash('Login successful!', 'success')
|
| 589 |
|
| 590 |
+
# Create response and explicitly save session
|
| 591 |
+
response = redirect(url_for('dashboard'))
|
| 592 |
+
|
| 593 |
+
# Add session cookie headers manually for debugging
|
| 594 |
+
response.set_cookie(
|
| 595 |
+
'debug_session',
|
| 596 |
+
f"logged_in_at_{int(datetime.now().timestamp())}",
|
| 597 |
+
max_age=3600,
|
| 598 |
+
httponly=False
|
| 599 |
+
)
|
| 600 |
+
|
| 601 |
+
return response
|
| 602 |
else:
|
| 603 |
print("Invalid credentials")
|
| 604 |
flash('Invalid credentials. Please try again.', 'danger')
|
|
|
|
| 609 |
flash(f'Login failed: {str(e)}', 'danger')
|
| 610 |
return redirect(url_for('login_page'))
|
| 611 |
|
|
|
|
| 612 |
@app.route('/face-login', methods=['POST'])
|
| 613 |
def face_login():
|
| 614 |
try:
|
|
|
|
| 663 |
if result["verified"]:
|
| 664 |
# CRITICAL FIX: Simplified session setting
|
| 665 |
session.clear()
|
| 666 |
+
session.permanent = True
|
| 667 |
session['logged_in'] = True
|
| 668 |
session['user_type'] = face_role
|
| 669 |
session[id_field] = user[id_field]
|
| 670 |
session['name'] = user.get('name', 'Unknown')
|
| 671 |
+
session['login_time'] = datetime.now().isoformat()
|
| 672 |
|
| 673 |
print(f"Face login successful for {user.get('name')}, Session: {dict(session)}")
|
| 674 |
flash('Face login successful!', 'success')
|
|
|
|
| 707 |
flash(f'Face login failed: {str(e)}', 'danger')
|
| 708 |
return redirect(url_for('login_page'))
|
| 709 |
|
|
|
|
| 710 |
@app.route('/auto-face-login', methods=['POST'])
|
| 711 |
def auto_face_login():
|
| 712 |
"""Enhanced auto face login with role support"""
|
|
|
|
| 757 |
if result["verified"]:
|
| 758 |
# Clear any existing session
|
| 759 |
session.clear()
|
| 760 |
+
session.permanent = True
|
|
|
|
| 761 |
session['logged_in'] = True
|
| 762 |
session['user_type'] = face_role
|
| 763 |
session[id_field] = user[id_field]
|
| 764 |
session['name'] = user.get('name', 'Unknown')
|
| 765 |
+
session['login_time'] = datetime.now().isoformat()
|
|
|
|
| 766 |
|
| 767 |
print(f"Auto face login successful for {user.get('name')}, Session: {dict(session)}")
|
| 768 |
|
|
|
|
| 812 |
@app.route('/dashboard')
|
| 813 |
def dashboard():
|
| 814 |
print(f"=== DASHBOARD DEBUG ===")
|
| 815 |
+
print(f"Request headers: {dict(request.headers)}")
|
| 816 |
+
print(f"Request cookies: {dict(request.cookies)}")
|
| 817 |
print(f"Session data: {dict(session)}")
|
| 818 |
print(f"Session keys: {list(session.keys())}")
|
| 819 |
+
print(f"Session permanent: {getattr(session, 'permanent', 'N/A')}")
|
| 820 |
+
print(f"Session modified: {getattr(session, 'modified', 'N/A')}")
|
| 821 |
+
print(f"User-Agent: {request.headers.get('User-Agent', 'N/A')}")
|
| 822 |
|
| 823 |
+
# Check each session key individually
|
| 824 |
logged_in = session.get('logged_in')
|
| 825 |
user_type = session.get('user_type')
|
| 826 |
+
student_id = session.get('student_id')
|
| 827 |
|
| 828 |
print(f"logged_in: {logged_in} (type: {type(logged_in)})")
|
| 829 |
print(f"user_type: {user_type} (type: {type(user_type)})")
|
| 830 |
+
print(f"student_id: {student_id} (type: {type(student_id)})")
|
| 831 |
|
| 832 |
if not logged_in or user_type != 'student':
|
| 833 |
print("=== SESSION CHECK FAILED ===")
|
| 834 |
+
print("Possible causes:")
|
| 835 |
+
print("1. Session cookie not being sent")
|
| 836 |
+
print("2. Session data not persisting")
|
| 837 |
+
print("3. Different user agents between requests")
|
| 838 |
flash('Please log in to access the dashboard.', 'info')
|
| 839 |
return redirect(url_for('login_page'))
|
| 840 |
|
| 841 |
try:
|
|
|
|
|
|
|
|
|
|
| 842 |
student = students_collection.find_one({'student_id': student_id})
|
| 843 |
if not student:
|
| 844 |
print("Student not found in database")
|
|
|
|
| 863 |
flash(f'Error loading dashboard: {str(e)}', 'danger')
|
| 864 |
return redirect(url_for('login_page'))
|
| 865 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 866 |
@app.route('/mark-attendance', methods=['POST'])
|
| 867 |
def mark_attendance():
|
| 868 |
if 'logged_in' not in session or session.get('user_type') != 'student':
|
|
|
|
| 1175 |
if teacher and teacher.get('password') == password:
|
| 1176 |
# Clear any existing session
|
| 1177 |
session.clear()
|
| 1178 |
+
session.permanent = True
|
|
|
|
| 1179 |
session['logged_in'] = True
|
| 1180 |
session['user_type'] = 'teacher'
|
| 1181 |
session['teacher_id'] = teacher_id
|
| 1182 |
session['name'] = teacher.get('name', 'Unknown')
|
| 1183 |
+
session['login_time'] = datetime.now().isoformat()
|
|
|
|
| 1184 |
|
| 1185 |
print(f"Teacher session set: {dict(session)}")
|
| 1186 |
flash('Login successful!', 'success')
|
|
|
|
| 1334 |
'status': 'healthy',
|
| 1335 |
'platform': 'hugging_face',
|
| 1336 |
'session_working': bool(session.get('logged_in')),
|
| 1337 |
+
'session_storage': 'built-in_cookies', # Fixed: removed SESSION_TYPE reference
|
| 1338 |
'proxy_fix': 'enabled',
|
| 1339 |
'session_keys': list(session.keys()),
|
| 1340 |
'timestamp': datetime.now().isoformat()
|
|
|
|
| 1347 |
'session_keys': list(session.keys()),
|
| 1348 |
'cookies': dict(request.cookies),
|
| 1349 |
'headers': dict(request.headers),
|
| 1350 |
+
'session_storage': 'built-in_cookies',
|
|
|
|
| 1351 |
'proxy_fix': 'enabled',
|
| 1352 |
'session_modified': getattr(session, 'modified', 'N/A'),
|
| 1353 |
'session_permanent': getattr(session, 'permanent', 'N/A')
|
| 1354 |
})
|
| 1355 |
|
| 1356 |
+
@app.route('/debug-session-detailed')
|
| 1357 |
+
def debug_session_detailed():
|
| 1358 |
+
return jsonify({
|
| 1359 |
+
'session_data': dict(session),
|
| 1360 |
+
'session_keys': list(session.keys()),
|
| 1361 |
+
'cookies_received': dict(request.cookies),
|
| 1362 |
+
'headers': dict(request.headers),
|
| 1363 |
+
'user_agent': request.headers.get('User-Agent'),
|
| 1364 |
+
'remote_addr': request.remote_addr,
|
| 1365 |
+
'session_permanent': getattr(session, 'permanent', 'N/A'),
|
| 1366 |
+
'session_modified': getattr(session, 'modified', 'N/A'),
|
| 1367 |
+
'flask_secret_key_length': len(app.secret_key),
|
| 1368 |
+
'session_interface_type': str(type(app.session_interface)),
|
| 1369 |
+
'timestamp': datetime.now().isoformat()
|
| 1370 |
+
})
|
| 1371 |
+
|
| 1372 |
@app.route('/test-session')
|
| 1373 |
def test_session():
|
| 1374 |
"""Test session functionality"""
|
|
|
|
| 1376 |
session['test_data'] = 'working'
|
| 1377 |
session['timestamp'] = datetime.now().isoformat()
|
| 1378 |
session.permanent = True
|
|
|
|
| 1379 |
|
| 1380 |
return jsonify({
|
| 1381 |
'message': 'Session test completed',
|
|
|
|
| 1395 |
# MAIN APPLICATION ENTRY POINT
|
| 1396 |
if __name__ == '__main__':
|
| 1397 |
port = int(os.environ.get('PORT', 7860)) # Hugging Face uses port 7860
|
| 1398 |
+
print(f"Starting Flask app on port {port} with ProxyFix middleware and built-in sessions")
|
| 1399 |
app.run(host='0.0.0.0', port=port, debug=False)
|