import type { APIRoute } from 'astro'; import crypto from 'crypto'; const POCKETBASE_URL = import.meta.env.POCKETBASE_URL || 'http://127.0.0.1:8090'; const POCKETBASE_ID_REGEX = /^[a-z0-9]{15}$/; function getClientIp(request: Request): string { const forwarded = request.headers.get('x-forwarded-for'); if (forwarded) { return forwarded.split(',')[0].trim(); } return request.headers.get('x-real-ip') || 'unknown'; } function generateVisitorHash(ip: string, userAgent: string): string { return crypto.createHash('sha256').update(ip + userAgent).digest('hex').slice(0, 32); } export const POST: APIRoute = async ({ request, url }) => { try { const postId = url.searchParams.get('postId'); if (!postId || !POCKETBASE_ID_REGEX.test(postId)) { return new Response( JSON.stringify({ error: 'Некорректный postId' }), { status: 400, headers: { 'Content-Type': 'application/json' } } ); } const postRes = await fetch( `${POCKETBASE_URL}/api/collections/posts/records/${postId}`, ); if (!postRes.ok) { return new Response( JSON.stringify({ error: 'Пост не найден' }), { status: 404, headers: { 'Content-Type': 'application/json' } } ); } const post = await postRes.json(); const ip = getClientIp(request); const userAgent = request.headers.get('user-agent') || 'unknown'; const visitorHash = generateVisitorHash(ip, userAgent); const now = new Date(); const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000); const yesterdayStr = yesterday.toISOString(); const existingViewRes = await fetch( `${POCKETBASE_URL}/api/collections/post_views/records?` + new URLSearchParams({ filter: `post="${postId}" && visitor_hash="${visitorHash}" && created >= "${yesterdayStr}"`, }) ); let isNewView = false; if (existingViewRes.ok) { const existingData = await existingViewRes.json(); if (existingData.items?.length === 0) { isNewView = true; await fetch( `${POCKETBASE_URL}/api/collections/post_views/records`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ post: postId, visitor_hash: visitorHash, ip: ip, user_agent: userAgent, }), } ); } } let totalViews = post.views || 0; if (isNewView) { totalViews += 1; await fetch( `${POCKETBASE_URL}/api/collections/posts/records/${postId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ views: totalViews }), } ); } return new Response( JSON.stringify({ views: totalViews, isNewView }), { status: 200, headers: { 'Content-Type': 'application/json' } } ); } catch (error) { console.error('[Increment Views API] Error:', error); return new Response( JSON.stringify({ error: 'Внутренняя ошибка сервера' }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } };