104 lines
2.4 KiB
JavaScript
104 lines
2.4 KiB
JavaScript
import express from "express";
|
||
import axios from "axios";
|
||
import * as cheerio from "cheerio";
|
||
import { URL } from "url";
|
||
|
||
const router = express.Router();
|
||
|
||
// Normalizza URL relativi → assoluti
|
||
function normalize(base, relative) {
|
||
try {
|
||
return new URL(relative, base).href;
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// Scarica HTML con fallback CORS
|
||
async function fetchHTML(url) {
|
||
try {
|
||
const res = await axios.get(url, {
|
||
timeout: 8000,
|
||
headers: {
|
||
"User-Agent": "Mozilla/5.0"
|
||
}
|
||
});
|
||
return res.data;
|
||
} catch (err) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
router.get("/", async (req, res) => {
|
||
const siteUrl = req.query.url;
|
||
if (!siteUrl) return res.json({ error: "Missing URL" });
|
||
|
||
const html = await fetchHTML(siteUrl);
|
||
if (!html) return res.json({ name: null, icon: null });
|
||
|
||
const $ = cheerio.load(html);
|
||
|
||
// -----------------------------------------
|
||
// 1. Trova il nome più corto
|
||
// -----------------------------------------
|
||
let names = [];
|
||
|
||
const title = $("title").text().trim();
|
||
if (title) names.push(title);
|
||
|
||
$('meta[name="application-name"]').each((i, el) => {
|
||
const v = $(el).attr("content");
|
||
if (v) names.push(v.trim());
|
||
});
|
||
|
||
$('meta[property="og:site_name"]').each((i, el) => {
|
||
const v = $(el).attr("content");
|
||
if (v) names.push(v.trim());
|
||
});
|
||
|
||
const shortestName = names.length
|
||
? names.sort((a, b) => a.length - b.length)[0]
|
||
: null;
|
||
|
||
// -----------------------------------------
|
||
// 2. Trova l’icona più grande
|
||
// -----------------------------------------
|
||
let icons = [];
|
||
|
||
$('link[rel="icon"], link[rel="shortcut icon"], link[rel="apple-touch-icon"], link[rel="apple-touch-icon-precomposed"]').each((i, el) => {
|
||
const href = $(el).attr("href");
|
||
if (!href) return;
|
||
|
||
const sizeAttr = $(el).attr("sizes");
|
||
let size = 0;
|
||
|
||
if (sizeAttr && sizeAttr.includes("x")) {
|
||
const parts = sizeAttr.split("x");
|
||
size = parseInt(parts[0]) || 0;
|
||
}
|
||
|
||
icons.push({
|
||
url: normalize(siteUrl, href),
|
||
size
|
||
});
|
||
});
|
||
|
||
// fallback favicon
|
||
icons.push({
|
||
url: normalize(siteUrl, "/favicon.ico"),
|
||
size: 16
|
||
});
|
||
|
||
// Ordina per dimensione
|
||
icons = icons.filter(i => i.url);
|
||
icons.sort((a, b) => b.size - a.size);
|
||
|
||
const bestIcon = icons.length ? icons[0].url : null;
|
||
|
||
res.json({
|
||
name: shortestName,
|
||
icon: bestIcon
|
||
});
|
||
});
|
||
|
||
export default router;
|