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