Files
ALwrity/backend/services/oauth_token_monitoring_service.py

205 lines
8.4 KiB
Python

"""
OAuth Token Monitoring Service
Service for creating and managing OAuth token monitoring tasks.
"""
from datetime import datetime, timedelta
from typing import List, Optional
from sqlalchemy.orm import Session
from utils.logger_utils import get_service_logger
import os
# Use service logger for consistent logging (WARNING level visible in production)
logger = get_service_logger("oauth_token_monitoring")
from models.oauth_token_monitoring_models import OAuthTokenMonitoringTask
from services.gsc_service import GSCService
from services.integrations.bing_oauth import BingOAuthService
from services.integrations.wordpress_oauth import WordPressOAuthService
from services.integrations.wix_oauth import WixOAuthService
def get_connected_platforms(user_id: str) -> List[str]:
"""
Detect which platforms are connected for a user by checking token storage.
Checks:
- GSC: gsc_credentials table
- Bing: bing_oauth_tokens table
- WordPress: wordpress_oauth_tokens table
- Wix: wix_oauth_tokens table
Args:
user_id: User ID (Clerk string)
Returns:
List of connected platform identifiers: ['gsc', 'bing', 'wordpress', 'wix']
"""
connected = []
# Use DEBUG level for routine checks (called frequently by dashboard)
logger.debug(f"[OAuth Monitoring] Checking connected platforms for user: {user_id}")
try:
# Check GSC - use absolute database path
db_path = os.path.abspath("alwrity.db")
gsc_service = GSCService(db_path=db_path)
gsc_credentials = gsc_service.load_user_credentials(user_id)
if gsc_credentials:
connected.append('gsc')
logger.debug(f"[OAuth Monitoring] ✅ GSC connected for user {user_id}")
else:
logger.debug(f"[OAuth Monitoring] ❌ GSC not connected for user {user_id}")
except Exception as e:
logger.warning(f"[OAuth Monitoring] ⚠️ GSC check failed for user {user_id}: {e}", exc_info=True)
try:
# Check Bing - use absolute database path
db_path = os.path.abspath("alwrity.db")
bing_service = BingOAuthService(db_path=db_path)
token_status = bing_service.get_user_token_status(user_id)
has_active_tokens = token_status.get('has_active_tokens', False)
has_expired_tokens = token_status.get('has_expired_tokens', False)
expired_tokens = token_status.get('expired_tokens', [])
# Check if expired tokens have refresh tokens (can be refreshed)
has_refreshable_tokens = any(token.get('refresh_token') for token in expired_tokens)
# Consider connected if user has active tokens OR expired tokens with refresh tokens
if has_active_tokens or (has_expired_tokens and has_refreshable_tokens):
connected.append('bing')
logger.debug(f"[OAuth Monitoring] ✅ Bing connected for user {user_id}")
else:
logger.debug(f"[OAuth Monitoring] ❌ Bing not connected for user {user_id}")
except Exception as e:
logger.warning(f"[OAuth Monitoring] ⚠️ Bing check failed for user {user_id}: {e}", exc_info=True)
try:
# Check WordPress - use absolute database path
db_path = os.path.abspath("alwrity.db")
wordpress_service = WordPressOAuthService(db_path=db_path)
token_status = wordpress_service.get_user_token_status(user_id)
has_active_tokens = token_status.get('has_active_tokens', False)
has_tokens = token_status.get('has_tokens', False)
# Consider connected if user has any tokens (WordPress tokens may not have refresh tokens)
# If tokens exist, user was connected even if expired (may need re-auth)
if has_tokens:
connected.append('wordpress')
logger.debug(f"[OAuth Monitoring] ✅ WordPress connected for user {user_id}")
else:
logger.debug(f"[OAuth Monitoring] ❌ WordPress not connected for user {user_id}")
except Exception as e:
logger.warning(f"[OAuth Monitoring] ⚠️ WordPress check failed for user {user_id}: {e}", exc_info=True)
try:
# Check Wix - use absolute database path
db_path = os.path.abspath("alwrity.db")
wix_service = WixOAuthService(db_path=db_path)
token_status = wix_service.get_user_token_status(user_id)
has_active_tokens = token_status.get('has_active_tokens', False)
has_expired_tokens = token_status.get('has_expired_tokens', False)
expired_tokens = token_status.get('expired_tokens', [])
# Check if expired tokens have refresh tokens (can be refreshed)
has_refreshable_tokens = any(token.get('refresh_token') for token in expired_tokens)
# Consider connected if user has active tokens OR expired tokens with refresh tokens
if has_active_tokens or (has_expired_tokens and has_refreshable_tokens):
connected.append('wix')
logger.debug(f"[OAuth Monitoring] ✅ Wix connected for user {user_id}")
else:
logger.debug(f"[OAuth Monitoring] ❌ Wix not connected for user {user_id}")
except Exception as e:
logger.warning(f"[OAuth Monitoring] ⚠️ Wix check failed for user {user_id}: {e}", exc_info=True)
# Don't log here - let the caller log a formatted summary if needed
# This function is called frequently and should be silent
return connected
def create_oauth_monitoring_tasks(
user_id: str,
db: Session,
platforms: Optional[List[str]] = None
) -> List[OAuthTokenMonitoringTask]:
"""
Create OAuth token monitoring tasks for a user.
If platforms are not provided, automatically detects connected platforms.
Creates one task per platform with next_check set to 7 days from now.
Args:
user_id: User ID (Clerk string)
db: Database session
platforms: Optional list of platforms to create tasks for.
If None, auto-detects connected platforms.
Valid values: 'gsc', 'bing', 'wordpress', 'wix'
Returns:
List of created OAuthTokenMonitoringTask instances
"""
try:
# Auto-detect platforms if not provided
if platforms is None:
platforms = get_connected_platforms(user_id)
logger.warning(f"[OAuth Monitoring] Auto-detected {len(platforms)} connected platforms for user {user_id}: {platforms}")
else:
logger.warning(f"[OAuth Monitoring] Creating monitoring tasks for specified platforms: {platforms}")
if not platforms:
logger.warning(f"[OAuth Monitoring] No connected platforms found for user {user_id}. No monitoring tasks created.")
return []
created_tasks = []
now = datetime.utcnow()
next_check = now + timedelta(days=7) # 7 days from now
for platform in platforms:
# Check if task already exists for this user/platform combination
existing_task = db.query(OAuthTokenMonitoringTask).filter(
OAuthTokenMonitoringTask.user_id == user_id,
OAuthTokenMonitoringTask.platform == platform
).first()
if existing_task:
logger.warning(
f"[OAuth Monitoring] Monitoring task already exists for user {user_id}, platform {platform}. "
f"Skipping creation."
)
continue
# Create new monitoring task
task = OAuthTokenMonitoringTask(
user_id=user_id,
platform=platform,
status='active',
next_check=next_check,
created_at=now,
updated_at=now
)
db.add(task)
created_tasks.append(task)
logger.warning(
f"[OAuth Monitoring] Created OAuth token monitoring task for user {user_id}, "
f"platform {platform}, next_check: {next_check.isoformat()}"
)
db.commit()
logger.warning(
f"[OAuth Monitoring] Successfully created {len(created_tasks)} OAuth token monitoring tasks "
f"for user {user_id}"
)
return created_tasks
except Exception as e:
logger.error(
f"Error creating OAuth token monitoring tasks for user {user_id}: {e}",
exc_info=True
)
db.rollback()
return []