diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..325b198 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,22 @@ +# Kerf Engine — Docker build context exclusions +.git +.gsd +.venv +venv +__pycache__ +*.pyc +*.pyo +.pytest_cache +*.egg-info +.env +.env.* +node_modules +.next +dist +build +coverage +.cache +tmp +*.log +.DS_Store +Thumbs.db diff --git a/docker/Dockerfile.engine b/docker/Dockerfile.engine new file mode 100644 index 0000000..390bd86 --- /dev/null +++ b/docker/Dockerfile.engine @@ -0,0 +1,61 @@ +# ── Kerf Engine — multi-stage Docker build ── +# Standalone raster-to-vector conversion API (no App dependencies) + +# ── Stage 1: Build dependencies ── +FROM python:3.11-slim AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + pkg-config \ + libagg-dev \ + libpotrace-dev \ + libpotrace0 \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build + +# Install Python deps into a virtual-env we can copy later +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +COPY engine/pyproject.toml . +RUN pip install --no-cache-dir . 2>/dev/null || true +# The above may fail because package source isn't present yet — install deps directly +RUN pip install --no-cache-dir \ + "fastapi>=0.110" \ + "uvicorn[standard]>=0.29" \ + "opencv-python-headless>=4.9" \ + "pypotrace>=0.3" \ + "vtracer>=0.6" \ + "python-multipart>=0.0.9" \ + "Pillow>=10.2" \ + "ezdxf>=1.0" + +# ── Stage 2: Runtime image ── +FROM python:3.11-slim AS runtime + +# Runtime-only system libs for pypotrace +RUN apt-get update && apt-get install -y --no-install-recommends \ + libpotrace0 \ + libagg2 \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Copy the virtual-env from the builder +COPY --from=builder /opt/venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +# Copy engine source only — no App code +WORKDIR /app +COPY engine/main.py . +COPY engine/api/ ./api/ +COPY engine/pipeline/ ./pipeline/ +COPY engine/output/ ./output/ +COPY engine/presets/ ./presets/ + +EXPOSE 8000 + +HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 \ + CMD curl -sf http://localhost:8000/engine/health || exit 1 + +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/engine/api/routes.py b/engine/api/routes.py index 3a48445..3ca93dc 100644 --- a/engine/api/routes.py +++ b/engine/api/routes.py @@ -14,6 +14,13 @@ from presets.loader import all_presets, preset_names, resolve_params router = APIRouter() + +@router.get("/engine/health") +async def health(): + """Healthcheck endpoint for container orchestration.""" + return {"status": "ok"} + + VALID_MODES = {"potrace", "vtracer"} VALID_OUTPUT_FORMATS = {"svg", "dxf", "json"}