feat(docker): add self-hosted all-in-one container setup

- Add Dockerfile (multi-stage build, unprivileged user, healthcheck),
  docker-compose.yml and .dockerignore
- Server now serves static dist/ frontend and WebSocket relay on a
  single port (PORT, default 3001) with path-traversal protection,
  immutable asset caching and SPA fallback
- Client connects via same origin in production; dedicated ws port is
  only used for vite dev/preview
- Document Docker usage, env vars and manual production mode in README
This commit is contained in:
Tronax 2026-08-16 12:15:52 +02:00
parent c4347f8420
commit b506ffad55
Signed by: Tronax
SSH key fingerprint: SHA256:2pKKXDZucWvaF/GzXNz0FY53EAO1YDLN80bqS+TTz/o
6 changed files with 235 additions and 31 deletions

32
Dockerfile Normal file
View file

@ -0,0 +1,32 @@
# ---------- Build stage ----------
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---------- Runtime stage ----------
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
# production dependencies only (ws)
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY server/server.mjs server/server.mjs
COPY --from=build /app/dist dist/
# run as unprivileged user
USER node
EXPOSE 3001
ENV PORT=3001
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node -e "fetch('http://localhost:3001/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["node", "server/server.mjs"]