--- interface Props { title?: string; description?: string; image?: string; isWhitelabel?: boolean; whitelabel?: any; subsite?: any; } import { metaData } from "../config"; import { SEO } from "astro-seo"; import { JoyUIRootIsland } from "./Components" import { useHtmlThemeAttr } from "../hooks/useHtmlThemeAttr"; import { usePrimeReactThemeSwitcher } from "../hooks/usePrimeReactThemeSwitcher"; const { title, description, image, isWhitelabel, whitelabel, subsite } = Astro.props; const { url } = Astro; const trimmedTitle = title?.trim(); const seoTitle = trimmedTitle || metaData.title; // If a whitelabel/subsite override exists, prefer its shareTitle/shareDescription/ogImage/favicon const subsiteMeta = whitelabel ?? {}; const shareTitle = isWhitelabel ? (trimmedTitle || (subsiteMeta.shareTitle ?? metaData.shareTitle ?? metaData.title)) : (trimmedTitle ? `${trimmedTitle} | ${metaData.title}` : (metaData.shareTitle ?? metaData.title)); const seoTitleTemplate = isWhitelabel ? "%s" : (trimmedTitle ? `%s | ${metaData.title}` : "%s"); const shareDescription = isWhitelabel ? (trimmedTitle || (subsiteMeta.shareDescription ?? metaData.shareDescription ?? metaData.description)) : (description ?? metaData.shareDescription ?? metaData.description); // Compute canonical URL with these priorities: // 1. If the whitelabel/subsite provides a baseUrl, use that as the host for the canonical URL // 2. If a subsite was detected and the request host matches the subsite host, canonical uses that host // 3. If the request is for a path-based subsite (host is main site), prefer metaData.baseUrl so canonical remains on main site // 4. Fallback to the request URL or metaData.baseUrl const currentHost = (Astro.request?.headers?.get('host') || '').split(':')[0]; let canonicalBase: string | null = null; if (subsiteMeta?.baseUrl) { canonicalBase = subsiteMeta.baseUrl; } else if (subsite?.host) { // normalize hosts for comparison const requestedHost = (currentHost || '').toLowerCase(); const subsiteHost = String(subsite.host || '').toLowerCase(); if (requestedHost && requestedHost === subsiteHost) { canonicalBase = `https://${subsite.host}`; } else { // keep canonical on the main configured base (path-based subsites should remain under metaData.baseUrl) canonicalBase = metaData.baseUrl; } } // Decide whether canonical should be the site-root (e.g. https://req.boatson.boats/) // or include a path. Rules: // - If canonicalBase comes from a whitelabel/baseUrl or request host matches subsite host -> prefer site-root. // - Otherwise (path-based subsites), include the pathname/search so canonical remains under the main site path. const isHostMatchedSubsite = Boolean(subsite?.host && currentHost && currentHost.toLowerCase() === String(subsite.host).toLowerCase()); const isSubsiteRootCanonical = Boolean(subsiteMeta?.baseUrl || isHostMatchedSubsite); let canonicalUrl: string; if (canonicalBase) { if (isSubsiteRootCanonical) { // ensure canonicalBase ends with a single '/' canonicalUrl = canonicalBase.endsWith('/') ? canonicalBase : `${canonicalBase}/`; } else { canonicalUrl = new URL((url?.pathname ?? '') + (url?.search ?? ''), canonicalBase).toString(); } } else { canonicalUrl = url?.href ?? metaData.baseUrl; } // Prefer the whitelabel/subsite ogImage when this page is for a whitelabel site. // Otherwise fall back to an explicit image prop or the global ogImage. const resolvedOgImage = (isWhitelabel && subsiteMeta.ogImage) ? subsiteMeta.ogImage : (image ?? metaData.ogImage); // Keep relative/site-root paths as-is (e.g. '/images/req.png') and don't force // an absolute URL using metaData.baseUrl. Only keep absolute (http/https) // URLs if the value is already absolute. function keepRelativeOrAbsolute(val) { if (!val) return val; if (/^https?:\/\//i.test(val)) return val; // already absolute // Normalize to site-root-relative so local testing always resolves under '/' return val.startsWith('/') ? val : `/${val}`; } const shareImage = keepRelativeOrAbsolute(resolvedOgImage); const shareImageAlt = subsiteMeta.shareImageAlt ?? metaData.shareImageAlt ?? metaData.shareTitle ?? metaData.title; // Build icon links: prefer subsite icons when present. If a subsite provides a single // `favicon` but no `icons` array, do NOT append the global icons to avoid duplicates. const primaryIconHref = keepRelativeOrAbsolute(subsiteMeta.favicon ?? metaData.favicon); // Ensure extraIcons are used only when subsite doesn't provide a single favicon (prevents duplicates) const extraIcons = subsiteMeta.icons ? subsiteMeta.icons : (subsiteMeta.favicon ? [] : (metaData.icons ?? [])); ---