Files
ajaysi 923fa671fe feat: ContentGuardianAgent, onboarding UX, Team Activity action wiring, docs, agent help modal
ContentGuardianAgent consolidation:
- Merge 3 duplicate classes into single source in specialized/content_guardian.py
- Watchdog audit_committee() with heuristic scoring, coverage gaps, overlaps, alerts
- Remove misleading rejection_rate() helper; use acceptance_rate directly
- Integrate audit + alerts + trend signals into today_workflow_service.py

Team Activity page:
- QualityAuditPanel: health ring, per-agent critiques, coverage gaps, overlaps
- TrendSignalsPanel: opportunity cards with urgency/impact/coverage bars
- AlertBanner: persistent dismiss via POST /alerts/{id}/mark-read
- AgentHelpModal: dialog showing all 8 agents with descriptions, tools, schedule
- QualityAuditPanel action buttons: Fill gap -> /content-planning, Resolve overlap, View CTA on alerts/issues
- TrendSignalsPanel action buttons: Create content from this trend -> /blog-writer with trend context state

Onboarding system:
- Step 4 validation: no auto-pass via basic_ready; requires persona data or explicit progression
- Step 5 validation: logs warning on auto-pass without integration data
- OnboardingCompletionService: single DB session, transactional task creation, upsert pattern
- Business-without-website: nullable website_url on SIFIndexingTask and MarketTrendsTask
- DeepCompetitorAnalysisExecutor: 5-min timeout, 10-competitor cap, asyncio.wait_for
- Persona generation: async with 30s timeout, falls back to scheduler
- OnboardingProgressService.reset_onboarding(): resets session + pauses all DB tasks
- OnboardingControlService.reset_onboarding(): also cancels APScheduler jobs
- FinalStep TaskSchedulingPanel: shows scheduled/failed tasks after completion, 8s auto-redirect
- onboarding_completed agent activity event logged to feed

Documentation:
- docs-site/features/onboarding/: overview, steps, scheduler-tasks, technical-reference (4 pages)
- docs-site/mkdocs.yml: added Onboarding System nav section
- docs-site/features/sif-agents/: overview, agent-directory, committee-system, content-guardian (4 pages)
- docs-site/features/team-activity/: overview, quality-audit, trend-signals, alert-system (4 pages)
- docs-site/features/todays-workflow/: updated overview, technical-architecture, workflow-guide, api-reference
2026-06-01 12:24:31 +05:30

136 lines
4.5 KiB
Python

"""
Authentication utilities for Wix API requests.
Supports both OAuth Bearer tokens and API keys for Wix Headless apps.
"""
import os
from typing import Dict, Optional
from loguru import logger
def get_wix_headers(
access_token: str,
client_id: Optional[str] = None,
extra: Optional[Dict[str, str]] = None
) -> Dict[str, str]:
"""
Build headers for Wix API requests with automatic token type detection.
Supports:
- OAuth Bearer tokens (JWT format: xxx.yyy.zzz)
- Wix API keys (for Headless apps)
Args:
access_token: OAuth token OR API key
client_id: Optional Wix client ID
extra: Additional headers to include
Returns:
Headers dict with proper Authorization format
"""
headers: Dict[str, str] = {
'Content-Type': 'application/json',
}
if access_token:
# Ensure access_token is a string (defensive check)
if not isinstance(access_token, str):
from services.integrations.wix.utils import normalize_token_string
normalized = normalize_token_string(access_token)
if normalized:
access_token = normalized
else:
access_token = str(access_token)
token = access_token.strip()
if token:
# Detect token type
# API keys are typically longer and don't have JWT structure (xxx.yyy.zzz)
# JWT tokens have exactly 2 dots separating 3 parts
# Wix OAuth tokens can have format "OauthNG.JWS.xxx.yyy.zzz"
# CRITICAL: Wix OAuth tokens can have format "OauthNG.JWS.xxx.yyy.zzz"
# These should use "Bearer" prefix even though they have more than 2 dots
if token.startswith('OauthNG.JWS.'):
# Wix OAuth token - use Bearer prefix
headers['Authorization'] = f'Bearer {token}'
logger.debug("Using Wix OAuth token with Bearer prefix (OauthNG.JWS. format detected)")
elif token.startswith('IST.'):
# Wix Headless API key - send as-is, no Bearer
headers['Authorization'] = token
logger.debug("Using Wix API key for authorization (IST. format detected)")
else:
# Standard JWT has exactly 2 dots separating header.payload.signature
dot_count = token.count('.')
if dot_count == 2:
headers['Authorization'] = f'Bearer {token}'
logger.debug("Using OAuth Bearer token (JWT format: 2 dots detected)")
else:
headers['Authorization'] = token
logger.debug("Using token as-is (non-JWT format detected)")
if client_id:
headers['wix-client-id'] = client_id
if extra:
headers.update(extra)
return headers
def get_wix_api_key() -> Optional[str]:
"""
Get Wix API key from environment.
For Wix Headless apps, API keys provide admin-level access.
Returns:
API key if set, None otherwise
"""
api_key = os.getenv('WIX_API_KEY')
if api_key:
logger.warning(f"✅ Wix API key found in environment ({len(api_key)} chars)")
else:
logger.warning("❌ No Wix API key in environment")
return api_key
def should_use_api_key(access_token: Optional[str] = None) -> bool:
"""
Determine if we should use API key instead of OAuth token.
Use API key if:
- No OAuth token provided
- OAuth token is getting 403 errors
- API key is available in environment
Args:
access_token: Optional OAuth token
Returns:
True if should use API key, False otherwise
"""
# If no access token, check for API key
if not access_token or not access_token.strip():
return get_wix_api_key() is not None
# If access token looks like API key already, use it
# Ensure access_token is a string (defensive check)
if not isinstance(access_token, str):
from services.integrations.wix.utils import normalize_token_string
normalized = normalize_token_string(access_token)
if normalized:
access_token = normalized
else:
access_token = str(access_token)
token = access_token.strip()
if token.startswith('OauthNG.JWS.'):
return False
if token.startswith('IST.'):
return True
# Standard JWT has exactly 2 dots
return token.count('.') != 2