Files
weibo_signin/backend/signin_executor/app/main.py

229 lines
7.2 KiB
Python
Raw Normal View History

2026-03-09 14:05:00 +08:00
"""
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
logger = logging.getLogger(__name__)
2026-03-09 14:05:00 +08:00
# 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"
)