diff --git a/app/routers/runs.py b/app/routers/runs.py index 24226cf..d151fbf 100644 --- a/app/routers/runs.py +++ b/app/routers/runs.py @@ -155,7 +155,8 @@ async def create_run( @router.post("/stream", response_model=RunStatus) async def create_stream_run( request: StreamRequest, - ctx: UserContext = Depends(require_auth), + req: Request, + ctx: UserContext = Depends(get_current_user), ): """Start a streaming video render. @@ -163,13 +164,26 @@ async def create_stream_run( (stream ...) form defining the pipeline. Assets can be referenced by CID or friendly name in the recipe. + Requires authentication OR admin token in X-Admin-Token header. """ import uuid import tempfile + import os from pathlib import Path import database from tasks.streaming import run_stream + # Check for admin token if no user auth + admin_token = os.environ.get("ADMIN_TOKEN") + request_token = req.headers.get("X-Admin-Token") + admin_actor_id = req.headers.get("X-Actor-Id", "admin@local") + + if not ctx and (not admin_token or request_token != admin_token): + raise HTTPException(401, "Authentication required") + + # Use context actor_id or admin actor_id + actor_id = ctx.actor_id if ctx else admin_actor_id + # Generate run ID run_id = str(uuid.uuid4()) @@ -192,7 +206,7 @@ async def create_stream_run( # Track ownership in item_types await database.save_item_metadata( cid=recipe_id, - actor_id=ctx.actor_id, + actor_id=actor_id, item_type="recipe", description=f"Streaming recipe: {recipe_name}", filename=f"{recipe_name}.sexp", @@ -203,7 +217,7 @@ async def create_stream_run( naming = get_naming_service() await naming.assign_name( cid=recipe_id, - actor_id=ctx.actor_id, + actor_id=actor_id, item_type="recipe", display_name=recipe_name, ) @@ -220,7 +234,7 @@ async def create_stream_run( output_name=request.output_name, duration=request.duration, fps=request.fps, - actor_id=ctx.actor_id, + actor_id=actor_id, sources_sexp=request.sources_sexp, audio_sexp=request.audio_sexp, ) @@ -231,7 +245,7 @@ async def create_stream_run( celery_task_id=task.id, recipe=recipe_id or "streaming", # Use recipe CID if available inputs=[], # Streaming recipes don't have traditional inputs - actor_id=ctx.actor_id, + actor_id=actor_id, dag_json=request.recipe_sexp, # Store recipe content for viewing output_name=request.output_name, )