# ─── Этап 1: сборка SPA ─────────────────────────────────────────────────────── FROM node:20-bookworm-slim AS frontend WORKDIR /app/frontend # Сначала зависимости (кеш слоёв). Используем lock-файл, если он есть. COPY frontend/package*.json ./ RUN npm install --no-audit --no-fund COPY frontend/ ./ # Типы schema.d.ts уже в репозитории — бэкенд для сборки не нужен. RUN npm run build # ─── Этап 2: рантайм FastAPI (ARM64-совместимый glibc-образ) ────────────────── FROM python:3.12-slim-bookworm AS runtime # Контейнер — всегда production. БД/загрузки выбираются по APP_ENV (см. config.py): # prod → /data на томе. SPA отдаётся из /app/static. ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ APP_ENV=production \ STATIC_DIR=/app/static # tini — корректная обработка сигналов (чек-пойнт WAL при остановке). RUN apt-get update \ && apt-get install -y --no-install-recommends tini \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY backend/requirements.txt ./requirements.txt RUN pip install --no-cache-dir -r requirements.txt COPY backend/ /app/ COPY --from=frontend /app/frontend/dist /app/static # Непривилегированный пользователь + том данных + исполняемый entrypoint (LF). RUN sed -i 's/\r$//' /app/entrypoint.sh \ && chmod +x /app/entrypoint.sh \ && useradd --create-home --uid 10001 appuser \ && mkdir -p /data/uploads \ && chown -R appuser:appuser /app /data USER appuser EXPOSE 8000 ENTRYPOINT ["/usr/bin/tini", "--", "/app/entrypoint.sh"]