diff --git a/Dockerfile b/Dockerfile index 042103c7..6cf497bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,21 @@ # ---- Stage 1: build static assets ---- +# The frontend was fully rewritten from scratch under web/ (Vite + React + TS) on branch +# redesign/desktop-multicolumn, merged into dev 2026-09-09 -- this replaces the old webpack build +# that used to live at the repo root (umd/, css/base.min.css, index.html, etc.). See web/README- +# equivalent context in the redesign plan for why: this Dockerfile previously never touched web/ at +# all, so every "deploy" kept shipping the old app regardless of what was on dev. FROM node:22-alpine AS build -WORKDIR /app -COPY package.json package-lock.json ./ +WORKDIR /app/web +COPY web/package.json web/package-lock.json ./ RUN npm ci -COPY . . +COPY web/. . -# TINODE_API_KEY is read by webpack.config.js (via DefinePlugin) at build time. +# VITE_TINODE_API_KEY is read by src/lib/config.ts (import.meta.env.VITE_TINODE_API_KEY) at build +# time -- Vite only exposes env vars prefixed VITE_ to client code, so the ARG is renamed on its +# way into the build's environment rather than passed through under its old webpack-era name. ARG TINODE_API_KEY= -ENV TINODE_API_KEY=$TINODE_API_KEY +ENV VITE_TINODE_API_KEY=$TINODE_API_KEY RUN npm run build @@ -19,17 +26,11 @@ COPY docker/nginx.conf /etc/nginx/conf.d/default.conf WORKDIR /usr/share/nginx/html -COPY --from=build /app/umd ./umd -COPY --from=build /app/css/base.min.css ./css/base.min.css -COPY --from=build /app/index.html ./index.html -COPY --from=build /app/index-dev.html ./index-dev.html -COPY --from=build /app/manifest.json ./manifest.json -COPY --from=build /app/service-worker.js ./service-worker.js -COPY --from=build /app/version.js ./version.js -COPY --from=build /app/img ./img -COPY --from=build /app/audio ./audio -# Push notifications are not configured for this deployment yet. -RUN echo "" > ./firebase-init.js +# Vite's build output is fully self-contained: index.html + favicon.svg/icons.svg (from web/public/, +# copied verbatim) + assets/ (content-hashed JS/CSS chunks) -- no separate umd/css/img/audio/ +# manifest/service-worker/firebase files the way the old build had, none of that machinery exists +# in the new app. +COPY --from=build /app/web/dist/. ./ HEALTHCHECK --interval=1m --timeout=3s --start-period=10s \ CMD wget -q -O /dev/null http://localhost/ || exit 1 diff --git a/docker/nginx.conf b/docker/nginx.conf index bbc6844c..05b94cd3 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -5,21 +5,20 @@ server { root /usr/share/nginx/html; index index.html; - # Tinode webapp uses real (History API) client-side routing, so any unknown path - # can safely fall back to index.html -- the app resolves the actual view/topic - # client-side from the path itself once index.html loads. + # The webapp uses real (History API) client-side routing, so any unknown path can safely fall + # back to index.html -- the app resolves the actual view/topic client-side from the path itself + # once index.html loads. index.html itself is never content-hashed, so it must always be + # revalidated -- otherwise a browser holding a cached copy would keep requesting the *previous* + # deploy's now-deleted /assets/*.js filenames after a new deploy replaces them. location / { + add_header Cache-Control "no-cache"; try_files $uri $uri/ /index.html; } - # Filenames here aren't content-hashed, so a long/immutable cache would keep - # serving a stale bundle after every deploy. Force revalidation instead: - # cheap 304s when unchanged, immediate pickup of new content when not. - location /umd/ { - add_header Cache-Control "no-cache"; - } - - location /img/ { - add_header Cache-Control "public, max-age=86400"; + # Vite content-hashes every filename under assets/ (e.g. index-DYjopaLI.js) -- a new build never + # reuses an old asset's URL, so these can be cached aggressively and indefinitely; a stale cached + # copy simply never gets requested again once index.html points at the new hashed filenames. + location /assets/ { + add_header Cache-Control "public, max-age=31536000, immutable"; } }