mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 09:15:19 +03:00
A single secret that turned any request into an operator was the only credential several routes accepted, and it had no caller left: the device pairing it existed for is on hold, and nothing in this tree or any client sent it. What remained was a key that bypassed authentication entirely, required to boot, and checked by nobody. Every route behind it had a better answer available: - Library and game sync move to host credentials. Both took a `userId` in the body, which meant one secret could write into anybody's library. A host now says which of its enrolled users a batch is for, and that claim is checked against the Steam sign-ins it actually holds — one box carries several people's accounts, so the pair is the unit. - Download-state reporting narrows to hosts alone, and the body that could name a different host is gone. Which host is reporting comes from its own credentials, and a body that still names one is refused rather than ignored. - Linking a Steam account is always for the caller. - Creating a game by hand is deleted; syncing already upserts the catalogue. - Reading the waitlist is deleted. Every address on it belongs to someone who has not agreed to anything, and answering it over HTTP made that list something a leaked key could drain. - The pairing-code routes are deleted with the flow they served. The domain module and its table stay, so returning to it is a route file rather than a migration. Nothing in the API now accepts a credential that stands for more than one caller: every request resolves to a specific user or a specific host, which is what lets a route say "the caller's own library" and mean it. BREAKING CHANGE: the `x-nestri-admin-token` header is no longer accepted and `ADMIN_SHARED_SECRET` is no longer read. `POST /games`, `GET /waitlist` and the `/pairing-code` routes are gone; `POST /games/sync` and `POST /library/sync` now require host credentials and take `userId` in the body; `POST /steam/link` no longer accepts `userId`; `POST /games/download-state` no longer accepts `hostId`.
67 lines
2.5 KiB
Docker
67 lines
2.5 KiB
Docker
# The API as a container.
|
|
#
|
|
# Build from the repository root — the workspace lockfile and two shared
|
|
# packages live there, so a context rooted at this directory could not resolve
|
|
# them:
|
|
#
|
|
# docker build -f apps/api/Dockerfile -t nestri-api .
|
|
#
|
|
# The repository-wide `.dockerignore` is what this build excludes. It used to
|
|
# exclude the whole TypeScript half, because the guest rootfs build was the
|
|
# only Dockerfile here — that part now lives in `build/Dockerfile.dockerignore`,
|
|
# beside the build it belongs to.
|
|
# The registry host is part of the name on purpose: podman refuses a
|
|
# short name that resolves to nothing, and a self-hoster is as likely to
|
|
# have podman as docker.
|
|
FROM docker.io/oven/bun:1.3.11-alpine AS deps
|
|
|
|
WORKDIR /app
|
|
|
|
# Manifests first, source second. Dependencies change far less often than code
|
|
# does, so this layer survives most rebuilds. Every workspace member's manifest
|
|
# has to be here even if this image does not import it: the lockfile describes
|
|
# the whole workspace, and resolving it against a partial one is not frozen.
|
|
COPY package.json bun.lock ./
|
|
COPY apps/api/package.json apps/api/
|
|
COPY apps/auth/package.json apps/auth/
|
|
COPY packages/core/package.json packages/core/
|
|
COPY packages/auth/package.json packages/auth/
|
|
|
|
# No dev dependencies. Bun runs TypeScript without a build step, so nothing in
|
|
# them is reachable at runtime — they are the type definitions, the linter and
|
|
# the deployment CLI.
|
|
RUN bun install --frozen-lockfile --production
|
|
|
|
|
|
FROM docker.io/oven/bun:1.3.11-alpine AS runtime
|
|
|
|
WORKDIR /app
|
|
|
|
COPY --from=deps /app/node_modules node_modules
|
|
COPY tsconfig.json ./
|
|
COPY package.json bun.lock ./
|
|
COPY apps/api apps/api
|
|
COPY packages/core packages/core
|
|
COPY packages/auth packages/auth
|
|
|
|
# The image ships no configuration. Every setting arrives from the environment,
|
|
# which is what makes one image good for a self-hoster and for us:
|
|
#
|
|
# DATABASE_URL postgres://… required
|
|
# AUTH_ISSUER_URL the issuer's public URL required
|
|
# STEAM_API_KEY for linking an account
|
|
ENV NODE_ENV=production
|
|
ENV PORT=3000
|
|
EXPOSE 3000
|
|
|
|
# `bun` is a non-root user the base image already provides.
|
|
USER bun
|
|
|
|
# `/` answers without touching the database, which is the right shape for a
|
|
# liveness probe: it says this process is serving, and leaves "can it reach
|
|
# Postgres" to a readiness check that is allowed to fail loudly.
|
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
CMD wget -q -O /dev/null http://127.0.0.1:${PORT}/ || exit 1
|
|
|
|
CMD ["bun", "run", "apps/api/app/server.ts"]
|