feat: Created multi-stage Dockerfile.engine with healthcheck endpoint;…
- "docker/Dockerfile.engine" - "engine/api/routes.py" - ".dockerignore" GSD-Task: S03/T02
This commit is contained in:
parent
c693f5e1e2
commit
2d8efb15dd
8 changed files with 275 additions and 13 deletions
22
.dockerignore
Normal file
22
.dockerignore
Normal file
|
|
@ -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
|
||||||
|
|
@ -9,3 +9,4 @@
|
||||||
{"cmd":"complete-task","params":{"milestoneId":"M001","sliceId":"S02","taskId":"T03"},"ts":"2026-03-26T04:39:50.468Z","actor":"agent","hash":"00412cfd0b09e3c4","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
{"cmd":"complete-task","params":{"milestoneId":"M001","sliceId":"S02","taskId":"T03"},"ts":"2026-03-26T04:39:50.468Z","actor":"agent","hash":"00412cfd0b09e3c4","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
||||||
{"cmd":"complete-slice","params":{"milestoneId":"M001","sliceId":"S02"},"ts":"2026-03-26T04:41:58.014Z","actor":"agent","hash":"296c12d4a2f536c8","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
{"cmd":"complete-slice","params":{"milestoneId":"M001","sliceId":"S02"},"ts":"2026-03-26T04:41:58.014Z","actor":"agent","hash":"296c12d4a2f536c8","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
||||||
{"cmd":"complete-task","params":{"milestoneId":"M001","sliceId":"S03","taskId":"T01"},"ts":"2026-03-26T04:45:48.732Z","actor":"agent","hash":"a1c0c74b1d7c5d15","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
{"cmd":"complete-task","params":{"milestoneId":"M001","sliceId":"S03","taskId":"T01"},"ts":"2026-03-26T04:45:48.732Z","actor":"agent","hash":"a1c0c74b1d7c5d15","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
||||||
|
{"cmd":"complete-task","params":{"milestoneId":"M001","sliceId":"S03","taskId":"T02"},"ts":"2026-03-26T04:49:33.566Z","actor":"agent","hash":"fc8b517936769f11","session_id":"49f8e0fe-34a0-4608-b519-eca93850ed7c"}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
- Estimate: 45min
|
- Estimate: 45min
|
||||||
- Files: engine/presets/sign.json, engine/presets/patch.json, engine/presets/stencil.json, engine/presets/detailed.json, engine/presets/custom.json, engine/presets/loader.py, engine/api/routes.py
|
- Files: engine/presets/sign.json, engine/presets/patch.json, engine/presets/stencil.json, engine/presets/detailed.json, engine/presets/custom.json, engine/presets/loader.py, engine/api/routes.py
|
||||||
- Verify: cd engine && python -m pytest tests/ -v -k preset
|
- Verify: cd engine && python -m pytest tests/ -v -k preset
|
||||||
- [ ] **T02: Engine Dockerfile + healthcheck** — 1. Create docker/Dockerfile.engine (multi-stage build)
|
- [x] **T02: Created multi-stage Dockerfile.engine with healthcheck endpoint; container builds, starts, and responds healthy on /engine/health** — 1. Create docker/Dockerfile.engine (multi-stage build)
|
||||||
2. Install system deps: libopencv, potrace libs
|
2. Install system deps: libopencv, potrace libs
|
||||||
3. Install Python deps from pyproject.toml
|
3. Install Python deps from pyproject.toml
|
||||||
4. Add healthcheck endpoint: GET /engine/health → {status: 'ok'}
|
4. Add healthcheck endpoint: GET /engine/health → {status: 'ok'}
|
||||||
|
|
|
||||||
24
.gsd/milestones/M001/slices/S03/tasks/T01-VERIFY.json
Normal file
24
.gsd/milestones/M001/slices/S03/tasks/T01-VERIFY.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"taskId": "T01",
|
||||||
|
"unitId": "M001/S03/T01",
|
||||||
|
"timestamp": 1774500352182,
|
||||||
|
"passed": false,
|
||||||
|
"discoverySource": "task-plan",
|
||||||
|
"checks": [
|
||||||
|
{
|
||||||
|
"command": "cd engine",
|
||||||
|
"exitCode": 0,
|
||||||
|
"durationMs": 4,
|
||||||
|
"verdict": "pass"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "python -m pytest tests/ -v -k preset",
|
||||||
|
"exitCode": 127,
|
||||||
|
"durationMs": 3,
|
||||||
|
"verdict": "fail"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"retryAttempt": 1,
|
||||||
|
"maxRetries": 2
|
||||||
|
}
|
||||||
84
.gsd/milestones/M001/slices/S03/tasks/T02-SUMMARY.md
Normal file
84
.gsd/milestones/M001/slices/S03/tasks/T02-SUMMARY.md
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
---
|
||||||
|
id: T02
|
||||||
|
parent: S03
|
||||||
|
milestone: M001
|
||||||
|
provides: []
|
||||||
|
requires: []
|
||||||
|
affects: []
|
||||||
|
key_files: ["docker/Dockerfile.engine", "engine/api/routes.py", ".dockerignore"]
|
||||||
|
key_decisions: ["Multi-stage build separates build deps (build-essential, potrace-dev, agg-dev) from runtime (libpotrace0, libagg2, curl)", "Health endpoint at /engine/health for namespace consistency with other /engine/* routes", "Engine image contains only engine source (api, pipeline, output, presets, main.py) — no App dependencies"]
|
||||||
|
patterns_established: []
|
||||||
|
drill_down_paths: []
|
||||||
|
observability_surfaces: []
|
||||||
|
duration: ""
|
||||||
|
verification_result: "Built image successfully (exit 0), ran container on port 8100, confirmed /engine/health returns {"status":"ok"}, Docker health status shows "healthy" with 0 failing streak, presets endpoint works from container, no App files in image, full test suite passes (196/196), preset tests pass (28/28)."
|
||||||
|
completed_at: 2026-03-26T04:49:33.510Z
|
||||||
|
blocker_discovered: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# T02: Created multi-stage Dockerfile.engine with healthcheck endpoint; container builds, starts, and responds healthy on /engine/health
|
||||||
|
|
||||||
|
> Created multi-stage Dockerfile.engine with healthcheck endpoint; container builds, starts, and responds healthy on /engine/health
|
||||||
|
|
||||||
|
## What Happened
|
||||||
|
---
|
||||||
|
id: T02
|
||||||
|
parent: S03
|
||||||
|
milestone: M001
|
||||||
|
key_files:
|
||||||
|
- docker/Dockerfile.engine
|
||||||
|
- engine/api/routes.py
|
||||||
|
- .dockerignore
|
||||||
|
key_decisions:
|
||||||
|
- Multi-stage build separates build deps (build-essential, potrace-dev, agg-dev) from runtime (libpotrace0, libagg2, curl)
|
||||||
|
- Health endpoint at /engine/health for namespace consistency with other /engine/* routes
|
||||||
|
- Engine image contains only engine source (api, pipeline, output, presets, main.py) — no App dependencies
|
||||||
|
duration: ""
|
||||||
|
verification_result: passed
|
||||||
|
completed_at: 2026-03-26T04:49:33.527Z
|
||||||
|
blocker_discovered: false
|
||||||
|
---
|
||||||
|
|
||||||
|
# T02: Created multi-stage Dockerfile.engine with healthcheck endpoint; container builds, starts, and responds healthy on /engine/health
|
||||||
|
|
||||||
|
**Created multi-stage Dockerfile.engine with healthcheck endpoint; container builds, starts, and responds healthy on /engine/health**
|
||||||
|
|
||||||
|
## What Happened
|
||||||
|
|
||||||
|
Created docker/Dockerfile.engine as a two-stage build: builder stage installs build-essential, libagg-dev, and libpotrace-dev to compile pypotrace, then installs all Python deps into /opt/venv. Runtime stage uses python:3.11-slim with only runtime libs (libpotrace0, libagg2, curl), copies the venv, and copies only engine source code. Added GET /engine/health endpoint returning {"status": "ok"} to engine/api/routes.py. Configured Docker HEALTHCHECK instruction (15s interval, 5s timeout, 10s start-period, 3 retries). Added .dockerignore for clean builds. Verified container builds, starts, responds healthy, and contains no App/frontend code.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Built image successfully (exit 0), ran container on port 8100, confirmed /engine/health returns {"status":"ok"}, Docker health status shows "healthy" with 0 failing streak, presets endpoint works from container, no App files in image, full test suite passes (196/196), preset tests pass (28/28).
|
||||||
|
|
||||||
|
## Verification Evidence
|
||||||
|
|
||||||
|
| # | Command | Exit Code | Verdict | Duration |
|
||||||
|
|---|---------|-----------|---------|----------|
|
||||||
|
| 1 | `docker build -f docker/Dockerfile.engine -t kerf-engine:dev .` | 0 | ✅ pass | 78700ms |
|
||||||
|
| 2 | `curl -sf http://localhost:8100/engine/health` | 0 | ✅ pass | 5100ms |
|
||||||
|
| 3 | `docker inspect kerf-engine-test --format='{{json .State.Health.Status}}'` | 0 | ✅ pass | 200ms |
|
||||||
|
| 4 | `cd engine && .venv/bin/python -m pytest tests/ -v -k preset` | 0 | ✅ pass | 600ms |
|
||||||
|
| 5 | `cd engine && .venv/bin/python -m pytest tests/ -v` | 0 | ✅ pass | 940ms |
|
||||||
|
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
|
||||||
|
Added /engine/health on the router for namespace consistency (root /health already existed in main.py). Both endpoints now coexist.
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `docker/Dockerfile.engine`
|
||||||
|
- `engine/api/routes.py`
|
||||||
|
- `.dockerignore`
|
||||||
|
|
||||||
|
|
||||||
|
## Deviations
|
||||||
|
Added /engine/health on the router for namespace consistency (root /health already existed in main.py). Both endpoints now coexist.
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
None.
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"exported_at": "2026-03-26T04:45:48.731Z",
|
"exported_at": "2026-03-26T04:49:33.565Z",
|
||||||
"milestones": [
|
"milestones": [
|
||||||
{
|
{
|
||||||
"id": "M001",
|
"id": "M001",
|
||||||
|
|
@ -774,19 +774,27 @@
|
||||||
"milestone_id": "M001",
|
"milestone_id": "M001",
|
||||||
"slice_id": "S03",
|
"slice_id": "S03",
|
||||||
"id": "T02",
|
"id": "T02",
|
||||||
"title": "Engine Dockerfile + healthcheck",
|
"title": "Created multi-stage Dockerfile.engine with healthcheck endpoint; container builds, starts, and responds healthy on /engine/health",
|
||||||
"status": "pending",
|
"status": "complete",
|
||||||
"one_liner": "",
|
"one_liner": "Created multi-stage Dockerfile.engine with healthcheck endpoint; container builds, starts, and responds healthy on /engine/health",
|
||||||
"narrative": "",
|
"narrative": "Created docker/Dockerfile.engine as a two-stage build: builder stage installs build-essential, libagg-dev, and libpotrace-dev to compile pypotrace, then installs all Python deps into /opt/venv. Runtime stage uses python:3.11-slim with only runtime libs (libpotrace0, libagg2, curl), copies the venv, and copies only engine source code. Added GET /engine/health endpoint returning {\"status\": \"ok\"} to engine/api/routes.py. Configured Docker HEALTHCHECK instruction (15s interval, 5s timeout, 10s start-period, 3 retries). Added .dockerignore for clean builds. Verified container builds, starts, responds healthy, and contains no App/frontend code.",
|
||||||
"verification_result": "",
|
"verification_result": "Built image successfully (exit 0), ran container on port 8100, confirmed /engine/health returns {\"status\":\"ok\"}, Docker health status shows \"healthy\" with 0 failing streak, presets endpoint works from container, no App files in image, full test suite passes (196/196), preset tests pass (28/28).",
|
||||||
"duration": "",
|
"duration": "",
|
||||||
"completed_at": null,
|
"completed_at": "2026-03-26T04:49:33.510Z",
|
||||||
"blocker_discovered": false,
|
"blocker_discovered": false,
|
||||||
"deviations": "",
|
"deviations": "Added /engine/health on the router for namespace consistency (root /health already existed in main.py). Both endpoints now coexist.",
|
||||||
"known_issues": "",
|
"known_issues": "None.",
|
||||||
"key_files": [],
|
"key_files": [
|
||||||
"key_decisions": [],
|
"docker/Dockerfile.engine",
|
||||||
"full_summary_md": "",
|
"engine/api/routes.py",
|
||||||
|
".dockerignore"
|
||||||
|
],
|
||||||
|
"key_decisions": [
|
||||||
|
"Multi-stage build separates build deps (build-essential, potrace-dev, agg-dev) from runtime (libpotrace0, libagg2, curl)",
|
||||||
|
"Health endpoint at /engine/health for namespace consistency with other /engine/* routes",
|
||||||
|
"Engine image contains only engine source (api, pipeline, output, presets, main.py) — no App dependencies"
|
||||||
|
],
|
||||||
|
"full_summary_md": "---\nid: T02\nparent: S03\nmilestone: M001\nkey_files:\n - docker/Dockerfile.engine\n - engine/api/routes.py\n - .dockerignore\nkey_decisions:\n - Multi-stage build separates build deps (build-essential, potrace-dev, agg-dev) from runtime (libpotrace0, libagg2, curl)\n - Health endpoint at /engine/health for namespace consistency with other /engine/* routes\n - Engine image contains only engine source (api, pipeline, output, presets, main.py) — no App dependencies\nduration: \"\"\nverification_result: passed\ncompleted_at: 2026-03-26T04:49:33.527Z\nblocker_discovered: false\n---\n\n# T02: Created multi-stage Dockerfile.engine with healthcheck endpoint; container builds, starts, and responds healthy on /engine/health\n\n**Created multi-stage Dockerfile.engine with healthcheck endpoint; container builds, starts, and responds healthy on /engine/health**\n\n## What Happened\n\nCreated docker/Dockerfile.engine as a two-stage build: builder stage installs build-essential, libagg-dev, and libpotrace-dev to compile pypotrace, then installs all Python deps into /opt/venv. Runtime stage uses python:3.11-slim with only runtime libs (libpotrace0, libagg2, curl), copies the venv, and copies only engine source code. Added GET /engine/health endpoint returning {\"status\": \"ok\"} to engine/api/routes.py. Configured Docker HEALTHCHECK instruction (15s interval, 5s timeout, 10s start-period, 3 retries). Added .dockerignore for clean builds. Verified container builds, starts, responds healthy, and contains no App/frontend code.\n\n## Verification\n\nBuilt image successfully (exit 0), ran container on port 8100, confirmed /engine/health returns {\"status\":\"ok\"}, Docker health status shows \"healthy\" with 0 failing streak, presets endpoint works from container, no App files in image, full test suite passes (196/196), preset tests pass (28/28).\n\n## Verification Evidence\n\n| # | Command | Exit Code | Verdict | Duration |\n|---|---------|-----------|---------|----------|\n| 1 | `docker build -f docker/Dockerfile.engine -t kerf-engine:dev .` | 0 | ✅ pass | 78700ms |\n| 2 | `curl -sf http://localhost:8100/engine/health` | 0 | ✅ pass | 5100ms |\n| 3 | `docker inspect kerf-engine-test --format='{{json .State.Health.Status}}'` | 0 | ✅ pass | 200ms |\n| 4 | `cd engine && .venv/bin/python -m pytest tests/ -v -k preset` | 0 | ✅ pass | 600ms |\n| 5 | `cd engine && .venv/bin/python -m pytest tests/ -v` | 0 | ✅ pass | 940ms |\n\n\n## Deviations\n\nAdded /engine/health on the router for namespace consistency (root /health already existed in main.py). Both endpoints now coexist.\n\n## Known Issues\n\nNone.\n\n## Files Created/Modified\n\n- `docker/Dockerfile.engine`\n- `engine/api/routes.py`\n- `.dockerignore`\n",
|
||||||
"description": "1. Create docker/Dockerfile.engine (multi-stage build)\n2. Install system deps: libopencv, potrace libs\n3. Install Python deps from pyproject.toml\n4. Add healthcheck endpoint: GET /engine/health → {status: 'ok'}\n5. Configure Dockerfile HEALTHCHECK instruction\n6. Build and test container starts\n7. Verify API responds from inside container\n8. Ensure no App dependencies in engine image",
|
"description": "1. Create docker/Dockerfile.engine (multi-stage build)\n2. Install system deps: libopencv, potrace libs\n3. Install Python deps from pyproject.toml\n4. Add healthcheck endpoint: GET /engine/health → {status: 'ok'}\n5. Configure Dockerfile HEALTHCHECK instruction\n6. Build and test container starts\n7. Verify API responds from inside container\n8. Ensure no App dependencies in engine image",
|
||||||
"estimate": "30min",
|
"estimate": "30min",
|
||||||
"files": [
|
"files": [
|
||||||
|
|
@ -942,6 +950,61 @@
|
||||||
"verdict": "✅ pass",
|
"verdict": "✅ pass",
|
||||||
"duration_ms": 960,
|
"duration_ms": 960,
|
||||||
"created_at": "2026-03-26T04:45:48.689Z"
|
"created_at": "2026-03-26T04:45:48.689Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"task_id": "T02",
|
||||||
|
"slice_id": "S03",
|
||||||
|
"milestone_id": "M001",
|
||||||
|
"command": "docker build -f docker/Dockerfile.engine -t kerf-engine:dev .",
|
||||||
|
"exit_code": 0,
|
||||||
|
"verdict": "✅ pass",
|
||||||
|
"duration_ms": 78700,
|
||||||
|
"created_at": "2026-03-26T04:49:33.510Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"task_id": "T02",
|
||||||
|
"slice_id": "S03",
|
||||||
|
"milestone_id": "M001",
|
||||||
|
"command": "curl -sf http://localhost:8100/engine/health",
|
||||||
|
"exit_code": 0,
|
||||||
|
"verdict": "✅ pass",
|
||||||
|
"duration_ms": 5100,
|
||||||
|
"created_at": "2026-03-26T04:49:33.510Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"task_id": "T02",
|
||||||
|
"slice_id": "S03",
|
||||||
|
"milestone_id": "M001",
|
||||||
|
"command": "docker inspect kerf-engine-test --format='{{json .State.Health.Status}}'",
|
||||||
|
"exit_code": 0,
|
||||||
|
"verdict": "✅ pass",
|
||||||
|
"duration_ms": 200,
|
||||||
|
"created_at": "2026-03-26T04:49:33.510Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"task_id": "T02",
|
||||||
|
"slice_id": "S03",
|
||||||
|
"milestone_id": "M001",
|
||||||
|
"command": "cd engine && .venv/bin/python -m pytest tests/ -v -k preset",
|
||||||
|
"exit_code": 0,
|
||||||
|
"verdict": "✅ pass",
|
||||||
|
"duration_ms": 600,
|
||||||
|
"created_at": "2026-03-26T04:49:33.510Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"task_id": "T02",
|
||||||
|
"slice_id": "S03",
|
||||||
|
"milestone_id": "M001",
|
||||||
|
"command": "cd engine && .venv/bin/python -m pytest tests/ -v",
|
||||||
|
"exit_code": 0,
|
||||||
|
"verdict": "✅ pass",
|
||||||
|
"duration_ms": 940,
|
||||||
|
"created_at": "2026-03-26T04:49:33.510Z"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
61
docker/Dockerfile.engine
Normal file
61
docker/Dockerfile.engine
Normal file
|
|
@ -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"]
|
||||||
|
|
@ -14,6 +14,13 @@ from presets.loader import all_presets, preset_names, resolve_params
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/engine/health")
|
||||||
|
async def health():
|
||||||
|
"""Healthcheck endpoint for container orchestration."""
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
|
||||||
VALID_MODES = {"potrace", "vtracer"}
|
VALID_MODES = {"potrace", "vtracer"}
|
||||||
VALID_OUTPUT_FORMATS = {"svg", "dxf", "json"}
|
VALID_OUTPUT_FORMATS = {"svg", "dxf", "json"}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue