import axios from 'axios';
import * as cheerio from 'cheerio';
export interface WebPage {
url: string;
title: string;
content: string;
text: string;
links: string[];
}
export class WebService {
private allowedHosts: string[];
private userAgent: string;
constructor() {
this.allowedHosts = (process.env.WEB_ALLOWED_HOSTS || 'example.com,developer.mozilla.org')
.split(',')
.map(host => host.trim());
this.userAgent = 'AI-Ops-Hub/1.0 (MCP Server)';
}
async fetchPage(url: string): Promise<WebPage> {
try {
console.log(`🌐 Получение страницы: ${url}`);
// Проверяем разрешенные хосты
this.validateUrl(url);
// Получаем страницу
const response = await axios.get(url, {
headers: {
'User-Agent': this.userAgent,
},
timeout: 10000, // 10 секунд таймаут
});
// Парсим HTML
const $ = cheerio.load(response.data);
// Извлекаем основную информацию
const title = $('title').first().text().trim() || 'Без заголовка';
// Убираем скрипты, стили и другие ненужные элементы
$('script, style, noscript, iframe, img, svg').remove();
// Получаем чистый текст
const text = $('body').text()
.replace(/\s+/g, ' ')
.trim();
// Получаем ссылки
const links = $('a[href]')
.map((_, el) => $(el).attr('href'))
.get()
.filter(href => href && href.startsWith('http'))
.slice(0, 20); // Ограничиваем количество ссылок
const page: WebPage = {
url,
title,
content: response.data,
text: text.substring(0, 5000), // Ограничиваем размер текста
links,
};
console.log(`✅ Страница получена: ${title} (${text.length} символов, ${links.length} ссылок)`);
return page;
} catch (error) {
console.error('Ошибка получения страницы:', error);
throw new Error(`Ошибка получения страницы: ${error}`);
}
}
private validateUrl(url: string): void {
try {
const urlObj = new URL(url);
const host = urlObj.hostname.toLowerCase();
// Проверяем, что хост разрешен
const isAllowed = this.allowedHosts.some(allowedHost =>
host === allowedHost || host.endsWith('.' + allowedHost)
);
if (!isAllowed) {
throw new Error(`Хост ${host} не разрешен. Разрешенные хосты: ${this.allowedHosts.join(', ')}`);
}
// Проверяем протокол
if (urlObj.protocol !== 'http:' && urlObj.protocol !== 'https:') {
throw new Error('Поддерживаются только HTTP и HTTPS протоколы');
}
} catch (error) {
if (error instanceof Error) {
throw error;
}
throw new Error('Некорректный URL');
}
}
async isUrlAllowed(url: string): Promise<boolean> {
try {
this.validateUrl(url);
return true;
} catch {
return false;
}
}
getAllowedHosts(): string[] {
return [...this.allowedHosts];
}
}