# Full-stack web UI image for ps5upload-engine. # Serves the complete React frontend at http://: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: # docker run --rm -p 19113:19113 \ # -e PS5_ADDR=192.168.1.50:9113 \ # -e PS5UPLOAD_ALLOW_IP=192.168.1.20 \ # ps5upload-engine-webui # # Then open http://192.168.1.: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 ──────────────────────────────────────── FROM node:26-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 ../ 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 # ── Stage 3: minimal scratch image ─────────────────────────────────────────── FROM scratch COPY --from=builder /build/engine/target/release/ps5upload-engine /ps5upload-engine EXPOSE 19113 # Run as an unprivileged user (65532 = nobody in distroless convention). # See engine/Dockerfile for rationale on numeric UID in scratch. USER 65532:65532 # Health: GET /api/jobs returns 200 once the HTTP server is live (no PS5 # contact needed). scratch has no shell/wget, so no in-image HEALTHCHECK; # use an external probe (see engine/Dockerfile). ENTRYPOINT ["/ps5upload-engine"]