Skip to content

fix(web): serve repo assets statically so they survive serverless (#43)#61

Merged
Jose-Gael-Cruz-Lopez merged 2 commits into
mainfrom
fix/repo-assets-serverless
Jul 7, 2026
Merged

fix(web): serve repo assets statically so they survive serverless (#43)#61
Jose-Gael-Cruz-Lopez merged 2 commits into
mainfrom
fix/repo-assets-serverless

Conversation

@Jose-Gael-Cruz-Lopez

Copy link
Copy Markdown
Collaborator

Closes #43. Stacked on #60 — base is security/ci-pipeline-hardening, not main. Review/merge #60 first (GitHub will retarget this to main automatically once #60 merges).

Problem

/repo-assets/[...path] read files from process.cwd()/../assets at request time. On serverless those files aren't in the function bundle → the stats banner and gallery 404 in production, and the build emitted a whole-project NFT trace warning for the route.

Fix

  • Copy ../assetsweb/public/repo-assets at build/dev via scripts/copy-repo-assets.mjs, wired through predev/prebuild. resolveAssetSrc() already emits /repo-assets/<path>, which now resolves to a bundled static file.
  • Delete the dynamic route — all pages are now ○ Static; the asset-route NFT warning is gone.
  • cpSync(dereference: true) materializes real files → no symlink escape.
  • next.config headers() serves /repo-assets/* with X-Content-Type-Options: nosniff + immutable long cache.
  • public/repo-assets is gitignored (generated).

Acceptance criteria

  • Banner + gallery load in production (now bundled static files, not a runtime read)
  • Asset-route NFT warning gone (route removed)
  • nosniff set; symlink escape blocked; no request-time reads for assets

Note

A separate whole-project NFT trace still comes from parse-readme.ts / listings.ts reading ../README.md / ../listings.json at build (a different import trace than the asset route). That read is restructured in the reliability PR (#46).

🤖 Generated with Claude Code

Closes #43.

The /repo-assets/[...path] route read files from process.cwd()/../assets at
request time. On serverless those files aren't in the function bundle, so the
stats banner and gallery 404 in production, and the build emitted a
whole-project NFT trace warning for the route.

- Copy ../assets -> web/public/repo-assets at build/dev time via
  scripts/copy-repo-assets.mjs, wired through the predev/prebuild npm hooks.
  resolveAssetSrc already emits /repo-assets/<path>, which now resolves to a
  bundled static file.
- Delete the dynamic route entirely (all pages are now ○ Static; the
  asset-route NFT warning is gone).
- cpSync(dereference: true) materializes real files, so no symlink can point
  outside the bundle.
- next.config headers() serves /repo-assets/* with X-Content-Type-Options:
  nosniff and an immutable long cache.
- public/repo-assets is gitignored (generated from ../assets).

Note: a separate whole-project NFT trace still comes from parse-readme.ts /
listings.ts reading ../README.md and ../listings.json at build; that read is
restructured in the reliability PR (#46).

Verified: next build compiles; prebuild copies assets into public/repo-assets;
route table no longer lists /repo-assets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…raw URL

Addresses review findings on the static repo-asset serving:

- Cache-Control: the /repo-assets paths are stable but their content is
  not (the stats banner and gallery photos are regenerated and committed
  by CI at the same filename). The previous 1-year `immutable` cache froze
  the live banner for returning visitors and next/image. Switch to
  `max-age=0, s-maxage=300, stale-while-revalidate=86400` so the CDN keeps
  it fresh while still caching.
- Build: fold the asset copy into the `build` script (was a `prebuild`
  hook) so it runs under any package manager / build invocation that would
  otherwise skip pre* lifecycle hooks, keeping resolveAssetSrc's
  /repo-assets output consistent with what's actually on disk.
- Raw fallback: point REPO_RAW_BASE and images.remotePatterns at the
  canonical Hack-HQ/hackhq repo instead of a personal fork that can go
  stale.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Jose-Gael-Cruz-Lopez Jose-Gael-Cruz-Lopez deleted the branch main July 7, 2026 15:25
@Jose-Gael-Cruz-Lopez Jose-Gael-Cruz-Lopez changed the base branch from security/ci-pipeline-hardening to main July 7, 2026 15:28
@Jose-Gael-Cruz-Lopez Jose-Gael-Cruz-Lopez merged commit 4c0bebb into main Jul 7, 2026
Jose-Gael-Cruz-Lopez added a commit that referenced this pull request Jul 7, 2026
Resolve conflicts from the lower-stack fixes now on main:
- next.config.ts / parse-readme.ts: keep this branch's repo-identity
  consolidation (REPO_SLUG + REPO_RAW_BASE imported from lib/repo.ts) over
  the hardcoded raw URL from the #61 fix.
- lib/repo.ts: set the REPO_SLUG default to the canonical Hack-HQ/hackhq
  (was Jose-Gael-Cruz-Lopez/hackhq) so the #61 owner fix is preserved through
  the consolidation instead of silently reverted.
- The #62 ISR tracing config, #61 cache header, and #62 guarded-read
  re-throws auto-merged cleanly; lockfile auto-merged (identical).

Verified: npm ci, lint, test (15/15), build, tsc, and scripts tests all pass.
@Jose-Gael-Cruz-Lopez Jose-Gael-Cruz-Lopez deleted the fix/repo-assets-serverless branch July 7, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[High] Reliability: /repo-assets route reads files outside the project root at request time (breaks on serverless)

1 participant