Drop leftover /trust path prefix handling
Portals are host-routed on slug subdomains, so SEO path stripping and the frontend basename helper were dead code. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -20,7 +20,6 @@
|
||||
|
||||
import { FullNameRequiredError, NDASignatureRequiredError } from "@probo/relay";
|
||||
|
||||
import { getPathPrefix } from "#/lib/http/pathPrefix";
|
||||
import { localizedPath, resolveUrlLocale, type UrlLocale } from "#/lib/i18n/locale";
|
||||
|
||||
// Markers appended to a post-auth `continue` URL so the portal fires the pending
|
||||
@@ -38,12 +37,11 @@ export const NEW_REQUEST_PARAM = "new-request";
|
||||
export const SUBSCRIBE_PARAM = "subscribe";
|
||||
|
||||
// Validates a `continue` target before we navigate to it. Only same-origin URLs
|
||||
// under the portal's path prefix are accepted; anything else falls back to the
|
||||
// portal home, so a crafted `?continue=` can never bounce the user off-site.
|
||||
// are accepted; anything else falls back to the portal home, so a crafted
|
||||
// `?continue=` can never bounce the user off-site.
|
||||
export function getSafeContinueUrl(param: string | null | undefined): string {
|
||||
const prefix = getPathPrefix();
|
||||
const localeHome = localizedPath(resolveUrlLocale(), "/");
|
||||
const fallback = window.location.origin + (prefix || "") + localeHome;
|
||||
const fallback = window.location.origin + localeHome;
|
||||
|
||||
if (!param) {
|
||||
return fallback;
|
||||
@@ -51,10 +49,7 @@ export function getSafeContinueUrl(param: string | null | undefined): string {
|
||||
|
||||
try {
|
||||
const url = new URL(param, window.location.origin);
|
||||
const underPrefix = prefix === ""
|
||||
? url.pathname.startsWith("/")
|
||||
: url.pathname === prefix || url.pathname.startsWith(`${prefix}/`);
|
||||
if (url.origin === window.location.origin && underPrefix) {
|
||||
if (url.origin === window.location.origin) {
|
||||
return window.location.origin + url.pathname + url.search;
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import { matchPath } from "react-router";
|
||||
|
||||
export function getPathPrefix() {
|
||||
const match = matchPath(
|
||||
{ path: "/trust/:id", caseSensitive: false, end: false },
|
||||
window.location.pathname,
|
||||
);
|
||||
|
||||
let prefix = "";
|
||||
if (match) {
|
||||
prefix = `/trust/${match.params.id}`;
|
||||
}
|
||||
|
||||
return prefix;
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import { createBrowserRouter, redirect } from "react-router";
|
||||
|
||||
import { PageErrorBoundary } from "#/components/errors/PageErrorBoundary";
|
||||
import { RootErrorBoundary } from "#/components/errors/RootErrorBoundary";
|
||||
import { getPathPrefix } from "#/lib/http/pathPrefix";
|
||||
import { localeLayoutLoader } from "#/lib/i18n/localeRedirect";
|
||||
import { resolveUrlLocale } from "#/lib/i18n/locale";
|
||||
import { authRoutes } from "#/pages/auth/routes";
|
||||
@@ -89,7 +88,7 @@ const routes = [
|
||||
...ndaRoutes,
|
||||
],
|
||||
},
|
||||
// Bare basename root (no locale segment) → guessed locale home.
|
||||
// Bare root (no locale segment) → guessed locale home.
|
||||
{
|
||||
path: "/",
|
||||
loader: () => {
|
||||
@@ -100,8 +99,5 @@ const routes = [
|
||||
},
|
||||
] satisfies AppRoute[];
|
||||
|
||||
// The portal is served under a /trust/{slug} path prefix (or a bare custom
|
||||
// domain). Match the router basename to that prefix so the routes resolve.
|
||||
export const router = createBrowserRouter(routes.map(routeFromAppRoute), {
|
||||
basename: getPathPrefix() || "/",
|
||||
});
|
||||
// Host-routed portal (slug subdomain / custom domain); paths are site-root.
|
||||
export const router = createBrowserRouter(routes.map(routeFromAppRoute));
|
||||
|
||||
@@ -35,9 +35,14 @@ const defaultCompliancePortalLocale = "en"
|
||||
|
||||
// SEOFromRequest derives html lang, a self-referencing canonical URL, and
|
||||
// hreflang alternates (including x-default → English) for the SPA shell.
|
||||
// Portals are host-routed (slug subdomain / custom domain); the request path
|
||||
// is already relative to the portal root.
|
||||
func SEOFromRequest(r *http.Request, pageBaseURL string) (htmlLang, canonical string, hreflang []HreflangLink) {
|
||||
appPath := complianceAppPath(r.URL.Path)
|
||||
locale, rest := splitLocaleFromAppPath(appPath)
|
||||
pathname := r.URL.Path
|
||||
if pathname == "" {
|
||||
pathname = "/"
|
||||
}
|
||||
locale, rest := splitLocaleFromAppPath(pathname)
|
||||
|
||||
htmlLang = locale
|
||||
canonical = localizedPageURL(pageBaseURL, locale, rest)
|
||||
@@ -57,26 +62,6 @@ func SEOFromRequest(r *http.Request, pageBaseURL string) (htmlLang, canonical st
|
||||
return htmlLang, canonical, hreflang
|
||||
}
|
||||
|
||||
// complianceAppPath returns the path relative to the portal root: under
|
||||
// /trust/:slug it strips that prefix; on a custom domain it returns the path as-is.
|
||||
func complianceAppPath(pathname string) string {
|
||||
trimmed := strings.TrimPrefix(pathname, "/")
|
||||
if strings.HasPrefix(trimmed, "trust/") {
|
||||
parts := strings.SplitN(trimmed, "/", 3)
|
||||
if len(parts) < 2 {
|
||||
return "/"
|
||||
}
|
||||
if len(parts) == 2 {
|
||||
return "/"
|
||||
}
|
||||
return "/" + parts[2]
|
||||
}
|
||||
if pathname == "" {
|
||||
return "/"
|
||||
}
|
||||
return pathname
|
||||
}
|
||||
|
||||
func splitLocaleFromAppPath(appPath string) (locale, rest string) {
|
||||
segments := strings.Split(strings.Trim(appPath, "/"), "/")
|
||||
if len(segments) == 0 || segments[0] == "" {
|
||||
@@ -91,7 +76,7 @@ func splitLocaleFromAppPath(appPath string) (locale, rest string) {
|
||||
return locale, "/" + strings.Join(segments[1:], "/")
|
||||
}
|
||||
|
||||
// Unprefixed legacy path — treat content path as-is; default lang for tags.
|
||||
// Unprefixed path — treat content path as-is; default lang for tags.
|
||||
return defaultCompliancePortalLocale, appPath
|
||||
}
|
||||
|
||||
|
||||
@@ -32,12 +32,19 @@ import (
|
||||
func TestSEOFromRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://app.example.com/trust/acme/fr/documents", nil)
|
||||
req, err := http.NewRequest(
|
||||
http.MethodGet,
|
||||
"https://acme.probopage.localhost/fr/documents",
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
lang, canonical, hreflang := complianceportal_v1.SEOFromRequest(req, "https://app.example.com/trust/acme")
|
||||
lang, canonical, hreflang := complianceportal_v1.SEOFromRequest(
|
||||
req,
|
||||
"https://acme.probopage.localhost",
|
||||
)
|
||||
assert.Equal(t, "fr", lang)
|
||||
assert.Equal(t, "https://app.example.com/trust/acme/fr/documents", canonical)
|
||||
assert.Equal(t, "https://acme.probopage.localhost/fr/documents", canonical)
|
||||
require.NotEmpty(t, hreflang)
|
||||
|
||||
var xDefault string
|
||||
@@ -50,6 +57,6 @@ func TestSEOFromRequest(t *testing.T) {
|
||||
enHref = link.Href
|
||||
}
|
||||
}
|
||||
assert.Equal(t, "https://app.example.com/trust/acme/en/documents", enHref)
|
||||
assert.Equal(t, "https://acme.probopage.localhost/en/documents", enHref)
|
||||
assert.Equal(t, enHref, xDefault)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user