227 lines
7.2 KiB
Python
227 lines
7.2 KiB
Python
"""
|
|
Weibo-HotSign Sign-in Executor Service
|
|
Core service that executes sign-in tasks and handles Weibo interactions
|
|
"""
|
|
|
|
from fastapi import FastAPI, BackgroundTasks, HTTPException, status, Depends, Request
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
import uvicorn
|
|
import asyncio
|
|
import httpx
|
|
import logging
|
|
from datetime import datetime
|
|
from typing import Dict, Any, Optional
|
|
import os
|
|
|
|
from app.config import settings
|
|
from app.services.signin_service import SignInService
|
|
from app.services.weibo_client import WeiboClient
|
|
from app.models.signin_models import SignInRequest, SignInResult, TaskStatus
|
|
|
|
# Initialize FastAPI app
|
|
app = FastAPI(
|
|
title="Weibo-HotSign Sign-in Executor",
|
|
description="Core service for executing Weibo super topic sign-in tasks",
|
|
version="1.0.0",
|
|
docs_url="/docs",
|
|
redoc_url="/redoc"
|
|
)
|
|
|
|
# CORS middleware
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["*"], # In production, specify actual origins
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# Initialize services
|
|
signin_service = SignInService()
|
|
weibo_client = WeiboClient()
|
|
|
|
@app.on_event("startup")
|
|
async def startup_event():
|
|
"""Initialize executor service on startup"""
|
|
print("🚀 Weibo-HotSign Sign-in Executor starting up...")
|
|
print(f"📡 Service Documentation: http://{settings.HOST}:{settings.PORT}/docs")
|
|
print("🔧 Ready to process sign-in tasks...")
|
|
|
|
@app.on_event("shutdown")
|
|
async def shutdown_event():
|
|
"""Cleanup on shutdown"""
|
|
print("👋 Weibo-HotSign Sign-in Executor shutting down...")
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
return {
|
|
"service": "Weibo-HotSign Sign-in Executor",
|
|
"status": "running",
|
|
"version": "1.0.0",
|
|
"description": "Core sign-in execution service for Weibo super topics",
|
|
"capabilities": [
|
|
"Weibo login and verification",
|
|
"Super topic sign-in automation",
|
|
"Anti-bot protection handling",
|
|
"Proxy integration",
|
|
"Browser fingerprint simulation"
|
|
]
|
|
}
|
|
|
|
@app.get("/health")
|
|
async def health_check():
|
|
"""Health check endpoint"""
|
|
return {
|
|
"status": "healthy",
|
|
"service": "signin-executor",
|
|
"timestamp": datetime.now().isoformat(),
|
|
"dependencies": {
|
|
"database": "connected",
|
|
"redis": "connected",
|
|
"proxy_pool": f"{settings.PROXY_POOL_URL}",
|
|
"browser_automation": f"{settings.BROWSER_AUTOMATION_URL}"
|
|
}
|
|
}
|
|
|
|
@app.post("/api/v1/signin/execute", response_model=SignInResult)
|
|
async def execute_signin_task(
|
|
signin_request: SignInRequest,
|
|
background_tasks: BackgroundTasks
|
|
):
|
|
"""
|
|
Execute sign-in task for specified account
|
|
This endpoint is called by the task scheduler
|
|
"""
|
|
try:
|
|
logger.info(f"🎯 Received sign-in request for account: {signin_request.account_id}")
|
|
|
|
# Execute sign-in in background to avoid timeout
|
|
background_tasks.add_task(
|
|
signin_service.execute_signin_task,
|
|
signin_request.account_id,
|
|
signin_request.task_id
|
|
)
|
|
|
|
# Return immediate response
|
|
return SignInResult(
|
|
task_id=signin_request.task_id,
|
|
account_id=signin_request.account_id,
|
|
status="accepted",
|
|
message="Sign-in task accepted and queued for execution",
|
|
started_at=datetime.now(),
|
|
estimated_completion=None
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"❌ Failed to accept sign-in task: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Failed to accept sign-in task: {str(e)}"
|
|
)
|
|
|
|
@app.get("/api/v1/signin/status/{task_id}", response_model=TaskStatus)
|
|
async def get_task_status(task_id: str):
|
|
"""Get status of a sign-in task"""
|
|
try:
|
|
status_info = await signin_service.get_task_status(task_id)
|
|
if not status_info:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Task {task_id} not found"
|
|
)
|
|
return status_info
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"❌ Error getting task status: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="Internal server error"
|
|
)
|
|
|
|
@app.post("/api/v1/signin/test")
|
|
async def test_signin_capability():
|
|
"""Test sign-in service capabilities (for debugging)"""
|
|
try:
|
|
# Test basic service connectivity
|
|
tests = {
|
|
"weibo_connectivity": await _test_weibo_connectivity(),
|
|
"proxy_pool_access": await _test_proxy_pool(),
|
|
"browser_automation": await _test_browser_automation(),
|
|
"database_connection": await _test_database_connection()
|
|
}
|
|
|
|
return {
|
|
"test_timestamp": datetime.now().isoformat(),
|
|
"tests": tests,
|
|
"overall_status": "operational" if all(tests.values()) else "degraded"
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"❌ Capability test failed: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Capability test failed: {str(e)}"
|
|
)
|
|
|
|
async def _test_weibo_connectivity() -> bool:
|
|
"""Test connectivity to Weibo"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
response = await client.get("https://weibo.com", follow_redirects=True)
|
|
return response.status_code == 200
|
|
except:
|
|
return False
|
|
|
|
async def _test_proxy_pool() -> bool:
|
|
"""Test proxy pool service availability"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
response = await client.get(f"{settings.PROXY_POOL_URL}/health", timeout=5.0)
|
|
return response.status_code == 200
|
|
except:
|
|
return False
|
|
|
|
async def _test_browser_automation() -> bool:
|
|
"""Test browser automation service availability"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
response = await client.get(f"{settings.BROWSER_AUTOMATION_URL}/health", timeout=5.0)
|
|
return response.status_code == 200
|
|
except:
|
|
return False
|
|
|
|
async def _test_database_connection() -> bool:
|
|
"""Test database connectivity"""
|
|
try:
|
|
# Simple database ping test
|
|
return True # Simplified for demo
|
|
except:
|
|
return False
|
|
|
|
@app.exception_handler(HTTPException)
|
|
async def http_exception_handler(request: Request, exc: HTTPException):
|
|
"""Global HTTP exception handler"""
|
|
return JSONResponse(
|
|
status_code=exc.status_code,
|
|
content={
|
|
"success": False,
|
|
"data": None,
|
|
"message": exc.detail,
|
|
"error": {
|
|
"code": f"HTTP_{exc.status_code}",
|
|
"details": []
|
|
}
|
|
}
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
host = os.getenv("HOST", settings.HOST)
|
|
port = int(os.getenv("PORT", settings.PORT))
|
|
uvicorn.run(
|
|
app,
|
|
host=host,
|
|
port=port,
|
|
log_level="info" if not settings.DEBUG else "debug"
|
|
)
|