| from flask import Blueprint, request, jsonify, current_app |
| from flask_jwt_extended import jwt_required, get_jwt_identity |
| from backend.services.linkedin_service import LinkedInService |
| import secrets |
|
|
| accounts_bp = Blueprint('accounts', __name__) |
|
|
| @accounts_bp.route('/', methods=['OPTIONS']) |
| @accounts_bp.route('', methods=['OPTIONS']) |
| def handle_options(): |
| """Handle OPTIONS requests for preflight CORS checks.""" |
| return '', 200 |
|
|
| @accounts_bp.route('/', methods=['GET']) |
| @accounts_bp.route('', methods=['GET']) |
| @jwt_required() |
| def get_accounts(): |
| """ |
| Get all social media accounts for the current user. |
| |
| Returns: |
| JSON: List of social media accounts |
| """ |
| try: |
| user_id = get_jwt_identity() |
| |
| |
| if not hasattr(current_app, 'supabase') or current_app.supabase is None: |
| |
| response_data = jsonify({ |
| 'success': False, |
| 'message': 'Database connection not initialized' |
| }) |
| response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') |
| response_data.headers.add('Access-Control-Allow-Credentials', 'true') |
| return response_data, 500 |
| |
| |
| response = ( |
| current_app.supabase |
| .table("Social_network") |
| .select("*") |
| .eq("id_utilisateur", user_id) |
| .execute() |
| ) |
| |
| accounts = response.data if response.data else [] |
| |
| |
| response_data = jsonify({ |
| 'success': True, |
| 'accounts': accounts |
| }) |
| response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') |
| response_data.headers.add('Access-Control-Allow-Credentials', 'true') |
| return response_data, 200 |
| |
| except Exception as e: |
| current_app.logger.error(f"π± [Accounts] Get accounts error: {str(e)}") |
| current_app.logger.error(f"π± [Accounts] Error type: {type(e)}") |
| import traceback |
| current_app.logger.error(f"π± [Accounts] Traceback: {traceback.format_exc()}") |
| |
| response_data = jsonify({ |
| 'success': False, |
| 'message': 'An error occurred while fetching accounts' |
| }) |
| response_data.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000') |
| response_data.headers.add('Access-Control-Allow-Credentials', 'true') |
| return response_data, 500 |
|
|
| @accounts_bp.route('/', methods=['POST']) |
| @accounts_bp.route('', methods=['POST']) |
| @jwt_required() |
| def add_account(): |
| """ |
| Add a new social media account for the current user. |
| |
| Request Body: |
| account_name (str): Account name |
| social_network (str): Social network name |
| |
| Returns: |
| JSON: Add account result |
| """ |
| try: |
| user_id = get_jwt_identity() |
| data = request.get_json() |
| |
| |
| if not data or not all(k in data for k in ('account_name', 'social_network')): |
| return jsonify({ |
| 'success': False, |
| 'message': 'Account name and social network are required' |
| }), 400 |
| |
| account_name = data['account_name'] |
| social_network = data['social_network'] |
| |
| |
| if social_network.lower() == 'linkedin': |
| linkedin_service = LinkedInService() |
| |
| state = secrets.token_urlsafe(32) |
| |
| |
| |
| try: |
| authorization_url = linkedin_service.get_authorization_url(state) |
| except Exception as oauth_error: |
| return jsonify({ |
| 'success': False, |
| 'message': f'Failed to generate authorization URL: {str(oauth_error)}' |
| }), 500 |
| |
| return jsonify({ |
| 'success': True, |
| 'message': 'Please authenticate with LinkedIn', |
| 'authorization_url': authorization_url, |
| 'state': state |
| }), 200 |
| else: |
| current_app.logger.warning(f"β [Accounts] Unsupported social network: {social_network}") |
| return jsonify({ |
| 'success': False, |
| 'message': 'Unsupported social network' |
| }), 400 |
| |
| except Exception as e: |
| current_app.logger.error(f"β [Accounts] Add account error: {str(e)}") |
| current_app.logger.error(f"β [Accounts] Error type: {type(e)}") |
| import traceback |
| current_app.logger.error(f"β [Accounts] Traceback: {traceback.format_exc()}") |
| return jsonify({ |
| 'success': False, |
| 'message': f'An error occurred while adding account: {str(e)}' |
| }), 500 |
|
|
| @accounts_bp.route('/callback', methods=['POST']) |
| @jwt_required() |
| def handle_oauth_callback(): |
| """ |
| Handle OAuth callback from social network. |
| |
| Request Body: |
| code (str): Authorization code |
| state (str): State parameter |
| social_network (str): Social network name |
| |
| Returns: |
| JSON: OAuth callback result |
| """ |
| try: |
| user_id = get_jwt_identity() |
| data = request.get_json() |
| |
| |
| if not data or not all(k in data for k in ('code', 'state', 'social_network')): |
| return jsonify({ |
| 'success': False, |
| 'message': 'Code, state, and social network are required' |
| }), 400 |
| |
| code = data['code'] |
| state = data['state'] |
| social_network = data['social_network'] |
| |
| |
| |
| |
| if social_network.lower() == 'linkedin': |
| |
| if not hasattr(current_app, 'supabase') or current_app.supabase is None: |
| return jsonify({ |
| 'success': False, |
| 'message': 'Database connection not available' |
| }), 500 |
| |
| linkedin_service = LinkedInService() |
| |
| |
| current_app.logger.info(f"π [OAuth] Exchanging code for access token...") |
| try: |
| token_response = linkedin_service.get_access_token(code) |
| access_token = token_response['access_token'] |
| current_app.logger.info(f"π [OAuth] Token exchange successful. Token length: {len(access_token)}") |
| except Exception as token_error: |
| current_app.logger.error(f"π [OAuth] Token exchange failed: {str(token_error)}") |
| return jsonify({ |
| 'success': False, |
| 'message': f'Failed to exchange code for token: {str(token_error)}' |
| }), 500 |
| |
| |
| current_app.logger.info(f"π [OAuth] Fetching user info...") |
| try: |
| user_info = linkedin_service.get_user_info(access_token) |
| current_app.logger.info(f"π [OAuth] User info fetched: {user_info}") |
| except Exception as user_info_error: |
| current_app.logger.error(f"π [OAuth] User info fetch failed: {str(user_info_error)}") |
| return jsonify({ |
| 'success': False, |
| 'message': f'Failed to fetch user info: {str(user_info_error)}' |
| }), 500 |
| |
| |
| account_data = { |
| "social_network": social_network, |
| "account_name": user_info.get('given_name'), |
| "id_utilisateur": user_id, |
| "token": access_token, |
| "sub": user_info.get('sub'), |
| "given_name": user_info.get('given_name'), |
| "family_name": user_info.get('family_name'), |
| "picture": user_info.get('picture') |
| } |
| current_app.logger.info(f"π [OAuth] Prepared account data: {account_data}") |
| |
| |
| current_app.logger.info(f"π [OAuth] Inserting account into database...") |
| try: |
| response = ( |
| current_app.supabase |
| .table("Social_network") |
| .insert(account_data) |
| .execute() |
| ) |
| |
| |
| current_app.logger.info(f"π [OAuth] Database response: {response}") |
| current_app.logger.info(f"π [OAuth] Response data: {response.data}") |
| current_app.logger.info(f"π [OAuth] Response error: {getattr(response, 'error', None)}") |
| |
| if response.data: |
| current_app.logger.info(f"π [OAuth] Account linked successfully for user: {user_id}") |
| return jsonify({ |
| 'success': True, |
| 'message': 'Account linked successfully', |
| 'account': response.data[0] |
| }), 200 |
| else: |
| current_app.logger.error(f"π [OAuth] No data returned from database insertion for user: {user_id}") |
| return jsonify({ |
| 'success': False, |
| 'message': 'Failed to link account - no data returned' |
| }), 500 |
| |
| except Exception as db_error: |
| current_app.logger.error(f"π [OAuth] Database insertion failed: {str(db_error)}") |
| current_app.logger.error(f"π [OAuth] Database error type: {type(db_error)}") |
| return jsonify({ |
| 'success': False, |
| 'message': f'Failed to insert account into database: {str(db_error)}' |
| }), 500 |
| else: |
| current_app.logger.warning(f"π [OAuth] Unsupported social network: {social_network}") |
| return jsonify({ |
| 'success': False, |
| 'message': 'Unsupported social network' |
| }), 400 |
| |
| except Exception as e: |
| current_app.logger.error(f"π [OAuth] Unexpected error in callback: {str(e)}") |
| current_app.logger.error(f"π [OAuth] Error type: {type(e)}") |
| import traceback |
| current_app.logger.error(f"π [OAuth] Traceback: {traceback.format_exc()}") |
| return jsonify({ |
| 'success': False, |
| 'message': f'An error occurred during OAuth callback: {str(e)}' |
| }), 500 |
|
|
| @accounts_bp.route('/<account_id>', methods=['OPTIONS']) |
| def handle_account_options(account_id): |
| """Handle OPTIONS requests for preflight CORS checks for specific account.""" |
| return '', 200 |
|
|
| @accounts_bp.route('/<account_id>', methods=['DELETE']) |
| @jwt_required() |
| def delete_account(account_id): |
| """ |
| Delete a social media account. |
| |
| Path Parameters: |
| account_id (str): Account ID |
| |
| Returns: |
| JSON: Delete account result |
| """ |
| try: |
| user_id = get_jwt_identity() |
| |
| |
| response = ( |
| current_app.supabase |
| .table("Social_network") |
| .delete() |
| .eq("id", account_id) |
| .eq("id_utilisateur", user_id) |
| .execute() |
| ) |
| |
| if response.data: |
| return jsonify({ |
| 'success': True, |
| 'message': 'Account deleted successfully' |
| }), 200 |
| else: |
| return jsonify({ |
| 'success': False, |
| 'message': 'Account not found or unauthorized' |
| }), 404 |
| |
| except Exception as e: |
| current_app.logger.error(f"Delete account error: {str(e)}") |
| return jsonify({ |
| 'success': False, |
| 'message': 'An error occurred while deleting account' |
| }), 500 |
|
|
| @accounts_bp.route('/<account_id>/primary', methods=['OPTIONS']) |
| def handle_primary_options(account_id): |
| """Handle OPTIONS requests for preflight CORS checks for primary account.""" |
| return '', 200 |
|
|
| @accounts_bp.route('/<account_id>/primary', methods=['PUT']) |
| @jwt_required() |
| def set_primary_account(account_id): |
| """ |
| Set an account as primary for the user. |
| |
| Path Parameters: |
| account_id (str): Account ID |
| |
| Returns: |
| JSON: Update result |
| """ |
| try: |
| user_id = get_jwt_identity() |
| |
| |
| response = ( |
| current_app.supabase |
| .table("Social_network") |
| .select("*") |
| .eq("id_utilisateur", user_id) |
| .execute() |
| ) |
| |
| accounts = response.data if response.data else [] |
| |
| |
| account_exists = any(account['id'] == account_id and account['id_utilisateur'] == user_id for account in accounts) |
| |
| if not account_exists: |
| return jsonify({ |
| 'success': False, |
| 'message': 'Account not found or unauthorized' |
| }), 404 |
| |
| |
| |
| |
| |
| return jsonify({ |
| 'success': True, |
| 'message': 'Account set as primary successfully' |
| }), 200 |
| |
| except Exception as e: |
| current_app.logger.error(f"Set primary account error: {str(e)}") |
| return jsonify({ |
| 'success': False, |
| 'message': 'An error occurred while setting primary account' |
| }), 500 |
|
|
| @accounts_bp.route('/session-data', methods=['GET']) |
| @jwt_required() |
| def get_session_data(): |
| """ |
| Get OAuth data from session for frontend processing. |
| |
| Returns: |
| JSON: Session data or empty if not found |
| """ |
| try: |
| from flask import session |
| |
| |
| user_id = get_jwt_identity() |
| current_app.logger.info(f"π [Session] Session data request for user: {user_id}") |
| |
| oauth_data = session.get('oauth_data', None) |
| |
| if oauth_data: |
| current_app.logger.info(f"π [Session] OAuth data found for user: {user_id}") |
| current_app.logger.info(f"π [Session] OAuth data: {oauth_data}") |
| return jsonify({ |
| 'success': True, |
| 'oauth_data': oauth_data |
| }), |
| else: |
| current_app.logger.warning(f"π [Session] No OAuth data found in session for user: {user_id}") |
| current_app.logger.info(f"π [Session] Current session keys: {list(session.keys())}") |
| return jsonify({ |
| 'success': False, |
| 'message': 'No OAuth data found in session' |
| }), 404 |
| |
| except Exception as e: |
| current_app.logger.error(f"π [Session] Get session data error: {str(e)}") |
| current_app.logger.error(f"π [Session] Error type: {type(e)}") |
| import traceback |
| current_app.logger.error(f"π [Session] Traceback: {traceback.format_exc()}") |
| return jsonify({ |
| 'success': False, |
| 'message': 'An error occurred while retrieving session data' |
| }), 500 |