mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-15 03:30:39 +01:00
109 lines
3.3 KiB
JavaScript
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 };
|