Lin / backend /api /accounts.py
Zelyanoth's picture
gg
c7d5529
raw
history blame
10.5 kB
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()
# 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('/<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()
# 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('/<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()
# 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