import { NextApiRequest, NextApiResponse } from 'next'; import { createReadStream } from 'node:fs'; import { resolve } from 'node:path'; import { downloadImageFromUrl, getFavicon, makeRequestWithUserAgent, } from 'lib/request'; import { Favicon } from 'types/types'; import { buildFaviconUrl, findFaviconPath } from 'lib/url'; import { convertBase64ToBuffer, isBase64Image, isImage } from 'lib/image'; export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { const urlParam = (req.query?.urlParam as string) || ''; if (!urlParam) { throw new Error('URL is missing'); } const faviconRequestUrl = buildFaviconUrl(urlParam, '/favicon.ico'); try { return sendImage(res, await getFavicon(faviconRequestUrl)); } catch (error) { console.error( '[Favicon]', `[first: ${faviconRequestUrl}]`, 'Unable to retrieve favicon from favicon.ico url', ); } const requestDocument = await makeRequestWithUserAgent(urlParam); const documentAsText = await requestDocument.text(); const faviconPath = findFaviconPath(documentAsText); if (!faviconPath) { console.error( '[Favicon]', `[first: ${faviconRequestUrl}]`, 'No link/href attribute found', ); return sendDefaultImage(res); } const finalUrl = buildFaviconUrl(requestDocument.url, faviconPath); try { if (!faviconPath) { throw new Error('Unable to find favicon path'); } if (isBase64Image(faviconPath)) { console.log( '[Favicon]', `[second: ${faviconRequestUrl}]`, 'info: base64, convert it to buffer', ); const buffer = convertBase64ToBuffer(faviconPath); return sendImage(res, { buffer, type: 'image/x-icon', size: buffer.length, url: faviconPath, }); } const finalUrl = faviconPath.startsWith('http') ? faviconPath : buildFaviconUrl(requestDocument.url, faviconPath); const favicon = await downloadImageFromUrl(finalUrl); if (!isImage(favicon.type)) { throw new Error('Favicon path does not return an image'); } console.log('[Favicon]', `[second: ${finalUrl}]`, 'success: image found'); return sendImage(res, favicon); } catch (error) { const errorMessage = error?.message || 'Unable to retrieve favicon'; console.log('[Favicon]', `[second: ${finalUrl}], error:`, errorMessage); return sendDefaultImage(res); } } function sendImage(res: NextApiResponse, { buffer, type, size }: Favicon) { res.setHeader('Content-Type', type); res.setHeader('Content-Length', size); res.send(buffer); } function sendDefaultImage(res: NextApiResponse) { const readStream = createReadStream( resolve(process.cwd(), './public/empty-image.png'), ); res.writeHead(206); readStream.pipe(res); }