Initial release: CPCHECK cloud port detection tool
Web-based TCP/UDP port checker with firewall/GFW diagnosis, PM2 deployment config, and Ubuntu one-click install script. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+101
@@ -0,0 +1,101 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user