(session_id: str)
| 16 | |
| 17 | @router.get("/api/sessions/{session_id}/download") |
| 18 | async def download_session(session_id: str): |
| 19 | try: |
| 20 | if not re.match(r"^[a-zA-Z0-9_-]+$", session_id): |
| 21 | logger = get_server_logger() |
| 22 | logger.log_security_event( |
| 23 | "INVALID_SESSION_ID_FORMAT", |
| 24 | f"Invalid session_id format: {session_id}", |
| 25 | details={"received_session_id": session_id}, |
| 26 | ) |
| 27 | raise ValidationError( |
| 28 | "Invalid session_id: only letters, digits, underscores, and hyphens are allowed", |
| 29 | field="session_id", |
| 30 | ) |
| 31 | |
| 32 | dir_name = f"session_{session_id}" |
| 33 | session_path = WARE_HOUSE_DIR / dir_name |
| 34 | |
| 35 | if not session_path.exists() or not session_path.is_dir(): |
| 36 | raise ResourceNotFoundError( |
| 37 | "Session directory not found", |
| 38 | resource_type="session", |
| 39 | resource_id=session_id, |
| 40 | ) |
| 41 | |
| 42 | with tempfile.NamedTemporaryFile(delete=False, suffix=".zip") as tmp_file: |
| 43 | zip_path = Path(tmp_file.name) |
| 44 | |
| 45 | archive_base = zip_path.with_suffix("") |
| 46 | try: |
| 47 | shutil.make_archive(str(archive_base), "zip", root_dir=WARE_HOUSE_DIR, base_dir=dir_name) |
| 48 | except Exception as exc: |
| 49 | if zip_path.exists(): |
| 50 | zip_path.unlink() |
| 51 | logger = get_server_logger() |
| 52 | logger.log_exception(exc, f"Failed to create zip archive for session: {session_id}") |
| 53 | raise HTTPException(status_code=500, detail="Failed to create zip archive") |
| 54 | |
| 55 | logger = get_server_logger() |
| 56 | logger.info( |
| 57 | "Session download prepared", |
| 58 | log_type=LogType.WORKFLOW, |
| 59 | session_id=session_id, |
| 60 | archive_path=str(zip_path), |
| 61 | ) |
| 62 | |
| 63 | def cleanup_zip(): |
| 64 | if zip_path.exists(): |
| 65 | zip_path.unlink() |
| 66 | |
| 67 | atexit.register(cleanup_zip) |
| 68 | |
| 69 | return FileResponse( |
| 70 | path=zip_path, |
| 71 | filename=f"{dir_name}.zip", |
| 72 | media_type="application/zip", |
| 73 | headers={"Content-Disposition": f"attachment; filename={dir_name}.zip"}, |
| 74 | ) |
| 75 | except ValidationError as exc: |
nothing calls this directly
no test coverage detected