mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-09 23:15:36 +00:00
- Changement de structure de fichier - Ajout des favicons des sites - Suppression et mise à jour de dépendances - Ajout React-Icons pour gérer les icons - Amélioration du l'UI
164 lines
3.7 KiB
TypeScript
164 lines
3.7 KiB
TypeScript
import { NextApiRequest, NextApiResponse } from "next";
|
|
import { parse } from "node-html-parser";
|
|
|
|
const USER_AGENT =
|
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54";
|
|
|
|
export default async function handler(
|
|
req: NextApiRequest,
|
|
res: NextApiResponse
|
|
) {
|
|
const urlRequest = (req.query?.url as string) || "";
|
|
if (!urlRequest) {
|
|
throw new Error("URL's missing");
|
|
}
|
|
|
|
try {
|
|
const { favicon, type, size } = await downloadImageFromUrl(
|
|
urlRequest + "/favicon.ico"
|
|
);
|
|
if (size === 0) {
|
|
throw new Error("Empty favicon");
|
|
}
|
|
if (!isImage(type)) {
|
|
throw new Error("Favicon path does not return an image");
|
|
}
|
|
return sendImage({
|
|
content: favicon,
|
|
res,
|
|
type,
|
|
size,
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
|
|
try {
|
|
const requestDocument = await makeRequest(urlRequest);
|
|
const text = await requestDocument.text();
|
|
|
|
const faviconPath = findFaviconPath(text);
|
|
if (!faviconPath) {
|
|
throw new Error("Unable to find favicon path");
|
|
}
|
|
|
|
if (isBase64Image(faviconPath)) {
|
|
console.log("base64, convert it to buffer");
|
|
const buffer = convertBase64ToBuffer(faviconPath);
|
|
return sendImage({
|
|
content: buffer,
|
|
res,
|
|
type: "image/vnd.microsoft.icon",
|
|
size: buffer.length,
|
|
});
|
|
}
|
|
|
|
const pathWithoutFile = popLastSegment(requestDocument.url);
|
|
const finalUrl = buildFaviconUrl(faviconPath, pathWithoutFile);
|
|
|
|
const { favicon, type, size } = await downloadImageFromUrl(finalUrl);
|
|
if (!isImage(type)) {
|
|
throw new Error("Favicon path does not return an image");
|
|
}
|
|
|
|
return sendImage({
|
|
content: favicon,
|
|
res,
|
|
type,
|
|
size,
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
res.status(404).send({ error: "Unable to retrieve favicon" });
|
|
}
|
|
}
|
|
|
|
async function makeRequest(url: string) {
|
|
const headers = new Headers();
|
|
headers.set("User-Agent", USER_AGENT);
|
|
|
|
const request = await fetch(url, { headers });
|
|
return request;
|
|
}
|
|
|
|
async function downloadImageFromUrl(url: string): Promise<{
|
|
favicon: Buffer;
|
|
url: string;
|
|
type: string;
|
|
size: number;
|
|
}> {
|
|
const request = await makeRequest(url);
|
|
const blob = await request.blob();
|
|
|
|
return {
|
|
favicon: Buffer.from(await blob.arrayBuffer()),
|
|
url: request.url,
|
|
type: blob.type,
|
|
size: blob.size,
|
|
};
|
|
}
|
|
|
|
function sendImage({
|
|
content,
|
|
res,
|
|
type,
|
|
size,
|
|
}: {
|
|
content: Buffer;
|
|
res: NextApiResponse;
|
|
type: string;
|
|
size: number;
|
|
}) {
|
|
res.setHeader("Content-Type", type);
|
|
res.setHeader("Content-Length", size);
|
|
res.send(content);
|
|
}
|
|
|
|
function findFaviconPath(text) {
|
|
const document = parse(text);
|
|
const links = document.querySelectorAll(
|
|
'link[rel="icon"], link[rel="shortcut icon"]'
|
|
);
|
|
const link = links.find(
|
|
(link) => !link.getAttribute("href").startsWith("data:image/")
|
|
);
|
|
if (!link) {
|
|
return console.warn("nothing, exit");
|
|
}
|
|
|
|
return link.getAttribute("href") || "";
|
|
}
|
|
|
|
function popLastSegment(url = "") {
|
|
const { href } = new URL(url);
|
|
const pathWithoutFile = href.split("/");
|
|
pathWithoutFile.pop();
|
|
return pathWithoutFile.join("/") || "";
|
|
}
|
|
|
|
function buildFaviconUrl(faviconPath, pathWithoutFile) {
|
|
if (faviconPath.startsWith("http")) {
|
|
console.log("startsWith http, result", faviconPath);
|
|
return faviconPath;
|
|
} else if (faviconPath.startsWith("/")) {
|
|
console.log("startsWith /, result", pathWithoutFile + faviconPath);
|
|
return pathWithoutFile + faviconPath;
|
|
} else {
|
|
console.log("else, result", pathWithoutFile + "/" + faviconPath);
|
|
return pathWithoutFile + "/" + faviconPath;
|
|
}
|
|
}
|
|
|
|
function isImage(type: string) {
|
|
return type.includes("image");
|
|
}
|
|
|
|
function isBase64Image(data) {
|
|
return data.startsWith("data:image/");
|
|
}
|
|
|
|
function convertBase64ToBuffer(base64 = ""): Buffer {
|
|
const buffer = Buffer.from(base64, "base64");
|
|
return buffer;
|
|
}
|