diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..baf2161 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +.git +.gitignore +.pytest_cache +.ruff_cache +.mypy_cache +.venv +__pycache__ +*.py[cod] +*.egg-info + +backend/.env +backend/__pycache__ +backend/**/*.py[cod] +backend/**/__pycache__ + +opencad_viewport/.env +opencad_viewport/node_modules +opencad_viewport/dist +opencad_viewport/tsconfig.tsbuildinfo + +*.log diff --git a/README.md b/README.md index f4c53e4..c07188e 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,47 @@ The viewport uses **mock geometry/solver data** by default (no backend required Chat targets the live agent service by default; set `VITE_USE_CHAT_MOCK=true` if you explicitly want mocked chat output. Set `VITE_USE_MOCK=false` to connect the rest of the viewport to the live services above. +### 5. Run with Docker + +Build the backend image from the repository root so Docker can see the Python +project metadata and backend package files: + +```bash +docker build -f backend/Dockerfile -t opencad-backend . +``` + +Build the frontend image from the viewport directory: + +```bash +docker build -f opencad_viewport/Dockerfile -t opencad-frontend opencad_viewport +``` + +Run the backend API on port `8000`: + +```bash +docker run --rm -p 8000:8000 opencad-backend +``` + +Run the frontend on port `5173`: + +```bash +docker run --rm -p 5173:80 opencad-frontend +``` + +By default, the frontend image is built with `VITE_BASE_URL=http://localhost:8000` +and live API calls enabled. To point the frontend at another API URL or enable +mock mode, pass build args: + +```bash +docker build \ + -f opencad_viewport/Dockerfile \ + -t opencad-frontend \ + --build-arg VITE_BASE_URL=http://localhost:8000 \ + --build-arg VITE_USE_MOCK=false \ + --build-arg VITE_USE_CHAT_MOCK=false \ + opencad_viewport +``` + ## Configuration Runtime defaults are documented in `.env.example`. diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..7b5bdc6 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,36 @@ +# syntax=docker/dockerfile:1 + +FROM python:3.11-slim AS runtime + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=1 \ + OPENCAD_KERNEL_BACKEND=occt \ + OPENCAD_SOLVER_BACKEND=auto \ + OPENCAD_CORS_ALLOW_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 + +WORKDIR /app + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + libgl1 \ + libglib2.0-0 \ + libgomp1 \ + libsm6 \ + libxext6 \ + libxrender1 \ + && rm -rf /var/lib/apt/lists/* + +COPY pyproject.toml README.md ./ +COPY backend ./backend + +RUN python -m pip install --upgrade pip \ + && python -m pip install ".[full]" + +EXPOSE 8000 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ + CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/kernel/healthz', timeout=3).read()" || exit 1 + +CMD ["python", "-m", "uvicorn", "api:app", "--app-dir", "backend", "--host", "0.0.0.0", "--port", "8000"] diff --git a/opencad_viewport/.dockerignore b/opencad_viewport/.dockerignore new file mode 100644 index 0000000..4f4169e --- /dev/null +++ b/opencad_viewport/.dockerignore @@ -0,0 +1,5 @@ +.env +dist +node_modules +tsconfig.tsbuildinfo +*.log diff --git a/opencad_viewport/Dockerfile b/opencad_viewport/Dockerfile new file mode 100644 index 0000000..c6cdd26 --- /dev/null +++ b/opencad_viewport/Dockerfile @@ -0,0 +1,32 @@ +# syntax=docker/dockerfile:1 + +FROM node:20-alpine AS build + +WORKDIR /app + +RUN corepack enable + +COPY package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile + +COPY . . + +ARG VITE_BASE_URL=http://localhost:8000 +ARG VITE_USE_MOCK=false +ARG VITE_USE_CHAT_MOCK=false + +ENV VITE_BASE_URL=${VITE_BASE_URL} \ + VITE_USE_MOCK=${VITE_USE_MOCK} \ + VITE_USE_CHAT_MOCK=${VITE_USE_CHAT_MOCK} + +RUN pnpm build + +FROM nginx:1.27-alpine AS runtime + +COPY nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build /app/dist /usr/share/nginx/html + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD wget -qO- http://127.0.0.1/ >/dev/null || exit 1 diff --git a/opencad_viewport/nginx.conf b/opencad_viewport/nginx.conf new file mode 100644 index 0000000..3aa17e6 --- /dev/null +++ b/opencad_viewport/nginx.conf @@ -0,0 +1,11 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/opencad_viewport/src/mock/mockData.ts b/opencad_viewport/src/mock/mockData.ts index 0d3433a..e5b2879 100644 --- a/opencad_viewport/src/mock/mockData.ts +++ b/opencad_viewport/src/mock/mockData.ts @@ -74,6 +74,8 @@ export const mockFeatureTree: FeatureTreeView = { typed_parameters: {}, parameter_bindings: [], sketch_id: "sketch-base", + parent_id: null, + tool_refs: [], depends_on: [], shape_id: "shape-base-plate", status: "built", @@ -86,6 +88,8 @@ export const mockFeatureTree: FeatureTreeView = { parameters: { tool: "cutout-tool" }, typed_parameters: {}, parameter_bindings: [], + parent_id: "base", + tool_refs: ["cutout-tool"], depends_on: ["base"], shape_id: "shape-cutout", status: "stale", @@ -98,6 +102,8 @@ export const mockFeatureTree: FeatureTreeView = { parameters: { radius: 1.5 }, typed_parameters: {}, parameter_bindings: [], + parent_id: "cutout", + tool_refs: [], depends_on: ["cutout"], shape_id: null, status: "pending",