From 65f6b979c3670b9c755815048992e0fb74dda67a Mon Sep 17 00:00:00 2001 From: remsky Date: Sat, 29 Mar 2025 17:01:15 -0600 Subject: [PATCH] Enhance temp file handling with error tracking and update Docker Compose to run as non-root user --- .github/workflows/sync-develop.yml | 55 --------------------- api/src/routers/openai_compatible.py | 4 ++ api/src/services/temp_manager.py | 73 ++++++++++++++++++++-------- docker/gpu/docker-compose.yml | 1 + 4 files changed, 57 insertions(+), 76 deletions(-) delete mode 100644 .github/workflows/sync-develop.yml diff --git a/.github/workflows/sync-develop.yml b/.github/workflows/sync-develop.yml deleted file mode 100644 index 56b881f..0000000 --- a/.github/workflows/sync-develop.yml +++ /dev/null @@ -1,55 +0,0 @@ -# name: Sync develop with master - -# on: -# push: -# branches: -# - master - -# jobs: -# sync-develop: -# runs-on: ubuntu-latest -# permissions: -# contents: write -# issues: write -# steps: -# - name: Checkout repository -# uses: actions/checkout@v4 -# with: -# fetch-depth: 0 -# ref: develop - -# - name: Configure Git -# run: | -# git config user.name "GitHub Actions" -# git config user.email "actions@github.com" - -# - name: Merge master into develop -# run: | -# git fetch origin master:master -# git merge --no-ff origin/master -m "chore: Merge master into develop branch" - -# - name: Push changes -# run: | -# if ! git push origin develop; then -# echo "Failed to push to develop branch" -# exit 1 -# fi - -# - name: Handle Failure -# if: failure() -# uses: actions/github-script@v7 -# with: -# script: | -# const issueBody = `Automatic merge from master to develop failed. - -# Please resolve this manually - -# Workflow run: ${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`; - -# await github.rest.issues.create({ -# owner: context.repo.owner, -# repo: context.repo.repo, -# title: '🔄 Automatic master to develop merge failed', -# body: issueBody, -# labels: ['merge-failed', 'automation'] -# }); diff --git a/api/src/routers/openai_compatible.py b/api/src/routers/openai_compatible.py index 1e89151..3106f3b 100644 --- a/api/src/routers/openai_compatible.py +++ b/api/src/routers/openai_compatible.py @@ -215,6 +215,10 @@ async def create_speech( "Transfer-Encoding": "chunked", "X-Download-Path": download_path, } + + # Add header to indicate if temp file writing is available + if temp_writer._write_error: + headers["X-Download-Status"] = "unavailable" # Create async generator for streaming async def dual_output(): diff --git a/api/src/services/temp_manager.py b/api/src/services/temp_manager.py index 98d4988..08d22b1 100644 --- a/api/src/services/temp_manager.py +++ b/api/src/services/temp_manager.py @@ -81,26 +81,36 @@ class TempFileWriter: self.format = format self.temp_file = None self._finalized = False + self._write_error = False # Flag to track if we've had a write error async def __aenter__(self): """Async context manager entry""" - # Clean up old files first - await cleanup_temp_files() + try: + # Clean up old files first + await cleanup_temp_files() - # Create temp file with proper extension - await aiofiles.os.makedirs(settings.temp_file_dir, exist_ok=True) - temp = tempfile.NamedTemporaryFile( - dir=settings.temp_file_dir, - delete=False, - suffix=f".{self.format}", - mode="wb", - ) - self.temp_file = await aiofiles.open(temp.name, mode="wb") - self.temp_path = temp.name - temp.close() # Close sync file, we'll use async version + # Create temp file with proper extension + await aiofiles.os.makedirs(settings.temp_file_dir, exist_ok=True) + temp = tempfile.NamedTemporaryFile( + dir=settings.temp_file_dir, + delete=False, + suffix=f".{self.format}", + mode="wb", + ) + self.temp_file = await aiofiles.open(temp.name, mode="wb") + self.temp_path = temp.name + temp.close() # Close sync file, we'll use async version - # Generate download path immediately - self.download_path = f"/download/{os.path.basename(self.temp_path)}" + # Generate download path immediately + self.download_path = f"/download/{os.path.basename(self.temp_path)}" + except Exception as e: + # Handle permission issues or other errors gracefully + logger.error(f"Failed to create temp file: {e}") + self._write_error = True + # Set a placeholder path so the API can still function + self.temp_path = f"unavailable_{self.format}" + self.download_path = f"/download/{self.temp_path}" + return self async def __aexit__(self, exc_type, exc_val, exc_tb): @@ -111,6 +121,7 @@ class TempFileWriter: self._finalized = True except Exception as e: logger.error(f"Error closing temp file: {e}") + self._write_error = True async def write(self, chunk: bytes) -> None: """Write a chunk of audio data @@ -120,9 +131,18 @@ class TempFileWriter: """ if self._finalized: raise RuntimeError("Cannot write to finalized temp file") - - await self.temp_file.write(chunk) - await self.temp_file.flush() + + # Skip writing if we've already encountered an error + if self._write_error or not self.temp_file: + return + + try: + await self.temp_file.write(chunk) + await self.temp_file.flush() + except Exception as e: + # Handle permission issues or other errors gracefully + logger.error(f"Failed to write to temp file: {e}") + self._write_error = True async def finalize(self) -> str: """Close temp file and return download path @@ -133,7 +153,18 @@ class TempFileWriter: if self._finalized: raise RuntimeError("Temp file already finalized") - await self.temp_file.close() - self._finalized = True + # Skip finalizing if we've already encountered an error + if self._write_error or not self.temp_file: + self._finalized = True + return self.download_path + + try: + await self.temp_file.close() + self._finalized = True + except Exception as e: + # Handle permission issues or other errors gracefully + logger.error(f"Failed to finalize temp file: {e}") + self._write_error = True + self._finalized = True - return f"/download/{os.path.basename(self.temp_path)}" + return self.download_path diff --git a/docker/gpu/docker-compose.yml b/docker/gpu/docker-compose.yml index 762aca6..2623394 100644 --- a/docker/gpu/docker-compose.yml +++ b/docker/gpu/docker-compose.yml @@ -7,6 +7,7 @@ services: dockerfile: docker/gpu/Dockerfile volumes: - ../../api:/app/api + user: "1001:1001" # Ensure container runs as UID 1001 (appuser) ports: - "8880:8880" environment: