Новые правки в компоенты

This commit is contained in:
Web-serfer 2026-04-18 18:25:10 +05:00
parent e85d1ce668
commit 6f727aae7b
23 changed files with 1483 additions and 37 deletions

View file

@ -0,0 +1,248 @@
import type { APIRoute } from "astro";
const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || "http://localhost:8090";
export const GET: APIRoute = async ({ url, cookies }) => {
const postSlug = url.searchParams.get("post_slug");
const parent = url.searchParams.get("parent");
if (!postSlug) {
return new Response(
JSON.stringify({ error: "post_slug required" }),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
const pbAuthCookie = cookies.get("pb_auth")?.value;
const token = pbAuthCookie?.trim();
// Фильтр: комментарии для поста + статус published
let filter = `post_slug = "${postSlug}" && status = "published"`;
// Если parent = null — это основные комментарии (не ответы)
// Если parent указан — это ответы на конкретный комментарий
if (parent === "null" || parent === null) {
filter += ` && parent = ""`;
} else if (parent) {
filter += ` && parent = "${parent}"`;
}
const expand = "user";
const sort = "-created";
try {
const response = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records?expand=${expand}&filter=${encodeURIComponent(filter)}&sort=${sort}`,
{
headers: token ? { Authorization: `Bearer ${token}` } : {},
}
);
const data = await response.json();
return new Response(JSON.stringify(data), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} catch (error) {
console.error("[Comments API GET] Error:", error);
return new Response(
JSON.stringify({ error: "Failed to fetch comments" }),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
}
};
export const POST: APIRoute = async ({ request, cookies }) => {
const pbAuthCookie = cookies.get("pb_auth")?.value;
const token = pbAuthCookie?.trim();
console.log("[Comments API POST] Токен получен:", !!token);
console.log("[Comments API POST] Длина токена:", token?.length);
console.log("[Comments API POST] Cookie:", pbAuthCookie ? "да" : "нет");
if (!token) {
console.log("[Comments API POST] Токен не найден - 401");
return new Response(
JSON.stringify({ error: "Unauthorized" }),
{
status: 401,
headers: { "Content-Type": "application/json" },
}
);
}
// Получаем данные пользователя из токена
let userId: string | null = null;
try {
const userResponse = await fetch(
`${POCKETBASE_URL}/api/collections/users/auth-refresh`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
}
);
if (userResponse.ok) {
const userData = await userResponse.json();
userId = userData.record?.id;
}
} catch (e) {
console.error("[Comments API POST] Не удалось получить данные пользователя:", e);
}
if (!userId) {
console.log("[Comments API POST] Не удалось получить ID пользователя");
return new Response(
JSON.stringify({ error: "Unauthorized" }),
{
status: 401,
headers: { "Content-Type": "application/json" },
}
);
}
try {
const body = await request.json();
const { content, post_slug, parent = null } = body;
// Валидация
if (!content || !post_slug) {
return new Response(
JSON.stringify({ error: "content and post_slug required" }),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
if (content.length < 10 || content.length > 2000) {
return new Response(
JSON.stringify({ error: "content must be between 10 and 2000 characters" }),
{
status: 400,
headers: { "Content-Type": "application/json" },
}
);
}
// Проверка: если это ответ на комментарий
if (parent) {
const parentCommentResponse = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records/${parent}`,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
if (parentCommentResponse.ok) {
const parentComment = await parentCommentResponse.json();
// Если родительский комментарий уже сам является ответом — запрещаем (только 2 уровня)
if (parentComment.parent && parentComment.parent !== "") {
return new Response(
JSON.stringify({ error: "Нельзя отвечать на ответы. Вы можете ответить только на основные комментарии." }),
{
status: 403,
headers: { "Content-Type": "application/json" },
}
);
}
// Нельзя отвечать на свой комментарий
if (parentComment.user === userId) {
return new Response(
JSON.stringify({ error: "Нельзя отвечать на свой собственный коммен<D0B5><D0BD>арий" }),
{
status: 403,
headers: { "Content-Type": "application/json" },
}
);
}
// Проверяем: если родительский комментарий уже имеет ответы — запрещаем (только 1 уровень ответов)
const existingRepliesResponse = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records?filter=parent="${parent}"&perPage=1`,
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
if (existingRepliesResponse.ok) {
const existingRepliesData = await existingRepliesResponse.json();
if (existingRepliesData.totalItems > 0) {
return new Response(
JSON.stringify({ error: "На этот комментарий уже есть ответ. Вы можете ответить только на основные комментарии." }),
{
status: 403,
headers: { "Content-Type": "application/json" },
}
);
}
}
} else {
return new Response(
JSON.stringify({ error: "Родительский комментарий не найден" }),
{
status: 404,
headers: { "Content-Type": "application/json" },
}
);
}
}
const response = await fetch(
`${POCKETBASE_URL}/api/collections/comments/records`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
content,
post_slug,
parent: parent || "",
status: "published",
user: userId, // Передаём ID текущего пользователя
}),
}
);
const data = await response.json();
console.log("[Comments API POST] Ответ PocketBase:", response.status, JSON.stringify(data));
if (!response.ok) {
return new Response(JSON.stringify(data), {
status: response.status,
headers: { "Content-Type": "application/json" },
});
}
return new Response(JSON.stringify(data), {
status: 201,
headers: { "Content-Type": "application/json" },
});
} catch (error) {
console.error("[Comments API POST] Error:", error);
return new Response(
JSON.stringify({ error: "Failed to create comment" }),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
}
};