180 lines
6.1 KiB
Python
180 lines
6.1 KiB
Python
import json
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from starlette.responses import StreamingResponse
|
|
|
|
from ..schemas.auth import User
|
|
from ..schemas.config import (
|
|
CredentialCreate,
|
|
CredentialRecord,
|
|
CredentialUpdate,
|
|
CredentialsList,
|
|
EntriesPayload,
|
|
)
|
|
from ..schemas.scanner import ScannerStatus
|
|
from ..services.config_repo import ConfigRepository
|
|
from ..services.match_store import MatchStore
|
|
from ..services.scanner_manager import ScannerManager
|
|
|
|
|
|
def get_router(
|
|
repo: ConfigRepository,
|
|
match_store: MatchStore,
|
|
scanner_manager: ScannerManager,
|
|
require_user,
|
|
token_verifier,
|
|
) -> APIRouter:
|
|
router = APIRouter()
|
|
|
|
def get_repo() -> ConfigRepository:
|
|
return repo
|
|
|
|
def get_match_store() -> MatchStore:
|
|
return match_store
|
|
|
|
def get_scanner() -> ScannerManager:
|
|
return scanner_manager
|
|
|
|
@router.get("/credentials", response_model=CredentialsList)
|
|
def list_credentials(
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
return CredentialsList(items=repository.list_credentials())
|
|
|
|
@router.get("/credentials/active", response_model=CredentialRecord)
|
|
def get_active_credentials(
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
data = repository.get_active_credentials()
|
|
if not data:
|
|
raise HTTPException(status_code=404, detail="Credentials not configured.")
|
|
return data
|
|
|
|
@router.post("/credentials", response_model=CredentialRecord)
|
|
def create_credentials(
|
|
payload: CredentialCreate,
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
return repository.create_credentials(payload)
|
|
|
|
@router.put("/credentials/{credential_id}", response_model=CredentialRecord)
|
|
def update_credentials(
|
|
credential_id: str,
|
|
payload: CredentialUpdate,
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
try:
|
|
return repository.update_credentials(credential_id, payload)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
@router.delete("/credentials/{credential_id}")
|
|
def delete_credentials(
|
|
credential_id: str,
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
try:
|
|
repository.delete_credentials(credential_id)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
return {"deleted": True}
|
|
|
|
@router.post("/credentials/{credential_id}/activate", response_model=CredentialRecord)
|
|
def activate_credentials(
|
|
credential_id: str,
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
try:
|
|
return repository.activate_credentials(credential_id)
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
@router.get("/keywords", response_model=EntriesPayload)
|
|
def get_keywords(
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
return EntriesPayload(items=repository.read_keywords())
|
|
|
|
@router.put("/keywords", response_model=EntriesPayload)
|
|
def put_keywords(
|
|
payload: EntriesPayload,
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
return EntriesPayload(items=repository.write_keywords(payload.items))
|
|
|
|
@router.get("/channels", response_model=EntriesPayload)
|
|
def get_channels(
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
return EntriesPayload(items=repository.read_channels())
|
|
|
|
@router.put("/channels", response_model=EntriesPayload)
|
|
def put_channels(
|
|
payload: EntriesPayload,
|
|
repository: ConfigRepository = Depends(get_repo),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
return EntriesPayload(items=repository.write_channels(payload.items))
|
|
|
|
@router.post("/scanner/start", response_model=ScannerStatus)
|
|
async def start_scanner(
|
|
scanner: ScannerManager = Depends(get_scanner),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
await scanner.start()
|
|
return await scanner.status()
|
|
|
|
@router.post("/scanner/stop", response_model=ScannerStatus)
|
|
async def stop_scanner(
|
|
scanner: ScannerManager = Depends(get_scanner),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
await scanner.stop()
|
|
return await scanner.status()
|
|
|
|
@router.get("/scanner/status", response_model=ScannerStatus)
|
|
async def get_status(
|
|
scanner: ScannerManager = Depends(get_scanner),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
return await scanner.status()
|
|
|
|
@router.get("/matches")
|
|
async def get_matches(
|
|
limit: int = Query(50, ge=1, le=200),
|
|
store: MatchStore = Depends(get_match_store),
|
|
current_user: User = Depends(require_user),
|
|
):
|
|
return await store.get_recent(limit)
|
|
|
|
@router.get("/matches/stream")
|
|
async def stream_matches(
|
|
store: MatchStore = Depends(get_match_store),
|
|
token: str | None = Query(default=None),
|
|
):
|
|
if not token:
|
|
raise HTTPException(status_code=401, detail="Missing token")
|
|
token_verifier(token)
|
|
queue = await store.subscribe()
|
|
|
|
async def event_generator():
|
|
try:
|
|
while True:
|
|
item = await queue.get()
|
|
data = json.dumps(item.model_dump(), default=str)
|
|
yield f"data: {data}\n\n"
|
|
finally:
|
|
await store.unsubscribe(queue)
|
|
|
|
return StreamingResponse(event_generator(), media_type="text/event-stream")
|
|
|
|
return router
|