|
| 1 | +# ============================================ |
| 2 | +# DeepTutor Multi-Stage Dockerfile |
| 3 | +# ============================================ |
| 4 | +# This Dockerfile builds a production-ready image for DeepTutor |
| 5 | +# containing both the FastAPI backend and Next.js frontend |
| 6 | +# |
| 7 | +# Build: docker build -t deeptutor . |
| 8 | +# Run: docker run -p 8001:8001 -p 3782:3782 --env-file .env deeptutor |
| 9 | +# ============================================ |
| 10 | + |
| 11 | +# ============================================ |
| 12 | +# Stage 1: Frontend Builder |
| 13 | +# ============================================ |
| 14 | +FROM node:20-alpine AS frontend-builder |
| 15 | + |
| 16 | +WORKDIR /app/web |
| 17 | + |
| 18 | +# Copy package files first for better caching |
| 19 | +COPY web/package.json web/package-lock.json* ./ |
| 20 | + |
| 21 | +# Install dependencies |
| 22 | +RUN npm ci --legacy-peer-deps |
| 23 | + |
| 24 | +# Copy frontend source code |
| 25 | +COPY web/ ./ |
| 26 | + |
| 27 | +# Build the Next.js application |
| 28 | +# Create a placeholder .env.local for build (will be overridden at runtime) |
| 29 | +RUN echo "NEXT_PUBLIC_API_BASE=http://localhost:8001" > .env.local |
| 30 | + |
| 31 | +# Build Next.js for production |
| 32 | +RUN npm run build |
| 33 | + |
| 34 | +# ============================================ |
| 35 | +# Stage 2: Python Base with Dependencies |
| 36 | +# ============================================ |
| 37 | +FROM python:3.11-slim AS python-base |
| 38 | + |
| 39 | +# Set environment variables |
| 40 | +ENV PYTHONDONTWRITEBYTECODE=1 \ |
| 41 | + PYTHONUNBUFFERED=1 \ |
| 42 | + PYTHONIOENCODING=utf-8 \ |
| 43 | + PIP_NO_CACHE_DIR=1 \ |
| 44 | + PIP_DISABLE_PIP_VERSION_CHECK=1 |
| 45 | + |
| 46 | +WORKDIR /app |
| 47 | + |
| 48 | +# Install system dependencies |
| 49 | +RUN apt-get update && apt-get install -y --no-install-recommends \ |
| 50 | + curl \ |
| 51 | + git \ |
| 52 | + build-essential \ |
| 53 | + && rm -rf /var/lib/apt/lists/* |
| 54 | + |
| 55 | +# Copy requirements and install Python dependencies |
| 56 | +COPY requirements.txt ./ |
| 57 | +RUN pip install --upgrade pip && \ |
| 58 | + pip install -r requirements.txt |
| 59 | + |
| 60 | +# ============================================ |
| 61 | +# Stage 3: Production Image |
| 62 | +# ============================================ |
| 63 | +FROM python:3.11-slim AS production |
| 64 | + |
| 65 | +# Labels |
| 66 | +LABEL maintainer="DeepTutor Team" \ |
| 67 | + description="DeepTutor: AI-Powered Personalized Learning Assistant" \ |
| 68 | + version="0.1.0" |
| 69 | + |
| 70 | +# Set environment variables |
| 71 | +ENV PYTHONDONTWRITEBYTECODE=1 \ |
| 72 | + PYTHONUNBUFFERED=1 \ |
| 73 | + PYTHONIOENCODING=utf-8 \ |
| 74 | + NODE_ENV=production \ |
| 75 | + # Default ports (can be overridden) |
| 76 | + BACKEND_PORT=8001 \ |
| 77 | + FRONTEND_PORT=3782 |
| 78 | + |
| 79 | +WORKDIR /app |
| 80 | + |
| 81 | +# Install Node.js 20.x (LTS) and npm |
| 82 | +RUN apt-get update && apt-get install -y --no-install-recommends \ |
| 83 | + curl \ |
| 84 | + ca-certificates \ |
| 85 | + gnupg \ |
| 86 | + supervisor \ |
| 87 | + && mkdir -p /etc/apt/keyrings \ |
| 88 | + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ |
| 89 | + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \ |
| 90 | + && apt-get update \ |
| 91 | + && apt-get install -y nodejs \ |
| 92 | + && rm -rf /var/lib/apt/lists/* \ |
| 93 | + && npm --version |
| 94 | + |
| 95 | +# Copy Python packages from builder stage |
| 96 | +COPY --from=python-base /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages |
| 97 | +COPY --from=python-base /usr/local/bin /usr/local/bin |
| 98 | + |
| 99 | +# Copy built frontend from frontend-builder stage |
| 100 | +COPY --from=frontend-builder /app/web/.next ./web/.next |
| 101 | +COPY --from=frontend-builder /app/web/public ./web/public |
| 102 | +COPY --from=frontend-builder /app/web/package.json ./web/package.json |
| 103 | +COPY --from=frontend-builder /app/web/next.config.js ./web/next.config.js |
| 104 | +COPY --from=frontend-builder /app/web/node_modules ./web/node_modules |
| 105 | + |
| 106 | +# Create a startup script for the frontend |
| 107 | +RUN echo '#!/bin/bash\ncd /app/web && exec node node_modules/next/dist/bin/next start -H 0.0.0.0 -p ${FRONTEND_PORT:-3782}' > /app/start-frontend.sh && \ |
| 108 | + chmod +x /app/start-frontend.sh |
| 109 | + |
| 110 | +# Copy application source code |
| 111 | +COPY src/ ./src/ |
| 112 | +COPY config/ ./config/ |
| 113 | +COPY scripts/ ./scripts/ |
| 114 | +COPY pyproject.toml ./ |
| 115 | +COPY requirements.txt ./ |
| 116 | + |
| 117 | +# Create necessary directories |
| 118 | +RUN mkdir -p \ |
| 119 | + data/user/solve \ |
| 120 | + data/user/question \ |
| 121 | + data/user/research/cache \ |
| 122 | + data/user/research/reports \ |
| 123 | + data/user/guide \ |
| 124 | + data/user/notebook \ |
| 125 | + data/user/co-writer/audio \ |
| 126 | + data/user/co-writer/tool_calls \ |
| 127 | + data/user/logs \ |
| 128 | + data/user/run_code_workspace \ |
| 129 | + data/user/performance \ |
| 130 | + data/knowledge_bases |
| 131 | + |
| 132 | +# Create supervisord configuration for running both services |
| 133 | +RUN mkdir -p /etc/supervisor/conf.d |
| 134 | + |
| 135 | +COPY <<EOF /etc/supervisor/conf.d/deeptutor.conf |
| 136 | +[supervisord] |
| 137 | +nodaemon=true |
| 138 | +logfile=/var/log/supervisor/supervisord.log |
| 139 | +pidfile=/var/run/supervisord.pid |
| 140 | +childlogdir=/var/log/supervisor |
| 141 | + |
| 142 | +[program:backend] |
| 143 | +command=python -m uvicorn src.api.main:app --host 0.0.0.0 --port %(ENV_BACKEND_PORT)s |
| 144 | +directory=/app |
| 145 | +autostart=true |
| 146 | +autorestart=true |
| 147 | +stderr_logfile=/var/log/supervisor/backend.err.log |
| 148 | +stdout_logfile=/var/log/supervisor/backend.out.log |
| 149 | +environment=PYTHONPATH="/app",PYTHONUNBUFFERED="1" |
| 150 | + |
| 151 | +[program:frontend] |
| 152 | +command=/bin/bash /app/start-frontend.sh |
| 153 | +directory=/app/web |
| 154 | +autostart=true |
| 155 | +autorestart=true |
| 156 | +startsecs=5 |
| 157 | +stderr_logfile=/var/log/supervisor/frontend.err.log |
| 158 | +stdout_logfile=/var/log/supervisor/frontend.out.log |
| 159 | +environment=NODE_ENV="production" |
| 160 | +EOF |
| 161 | + |
| 162 | +# Create log directories |
| 163 | +RUN mkdir -p /var/log/supervisor |
| 164 | + |
| 165 | +# Create entrypoint script |
| 166 | +COPY <<'EOF' /app/entrypoint.sh |
| 167 | +#!/bin/bash |
| 168 | +set -e |
| 169 | + |
| 170 | +echo "============================================" |
| 171 | +echo "🚀 Starting DeepTutor" |
| 172 | +echo "============================================" |
| 173 | + |
| 174 | +# Set default ports if not provided |
| 175 | +export BACKEND_PORT=${BACKEND_PORT:-8001} |
| 176 | +export FRONTEND_PORT=${FRONTEND_PORT:-3782} |
| 177 | + |
| 178 | +# Update Next.js environment file with actual backend URL |
| 179 | +echo "NEXT_PUBLIC_API_BASE=http://localhost:${BACKEND_PORT}" > /app/web/.env.local |
| 180 | + |
| 181 | +# If NEXT_PUBLIC_API_BASE is provided externally, use it |
| 182 | +if [ -n "$NEXT_PUBLIC_API_BASE_EXTERNAL" ]; then |
| 183 | + echo "NEXT_PUBLIC_API_BASE=${NEXT_PUBLIC_API_BASE_EXTERNAL}" > /app/web/.env.local |
| 184 | +fi |
| 185 | + |
| 186 | +echo "📌 Backend Port: ${BACKEND_PORT}" |
| 187 | +echo "📌 Frontend Port: ${FRONTEND_PORT}" |
| 188 | +echo "============================================" |
| 189 | + |
| 190 | +# Check for required environment variables |
| 191 | +if [ -z "$LLM_BINDING_API_KEY" ]; then |
| 192 | + echo "⚠️ Warning: LLM_BINDING_API_KEY not set" |
| 193 | + echo " Please provide LLM configuration via environment variables" |
| 194 | +fi |
| 195 | + |
| 196 | +# Start supervisord |
| 197 | +exec /usr/bin/supervisord -c /etc/supervisor/conf.d/deeptutor.conf |
| 198 | +EOF |
| 199 | + |
| 200 | +RUN chmod +x /app/entrypoint.sh |
| 201 | + |
| 202 | +# Expose ports |
| 203 | +EXPOSE 8001 3782 |
| 204 | + |
| 205 | +# Health check |
| 206 | +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ |
| 207 | + CMD curl -f http://localhost:${BACKEND_PORT:-8001}/ || exit 1 |
| 208 | + |
| 209 | +# Set entrypoint |
| 210 | +ENTRYPOINT ["/app/entrypoint.sh"] |
| 211 | + |
| 212 | +# ============================================ |
| 213 | +# Stage 4: Development Image (Optional) |
| 214 | +# ============================================ |
| 215 | +FROM production AS development |
| 216 | + |
| 217 | +# Install development tools |
| 218 | +RUN apt-get update && apt-get install -y --no-install-recommends \ |
| 219 | + vim \ |
| 220 | + git \ |
| 221 | + && rm -rf /var/lib/apt/lists/* |
| 222 | + |
| 223 | +# Install development Python packages |
| 224 | +RUN pip install --no-cache-dir \ |
| 225 | + pre-commit \ |
| 226 | + black \ |
| 227 | + ruff |
| 228 | + |
| 229 | +# Override supervisord config for development (with reload) |
| 230 | +COPY <<EOF /etc/supervisor/conf.d/deeptutor.conf |
| 231 | +[supervisord] |
| 232 | +nodaemon=true |
| 233 | +logfile=/var/log/supervisor/supervisord.log |
| 234 | +pidfile=/var/run/supervisord.pid |
| 235 | +childlogdir=/var/log/supervisor |
| 236 | + |
| 237 | +[program:backend] |
| 238 | +command=python -m uvicorn src.api.main:app --host 0.0.0.0 --port %(ENV_BACKEND_PORT)s --reload |
| 239 | +directory=/app |
| 240 | +autostart=true |
| 241 | +autorestart=true |
| 242 | +stderr_logfile=/var/log/supervisor/backend.err.log |
| 243 | +stdout_logfile=/var/log/supervisor/backend.out.log |
| 244 | +environment=PYTHONPATH="/app",PYTHONUNBUFFERED="1" |
| 245 | + |
| 246 | +[program:frontend] |
| 247 | +command=/bin/bash -c "cd /app/web && node node_modules/next/dist/bin/next dev -H 0.0.0.0 -p ${FRONTEND_PORT:-3782}" |
| 248 | +directory=/app/web |
| 249 | +autostart=true |
| 250 | +autorestart=true |
| 251 | +startsecs=5 |
| 252 | +stderr_logfile=/var/log/supervisor/frontend.err.log |
| 253 | +stdout_logfile=/var/log/supervisor/frontend.out.log |
| 254 | +environment=NODE_ENV="development" |
| 255 | +EOF |
| 256 | + |
| 257 | +# Development ports |
| 258 | +EXPOSE 8001 3782 |
0 commit comments