diff --git a/docker-compose.yml b/docker-compose.yml index b69fa60..5ebcc7c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,11 +19,11 @@ services: - ADMIN_USER=${ADMIN_USER:-admin} - ADMIN_PASSWORD=${ADMIN_PASSWORD:--whisper12510-} - LOG_RETENTION_DAYS=${LOG_RETENTION_DAYS:-30} - - DATABASE_URL=sqlite:///app/data/whisper_api.db + - DATABASE_URL=sqlite:////app/data/whisper_api.db volumes: - - ./models:/app/models - - ./data:/app/data - - ./uploads:/app/uploads + - whisper_models:/app/models + - whisper_data:/app/data + - whisper_uploads:/app/uploads deploy: resources: reservations: @@ -37,3 +37,8 @@ services: timeout: 10s retries: 3 start_period: 60s + +volumes: + whisper_models: + whisper_data: + whisper_uploads: diff --git a/docker/Dockerfile b/docker/Dockerfile index 28c94d6..f05a216 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -3,18 +3,33 @@ FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04 # Prevent interactive prompts ENV DEBIAN_FRONTEND=noninteractive -# Install system dependencies +# Install system dependencies with Python 3.12 from deadsnakes PPA RUN apt-get update && apt-get install -y \ - python3.11 \ - python3.11-pip \ - python3.11-venv \ + software-properties-common \ + pkg-config \ + && add-apt-repository ppa:deadsnakes/ppa -y \ + && apt-get update \ + && apt-get install -y \ + python3.12 \ + python3.12-dev \ + python3.12-venv \ ffmpeg \ + libavformat-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavutil-dev \ + libavfilter-dev \ + libswscale-dev \ + libswresample-dev \ curl \ && rm -rf /var/lib/apt/lists/* -# Set Python 3.11 as default -RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.11 1 -RUN update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1 +# Install pip for Python 3.12 +RUN curl -sS https://bootstrap.pypa.io/get-pip.py | python3.12 + +# Set Python 3.12 as default +RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.12 1 +RUN update-alternatives --install /usr/bin/pip pip /usr/local/bin/pip3 1 # Set working directory WORKDIR /app @@ -23,12 +38,19 @@ WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt +# Copy entrypoint script +COPY docker/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + # Create necessary directories RUN mkdir -p /app/models /app/data /app/uploads # Copy application code COPY src/ ./src/ +# Set entrypoint +ENTRYPOINT ["/entrypoint.sh"] + # Set environment variables ENV PYTHONPATH=/app ENV PYTHONUNBUFFERED=1 diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..9267856 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +# Create directories if they don't exist and set permissions +mkdir -p /app/data /app/uploads /app/models +chmod 777 /app/data /app/uploads /app/models + +# Start the application +exec "$@" diff --git a/requirements.txt b/requirements.txt index c387a9e..f10dc5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,17 +6,17 @@ jinja2==3.1.3 aiofiles==23.2.1 # Whisper -openai-whisper==20231117 -faster-whisper==0.10.0 -torch==2.1.2 -torchaudio==2.1.2 +openai-whisper==20240930 +faster-whisper==1.1.0 +torch==2.5.1 +torchaudio==2.5.1 # Database -sqlalchemy==2.0.25 -alembic==1.13.1 +sqlalchemy==2.0.36 +alembic==1.14.0 # Audio processing -librosa==0.10.1 +librosa==0.10.2.post1 soundfile==0.12.1 # Utilities diff --git a/src/api/transcriptions.py b/src/api/transcriptions.py index aac0dda..5c56467 100644 --- a/src/api/transcriptions.py +++ b/src/api/transcriptions.py @@ -14,6 +14,10 @@ from sqlalchemy.orm import Session router = APIRouter() +from src.services.stats_service import hash_api_key +from src.database.db import SessionLocal +from src.database.models import ApiKey + def verify_api_key(authorization: Optional[str] = Header(None)): """Verify API key from Authorization header""" if not authorization: @@ -24,15 +28,27 @@ def verify_api_key(authorization: Optional[str] = Header(None)): raise HTTPException(status_code=401, detail="Invalid authorization format") api_key = authorization.replace("Bearer ", "").strip() + + # Check environment variable keys first valid_keys = settings.get_api_keys_list() + if api_key in valid_keys: + return api_key - if not valid_keys: - raise HTTPException(status_code=500, detail="No API keys configured") + # Check database keys + db = SessionLocal() + try: + key_hash = hash_api_key(api_key) + db_key = db.query(ApiKey).filter( + ApiKey.key_hash == key_hash, + ApiKey.is_active == True + ).first() + + if db_key: + return api_key + finally: + db.close() - if api_key not in valid_keys: - raise HTTPException(status_code=401, detail="Invalid API key") - - return api_key + raise HTTPException(status_code=401, detail="Invalid API key") @router.get("/models")