Skip to content

Frontend: production image, security headers, SEO metadata and resolution state (#1439-#1442) - #1492

Merged
Ehonrie merged 6 commits into
Netwalls:mainfrom
janipauwels-sys:feat/frontend-f101-f104
Oct 21, 2026
Merged

Ehonrie merged 6 commits into
Netwalls:mainfrom
janipauwels-sys:feat/frontend-f101-f104

Conversation

@janipauwels-sys

Copy link
Copy Markdown
Contributor

Summary

Implements the four frontend issues F-101 to F-104, plus the build repairs they depend on. The frontend did not build on main, so the first commit fixes the blockers before any feature work.

Commits

Commit Issue Change
3ecefc01 prerequisite Repair frontend build blockers: missing zod, autoprefixer and eslint declarations, regenerated lockfile, useWallet.ts → .tsx, duplicated hook body, missing error exports, stale @ts-expect-error, test files excluded from the app type-check
d2732e8e #1439 Multi-stage frontend Dockerfile (standalone output, non-root, healthcheck), .dockerignore, web service in docker-compose.yml
ea32702f #1440 Security headers for every route, including a CSP whose connect-src comes from the validated API, Horizon and RPC config
45068d12 #1441 metadataBase from NEXT_PUBLIC_SITE_URL, robots.ts, sitemap.ts (home plus open markets), manifest.ts, icon.svg
c83f40a9 #1442 Resolution timeline, dispute-window countdown, disputed-state notice, claim button with a reason when a claim is blocked

Closes

Closes #1439
Closes #1440
Closes #1441
Closes #1442

Testing

  • New Jest suites: security headers, sitemap, resolution logic, ResolutionTimeline (each stage), ClaimButton (each claim state). All 20 new tests pass.
  • Full Jest run: the failing suites are a subset of the ones failing on main. This branch fixes three of them and introduces none.
  • Playwright smoke test for CSP violations (e2e/security-headers.spec.ts) is written but not run here: it needs the app build below to pass.

Known gaps

  • next build still fails. The remaining type errors are pre-existing and out of scope: lib/stellar.ts is written against a newer @stellar/stellar-sdk API than the pinned ^12.0.0, and BetForm, BettingInterface, HomeContent, WalletConnectButton and useWallet have mismatched props or types. Until those are fixed, the Docker image cannot be built, so F-101: Add a production Dockerfile and compose service for the frontend #1439 is not verified end-to-end.
  • lib/stellar.test.ts imports ../lib/stellar from the wrong folder, so it cannot run. Not changed here.
  • frontend/.next build output is tracked in git. It was left untouched and kept out of these commits.
  • The sitemap and social card are not checked in a running browser. The sitemap depends on the API returning open markets.
  • package-lock.json was regenerated, so it has a large diff. That was needed because npm ci rejected the old lockfile.

The frontend did not build on main, which blocks the production image
(Netwalls#1439). Fixes:

- Declare zod, autoprefixer and ESLint, which were imported or configured
  but missing from package.json; regenerate package-lock.json so `npm ci`
  matches the manifest again
- Rename hooks/useWallet.ts to .tsx (the file contains JSX)
- Restore the usePayoutEstimate hook body that a bad merge had duplicated
- Add the missing ContractErrorException and parseContractErrorCode exports
- Import TransactionStatus from lib/stellar where it is defined
- Drop a stale @ts-expect-error in the market OG image route
- Exclude test, story and e2e files from the app type-check
Closes the gap where the backend and indexer had images but the frontend
could not be started with `docker compose up`.

- frontend/Dockerfile: multi-stage build on Next's standalone output,
  runs as a non-root user, healthcheck on port 3000. NEXT_PUBLIC_* values
  are build args because Next inlines them into the bundle.
- frontend/.dockerignore: keep node_modules, build output and local env
  files out of the build context
- next.config.js: output: "standalone"
- docker-compose.yml: web service on port 3000, built with the same
  NEXT_PUBLIC_* defaults the API uses locally

Refs Netwalls#1439
next.config.js defined no headers(), so the wallet-connected app could be
framed and had no script or connect restrictions.

- lib/security-headers.js builds the header list: a CSP whose connect-src
  comes from the validated API URL, Horizon for the network and the Soroban
  RPC, plus X-Frame-Options, Referrer-Policy, X-Content-Type-Options and
  Permissions-Policy
- 'unsafe-eval' is allowed in development only
- next.config.js returns those headers for every route
- Unit tests cover the connect-src sources, dev-only eval and invalid URLs
- Playwright smoke test loads the core pages and fails on any CSP violation

Refs Netwalls#1440
Without metadataBase the per-market OpenGraph image could not resolve to
an absolute URL, and the site had no robots.txt, sitemap, icon or manifest.

- NEXT_PUBLIC_SITE_URL is validated in lib/config.ts (default localhost)
  and used as the root metadataBase
- app/sitemap.ts lists the home page and open markets from the API; an
  API outage falls back to the home page alone
- app/robots.ts allows all crawlers and points at the sitemap
- app/manifest.ts and app/icon.svg provide the web app manifest and icon
- Unit tests cover sitemap entries, absolute URLs and id encoding

Refs Netwalls#1441
After resolution users could not tell when winnings became claimable: the
page showed only a status badge, and a winner on a disputed market saw no
claim button or explanation.

- lib/resolution.ts: one place that maps a market to its lifecycle stage
  (open, locked, dispute window, finalized, disputed, cancelled), the
  24h dispute window, and why a claim is blocked. Uses resolvedAt when the
  API sends it and falls back to scheduledAt otherwise, as before.
- ResolutionTimeline: Locked, Resolved, Dispute window (countdown) and
  Finalized steps; a disputed notice; a cancelled note
- ClaimButton: a disabled claim with its reason during the dispute window
  and on disputed markets. Disputed markets now count as resolved for
  winner checks so the explanation appears.
- MarketDetailClient: renders the timeline and passes DisputeModal the
  isOpen and onClose props it requires
- Component and unit tests cover each stage and claim state

Refs Netwalls#1442
@Ehonrie
Ehonrie merged commit 510bb60 into Netwalls:main Oct 21, 2026
0 of 2 checks passed
Main already ships the SEO metadata, site url config, contract error mapping
and env-validated stellar config, so take main's versions of those files and
keep this PR's production image, security headers and resolution state work.
Add autoprefixer to package.json and regenerate package-lock.json from main's.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants