astro_avtourist/frontend/src/pages/api/reviews/vote.ts

225 lines
No EOL
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { APIRoute } from 'astro';
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090';
const PB_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: 'Нед<D0B5><D0B4>йствительная сессия' }),
{ status: 401, headers: { 'Content-Type': 'application/json' } }
);
}
const authData = await authResponse.json();
const userId = authData.record?.id;
if (!userId || !PB_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 || !PB_ID_REGEX.test(review_id)) {
return new Response(
JSON.stringify({ error: 'Некорректный review_id' }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
if (!vote_type || !['likes', 'dislikes'].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?filter=(review="${review_id}")&&(user="${userId}")`,
{
headers: { 'Authorization': `Bearer ${token}` },
}
);
let userVote: 'likes' | 'dislikes' | null = null;
let method = 'POST';
let url = `${POCKETBASE_URL}/api/collections/review_votes/records`;
let voteId: string | 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: vote_type })
: method === 'PATCH'
? JSON.stringify({ vote_type: vote_type })
: undefined;
const voteRes = await fetch(url, {
method,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': method !== 'DELETE' ? 'application/json' : undefined,
},
body: voteBody,
});
if (!voteRes.ok && method !== 'DELETE') {
const errorText = await voteRes.text();
console.error('[ReviewVote API] Vote error:', errorText);
throw new Error('Failed to save vote');
}
if (method === 'POST') userVote = vote_type;
if (method === 'DELETE') userVote = null;
const votesRes = await fetch(
`${POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${review_id}")`,
{ headers: { 'Authorization': `Bearer ${token}` } }
);
console.log('[ReviewVote API] Votes response:', votesRes.status);
let likes = 0;
let dislikes = 0;
if (votesRes.ok) {
const votesData = await votesRes.json();
console.log('[ReviewVote API] Votes data:', JSON.stringify(votesData));
likes = votesData.items?.filter((v: any) => v.vote_type === 'likes').length || 0;
dislikes = votesData.items?.filter((v: any) => v.vote_type === 'dislikes').length || 0;
} else {
const errorText = await votesRes.text();
console.error('[ReviewVote API] Votes error:', errorText);
}
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 || !PB_ID_REGEX.test(reviewId)) {
return new Response(
JSON.stringify({ error: 'Некорректный review_id' }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
const authHeaders = token ? { 'Authorization': `Bearer ${token}` } : {};
const votesRes = await fetch(
`${POCKETBASE_URL}/api/collections/review_votes/records?filter=(review="${reviewId}")`,
{ headers: authHeaders }
);
console.log('[ReviewVote API GET] Votes response:', votesRes.status);
let likes = 0;
let dislikes = 0;
if (votesRes.ok) {
const votesData = await votesRes.json();
console.log('[ReviewVote API GET] Votes data:', JSON.stringify(votesData));
likes = votesData.items?.filter((v: any) => v.vote_type === 'likes').length || 0;
dislikes = votesData.items?.filter((v: any) => v.vote_type === 'dislikes').length || 0;
}
let userVote: 'likes' | 'dislikes' | 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 && PB_ID_REGEX.test(userId)) {
const userVoteRes = await fetch(
`${POCKETBASE_URL}/api/collections/review_votes/records?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 GET] Error:', 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' } }
);
}
};