import type { APIRoute } from 'astro'; const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://localhost:8090'; const RATE_LIMIT_MAX_REQUESTS = 3; const RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000; const rateLimitStore = new Map(); function cleanOldRecords(email: string, now: number) { const timestamps = rateLimitStore.get(email) || []; const windowStart = now - RATE_LIMIT_WINDOW_MS; const validTimestamps = timestamps.filter(ts => ts > windowStart); if (validTimestamps.length === 0) { rateLimitStore.delete(email); } else { rateLimitStore.set(email, validTimestamps); } } function checkRateLimit(email: string): { allowed: boolean; remaining?: number; resetIn?: number } { const now = Date.now(); const normalizedEmail = email.toLowerCase().trim(); cleanOldRecords(normalizedEmail, now); const timestamps = rateLimitStore.get(normalizedEmail) || []; if (timestamps.length >= RATE_LIMIT_MAX_REQUESTS) { const oldestTimestamp = timestamps[0]; const resetIn = RATE_LIMIT_WINDOW_MS - (now - oldestTimestamp); return { allowed: false, resetIn }; } timestamps.push(now); rateLimitStore.set(normalizedEmail, timestamps); return { allowed: true, remaining: RATE_LIMIT_MAX_REQUESTS - timestamps.length }; } export const POST: APIRoute = async ({ request }) => { try { let body; try { body = await request.json(); } catch { return new Response( JSON.stringify({ error: 'Неверный формат данных' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } const { email } = body; if (!email) { return new Response( JSON.stringify({ error: 'Email обязателен' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } const rateLimitResult = checkRateLimit(email); if (!rateLimitResult.allowed) { const resetMinutes = Math.ceil((rateLimitResult.resetIn || 0) / 60000); return new Response( JSON.stringify({ error: 'Слишком много запросов. Попробуйте позже', retryAfter: resetMinutes }), { status: 429, headers: { 'Content-Type': 'application/json', 'Retry-After': String(resetMinutes * 60) } } ); } const resetUrl = `${PB_POCKETBASE_URL}/api/collections/users/request-password-reset`; const response = await fetch(resetUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }), }); if (response.status === 204) { return new Response( JSON.stringify({ success: true, message: 'Письмо для сброса пароля отправлено' }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } let data; try { data = await response.json(); } catch { return new Response( JSON.stringify({ error: 'Ошибка обработки ответа' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } return new Response( JSON.stringify({ error: data.message || 'Ошибка при отправке письма для сброса пароля' }), { status: response.status, headers: { 'Content-Type': 'application/json' } } ); } catch (error) { console.error('[PASSWORD_RESET] Error:', error); return new Response( JSON.stringify({ error: 'Внутренняя ошибка сервера' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } };