refactor: FastAPI best practices — return types, Pydantic schemas, middleware, code dedup
- Все 18 роутов получили return type annotations
- Создан schemas.py с Pydantic-моделями (ScanOut, PackageOut, FindingOut, ...)
- API-роуты: response_model на list/detail/export/stats
- 404 через HTTPException(404) вместо {'detail':'Not found'} (200)
- RequestLoggingMiddleware: method, path, status, duration_ms
- Глобальный exception handler: ловит необработанные исключения → 500
- _parse_flagged(): вынесен дублирующийся string→bool
- parse_package_path(): общий для web.py и api_packages.py
- selectinload: вынесены в top-level imports
- harvester: makedirs/mkdtemp/rmtree обёрнуты в asyncio.to_thread()
This commit is contained in:
102
guarddog_nexus/schemas.py
Normal file
102
guarddog_nexus/schemas.py
Normal file
@@ -0,0 +1,102 @@
|
||||
"""Pydantic schemas for API request/response models."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ScanOut(BaseModel):
|
||||
id: int
|
||||
package_name: str
|
||||
package_version: str
|
||||
ecosystem: str
|
||||
repository: str
|
||||
status: str
|
||||
total_findings: int
|
||||
flagged: bool
|
||||
started_at: datetime | None = None
|
||||
finished_at: datetime | None = None
|
||||
error_message: str | None = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class ScanListResponse(BaseModel):
|
||||
total: int
|
||||
limit: int
|
||||
offset: int
|
||||
scans: list[ScanOut]
|
||||
|
||||
|
||||
class ScanDetailOut(ScanOut):
|
||||
nexus_asset_url: str | None = None
|
||||
sha256: str | None = None
|
||||
initiator: str | None = None
|
||||
source_ip: str | None = None
|
||||
findings: list[dict] = []
|
||||
|
||||
|
||||
class FindingOut(BaseModel):
|
||||
id: int
|
||||
scan_id: int
|
||||
rule: str = ""
|
||||
severity: str = ""
|
||||
message: str = ""
|
||||
location: str = ""
|
||||
code: str = ""
|
||||
report: dict | None = None
|
||||
created_at: datetime | None = None
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class FindingsListResponse(BaseModel):
|
||||
total: int
|
||||
limit: int
|
||||
offset: int
|
||||
findings: list[FindingOut]
|
||||
|
||||
|
||||
class PackageOut(BaseModel):
|
||||
name: str
|
||||
version: str
|
||||
ecosystem: str
|
||||
repository: str
|
||||
last_scanned_at: datetime | None = None
|
||||
flagged: bool
|
||||
total_findings: int
|
||||
latest_scan_id: int
|
||||
|
||||
|
||||
class PackageListResponse(BaseModel):
|
||||
total: int
|
||||
limit: int
|
||||
offset: int
|
||||
packages: list[PackageOut]
|
||||
|
||||
|
||||
class PackageScanOut(BaseModel):
|
||||
id: int
|
||||
status: str
|
||||
total_findings: int
|
||||
flagged: bool
|
||||
started_at: datetime | None = None
|
||||
|
||||
|
||||
class PackageDetailOut(BaseModel):
|
||||
name: str
|
||||
version: str
|
||||
ecosystem: str
|
||||
repository: str
|
||||
flagged: bool
|
||||
scans: list[PackageScanOut]
|
||||
findings: list[dict]
|
||||
|
||||
|
||||
class StatsResponse(BaseModel):
|
||||
total_scans: int
|
||||
flagged_scans: int
|
||||
recent_flagged: int
|
||||
total_findings: int
|
||||
top_rules: list[dict]
|
||||
latest_scan_at: datetime | None = None
|
||||
Reference in New Issue
Block a user