Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
121 lines (107 loc) · 6.2 KB
/
Copy pathDockerfile
File metadata and controls
121 lines (107 loc) · 6.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# 3D Gen Studio — shared asset/project server.
#
# This image runs server.js with GENSTUDIO_MODE=server: the data routes, auth
# and the asset store only. It has no GPU, no ComfyUI, no Python sidecars and no
# third-party API keys — every user keeps those on their own machine. See
# serverMode.js for the exact route split.
# ---------------------------------------------------------------------------
# Builder: full toolchain. Produces the Vite bundle and a production-only
# node_modules with sqlite3 compiled for this base image, so the runtime image
# needs no compiler of its own.
# ---------------------------------------------------------------------------
FROM node:24-bookworm AS builder
WORKDIR /app
# sqlite3 is the only native dependency, and its published prebuilt binary is
# linked against glibc >= 2.38 while the bookworm runtime ships 2.36 — loading
# it there fails with "GLIBC_2.38 not found". Compiling from source binds it to
# the same Debian release the runtime stage uses, so the two cannot drift.
ENV npm_config_build_from_source=true
RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ && rm -rf /var/lib/apt/lists/*
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# Reduce to production dependencies. dist/ is already built, so dropping the dev
# tree here keeps it out of the runtime image entirely.
RUN npm ci --omit=dev && npm cache clean --force
# ---------------------------------------------------------------------------
# Runtime
# ---------------------------------------------------------------------------
FROM node:24-bookworm-slim AS runtime
# WORKDIR is load-bearing: storage.js derives DATA_DIR from process.cwd(), so
# the data volume must be mounted at exactly <workdir>/data.
WORKDIR /app
# Baked defaults, so a NAS/Container Manager import of the saved .tar comes up
# configured instead of needing every variable typed into the UI by hand. Each
# is a plain ENV, which is what makes it pre-fill in that dialog; anything the
# host sets (compose `environment:`, Container Manager, `docker run -e`) still
# wins over these at runtime.
#
# Override at build time with e.g.
# docker compose build --build-arg GENSTUDIO_JWT_SECRET=<64 hex chars>
#
# NOTE: these values live in the image layers, so the saved .tar carries the
# signing secret and the first-run admin password to anyone who can read it.
# Fine for a private NAS; change them before the image travels further.
ARG GENSTUDIO_JWT_SECRET=123456Secret123456Secret123456
ARG GENSTUDIO_ADMIN_LOGIN=admin
ARG GENSTUDIO_ADMIN_PASSWORD=admin123
ARG TRUST_PROXY_HEADERS=0
ENV NODE_ENV=production \
GENSTUDIO_MODE=server \
PORT=3001 \
GENSTUDIO_JWT_SECRET=${GENSTUDIO_JWT_SECRET} \
GENSTUDIO_ADMIN_LOGIN=${GENSTUDIO_ADMIN_LOGIN} \
GENSTUDIO_ADMIN_PASSWORD=${GENSTUDIO_ADMIN_PASSWORD} \
TRUST_PROXY_HEADERS=${TRUST_PROXY_HEADERS}
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --chown=node:node package.json version.json ./
COPY --chown=node:node server.js storage.js wikiStorage.js auth.js serverMode.js gateway.js dataStore.js uploadQueue.js logs.js meshPivot.js meshNormals.js meshRigTransfer.js skinTransfer.js vfxPreview.js pgEmbedded.js ./
# The SQL engine. db/index.js chooses a driver at startup -- PostgreSQL here,
# SQLite on a desktop install -- and loads it by dynamic import, so the whole
# directory ships rather than a name-by-name list that would look complete.
# db/schema.pg.sql is read at runtime and travels with it.
COPY --chown=node:node db ./db
# The SQLite to PostgreSQL migration, run once per upgraded deployment (see
# docker-compose.yml for the exact command). It needs the sqlite3 module, which
# is why that dependency stays in the image even though the server itself never
# loads it when running on PostgreSQL.
COPY --chown=node:node tools/migrate-sqlite-to-postgres.mjs ./tools/
COPY --chown=node:node mcp ./mcp
COPY --chown=node:node vfx ./vfx
# The building generator contract (schema, catalog, compiler, BuildingIR).
COPY --chown=node:node building ./building
# The Batch document model, shared by the Batch page and the MCP batch tools.
COPY --chown=node:node batch ./batch
# The particle simulation, and the one place src/ is copied. vfxPreview.js needs
# it to render an effect server-side with no GPU; the subtree imports nothing
# but vfx/ and has always run in plain Node. See the same note in
# electron-builder.yml - keep it pure or the backend dies at startup.
COPY --chown=node:node src/utils/vfx ./src/utils/vfx
# The UV atlas packer, the second src/ exception on the same terms: it imports
# nothing, and the batch Flatten stage (batch/flatten.js) packs with it here.
COPY --chown=node:node src/utils/assemblyAtlas.js ./src/utils/
COPY --chown=node:node wiki ./wiki
# The VFX starter-effect library, served read-only from /api/vfx/presets.
COPY --chown=node:node resources/vfx ./resources/vfx
# The building style packs, served read-only from /api/buildings/styles.
COPY --chown=node:node resources/buildings ./resources/buildings
# The Unity importer, offered alongside an exported VFX bundle. Named
# precisely: plugins/unity also holds the Phase 0 spikes and the packaging
# tool, which are development artifacts with no place in a server image.
COPY --chown=node:node plugins/unity/com.3dgenstudio.vfx-import ./plugins/unity/com.3dgenstudio.vfx-import
COPY --chown=node:node plugins/unity/3dgenstudio-vfx-import.unitypackage ./plugins/unity/
# Run unprivileged. Ownership is set by --chown on each COPY above rather than a
# trailing `chown -R /app`: that recursive form rewrites every one of the ~40k
# node_modules files into an extra image layer, which roughly doubles the image
# and adds minutes to the build. Only data/ needs creating here, so the volume
# inherits the right owner on first run.
RUN mkdir -p /app/data && chown node:node /app /app/data
USER node
EXPOSE 3001
VOLUME ["/app/data"]
# node:24 has a global fetch, so no curl is needed in the slim image.
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||3001)+'/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["node", "server.js"]