Repository navigation
feat(ui): The API image serves the UI with production caching and security headers (#359) - #378
Merged
Merged
Conversation
…eaders (#359) Hashed /assets are immutable, every other file is no-cache, and a GET or HEAD to a path without an extension serves index.html. An unknown /api path is a JSON 404 and a known route with the wrong method stays 405, since the fallbacks run as middleware after routing rather than as endpoints. Every response carries the security headers: the full CSP with Trusted Types outside /api, nosniff and frame-ancestors on /api. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…owsers (#359) The API Dockerfile builds the UI in a Node stage on the build platform and copies it to the web root. The final stage creates the INI folder for the app user: without it the non-root image could not write app.ini and did not start. A Playwright smoke test loads the placeholder from the image, started with PostgreSQL on tmpfs by the e2e compose file, in WebKit (iPhone), Chromium (Pixel) and Firefox (phone viewport). It asserts the headers, the assets' caching, no CSP or Trusted Types violation, no request to another origin and no axe violation. The E2E job runs it against the amd64 image built from the PR and is in the CI gate's needs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers (#359) Pins the middleware order in Program.cs, which the in-process tests compose themselves. Also notes that a client route never ends in a dot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Types check covers (#359) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #359. Part of #344.
Summary
The API image builds the UI and serves it on the same origin as
/api/v1. Every response gets security headers, and a newE2Ejob checks the image built from this PR in three browsers.The fallbacks are middleware, not
MapFallbackendpoints. A catch-all endpoint matches every method, so it would turn a405on a known/apiroute into a404.Startup fix: on master the image crashed at start (
Access to the path '/app/configuration' is denied): it runs as$APP_UIDand could not writeapp.ini. The final stage now creates that folder for the app user.Evidence
docker run anichron-apibuilt from master exits withUnauthorizedAccessExceptionon/app/configuration. There was no UI in the image and no headers.After: the image starts.
GET /loginreturns200 text/htmlwithno-cacheand every header, andGET /api/v1/nopereturns404 application/problem+json.UiServingTests(20) andSecurityHeadersTests(11) run the in-process host on a fixture web root. The API unit project passes 716/716.scripts/mutation-changed.shkills 25/25 mutants (100%), and the UI scoped mutation is at 100%.innerHTMLsink and on a third-party<img>. Trusted Types does not break today's bundle, so no directive was dropped. The bundle has no route chunks yet, so the first lazy route will exercise Vite's preload helper.Merge Danger
Door: two-way
Everything here is additive. Reverting it removes the UI from the image and brings back the startup crash.
Blast Radius: API image
Permissions-Policy./people/j.doe) is treated as a file and gets a404.E2Ejoins theCI gate.:edge, which is published withcache-to: type=inline, so only final-stage layers are cached and the UI and .NET build stages rebuild on each run..nvmrc,enginesanddevEngines.runtime.versionbefore it merges.🤖 Generated with Claude Code