fix(materials): stop losing uploaded files on redeploy (502 on file view)#16
Merged
Conversation
UPLOAD_DIR was never set for the production container, so storage.ts
fell back to `$cwd/data/uploads` — /app/data/uploads inside the
runner image — instead of the /data/uploads volume that
docker-compose.yml actually mounts. Every restart/redeploy wiped
that directory, leaving material rows pointing at files that no
longer exist on disk. Reading one then errored mid-stream, which the
reverse proxy in front of the app turns into a 502 for the client
("Unexpected server response (502)" in the PDF viewer).
Set UPLOAD_DIR=/data/uploads in the Dockerfile to match the mkdir'd,
chown'd, volume-mounted path. Also make the file route check the
file exists before streaming, returning a clean 404 instead of an
aborted connection if it's ever missing again.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
UPLOAD_DIR, sosrc/lib/storage.tsfell back to its default,path.join(process.cwd(), "data", "uploads"). Inside the runner image (WORKDIR /app), that resolves to/app/data/uploads— not the/data/uploadspath thatdocker-compose.ymlactually mounts as a volume and that theDockerfilemkdirs/chowns. Uploaded files were silently written outside the persisted volume./app/data/uploads, so any material row created before that point pointed at a file that no longer existed on disk. Opening it triggered an error mid-stream (the response had already started with a 200 +Content-Length), which the reverse proxy in front of the app surfaces to the client as a 502 — matching the reported error:Unexpected server response (502) while retrieving PDF "https://study-helper.hesa.dev/api/materials/.../file".ENV UPLOAD_DIR=/data/uploadsin theDockerfileso it matches the volume mount regardless of orchestrator (Compose, k8s, plaindocker run, …).src/app/api/materials/[id]/file/route.tsnow checks the file actually exists on disk before starting to stream it, returning a clean404instead of an aborted connection if it's ever missing again (misconfiguration, manual deletion, etc.) — rather than a confusing 502-style failure.Note: files that were already lost from prior redeploys before this fix can't be recovered by this change — going forward, uploads will persist correctly across restarts.
Test plan
npx tsc --noEmitnpx eslinton changed filesnpx next buildGenerated by Claude Code