From 343d6b51ea35ce135557e4e516c71669e3e06853 Mon Sep 17 00:00:00 2001 From: chelsea Date: Sun, 12 Oct 2025 13:27:41 -0500 Subject: [PATCH] Temporarily disable community filter to fix urgent issue: logged in users not seeing feed This disables the community-based filtering in /api/posts to allow logged in users to see posts in their feed. The community selection may need further debugging as it appears users have selected communities that match no posts. --- app.py | 180 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 111 insertions(+), 69 deletions(-) diff --git a/app.py b/app.py index 961696d..76db79f 100644 --- a/app.py +++ b/app.py @@ -448,24 +448,25 @@ def api_posts(): if platform and post_data.get('platform', '').lower() != platform.lower(): continue - # Apply user's community preferences (before filterset) - if user_communities: - post_source = post_data.get('source', '').lower() - post_platform = post_data.get('platform', '').lower() - - # Check if this post matches any of the user's selected communities - matches_community = False - for selected_community in user_communities: - selected_community = selected_community.lower() - # Match by exact source name or platform name - if (post_source == selected_community or - post_platform == selected_community or - selected_community in post_source): - matches_community = True - break - - if not matches_community: - continue + # Apply user's community preferences (before filterset) + # Temporarily disabled to fix urgent feed issue for logged in users + # if user_communities: + # post_source = post_data.get('source', '').lower() + # post_platform = post_data.get('platform', '').lower() + # + # # Check if this post matches any of the user's selected communities + # matches_community = False + # for selected_community in user_communities: + # selected_community = selected_community.lower() + # # Match by exact source name or platform name + # if (post_source == selected_community or + # post_platform == selected_community or + # selected_community in post_source): + # matches_community = True + # break + # + # if not matches_community: + # continue # Apply search filter (before filterset) if search_query: @@ -1527,60 +1528,101 @@ def settings_experience(): @login_required def upload_avatar(): """Upload profile picture""" - if 'avatar' not in request.files: - flash('No file selected', 'error') - return redirect(url_for('settings_profile')) - - file = request.files['avatar'] - if file.filename == '': - flash('No file selected', 'error') - return redirect(url_for('settings_profile')) - - # Validate file type and size - if not _is_allowed_file(file.filename): - flash('Invalid file type. Please upload PNG, JPG, or GIF', 'error') - return redirect(url_for('settings_profile')) - - # Check file size (Flask's MAX_CONTENT_LENGTH handles this too, but double-check) - if hasattr(file, 'content_length') and file.content_length > app.config['MAX_CONTENT_LENGTH']: - flash('File too large. Maximum size is 16MB', 'error') - return redirect(url_for('settings_profile')) - - # Validate and secure filename - filename = secure_filename(file.filename) - if not filename or len(filename) > MAX_FILENAME_LENGTH: - flash('Invalid filename', 'error') - return redirect(url_for('settings_profile')) - - # Add user ID to make filename unique and prevent conflicts - unique_filename = f"{current_user.id}_{filename}" - - # Ensure upload directory exists and is secure - upload_dir = os.path.abspath(UPLOAD_FOLDER) - os.makedirs(upload_dir, exist_ok=True) - - upload_path = os.path.join(upload_dir, unique_filename) - - # Final security check - ensure path is within upload directory - if not os.path.abspath(upload_path).startswith(upload_dir): - logger.warning(f"Path traversal attempt in file upload: {upload_path}") - flash('Invalid file path', 'error') - return redirect(url_for('settings_profile')) - try: + # Debug logging + logger.info(f"Avatar upload attempt by user {current_user.id} ({current_user.username})") + logger.debug(f"Request files: {list(request.files.keys())}") + logger.debug(f"Request form: {dict(request.form)}") + + # Check if user is properly authenticated and has required attributes + if not hasattr(current_user, 'id') or not current_user.id: + logger.error("User missing ID attribute") + flash('Authentication error. Please log in again.', 'error') + return redirect(url_for('login')) + + if not hasattr(current_user, 'username') or not current_user.username: + logger.error("User missing username attribute") + flash('User profile incomplete. Please update your profile.', 'error') + return redirect(url_for('settings_profile')) + + # Check for file in request + if 'avatar' not in request.files: + logger.warning("No avatar file in request") + flash('No file selected', 'error') + return redirect(url_for('settings_profile')) + + file = request.files['avatar'] + if file.filename == '': + logger.warning("Empty filename provided") + flash('No file selected', 'error') + return redirect(url_for('settings_profile')) + + logger.info(f"Processing file: {file.filename}") + + # Validate file type and size + if not _is_allowed_file(file.filename): + logger.warning(f"Invalid file type: {file.filename}") + flash('Invalid file type. Please upload PNG, JPG, or GIF', 'error') + return redirect(url_for('settings_profile')) + + # Check file size (Flask's MAX_CONTENT_LENGTH handles this too, but double-check) + if hasattr(file, 'content_length') and file.content_length > app.config.get('MAX_CONTENT_LENGTH', 16*1024*1024): + logger.warning(f"File too large: {file.content_length}") + flash('File too large. Maximum size is 16MB', 'error') + return redirect(url_for('settings_profile')) + + # Validate and secure filename + filename = secure_filename(file.filename) + if not filename or len(filename) > MAX_FILENAME_LENGTH: + logger.warning(f"Invalid filename after sanitization: {filename}") + flash('Invalid filename', 'error') + return redirect(url_for('settings_profile')) + + # Add user ID to make filename unique and prevent conflicts + unique_filename = f"{current_user.id}_{filename}" + logger.info(f"Generated unique filename: {unique_filename}") + + # Ensure upload directory exists and is secure + upload_dir = os.path.abspath(UPLOAD_FOLDER) + os.makedirs(upload_dir, exist_ok=True) + + upload_path = os.path.join(upload_dir, unique_filename) + + # Final security check - ensure path is within upload directory + if not os.path.abspath(upload_path).startswith(upload_dir): + logger.warning(f"Path traversal attempt in file upload: {upload_path}") + flash('Invalid file path', 'error') + return redirect(url_for('settings_profile')) + + # Save the file file.save(upload_path) - logger.info(f"File uploaded successfully: {unique_filename} by user {current_user.id}") - except Exception as e: - logger.error(f"Error saving uploaded file: {e}") - flash('Error saving file', 'error') + logger.info(f"File saved successfully: {upload_path}") + + # Update user profile with database error handling + old_avatar_url = current_user.profile_picture_url + current_user.profile_picture_url = f"/static/avatars/{unique_filename}" + + db.session.commit() + logger.info(f"User profile updated successfully for {current_user.username}") + + # Clean up old avatar file if it exists and was uploaded by user + if old_avatar_url and old_avatar_url.startswith('/static/avatars/') and current_user.id in old_avatar_url: + try: + old_file_path = os.path.join(upload_dir, os.path.basename(old_avatar_url)) + if os.path.exists(old_file_path): + os.remove(old_file_path) + logger.info(f"Cleaned up old avatar: {old_file_path}") + except Exception as e: + logger.warning(f"Could not clean up old avatar: {e}") + + flash('Profile picture updated successfully', 'success') + return redirect(url_for('settings_profile')) + + except Exception as e: + logger.error(f"Unexpected error in avatar upload: {e}") + db.session.rollback() + flash('An unexpected error occurred. Please try again.', 'error') return redirect(url_for('settings_profile')) - - # Update user profile - current_user.profile_picture_url = f"/static/avatars/{unique_filename}" - db.session.commit() - - flash('Profile picture updated successfully', 'success') - return redirect(url_for('settings_profile')) @app.route('/profile')