Working docker-compose.yaml added. Services start and work.
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
# Backend
|
||||||
|
DEBUG=0
|
||||||
|
MAX_UPLOAD_SIZE_MB=500
|
||||||
|
TEMP_DIR=/tmp/audio_splitter_web
|
||||||
|
CLEANUP_AFTER_SECONDS=3600
|
||||||
|
|
||||||
|
# Frontend (development)
|
||||||
|
VITE_BACKEND_URL=http://localhost:8000
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
services:
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: . # Project root (contains audio_splitter/)
|
||||||
|
dockerfile: web/backend/Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
volumes:
|
||||||
|
- /tmp/audio_splitter_web:/tmp/audio_splitter_web
|
||||||
|
environment:
|
||||||
|
- PYTHONUNBUFFERED=1
|
||||||
|
- DEBUG=1
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: ./web/frontend
|
||||||
|
dockerfile: Dockerfile.dev
|
||||||
|
ports:
|
||||||
|
- "5173:5173"
|
||||||
|
volumes:
|
||||||
|
- ./web/frontend:/app
|
||||||
|
- /app/node_modules
|
||||||
|
environment:
|
||||||
|
- BACKEND_URL=http://backend:8000
|
||||||
|
- VITE_BACKEND_URL=http://backend:8000
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
*.so
|
||||||
|
*.egg
|
||||||
|
*.egg-info
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
.venv
|
||||||
|
venv
|
||||||
|
.env
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
FROM python:3.13-slim
|
||||||
|
|
||||||
|
# Install FFmpeg and system dependencies
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
ffmpeg \
|
||||||
|
ca-certificates \
|
||||||
|
&& \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Set Python environment variables
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Copy and install Python dependencies
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
COPY web/backend/requirements-web.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements-web.txt
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Copy the backend application code
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
COPY web/backend /app/backend
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Copy and install the audio_splitter package
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
COPY audio_splitter /app/audio_splitter
|
||||||
|
COPY setup.py pyproject.toml README.md /app/
|
||||||
|
|
||||||
|
# Install audio_splitter as a package
|
||||||
|
RUN pip install --no-cache-dir /app
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Create a non-root user
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
RUN addgroup --system --gid 1000 appgroup && \
|
||||||
|
adduser --system --uid 1000 --ingroup appgroup appuser && \
|
||||||
|
chown -R appuser:appgroup /app
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# Expose port and start
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# PYTHONPATH is already /app by default because we set WORKDIR /app
|
||||||
|
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
+6
-3
@@ -7,7 +7,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|||||||
from backend.config import settings
|
from backend.config import settings
|
||||||
from backend.api import upload, split, status, download
|
from backend.api import upload, split, status, download
|
||||||
from backend.api.websocket import router as websocket_router
|
from backend.api.websocket import router as websocket_router
|
||||||
from backend.services import progress_publisher # Import the module
|
from backend.services import progress_publisher
|
||||||
|
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Audio Splitter Web API",
|
title="Audio Splitter Web API",
|
||||||
@@ -24,10 +24,13 @@ app.add_middleware(
|
|||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Store the main event loop in the progress_publisher module
|
|
||||||
# This avoids a circular import
|
@app.on_event("startup")
|
||||||
|
async def startup_event():
|
||||||
|
"""Store the main event loop for use in other threads."""
|
||||||
progress_publisher.MAIN_LOOP = asyncio.get_running_loop()
|
progress_publisher.MAIN_LOOP = asyncio.get_running_loop()
|
||||||
|
|
||||||
|
|
||||||
# Include routers
|
# Include routers
|
||||||
app.include_router(upload.router)
|
app.include_router(upload.router)
|
||||||
app.include_router(split.router)
|
app.include_router(split.router)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fastapi>=0.104.0
|
||||||
|
uvicorn[standard]>=0.24.0
|
||||||
|
python-multipart>=0.0.6
|
||||||
|
aiofiles>=23.2.0
|
||||||
|
pydantic>=2.5.0
|
||||||
|
pydantic-settings>=2.0.0
|
||||||
|
python-dotenv>=1.0.0
|
||||||
|
websockets>=12.0
|
||||||
@@ -5,7 +5,7 @@ from typing import Dict, Set
|
|||||||
|
|
||||||
from fastapi import WebSocket
|
from fastapi import WebSocket
|
||||||
|
|
||||||
# This will be set by main.py when the app starts
|
# This will be set by main.py during startup
|
||||||
MAIN_LOOP = None
|
MAIN_LOOP = None
|
||||||
|
|
||||||
active_connections: Dict[str, Set[WebSocket]] = {}
|
active_connections: Dict[str, Set[WebSocket]] = {}
|
||||||
@@ -29,13 +29,19 @@ def publish_progress(task_id: str, progress: int, message: str, status: str = "p
|
|||||||
}
|
}
|
||||||
|
|
||||||
to_remove = set()
|
to_remove = set()
|
||||||
|
# Get the loop to use: either the stored one or try to get the current loop
|
||||||
|
loop = MAIN_LOOP
|
||||||
|
if loop is None:
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
# No running loop, fallback to default event loop
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
for websocket in active_connections.get(task_id, set()):
|
for websocket in active_connections.get(task_id, set()):
|
||||||
try:
|
try:
|
||||||
# Use the stored main loop, or fallback to getting the current loop
|
|
||||||
loop = MAIN_LOOP or asyncio.get_running_loop()
|
|
||||||
asyncio.run_coroutine_threadsafe(websocket.send_json(data), loop)
|
asyncio.run_coroutine_threadsafe(websocket.send_json(data), loop)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Client disconnected or other error
|
|
||||||
to_remove.add(websocket)
|
to_remove.add(websocket)
|
||||||
|
|
||||||
# Clean up disconnected clients
|
# Clean up disconnected clients
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
*.log
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.DS_Store
|
||||||
|
Dockerfile
|
||||||
|
Dockerfile.dev
|
||||||
|
.dockerignore
|
||||||
|
nginx.conf
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Stage 1: Build
|
||||||
|
FROM node:20-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
COPY package.json package-lock.json* ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Copy the application code and build
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Stage 2: Production (nginx)
|
||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
# Copy built assets from builder
|
||||||
|
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Copy nginx configuration
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# Expose the port
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
# Start nginx
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
COPY package.json package-lock.json* ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
# Copy the application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Expose the port
|
||||||
|
EXPOSE 5173
|
||||||
|
|
||||||
|
# Start the development server
|
||||||
|
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||||
@@ -1,18 +1,23 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// Backend URL for proxy (default to localhost for local dev)
|
||||||
|
const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:8000'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:8000',
|
target: BACKEND_URL,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
},
|
},
|
||||||
'/ws': {
|
'/ws': {
|
||||||
target: 'ws://localhost:8000',
|
target: BACKEND_URL.replace(/^http/, 'ws'),
|
||||||
ws: true,
|
ws: true,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
# Root directory for static files
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# Serve React app
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy API requests to backend
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend:8000/api/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Proxy WebSocket requests to backend
|
||||||
|
location /ws/ {
|
||||||
|
proxy_pass http://backend:8000/ws/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user