feat: Brainstorm Topics with GSC + Issue #518 fixes + Blog Editor enhancements
Issue #518 - Subscription not updating after checkout: - Fix stale closure in SubscriptionContext checkout polling (use subscriptionRef) - Move checkout success polling from InitialRouteHandler into SubscriptionContext - Remove redundant polling code from InitialRouteHandler - Fix plan label: 'Free' instead of 'No Plan', proper capitalization - Add plan refresh button in UserBadge - Add 'View Costing Details' to UserBadge dropdown - Rename 'ALwrity Podcast Maker' to 'Podcast Creator' across UI - Clean subscription=success URL param after verification Blog Writer WYSIWYG Editor enhancements: - Per-section preview toggle (view/edit icons) - Enhanced hover-based toolbar - Circular SVG progress stats bar with detailed tooltip - Research tool chips in stats bar footer - Per-section TTS with useTextToSpeech hook (browser native) - Full blog preview modal with print/PDF support - PlayAllTTSButton: sequential playback with progress bar - OnThisPageNav: floating sidebar with scroll tracking - Section data attributes for scroll anchoring GSC Brainstorm Topics feature: - Backend: gsc_brainstorm_service.py (rule-based + LLM recommendations) - Backend: POST /gsc/brainstorm endpoint with 3-word minimum validation - Frontend: gscBrainstorm.ts API client - Frontend: useGSCBrainstormConnection hook (popup OAuth, no /onboarding redirect) - Frontend: useGSCBrainstorm hook (connect check + brainstorm call) - Frontend: GSCBrainstormModal (3-tab results: Opportunities, Gaps, AI Recs) - Frontend: BrainstormButton (visible at 3+ words, GSC connect overlay) - Wire BrainstormButton into ManualResearchForm and ResearchAction - Add blog_writer to gsc_auth router features for ALWRITY_ENABLED_FEATURES
This commit is contained in:
@@ -8,6 +8,7 @@ from loguru import logger
|
||||
import os
|
||||
|
||||
from services.gsc_service import GSCService
|
||||
from services.gsc_brainstorm_service import GSCBrainstormService
|
||||
from middleware.auth_middleware import get_current_user
|
||||
|
||||
# Initialize router
|
||||
@@ -15,6 +16,7 @@ router = APIRouter(prefix="/gsc", tags=["Google Search Console"])
|
||||
|
||||
# Initialize GSC service
|
||||
gsc_service = GSCService()
|
||||
brainstorm_service = GSCBrainstormService(gsc_service)
|
||||
|
||||
# Pydantic models
|
||||
class GSCAnalyticsRequest(BaseModel):
|
||||
@@ -22,6 +24,10 @@ class GSCAnalyticsRequest(BaseModel):
|
||||
start_date: Optional[str] = None
|
||||
end_date: Optional[str] = None
|
||||
|
||||
class GSCBrainstormRequest(BaseModel):
|
||||
keywords: str
|
||||
site_url: Optional[str] = None
|
||||
|
||||
class GSCStatusResponse(BaseModel):
|
||||
connected: bool
|
||||
sites: Optional[List[Dict[str, Any]]] = None
|
||||
@@ -199,6 +205,49 @@ async def get_gsc_analytics(
|
||||
logger.error(f"Error getting GSC analytics: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error getting analytics: {str(e)}")
|
||||
|
||||
@router.post("/brainstorm")
|
||||
async def brainstorm_topics(
|
||||
request: GSCBrainstormRequest,
|
||||
user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Brainstorm blog topic suggestions based on the user's GSC data.
|
||||
|
||||
The user must have GSC connected. If no site_url is provided,
|
||||
the first verified site is used automatically.
|
||||
"""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
tokens = request.keywords.strip().split()
|
||||
if len(tokens) < 3:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Please provide at least 3 words for brainstorming topic suggestions.",
|
||||
)
|
||||
|
||||
logger.info(f"GSC brainstorm for user: {user_id}, keywords: {request.keywords!r}")
|
||||
|
||||
result = brainstorm_service.brainstorm_topics(
|
||||
user_id=user_id,
|
||||
keywords=request.keywords,
|
||||
site_url=request.site_url,
|
||||
)
|
||||
|
||||
if "error" in result and not result.get("content_opportunities"):
|
||||
status = 400 if "No GSC sites" in result["error"] else 500
|
||||
raise HTTPException(status_code=status, detail=result["error"])
|
||||
|
||||
logger.info(f"GSC brainstorm completed for user: {user_id}")
|
||||
return result
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error in GSC brainstorm: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error brainstorming topics: {str(e)}")
|
||||
|
||||
@router.get("/sitemaps/{site_url:path}")
|
||||
async def get_gsc_sitemaps(
|
||||
site_url: str,
|
||||
|
||||
Reference in New Issue
Block a user