fixes generated image download

This commit is contained in:
2026-07-08 23:18:07 -07:00
parent 57198cd671
commit a4093e86b1
6 changed files with 101 additions and 30 deletions
+46 -3
View File
@@ -9,11 +9,18 @@ from pathlib import Path
from typing import Literal
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
from fastapi.security import HTTPAuthorizationCredentials
from fastapi.responses import FileResponse
from sqlalchemy import delete, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.auth import get_current_admin_user
from app.auth import (
AuthBearer,
create_download_token,
decode_token,
get_current_admin_user,
verify_download_token,
)
from app.config import settings
from app.database import async_session, get_session
from app.models import MobileBuildArtifact, MobileBuildRequest, MobileBuildUploadedFile, StationConfig
@@ -834,12 +841,48 @@ async def trigger_mobile_build(
return await _hydrate_request(session, request)
@router.get("/artifacts/{artifact_id}/download")
async def download_mobile_build_artifact(
@router.get("/artifacts/{artifact_id}/download-url")
async def get_download_url(
artifact_id: int,
session: AsyncSession = Depends(get_session),
_: User = Depends(get_current_admin_user),
):
"""Return a short-lived signed download URL for the artifact."""
result = await session.execute(
select(MobileBuildArtifact).where(MobileBuildArtifact.id == artifact_id)
)
row = result.scalar_one_or_none()
if row is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Artifact not found")
token = create_download_token(artifact_id)
download_url = f"/api/mobile-builds/artifacts/{artifact_id}/download?token={token}"
return {"download_url": download_url}
@router.get("/artifacts/{artifact_id}/download")
async def download_mobile_build_artifact(
artifact_id: int,
token: str | None = Query(None),
credentials: HTTPAuthorizationCredentials | None = Depends(AuthBearer(auto_error=False)),
session: AsyncSession = Depends(get_session),
):
"""Download an artifact. Accepts either Bearer auth OR a signed token query param."""
# Authenticate: signed token takes precedence, fall back to Bearer JWT
if token:
verified_id = verify_download_token(token)
if verified_id is None or verified_id != artifact_id:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired download token")
elif credentials:
payload = decode_token(credentials.credentials)
user_id = int(payload["sub"])
result = await session.execute(select(User).where(User.id == user_id))
user = result.scalar_one_or_none()
if user is None or not user.is_admin:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required")
else:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required")
result = await session.execute(
select(MobileBuildArtifact).where(MobileBuildArtifact.id == artifact_id)
)