// app/api/proxy/[...path]/route.ts // API Proxy для обращения к Marzban серверу подписок // ТОЛЬКО разрешённые пути: /sub/{token}/* import { logger } from '@/lib/logger'; // Белый список разрешённых path-паттернов (regex) const ALLOWED_PATHS = [ /^sub\/[a-zA-Z0-9_-]+\/?$/, // /sub/{token}/ /^sub\/[a-zA-Z0-9_-]+\/info$/, // /sub/{token}/info /^sub\/[a-zA-Z0-9_-]+\/usage$/, // /sub/{token}/usage /^sub\/[a-zA-Z0-9_-]+\/(sing-box|clash-meta|clash|outline|v2ray|v2ray-json)$/, // configs ]; function isPathAllowed(path: string): boolean { return ALLOWED_PATHS.some((pattern) => pattern.test(path)); } export async function GET( request: Request, { params }: { params: { path: string[] } } ) { const path = params.path.join('/'); const baseUrl = 'https://umbrix2.3to3.sbs'; // Проверяем что путь в белом списке if (!isPathAllowed(path)) { logger.warn(`[Proxy] Blocked path: ${path}`); return Response.json( { error: 'Forbidden: path not allowed' }, { status: 403 } ); } const url = `${baseUrl}/${path}`; logger.debug('[Proxy] Fetching:', url); try { const response = await fetch(url, { headers: { 'Accept': 'application/json', 'User-Agent': 'Umbrix-TelegramBot/1.0', }, cache: 'no-store', }); if (!response.ok) { logger.error('[Proxy] HTTP error:', response.status); throw new Error(`HTTP error! status: ${response.status}`); } const contentType = response.headers.get('content-type'); if (contentType?.includes('application/json')) { const data = await response.json(); return Response.json(data); } const text = await response.text(); return new Response(text, { headers: { 'Content-Type': contentType || 'text/plain', }, }); } catch (error) { logger.error('[Proxy] Error:', error); return Response.json( { error: 'Failed to fetch data from subscription server' }, { status: 500 } ); } } // POST запрещён — нет причин для POST к серверу подписок export async function POST() { return Response.json( { error: 'Method not allowed' }, { status: 405 } ); }