mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-07 07:55:16 +02:00
* Add tuner integration * bump pipecat version * chore: update pipecat submodule to match upstream and use tuner-pipecat-sdk 0.2.0 Update pipecat submodule from 0.0.109.dev23 to 13e98d0d9 (the exact commit upstream dograh-hq/dograh uses after v1.30.1). This installs pipecat-ai as 1.1.0.post277 via setuptools_scm, satisfying tuner-pipecat-sdk 0.2.0's pipecat-ai>=1.0.0 requirement. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * wire tuner * feat: refactor integrations into self contained packages * chore: simplify ensure_public_access_token * fix: remove NodeSpec and make DTOs the source of truth * feat: send relevant signal to mcp using to_mcp_dict * fix: fix tests * cleanup: remove nango integrations * feat: add agents.md for integrations --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Abhishek Kumar <abhishek@a6k.me>
96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
"""Public download endpoints for workflow recordings and transcripts.
|
|
|
|
These endpoints provide secure, token-based public access to workflow artifacts
|
|
without requiring authentication. Tokens are generated on-demand during
|
|
post-call processing for runs that execute integrations, QA, or campaign
|
|
reporting.
|
|
"""
|
|
|
|
from typing import Literal
|
|
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
from fastapi.responses import RedirectResponse
|
|
from loguru import logger
|
|
|
|
from api.db import db_client
|
|
from api.services.storage import get_storage_for_backend
|
|
|
|
router = APIRouter(prefix="/public/download")
|
|
|
|
|
|
@router.get("/workflow/{token}/{artifact_type}")
|
|
async def download_workflow_artifact(
|
|
token: str,
|
|
artifact_type: Literal["recording", "transcript"],
|
|
inline: bool = Query(
|
|
default=False, description="Display inline in browser instead of download"
|
|
),
|
|
):
|
|
"""Download a workflow recording or transcript via public access token.
|
|
|
|
This endpoint:
|
|
1. Validates the public access token
|
|
2. Looks up the corresponding workflow run
|
|
3. Generates a signed URL for the requested artifact
|
|
4. Redirects to the signed URL
|
|
|
|
Args:
|
|
token: The public access token (UUID format)
|
|
artifact_type: Type of artifact - "recording" or "transcript"
|
|
inline: If true, sets Content-Disposition to inline for browser preview
|
|
|
|
Returns:
|
|
RedirectResponse to the signed URL (302 redirect)
|
|
|
|
Raises:
|
|
HTTPException 404: If token is invalid or artifact not found
|
|
"""
|
|
# 1. Lookup workflow run by token
|
|
workflow_run = await db_client.get_workflow_run_by_public_token(token)
|
|
if not workflow_run:
|
|
logger.warning(f"Invalid public access token: {token[:8]}...")
|
|
raise HTTPException(status_code=404, detail="Invalid or expired token")
|
|
|
|
# 2. Get file path based on artifact type
|
|
if artifact_type == "recording":
|
|
file_path = workflow_run.recording_url
|
|
else: # transcript
|
|
file_path = workflow_run.transcript_url
|
|
|
|
if not file_path:
|
|
logger.warning(
|
|
f"Artifact not found: type={artifact_type}, workflow_run_id={workflow_run.id}"
|
|
)
|
|
raise HTTPException(
|
|
status_code=404,
|
|
detail=f"No {artifact_type} available for this workflow run",
|
|
)
|
|
|
|
# 3. Get storage backend for this workflow run
|
|
try:
|
|
storage = get_storage_for_backend(workflow_run.storage_backend)
|
|
except ValueError as e:
|
|
logger.error(f"Invalid storage backend: {workflow_run.storage_backend}")
|
|
raise HTTPException(status_code=500, detail="Storage configuration error")
|
|
|
|
# 4. Generate signed URL (1 hour expiration)
|
|
try:
|
|
signed_url = await storage.aget_signed_url(
|
|
file_path=file_path,
|
|
expiration=3600, # 1 hour
|
|
force_inline=inline,
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Failed to generate signed URL: {e}")
|
|
raise HTTPException(status_code=500, detail="Failed to generate download URL")
|
|
|
|
if not signed_url:
|
|
logger.error(f"Storage returned None for signed URL: {file_path}")
|
|
raise HTTPException(status_code=500, detail="Failed to generate download URL")
|
|
|
|
logger.info(
|
|
f"Generated signed URL for {artifact_type}: workflow_run_id={workflow_run.id}, token={token[:8]}..."
|
|
)
|
|
|
|
# 5. Redirect to signed URL
|
|
return RedirectResponse(url=signed_url, status_code=302)
|