Uh oh!
There was an error while loading. Please reload this page.
Add publishing routing-strategy shim to phoenix_kit_routes/0 - #524
Merged
Conversation
Emit a `def call/2` override on the host router (via Phoenix.Router's documented `defoverridable init: 1, call: 2` extension point) that path-rewrites publishing-bound URLs onto an internal `__phoenix_kit_publishing_dispatch` prefix. Register publishing's catch-all under that internal scope with the standard `:browser` and `:phoenix_kit_*` pipelines plus `RouterDispatch.restore_path/2` so canonical-URL generation in the controller sees the URL the client sent, not the internal prefix. URLs that don't resolve to a known publishing group pass through unchanged so host routes shaped `/:locale/<literal>/...` declared after `phoenix_kit_routes()` win matching. Fixes the silent shadowing where publishing's `/:language/:group/*path` claimed every two-or-more- segment URL. Compile-time gated on `Code.ensure_loaded?(PhoenixKitPublishing.RouterDispatch)`; installs without publishing in the dep tree get an empty AST. `apply/3` is used to dodge the static-resolution warning when the optional dep is absent.
Pair commit with `phoenix_kit_publishing` 797e40d, which adds the `localized_segment/0` and `root_segment/0` helpers on `PhoenixKitPublishing.RouterDispatch`. The `compile_publishing_routing/1` helper now splits the internal-prefix scope in two: * `/<prefix>/__phoenix_kit_publishing_dispatch/localized/:language/:group(/*path)` * `/<prefix>/__phoenix_kit_publishing_dispatch/root/:group(/*path)` The override emitted in the host router calls `RouterDispatch.maybe_rewrite/1`, which now returns rewrites that include the appropriate discriminator segment. Phoenix's matcher sees only one viable route shape per sub-scope and dispatches the controller with correct bindings (no more `language=<group-slug>` collision when the slug matches the language-code regex). AGENTS.md updated to describe the sub-scope split. Verified: original collision fix still holds (host route wins for `/<locale>/services/view/<slug>`); the no-locale slug-mode regression the canary install caught is closed.
ddon added a commit
that referenced
this pull request
May 8, 2026
…525 Code/doc fixes addressing one finding per PR (or several where trivial). Each closes a NITPICK or IMPROVEMENT-LOW from the matching CLAUDE_REVIEW.md; design-level / breaking / risky items deferred per the FOLLOW_UP.md "Skipped" sections. - #516: Drop dead `String.to_atom` fallback in OAuth interpolate_url - #518: Delete stray 0-byte pages_html.ex - #519: Fix stale `viewer={true}` template comment + login_path trailing-slash self-loop guard - #521: Resolution-order doc on permission_key_for_admin_view/1 - #522: Hot-reload safety pitfall in per-module-i18n.md - #523: KnownPackages — max-pages cap, ensure_table race comment, Logger-levels operational signals in moduledoc - #524: __mix_recompile__?/0 note next to apply/3 explanation - #525: LanguageSwitcher attr doc atom/string keys + DRY resolve_url per-language + JS sortable:flash defensive status check Plus FOLLOW_UP.md per PR enumerating closed vs deferred items. PR #525's FOLLOW_UP also captures the bundled DnD audit trail (table_default drag-handle scoping, sortable:flash, TR cell-width preservation) absent from the original PR body. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ddon added a commit
that referenced
this pull request
May 8, 2026
Three-day window of accumulated work since 1.7.105 (2026-05-05): PRs #516, #518, #519, #521, #522, #523, #524, #525, plus the review-doc suite and post-merge triage closing nitpicks across all eight. Headline changes — V111 PDF library tables, DB module extracted to phoenix_kit_db, MediaBrowser modal viewer, sidebar gettext API, live Hex.pm catalog, publishing routing-strategy shim closing the /:locale/<literal>/... host-route shadowing bug, LanguageSwitcher :per_translation_urls, and bundled DnD improvements (drag-handle scoping, sortable:flash, TR cell-width preservation). All changes are strictly additive / non-breaking; one transitional extraction (DB → phoenix_kit_db) requires the paired Hex package once it ships. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 freeto 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.
Summary
Pairs with
BeamLabEU/phoenix_kit_publishing#14.phoenix_kit_routes/0now emits a publishing-specific dispatch shimwhen
PhoenixKitPublishing.RouterDispatchis loaded:/<url_prefix>/__phoenix_kit_publishing_dispatchwith two sub-scopes(
/localizedbinding:language+:group,/rootbinding:grouponly), each piped through the standard:browser+:phoenix_kit_*pipelines plus a new:phoenix_kit_publishing_internalpipeline that runs
RouterDispatch.restore_path/2;def call/2override on the host router (Phoenix.Router publishesdefoverridable init: 1, call: 2frommatch_dispatch/0— adocumented extension point) that calls
RouterDispatch.maybe_rewrite/1before
super(conn, opts). Publishing-bound URLs get path-rewrittenonto the internal prefix and dispatched via Phoenix's normal
pipeline; URLs that don't resolve to a known publishing group pass
through unchanged so host routes win Phoenix's first-match.
Compile-time gated on
Code.ensure_loaded?(PhoenixKitPublishing.RouterDispatch). Installsthat don't have publishing in the dep tree get an empty AST — no
behaviour change.
Why
Publishing's old
Routes.public_routes/1registered/:language/:group(/*path)and/:group(/*path)directly under thehost's URL prefix. Under
url_prefix: "/"those routes match everytwo-or-more-segment URL, and Phoenix.Router has no per-segment regex
constraint mechanism — so any host route declared after
phoenix_kit_routes()shaped/:locale/<literal>/...was silentlyshadowed: publishing's controller matched first, didn't find the
group, returned 404. Canary host's
/fr/services/view/nos-services-a-nice(and three other/:locale/services/...routes) were dead this way after theyinstalled
phoenix_kit_legal(which transitively pulls publishing).The dispatch shim flips the trust direction. Publishing's catch-all
no longer sits at the host's absolute root claiming everything;
instead the override checks each request against the listing-cache /
DB and rewrites only when the URL belongs to a known publishing
group. Host routes get a fair shot at every URL.
See
BeamLabEU/phoenix_kit_publishing#14for the matchingPhoenixKitPublishing.RouterDispatchmodule, the policy details(
maybe_rewrite/1,restore_path/2, thedefoverridableedge casefor hosts with their own
def call/2), and the 19-test pinning suite.Coordination notice
This change is conditional on
PhoenixKitPublishing.RouterDispatchbeing present, so:
RouterDispatchmodule) —Code.ensure_loaded?returns false, thehelper emits no-op AST, publishing's old
Routes.public_routes/1still registers the catch-all as before. Behaviour unchanged.
(
RouterDispatchpresent,Routes.public_routes/1returns no-op) —the macro emits the override + internal scope. Bug fixed.
macro change) — publishing's
Routes.public_routes/1no longerregisters the catch-all and the override isn't emitted, so
publishing public URLs return Phoenix
NoRouteError.Regression for them — so the publishing PR shouldn't ship to
Hex without this core release also being out.
Both versions need to ship together (or core ships first, then
publishing). The publishing PR description flags the same constraint
in the other direction.
Verification
Browser smoke (workbench
phoenix_kit_parentsetup with the canarycollision shape —
url_prefix: "/"+/:localescope afterphoenix_kit_routes()):/en/services/view/test-slug/fr/services/view/x/en/db-test-1/markdown-rendering-demo/db-test-1/..., body shows post/db-test-1/markdown-rendering-demo/ku-ku/the-new-post/admin/dashboard//some-unknown-routedocument.documentElement.outerHTML.includes('__phoenix_kit_publishing_dispatch')returned
falseon every rendered publishing page checked. Zero leakageof the internal prefix or either discriminator segment in canonical, og,
links, JS, headers.
Canary install (separate host, four-locale config, three publishing
groups including
ku-kuslug-mode) verified the same matrixend-to-end + session/CSRF + view-source — all green after both
commits land.
Test plan
mix precommitcleanlocalized/rootsegment leakage — 0 occurrencesFiles
lib/phoenix_kit_web/integration.ex(+94 lines:compile_publishing_routing/1private helper + splice point inphoenix_kit_routes/0)AGENTS.md(+44 lines: "Publishing routing strategy" section under "External module route discovery")