ALwrity + Wordpress + Wix + GSC integration
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""Google Search Console Authentication Router for ALwrity."""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||
from fastapi.responses import HTMLResponse, JSONResponse
|
||||
from typing import Dict, List, Any, Optional
|
||||
from pydantic import BaseModel
|
||||
from loguru import logger
|
||||
@@ -39,10 +40,12 @@ async def get_gsc_auth_url(user: dict = Depends(get_current_user)):
|
||||
auth_url = gsc_service.get_oauth_url(user_id)
|
||||
|
||||
logger.info(f"GSC OAuth URL generated successfully for user: {user_id}")
|
||||
logger.info(f"OAuth URL: {auth_url[:100]}...")
|
||||
return {"auth_url": auth_url}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating GSC OAuth URL: {e}")
|
||||
logger.error(f"Error details: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"Error generating OAuth URL: {str(e)}")
|
||||
|
||||
@router.get("/callback")
|
||||
@@ -50,7 +53,12 @@ async def handle_gsc_callback(
|
||||
code: str = Query(..., description="Authorization code from Google"),
|
||||
state: str = Query(..., description="State parameter for security")
|
||||
):
|
||||
"""Handle Google Search Console OAuth callback."""
|
||||
"""Handle Google Search Console OAuth callback.
|
||||
|
||||
For a smoother UX when opened in a popup, this endpoint returns a tiny HTML
|
||||
page that posts a completion message back to the opener window and closes
|
||||
itself. The JSON payload is still included in the page for debugging.
|
||||
"""
|
||||
try:
|
||||
logger.info(f"Handling GSC OAuth callback with code: {code[:10]}...")
|
||||
|
||||
@@ -58,14 +66,52 @@ async def handle_gsc_callback(
|
||||
|
||||
if success:
|
||||
logger.info("GSC OAuth callback handled successfully")
|
||||
return {"success": True, "message": "GSC connected successfully"}
|
||||
html = """
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head><meta charset=\"utf-8\"><title>GSC Connected</title></head>
|
||||
<body style=\"font-family: sans-serif; padding: 24px;\">
|
||||
<p>Connection Successful. You can close this window.</p>
|
||||
<script>
|
||||
try {{ window.opener && window.opener.postMessage({{ type: 'GSC_AUTH_SUCCESS' }}, '*'); }} catch (e) {{}}
|
||||
try {{ window.close(); }} catch (e) {{}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html)
|
||||
else:
|
||||
logger.error("Failed to handle GSC OAuth callback")
|
||||
raise HTTPException(status_code=400, detail="Failed to connect GSC")
|
||||
html = """
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head><meta charset=\"utf-8\"><title>GSC Connection Failed</title></head>
|
||||
<body style=\"font-family: sans-serif; padding: 24px;\">
|
||||
<p>Connection Failed. Please close this window and try again.</p>
|
||||
<script>
|
||||
try {{ window.opener && window.opener.postMessage({{ type: 'GSC_AUTH_ERROR' }}, '*'); }} catch (e) {{}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(status_code=400, content=html)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error handling GSC OAuth callback: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error handling OAuth callback: {str(e)}")
|
||||
html = f"""
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head><meta charset=\"utf-8\"><title>GSC Connection Error</title></head>
|
||||
<body style=\"font-family: sans-serif; padding: 24px;\">
|
||||
<p>Connection Error. Please close this window and try again.</p>
|
||||
<pre style=\"white-space: pre-wrap;\">{str(e)}</pre>
|
||||
<script>
|
||||
try {{ window.opener && window.opener.postMessage({{ type: 'GSC_AUTH_ERROR' }}, '*'); }} catch (e) {{}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(status_code=500, content=html)
|
||||
|
||||
@router.get("/sites")
|
||||
async def get_gsc_sites(user: dict = Depends(get_current_user)):
|
||||
@@ -155,6 +201,8 @@ async def get_gsc_status(user: dict = Depends(get_current_user)):
|
||||
sites = gsc_service.get_site_list(user_id)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not get sites for user {user_id}: {e}")
|
||||
# Clear incomplete credentials and mark as disconnected
|
||||
gsc_service.clear_incomplete_credentials(user_id)
|
||||
connected = False
|
||||
|
||||
status_response = GSCStatusResponse(
|
||||
@@ -193,6 +241,29 @@ async def disconnect_gsc(user: dict = Depends(get_current_user)):
|
||||
logger.error(f"Error disconnecting GSC: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error disconnecting GSC: {str(e)}")
|
||||
|
||||
@router.post("/clear-incomplete")
|
||||
async def clear_incomplete_credentials(user: dict = Depends(get_current_user)):
|
||||
"""Clear incomplete GSC credentials that are missing required fields."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
logger.info(f"Clearing incomplete GSC credentials for user: {user_id}")
|
||||
|
||||
success = gsc_service.clear_incomplete_credentials(user_id)
|
||||
|
||||
if success:
|
||||
logger.info(f"Incomplete GSC credentials cleared for user: {user_id}")
|
||||
return {"success": True, "message": "Incomplete credentials cleared"}
|
||||
else:
|
||||
logger.error(f"Failed to clear incomplete credentials for user: {user_id}")
|
||||
raise HTTPException(status_code=500, detail="Failed to clear incomplete credentials")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error clearing incomplete credentials: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error clearing incomplete credentials: {str(e)}")
|
||||
|
||||
@router.get("/health")
|
||||
async def gsc_health_check():
|
||||
"""Health check for GSC service."""
|
||||
|
||||
409
backend/routers/wordpress.py
Normal file
409
backend/routers/wordpress.py
Normal file
@@ -0,0 +1,409 @@
|
||||
"""
|
||||
WordPress API Routes
|
||||
REST API endpoints for WordPress integration management.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Depends, status
|
||||
from fastapi.responses import JSONResponse
|
||||
from typing import List, Optional, Dict, Any
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
from loguru import logger
|
||||
|
||||
from services.integrations.wordpress_service import WordPressService
|
||||
from services.integrations.wordpress_publisher import WordPressPublisher
|
||||
from middleware.auth_middleware import get_current_user
|
||||
|
||||
|
||||
router = APIRouter(prefix="/wordpress", tags=["WordPress"])
|
||||
|
||||
|
||||
# Pydantic Models
|
||||
class WordPressSiteRequest(BaseModel):
|
||||
site_url: str
|
||||
site_name: str
|
||||
username: str
|
||||
app_password: str
|
||||
|
||||
|
||||
class WordPressSiteResponse(BaseModel):
|
||||
id: int
|
||||
site_url: str
|
||||
site_name: str
|
||||
username: str
|
||||
is_active: bool
|
||||
created_at: str
|
||||
updated_at: str
|
||||
|
||||
|
||||
class WordPressPublishRequest(BaseModel):
|
||||
site_id: int
|
||||
title: str
|
||||
content: str
|
||||
excerpt: Optional[str] = ""
|
||||
featured_image_path: Optional[str] = None
|
||||
categories: Optional[List[str]] = None
|
||||
tags: Optional[List[str]] = None
|
||||
status: str = "draft"
|
||||
meta_description: Optional[str] = ""
|
||||
|
||||
|
||||
class WordPressPublishResponse(BaseModel):
|
||||
success: bool
|
||||
post_id: Optional[int] = None
|
||||
post_url: Optional[str] = None
|
||||
error: Optional[str] = None
|
||||
|
||||
|
||||
class WordPressPostResponse(BaseModel):
|
||||
id: int
|
||||
wp_post_id: int
|
||||
title: str
|
||||
status: str
|
||||
published_at: Optional[str]
|
||||
created_at: str
|
||||
site_name: str
|
||||
site_url: str
|
||||
|
||||
|
||||
class WordPressStatusResponse(BaseModel):
|
||||
connected: bool
|
||||
sites: Optional[List[WordPressSiteResponse]] = None
|
||||
total_sites: int = 0
|
||||
|
||||
|
||||
# Initialize services
|
||||
wp_service = WordPressService()
|
||||
wp_publisher = WordPressPublisher()
|
||||
|
||||
|
||||
@router.get("/status", response_model=WordPressStatusResponse)
|
||||
async def get_wordpress_status(user: dict = Depends(get_current_user)):
|
||||
"""Get WordPress connection status for the current user."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
logger.info(f"Checking WordPress status for user: {user_id}")
|
||||
|
||||
# Get user's WordPress sites
|
||||
sites = wp_service.get_all_sites(user_id)
|
||||
|
||||
if sites:
|
||||
# Convert to response format
|
||||
site_responses = [
|
||||
WordPressSiteResponse(
|
||||
id=site['id'],
|
||||
site_url=site['site_url'],
|
||||
site_name=site['site_name'],
|
||||
username=site['username'],
|
||||
is_active=site['is_active'],
|
||||
created_at=site['created_at'],
|
||||
updated_at=site['updated_at']
|
||||
)
|
||||
for site in sites
|
||||
]
|
||||
|
||||
logger.info(f"Found {len(sites)} WordPress sites for user {user_id}")
|
||||
return WordPressStatusResponse(
|
||||
connected=True,
|
||||
sites=site_responses,
|
||||
total_sites=len(sites)
|
||||
)
|
||||
else:
|
||||
logger.info(f"No WordPress sites found for user {user_id}")
|
||||
return WordPressStatusResponse(
|
||||
connected=False,
|
||||
sites=[],
|
||||
total_sites=0
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting WordPress status for user {user_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error checking WordPress status: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/sites", response_model=WordPressSiteResponse)
|
||||
async def add_wordpress_site(
|
||||
site_request: WordPressSiteRequest,
|
||||
user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Add a new WordPress site connection."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
logger.info(f"Adding WordPress site for user {user_id}: {site_request.site_name}")
|
||||
|
||||
# Add the site
|
||||
success = wp_service.add_site(
|
||||
user_id=user_id,
|
||||
site_url=site_request.site_url,
|
||||
site_name=site_request.site_name,
|
||||
username=site_request.username,
|
||||
app_password=site_request.app_password
|
||||
)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Failed to connect to WordPress site. Please check your credentials."
|
||||
)
|
||||
|
||||
# Get the added site info
|
||||
sites = wp_service.get_all_sites(user_id)
|
||||
if sites:
|
||||
latest_site = sites[0] # Most recent site
|
||||
return WordPressSiteResponse(
|
||||
id=latest_site['id'],
|
||||
site_url=latest_site['site_url'],
|
||||
site_name=latest_site['site_name'],
|
||||
username=latest_site['username'],
|
||||
is_active=latest_site['is_active'],
|
||||
created_at=latest_site['created_at'],
|
||||
updated_at=latest_site['updated_at']
|
||||
)
|
||||
else:
|
||||
raise HTTPException(status_code=500, detail="Site added but could not retrieve details")
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding WordPress site: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error adding WordPress site: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/sites", response_model=List[WordPressSiteResponse])
|
||||
async def get_wordpress_sites(user: dict = Depends(get_current_user)):
|
||||
"""Get all WordPress sites for the current user."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
logger.info(f"Getting WordPress sites for user: {user_id}")
|
||||
|
||||
sites = wp_service.get_all_sites(user_id)
|
||||
|
||||
site_responses = [
|
||||
WordPressSiteResponse(
|
||||
id=site['id'],
|
||||
site_url=site['site_url'],
|
||||
site_name=site['site_name'],
|
||||
username=site['username'],
|
||||
is_active=site['is_active'],
|
||||
created_at=site['created_at'],
|
||||
updated_at=site['updated_at']
|
||||
)
|
||||
for site in sites
|
||||
]
|
||||
|
||||
logger.info(f"Retrieved {len(sites)} WordPress sites for user {user_id}")
|
||||
return site_responses
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting WordPress sites for user {user_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error retrieving WordPress sites: {str(e)}")
|
||||
|
||||
|
||||
@router.delete("/sites/{site_id}")
|
||||
async def disconnect_wordpress_site(
|
||||
site_id: int,
|
||||
user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Disconnect a WordPress site."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
logger.info(f"Disconnecting WordPress site {site_id} for user {user_id}")
|
||||
|
||||
success = wp_service.disconnect_site(user_id, site_id)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="WordPress site not found or already disconnected"
|
||||
)
|
||||
|
||||
logger.info(f"WordPress site {site_id} disconnected successfully")
|
||||
return {"success": True, "message": "WordPress site disconnected successfully"}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error disconnecting WordPress site {site_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error disconnecting WordPress site: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/publish", response_model=WordPressPublishResponse)
|
||||
async def publish_to_wordpress(
|
||||
publish_request: WordPressPublishRequest,
|
||||
user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Publish content to a WordPress site."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
logger.info(f"Publishing to WordPress site {publish_request.site_id} for user {user_id}")
|
||||
|
||||
# Publish the content
|
||||
result = wp_publisher.publish_blog_post(
|
||||
user_id=user_id,
|
||||
site_id=publish_request.site_id,
|
||||
title=publish_request.title,
|
||||
content=publish_request.content,
|
||||
excerpt=publish_request.excerpt,
|
||||
featured_image_path=publish_request.featured_image_path,
|
||||
categories=publish_request.categories,
|
||||
tags=publish_request.tags,
|
||||
status=publish_request.status,
|
||||
meta_description=publish_request.meta_description
|
||||
)
|
||||
|
||||
if result['success']:
|
||||
logger.info(f"Content published successfully to WordPress: {result['post_id']}")
|
||||
return WordPressPublishResponse(
|
||||
success=True,
|
||||
post_id=result['post_id'],
|
||||
post_url=result.get('post_url')
|
||||
)
|
||||
else:
|
||||
logger.error(f"Failed to publish content: {result['error']}")
|
||||
return WordPressPublishResponse(
|
||||
success=False,
|
||||
error=result['error']
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error publishing to WordPress: {e}")
|
||||
return WordPressPublishResponse(
|
||||
success=False,
|
||||
error=f"Error publishing content: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/posts", response_model=List[WordPressPostResponse])
|
||||
async def get_wordpress_posts(
|
||||
site_id: Optional[int] = None,
|
||||
user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Get published posts from WordPress sites."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
logger.info(f"Getting WordPress posts for user {user_id}, site_id: {site_id}")
|
||||
|
||||
posts = wp_service.get_posts_for_site(user_id, site_id) if site_id else wp_service.get_posts_for_all_sites(user_id)
|
||||
|
||||
post_responses = [
|
||||
WordPressPostResponse(
|
||||
id=post['id'],
|
||||
wp_post_id=post['wp_post_id'],
|
||||
title=post['title'],
|
||||
status=post['status'],
|
||||
published_at=post['published_at'],
|
||||
created_at=post['created_at'],
|
||||
site_name=post['site_name'],
|
||||
site_url=post['site_url']
|
||||
)
|
||||
for post in posts
|
||||
]
|
||||
|
||||
logger.info(f"Retrieved {len(posts)} WordPress posts for user {user_id}")
|
||||
return post_responses
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting WordPress posts for user {user_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error retrieving WordPress posts: {str(e)}")
|
||||
|
||||
|
||||
@router.put("/posts/{post_id}/status")
|
||||
async def update_post_status(
|
||||
post_id: int,
|
||||
status: str,
|
||||
user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Update the status of a WordPress post (draft/publish)."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
if status not in ['draft', 'publish', 'private']:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Invalid status. Must be 'draft', 'publish', or 'private'"
|
||||
)
|
||||
|
||||
logger.info(f"Updating WordPress post {post_id} status to {status} for user {user_id}")
|
||||
|
||||
success = wp_publisher.update_post_status(user_id, post_id, status)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Post not found or update failed"
|
||||
)
|
||||
|
||||
logger.info(f"WordPress post {post_id} status updated to {status}")
|
||||
return {"success": True, "message": f"Post status updated to {status}"}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating WordPress post {post_id} status: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error updating post status: {str(e)}")
|
||||
|
||||
|
||||
@router.delete("/posts/{post_id}")
|
||||
async def delete_wordpress_post(
|
||||
post_id: int,
|
||||
force: bool = False,
|
||||
user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""Delete a WordPress post."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=400, detail="User ID not found")
|
||||
|
||||
logger.info(f"Deleting WordPress post {post_id} for user {user_id}, force: {force}")
|
||||
|
||||
success = wp_publisher.delete_post(user_id, post_id, force)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="Post not found or deletion failed"
|
||||
)
|
||||
|
||||
logger.info(f"WordPress post {post_id} deleted successfully")
|
||||
return {"success": True, "message": "Post deleted successfully"}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting WordPress post {post_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail=f"Error deleting post: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
async def wordpress_health_check():
|
||||
"""WordPress integration health check."""
|
||||
try:
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "wordpress",
|
||||
"timestamp": "2024-01-01T00:00:00Z",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"WordPress health check failed: {e}")
|
||||
raise HTTPException(status_code=500, detail="WordPress service unhealthy")
|
||||
282
backend/routers/wordpress_oauth.py
Normal file
282
backend/routers/wordpress_oauth.py
Normal file
@@ -0,0 +1,282 @@
|
||||
"""
|
||||
WordPress OAuth2 Routes
|
||||
Handles WordPress.com OAuth2 authentication flow.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
||||
from fastapi.responses import RedirectResponse
|
||||
from typing import Dict, Any, Optional
|
||||
from pydantic import BaseModel
|
||||
from loguru import logger
|
||||
|
||||
from services.integrations.wordpress_oauth import WordPressOAuthService
|
||||
from middleware.auth_middleware import get_current_user
|
||||
|
||||
router = APIRouter(prefix="/wp", tags=["WordPress OAuth"])
|
||||
|
||||
# Initialize OAuth service
|
||||
oauth_service = WordPressOAuthService()
|
||||
|
||||
# Pydantic Models
|
||||
class WordPressOAuthResponse(BaseModel):
|
||||
auth_url: str
|
||||
state: str
|
||||
|
||||
class WordPressCallbackResponse(BaseModel):
|
||||
success: bool
|
||||
message: str
|
||||
blog_url: Optional[str] = None
|
||||
blog_id: Optional[str] = None
|
||||
|
||||
class WordPressStatusResponse(BaseModel):
|
||||
connected: bool
|
||||
sites: list
|
||||
total_sites: int
|
||||
|
||||
@router.get("/auth/url", response_model=WordPressOAuthResponse)
|
||||
async def get_wordpress_auth_url(
|
||||
user: Dict[str, Any] = Depends(get_current_user)
|
||||
):
|
||||
"""Get WordPress OAuth2 authorization URL."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User ID not found.")
|
||||
|
||||
auth_data = oauth_service.generate_authorization_url(user_id)
|
||||
if not auth_data:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="WordPress OAuth is not properly configured. Please check that WORDPRESS_CLIENT_ID and WORDPRESS_CLIENT_SECRET environment variables are set with valid WordPress.com application credentials."
|
||||
)
|
||||
|
||||
return WordPressOAuthResponse(**auth_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error generating WordPress OAuth URL: {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to generate WordPress OAuth URL."
|
||||
)
|
||||
|
||||
@router.get("/callback")
|
||||
async def handle_wordpress_callback(
|
||||
code: str = Query(..., description="Authorization code from WordPress"),
|
||||
state: str = Query(..., description="State parameter for security"),
|
||||
error: Optional[str] = Query(None, description="Error from WordPress OAuth")
|
||||
):
|
||||
"""Handle WordPress OAuth2 callback."""
|
||||
try:
|
||||
if error:
|
||||
logger.error(f"WordPress OAuth error: {error}")
|
||||
html_content = f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WordPress.com Connection Failed</title>
|
||||
<script>
|
||||
// Send error message to parent window
|
||||
window.onload = function() {{
|
||||
window.parent.postMessage({{
|
||||
type: 'WPCOM_OAUTH_ERROR',
|
||||
success: false,
|
||||
error: '{error}'
|
||||
}}, '*');
|
||||
window.close();
|
||||
}};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Connection Failed</h1>
|
||||
<p>There was an error connecting to WordPress.com.</p>
|
||||
<p>You can close this window and try again.</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html_content, headers={
|
||||
"Cross-Origin-Opener-Policy": "unsafe-none",
|
||||
"Cross-Origin-Embedder-Policy": "unsafe-none"
|
||||
})
|
||||
|
||||
if not code or not state:
|
||||
logger.error("Missing code or state parameter in WordPress OAuth callback")
|
||||
html_content = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WordPress.com Connection Failed</title>
|
||||
<script>
|
||||
// Send error message to opener/parent window
|
||||
window.onload = function() {{
|
||||
(window.opener || window.parent).postMessage({{
|
||||
type: 'WPCOM_OAUTH_ERROR',
|
||||
success: false,
|
||||
error: 'Missing parameters'
|
||||
}}, '*');
|
||||
window.close();
|
||||
}};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Connection Failed</h1>
|
||||
<p>Missing required parameters.</p>
|
||||
<p>You can close this window and try again.</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html_content, headers={
|
||||
"Cross-Origin-Opener-Policy": "unsafe-none",
|
||||
"Cross-Origin-Embedder-Policy": "unsafe-none"
|
||||
})
|
||||
|
||||
# Exchange code for token
|
||||
result = oauth_service.handle_oauth_callback(code, state)
|
||||
|
||||
if not result or not result.get('success'):
|
||||
logger.error("Failed to exchange WordPress OAuth code for token")
|
||||
html_content = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WordPress.com Connection Failed</title>
|
||||
<script>
|
||||
// Send error message to opener/parent window
|
||||
window.onload = function() {{
|
||||
(window.opener || window.parent).postMessage({{
|
||||
type: 'WPCOM_OAUTH_ERROR',
|
||||
success: false,
|
||||
error: 'Token exchange failed'
|
||||
}}, '*');
|
||||
window.close();
|
||||
}};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Connection Failed</h1>
|
||||
<p>Failed to exchange authorization code for access token.</p>
|
||||
<p>You can close this window and try again.</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html_content)
|
||||
|
||||
# Return success page with postMessage script
|
||||
blog_url = result.get('blog_url', '')
|
||||
html_content = f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WordPress.com Connection Successful</title>
|
||||
<script>
|
||||
// Send success message to opener/parent window
|
||||
window.onload = function() {{
|
||||
(window.opener || window.parent).postMessage({{
|
||||
type: 'WPCOM_OAUTH_SUCCESS',
|
||||
success: true,
|
||||
blogUrl: '{blog_url}',
|
||||
blogId: '{result.get('blog_id', '')}'
|
||||
}}, '*');
|
||||
window.close();
|
||||
}};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Connection Successful!</h1>
|
||||
<p>Your WordPress.com site has been connected successfully.</p>
|
||||
<p>You can close this window now.</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
return HTMLResponse(content=html_content, headers={
|
||||
"Cross-Origin-Opener-Policy": "unsafe-none",
|
||||
"Cross-Origin-Embedder-Policy": "unsafe-none"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error handling WordPress OAuth callback: {e}")
|
||||
html_content = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WordPress.com Connection Failed</title>
|
||||
<script>
|
||||
// Send error message to opener/parent window
|
||||
window.onload = function() {{
|
||||
(window.opener || window.parent).postMessage({{
|
||||
type: 'WPCOM_OAUTH_ERROR',
|
||||
success: false,
|
||||
error: 'Callback error'
|
||||
}}, '*');
|
||||
window.close();
|
||||
}};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Connection Failed</h1>
|
||||
<p>An unexpected error occurred during connection.</p>
|
||||
<p>You can close this window and try again.</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return HTMLResponse(content=html_content, headers={
|
||||
"Cross-Origin-Opener-Policy": "unsafe-none",
|
||||
"Cross-Origin-Embedder-Policy": "unsafe-none"
|
||||
})
|
||||
|
||||
@router.get("/status", response_model=WordPressStatusResponse)
|
||||
async def get_wordpress_oauth_status(
|
||||
user: Dict[str, Any] = Depends(get_current_user)
|
||||
):
|
||||
"""Get WordPress OAuth connection status."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User ID not found.")
|
||||
|
||||
status_data = oauth_service.get_connection_status(user_id)
|
||||
return WordPressStatusResponse(**status_data)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting WordPress OAuth status: {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to get WordPress connection status."
|
||||
)
|
||||
|
||||
@router.delete("/disconnect/{token_id}")
|
||||
async def disconnect_wordpress_site(
|
||||
token_id: int,
|
||||
user: Dict[str, Any] = Depends(get_current_user)
|
||||
):
|
||||
"""Disconnect a WordPress site."""
|
||||
try:
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="User ID not found.")
|
||||
|
||||
success = oauth_service.revoke_token(user_id, token_id)
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="WordPress token not found or could not be disconnected."
|
||||
)
|
||||
|
||||
return {"success": True, "message": f"WordPress site disconnected successfully."}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error disconnecting WordPress site: {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to disconnect WordPress site."
|
||||
)
|
||||
|
||||
@router.get("/health")
|
||||
async def wordpress_oauth_health():
|
||||
"""WordPress OAuth health check."""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"service": "wordpress_oauth",
|
||||
"timestamp": "2024-01-01T00:00:00Z",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
Reference in New Issue
Block a user