dev
This commit is contained in:
1
domain-api/app/api/__init__.py
Normal file
1
domain-api/app/api/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
1
domain-api/app/api/routes/__init__.py
Normal file
1
domain-api/app/api/routes/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
23
domain-api/app/api/routes/auth.py
Normal file
23
domain-api/app/api/routes/auth.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.core.config import settings
|
||||
from app.schemas.auth import LoginRequest
|
||||
from app.schemas.common import ApiResponse
|
||||
|
||||
router = APIRouter(tags=["auth"])
|
||||
|
||||
|
||||
@router.post("/auth/login", response_model=ApiResponse)
|
||||
def login(payload: LoginRequest) -> ApiResponse:
|
||||
if payload.username != settings.admin_username or payload.password != settings.admin_password:
|
||||
return ApiResponse(code=1, message="账号或密码错误", data=None)
|
||||
token = f"domain-web-token-{payload.username}"
|
||||
return ApiResponse(
|
||||
data={
|
||||
"access_token": token,
|
||||
"user": {
|
||||
"username": payload.username,
|
||||
"display_name": "管理员" if payload.username == "admin" else payload.username,
|
||||
},
|
||||
}
|
||||
)
|
||||
11
domain-api/app/api/routes/dashboard.py
Normal file
11
domain-api/app/api/routes/dashboard.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.dashboard import fetch_overview
|
||||
|
||||
router = APIRouter(tags=["dashboard"])
|
||||
|
||||
|
||||
@router.get("/dashboard/overview", response_model=ApiResponse)
|
||||
def overview() -> ApiResponse:
|
||||
return ApiResponse(data=fetch_overview())
|
||||
40
domain-api/app/api/routes/detect.py
Normal file
40
domain-api/app/api/routes/detect.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.detect_service import get_detect_status
|
||||
from app.services.worker_control_service import start_worker, stop_worker
|
||||
|
||||
router = APIRouter(tags=["detect"])
|
||||
|
||||
|
||||
@router.get("/detect/status", response_model=ApiResponse)
|
||||
def detect_status() -> ApiResponse:
|
||||
return ApiResponse(data=get_detect_status())
|
||||
|
||||
|
||||
@router.post("/detect/start", response_model=ApiResponse)
|
||||
def start_detect() -> ApiResponse:
|
||||
ok, message = start_worker()
|
||||
return ApiResponse(
|
||||
code=0 if ok else 1,
|
||||
message=message,
|
||||
data={
|
||||
"action": "start",
|
||||
"poll_after_seconds": 2,
|
||||
"refresh_status": True,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/detect/stop", response_model=ApiResponse)
|
||||
def stop_detect() -> ApiResponse:
|
||||
ok, message = stop_worker()
|
||||
return ApiResponse(
|
||||
code=0 if ok else 1,
|
||||
message=message,
|
||||
data={
|
||||
"action": "stop",
|
||||
"poll_after_seconds": 2,
|
||||
"refresh_status": True,
|
||||
},
|
||||
)
|
||||
50
domain-api/app/api/routes/domains.py
Normal file
50
domain-api/app/api/routes/domains.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from fastapi import APIRouter, Query
|
||||
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.domains_service import batch_update_domains, domain_filter_options, fetch_domains
|
||||
|
||||
router = APIRouter(tags=["domains"])
|
||||
|
||||
|
||||
@router.get("/domains/filters", response_model=ApiResponse)
|
||||
def domain_filters() -> ApiResponse:
|
||||
return ApiResponse(data=domain_filter_options())
|
||||
|
||||
|
||||
@router.get("/domains", response_model=ApiResponse)
|
||||
def domain_list(
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=200),
|
||||
domain_keyword: str | None = Query(default=None),
|
||||
register_status: int | None = Query(default=None),
|
||||
detect_status: int | None = Query(default=None),
|
||||
use_status: int | None = Query(default=None),
|
||||
review_status: int | None = Query(default=None),
|
||||
has_beian: int | None = Query(default=None),
|
||||
beian_year: int | None = Query(default=None),
|
||||
snapshot_year: str | None = Query(default=None),
|
||||
website_url: str | None = Query(default=None),
|
||||
backlink_gt_10: bool | None = Query(default=None),
|
||||
) -> ApiResponse:
|
||||
return ApiResponse(
|
||||
data=fetch_domains(
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
domain_keyword=domain_keyword,
|
||||
register_status=register_status,
|
||||
detect_status=detect_status,
|
||||
use_status=use_status,
|
||||
review_status=review_status,
|
||||
has_beian=has_beian,
|
||||
beian_year=beian_year,
|
||||
snapshot_year=snapshot_year,
|
||||
website_url=website_url,
|
||||
backlink_gt_10=backlink_gt_10,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@router.post("/domains/batch-update", response_model=ApiResponse)
|
||||
def domain_batch_update(payload: dict) -> ApiResponse:
|
||||
result = batch_update_domains(payload.get("domain_ids", []), payload.get("updates", {}))
|
||||
return ApiResponse(message=f"成功更新 {result['updated_count']} 个域名", data=result)
|
||||
29
domain-api/app/api/routes/exports.py
Normal file
29
domain-api/app/api/routes/exports.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.export_service import create_export_file, list_exports
|
||||
from app.core.files import exports_root
|
||||
|
||||
router = APIRouter(tags=["exports"])
|
||||
|
||||
|
||||
@router.get("/exports", response_model=ApiResponse)
|
||||
def export_list() -> ApiResponse:
|
||||
return ApiResponse(data=list_exports())
|
||||
|
||||
|
||||
@router.post("/exports/run", response_model=ApiResponse)
|
||||
def export_run(payload: dict) -> ApiResponse:
|
||||
record = create_export_file(payload)
|
||||
return ApiResponse(message="导出文件已生成", data=record)
|
||||
|
||||
|
||||
@router.get("/exports/download/{filename}")
|
||||
def export_download(filename: str):
|
||||
path = exports_root() / Path(filename).name
|
||||
if not path.exists():
|
||||
raise HTTPException(status_code=404, detail="文件不存在")
|
||||
return FileResponse(path, filename=path.name)
|
||||
30
domain-api/app/api/routes/imports.py
Normal file
30
domain-api/app/api/routes/imports.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from fastapi import APIRouter, File, UploadFile
|
||||
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.import_task_service import create_import_task, list_import_tasks, retry_import_task
|
||||
from app.services.imports_service import get_import_summary
|
||||
|
||||
router = APIRouter(tags=["imports"])
|
||||
|
||||
|
||||
@router.get("/imports/summary", response_model=ApiResponse)
|
||||
def imports_summary() -> ApiResponse:
|
||||
return ApiResponse(data=get_import_summary())
|
||||
|
||||
|
||||
@router.get("/imports/tasks", response_model=ApiResponse)
|
||||
def import_tasks() -> ApiResponse:
|
||||
return ApiResponse(data=list_import_tasks())
|
||||
|
||||
|
||||
@router.post("/imports/upload", response_model=ApiResponse)
|
||||
async def upload_import(file: UploadFile = File(...)) -> ApiResponse:
|
||||
content = await file.read()
|
||||
task = create_import_task(content, file.filename or "domains.txt")
|
||||
return ApiResponse(message="导入任务已创建", data=task)
|
||||
|
||||
|
||||
@router.post("/imports/tasks/{task_id}/retry", response_model=ApiResponse)
|
||||
def import_task_retry(task_id: str) -> ApiResponse:
|
||||
task = retry_import_task(task_id)
|
||||
return ApiResponse(message="导入任务已重新加入队列", data=task)
|
||||
18
domain-api/app/api/routes/logs.py
Normal file
18
domain-api/app/api/routes/logs.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.logs_service import build_diagnostic_bundle, latest_logs as get_latest_logs
|
||||
|
||||
router = APIRouter(tags=["logs"])
|
||||
|
||||
|
||||
@router.get("/logs/latest", response_model=ApiResponse)
|
||||
def latest_logs() -> ApiResponse:
|
||||
return ApiResponse(data=get_latest_logs())
|
||||
|
||||
|
||||
@router.get("/logs/bundle")
|
||||
def download_logs_bundle() -> FileResponse:
|
||||
path, filename = build_diagnostic_bundle()
|
||||
return FileResponse(path=path, filename=filename, media_type="application/zip")
|
||||
23
domain-api/app/api/routes/runtime.py
Normal file
23
domain-api/app/api/routes/runtime.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.runtime_control_service import runtime_action
|
||||
from app.services.runtime_status_service import get_runtime_preflight, get_runtime_status
|
||||
|
||||
router = APIRouter(tags=["runtime"])
|
||||
|
||||
|
||||
@router.get("/runtime/status", response_model=ApiResponse)
|
||||
def runtime_status() -> ApiResponse:
|
||||
return ApiResponse(data=get_runtime_status())
|
||||
|
||||
|
||||
@router.get("/runtime/preflight", response_model=ApiResponse)
|
||||
def runtime_preflight() -> ApiResponse:
|
||||
return ApiResponse(data=get_runtime_preflight())
|
||||
|
||||
|
||||
@router.post("/runtime/actions/{action}", response_model=ApiResponse)
|
||||
def runtime_action_trigger(action: str) -> ApiResponse:
|
||||
ok, message, data = runtime_action(action)
|
||||
return ApiResponse(code=0 if ok else 1, message=message, data=data)
|
||||
69
domain-api/app/api/routes/settings.py
Normal file
69
domain-api/app/api/routes/settings.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from app.core.files import settings_backup_root
|
||||
from app.schemas.common import ApiResponse
|
||||
from app.services.settings_service import (
|
||||
backup_current_settings,
|
||||
export_settings_snapshot,
|
||||
get_settings_payload,
|
||||
import_settings_snapshot,
|
||||
list_settings_backups,
|
||||
update_settings_payload,
|
||||
validate_settings_payload,
|
||||
)
|
||||
|
||||
router = APIRouter(tags=["settings"])
|
||||
|
||||
|
||||
@router.get("/settings", response_model=ApiResponse)
|
||||
def get_settings() -> ApiResponse:
|
||||
return ApiResponse(data=get_settings_payload())
|
||||
|
||||
|
||||
@router.put("/settings", response_model=ApiResponse)
|
||||
def update_settings(payload: dict) -> ApiResponse:
|
||||
return ApiResponse(message="settings updated", data=update_settings_payload(payload))
|
||||
|
||||
|
||||
@router.get("/settings/export", response_model=ApiResponse)
|
||||
def export_settings() -> ApiResponse:
|
||||
return ApiResponse(message="settings exported", data=export_settings_snapshot())
|
||||
|
||||
|
||||
@router.post("/settings/import", response_model=ApiResponse)
|
||||
def import_settings(payload: dict) -> ApiResponse:
|
||||
try:
|
||||
settings_payload = import_settings_snapshot(payload)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
return ApiResponse(message="settings imported", data=settings_payload)
|
||||
|
||||
|
||||
@router.post("/settings/validate-import", response_model=ApiResponse)
|
||||
def validate_import_settings(payload: dict) -> ApiResponse:
|
||||
try:
|
||||
validate_settings_payload(payload.get("settings", payload))
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
return ApiResponse(message="settings payload valid", data={"valid": True})
|
||||
|
||||
|
||||
@router.post("/settings/backup", response_model=ApiResponse)
|
||||
def create_settings_backup() -> ApiResponse:
|
||||
return ApiResponse(message="settings backed up", data=backup_current_settings("manual"))
|
||||
|
||||
|
||||
@router.get("/settings/backups", response_model=ApiResponse)
|
||||
def get_settings_backups() -> ApiResponse:
|
||||
return ApiResponse(data=list_settings_backups())
|
||||
|
||||
|
||||
@router.get("/settings/backups/download/{filename}")
|
||||
def download_settings_backup(filename: str):
|
||||
path = settings_backup_root() / Path(filename).name
|
||||
if not path.exists():
|
||||
raise HTTPException(status_code=404, detail="backup not found")
|
||||
return FileResponse(path, filename=path.name)
|
||||
Reference in New Issue
Block a user