Skip to content

Commit

Permalink
update for TypeError: Cannot read properties of undefined (reading 'n…
Browse files Browse the repository at this point in the history
…avigator')
  • Loading branch information
seadfeng committed Aug 27, 2024
1 parent 1003db3 commit eb2af45
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 119 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/app/api/favicon/[domain]/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getFavicons } from '@/lib/utils';
import { getFavicons } from '@/lib/server';
import { ResponseInfo } from '@/types';
import type { NextRequest } from 'next/server';

Expand Down
2 changes: 1 addition & 1 deletion src/app/favicon/[domain]/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getFavicons, proxyFavicon } from '@/lib/utils';
import { getFavicons, proxyFavicon } from '@/lib/server';
import { ResponseInfo } from '@/types';
import type { NextRequest } from 'next/server';
export const runtime = 'edge';
Expand Down
5 changes: 4 additions & 1 deletion src/components/frontend/page/home/results.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,14 @@ function isBrowser() {
return typeof window !== 'undefined' && typeof navigator !== 'undefined';
}
const IconImage = ({ icon, index, onLoad, domain }: { icon: any; index: number; domain: string; onLoad?: (sizes: string)=> void }) => {
if (!isBrowser()) {
return null;
}
const [sizes, setSizes] = useState<string>(icon.sizes);
const imgRef = useRef<HTMLImageElement>(null);
const t = useTranslations();
useEffect(() => {
if (isBrowser() &&imgRef.current) {
if (isBrowser() && imgRef.current) {
const img = imgRef.current;
const handleImageLoad = () => {
setSizes(`${img.naturalWidth}x${img.naturalHeight}`);
Expand Down
114 changes: 114 additions & 0 deletions src/lib/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { ResponseInfo } from "@/types";
import { NextRequest } from "next/server";

// Fetch favicons from a given URL and return ResponseInfo
export const getFavicons = async ({ url, headers }: { url: string, headers?: Headers }): Promise<ResponseInfo> => {
const newUrl = new URL(url); // Create a URL object to extract the host

try {
// Perform the fetch request with optional headers and redirection follow
const response = await fetch(newUrl.toString(), {
method: "GET",
redirect: "follow",
headers
});

const body = await response.text();
const responseUrl = new URL(response.url);

// Regex to match <link> tags with "rel" containing "icon"
const regex = /<link[^>]*rel=['"][^'"]*icon[^'"]*['"][^>]*>/gi;
const matches = Array.from(body.matchAll(regex));
const icons: { sizes: string, href: string }[] = [];

matches.forEach((match) => {
const linkTag = match[0];

// Extract href value
const hrefMatch = linkTag.match(/href=['"](.*?)['"]/i);
const href = hrefMatch ? hrefMatch[1] : null;

// Extract sizes value
const sizesMatch = linkTag.match(/sizes=['"](.*?)['"]/i);
const sizes = sizesMatch ? sizesMatch[1] : null;

if (href) {
icons.push({
sizes: sizes || 'unknown',
href: (href.startsWith('http') || href.startsWith('data:image')) ? href : `${responseUrl.protocol}//${responseUrl.host}${href}`
});
}
});

return {
url: responseUrl.href,
host: responseUrl.host,
status: response.status,
statusText: response.statusText,
icons
};
} catch (error) {
console.error(`Error fetching favicons: ${error}`);
return {
url: newUrl.href,
host: newUrl.host,
status: 500,
statusText: 'Failed to fetch icons',
icons: []
};
}
};

// Function to fetch favicon from alternative sources
export const proxyFavicon = async ({ domain, request }: { domain: string; request: NextRequest }) => {
// List of alternative sources to fetch favicons
const sources = [
`https://www.google.com/s2/favicons?domain=${domain}`,
`https://icons.duckduckgo.com/ip3/${domain}.ico`
// `https://icon.horse/icon/${domain}`
];
let response: Response = new Response();
// Attempt to fetch favicon from each source
for (const source of sources) {
try {
response = await fetch(source, {
method: request.method,
headers: request.headers,
redirect: 'follow'
});
if (response.status == 200) {
break;
}

console.log("icon source:", source);

} catch (error) {
console.error(`Error fetching proxy favicon: ${error}`);
}
}
if (response.status !== 200) {
const firstLetter = domain.charAt(0).toUpperCase();
const svgContent = `
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#cccccc"/>
<text x="50%" y="50%" font-size="48" text-anchor="middle" dominant-baseline="middle" fill="#000000">${firstLetter}</text>
</svg>
`;
return new Response(svgContent, {
status: 200,
headers: {
'Cache-Control': 'public, max-age=86400',
'Content-Type': 'image/svg+xml'
}
});
} else {
// Return the fetched favicon
return new Response(response.body, {
headers: {
'Content-Type': response.headers.get('Content-Type') || 'image/x-icon',
'Cache-Control': 'public, max-age=86400',
},
});
}

};
114 changes: 0 additions & 114 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { appConfig, LocaleType } from "@/config";
import { ResponseInfo } from "@/types";
import { type ClassValue, clsx } from "clsx";
import { NextRequest } from "next/server";
import { twMerge } from "tailwind-merge";

// Utility function to combine Tailwind CSS classes with clsx and merge conflicts
Expand Down Expand Up @@ -40,118 +38,6 @@ export const createAlternates = ({ headers }: { headers: Headers; }) => {
}
}

// Fetch favicons from a given URL and return ResponseInfo
export const getFavicons = async ({ url, headers }: { url: string, headers?: Headers }): Promise<ResponseInfo> => {
const newUrl = new URL(url); // Create a URL object to extract the host

try {
// Perform the fetch request with optional headers and redirection follow
const response = await fetch(newUrl.toString(), {
method: "GET",
redirect: "follow",
headers
});

const body = await response.text();
const responseUrl = new URL(response.url);

// Regex to match <link> tags with "rel" containing "icon"
const regex = /<link[^>]*rel=['"][^'"]*icon[^'"]*['"][^>]*>/gi;
const matches = Array.from(body.matchAll(regex));
const icons: { sizes: string, href: string }[] = [];

matches.forEach((match) => {
const linkTag = match[0];

// Extract href value
const hrefMatch = linkTag.match(/href=['"](.*?)['"]/i);
const href = hrefMatch ? hrefMatch[1] : null;

// Extract sizes value
const sizesMatch = linkTag.match(/sizes=['"](.*?)['"]/i);
const sizes = sizesMatch ? sizesMatch[1] : null;

if (href) {
icons.push({
sizes: sizes || 'unknown',
href: (href.startsWith('http') || href.startsWith('data:image')) ? href : `${responseUrl.protocol}//${responseUrl.host}${href}`
});
}
});

return {
url: responseUrl.href,
host: responseUrl.host,
status: response.status,
statusText: response.statusText,
icons
};
} catch (error) {
console.error(`Error fetching favicons: ${error}`);
return {
url: newUrl.href,
host: newUrl.host,
status: 500,
statusText: 'Failed to fetch icons',
icons: []
};
}
};

// Function to fetch favicon from alternative sources
export const proxyFavicon = async ({ domain, request }: { domain: string; request: NextRequest }) => {
// List of alternative sources to fetch favicons
const sources = [
`https://www.google.com/s2/favicons?domain=${domain}`,
`https://icons.duckduckgo.com/ip3/${domain}.ico`
// `https://icon.horse/icon/${domain}`
];
let response: Response = new Response();
// Attempt to fetch favicon from each source
for (const source of sources) {
try {
response = await fetch(source, {
method: request.method,
headers: request.headers,
redirect: 'follow'
});
if (response.status == 200) {
break;
}

console.log("icon source:", source);

} catch (error) {
console.error(`Error fetching proxy favicon: ${error}`);
}
}
if (response.status !== 200) {
const firstLetter = domain.charAt(0).toUpperCase();
const svgContent = `
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#cccccc"/>
<text x="50%" y="50%" font-size="48" text-anchor="middle" dominant-baseline="middle" fill="#000000">${firstLetter}</text>
</svg>
`;
return new Response(svgContent, {
status: 200,
headers: {
'Cache-Control': 'public, max-age=86400',
'Content-Type': 'image/svg+xml'
}
});
} else {
// Return the fetched favicon
return new Response(response.body, {
headers: {
'Content-Type': response.headers.get('Content-Type') || 'image/x-icon',
'Cache-Control': 'public, max-age=86400',
},
});
}

};

export const downloadBase64Image = ({ base64Data, domain }: { base64Data: string, domain: string }) => {
const link = document.createElement('a');

Expand Down

0 comments on commit eb2af45

Please sign in to comment.