Исправлено: - engine: _calculate_score() при ошибке загрузки (score не 100 для мёртвых сайтов) - privacy_link: замена page.goto() на aiohttp (гонка данных) - form_checkbox: 3-уровневый поиск label + dark-pattern detection - server_location: синхронный urllib → aiohttp - api/server: перепутанные request/response модели в batch - main.py: csv --checks флаг - engine: фазовая модель (cookie_audit отдельно, остальные параллельно) - engine: retry-логика (3 попытки, exponential backoff) - PDF: emoji заменены на текст (WeasyPrint) Добавлено: - consent_doc: проверка отдельного согласия на обработку ПД - operator_info: проверка ИНН/ОГРН/DPO (читает главную + политику) - cookie_audit: 4-фазный аудит (load→detect→reject→compare) - llm_policy: опциональный LLM-анализ 16 разделов (--deep) - external_scripts: YAML-словари trackers.yaml (critical/warning/russian) - logging (output.py), .gitignore, pyproject.toml - smoke-тесты (16/16)
102 lines
3.6 KiB
Python
102 lines
3.6 KiB
Python
"""
|
|
FastAPI сервер для 152-ФЗ Scanner.
|
|
REST API для запуска сканирования и получения отчетов.
|
|
"""
|
|
from fastapi import FastAPI, HTTPException, BackgroundTasks
|
|
from scanner.engine import ScannerEngine
|
|
from api.schemas import (
|
|
HealthResponse,
|
|
ScanRequest,
|
|
CSVScanRequest,
|
|
ScanResultResponse,
|
|
BatchScanResultResponse,
|
|
)
|
|
|
|
app = FastAPI(
|
|
title="152-ФЗ Scanner API",
|
|
description="Автоматизированный сканер для аудита сайтов по 152-ФЗ",
|
|
version="1.0.0",
|
|
)
|
|
|
|
scanner = ScannerEngine()
|
|
|
|
|
|
@app.get("/health", response_model=HealthResponse, tags=["Health"])
|
|
async def health_check():
|
|
"""Проверка состояния API."""
|
|
return HealthResponse(status="ok")
|
|
|
|
|
|
@app.post("/api/scan", tags=["Scan"])
|
|
async def scan_single(request: ScanRequest):
|
|
"""
|
|
Просканировать один URL.
|
|
|
|
Возвращает результаты всех проверок и итоговый балл.
|
|
"""
|
|
try:
|
|
result = await scanner.scan_url(request.url, request.checks)
|
|
|
|
checks_passed = sum(1 for c in result.checks if c.status.value == "pass")
|
|
checks_failed = sum(1 for c in result.checks if c.status.value == "fail")
|
|
checks_errors = sum(1 for c in result.checks if c.status.value == "error")
|
|
|
|
return ScanResultResponse(
|
|
scan_id=result.url.replace("://", "_").replace("/", "_")[:30],
|
|
url=result.url,
|
|
score=result.score,
|
|
is_compliant=result.is_compliant,
|
|
summary=result.summary,
|
|
checks_passed=checks_passed,
|
|
checks_failed=checks_failed,
|
|
checks_errors=checks_errors,
|
|
)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@app.post("/api/scan/batch", tags=["Scan"])
|
|
async def scan_batch(request: CSVScanRequest):
|
|
"""
|
|
Просканировать несколько URL.
|
|
|
|
Запрос: {"urls": ["https://site1.ru", "https://site2.ru"], "checks": [...]}
|
|
"""
|
|
try:
|
|
results = await scanner.scan_urls_batch(request.urls, request.checks)
|
|
|
|
scan_results = []
|
|
for r in results:
|
|
checks_passed = sum(1 for c in r.checks if c.status.value == "pass")
|
|
checks_failed = sum(1 for c in r.checks if c.status.value == "fail")
|
|
checks_errors = sum(1 for c in r.checks if c.status.value == "error")
|
|
|
|
scan_results.append(ScanResultResponse(
|
|
scan_id=r.url.replace("://", "_").replace("/", "_")[:30],
|
|
url=r.url,
|
|
score=r.score,
|
|
is_compliant=r.is_compliant,
|
|
summary=r.summary,
|
|
checks_passed=checks_passed,
|
|
checks_failed=checks_failed,
|
|
checks_errors=checks_errors,
|
|
))
|
|
|
|
compliant_count = sum(1 for r in scan_results if r.is_compliant)
|
|
|
|
return BatchScanResultResponse(
|
|
scan_id="batch",
|
|
total=len(scan_results),
|
|
results=scan_results,
|
|
compliant_count=compliant_count,
|
|
non_compliant_count=len(scan_results) - compliant_count,
|
|
)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@app.get("/api/results", tags=["Results"])
|
|
async def get_results():
|
|
"""Получить список всех выполненных сканирований."""
|
|
return {"message": "Результаты хранятся в памяти (v1). В будущем будет JSON-файл или БД."}
|