from flask import Blueprint, request, jsonify, current_app from flask_jwt_extended import jwt_required, get_jwt_identity from 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() # Check if Supabase client is initialized if not hasattr(current_app, 'supabase') or current_app.supabase is None: # Add CORS headers to error response 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 # Fetch accounts from Supabase response = ( current_app.supabase .table("Social_network") .select("*") .eq("id_utilisateur", user_id) .execute() ) accounts = response.data if response.data else [] # Add CORS headers explicitly 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"Get accounts error: {str(e)}") # Add CORS headers to error response 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() # Validate required fields 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'] # For LinkedIn, initiate OAuth flow if social_network.lower() == 'linkedin': linkedin_service = LinkedInService() # Generate a random state for security state = secrets.token_urlsafe(32) # Store state in session or database for verification later # For now, we'll return it to the frontend authorization_url = linkedin_service.get_authorization_url(state) return jsonify({ 'success': True, 'message': 'Please authenticate with LinkedIn', 'authorization_url': authorization_url, 'state': state }), 200 else: return jsonify({ 'success': False, 'message': 'Unsupported social network' }), 400 except Exception as e: current_app.logger.error(f"Add account error: {str(e)}") 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() # Validate required fields 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'] # Verify state (in a real implementation, you would check against stored state) # For now, we'll skip this verification if social_network.lower() == 'linkedin': linkedin_service = LinkedInService() # Exchange code for access token token_response = linkedin_service.get_access_token(code) access_token = token_response['access_token'] # Get user info user_info = linkedin_service.get_user_info(access_token) # Store account info in Supabase response = ( current_app.supabase .table("Social_network") .insert({ "social_network": social_network, "account_name": user_info.get('name', 'LinkedIn Account'), "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') }) .execute() ) if response.data: return jsonify({ 'success': True, 'message': 'Account linked successfully', 'account': response.data[0] }), 200 else: return jsonify({ 'success': False, 'message': 'Failed to link account' }), 500 else: return jsonify({ 'success': False, 'message': 'Unsupported social network' }), 400 except Exception as e: current_app.logger.error(f"OAuth callback error: {str(e)}") return jsonify({ 'success': False, 'message': f'An error occurred during OAuth callback: {str(e)}' }), 500 @accounts_bp.route('/', methods=['OPTIONS']) def handle_account_options(account_id): """Handle OPTIONS requests for preflight CORS checks for specific account.""" return '', 200 @accounts_bp.route('/', 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() # Delete account from Supabase 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('//primary', methods=['OPTIONS']) def handle_primary_options(account_id): """Handle OPTIONS requests for preflight CORS checks for primary account.""" return '', 200 @accounts_bp.route('//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() # First, get all accounts for this user response = ( current_app.supabase .table("Social_network") .select("*") .eq("id_utilisateur", user_id) .execute() ) accounts = response.data if response.data else [] # Check if account exists and belongs to user 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 # For now, we'll just return success # In a real implementation, you might want to add a 'is_primary' field # and update all accounts accordingly 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