diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4db2a3c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +node_modules +tools/venv +uploads +thumbnails +glb +data +.git +.gitignore +.env +*.log +.DS_Store +npm-debug.log* +Dockerfile +.dockerignore +docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9b29a9e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# Printhive — Node app + Python/trimesh sidecar (for 3MF -> GLB conversion) +FROM node:22-bookworm-slim + +# Python is needed at runtime for the 3MF->GLB / volume sidecar. +RUN apt-get update \ + && apt-get install -y --no-install-recommends python3 python3-venv \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Tell the converter where Python lives (the venv built below). +ENV PYTHON_BIN=/app/tools/venv/bin/python + +# Python sidecar deps (cached unless requirements.txt changes). +COPY tools/requirements.txt ./tools/requirements.txt +RUN python3 -m venv /app/tools/venv \ + && /app/tools/venv/bin/pip install --no-cache-dir -r tools/requirements.txt + +# Node deps (cached unless package files change). +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev + +# App source. +COPY . . + +ENV NODE_ENV=production \ + PORT=3000 +EXPOSE 3000 + +CMD ["node", "server.js"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6c5a3fb --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,50 @@ +services: + db: + image: postgres:16-alpine + environment: + POSTGRES_USER: printhive + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-printhive} + POSTGRES_DB: printhive + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U printhive -d printhive"] + interval: 5s + timeout: 5s + retries: 10 + restart: unless-stopped + + app: + build: . + image: ghcr.io/dlawler489/printhive:latest + depends_on: + db: + condition: service_healthy + environment: + DATABASE_URL: postgres://printhive:${POSTGRES_PASSWORD:-printhive}@db:5432/printhive + PORT: 3000 + # Optional — these can also be set in the app's Settings menu (stored in the DB). + THINGIVERSE_TOKEN: ${THINGIVERSE_TOKEN:-} + FLARESOLVERR_URL: ${FLARESOLVERR_URL:-} + MAKERWORLD_TOKEN: ${MAKERWORLD_TOKEN:-} + PRINTABLES_TOKEN: ${PRINTABLES_TOKEN:-} + volumes: + - uploads:/app/uploads + - thumbnails:/app/thumbnails + - glb:/app/glb + ports: + - "3000:3000" + restart: unless-stopped + # Traefik (if using labels rather than your file provider): + # labels: + # - "traefik.enable=true" + # - "traefik.http.routers.printhive.rule=Host(`printhive.plexultra.com`)" + # - "traefik.http.routers.printhive.entrypoints=websecure" + # - "traefik.http.routers.printhive.tls.certresolver=letsencrypt" + # - "traefik.http.services.printhive.loadbalancer.server.port=3000" + +volumes: + pgdata: + uploads: + thumbnails: + glb: diff --git a/lib/converter.js b/lib/converter.js index 2bdd280..f2939f7 100644 --- a/lib/converter.js +++ b/lib/converter.js @@ -4,7 +4,7 @@ const path = require('path'); const { execFile } = require('child_process'); -const PYTHON = path.join(__dirname, '..', 'tools', 'venv', 'bin', 'python'); +const PYTHON = process.env.PYTHON_BIN || path.join(__dirname, '..', 'tools', 'venv', 'bin', 'python'); const SCRIPT = path.join(__dirname, '..', 'tools', 'convert_3mf.py'); // Returns { glb, volume_cm3, bbox, faces } or throws. diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 0000000..5af82ec --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1,4 @@ +trimesh +numpy +networkx +lxml