import type { APIRoute } from 'astro'; const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090'; const POCKETBASE_ID_REGEX = /^[a-z0-9]{15}$/; export const POST: APIRoute = async ({ request, cookies }) => { try { const token = cookies.get('pb_auth')?.value; if (!token) { return new Response( JSON.stringify({ error: 'Требуется авторизация' }), { status: 401, headers: { 'Content-Type': 'application/json' } } ); } const authResponse = await fetch( `${POCKETBASE_URL}/api/collections/users/auth-refresh`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, } ); if (!authResponse.ok) { return new Response( JSON.stringify({ error: 'Недействительная сессия' }), { status: 401, headers: { 'Content-Type': 'application/json' } } ); } const authData = await authResponse.json(); const userId = authData.record?.id; if (!userId || !POCKETBASE_ID_REGEX.test(userId)) { return new Response( JSON.stringify({ error: 'Ошибка идентификации пользователя' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } const body = await request.json(); const { review_id, vote_type } = body; if (!review_id || !POCKETBASE_ID_REGEX.test(review_id)) { return new Response( JSON.stringify({ error: 'Некорректный review_id' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } if (!vote_type || !['like', 'dislike'].includes(vote_type)) { return new Response( JSON.stringify({ error: 'Некорректный vote_type' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } const existingVoteRes = await fetch( `${POCKETBASE_URL}/api/collections/review_votes/records?` + new URLSearchParams({ filter: `review="${review_id}" && user="${userId}"`, }), { headers: { 'Authorization': `Bearer ${token}` }, } ); let method = 'POST'; let url = `${POCKETBASE_URL}/api/collections/review_votes/records`; let voteId = null; let userVote: 'like' | 'dislike' | null = null; if (existingVoteRes.ok) { const existingData = await existingVoteRes.json(); if (existingData.items?.length > 0) { const existing = existingData.items[0]; voteId = existing.id; if (existing.vote_type === vote_type) { method = 'DELETE'; url = `${POCKETBASE_URL}/api/collections/review_votes/records/${voteId}`; } else { method = 'PATCH'; url = `${POCKETBASE_URL}/api/collections/review_votes/records/${voteId}`; userVote = vote_type; } } } const voteBody = method === 'POST' ? JSON.stringify({ review: review_id, user: userId, vote_type }) : method === 'PATCH' ? JSON.stringify({ vote_type }) : null; const voteRes = await fetch(url, { method, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: voteBody, }); if (!voteRes.ok && method !== 'DELETE') { const errorText = await voteRes.text(); console.error('[ReviewVote API] Failed to save vote:', errorText); throw new Error('Failed to save vote'); } if (method === 'POST') userVote = vote_type; if (method === 'DELETE') userVote = null; const likesRes = await fetch( `${POCKETBASE_URL}/api/collections/review_votes/records?` + new URLSearchParams({ filter: `review="${review_id}"`, fields: 'vote_type', }) ); let likes = 0; let dislikes = 0; if (likesRes.ok) { const votesData = await likesRes.json(); likes = votesData.items.filter((v: any) => v.vote_type === 'like').length; dislikes = votesData.items.filter((v: any) => v.vote_type === 'dislike').length; } await fetch( `${POCKETBASE_URL}/api/collections/reviews/records/${review_id}`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ votesCount: likes }), } ); return new Response( JSON.stringify({ likes, dislikes, userVote }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } catch (error) { console.error('[ReviewVote API] Error:', error); return new Response( JSON.stringify({ error: 'Внутренняя ошибка сервера' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } }; export const GET: APIRoute = async ({ url, cookies }) => { try { const reviewId = url.searchParams.get('review_id'); const token = cookies.get('pb_auth')?.value; if (!reviewId || !POCKETBASE_ID_REGEX.test(reviewId)) { return new Response( JSON.stringify({ error: 'Некорректный review_id' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } const reviewRes = await fetch( `${POCKETBASE_URL}/api/collections/reviews/records/${reviewId}` ); if (!reviewRes.ok) { return new Response( JSON.stringify({ error: 'Отзыв не найден' }), { status: 404, headers: { 'Content-Type': 'application/json' } } ); } const votesRes = await fetch( `${POCKETBASE_URL}/api/collections/review_votes/records?` + new URLSearchParams({ filter: `review="${reviewId}"`, fields: 'vote_type', }) ); let likes = 0; let dislikes = 0; if (votesRes.ok) { const votesData = await votesRes.json(); likes = votesData.items.filter((v: any) => v.vote_type === 'like').length; dislikes = votesData.items.filter((v: any) => v.vote_type === 'dislike').length; } let userVote: 'like' | 'dislike' | null = null; if (token) { try { const authRes = await fetch( `${POCKETBASE_URL}/api/collections/users/auth-refresh`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, } ); if (authRes.ok) { const authData = await authRes.json(); const userId = authData.record?.id; if (userId && POCKETBASE_ID_REGEX.test(userId)) { const userVoteRes = await fetch( `${POCKETBASE_URL}/api/collections/review_votes/records?` + new URLSearchParams({ filter: `review="${reviewId}" && user="${userId}"`, }), { headers: { 'Authorization': `Bearer ${token}` }, } ); if (userVoteRes.ok) { const userVoteData = await userVoteRes.json(); if (userVoteData.items?.length > 0) { userVote = userVoteData.items[0].vote_type; } } } } } catch (e) { console.error('[ReviewVote API] Error fetching user vote:', e); } } return new Response( JSON.stringify({ likes, dislikes, userVote }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } catch (error) { console.error('[ReviewVote API GET] Error:', error); return new Response( JSON.stringify({ error: 'Внутренняя ошибка сервера' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } };