69 lines
2.3 KiB
TypeScript
69 lines
2.3 KiB
TypeScript
import { Router } from 'express';
|
|
import axios from 'axios';
|
|
import * as cheerio from 'cheerio';
|
|
import { z } from 'zod';
|
|
|
|
const router = Router();
|
|
|
|
const previewSchema = z.object({
|
|
url: z.string().min(1)
|
|
});
|
|
|
|
router.post('/meta-preview', async (req, res) => {
|
|
try {
|
|
let { url } = previewSchema.parse(req.body);
|
|
|
|
// Add protocol if missing
|
|
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
url = `https://${url}`;
|
|
}
|
|
|
|
const response = await axios.get(url, {
|
|
headers: {
|
|
'User-Agent': 'Mozilla/5.0 (compatible; WebsiteMonitorBot/1.0; +https://websitemonitor.com)'
|
|
},
|
|
timeout: 5000,
|
|
validateStatus: (status) => status < 500 // Resolve even if 404/403 to avoid crashing flow immediately
|
|
});
|
|
|
|
const html = response.data;
|
|
const $ = cheerio.load(html);
|
|
|
|
const title = $('title').text() || $('meta[property="og:title"]').attr('content') || '';
|
|
const description = $('meta[name="description"]').attr('content') || $('meta[property="og:description"]').attr('content') || '';
|
|
|
|
// Attempt to find favicon
|
|
let favicon = '';
|
|
const linkIcon = $('link[rel="icon"], link[rel="shortcut icon"], link[rel="apple-touch-icon"]').attr('href');
|
|
if (linkIcon) {
|
|
if (linkIcon.startsWith('http')) {
|
|
favicon = linkIcon;
|
|
} else if (linkIcon.startsWith('//')) {
|
|
favicon = `https:${linkIcon}`;
|
|
} else {
|
|
const urlObj = new URL(url);
|
|
favicon = `${urlObj.protocol}//${urlObj.host}${linkIcon.startsWith('/') ? '' : '/'}${linkIcon}`;
|
|
}
|
|
} else {
|
|
const urlObj = new URL(url);
|
|
favicon = `${urlObj.protocol}//${urlObj.host}/favicon.ico`;
|
|
}
|
|
|
|
res.json({
|
|
title: title.trim(),
|
|
description: description.trim(),
|
|
favicon,
|
|
url: url
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Meta preview error:', error);
|
|
if (error instanceof z.ZodError) {
|
|
return res.status(400).json({ error: 'Invalid URL provided' });
|
|
}
|
|
res.status(500).json({ error: 'Failed to fetch page metadata' });
|
|
}
|
|
});
|
|
|
|
export const toolsRouter = router;
|