Add status api to get scanner's status

This commit is contained in:
2025-12-01 19:45:36 +01:00
parent d7699389bc
commit b39fa4262d
5 changed files with 88 additions and 27 deletions

View File

@@ -38,7 +38,7 @@ PUBLIC_URL=http://localhost:8901
# Set `verified` to true if you don't wanna manually verify users. # Set `verified` to true if you don't wanna manually verify users.
EXTRA_CLAIMS='{"permissions": ["core.read", "core.play"], "verified": false}' EXTRA_CLAIMS='{"permissions": ["core.read", "core.play"], "verified": false}'
# This is the permissions of the first user (aka the first user is admin) # This is the permissions of the first user (aka the first user is admin)
FIRST_USER_CLAIMS='{"permissions": ["users.read", "users.write", "apikeys.read", "apikeys.write", "users.delete", "core.read", "core.write", "core.play", "scanner.trigger"], "verified": true}' FIRST_USER_CLAIMS='{"permissions": ["users.read", "users.write", "users.delete", "apikeys.read", "apikeys.write", "core.read", "core.write", "core.play", "scanner.trigger"], "verified": true}'
# Guest (meaning unlogged in users) can be: # Guest (meaning unlogged in users) can be:
# unauthorized (they need to connect before doing anything) # unauthorized (they need to connect before doing anything)

View File

@@ -1,4 +1,5 @@
from __future__ import annotations from __future__ import annotations
from datetime import datetime
from typing import Literal from typing import Literal
from pydantic import Field from pydantic import Field
@@ -18,3 +19,15 @@ class Request(Model, extra="allow"):
class Video(Model): class Video(Model):
id: str id: str
episodes: list[Guess.Episode] episodes: list[Guess.Episode]
class RequestRet(Model):
id: str
kind: Literal["episode", "movie"]
title: str
year: int | None
status: Literal[
"pending",
"running",
"failed",
]
started_at: datetime | None

View File

@@ -0,0 +1,16 @@
from fastapi import APIRouter
router = APIRouter()
@router.get("/health")
def get_health():
return {"status": "healthy"}
@router.get("/ready")
def get_ready():
# child spans (`select 1` & db connection reset) was still logged,
# since i don't really wanna deal with it, let's just do that.
return {"status": "healthy"}

View File

@@ -1,9 +1,9 @@
from typing import Annotated from typing import Annotated, Literal
from asyncpg import Connection from fastapi import APIRouter, BackgroundTasks, Depends, Security
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Security
from scanner.database import get_db_fapi from scanner.models.request import RequestRet
from scanner.status import StatusService
from ..fsscan import create_scanner from ..fsscan import create_scanner
from ..jwt import validate_bearer from ..jwt import validate_bearer
@@ -11,6 +11,19 @@ from ..jwt import validate_bearer
router = APIRouter() router = APIRouter()
@router.get("/scan")
async def get_scan_status(
svc: Annotated[StatusService, Depends(StatusService.create)],
_: Annotated[None, Security(validate_bearer, scopes=["scanner.trigger"])],
status: Literal["pending", "running", "failed"] | None = None,
) -> list[RequestRet]:
"""
Get scan status, know what tasks are running, pending or failed.
"""
return await svc.list_requests(status=status)
@router.put( @router.put(
"/scan", "/scan",
status_code=204, status_code=204,
@@ -29,25 +42,3 @@ async def trigger_scan(
await scanner.scan() await scanner.scan()
tasks.add_task(run) tasks.add_task(run)
@router.get("/health")
def get_health():
return {"status": "healthy"}
@router.get("/ready")
def get_ready():
# child spans (`select 1` & db connection reset) was still logged,
# since i don't really wanna deal with it, let's just do that.
return {"status": "healthy"}
# async def get_ready(db: Annotated[Connection, Depends(get_db_fapi)]):
# try:
# _ = await db.execute("select 1")
# return {"status": "healthy", "database": "healthy"}
# except Exception as e:
# raise HTTPException(
# status_code=500, detail={"status": "unhealthy", "database": str(e)}
# )

41
scanner/scanner/status.py Normal file
View File

@@ -0,0 +1,41 @@
from typing import Literal
from asyncpg import Connection
from pydantic import TypeAdapter
from scanner.database import get_db
from .models.request import RequestRet
class StatusService:
def __init__(self, database: Connection):
self._database = database
@classmethod
async def create(cls):
async with get_db() as db:
yield StatusService(db)
async def list_requests(
self, *, status: Literal["pending", "running", "failed"] | None = None
) -> list[RequestRet]:
ret = await self._database.fetch(
f"""
select
pk::text as id,
kind,
title,
year,
status,
started_at
from
scanner.requests
order by
started_at,
pk
{"where status = $1" if status is not None else ""}
""",
*([status] if status is not None else []),
)
return TypeAdapter(list[RequestRet]).validate_python(ret)