import type { APIRoute } from 'astro'; const PB_POCKETBASE_URL = import.meta.env.PB_POCKETBASE_URL || 'http://localhost:8090'; const PB_ADMIN_EMAIL = import.meta.env.PB_ADMIN_EMAIL || 'redibedi2019@gmail.com'; const PB_ADMIN_PASSWORD = import.meta.env.PB_ADMIN_PASSWORD || 'Stalin4444'; const PASSWORD_MIN_LENGTH = 8; const PASSWORD_MAX_LENGTH = 12; const PASSWORD_REGEX = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d_!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]+$/; async function getAdminToken(): Promise { const response = await fetch(`${PB_POCKETBASE_URL}/api/admins/auth-with-password`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identity: PB_ADMIN_EMAIL, password: PB_ADMIN_PASSWORD }) }); if (!response.ok) { throw new Error('Не удалось авторизоваться как админ'); } const data = await response.json(); return data.token; } async function getUserIdByResetToken(token: string): Promise { try { const adminToken = await getAdminToken(); const response = await fetch(`${PB_POCKETBASE_URL}/api/collections/users/records?filter=reset_token="${token}"&limit=1`, { headers: { 'Authorization': `Bearer ${adminToken}` } }); if (!response.ok) return null; const data = await response.json(); if (data.items && data.items.length > 0) { return data.items[0].id; } return null; } catch (e) { console.error('[CONFIRM_RESET] Error getting user by token:', e); return null; } } 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' } } ); } // Ищем пользователя по токену через Admin API const userId = await getUserIdByResetToken(token); if (!userId) { return new Response( JSON.stringify({ error: 'Неверный или истёкший токен сброса пароля' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } // Обновляем пароль через Admin API (обходит валидацию PB) const adminToken = await getAdminToken(); const updateResponse = await fetch(`${PB_POCKETBASE_URL}/api/collections/users/records/${userId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${adminToken}` }, body: JSON.stringify({ password: password, passwordConfirm: passwordConfirm, reset_token: '', // Очищаем токен reset_token_expires: '' }) }); if (!updateResponse.ok) { const updateData = await updateResponse.json(); console.log('[CONFIRM_RESET] Update error:', updateData); return new Response( JSON.stringify({ error: 'Не удалось обновить пароль' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } return new Response( JSON.stringify({ success: true, message: 'Пароль успешно изменён' }), { status: 200, 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' } } ); } };