import type { APIRoute } from 'astro'; const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://localhost:8090'; const PASSWORD_MIN_LENGTH = 8; const PASSWORD_MAX_LENGTH = 12; const PASSWORD_REGEX = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+$/; function validatePassword(password: string): { valid: boolean; error?: string } { if (!password || password.length < PASSWORD_MIN_LENGTH) { return { valid: false, error: 'Пароль должен быть не менее 8 символов' }; } if (password.length > PASSWORD_MAX_LENGTH) { return { valid: false, error: 'Пароль не должен превышать 12 символов' }; } if (!PASSWORD_REGEX.test(password)) { return { valid: false, error: 'Пароль должен содержать хотя бы одну букву и одну цифру' }; } return { valid: true }; } 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 { token, password, passwordConfirm } = body; if (!token) { return new Response( JSON.stringify({ error: 'Токен сброса пароля обязателен' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } if (!password || !passwordConfirm) { return new Response( JSON.stringify({ error: 'Пароль и подтверждение обязательны' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } if (password !== passwordConfirm) { return new Response( JSON.stringify({ error: 'Пароли не совпадают' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } const passwordValidation = validatePassword(password); if (!passwordValidation.valid) { return new Response( JSON.stringify({ error: passwordValidation.error }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } const confirmUrl = `${POCKETBASE_URL}/api/collections/users/confirm-password-reset`; const response = await fetch(confirmUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token, password, passwordConfirm }), }); 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('[CONFIRM_RESET] Error:', error); return new Response( JSON.stringify({ error: 'Внутренняя ошибка сервера' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } };