nakama/backend/routes/search.js

248 lines
7.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express');
const router = express.Router();
const axios = require('axios');
const { authenticate } = require('../middleware/auth');
const config = require('../config');
// Функция для создания прокси 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,
api_key: config.gelbooruApiKey,
user_id: config.gelbooruUserId
},
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,
api_key: config.gelbooruApiKey,
user_id: config.gelbooruUserId
},
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;