const express = require('express'); const router = express.Router(); const axios = require('axios'); const { authenticate } = require('../middleware/auth'); // Функция для создания прокси URL function createProxyUrl(originalUrl) { if (!originalUrl) return null; // Кодируем URL в base64 const encodedUrl = Buffer.from(originalUrl).toString('base64'); return `/api/search/proxy/${encodedUrl}`; } // Эндпоинт для проксирования изображений router.get('/proxy/:encodedUrl', async (req, res) => { try { const { encodedUrl } = req.params; // Декодируем URL const originalUrl = Buffer.from(encodedUrl, 'base64').toString('utf-8'); // Проверяем, что URL от разрешенных доменов const allowedDomains = [ 'e621.net', 'static1.e621.net', 'gelbooru.com', 'img3.gelbooru.com', 'img2.gelbooru.com', 'img1.gelbooru.com', 'simg3.gelbooru.com', 'simg4.gelbooru.com' ]; const urlObj = new URL(originalUrl); if (!allowedDomains.some(domain => urlObj.hostname.includes(domain))) { return res.status(403).json({ error: 'Запрещенный домен' }); } // Запрашиваем изображение const response = await axios.get(originalUrl, { responseType: 'stream', headers: { 'User-Agent': 'NakamaSpace/1.0', 'Referer': urlObj.origin }, timeout: 30000 // 30 секунд таймаут }); // Копируем заголовки res.setHeader('Content-Type', response.headers['content-type']); res.setHeader('Cache-Control', 'public, max-age=86400'); // Кешируем на 24 часа if (response.headers['content-length']) { res.setHeader('Content-Length', response.headers['content-length']); } // Стримим изображение response.data.pipe(res); } catch (error) { console.error('Ошибка проксирования изображения:', error.message); res.status(500).json({ error: 'Ошибка загрузки изображения' }); } }); // e621 API поиск router.get('/furry', authenticate, async (req, res) => { try { const { query, limit = 50, page = 1 } = req.query; if (!query) { return res.status(400).json({ error: 'Параметр query обязателен' }); } const response = await axios.get('https://e621.net/posts.json', { params: { tags: query, limit, page }, headers: { 'User-Agent': 'NakamaSpace/1.0' } }); const posts = response.data.posts.map(post => ({ id: post.id, url: createProxyUrl(post.file.url), preview: createProxyUrl(post.preview.url), tags: post.tags.general, rating: post.rating, score: post.score.total, source: 'e621' })); res.json({ posts }); } catch (error) { console.error('Ошибка e621 API:', error); res.status(500).json({ error: 'Ошибка поиска' }); } }); // Gelbooru API поиск router.get('/anime', authenticate, async (req, res) => { try { const { query, limit = 50, page = 1 } = req.query; if (!query) { return res.status(400).json({ error: 'Параметр query обязателен' }); } const response = await axios.get('https://gelbooru.com/index.php', { params: { page: 'dapi', s: 'post', q: 'index', json: 1, tags: query, limit, pid: page }, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }, timeout: 30000 }); // Обработка разных форматов ответа Gelbooru let postsData = []; if (Array.isArray(response.data)) { postsData = response.data; } else if (response.data && response.data.post) { postsData = Array.isArray(response.data.post) ? response.data.post : [response.data.post]; } else if (response.data && Array.isArray(response.data)) { postsData = response.data; } const posts = postsData.map(post => ({ id: post.id, url: createProxyUrl(post.file_url), preview: createProxyUrl(post.preview_url || post.thumbnail_url || post.file_url), tags: post.tags ? (typeof post.tags === 'string' ? post.tags.split(' ') : post.tags) : [], rating: post.rating || 'unknown', score: post.score || 0, source: 'gelbooru' })); res.json({ posts }); } catch (error) { console.error('Ошибка Gelbooru API:', error.message); if (error.response) { console.error('Gelbooru ответ:', error.response.status, error.response.data); } res.status(500).json({ error: 'Ошибка поиска Gelbooru', details: error.message }); } }); // Автокомплит тегов для e621 router.get('/furry/tags', authenticate, async (req, res) => { try { const { query } = req.query; if (!query || query.length < 2) { return res.json({ tags: [] }); } const response = await axios.get('https://e621.net/tags.json', { params: { 'search[name_matches]': `${query}*`, 'search[order]': 'count', limit: 10 }, headers: { 'User-Agent': 'NakamaSpace/1.0' } }); const tags = response.data.map(tag => ({ name: tag.name, count: tag.post_count })); res.json({ tags }); } catch (error) { console.error('Ошибка получения тегов:', error); res.status(500).json({ error: 'Ошибка получения тегов' }); } }); // Автокомплит тегов для Gelbooru router.get('/anime/tags', authenticate, async (req, res) => { try { const { query } = req.query; if (!query || query.length < 2) { return res.json({ tags: [] }); } const response = await axios.get('https://gelbooru.com/index.php', { params: { page: 'dapi', s: 'tag', q: 'index', json: 1, name_pattern: `${query}%`, orderby: 'count', limit: 10 }, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }, timeout: 30000 }); // Обработка разных форматов ответа Gelbooru let tagsData = []; if (Array.isArray(response.data)) { tagsData = response.data; } else if (response.data && response.data.tag) { tagsData = Array.isArray(response.data.tag) ? response.data.tag : [response.data.tag]; } else if (response.data && Array.isArray(response.data)) { tagsData = response.data; } const tags = tagsData.map(tag => ({ name: tag.name || tag.tag || '', count: tag.count || tag.post_count || 0 })).filter(tag => tag.name); res.json({ tags }); } catch (error) { console.error('Ошибка получения тегов Gelbooru:', error.message); if (error.response) { console.error('Gelbooru ответ:', error.response.status, error.response.data); } // В случае ошибки возвращаем пустой массив вместо ошибки res.json({ tags: [] }); } }); module.exports = router;