fix(router): resolve deep links in both URL forms - #1451
Merged
rubenvdlinde merged 1 commit intoSep 1, 2026
Conversation
Nextcloud serves an app under both /apps/shillinq/... and /index.php/apps/shillinq/..., but generateUrl() returns only the form the instance is configured for. Used as the vue-router base, that means a visitor arriving on the other form has a pathname the router cannot strip its base from. No route matches, the catch-all takes over, and they land on the dashboard with no error at all. Measured live on learniq, which had the identical base, across all 282 of its routes: /apps/learniq/courses resolved to Courses, and /index.php/apps/learniq/courses resolved to the dashboard. Not one broken page, every deep link in that URL form. routerBase() derives the base from the pathname, so it always matches the URL the visitor actually arrived on. openregister, opencatalogi, stackiq, larpinq, zaakafhandelapp, pipelinq and keepiq already do exactly this.
Contributor
Quality Report — ConductionNL/shillinq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-manifest | ✅ | ||||
| check-manifest-shell | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| check-manifest-budget | ✅ | ||||
| check-markers | ✅ | ||||
| check-registers | ✅ | ||||
| check-seeds | ✅ | ||||
| check-fragment-required | ✅ | ||||
| check-nav-reachability | ✅ | ||||
| check-job-registration | ✅ | ||||
| test-l10n | ✅ | ||||
| composer | ✅ | ✅ 106/106 | |||
| npm | ✅ | ✅ 537/537 | |||
| 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 17:02 UTC
Download the full PDF report from the workflow artifacts.
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.
Nextcloud serves an app under both
/apps/<id>/...and/index.php/apps/<id>/..., butgenerateUrl()returns only the form the instance is configured for. Used as the vue-router base, a visitor arriving on the other form has a pathname the router cannot strip its base from: no route matches, the catch-all takes over, and they land on the dashboard with no error.I measured this on learniq, which had the identical base, against a live instance and all 282 of its routes:
/apps/learniq/courses/index.php/apps/learniq/coursesEvery route behaved the same way. This is not one broken page, it is every deep link in that URL form, and it fails silently, which is what makes it expensive: a bookmark, an emailed link or an integration that hardcodes
/index.phpjust quietly shows the wrong screen.routerBase()derives the base from the pathname, so it always matches the URL the visitor actually arrived on. This is not a new idea in the fleet: openregister, opencatalogi, stackiq, larpinq, zaakafhandelapp, pipelinq and keepiq already do exactly this. This app is one of ten that were still on the naive form.The change is the same in every one of them: add
routerBase()and use it as the history base.generateUrl()stays as the fallback for the case where the pathname does not match at all.Honest scope note. I verified the behaviour and the fix end to end on learniq (23 e2e cases passed, against 1 passed and 23 failed before). Here I have verified that the file parses and lints, and that the router base is the only thing that changed. The E2E job runs on the push to development rather than on this PR, so the suite verdict for this app arrives after merge.