mirror of
https://github.com/phantomptr/ps5upload.git
synced 2026-10-22 20:03:07 -05:00
CI and release builds use the newest Node 22.x (check-latest; the runner image can lag behind 22.23.4), and the web UI image's frontend stage is pinned to node:26.12.1-alpine. Node only runs at build time; no shipped artifact contains it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JoFXdUvVCDAG8xRjtp52qt
113 lines
5.7 KiB
Docker
113 lines
5.7 KiB
Docker
# Full-stack web UI image for ps5upload-engine.
|
|
# Serves the complete React frontend at http://<host>:19113 so any browser
|
|
# on an allowlisted IP can manage a PS5 — no native client required.
|
|
#
|
|
# Build (from the repo root, NOT engine/):
|
|
# docker build -f engine/Dockerfile.webui -t ps5upload-engine-webui .
|
|
#
|
|
# Run (Linux server; engine/compose.yaml does the same):
|
|
# docker run -d --network host -v ps5upload-data:/data -v /srv/pkgs:/pkgs:ro \
|
|
# -e PS5_ADDR=192.168.1.50 \
|
|
# -e PS5UPLOAD_ALLOW_IP=192.168.1.0/24 \
|
|
# ps5upload-engine-webui
|
|
#
|
|
# Then open http://192.168.1.<host>:19113 in a browser on the machine whose
|
|
# IP you set in PS5UPLOAD_ALLOW_IP.
|
|
#
|
|
# DOCKER DESKTOP (macOS/Windows): the engine's loopback guard rejects
|
|
# connections from Docker's NAT bridge. You MUST set PS5UPLOAD_ALLOW_IP to
|
|
# the bridge gateway (usually 172.17.0.1) for port-forwarded connections to
|
|
# work. On Linux, prefer --network host instead. See engine/Dockerfile for
|
|
# full networking details. Note: on macOS/Windows the Docker VM is NAT'd
|
|
# and may not be able to reach the PS5 on your LAN — this is a Docker
|
|
# Desktop limitation, not an engine bug.
|
|
#
|
|
# The image also carries the two PS5 helper ELFs (ps5upload.elf and the DPI
|
|
# install daemon). The browser cannot open a socket to the console's loader
|
|
# or hold those bytes itself, so without them the web UI can install base
|
|
# games but not updates or DLC — the web UI half of #152. Override with
|
|
# PS5UPLOAD_PAYLOAD_DIR if you need to pin different ones.
|
|
#
|
|
# SECURITY: the web UI is UNAUTHENTICATED. Anyone whose IP is in
|
|
# PS5UPLOAD_ALLOW_IP gets full control over your PS5. Only add IPs from
|
|
# your own trusted LAN. Never expose port 19113 to the internet.
|
|
|
|
# ── Stage 1: build the React frontend ────────────────────────────────────────
|
|
# Pinned to a release with the October 2026 Node security fixes (Brotli, TLS hostname checks).
|
|
FROM node:26.12.1-alpine AS frontend
|
|
|
|
# The client at its repo-relative place: the FAQ and Changelog screens bundle
|
|
# ../FAQ.md and ../CHANGELOG.md from the repo root.
|
|
WORKDIR /repo/client
|
|
# Copy only the client package files first so npm ci is cached
|
|
COPY client/package.json client/package-lock.json ./
|
|
RUN npm ci --silent
|
|
|
|
# Now copy the rest of the client source and build
|
|
COPY client/ .
|
|
COPY FAQ.md CHANGELOG.md ../
|
|
ARG PS5UPLOAD_BASE_URL=/
|
|
ENV VITE_BASE_URL=$PS5UPLOAD_BASE_URL
|
|
RUN npm run build:vite
|
|
|
|
# ── Stage 2: build the Rust engine with embedded web UI ──────────────────────
|
|
FROM rust:alpine AS builder
|
|
|
|
# g++ compiles unrar's bundled C++ against musl; musl target links static.
|
|
RUN apk add --no-cache musl-dev g++ make
|
|
|
|
# Keep engine/ and third_party/ at their real repo-relative positions
|
|
# rather than flattening engine/ into /build: the workspace patches
|
|
# `unrar` to ../third_party/unrar (see third_party/unrar/README.md), and
|
|
# flattening puts that path outside the image entirely.
|
|
WORKDIR /build
|
|
COPY engine/ ./engine/
|
|
COPY third_party/ ./third_party/
|
|
# See engine/Dockerfile: build.rs embeds these, and the web UI's update /
|
|
# DLC install is exactly what breaks without them (the web UI half of #152).
|
|
COPY payload/ ./payload/
|
|
WORKDIR /build/engine
|
|
|
|
# Populate the webui/ embed directory from the frontend build output.
|
|
# rust-embed will bake every file in this directory into the binary.
|
|
COPY --from=frontend /repo/client/dist/ ./crates/ps5upload-engine/webui/
|
|
|
|
RUN cargo build --release -p ps5upload-engine --features webui
|
|
|
|
# The runtime image is `scratch`, which has no /tmp and no home: the engine
|
|
# could not stage an uploaded package ("could not create upload dir:
|
|
# Permission denied"), cache artwork, or remember saved servers and install
|
|
# history. Make both here (scratch has no mkdir) and copy them across below.
|
|
# /data is world-writable too (sticky, like /tmp) so a `user:` override in
|
|
# Compose — common on NAS boxes, e.g. `user: "1000:1000"` (#346) — can still
|
|
# write its state there; its owner is the default runtime user.
|
|
RUN mkdir -p /rootfs/tmp /rootfs-data/data && chmod 1777 /rootfs/tmp /rootfs-data/data
|
|
|
|
# ── Stage 3: minimal scratch image ───────────────────────────────────────────
|
|
FROM scratch
|
|
ARG PS5UPLOAD_BASE_URL=/
|
|
COPY --from=builder /build/engine/target/release/ps5upload-engine /ps5upload-engine
|
|
# /tmp: staging for uploaded packages, archive extraction and the like. It
|
|
# is copied as a child of /rootfs because copying a directory applies only
|
|
# its contents: `COPY /rootfs/tmp /tmp` left a 0755 root-owned /tmp and the
|
|
# upload still failed. /data: the engine's home (HOME=/data puts its state in
|
|
# /data/.ps5upload), owned by the runtime user so it is writable.
|
|
COPY --from=builder /rootfs/ /
|
|
COPY --from=builder --chown=65532:65532 /rootfs-data/ /
|
|
ENV HOME=/data TMPDIR=/tmp PS5UPLOAD_BASE_URL=$PS5UPLOAD_BASE_URL
|
|
# Saved servers (with their encrypted credentials), install history, the
|
|
# artwork cache and the backport library live here. Mount a volume on it so
|
|
# they survive a container upgrade: `-v ps5upload-data:/data`.
|
|
VOLUME ["/data"]
|
|
EXPOSE 19113
|
|
# Run as an unprivileged user (65532 = nobody in distroless convention).
|
|
# scratch has no /etc/passwd, so we use a raw numeric UID — the kernel
|
|
# doesn't need a passwd entry to run a process. Port 19113 > 1024 so
|
|
# no CAP_NET_BIND_SERVICE needed.
|
|
USER 65532:65532
|
|
# scratch has no shell or HTTP client, so the engine probes itself:
|
|
# `--healthcheck` exits 0 when GET /api/jobs answers 200 (no PS5 needed).
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD ["/ps5upload-engine", "--healthcheck"]
|
|
ENTRYPOINT ["/ps5upload-engine"]
|