Merge pull request #1402 from guangyang1206/fix/extract-domain-helper-1368

Fix/extract domain helper 1368
This commit is contained in:
Rohan Verma 2026-05-17 18:17:25 -07:00 committed by GitHub
commit 8fc4b98593
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 24 additions and 38 deletions

View file

@ -24,6 +24,8 @@ import dynamic from "next/dynamic";
import type { FC } from "react"; import type { FC } from "react";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { commentsEnabledAtom, targetCommentIdAtom } from "@/atoms/chat/current-thread.atom"; import { commentsEnabledAtom, targetCommentIdAtom } from "@/atoms/chat/current-thread.atom";
import { tryGetHostname } from "@/lib/url";
import { import {
globalNewLLMConfigsAtom, globalNewLLMConfigsAtom,
newLLMConfigsAtom, newLLMConfigsAtom,
@ -99,20 +101,12 @@ const GenerateImageToolUI = dynamic(
import("@/components/tool-ui/generate-image").then((m) => ({ default: m.GenerateImageToolUI })), import("@/components/tool-ui/generate-image").then((m) => ({ default: m.GenerateImageToolUI })),
{ ssr: false } { ssr: false }
); );
function extractDomain(url: string): string | undefined {
try {
return new URL(url).hostname.replace(/^www\./, "");
} catch {
return undefined;
}
}
function useCitationsFromMetadata(): SerializableCitation[] { function useCitationsFromMetadata(): SerializableCitation[] {
const allCitations = useAllCitationMetadata(); const allCitations = useAllCitationMetadata();
return useMemo(() => { return useMemo(() => {
const result: SerializableCitation[] = []; const result: SerializableCitation[] = [];
for (const [url, meta] of allCitations) { for (const [url, meta] of allCitations) {
const domain = extractDomain(url); const domain = tryGetHostname(url);
result.push({ result.push({
id: `url-cite-${url}`, id: `url-cite-${url}`,
href: url, href: url,

View file

@ -193,14 +193,7 @@ const SurfsenseDocCitation: FC<{ chunkId: number }> = ({ chunkId }) => {
); );
}; };
function extractDomain(url: string): string { import { tryGetHostname } from "@/lib/url";
try {
const hostname = new URL(url).hostname;
return hostname.replace(/^www\./, "");
} catch {
return url;
}
}
interface UrlCitationProps { interface UrlCitationProps {
url: string; url: string;
@ -212,7 +205,7 @@ interface UrlCitationProps {
* page title and snippet (extracted deterministically from web_search tool results). * page title and snippet (extracted deterministically from web_search tool results).
*/ */
export const UrlCitation: FC<UrlCitationProps> = ({ url }) => { export const UrlCitation: FC<UrlCitationProps> = ({ url }) => {
const domain = extractDomain(url); const domain = tryGetHostname(url) ?? url;
const meta = useCitationMetadata(url); const meta = useCitationMetadata(url);
return ( return (

View file

@ -23,6 +23,8 @@ import "katex/dist/katex.min.css";
import { toast } from "sonner"; import { toast } from "sonner";
import { processChildrenWithCitations } from "@/components/citations/citation-renderer"; import { processChildrenWithCitations } from "@/components/citations/citation-renderer";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { tryGetHostname } from "@/lib/url";
import { import {
Table, Table,
TableBody, TableBody,
@ -139,15 +141,6 @@ const MarkdownTextImpl = () => {
export const MarkdownText = memo(MarkdownTextImpl); export const MarkdownText = memo(MarkdownTextImpl);
function extractDomain(url: string): string {
try {
const parsed = new URL(url);
return parsed.hostname.replace(/^www\./, "");
} catch {
return "";
}
}
// Canonical local-file virtual paths are mount-prefixed: /<mount>/<relative/path> // Canonical local-file virtual paths are mount-prefixed: /<mount>/<relative/path>
const LOCAL_FILE_PATH_REGEX = /^\/[a-z0-9_-]+\/[^\s`]+(?:\/[^\s`]+)*$/; const LOCAL_FILE_PATH_REGEX = /^\/[a-z0-9_-]+\/[^\s`]+(?:\/[^\s`]+)*$/;
@ -288,7 +281,7 @@ function FilePathLink({ path, className }: { path: string; className?: string })
function MarkdownImage({ src, alt }: { src?: string; alt?: string }) { function MarkdownImage({ src, alt }: { src?: string; alt?: string }) {
if (!src) return null; if (!src) return null;
const domain = extractDomain(src); const domain = tryGetHostname(src) ?? "";
return ( return (
<div className="my-4 w-fit max-w-lg overflow-hidden rounded-2xl border bg-muted/30 select-none"> <div className="my-4 w-fit max-w-lg overflow-hidden rounded-2xl border bg-muted/30 select-none">

View file

@ -6,19 +6,11 @@ import * as React from "react";
import { openSafeNavigationHref, sanitizeHref } from "../shared/media"; import { openSafeNavigationHref, sanitizeHref } from "../shared/media";
import { cn, Popover, PopoverContent, PopoverTrigger } from "./_adapter"; import { cn, Popover, PopoverContent, PopoverTrigger } from "./_adapter";
import type { CitationVariant, SerializableCitation } from "./schema"; import type { CitationVariant, SerializableCitation } from "./schema";
import { tryGetHostname } from "@/lib/url";
import { TYPE_ICONS } from "./type-icons"; import { TYPE_ICONS } from "./type-icons";
const FALLBACK_LOCALE = "en-US"; const FALLBACK_LOCALE = "en-US";
function extractDomain(url: string): string | undefined {
try {
const urlObj = new URL(url);
return urlObj.hostname.replace(/^www\./, "");
} catch {
return undefined;
}
}
function formatDate(isoString: string, locale: string): string { function formatDate(isoString: string, locale: string): string {
try { try {
const date = new Date(isoString); const date = new Date(isoString);
@ -78,7 +70,7 @@ export function Citation(props: CitationProps) {
const locale = providedLocale ?? FALLBACK_LOCALE; const locale = providedLocale ?? FALLBACK_LOCALE;
const sanitizedHref = sanitizeHref(rawHref); const sanitizedHref = sanitizeHref(rawHref);
const domain = providedDomain ?? extractDomain(rawHref); const domain = providedDomain ?? tryGetHostname(rawHref);
const citationData: SerializableCitation = { const citationData: SerializableCitation = {
...serializable, ...serializable,

14
surfsense_web/lib/url.ts Normal file
View file

@ -0,0 +1,14 @@
/**
* Extract a normalized hostname from a URL. Strips a leading `www.`.
* Returns `undefined` if the input is not a parseable URL.
*
* This is the canonical replacement for the four previously-duplicated
* `extractDomain` helpers that had subtly different error fallbacks.
*/
export function tryGetHostname(url: string): string | undefined {
try {
return new URL(url).hostname.replace(/^www\./, "");
} catch {
return undefined;
}
}