From 1750e2a341378a38cc448768abd9bcb44b294344 Mon Sep 17 00:00:00 2001 From: Raj Nandan Sharma Date: Sat, 6 Jun 2026 12:25:42 +0530 Subject: [PATCH] refactor: implement absolute URL resolution for social media meta tags --- src/lib/client/resolver.ts | 36 +++++++++++++++++++ src/routes/(kener)/+page.svelte | 6 ++-- src/routes/(kener)/[page_path]/+page.svelte | 6 ++-- .../events/[MMMM]-[YYYY]/+page.svelte | 6 ++-- .../(kener)/events/[MMMM]-[YYYY]/+page.svelte | 6 ++-- .../incidents/[incident_id]/+page.svelte | 6 ++-- .../[maintenance_id]/+page.svelte | 6 ++-- .../monitors/[monitor_tag]/+page.svelte | 6 ++-- 8 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/lib/client/resolver.ts b/src/lib/client/resolver.ts index a0274e2b..59d5a29a 100644 --- a/src/lib/client/resolver.ts +++ b/src/lib/client/resolver.ts @@ -31,3 +31,39 @@ export default function urlResolve(resolve: ResolveFn, path: string, params?: Re } return resolve(path); } + +/** + * Resolves a path to an absolute URL by prefixing the site URL. + * Required for meta tags like og:image and twitter:image that need absolute URLs. + * @param resolve - The resolve function from $app/paths + * @param siteUrl - The site URL (e.g., "https://status.example.com") + * @param path - The route path or absolute URL + * @param params - Optional parameters for dynamic route segments + * @returns An absolute URL, or the resolved relative URL if siteUrl is empty + * + * @example + * ```ts + * absoluteResolve(resolve, "https://status.example.com", "/uploads/preview.png") + * // => "https://status.example.com/uploads/preview.png" + * ``` + */ +export function absoluteResolve( + resolve: ResolveFn, + siteUrl: string, + path: string, + params?: Record +): string { + // Normalize relative paths like "./assets/..." to "/assets/..." so the + // final URL doesn't contain "/./" segments (crawlers don't normalize these) + const normalizedPath = path.startsWith("./") ? path.slice(1) : path; + const resolved = urlResolve(resolve, normalizedPath, params); + // Already absolute, return as-is + if (resolved.startsWith("http://") || resolved.startsWith("https://")) { + return resolved; + } + if (!siteUrl) { + return resolved; + } + const trimmedSiteUrl = siteUrl.replace(/\/+$/, ""); + return trimmedSiteUrl + (resolved.startsWith("/") ? resolved : "/" + resolved); +} diff --git a/src/routes/(kener)/+page.svelte b/src/routes/(kener)/+page.svelte index ea45b08a..85ad4394 100644 --- a/src/routes/(kener)/+page.svelte +++ b/src/routes/(kener)/+page.svelte @@ -7,7 +7,7 @@ import IncidentItem from "$lib/components/IncidentItem.svelte"; import MaintenanceItem from "$lib/components/MaintenanceItem.svelte"; import mdToHTML from "$lib/marked.js"; - import clientResolver from "$lib/client/resolver.js"; + import clientResolver, { absoluteResolve } from "$lib/client/resolver.js"; import { resolve } from "$app/paths"; import { selectedTimezone } from "$lib/stores/timezone"; import { getEndOfDayAtTz } from "$lib/client/datetime"; @@ -136,8 +136,8 @@ {#if data.socialPagePreviewImage} - - + + {/if} diff --git a/src/routes/(kener)/[page_path]/+page.svelte b/src/routes/(kener)/[page_path]/+page.svelte index ea45b08a..85ad4394 100644 --- a/src/routes/(kener)/[page_path]/+page.svelte +++ b/src/routes/(kener)/[page_path]/+page.svelte @@ -7,7 +7,7 @@ import IncidentItem from "$lib/components/IncidentItem.svelte"; import MaintenanceItem from "$lib/components/MaintenanceItem.svelte"; import mdToHTML from "$lib/marked.js"; - import clientResolver from "$lib/client/resolver.js"; + import clientResolver, { absoluteResolve } from "$lib/client/resolver.js"; import { resolve } from "$app/paths"; import { selectedTimezone } from "$lib/stores/timezone"; import { getEndOfDayAtTz } from "$lib/client/datetime"; @@ -136,8 +136,8 @@ {#if data.socialPagePreviewImage} - - + + {/if} diff --git a/src/routes/(kener)/[page_path]/events/[MMMM]-[YYYY]/+page.svelte b/src/routes/(kener)/[page_path]/events/[MMMM]-[YYYY]/+page.svelte index 6b7e44f9..2538f1d0 100644 --- a/src/routes/(kener)/[page_path]/events/[MMMM]-[YYYY]/+page.svelte +++ b/src/routes/(kener)/[page_path]/events/[MMMM]-[YYYY]/+page.svelte @@ -13,7 +13,7 @@ import { t } from "$lib/stores/i18n"; import { formatDate } from "$lib/stores/datetime"; import { resolve } from "$app/paths"; - import clientResolver from "$lib/client/resolver.js"; + import clientResolver, { absoluteResolve } from "$lib/client/resolver.js"; import { format, parse, addMonths, subMonths, getUnixTime, startOfDay, formatDistanceStrict } from "date-fns"; import { page } from "$app/state"; import type { IncidentForMonitorListWithComments, MaintenanceEventsMonitorList } from "$lib/server/types/db"; @@ -165,8 +165,8 @@ {#if data.socialPreviewImage} - - + + {/if} diff --git a/src/routes/(kener)/events/[MMMM]-[YYYY]/+page.svelte b/src/routes/(kener)/events/[MMMM]-[YYYY]/+page.svelte index 833b0259..c6378d44 100644 --- a/src/routes/(kener)/events/[MMMM]-[YYYY]/+page.svelte +++ b/src/routes/(kener)/events/[MMMM]-[YYYY]/+page.svelte @@ -13,7 +13,7 @@ import { t } from "$lib/stores/i18n"; import { formatDate } from "$lib/stores/datetime"; import { resolve } from "$app/paths"; - import clientResolver from "$lib/client/resolver.js"; + import clientResolver, { absoluteResolve } from "$lib/client/resolver.js"; import { format, parse, addMonths, subMonths, getUnixTime, startOfDay, formatDistanceStrict } from "date-fns"; import { page } from "$app/state"; import type { IncidentForMonitorListWithComments, MaintenanceEventsMonitorList } from "$lib/server/types/db"; @@ -165,8 +165,8 @@ {#if data.socialPreviewImage} - - + + {/if} diff --git a/src/routes/(kener)/incidents/[incident_id]/+page.svelte b/src/routes/(kener)/incidents/[incident_id]/+page.svelte index f099b208..37083eda 100644 --- a/src/routes/(kener)/incidents/[incident_id]/+page.svelte +++ b/src/routes/(kener)/incidents/[incident_id]/+page.svelte @@ -12,7 +12,7 @@ import { SveltePurify } from "@humanspeak/svelte-purify"; import { t } from "$lib/stores/i18n"; import { formatDate, formatDuration } from "$lib/stores/datetime"; - import clientResolver from "$lib/client/resolver.js"; + import clientResolver, { absoluteResolve } from "$lib/client/resolver.js"; import { page } from "$app/state"; let { data } = $props(); @@ -28,8 +28,8 @@ {/if} {#if data.socialPreviewImage} - - + + {/if} diff --git a/src/routes/(kener)/maintenances/[maintenance_id]/+page.svelte b/src/routes/(kener)/maintenances/[maintenance_id]/+page.svelte index 8835ad5e..3f850dc9 100644 --- a/src/routes/(kener)/maintenances/[maintenance_id]/+page.svelte +++ b/src/routes/(kener)/maintenances/[maintenance_id]/+page.svelte @@ -15,7 +15,7 @@ import STATUS_ICON from "$lib/icons"; import { t } from "$lib/stores/i18n"; import { formatDate, formatDuration } from "$lib/stores/datetime"; - import clientResolver from "$lib/client/resolver.js"; + import clientResolver, { absoluteResolve } from "$lib/client/resolver.js"; import { SveltePurify } from "@humanspeak/svelte-purify"; import { page } from "$app/state"; @@ -73,8 +73,8 @@ {/if} {#if data.socialPreviewImage} - - + + {/if} diff --git a/src/routes/(kener)/monitors/[monitor_tag]/+page.svelte b/src/routes/(kener)/monitors/[monitor_tag]/+page.svelte index ef5e467f..e51581a6 100644 --- a/src/routes/(kener)/monitors/[monitor_tag]/+page.svelte +++ b/src/routes/(kener)/monitors/[monitor_tag]/+page.svelte @@ -6,7 +6,7 @@ import ThemePlus from "$lib/components/ThemePlus.svelte"; import MonitorOverview from "$lib/components/MonitorOverview.svelte"; import ArrowUpRight from "@lucide/svelte/icons/arrow-up-right"; - import clientResolver from "$lib/client/resolver.js"; + import clientResolver, { absoluteResolve } from "$lib/client/resolver.js"; import { resolve } from "$app/paths"; import trackEvent from "$lib/beacon"; import IncidentItem from "$lib/components/IncidentItem.svelte"; @@ -37,8 +37,8 @@ {/if} {#if data.socialPreviewImage} - - + + {/if}