track creator on vlm watch jobs and scope view/cancel to admin or creator

This commit is contained in:
Josh Hawkins 2026-05-13 09:41:38 -05:00
parent c3ef7f01ad
commit b2e16ee5de
2 changed files with 39 additions and 3 deletions

View File

@ -10,7 +10,7 @@ from functools import reduce
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
import cv2 import cv2
from fastapi import APIRouter, Body, Depends, Request from fastapi import APIRouter, Body, Depends, HTTPException, Request
from fastapi.responses import JSONResponse, StreamingResponse from fastapi.responses import JSONResponse, StreamingResponse
from pydantic import BaseModel from pydantic import BaseModel
@ -1641,6 +1641,7 @@ async def start_vlm_monitor(
dispatcher=request.app.dispatcher, dispatcher=request.app.dispatcher,
labels=body.labels, labels=body.labels,
zones=body.zones, zones=body.zones,
username=request.headers.get("remote-user", ""),
) )
except RuntimeError as e: except RuntimeError as e:
logger.error("Failed to start VLM watch job: %s", e, exc_info=True) logger.error("Failed to start VLM watch job: %s", e, exc_info=True)
@ -1661,10 +1662,22 @@ async def start_vlm_monitor(
summary="Get current VLM watch job", summary="Get current VLM watch job",
description="Returns the current (or most recently completed) VLM watch job.", description="Returns the current (or most recently completed) VLM watch job.",
) )
async def get_vlm_monitor() -> JSONResponse: async def get_vlm_monitor(request: Request) -> JSONResponse:
job = get_vlm_watch_job() job = get_vlm_watch_job()
if job is None: if job is None:
return JSONResponse(content={"active": False}, status_code=200) return JSONResponse(content={"active": False}, status_code=200)
role = request.headers.get("remote-role", "viewer")
username = request.headers.get("remote-user", "")
# Admin and the job's creator always see the job. Other users only see it
# if they have access to the camera being watched; otherwise hide it.
if role != "admin" and username != job.username:
try:
await require_camera_access(job.camera, request=request)
except HTTPException:
return JSONResponse(content={"active": False}, status_code=200)
return JSONResponse(content={"active": True, **job.to_dict()}, status_code=200) return JSONResponse(content={"active": True, **job.to_dict()}, status_code=200)
@ -1674,7 +1687,27 @@ async def get_vlm_monitor() -> JSONResponse:
summary="Cancel the current VLM watch job", summary="Cancel the current VLM watch job",
description="Cancels the running watch job if one exists.", description="Cancels the running watch job if one exists.",
) )
async def cancel_vlm_monitor() -> JSONResponse: async def cancel_vlm_monitor(request: Request) -> JSONResponse:
job = get_vlm_watch_job()
if job is None:
return JSONResponse(
content={"success": False, "message": "No active watch job to cancel."},
status_code=404,
)
role = request.headers.get("remote-role", "viewer")
username = request.headers.get("remote-user", "")
# Admin can cancel any job; other users can only cancel jobs they started.
if role != "admin" and username != job.username:
return JSONResponse(
content={
"success": False,
"message": "Not authorized to cancel this watch job.",
},
status_code=403,
)
cancelled = stop_vlm_watch_job() cancelled = stop_vlm_watch_job()
if not cancelled: if not cancelled:
return JSONResponse( return JSONResponse(

View File

@ -45,6 +45,7 @@ class VLMWatchJob(Job):
last_reasoning: str = "" last_reasoning: str = ""
notification_message: str = "" notification_message: str = ""
iteration_count: int = 0 iteration_count: int = 0
username: str = ""
def to_dict(self) -> dict[str, Any]: def to_dict(self) -> dict[str, Any]:
return asdict(self) return asdict(self)
@ -374,6 +375,7 @@ def start_vlm_watch_job(
dispatcher: Any, dispatcher: Any,
labels: list[str] | None = None, labels: list[str] | None = None,
zones: list[str] | None = None, zones: list[str] | None = None,
username: str = "",
) -> str: ) -> str:
"""Start a new VLM watch job. Returns the job ID. """Start a new VLM watch job. Returns the job ID.
@ -397,6 +399,7 @@ def start_vlm_watch_job(
max_duration_minutes=max_duration_minutes, max_duration_minutes=max_duration_minutes,
labels=labels or [], labels=labels or [],
zones=zones or [], zones=zones or [],
username=username,
) )
cancel_ev = threading.Event() cancel_ev = threading.Event()
_current_job = job _current_job = job