679 lines
24 KiB
Python
679 lines
24 KiB
Python
"""
|
|
Wix Integration API Routes
|
|
|
|
Handles Wix authentication, connection status, and blog publishing.
|
|
"""
|
|
|
|
from fastapi import APIRouter, HTTPException, Depends, Request
|
|
from fastapi.responses import HTMLResponse
|
|
from typing import Dict, Any, Optional
|
|
from loguru import logger
|
|
from pydantic import BaseModel
|
|
|
|
from services.wix_service import WixService
|
|
from services.integrations.wix_oauth import WixOAuthService
|
|
from middleware.auth_middleware import get_current_user
|
|
import os
|
|
|
|
router = APIRouter(prefix="/api/wix", tags=["Wix Integration"])
|
|
|
|
# Initialize Wix service
|
|
wix_service = WixService()
|
|
|
|
# Initialize Wix OAuth service for token storage
|
|
wix_oauth_service = WixOAuthService(db_path=os.path.abspath("alwrity.db"))
|
|
|
|
|
|
class WixAuthRequest(BaseModel):
|
|
"""Request model for Wix authentication"""
|
|
code: str
|
|
state: Optional[str] = None
|
|
|
|
|
|
class WixPublishRequest(BaseModel):
|
|
"""Request model for publishing to Wix"""
|
|
title: str
|
|
content: str
|
|
cover_image_url: Optional[str] = None
|
|
category_ids: Optional[list] = None
|
|
tag_ids: Optional[list] = None
|
|
publish: bool = True
|
|
# Optional access token for test-real publish flow
|
|
access_token: Optional[str] = None
|
|
class WixCreateCategoryRequest(BaseModel):
|
|
access_token: str
|
|
label: str
|
|
description: Optional[str] = None
|
|
language: Optional[str] = None
|
|
|
|
|
|
class WixCreateTagRequest(BaseModel):
|
|
access_token: str
|
|
label: str
|
|
language: Optional[str] = None
|
|
|
|
|
|
class WixConnectionStatus(BaseModel):
|
|
"""Response model for Wix connection status"""
|
|
connected: bool
|
|
has_permissions: bool
|
|
site_info: Optional[Dict[str, Any]] = None
|
|
permissions: Optional[Dict[str, Any]] = None
|
|
error: Optional[str] = None
|
|
|
|
|
|
@router.get("/auth/url")
|
|
async def get_authorization_url(state: Optional[str] = None) -> Dict[str, str]:
|
|
"""
|
|
Get Wix OAuth authorization URL
|
|
|
|
Args:
|
|
state: Optional state parameter for security
|
|
|
|
Returns:
|
|
Authorization URL
|
|
"""
|
|
try:
|
|
url = wix_service.get_authorization_url(state)
|
|
return {"authorization_url": url}
|
|
except Exception as e:
|
|
logger.error(f"Failed to generate authorization URL: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/auth/callback")
|
|
async def handle_oauth_callback(request: WixAuthRequest, current_user: dict = Depends(get_current_user)) -> Dict[str, Any]:
|
|
"""
|
|
Handle OAuth callback and exchange code for tokens
|
|
|
|
Args:
|
|
request: OAuth callback request with code
|
|
current_user: Current authenticated user
|
|
|
|
Returns:
|
|
Token information and connection status
|
|
"""
|
|
try:
|
|
user_id = current_user.get('id')
|
|
if not user_id:
|
|
raise HTTPException(status_code=400, detail="User ID not found")
|
|
|
|
# Exchange code for tokens
|
|
tokens = wix_service.exchange_code_for_tokens(request.code)
|
|
|
|
# Get site information to extract site_id and member_id
|
|
site_info = wix_service.get_site_info(tokens['access_token'])
|
|
site_id = site_info.get('siteId') or site_info.get('site_id')
|
|
|
|
# Extract member_id from token if possible
|
|
member_id = None
|
|
try:
|
|
member_id = wix_service.extract_member_id_from_access_token(tokens['access_token'])
|
|
except Exception:
|
|
pass
|
|
|
|
# Check permissions
|
|
permissions = wix_service.check_blog_permissions(tokens['access_token'])
|
|
|
|
# Store tokens securely in database
|
|
stored = wix_oauth_service.store_tokens(
|
|
user_id=user_id,
|
|
access_token=tokens['access_token'],
|
|
refresh_token=tokens.get('refresh_token'),
|
|
expires_in=tokens.get('expires_in'),
|
|
token_type=tokens.get('token_type', 'Bearer'),
|
|
scope=tokens.get('scope'),
|
|
site_id=site_id,
|
|
member_id=member_id
|
|
)
|
|
|
|
if not stored:
|
|
logger.warning(f"Failed to store Wix tokens for user {user_id}, but OAuth succeeded")
|
|
|
|
return {
|
|
"success": True,
|
|
"tokens": {
|
|
"access_token": tokens['access_token'],
|
|
"refresh_token": tokens.get('refresh_token'),
|
|
"expires_in": tokens.get('expires_in'),
|
|
"token_type": tokens.get('token_type', 'Bearer')
|
|
},
|
|
"site_info": site_info,
|
|
"permissions": permissions,
|
|
"message": "Successfully connected to Wix"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to handle OAuth callback: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/callback")
|
|
async def handle_oauth_callback_get(code: str, state: Optional[str] = None, request: Request = None, current_user: dict = Depends(get_current_user)):
|
|
"""HTML callback page for Wix OAuth that exchanges code and notifies opener via postMessage."""
|
|
try:
|
|
tokens = wix_service.exchange_code_for_tokens(code)
|
|
site_info = wix_service.get_site_info(tokens['access_token'])
|
|
permissions = wix_service.check_blog_permissions(tokens['access_token'])
|
|
|
|
# Store tokens in database if we have user_id
|
|
user_id = current_user.get('id') if current_user else None
|
|
if user_id:
|
|
site_id = site_info.get('siteId') or site_info.get('site_id')
|
|
member_id = None
|
|
try:
|
|
member_id = wix_service.extract_member_id_from_access_token(tokens['access_token'])
|
|
except Exception:
|
|
pass
|
|
|
|
stored = wix_oauth_service.store_tokens(
|
|
user_id=user_id,
|
|
access_token=tokens['access_token'],
|
|
refresh_token=tokens.get('refresh_token'),
|
|
expires_in=tokens.get('expires_in'),
|
|
token_type=tokens.get('token_type', 'Bearer'),
|
|
scope=tokens.get('scope'),
|
|
site_id=site_id,
|
|
member_id=member_id
|
|
)
|
|
if not stored:
|
|
logger.warning(f"Failed to store Wix tokens for user {user_id} in GET callback")
|
|
|
|
# Build success payload for postMessage
|
|
payload = {
|
|
"type": "WIX_OAUTH_SUCCESS",
|
|
"success": True,
|
|
"tokens": {
|
|
"access_token": tokens['access_token'],
|
|
"refresh_token": tokens.get('refresh_token'),
|
|
"expires_in": tokens.get('expires_in'),
|
|
"token_type": tokens.get('token_type', 'Bearer')
|
|
},
|
|
"site_info": site_info,
|
|
"permissions": permissions
|
|
}
|
|
|
|
html = f"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head><title>Wix Connected</title></head>
|
|
<body>
|
|
<script>
|
|
(function() {{
|
|
try {{
|
|
var payload = {payload};
|
|
(window.opener || window.parent).postMessage(payload, '*');
|
|
}} catch (e) {{}}
|
|
window.close();
|
|
}})();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
return HTMLResponse(content=html, headers={
|
|
"Cross-Origin-Opener-Policy": "unsafe-none",
|
|
"Cross-Origin-Embedder-Policy": "unsafe-none"
|
|
})
|
|
except Exception as e:
|
|
logger.error(f"Wix OAuth GET callback failed: {e}")
|
|
html = f"""
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head><title>Wix Connection Failed</title></head>
|
|
<body>
|
|
<script>
|
|
(function() {{
|
|
try {{
|
|
(window.opener || window.parent).postMessage({{ type: 'WIX_OAUTH_ERROR', success: false, error: '{str(e)}' }}, '*');
|
|
}} catch (e) {{}}
|
|
window.close();
|
|
}})();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
return HTMLResponse(content=html, headers={
|
|
"Cross-Origin-Opener-Policy": "unsafe-none",
|
|
"Cross-Origin-Embedder-Policy": "unsafe-none"
|
|
})
|
|
|
|
|
|
@router.get("/connection/status")
|
|
async def get_connection_status(current_user: dict = Depends(get_current_user)) -> WixConnectionStatus:
|
|
"""
|
|
Check Wix connection status and permissions
|
|
|
|
Args:
|
|
current_user: Current authenticated user
|
|
|
|
Returns:
|
|
Connection status and permissions
|
|
"""
|
|
try:
|
|
# Check if user has Wix tokens stored in sessionStorage (frontend approach)
|
|
# This is a simplified check - in production you'd store tokens in database
|
|
return WixConnectionStatus(
|
|
connected=False,
|
|
has_permissions=False,
|
|
error="No Wix connection found. Please connect your Wix account first."
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to check connection status: {e}")
|
|
return WixConnectionStatus(
|
|
connected=False,
|
|
has_permissions=False,
|
|
error=str(e)
|
|
)
|
|
|
|
|
|
@router.get("/status")
|
|
async def get_wix_status(current_user: dict = Depends(get_current_user)) -> Dict[str, Any]:
|
|
"""
|
|
Get Wix connection status (similar to GSC/WordPress pattern)
|
|
Note: Wix tokens are stored in frontend sessionStorage, so we can't directly check them here.
|
|
The frontend will check sessionStorage and update the UI accordingly.
|
|
"""
|
|
try:
|
|
# Since Wix tokens are stored in frontend sessionStorage (not backend database),
|
|
# we return a default response. The frontend will check sessionStorage directly.
|
|
return {
|
|
"connected": False,
|
|
"sites": [],
|
|
"total_sites": 0,
|
|
"error": "Wix connection status managed by frontend sessionStorage"
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Failed to get Wix status: {e}")
|
|
return {
|
|
"connected": False,
|
|
"sites": [],
|
|
"total_sites": 0,
|
|
"error": str(e)
|
|
}
|
|
|
|
|
|
@router.post("/publish")
|
|
async def publish_to_wix(request: WixPublishRequest, current_user: dict = Depends(get_current_user)) -> Dict[str, Any]:
|
|
"""
|
|
Publish blog post to Wix
|
|
|
|
Args:
|
|
request: Blog post data
|
|
current_user: Current authenticated user
|
|
|
|
Returns:
|
|
Published blog post information
|
|
"""
|
|
try:
|
|
# TODO: Retrieve stored access token from database for current_user
|
|
# For now, we'll return an error asking user to connect first
|
|
|
|
return {
|
|
"success": False,
|
|
"error": "Wix account not connected. Please connect your Wix account first.",
|
|
"message": "Use the /api/wix/auth/url endpoint to get the authorization URL"
|
|
}
|
|
|
|
# Example of what the actual implementation would look like:
|
|
# access_token = get_stored_access_token(current_user['id'])
|
|
#
|
|
# if not access_token:
|
|
# raise HTTPException(status_code=401, detail="Wix account not connected")
|
|
#
|
|
# # Check if token is still valid, refresh if needed
|
|
# try:
|
|
# site_info = wix_service.get_site_info(access_token)
|
|
# except:
|
|
# # Token expired, try to refresh
|
|
# refresh_token = get_stored_refresh_token(current_user['id'])
|
|
# if refresh_token:
|
|
# new_tokens = wix_service.refresh_access_token(refresh_token)
|
|
# access_token = new_tokens['access_token']
|
|
# # Store new tokens
|
|
# else:
|
|
# raise HTTPException(status_code=401, detail="Wix session expired. Please reconnect.")
|
|
#
|
|
# # Get current member ID (required for third-party apps)
|
|
# member_info = wix_service.get_current_member(access_token)
|
|
# member_id = member_info.get('member', {}).get('id')
|
|
#
|
|
# if not member_id:
|
|
# raise HTTPException(status_code=400, detail="Could not retrieve member ID")
|
|
#
|
|
# # Create blog post
|
|
# result = wix_service.create_blog_post(
|
|
# access_token=access_token,
|
|
# title=request.title,
|
|
# content=request.content,
|
|
# cover_image_url=request.cover_image_url,
|
|
# category_ids=request.category_ids,
|
|
# tag_ids=request.tag_ids,
|
|
# publish=request.publish,
|
|
# member_id=member_id # Required for third-party apps
|
|
# )
|
|
#
|
|
# return {
|
|
# "success": True,
|
|
# "post_id": result.get('draftPost', {}).get('id'),
|
|
# "url": result.get('draftPost', {}).get('url'),
|
|
# "message": "Blog post published successfully to Wix"
|
|
# }
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to publish to Wix: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/categories")
|
|
async def get_blog_categories(current_user: dict = Depends(get_current_user)) -> Dict[str, Any]:
|
|
"""
|
|
Get available blog categories from Wix
|
|
|
|
Args:
|
|
current_user: Current authenticated user
|
|
|
|
Returns:
|
|
List of blog categories
|
|
"""
|
|
try:
|
|
# TODO: Retrieve stored access token from database for current_user
|
|
return {
|
|
"success": False,
|
|
"error": "Wix account not connected. Please connect your Wix account first."
|
|
}
|
|
|
|
# Example implementation:
|
|
# access_token = get_stored_access_token(current_user['id'])
|
|
# if not access_token:
|
|
# raise HTTPException(status_code=401, detail="Wix account not connected")
|
|
#
|
|
# categories = wix_service.get_blog_categories(access_token)
|
|
# return {"categories": categories}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to get blog categories: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/tags")
|
|
async def get_blog_tags(current_user: dict = Depends(get_current_user)) -> Dict[str, Any]:
|
|
"""
|
|
Get available blog tags from Wix
|
|
|
|
Args:
|
|
current_user: Current authenticated user
|
|
|
|
Returns:
|
|
List of blog tags
|
|
"""
|
|
try:
|
|
# TODO: Retrieve stored access token from database for current_user
|
|
return {
|
|
"success": False,
|
|
"error": "Wix account not connected. Please connect your Wix account first."
|
|
}
|
|
|
|
# Example implementation:
|
|
# access_token = get_stored_access_token(current_user['id'])
|
|
# if not access_token:
|
|
# raise HTTPException(status_code=401, detail="Wix account not connected")
|
|
#
|
|
# tags = wix_service.get_blog_tags(access_token)
|
|
# return {"tags": tags}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to get blog tags: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/disconnect")
|
|
async def disconnect_wix(current_user: dict = Depends(get_current_user)) -> Dict[str, Any]:
|
|
"""
|
|
Disconnect Wix account
|
|
|
|
Args:
|
|
current_user: Current authenticated user
|
|
|
|
Returns:
|
|
Disconnection status
|
|
"""
|
|
try:
|
|
# TODO: Remove stored tokens from database for current_user
|
|
return {
|
|
"success": True,
|
|
"message": "Wix account disconnected successfully"
|
|
}
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to disconnect Wix: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
# =============================================================================
|
|
# TEST ENDPOINTS - No authentication required for testing
|
|
# =============================================================================
|
|
|
|
@router.get("/test/connection/status")
|
|
async def get_test_connection_status() -> WixConnectionStatus:
|
|
"""
|
|
TEST ENDPOINT: Check Wix connection status without authentication
|
|
|
|
Returns:
|
|
Connection status and permissions
|
|
"""
|
|
try:
|
|
logger.info("TEST: Checking Wix connection status (no auth required)")
|
|
|
|
return WixConnectionStatus(
|
|
connected=False,
|
|
has_permissions=False,
|
|
error="No stored tokens found. Please connect your Wix account first."
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"TEST: Failed to check connection status: {e}")
|
|
return WixConnectionStatus(
|
|
connected=False,
|
|
has_permissions=False,
|
|
error=str(e)
|
|
)
|
|
|
|
|
|
@router.get("/test/auth/url")
|
|
async def get_test_authorization_url(state: Optional[str] = None) -> Dict[str, str]:
|
|
"""
|
|
TEST ENDPOINT: Get Wix OAuth authorization URL without authentication
|
|
|
|
Args:
|
|
state: Optional state parameter for security
|
|
|
|
Returns:
|
|
Authorization URL for user to visit
|
|
"""
|
|
try:
|
|
logger.info("TEST: Generating Wix authorization URL (no auth required)")
|
|
|
|
# Check if Wix service is properly configured
|
|
if not wix_service.client_id:
|
|
logger.warning("TEST: Wix Client ID not configured, returning mock URL")
|
|
return {
|
|
"url": (
|
|
"https://www.wix.com/oauth/access?client_id=YOUR_CLIENT_ID"
|
|
"&redirect_uri=http://localhost:3000/wix/callback"
|
|
"&response_type=code&scope="
|
|
"BLOG.CREATE-DRAFT,BLOG.PUBLISH-POST,BLOG.READ-CATEGORY,"
|
|
"BLOG.CREATE-CATEGORY,BLOG.READ-TAG,BLOG.CREATE-TAG,"
|
|
"MEDIA.SITE_MEDIA_FILES_IMPORT"
|
|
"&code_challenge=test&code_challenge_method=S256"
|
|
),
|
|
"state": state or "test_state",
|
|
"message": "WIX_CLIENT_ID not configured. Please set it in your .env file to get a real authorization URL."
|
|
}
|
|
|
|
auth_url = wix_service.get_authorization_url(state)
|
|
return {"url": auth_url, "state": state or "test_state"}
|
|
except Exception as e:
|
|
logger.error(f"TEST: Failed to generate authorization URL: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/test/publish")
|
|
async def test_publish_to_wix(request: WixPublishRequest) -> Dict[str, Any]:
|
|
"""
|
|
TEST ENDPOINT: Simulate publishing a blog post to Wix without authentication.
|
|
|
|
Returns a fake success response so the frontend can validate the flow.
|
|
"""
|
|
try:
|
|
logger.info("TEST: Simulating publish to Wix (no auth required)")
|
|
return {
|
|
"success": True,
|
|
"post_id": "test_post_id",
|
|
"url": "https://example.com/blog/test-post",
|
|
"message": "Simulated blog post published successfully (test mode)"
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"TEST: Failed to simulate publish: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/refresh-token")
|
|
async def refresh_wix_token(request: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
Refresh Wix access token using refresh token
|
|
|
|
Args:
|
|
request: Dict containing refresh_token
|
|
|
|
Returns:
|
|
New token information with access_token, refresh_token, expires_in
|
|
"""
|
|
try:
|
|
refresh_token = request.get("refresh_token")
|
|
if not refresh_token:
|
|
raise HTTPException(status_code=400, detail="Missing refresh_token")
|
|
|
|
# Refresh the token
|
|
new_tokens = wix_service.refresh_access_token(refresh_token)
|
|
|
|
return {
|
|
"success": True,
|
|
"access_token": new_tokens.get("access_token"),
|
|
"refresh_token": new_tokens.get("refresh_token"),
|
|
"expires_in": new_tokens.get("expires_in"),
|
|
"token_type": new_tokens.get("token_type", "Bearer")
|
|
}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Failed to refresh Wix token: {e}")
|
|
raise HTTPException(status_code=500, detail=f"Failed to refresh token: {str(e)}")
|
|
|
|
|
|
@router.post("/test/publish/real")
|
|
async def test_publish_real(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
"""
|
|
TEST ENDPOINT: Perform a real publish to Wix using a provided access token.
|
|
|
|
Notes:
|
|
- Expects request.access_token from the frontend's Wix SDK tokens
|
|
- Derives member_id server-side (required by Wix for third-party apps)
|
|
"""
|
|
try:
|
|
# Normalize access_token from payload (could be string, dict, or other format)
|
|
from services.integrations.wix.utils import normalize_token_string
|
|
raw_access_token = payload.get("access_token")
|
|
if not raw_access_token:
|
|
raise HTTPException(status_code=400, detail="Missing access_token")
|
|
|
|
# Normalize token to string (handles dict with accessToken.value, int, etc.)
|
|
access_token = normalize_token_string(raw_access_token)
|
|
if not access_token:
|
|
# Fallback: try to convert to string directly
|
|
access_token = str(raw_access_token).strip()
|
|
if not access_token or access_token == "None":
|
|
raise HTTPException(status_code=400, detail="Invalid access_token format")
|
|
|
|
# Derive current member id from token (try local decode first, then API fallback)
|
|
member_id = wix_service.extract_member_id_from_access_token(access_token)
|
|
if not member_id:
|
|
member_info = wix_service.get_current_member(access_token)
|
|
member_id = (
|
|
(member_info.get("member") or {}).get("id")
|
|
or member_info.get("id")
|
|
)
|
|
if not member_id:
|
|
raise HTTPException(status_code=400, detail="Unable to resolve member_id from token")
|
|
|
|
# Extract SEO metadata if provided
|
|
seo_metadata = payload.get("seo_metadata")
|
|
|
|
# Extract category/tag IDs or names
|
|
# Can be either:
|
|
# - IDs: List of UUID strings
|
|
# - Names: List of name strings (will be looked up/created)
|
|
category_ids = payload.get("category_ids") or payload.get("category_names")
|
|
tag_ids = payload.get("tag_ids") or payload.get("tag_names")
|
|
|
|
# If SEO metadata has categories/tags but they weren't explicitly provided, use them
|
|
if seo_metadata:
|
|
if not category_ids and seo_metadata.get("blog_categories"):
|
|
category_ids = seo_metadata.get("blog_categories")
|
|
if not tag_ids and seo_metadata.get("blog_tags"):
|
|
tag_ids = seo_metadata.get("blog_tags")
|
|
|
|
result = wix_service.create_blog_post(
|
|
access_token=access_token,
|
|
title=payload.get("title") or "Untitled",
|
|
content=payload.get("content") or "",
|
|
cover_image_url=payload.get("cover_image_url"),
|
|
category_ids=category_ids,
|
|
tag_ids=tag_ids,
|
|
publish=bool(payload.get("publish", True)),
|
|
member_id=member_id,
|
|
seo_metadata=seo_metadata,
|
|
)
|
|
|
|
return {
|
|
"success": True,
|
|
"post_id": (result.get("draftPost") or result.get("post") or {}).get("id"),
|
|
"url": (result.get("draftPost") or result.get("post") or {}).get("url"),
|
|
"message": "Blog post published to Wix",
|
|
"raw": result,
|
|
}
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"TEST: Real publish failed: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/test/category")
|
|
async def test_create_category(request: WixCreateCategoryRequest) -> Dict[str, Any]:
|
|
try:
|
|
result = wix_service.create_category(
|
|
access_token=request.access_token,
|
|
label=request.label,
|
|
description=request.description,
|
|
language=request.language,
|
|
)
|
|
return {"success": True, "category": result.get("category", {}), "raw": result}
|
|
except Exception as e:
|
|
logger.error(f"TEST: Create category failed: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/test/tag")
|
|
async def test_create_tag(request: WixCreateTagRequest) -> Dict[str, Any]:
|
|
try:
|
|
result = wix_service.create_tag(
|
|
access_token=request.access_token,
|
|
label=request.label,
|
|
language=request.language,
|
|
)
|
|
return {"success": True, "tag": result.get("tag", {}), "raw": result}
|
|
except Exception as e:
|
|
logger.error(f"TEST: Create tag failed: {e}")
|
|
raise HTTPException(status_code=500, detail=str(e))
|