"""CPCHECK - 云服务器端口检测工具 API""" import os from pathlib import Path from fastapi import FastAPI, HTTPException from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from pydantic import BaseModel, Field, field_validator from app.config import HOST, PORT from app.detector import CheckResult, run_check BASE_DIR = Path(__file__).resolve().parent.parent STATIC_DIR = BASE_DIR / "static" app = FastAPI( title="CPCHECK", description="云服务器端口检测工具 - 支持 TCP/UDP 端口检测与问题诊断", version="1.0.0", ) if STATIC_DIR.exists(): app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") class CheckRequest(BaseModel): host: str = Field(..., min_length=1, max_length=255, description="域名或 IP 地址") port: int = Field(..., ge=1, le=65535, description="端口号") protocol: str = Field(default="tcp", description="协议类型: tcp 或 udp") @field_validator("protocol") @classmethod def validate_protocol(cls, v: str) -> str: v = v.lower().strip() if v not in ("tcp", "udp"): raise ValueError("协议必须是 tcp 或 udp") return v @field_validator("host") @classmethod def validate_host(cls, v: str) -> str: v = v.strip() if not v: raise ValueError("主机地址不能为空") return v class CheckResponse(BaseModel): success: bool data: dict | None = None error: str | None = None def result_to_dict(result: CheckResult) -> dict: return { "host": result.host, "port": result.port, "protocol": result.protocol, "resolved_ip": result.resolved_ip, "port_status": result.port_status.value, "diagnosis": result.diagnosis.value, "diagnosis_message": result.diagnosis_message, "host_reachable": result.host_reachable, "dns_ok": result.dns_ok, "latency_ms": result.latency_ms, "details": result.details, "elapsed_ms": result.elapsed_ms, } @app.get("/") async def index(): index_file = STATIC_DIR / "index.html" if index_file.exists(): return FileResponse(str(index_file)) return {"message": "CPCHECK API is running. Visit /docs for API documentation."} @app.get("/api/health") async def health(): return {"status": "ok", "service": "cpcheck"} @app.post("/api/check", response_model=CheckResponse) async def check_port(req: CheckRequest): try: result = run_check(req.host, req.port, req.protocol) return CheckResponse(success=True, data=result_to_dict(result)) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) def start(): import uvicorn uvicorn.run("app.main:app", host=HOST, port=PORT, reload=False) if __name__ == "__main__": start()