2023-12-12 03:56:36 +02:00

109 lines
3.3 KiB
JavaScript

const fetch = require('node-fetch').default;
const express = require('express');
const { readSecret, SECRET_KEYS } = require('./secrets');
const { jsonParser } = require('../express-common');
const router = express.Router();
// Cosplay as Firefox
const visitHeaders = {
'Accept': 'text/html',
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:120.0) Gecko/20100101 Firefox/120.0',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
'TE': 'trailers',
'DNT': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
};
router.post('/search', jsonParser, async (request, response) => {
try {
const key = readSecret(SECRET_KEYS.SERPAPI);
if (!key) {
console.log('No SerpApi key found');
return response.sendStatus(400);
}
const { query } = request.body;
const result = await fetch(`https://serpapi.com/search.json?q=${encodeURIComponent(query)}&api_key=${key}`);
if (!result.ok) {
const text = await result.text();
console.log('SerpApi request failed', result.statusText, text);
return response.status(500).send(text);
}
const data = await result.json();
return response.json(data);
} catch (error) {
console.log(error);
return response.sendStatus(500);
}
});
router.post('/visit', jsonParser, async (request, response) => {
try {
const url = request.body.url;
if (!url) {
console.log('No url provided for /visit');
return response.sendStatus(400);
}
try {
const urlObj = new URL(url);
// Reject relative URLs
if (urlObj.protocol === null || urlObj.host === null) {
throw new Error('Invalid URL format');
}
// Reject non-HTTP URLs
if (urlObj.protocol !== 'http:' && urlObj.protocol !== 'https:') {
throw new Error('Invalid protocol');
}
// Reject URLs with a non-standard port
if (urlObj.port !== '') {
throw new Error('Invalid port');
}
// Reject IP addresses
if (urlObj.hostname.match(/^\d+\.\d+\.\d+\.\d+$/)) {
throw new Error('Invalid hostname');
}
} catch (error) {
console.log('Invalid url provided for /visit', url);
return response.sendStatus(400);
}
const result = await fetch(url, { headers: visitHeaders });
if (!result.ok) {
console.log(`Visit failed ${result.status} ${result.statusText}`);
return response.sendStatus(500);
}
const contentType = String(result.headers.get('content-type'));
if (!contentType.includes('text/html')) {
console.log(`Visit failed, content-type is ${contentType}, expected text/html`);
return response.sendStatus(500);
}
const text = await result.text();
return response.send(text);
} catch (error) {
console.log(error);
return response.sendStatus(500);
}
});
module.exports = { router };