domains.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. from fastapi import APIRouter, Depends, HTTPException, Query
  2. from pydantic import BaseModel
  3. from sqlalchemy import select, func
  4. from sqlalchemy.ext.asyncio import AsyncSession
  5. from app.database import get_db
  6. from app.models.domain import MonitoredDomain
  7. from app.models.monitoring import SuperAdmin
  8. from app.schemas.domain import (
  9. MonitoredDomainCreate,
  10. MonitoredDomainResponse,
  11. )
  12. from app.services.domain_fetch import fetch_domain_transactions
  13. router = APIRouter(prefix="/api/domains", tags=["domains"])
  14. @router.post("/", response_model=MonitoredDomainResponse, status_code=201)
  15. async def add_domain(
  16. payload: MonitoredDomainCreate,
  17. db: AsyncSession = Depends(get_db),
  18. ):
  19. """添加需要监控的域名,同时创建对应的超管记录"""
  20. existing = await db.execute(
  21. select(MonitoredDomain).where(MonitoredDomain.domain == payload.domain)
  22. )
  23. if existing.scalar_one_or_none():
  24. raise HTTPException(status_code=409, detail="域名已在监控中")
  25. # 如果未指定超管,自动创建一条超管记录
  26. sa_id = payload.super_admin_id
  27. if sa_id is None:
  28. max_id_result = await db.execute(select(func.max(SuperAdmin.id)))
  29. max_id = max_id_result.scalar() or 0
  30. new_sa = SuperAdmin(
  31. id=max_id + 1,
  32. username=payload.domain,
  33. nickname=payload.domain,
  34. remark=payload.remark or None,
  35. )
  36. db.add(new_sa)
  37. await db.flush()
  38. sa_id = new_sa.id
  39. record = MonitoredDomain(domain=payload.domain, remark=payload.remark or None, super_admin_id=sa_id)
  40. db.add(record)
  41. await db.commit()
  42. await db.refresh(record)
  43. return record
  44. @router.get("/", response_model=list[MonitoredDomainResponse])
  45. async def list_domains(db: AsyncSession = Depends(get_db)):
  46. """获取所有已监控的域名列表"""
  47. result = await db.execute(select(MonitoredDomain))
  48. return result.scalars().all()
  49. class MonitoredDomainUpdate(BaseModel):
  50. """更新域名备注"""
  51. remark: str = ""
  52. @router.patch("/{domain_id}", response_model=MonitoredDomainResponse)
  53. async def update_domain_remark(
  54. domain_id: int,
  55. payload: MonitoredDomainUpdate,
  56. db: AsyncSession = Depends(get_db),
  57. ):
  58. """更新域名备注,并同步到关联的超管"""
  59. result = await db.execute(
  60. select(MonitoredDomain).where(MonitoredDomain.id == domain_id)
  61. )
  62. record = result.scalar_one_or_none()
  63. if not record:
  64. raise HTTPException(status_code=404, detail="域名不存在")
  65. record.remark = payload.remark or None
  66. # 同步到关联的超管
  67. if record.super_admin_id:
  68. sa_result = await db.execute(
  69. select(SuperAdmin).where(SuperAdmin.id == record.super_admin_id)
  70. )
  71. sa = sa_result.scalar_one_or_none()
  72. if sa:
  73. sa.remark = payload.remark or None
  74. await db.commit()
  75. await db.refresh(record)
  76. return record
  77. @router.delete("/{domain_id}", status_code=204)
  78. async def remove_domain(domain_id: int, db: AsyncSession = Depends(get_db)):
  79. """移除指定 ID 的监控域名"""
  80. result = await db.execute(
  81. select(MonitoredDomain).where(MonitoredDomain.id == domain_id)
  82. )
  83. record = result.scalar_one_or_none()
  84. if not record:
  85. raise HTTPException(status_code=404, detail="域名不存在")
  86. await db.delete(record)
  87. await db.commit()
  88. @router.get("/{domain_id}/transactions")
  89. async def get_domain_transactions(
  90. domain_id: int,
  91. fetch_date: str | None = Query(None, description="爬取指定日期,格式 YYYY-MM-DD,不传则查全部"),
  92. db: AsyncSession = Depends(get_db),
  93. ):
  94. """爬取指定域名的监控数据并入库"""
  95. result = await db.execute(
  96. select(MonitoredDomain).where(MonitoredDomain.id == domain_id)
  97. )
  98. record = result.scalar_one_or_none()
  99. if not record:
  100. raise HTTPException(status_code=404, detail="域名不存在")
  101. data = await fetch_domain_transactions(record.domain, db, fetch_date=fetch_date)
  102. return {"status": "ok", "domain": record.domain, "data": data}
  103. @router.post("/fetch-all", status_code=202)
  104. async def fetch_all_transactions(
  105. fetch_date: str | None = Query(None, description="爬取指定日期,格式 YYYY-MM-DD,不传则查全部"),
  106. db: AsyncSession = Depends(get_db),
  107. ):
  108. """批量爬取所有已启用域名的监控数据"""
  109. result = await db.execute(
  110. select(MonitoredDomain).where(MonitoredDomain.is_active == True)
  111. )
  112. domains = result.scalars().all()
  113. errors = []
  114. for d in domains:
  115. try:
  116. await fetch_domain_transactions(d.domain, db, fetch_date=fetch_date)
  117. except Exception as e:
  118. errors.append({"domain": d.domain, "error": str(e)})
  119. return {"status": "ok", "total": len(domains), "errors": errors}