Skip to content

feat(router): move stackiq off hash routing to clean path URLs - #899

Merged
rubenvdlinde merged 2 commits into
developmentfrom
feat/stackiq-history-routing
Sep 1, 2026
Merged

feat(router): move stackiq off hash routing to clean path URLs#899
rubenvdlinde merged 2 commits into
developmentfrom
feat/stackiq-history-routing

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Pilot for moving the fleet's seven hash-routed apps to clean path URLs. The source change is two lines; everything else here is what the switch turned out to require.

Verified BEFORE switching

History mode fails at the server when the AppHost SPA catch-all is missing (fleet #133 is why apps fell back to hash). Probed first: /apps/stackiq/organisaties, /contracten and /organisaties/abc-123 already returned 200 with the app shell.

⚠️ Do not decide this by grepping routes.phpRoutes::standard() installs the catch-all without the string appearing. That grep named the wrong app: it flagged zaakafhandelapp (which is fine) and cleared larpinq, whose sub-paths actually 404.

The regression the e2e caught, and the fix

Two index specs failed reproducibly while manual browsing looked fine. Cause:

URL before
/apps/stackiq/komplianties ✅ Compliance
/index.php/apps/stackiq/komplianties silently rendered the Dashboard

Nextcloud serves the app under both forms, but generateUrl() returns only the one the instance is configured for. Arriving on the other left the path outside the router base, vue-router could not resolve it, and the catch-all redirected to / — the deep link silently swallowed, no error. Hash routing never had this, because the route travelled in the fragment.

routerBase() now derives the base from the URL actually being served, falling back to generateUrl(). This would have applied to every app in the rollout, so the pilot earned its keep.

Test surface that moved with it

gotoAppRoute() and three specs built URLs as `${APP_BASE}#${route}`; the smoke spec listed /stackiq/#/organisaties. All now use real paths. The helper docblock about vue-router 4's hash-relative createHref is rewritten rather than deleted — the id-based nav selector it defends is kept deliberately, because identifying the nav by a stable handle rather than an href format the router owns is what survives this change.

Verification

  • manifest-pages.spec.ts: 18/18 pass (was 16/18 before the base fix, 18/18 on development).
  • Playwright smoke project passes both routes, including the organisations sub-route now that it is a real path.
  • Browser: /, /organisaties, /contracten each resolve to the correct data-page-id; reload on a sub-path returns 200 and stays put — the case hash mode existed to avoid. Both URL forms now work. Zero JS errors.
  • eslint exits 0, prettier --check clean, webpack compiles.

Rollout note

Safe to convert next (sub-paths verified serving the SPA): openregister, opencatalogi, zaakafhandelapp, pipelinq, keepiq. larpinq needs its catch-all added first — its sub-paths 404 today.

Stackiq is one of seven fleet apps still serving its SPA behind a `#`.
This is the pilot for moving all of them: the source change is a single
line, and everything else here is the test surface that assumed hashes.

Verified BEFORE switching, because history mode fails at the SERVER when
the AppHost SPA catch-all is missing (fleet #133 is why apps fell back to
hash in the first place): /apps/stackiq/organisaties, /contracten and
/organisaties/abc-123 all already returned 200 with the app shell, so the
catch-all is present for this app.

What moved with it:

  tests/e2e/spec-coverage/_helpers.ts  gotoAppRoute built `${APP_BASE}#${route}`
  tests/e2e/manifest-pages.spec.ts     same URL construction
  tests/e2e/spec-coverage/catalog-ratings.spec.ts  2 hash deep-links
  tests/e2e/smoke/app-mounts.spec.ts   `/stackiq/#/organisaties` -> a real sub-path

The helper docblock explaining vue-router 4's hash-relative `createHref`
is rewritten rather than deleted: the id-based nav selector it defends is
deliberately KEPT, because identifying the nav by a stable handle instead
of an href format the router owns is what makes it survive this change.

Verified in the browser against the published @conduction/nextcloud-vue
(USE_LOCAL_LIB=false):

  /apps/stackiq/               -> 200, page id Dashboard,     no hash
  /apps/stackiq/organisaties   -> 200, page id Organisaties,  no hash
  /apps/stackiq/contracten     -> 200, page id Contracten,    no hash
  RELOAD on /organisaties      -> 200, still Organisaties

That reload is the point: it is the case hash mode existed to avoid, and
it is served by the catch-all rather than 404ing. Zero JS errors.

The Playwright smoke project passes on both routes, including the
organisations sub-route now that it is a real path. eslint exits 0 and
webpack compiles.
History mode broke deep links on the /index.php/... URL form. Nextcloud
serves the same app under BOTH /apps/stackiq/... and
/index.php/apps/stackiq/..., but generateUrl() returns only the form the
instance is configured for. Arriving on the other form left the path
outside the router base, vue-router could not resolve it, and the
catch-all redirected to '/' -- the visitor landed on the Dashboard with
no error and the deep link was silently swallowed.

Measured before the fix:

  /apps/stackiq/komplianties            -> Compliance
  /index.php/apps/stackiq/komplianties  -> Dashboard   <- silently wrong

Hash routing never had this: the route travelled in the fragment, so the
path prefix was irrelevant. This is the one real regression the switch
introduced, and it would have applied to every app in the rollout.

The e2e suite navigates via /index.php/..., which is exactly how it was
caught -- two index specs failed while manual browsing on the pretty URL
looked fine.

Now both forms resolve, with the path preserved.
@rubenvdlinde
rubenvdlinde merged commit 328db56 into development Sep 1, 2026
34 of 35 checks passed
@rubenvdlinde
rubenvdlinde deleted the feat/stackiq-history-routing branch September 1, 2026 06:05
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/stackiq @ 9ae6a4d

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
check-manifest
check-vue-demi
test-l10n
format
check-schema-l10n
check-l10n-js
composer ✅ 130/130
npm ✅ 711/711
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright ⏭️ deferred — runs on the promotion into beta/main, not on a pull request into development
Hydra gates

Quality workflow — 2026-09-01 06:15 UTC

Download the full PDF report from the workflow artifacts.

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.

1 participant