feat(router): move keepiq off hash routing to clean path URLs - #582
Merged
Conversation
Fourth app off `#` routing, after stackiq (softwarecatalog#899), zaakafhandelapp (#609) and opencatalogi (#1341). Two parts: 1. createWebHashHistory -> createWebHistory. 2. routerBase(), derived from the URL actually being served. Nextcloud serves the app under BOTH /apps/keepiq/... and /index.php/apps/keepiq/..., but generateUrl() returns only the form the instance is configured for. Arriving on the other leaves the path outside the router base, vue-router cannot resolve it, and the catch-all redirects to '/' -- no error, deep link silently swallowed. This app has the sharpest version of that problem: keepiq's share and send links (/share/link/:token, /send/:token, /share/request/:token) are handed to people OUTSIDE the app, in messages and emails, in whichever URL shape the sender's client produced. A swallowed deep link there is a share that silently does nothing. 6 spec files built URLs with a hash; all now use real paths. Verified against the published @conduction/nextcloud-vue (USE_LOCAL_LIB=false). The vault is locked in this environment, so every route redirects to the lock screen -- which is itself the proof that the router parsed the request first: /apps/keepiq/secrets -> /apps/keepiq/lock?returnUrl=/secrets /index.php/apps/keepiq/secrets -> /index.php/apps/keepiq/lock?returnUrl=/secrets RELOAD on the /index.php form -> 200, still on the lock path No hash anywhere, both URL forms work, and the requested route survives into returnUrl. (/folders answers returnUrl=/ because the real route is /folders/:folderId, so the catch-all correctly claims the bare path.) eslint exits 0 and prettier --check on the FULL glob is clean.
Contributor
Quality Report — ConductionNL/keepiq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| test-l10n | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 111/111 | |||
| npm | ✅ | ✅ 536/536 | |||
| 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 08:16 UTC
Download the full PDF report from the workflow artifacts.
This was referenced Sep 1, 2026
rubenvdlinde
added a commit
that referenced
this pull request
Sep 1, 2026
Follow-up to #582, which turned E2E red on development. Three selectors and one URL assertion still described hash routing: a[href$="#/"] the Dashboard nav entry a[href$="#/lock"] the Lock vault entry (twice) toHaveURL(/#\/lock$/) and /#\/(?!lock)/ after unlock Under history routing those match nothing, so the nav assertions failed on 'element(s) not found' rather than on anything about navigation. Matched as SUFFIXES rather than whole hrefs: Nextcloud serves the app as both /apps/keepiq/... and /index.php/apps/keepiq/..., and the link carries whichever base the page was loaded under, so pinning either would fail on the other for a reason that is not a defect. The existing comment already made that argument for the hash form; it now makes it for the path form. The post-unlock assertion is inverted rather than retargeted: it now asserts we LEFT /lock instead of naming the destination, which varies with the returnUrl the gate captured. Verified: no hash href selectors and no hash URL assertions remain in tests/; prettier --check on the full glob is clean. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
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.
Fourth app off
#routing, after stackiq (softwarecatalog#899), zaakafhandelapp (#609) and opencatalogi (#1341).Two parts
createWebHashHistory→createWebHistory.routerBase(), derived from the URL actually being served. Nextcloud serves the app under both/apps/keepiq/...and/index.php/apps/keepiq/...;generateUrl()returns only one. Arriving on the other leaves the path outside the router base, vue-router cannot resolve it, and the catch-all redirects to/— no error, deep link silently swallowed.This app has the sharpest version of that problem. keepiq's share and send links (
/share/link/:token,/send/:token,/share/request/:token) are handed to people outside the app, in messages and emails, in whichever URL shape the sender's client produced. A swallowed deep link there is a share that silently does nothing.6 spec files built URLs with a hash; all now use real paths.
Verification
The vault is locked in this environment, so every route redirects to the lock screen — which is itself the proof that the router parsed the request first:
/apps/keepiq/secrets/apps/keepiq/lock?returnUrl=/secrets/index.php/apps/keepiq/secrets/index.php/apps/keepiq/lock?returnUrl=/secrets/index.phpformNo hash anywhere, both URL forms work, and the requested route survives into
returnUrl. (/foldersanswersreturnUrl=/because the real route is/folders/:folderId, so the catch-all correctly claims the bare path.)eslintexits 0 andprettier --checkon the full glob (tests included) is clean; webpack compiles.returnUrlis the strongest available proof that routing resolves before the guard fires.