Skip to content

fix(portal): asset pages and share links no longer answer 404 (#2205) - #2207

Merged
cjimti merged 1 commit into
mainfrom
fix/2205-portal-asset-routes
Oct 19, 2026
Merged

cjimti merged 1 commit into
mainfrom
fix/2205-portal-asset-routes

Conversation

@cjimti

@cjimti cjimti commented Oct 19, 2026

Copy link
Copy Markdown
Member

Changes internal/ui/embed.go, adds internal/ui/routes_test.go and test/acceptance/issue_2205_test.go, and changes scripts/release-acceptance.sh. It fixes one bug and adds the gate that would have caught it.

1. #2205: /portal/assets/{id} answers 404 since v1.147.0

Since v1.147.0 every request for /portal/assets/{id} gets Go's plain-text 404 page not found instead of the portal. That breaks:

  • a share link (/portal/view/{token}) for a signed-in reader, which redirects there (pkg/portal/public.go:183)
  • notification links (internal/platform/notifylayer/publish.go:138)
  • the portal_url that save_asset and the trino, api and graphql exports return
  • a reload or bookmark of an open asset

Navigating inside the portal kept working because it never asks the server for the page.

Cause. #2146 (for #2139) made the SPA handler answer 404 for any path under assets/ that the build doesn't have. That way a page from another build can't get index.html under a stylesheet's or a chunk's URL. The portal is served under /portal/, so Vite's build files are at /portal/assets/*. The asset viewer route is /assets/:id, so it is under the same prefix, and every asset URL matched the rule.

Fix. A path under assets/ counts as a build file only when it has an extension (isBuildFile). Every file Vite writes there has one. Asset ids are hex (pkg/toolkits/portal/toolkit.go generateID) and never do. A missing .js or .css under /portal/assets/ still answers 404, so the #2139 guarantee holds.

2. #2206: the gate

Three suites could have caught this, and none did:

  • The internal/ui unit tests asked only for /, /dashboard and build files.
  • The interactive Playwright suite runs against the Vite dev server, which serves client routes itself and never reaches internal/ui.
  • ui/e2e/public-viewer/parquet-viewer.spec.ts ("renders a portal asset by range") loads /portal/assets/{id} from the Go binary and fails on v1.147.0. Only make frontend-e2e-public-viewer runs it, and neither make verify, make verify-release nor CI called that target.

This PR adds:

  • TestSPAHandler_EveryPortalRouteServesIndexHTML. It reads every page in the screenshot route manifest (ui/e2e/screenshots/route-*.ts, concrete paths with ids filled in), sends each through the SPA handler and requires index.html. It fails when the manifest stops listing the asset viewer, so it can't silently stop covering this case. Restoring the v1.147.0 rule makes it fail, along with TestSPAHandler_AssetViewerRoute_ServesIndexHTML.
  • TestBuildAssetsDir_MatchesViteConfig. It fails when ui/vite.config.ts sets a build.assetsDir that differs from the Go constant.
  • make verify-release runs the live-stack suite. scripts/release-acceptance.sh runs make frontend-e2e-public-viewer after the acceptance tests, against the same stack. When the script starts that stack itself, it first runs make frontend-build, because verify's embed-clean leaves the embed directories empty and the binary would otherwise serve no portal.

Acceptance

Run against the local dev stack (make dev) built from this branch with the portal embedded. The transcript is in build/2205/acceptance.md.

Wire forms: save_asset with its content as a string, its one form; the share create as one JSON object of strings; plain HTTP GETs of the share link with the dev API key as Bearer, and of the asset page with no credential.

  • TestIssue2205_AShareLinkOpensTheAssetInThePortal: a signed-in GET of /portal/view/{token} answers 302 to /portal/assets/{id}?share={token}, and that page answers 200 text/html with the portal's index.html.
  • TestIssue2205_AnAssetsPortalURLOpensThePortal: the path of save_asset's portal_url and /portal/assets/{id}/provenance each answer the portal's index.html.
  • make acceptance ISSUE=2139 against the same stack: both criteria pass. A missing build file still answers 404, and a present one is served as itself.
  • make frontend-e2e-public-viewer against the same stack: 27 passed.

Verification

  • make verify passed (log in build/2205/verify.txt).
  • Patch coverage: 3 of 3 changed executable lines, 100%.
  • make preverify-fast, the patch-scoped make lint, make semgrep-diff and shellcheck on the script are clean.

Readers of this state

No JSON field and no route changed. index.html is now served for paths under /portal/assets/ that have no extension, which is what v1.146.2 and earlier served there.

Closes #2205
Closes #2206

Changes internal/ui/embed.go, adds internal/ui/routes_test.go and test/acceptance/issue_2205_test.go, and changes scripts/release-acceptance.sh; one bug and the gate that would have caught it:

1. #2205: since v1.147.0 every /portal/assets/{id} answered Go's plain 404. That covers a share link after its redirect for a signed-in reader, notification links, save_asset's portal_url and a reload of an open asset. The asset viewer route shares the prefix of Vite's build files, and the #2139 rule answered 404 for any path under assets/ the build does not have. A path there is now a build file only when it has an extension: every file Vite writes has one and an asset id never does. A missing .js or .css under /portal/assets/ still answers 404.
2. #2206: TestSPAHandler_EveryPortalRouteServesIndexHTML serves every page in the screenshot route manifest (ui/e2e/screenshots/route-*.ts, ids filled in) through the SPA handler and requires index.html; it fails when the manifest stops listing the asset viewer. TestBuildAssetsDir_MatchesViteConfig holds the Go prefix to Vite's build.assetsDir. make verify-release now runs make frontend-e2e-public-viewer against the stack its acceptance step uses, after building the portal the binary embeds (verify's embed-clean leaves it empty). That suite loads /portal/assets/{id} from the binary and failed on v1.147.0, but no target ran it.

Also: docs/server/content-viewers.md, llms.txt, llms-full.txt and the Makefile help line say verify-release runs the live-stack suite.

Closes #2205
Closes #2206
@cjimti
cjimti merged commit 0a1601f into main Oct 19, 2026
10 checks passed
@codecov

codecov Bot commented Oct 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.82%. Comparing base (d0ed9a1) to head (6fa3483).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2207   +/-   ##
=======================================
  Coverage   94.82%   94.82%           
=======================================
  Files        1361     1361           
  Lines      110409   110410    +1     
=======================================
+ Hits       104696   104698    +2     
+ Misses       5695     5694    -1     
  Partials       18       18           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cjimti
cjimti deleted the fix/2205-portal-asset-routes branch October 19, 2026 17:37
@cjimti cjimti changed the title fix(portal): serve /portal/assets/{id} as the portal again (#2205) fix(portal): asset pages and share links answer 404 since v1.147.0 (#2205) Oct 19, 2026
@cjimti cjimti changed the title fix(portal): asset pages and share links answer 404 since v1.147.0 (#2205) fix(portal): asset pages and share links no longer answer 404 (#2205) Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant