website-monitor/backend/src/routes/tools.ts

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;