Recovered state: integrated TrendSurferAgent, restored frontend/backend files, and cleaned up recovery scripts
This commit is contained in:
@@ -14,11 +14,12 @@ import json
|
||||
from urllib.parse import quote
|
||||
from ..analytics_cache_service import analytics_cache
|
||||
|
||||
from services.database import get_user_db_path
|
||||
|
||||
class BingOAuthService:
|
||||
"""Manages Bing Webmaster Tools OAuth2 authentication flow."""
|
||||
|
||||
def __init__(self, db_path: str = "alwrity.db"):
|
||||
self.db_path = db_path
|
||||
def __init__(self):
|
||||
# Bing Webmaster OAuth2 credentials
|
||||
self.client_id = os.getenv('BING_CLIENT_ID', '')
|
||||
self.client_secret = os.getenv('BING_CLIENT_SECRET', '')
|
||||
@@ -26,16 +27,20 @@ class BingOAuthService:
|
||||
self.base_url = "https://www.bing.com"
|
||||
self.api_base_url = "https://www.bing.com/webmaster/api.svc/json"
|
||||
|
||||
# Validate configuration
|
||||
if not self.client_id or not self.client_secret or self.client_id == 'your_bing_client_id_here':
|
||||
logger.error("Bing Webmaster OAuth client credentials not configured. Please set BING_CLIENT_ID and BING_CLIENT_SECRET environment variables with valid Bing Webmaster application credentials.")
|
||||
logger.error("To get credentials: 1. Go to https://www.bing.com/webmasters/ 2. Sign in to Bing Webmaster Tools 3. Go to Settings > API Access 4. Create OAuth client")
|
||||
|
||||
self._init_db()
|
||||
logger.warning("Bing Webmaster OAuth client credentials not configured. Please set BING_CLIENT_ID and BING_CLIENT_SECRET environment variables with valid Bing Webmaster application credentials.")
|
||||
logger.warning("To get credentials: 1. Go to https://www.bing.com/webmasters/ 2. Sign in to Bing Webmaster Tools 3. Go to Settings > API Access 4. Create OAuth client")
|
||||
|
||||
def _init_db(self):
|
||||
def _get_db_path(self, user_id: str) -> str:
|
||||
return get_user_db_path(user_id)
|
||||
|
||||
def _init_db(self, user_id: str):
|
||||
"""Initialize database tables for OAuth tokens."""
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
# Ensure directory exists
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS bing_oauth_tokens (
|
||||
@@ -62,21 +67,26 @@ class BingOAuthService:
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
logger.info("Bing Webmaster OAuth database initialized.")
|
||||
|
||||
|
||||
def generate_authorization_url(self, user_id: str, scope: str = "webmaster.manage") -> Dict[str, Any]:
|
||||
"""Generate Bing Webmaster OAuth2 authorization URL."""
|
||||
try:
|
||||
# Check if credentials are properly configured
|
||||
if not self.client_id or not self.client_secret or self.client_id == 'your_bing_client_id_here':
|
||||
logger.error("Bing Webmaster OAuth client credentials not configured")
|
||||
logger.warning("Bing Webmaster OAuth client credentials not configured")
|
||||
return None
|
||||
|
||||
# Generate secure state parameter
|
||||
state = secrets.token_urlsafe(32)
|
||||
# Generate secure state parameter with user_id embedded
|
||||
# Format: user_id:random_token
|
||||
random_token = secrets.token_urlsafe(32)
|
||||
state = f"{user_id}:{random_token}"
|
||||
|
||||
# Ensure DB tables exist for this user
|
||||
self._init_db(user_id)
|
||||
db_path = self._get_db_path(user_id)
|
||||
|
||||
# Store state in database for validation
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO bing_oauth_states (state, user_id, expires_at)
|
||||
@@ -111,8 +121,23 @@ class BingOAuthService:
|
||||
try:
|
||||
logger.info(f"Bing Webmaster OAuth callback started - code: {code[:20]}..., state: {state[:20]}...")
|
||||
|
||||
# Extract user_id from state
|
||||
if ':' not in state:
|
||||
logger.error(f"Invalid state format (missing user_id): {state[:20]}...")
|
||||
return None
|
||||
|
||||
user_id = state.split(':')[0]
|
||||
if not user_id:
|
||||
logger.error("Empty user_id in state")
|
||||
return None
|
||||
|
||||
db_path = self._get_db_path(user_id)
|
||||
if not os.path.exists(db_path):
|
||||
logger.error(f"User database not found for user {user_id}")
|
||||
return None
|
||||
|
||||
# Validate state parameter
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
# First, look up the state regardless of expiry to provide clearer logs
|
||||
cursor.execute('''
|
||||
@@ -126,7 +151,13 @@ class BingOAuthService:
|
||||
logger.error(f"Bing OAuth: State not found or already used. state='{state[:12]}...'")
|
||||
return None
|
||||
|
||||
user_id, created_at, expires_at = row
|
||||
db_user_id, created_at, expires_at = row
|
||||
|
||||
# Verify user_id matches
|
||||
if db_user_id != user_id:
|
||||
logger.error(f"Bing OAuth: State user_id mismatch. Expected {user_id}, got {db_user_id}")
|
||||
return None
|
||||
|
||||
# Check expiry explicitly
|
||||
cursor.execute("SELECT datetime('now') < ?", (expires_at,))
|
||||
not_expired = cursor.fetchone()[0] == 1
|
||||
@@ -180,7 +211,7 @@ class BingOAuthService:
|
||||
# Calculate expiration
|
||||
expires_at = datetime.now() + timedelta(seconds=expires_in)
|
||||
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO bing_oauth_tokens
|
||||
@@ -191,6 +222,7 @@ class BingOAuthService:
|
||||
logger.info(f"Bing OAuth: Token inserted into database for user {user_id}")
|
||||
|
||||
# Proactively fetch and cache user sites using the fresh token
|
||||
|
||||
try:
|
||||
headers = {'Authorization': f'Bearer {access_token}'}
|
||||
response = requests.get(
|
||||
@@ -245,7 +277,11 @@ class BingOAuthService:
|
||||
Returns number of rows deleted.
|
||||
"""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
if not os.path.exists(db_path):
|
||||
return 0
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
# Delete tokens that are expired or explicitly inactive
|
||||
cursor.execute('''
|
||||
@@ -268,7 +304,11 @@ class BingOAuthService:
|
||||
def get_user_tokens(self, user_id: str) -> List[Dict[str, Any]]:
|
||||
"""Get all active Bing tokens for a user."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT id, access_token, refresh_token, token_type, expires_at, scope, created_at
|
||||
@@ -288,17 +328,19 @@ class BingOAuthService:
|
||||
"scope": row[5],
|
||||
"created_at": row[6]
|
||||
})
|
||||
|
||||
return tokens
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting Bing tokens for user {user_id}: {e}")
|
||||
logger.error(f"Error retrieving Bing tokens for user {user_id}: {e}")
|
||||
return []
|
||||
|
||||
def get_user_token_status(self, user_id: str) -> Dict[str, Any]:
|
||||
"""Get detailed token status for a user including expired tokens."""
|
||||
"""Get status of Bing OAuth tokens for a user."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
# Ensure DB tables exist for this user before querying
|
||||
self._init_db(user_id)
|
||||
db_path = self._get_db_path(user_id)
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Get all tokens (active and expired)
|
||||
@@ -437,7 +479,8 @@ class BingOAuthService:
|
||||
expires_in = token_info.get('expires_in', 3600)
|
||||
expires_at = datetime.now() + timedelta(seconds=expires_in)
|
||||
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
UPDATE bing_oauth_tokens
|
||||
@@ -467,7 +510,8 @@ class BingOAuthService:
|
||||
def revoke_token(self, user_id: str, token_id: int) -> bool:
|
||||
"""Revoke a Bing OAuth token."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
UPDATE bing_oauth_tokens
|
||||
@@ -566,13 +610,13 @@ class BingOAuthService:
|
||||
if refreshed_token:
|
||||
logger.info(f"Bing get_user_sites: Token {i+1} refreshed successfully")
|
||||
# Update the token in the database
|
||||
self.update_token_in_db(token["id"], refreshed_token)
|
||||
self.update_token_in_db(user_id, token["id"], refreshed_token)
|
||||
# Use the new token
|
||||
token["access_token"] = refreshed_token["access_token"]
|
||||
else:
|
||||
logger.warning(f"Bing get_user_sites: Failed to refresh token {i+1} - refresh token may be expired")
|
||||
# Mark token as inactive since refresh failed
|
||||
self.mark_token_inactive(token["id"])
|
||||
self.mark_token_inactive(user_id, token["id"])
|
||||
continue
|
||||
else:
|
||||
logger.warning(f"Bing get_user_sites: No refresh token available for token {i+1}")
|
||||
@@ -639,10 +683,11 @@ class BingOAuthService:
|
||||
logger.error(f"Error getting Bing user sites: {e}")
|
||||
return []
|
||||
|
||||
def update_token_in_db(self, token_id: str, refreshed_token: Dict[str, Any]) -> bool:
|
||||
def update_token_in_db(self, user_id: str, token_id: str, refreshed_token: Dict[str, Any]) -> bool:
|
||||
"""Update the access token in the database after refresh."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
# Compute expires_at from expires_in if expires_at missing
|
||||
expires_at_value = refreshed_token.get("expires_at")
|
||||
@@ -667,10 +712,11 @@ class BingOAuthService:
|
||||
logger.error(f"Error updating Bing token in database: {e}")
|
||||
return False
|
||||
|
||||
def mark_token_inactive(self, token_id: str) -> bool:
|
||||
def mark_token_inactive(self, user_id: str, token_id: str) -> bool:
|
||||
"""Mark a token as inactive in the database."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
UPDATE bing_oauth_tokens
|
||||
@@ -922,4 +968,4 @@ class BingOAuthService:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting comprehensive Bing analytics: {e}")
|
||||
return {"error": str(e)}
|
||||
return {"error": str(e)}
|
||||
|
||||
@@ -10,16 +10,26 @@ from datetime import datetime, timedelta
|
||||
from loguru import logger
|
||||
|
||||
|
||||
from services.database import get_user_db_path
|
||||
|
||||
class WixOAuthService:
|
||||
"""Manages Wix OAuth2 authentication flow and token storage."""
|
||||
|
||||
def __init__(self, db_path: str = "alwrity.db"):
|
||||
def __init__(self, db_path: Optional[str] = None):
|
||||
self.db_path = db_path
|
||||
self._init_db()
|
||||
|
||||
def _init_db(self):
|
||||
def _get_db_path(self, user_id: str) -> str:
|
||||
if self.db_path:
|
||||
return self.db_path
|
||||
return get_user_db_path(user_id)
|
||||
|
||||
def _init_db(self, user_id: str):
|
||||
"""Initialize database tables for OAuth tokens."""
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
# Ensure directory exists
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS wix_oauth_tokens (
|
||||
@@ -39,7 +49,6 @@ class WixOAuthService:
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
logger.info("Wix OAuth database initialized.")
|
||||
|
||||
def store_tokens(
|
||||
self,
|
||||
@@ -69,11 +78,15 @@ class WixOAuthService:
|
||||
True if tokens were stored successfully
|
||||
"""
|
||||
try:
|
||||
# Ensure DB is initialized for this user
|
||||
self._init_db(user_id)
|
||||
db_path = self._get_db_path(user_id)
|
||||
|
||||
expires_at = None
|
||||
if expires_in:
|
||||
expires_at = datetime.now() + timedelta(seconds=expires_in)
|
||||
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO wix_oauth_tokens
|
||||
@@ -92,7 +105,14 @@ class WixOAuthService:
|
||||
def get_user_tokens(self, user_id: str) -> List[Dict[str, Any]]:
|
||||
"""Get all active Wix tokens for a user."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
# Ensure database tables exist to prevent 'no such table' errors
|
||||
self._init_db(user_id)
|
||||
|
||||
db_path = self._get_db_path(user_id)
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT id, access_token, refresh_token, token_type, expires_at, expires_in, scope, site_id, member_id, created_at
|
||||
@@ -125,7 +145,22 @@ class WixOAuthService:
|
||||
def get_user_token_status(self, user_id: str) -> Dict[str, Any]:
|
||||
"""Get detailed token status for a user including expired tokens."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
# Ensure database tables exist to prevent 'no such table' errors
|
||||
self._init_db(user_id)
|
||||
|
||||
db_path = self._get_db_path(user_id)
|
||||
if not os.path.exists(db_path):
|
||||
return {
|
||||
"has_tokens": False,
|
||||
"has_active_tokens": False,
|
||||
"has_expired_tokens": False,
|
||||
"active_tokens": [],
|
||||
"expired_tokens": [],
|
||||
"total_tokens": 0,
|
||||
"last_token_date": None
|
||||
}
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Get all tokens (active and expired)
|
||||
@@ -213,11 +248,15 @@ class WixOAuthService:
|
||||
) -> bool:
|
||||
"""Update tokens for a user (e.g., after refresh)."""
|
||||
try:
|
||||
# Ensure DB initialized for this user
|
||||
self._init_db(user_id)
|
||||
db_path = self._get_db_path(user_id)
|
||||
|
||||
expires_at = None
|
||||
if expires_in:
|
||||
expires_at = datetime.now() + timedelta(seconds=expires_in)
|
||||
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
if refresh_token:
|
||||
cursor.execute('''
|
||||
@@ -245,7 +284,8 @@ class WixOAuthService:
|
||||
def revoke_token(self, user_id: str, token_id: int) -> bool:
|
||||
"""Revoke a Wix OAuth token."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
UPDATE wix_oauth_tokens
|
||||
|
||||
@@ -13,10 +13,13 @@ from loguru import logger
|
||||
import json
|
||||
import base64
|
||||
|
||||
from services.database import get_user_db_path
|
||||
|
||||
class WordPressOAuthService:
|
||||
"""Manages WordPress.com OAuth2 authentication flow."""
|
||||
|
||||
def __init__(self, db_path: str = "alwrity.db"):
|
||||
def __init__(self, db_path: str = None):
|
||||
# db_path is deprecated in favor of dynamic user_id based paths
|
||||
self.db_path = db_path
|
||||
# WordPress.com OAuth2 credentials
|
||||
self.client_id = os.getenv('WORDPRESS_CLIENT_ID', '')
|
||||
@@ -29,11 +32,15 @@ class WordPressOAuthService:
|
||||
logger.error("WordPress OAuth client credentials not configured. Please set WORDPRESS_CLIENT_ID and WORDPRESS_CLIENT_SECRET environment variables with valid WordPress.com application credentials.")
|
||||
logger.error("To get credentials: 1. Go to https://developer.wordpress.com/apps/ 2. Create a new application 3. Set redirect URI to: https://your-domain.com/wp/callback")
|
||||
|
||||
self._init_db()
|
||||
def _get_db_path(self, user_id: str) -> str:
|
||||
return get_user_db_path(user_id)
|
||||
|
||||
def _init_db(self):
|
||||
def _init_db(self, user_id: str):
|
||||
"""Initialize database tables for OAuth tokens."""
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS wordpress_oauth_tokens (
|
||||
@@ -61,7 +68,6 @@ class WordPressOAuthService:
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
logger.info("WordPress OAuth database initialized.")
|
||||
|
||||
def generate_authorization_url(self, user_id: str, scope: str = "global") -> Dict[str, Any]:
|
||||
"""Generate WordPress OAuth2 authorization URL."""
|
||||
@@ -71,11 +77,15 @@ class WordPressOAuthService:
|
||||
logger.error("WordPress OAuth client credentials not configured")
|
||||
return None
|
||||
|
||||
# Generate secure state parameter
|
||||
state = secrets.token_urlsafe(32)
|
||||
# Generate secure state parameter with user_id for routing
|
||||
random_token = secrets.token_urlsafe(32)
|
||||
state = f"{user_id}:{random_token}"
|
||||
|
||||
# Store state in database for validation
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
self._init_db(user_id)
|
||||
db_path = self._get_db_path(user_id)
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO wordpress_oauth_states (state, user_id)
|
||||
@@ -111,8 +121,20 @@ class WordPressOAuthService:
|
||||
try:
|
||||
logger.info(f"WordPress OAuth callback started - code: {code[:20]}..., state: {state[:20]}...")
|
||||
|
||||
# Extract user_id from state
|
||||
if ':' not in state:
|
||||
logger.error(f"Invalid WordPress state format: {state}")
|
||||
return None
|
||||
|
||||
user_id = state.split(':')[0]
|
||||
db_path = self._get_db_path(user_id)
|
||||
|
||||
if not os.path.exists(db_path):
|
||||
logger.error(f"User database not found for user {user_id}")
|
||||
return None
|
||||
|
||||
# Validate state parameter
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT user_id FROM wordpress_oauth_states
|
||||
@@ -124,9 +146,6 @@ class WordPressOAuthService:
|
||||
logger.error(f"Invalid or expired state parameter: {state}")
|
||||
return None
|
||||
|
||||
user_id = result[0]
|
||||
logger.info(f"WordPress OAuth: State validated for user {user_id}")
|
||||
|
||||
# Clean up used state
|
||||
cursor.execute('DELETE FROM wordpress_oauth_states WHERE state = ?', (state,))
|
||||
conn.commit()
|
||||
@@ -163,7 +182,7 @@ class WordPressOAuthService:
|
||||
# Calculate expiration (WordPress tokens typically expire in 2 weeks)
|
||||
expires_at = datetime.now() + timedelta(days=14)
|
||||
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO wordpress_oauth_tokens
|
||||
@@ -190,7 +209,14 @@ class WordPressOAuthService:
|
||||
def get_user_tokens(self, user_id: str) -> List[Dict[str, Any]]:
|
||||
"""Get all active WordPress tokens for a user."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
# Ensure database tables exist to prevent 'no such table' errors
|
||||
self._init_db(user_id)
|
||||
|
||||
db_path = self._get_db_path(user_id)
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT id, access_token, token_type, expires_at, scope, blog_id, blog_url, created_at
|
||||
@@ -221,7 +247,22 @@ class WordPressOAuthService:
|
||||
def get_user_token_status(self, user_id: str) -> Dict[str, Any]:
|
||||
"""Get detailed token status for a user including expired tokens."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
# Ensure database tables exist to prevent 'no such table' errors
|
||||
self._init_db(user_id)
|
||||
|
||||
db_path = self._get_db_path(user_id)
|
||||
if not os.path.exists(db_path):
|
||||
return {
|
||||
"has_tokens": False,
|
||||
"has_active_tokens": False,
|
||||
"has_expired_tokens": False,
|
||||
"active_tokens": [],
|
||||
"expired_tokens": [],
|
||||
"total_tokens": 0,
|
||||
"last_token_date": None
|
||||
}
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Get all tokens (active and expired)
|
||||
@@ -318,7 +359,8 @@ class WordPressOAuthService:
|
||||
def revoke_token(self, user_id: str, token_id: int) -> bool:
|
||||
"""Revoke a WordPress OAuth token."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
UPDATE wordpress_oauth_tokens
|
||||
|
||||
@@ -15,13 +15,57 @@ from .wordpress_content import WordPressContentManager
|
||||
import sqlite3
|
||||
|
||||
|
||||
from services.database import get_user_db_path
|
||||
|
||||
class WordPressPublisher:
|
||||
"""High-level WordPress publishing service."""
|
||||
"""Handles publishing content to WordPress."""
|
||||
|
||||
def __init__(self, db_path: str = "alwrity.db"):
|
||||
"""Initialize WordPress publisher."""
|
||||
self.wp_service = WordPressService(db_path)
|
||||
def __init__(self, db_path: str = None):
|
||||
# db_path is deprecated
|
||||
self.db_path = db_path
|
||||
|
||||
def _get_db_path(self, user_id: str) -> str:
|
||||
return get_user_db_path(user_id)
|
||||
|
||||
def _init_db(self, user_id: str):
|
||||
"""Initialize database tables for published posts."""
|
||||
db_path = self._get_db_path(user_id)
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS wordpress_posts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id TEXT NOT NULL,
|
||||
wp_post_id INTEGER NOT NULL,
|
||||
wp_url TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
|
||||
def save_post_info(self, user_id: str, wp_post_id: int, wp_url: str, title: str, status: str) -> bool:
|
||||
"""Save information about a published post."""
|
||||
try:
|
||||
self._init_db(user_id)
|
||||
db_path = self._get_db_path(user_id)
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO wordpress_posts (user_id, wp_post_id, wp_url, title, status)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
''', (user_id, wp_post_id, wp_url, title, status))
|
||||
conn.commit()
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving WordPress post info: {e}")
|
||||
return False
|
||||
|
||||
def publish_blog_post(self, user_id: str, site_id: int,
|
||||
title: str, content: str,
|
||||
|
||||
@@ -17,19 +17,27 @@ from PIL import Image
|
||||
from loguru import logger
|
||||
|
||||
|
||||
from services.database import get_user_db_path
|
||||
|
||||
class WordPressService:
|
||||
"""Main WordPress service class for managing WordPress integrations."""
|
||||
"""Service for WordPress integration."""
|
||||
|
||||
def __init__(self, db_path: str = "alwrity.db"):
|
||||
"""Initialize WordPress service with database path."""
|
||||
def __init__(self, db_path: str = None):
|
||||
# db_path is deprecated in favor of dynamic user_id based paths
|
||||
self.db_path = db_path
|
||||
self.api_version = "v2"
|
||||
self._ensure_tables()
|
||||
# self._ensure_tables() # Deferred to per-user calls
|
||||
|
||||
def _ensure_tables(self) -> None:
|
||||
def _get_db_path(self, user_id: str) -> str:
|
||||
return get_user_db_path(user_id)
|
||||
|
||||
def _ensure_tables(self, user_id: str) -> None:
|
||||
"""Ensure required database tables exist."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# WordPress sites table
|
||||
@@ -64,10 +72,10 @@ class WordPressService:
|
||||
''')
|
||||
|
||||
conn.commit()
|
||||
logger.info("WordPress database tables ensured")
|
||||
# logger.info("WordPress database tables ensured")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error ensuring WordPress tables: {e}")
|
||||
logger.error(f"Error ensuring WordPress tables for user {user_id}: {e}")
|
||||
raise
|
||||
|
||||
def add_site(self, user_id: str, site_url: str, site_name: str, username: str, app_password: str) -> bool:
|
||||
@@ -82,7 +90,10 @@ class WordPressService:
|
||||
logger.error(f"Failed to connect to WordPress site: {site_url}")
|
||||
return False
|
||||
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
self._ensure_tables(user_id)
|
||||
db_path = self._get_db_path(user_id)
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT OR REPLACE INTO wordpress_sites
|
||||
@@ -101,8 +112,18 @@ class WordPressService:
|
||||
def get_user_sites(self, user_id: str) -> List[Dict[str, Any]]:
|
||||
"""Get all WordPress sites for a user."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Check if table exists
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='wordpress_sites'")
|
||||
if not cursor.fetchone():
|
||||
return []
|
||||
|
||||
cursor.execute('''
|
||||
SELECT id, site_url, site_name, username, is_active, created_at, updated_at
|
||||
FROM wordpress_sites
|
||||
@@ -129,16 +150,17 @@ class WordPressService:
|
||||
logger.error(f"Error getting WordPress sites for user {user_id}: {e}")
|
||||
return []
|
||||
|
||||
def get_site_credentials(self, site_id: int) -> Optional[Dict[str, str]]:
|
||||
def get_site_credentials(self, user_id: str, site_id: int) -> Optional[Dict[str, str]]:
|
||||
"""Get credentials for a specific WordPress site."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT site_url, username, app_password
|
||||
FROM wordpress_sites
|
||||
WHERE id = ? AND is_active = 1
|
||||
''', (site_id,))
|
||||
WHERE id = ? AND user_id = ? AND is_active = 1
|
||||
''', (site_id, user_id))
|
||||
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
@@ -174,7 +196,8 @@ class WordPressService:
|
||||
def disconnect_site(self, user_id: str, site_id: int) -> bool:
|
||||
"""Disconnect a WordPress site."""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
db_path = self._get_db_path(user_id)
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
UPDATE wordpress_sites
|
||||
@@ -190,10 +213,10 @@ class WordPressService:
|
||||
logger.error(f"Error disconnecting WordPress site {site_id}: {e}")
|
||||
return False
|
||||
|
||||
def get_site_info(self, site_id: int) -> Optional[Dict[str, Any]]:
|
||||
def get_site_info(self, user_id: str, site_id: int) -> Optional[Dict[str, Any]]:
|
||||
"""Get detailed information about a WordPress site."""
|
||||
try:
|
||||
credentials = self.get_site_credentials(site_id)
|
||||
credentials = self.get_site_credentials(user_id, site_id)
|
||||
if not credentials:
|
||||
return None
|
||||
|
||||
@@ -224,26 +247,40 @@ class WordPressService:
|
||||
|
||||
def get_posts_for_all_sites(self, user_id: str) -> List[Dict[str, Any]]:
|
||||
"""Get all tracked WordPress posts for all sites of a user."""
|
||||
with sqlite3.connect(self.db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT wp.id, wp.wordpress_post_id, wp.title, wp.status, wp.published_at, wp.last_updated_at,
|
||||
ws.site_name, ws.site_url
|
||||
FROM wordpress_posts wp
|
||||
JOIN wordpress_sites ws ON wp.site_id = ws.id
|
||||
WHERE wp.user_id = ? AND ws.is_active = TRUE
|
||||
ORDER BY wp.published_at DESC
|
||||
''', (user_id,))
|
||||
posts = []
|
||||
for post_data in cursor.fetchall():
|
||||
posts.append({
|
||||
"id": post_data[0],
|
||||
"wp_post_id": post_data[1],
|
||||
"title": post_data[2],
|
||||
"status": post_data[3],
|
||||
"published_at": post_data[4],
|
||||
"created_at": post_data[5],
|
||||
"site_name": post_data[6],
|
||||
"site_url": post_data[7]
|
||||
})
|
||||
return posts
|
||||
db_path = self._get_db_path(user_id)
|
||||
if not os.path.exists(db_path):
|
||||
return []
|
||||
|
||||
try:
|
||||
with sqlite3.connect(db_path) as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Check if table exists
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='wordpress_posts'")
|
||||
if not cursor.fetchone():
|
||||
return []
|
||||
|
||||
cursor.execute('''
|
||||
SELECT wp.id, wp.wp_post_id, wp.title, wp.status, wp.published_at, wp.created_at,
|
||||
ws.site_name, ws.site_url
|
||||
FROM wordpress_posts wp
|
||||
JOIN wordpress_sites ws ON wp.site_id = ws.id
|
||||
WHERE wp.user_id = ? AND ws.is_active = 1
|
||||
ORDER BY wp.published_at DESC
|
||||
''', (user_id,))
|
||||
posts = []
|
||||
for post_data in cursor.fetchall():
|
||||
posts.append({
|
||||
"id": post_data[0],
|
||||
"wp_post_id": post_data[1],
|
||||
"title": post_data[2],
|
||||
"status": post_data[3],
|
||||
"published_at": post_data[4],
|
||||
"created_at": post_data[5],
|
||||
"site_name": post_data[6],
|
||||
"site_url": post_data[7]
|
||||
})
|
||||
return posts
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting posts for user {user_id}: {e}")
|
||||
return []
|
||||
Reference in New Issue
Block a user