Spaces:
Runtime error
Runtime error
| """ | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β β | |
| β FLASK INTEGRATION for Authentication Plugin (FIXED) β | |
| β β | |
| β FIXES: β | |
| β - User wird ERST nach Verifizierung gespeichert β | |
| β - Nach Login -> Redirect zu index_ultimate.html β | |
| β - Bessere Fehlerbehandlung β | |
| β β | |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| """ | |
| from flask import Flask, request, jsonify, session, redirect, send_file | |
| from functools import wraps | |
| import sys | |
| import logging | |
| # Import auth plugin | |
| try: | |
| from plugins.auth_plugin import AuthPlugin | |
| except ImportError: | |
| sys.path.insert(0, '.') | |
| from plugins.auth_plugin import AuthPlugin | |
| # Initialize plugin | |
| auth_plugin = AuthPlugin() | |
| logger = logging.getLogger(__name__) | |
| # Store pending registrations (email -> verification code) | |
| pending_registrations = {} | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # DECORATOR FOR PROTECTED ROUTES | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def require_auth(f): | |
| """Decorator to protect routes - requires valid token""" | |
| def decorated_function(*args, **kwargs): | |
| # Get token from header or query parameter | |
| token = request.headers.get('Authorization') | |
| if token and token.startswith('Bearer '): | |
| token = token[7:] # Remove 'Bearer ' prefix | |
| if not token: | |
| token = request.args.get('token') or request.json.get('token') if request.json else None | |
| if not token: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Authentication required', | |
| 'message': 'Bitte erst anmelden' | |
| }), 401 | |
| # Validate token | |
| valid, email = auth_plugin.validate_token(token) | |
| if not valid: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Invalid or expired token', | |
| 'message': 'Session abgelaufen, bitte neu anmelden' | |
| }), 401 | |
| # Add email to request context | |
| request.user_email = email | |
| return f(*args, **kwargs) | |
| return decorated_function | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # FLASK ROUTES | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def register_auth_routes(app: Flask): | |
| """Register authentication routes with Flask app""" | |
| def auth_register(): | |
| """ | |
| Register new user - IMPROVED VERSION | |
| User wird NICHT sofort gespeichert, nur Email + Code erstellt | |
| """ | |
| try: | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'No data provided' | |
| }), 400 | |
| email = data.get('email', '').strip().lower() | |
| password = data.get('password', '') | |
| username = data.get('username', '').strip() | |
| if not email or not password: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Email und Passwort erforderlich' | |
| }), 400 | |
| # Check if already registered AND verified | |
| if email in auth_plugin.users and auth_plugin.users[email].verified: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Email bereits registriert und verifiziert. Bitte melde dich an.' | |
| }), 400 | |
| # Validate password | |
| if len(password) < 8: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Passwort muss mindestens 8 Zeichen lang sein' | |
| }), 400 | |
| # Store pending registration (don't create user yet!) | |
| pending_registrations[email] = { | |
| 'password': password, | |
| 'username': username or None | |
| } | |
| # Generate verification code | |
| import time | |
| import secrets | |
| import hashlib | |
| from plugins.auth_plugin import VerificationCode, AuthConfig | |
| code = ''.join([str(secrets.randbelow(10)) for _ in range(6)]) | |
| expires_at = time.time() + (AuthConfig.VERIFICATION_EXPIRY_MINUTES * 60) | |
| verification = VerificationCode( | |
| email=email, | |
| code=code, | |
| created_at=time.time(), | |
| expires_at=expires_at | |
| ) | |
| auth_plugin.verifications[email] = verification | |
| auth_plugin._save_verifications() | |
| # Try to send email | |
| email_sent = auth_plugin.email_service.send_verification_email(email, code) | |
| if email_sent: | |
| logger.info(f"β Verification code sent to {email}") | |
| return jsonify({ | |
| 'success': True, | |
| 'message': f'Verifizierungscode wurde an {email} gesendet. Bitte ΓΌberprΓΌfe deine Emails.' | |
| }), 200 | |
| else: | |
| # Email failed, but code is saved - user can get it from JSON | |
| logger.warning(f"β Email failed for {email}, but code is saved in JSON") | |
| return jsonify({ | |
| 'success': True, | |
| 'message': f'Registrierung vorbereitet. WICHTIG: Email-Versand fehlgeschlagen! Code findest du in: noahski_data/auth/verifications.json' | |
| }), 200 | |
| except Exception as e: | |
| logger.error(f"Register endpoint error: {e}") | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_verify(): | |
| """ | |
| Verify email with code - IMPROVED VERSION | |
| User wird ERST JETZT erstellt! | |
| """ | |
| try: | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'No data provided' | |
| }), 400 | |
| email = data.get('email', '').strip().lower() | |
| code = data.get('code', '').strip() | |
| if not email or not code: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Email und Code erforderlich' | |
| }), 400 | |
| # Check if verification exists | |
| if email not in auth_plugin.verifications: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Keine Verifizierung gefunden. Bitte registriere dich erneut.' | |
| }), 400 | |
| verification = auth_plugin.verifications[email] | |
| # Check if expired | |
| if not verification.is_valid(): | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Verifizierungscode abgelaufen oder zu viele Versuche' | |
| }), 400 | |
| # Increment attempts | |
| verification.attempts += 1 | |
| auth_plugin._save_verifications() | |
| # Check code | |
| if verification.code != code: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': f'Falscher Code (Versuch {verification.attempts}/3)' | |
| }), 400 | |
| # Code is correct! NOW create the user | |
| if email not in pending_registrations: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Registrierung nicht gefunden. Bitte registriere dich erneut.' | |
| }), 400 | |
| # Get pending registration data | |
| reg_data = pending_registrations[email] | |
| # Create user NOW (verified from the start) | |
| import time | |
| from plugins.auth_plugin import User | |
| user = User( | |
| email=email, | |
| password_hash=auth_plugin._hash_password(reg_data['password']), | |
| created_at=time.time(), | |
| verified=True, # Already verified! | |
| username=reg_data['username'] | |
| ) | |
| auth_plugin.users[email] = user | |
| auth_plugin._save_users() | |
| # Clean up | |
| del auth_plugin.verifications[email] | |
| auth_plugin._save_verifications() | |
| del pending_registrations[email] | |
| # Send welcome email | |
| auth_plugin.email_service.send_welcome_email(email, user.username) | |
| logger.info(f"β User verified and created: {email}") | |
| return jsonify({ | |
| 'success': True, | |
| 'message': 'Email erfolgreich verifiziert! Du kannst dich jetzt anmelden.' | |
| }), 200 | |
| except Exception as e: | |
| logger.error(f"Verify endpoint error: {e}") | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_resend(): | |
| """Resend verification code""" | |
| try: | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'No data provided' | |
| }), 400 | |
| email = data.get('email', '').strip().lower() | |
| if not email: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Email erforderlich' | |
| }), 400 | |
| # Check if pending registration exists | |
| if email not in pending_registrations: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Keine ausstehende Registrierung gefunden' | |
| }), 400 | |
| # Generate new code | |
| import time | |
| import secrets | |
| from plugins.auth_plugin import VerificationCode, AuthConfig | |
| code = ''.join([str(secrets.randbelow(10)) for _ in range(6)]) | |
| expires_at = time.time() + (AuthConfig.VERIFICATION_EXPIRY_MINUTES * 60) | |
| verification = VerificationCode( | |
| email=email, | |
| code=code, | |
| created_at=time.time(), | |
| expires_at=expires_at | |
| ) | |
| auth_plugin.verifications[email] = verification | |
| auth_plugin._save_verifications() | |
| # Send email | |
| email_sent = auth_plugin.email_service.send_verification_email(email, code) | |
| if email_sent: | |
| return jsonify({ | |
| 'success': True, | |
| 'message': 'Neuer Verifizierungscode wurde gesendet' | |
| }), 200 | |
| else: | |
| return jsonify({ | |
| 'success': True, | |
| 'message': 'Code generiert, aber Email-Versand fehlgeschlagen. Code in noahski_data/auth/verifications.json' | |
| }), 200 | |
| except Exception as e: | |
| logger.error(f"Resend error: {e}") | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_login(): | |
| """Login user - IMPROVED VERSION with redirect info""" | |
| try: | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'No data provided' | |
| }), 400 | |
| email = data.get('email', '').strip().lower() | |
| password = data.get('password', '') | |
| if not email or not password: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Email und Passwort erforderlich' | |
| }), 400 | |
| # Get client info | |
| ip_address = request.remote_addr | |
| user_agent = request.headers.get('User-Agent') | |
| success, message, token = auth_plugin.login(email, password, ip_address, user_agent) | |
| response_data = { | |
| 'success': success, | |
| 'message': message | |
| } | |
| if success and token: | |
| response_data['token'] = token | |
| response_data['user'] = auth_plugin.get_user_info(email) | |
| response_data['redirect'] = '/' # Redirect to main page after login | |
| return jsonify(response_data), 200 if success else 401 | |
| except Exception as e: | |
| logger.error(f"Login endpoint error: {e}") | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_logout(): | |
| """Logout user""" | |
| try: | |
| token = request.headers.get('Authorization', '')[7:] # Remove 'Bearer ' | |
| success, message = auth_plugin.logout(token) | |
| return jsonify({ | |
| 'success': success, | |
| 'message': message | |
| }), 200 | |
| except Exception as e: | |
| logger.error(f"Logout endpoint error: {e}") | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_validate(): | |
| """Validate token""" | |
| try: | |
| token = request.headers.get('Authorization') | |
| if token and token.startswith('Bearer '): | |
| token = token[7:] | |
| if not token: | |
| token = request.args.get('token') or (request.json.get('token') if request.json else None) | |
| if not token: | |
| return jsonify({ | |
| 'success': False, | |
| 'valid': False, | |
| 'error': 'No token provided' | |
| }), 400 | |
| valid, email = auth_plugin.validate_token(token) | |
| response_data = { | |
| 'success': True, | |
| 'valid': valid | |
| } | |
| if valid: | |
| response_data['user'] = auth_plugin.get_user_info(email) | |
| return jsonify(response_data), 200 | |
| except Exception as e: | |
| logger.error(f"Validate endpoint error: {e}") | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_user_info(): | |
| """Get current user info""" | |
| try: | |
| email = request.user_email | |
| user_info = auth_plugin.get_user_info(email) | |
| if user_info: | |
| return jsonify({ | |
| 'success': True, | |
| 'user': user_info | |
| }), 200 | |
| else: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'User not found' | |
| }), 404 | |
| except Exception as e: | |
| logger.error(f"User info endpoint error: {e}") | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_stats(): | |
| """Get authentication statistics""" | |
| try: | |
| stats = auth_plugin.get_stats() | |
| stats['pending_registrations'] = len(pending_registrations) | |
| return jsonify({ | |
| 'success': True, | |
| 'stats': stats | |
| }), 200 | |
| except Exception as e: | |
| logger.error(f"Stats endpoint error: {e}") | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_settings_get(): | |
| """Get user settings - FIXED VERSION""" | |
| try: | |
| email = request.user_email | |
| # Get user data from auth plugin | |
| user_data = auth_plugin.get_user(email) | |
| if not user_data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'User not found' | |
| }), 404 | |
| # Return user settings (exclude password) | |
| settings = { | |
| 'email': user_data.get('email', email), | |
| 'username': user_data.get('username', ''), | |
| 'theme': user_data.get('theme', 'dark'), | |
| 'language': user_data.get('language', 'de'), | |
| 'notifications': user_data.get('notifications', True), | |
| 'email_verified': user_data.get('email_verified', False) | |
| } | |
| return jsonify({ | |
| 'success': True, | |
| 'settings': settings | |
| }) | |
| except Exception as e: | |
| logger.error(f"Settings GET error: {e}", exc_info=True) | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_settings_update(): | |
| """Update user settings - FIXED VERSION""" | |
| try: | |
| email = request.user_email | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'No data provided' | |
| }), 400 | |
| # Get current user data | |
| user_data = auth_plugin.get_user(email) | |
| if not user_data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'User not found' | |
| }), 404 | |
| # Update allowed fields | |
| allowed_fields = ['username', 'theme', 'language', 'notifications'] | |
| updated_fields = [] | |
| for field in allowed_fields: | |
| if field in data: | |
| user_data[field] = data[field] | |
| updated_fields.append(field) | |
| # Save updated user data | |
| auth_plugin.users[email] = user_data | |
| auth_plugin._save_users() | |
| return jsonify({ | |
| 'success': True, | |
| 'message': 'Settings updated successfully', | |
| 'updated_fields': updated_fields | |
| }) | |
| except Exception as e: | |
| logger.error(f"Settings UPDATE error: {e}", exc_info=True) | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_change_password(): | |
| """Change user password - FIXED VERSION""" | |
| try: | |
| import hashlib | |
| email = request.user_email | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'No data provided' | |
| }), 400 | |
| current_password = data.get('current_password', '') | |
| new_password = data.get('new_password', '') | |
| if not current_password or not new_password: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Current and new password required' | |
| }), 400 | |
| # Verify current password | |
| user_data = auth_plugin.get_user(email) | |
| if not user_data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'User not found' | |
| }), 404 | |
| # Check current password | |
| current_hash = hashlib.sha256(current_password.encode()).hexdigest() | |
| if user_data.get('password') != current_hash: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Current password is incorrect' | |
| }), 400 | |
| # Update password | |
| new_hash = hashlib.sha256(new_password.encode()).hexdigest() | |
| user_data['password'] = new_hash | |
| # Save | |
| auth_plugin.users[email] = user_data | |
| auth_plugin._save_users() | |
| return jsonify({ | |
| 'success': True, | |
| 'message': 'Password changed successfully' | |
| }) | |
| except Exception as e: | |
| logger.error(f"Password change error: {e}", exc_info=True) | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_user_stats(): | |
| """Get user-specific statistics - FIXED VERSION""" | |
| try: | |
| from datetime import datetime | |
| import time | |
| email = request.user_email | |
| # Get user data | |
| user_data = auth_plugin.get_user(email) | |
| if not user_data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'User not found' | |
| }), 404 | |
| # Calculate stats | |
| join_date = datetime.fromtimestamp(user_data.get('created_at', time.time())) | |
| days_since_join = (datetime.now() - join_date).days | |
| stats = { | |
| 'username': user_data.get('username', ''), | |
| 'email': email, | |
| 'member_since': join_date.strftime('%Y-%m-%d'), | |
| 'days_active': days_since_join, | |
| 'total_messages': user_data.get('total_messages', 0), | |
| 'total_images': user_data.get('total_images', 0), | |
| 'email_verified': user_data.get('email_verified', False), | |
| 'theme': user_data.get('theme', 'dark'), | |
| 'language': user_data.get('language', 'de') | |
| } | |
| return jsonify({ | |
| 'success': True, | |
| 'stats': stats | |
| }) | |
| except Exception as e: | |
| logger.error(f"User stats error: {e}", exc_info=True) | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| def auth_resend_code(): | |
| """Resend verification code - NEW""" | |
| try: | |
| import time | |
| import random | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'No data provided' | |
| }), 400 | |
| email = data.get('email', '').strip().lower() | |
| if not email: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'Email required' | |
| }), 400 | |
| # Check if pending verification exists | |
| if email not in pending_registrations: | |
| return jsonify({ | |
| 'success': False, | |
| 'error': 'No pending verification for this email' | |
| }), 400 | |
| # Generate new code | |
| new_code = str(random.randint(100000, 999999)) | |
| # Update pending data | |
| pending_registrations[email]['code'] = new_code | |
| pending_registrations[email]['timestamp'] = time.time() | |
| logger.info(f"π Resent verification code for {email}: {new_code}") | |
| return jsonify({ | |
| 'success': True, | |
| 'message': 'Verification code resent', | |
| 'code': new_code # In production, send via email | |
| }) | |
| except Exception as e: | |
| logger.error(f"Resend code error: {e}", exc_info=True) | |
| return jsonify({ | |
| 'success': False, | |
| 'error': str(e) | |
| }), 500 | |
| logger.info("β Authentication routes registered (IMPROVED VERSION WITH ALL FIXES)") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # EXPORT | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| __all__ = ['register_auth_routes', 'require_auth', 'auth_plugin'] |