import { txasupabase } from '../../../lib/txasupabase.js'; // 1. TẢI DANH SÁCH BÌNH LUẬN + DANH SÁCH BỊ CHẶN export async function GET({ request, cookies }) { const url = new URL(request.url); const slug = url.searchParams.get('slug'); const token = cookies.get('auth_token')?.value; let user = null; if (token) { try { user = JSON.parse(Buffer.from(token, 'base64').toString('utf-8')); } catch (e) {} } if (slug === 'all') { if (!user || user.role !== 'admin') { return new Response(JSON.stringify({ error: 'Bạn không có quyền truy cập dữ liệu này!' }), { status: 403, headers: { 'Content-Type': 'application/json' } }); } } else if (!slug) { return new Response(JSON.stringify({ error: 'Thiếu slug phim!' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } try { let query = txasupabase.supabase .from('comments') .select('*') .order('created_at', { ascending: false }); if (slug !== 'all') { query = query.eq('movie_slug', slug); } const { data: comments, error } = await query; if (error) throw error; const bannedUsers = await txasupabase.getSetting('banned_commenters') || []; // Nạp thông tin hồ sơ người dùng (profile) phục vụ cho popup khi hover const usernames = comments ? [...new Set(comments.map(c => c.username).filter(Boolean))] : []; const userProfiles = {}; if (usernames.length > 0) { // 1. Tải danh sách user (tỉnh, xã, phim yêu thích, avatar_url) const { data: users } = await txasupabase.supabase .from('users') .select('username, province, ward, favorites, avatar_url') .in('username', usernames); // 2. Tải số lượng bình luận của các user này trên toàn hệ thống const { data: allUserComments } = await txasupabase.supabase .from('comments') .select('username') .in('username', usernames); const userCommentsCounts = {}; if (allUserComments) { allUserComments.forEach(item => { userCommentsCounts[item.username] = (userCommentsCounts[item.username] || 0) + 1; }); } if (users) { users.forEach(u => { userProfiles[u.username] = { username: u.username, province: u.province || '', ward: u.ward || '', avatar_url: u.avatar_url || '', likesCount: Array.isArray(u.favorites) ? u.favorites.length : 0, commentsCount: userCommentsCounts[u.username] || 0 }; }); } } // Nếu tải cho admin, lấy thêm tên các phim để map hiển thị let movieMap = {}; if (slug === 'all') { const { data: dbMovies } = await txasupabase.supabase .from('movies') .select('slug, movie'); if (dbMovies) { dbMovies.forEach(m => { movieMap[m.slug] = m.movie?.name || m.slug; }); } } return new Response(JSON.stringify({ comments: comments || [], bannedUsers, userProfiles, movieMap }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (error) { return new Response(JSON.stringify({ error: 'Lỗi tải bình luận: ' + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } } // 2. GỬI BÌNH LUẬN MỚI export async function POST({ request, cookies }) { // Kiểm tra đăng nhập const token = cookies.get('auth_token')?.value; let user = null; if (token) { try { user = JSON.parse(Buffer.from(token, 'base64').toString('utf-8')); } catch (e) {} } if (!user) { return new Response(JSON.stringify({ error: 'Vui lòng đăng nhập trước khi bình luận!' }), { status: 401, headers: { 'Content-Type': 'application/json' } }); } try { // Kiểm tra xem người dùng có bị Admin chặn bình luận không const bannedUsers = await txasupabase.getSetting('banned_commenters') || []; if (Array.isArray(bannedUsers) && bannedUsers.includes(user.username)) { return new Response( JSON.stringify({ error: 'Tài khoản của bạn đã bị quản trị viên chặn bình luận trên hệ thống!' }), { status: 403, headers: { 'Content-Type': 'application/json' } } ); } const { movieSlug, content, isSpoiler, episodeName, serverName, movieType } = await request.json(); if (!movieSlug || !content) { return new Response(JSON.stringify({ error: 'Thiếu thông tin bình luận!' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } // Tự động làm sạch & chuẩn hóa tên tập phim trước khi lưu let finalEpisodeName = null; if (episodeName) { const isSingle = movieType === 'single' || movieType === 'movie'; if (isSingle) { finalEpisodeName = 'Full'; } else { const numMatch = String(episodeName).match(/\d+/); if (numMatch) { const num = parseInt(numMatch[0], 10); finalEpisodeName = `Tập ${num}`; } else { let cleaned = String(episodeName).trim().replace(/^tập\s*/i, ''); finalEpisodeName = `Tập ${cleaned}`; } } } const { data, error } = await txasupabase.supabase .from('comments') .insert({ movie_slug: movieSlug, username: user.username, content: content.trim(), is_spoiler: !!isSpoiler, episode_name: finalEpisodeName, server_name: serverName || null, likes: [], dislikes: [] }) .select(); if (error) throw error; return new Response(JSON.stringify({ message: 'Gửi bình luận thành công!', data: data?.[0] }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (error) { return new Response(JSON.stringify({ error: 'Lỗi gửi bình luận: ' + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } } // 3. TƯƠNG TÁC THÍCH / GHÉT & CẤM BÌNH LUẬN export async function PUT({ request, cookies }) { const token = cookies.get('auth_token')?.value; let user = null; if (token) { try { user = JSON.parse(Buffer.from(token, 'base64').toString('utf-8')); } catch (e) {} } if (!user) { return new Response(JSON.stringify({ error: 'Vui lòng đăng nhập!' }), { status: 401, headers: { 'Content-Type': 'application/json' } }); } try { const body = await request.json(); const { commentId, action } = body; if (!action) { return new Response(JSON.stringify({ error: 'Thiếu hành động tương tác!' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } // A. XỬ LÝ ADMIN CẤM / MỞ CHẶN NGƯỜI DÙNG if (action === 'ban' || action === 'unban') { if (user.role !== 'admin') { return new Response(JSON.stringify({ error: 'Bạn không có quyền quản trị để thực hiện!' }), { status: 403, headers: { 'Content-Type': 'application/json' } }); } const { targetUsername } = body; if (!targetUsername) { return new Response(JSON.stringify({ error: 'Thiếu tên tài khoản cần xử lý!' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } let bannedUsers = await txasupabase.getSetting('banned_commenters') || []; if (!Array.isArray(bannedUsers)) bannedUsers = []; if (action === 'ban') { if (!bannedUsers.includes(targetUsername)) { bannedUsers.push(targetUsername); } } else { bannedUsers = bannedUsers.filter(u => u !== targetUsername); } await txasupabase.saveSetting('banned_commenters', bannedUsers); return new Response(JSON.stringify({ message: action === 'ban' ? `Đã chặn bình luận của @${targetUsername} thành công!` : `Đã mở chặn bình luận cho @${targetUsername}!`, bannedUsers }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } // B. XỬ LÝ THÍCH / GHÉT BÌNH LUẬN if (!commentId || !['like', 'dislike'].includes(action)) { return new Response(JSON.stringify({ error: 'Hành động không hợp lệ!' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } const { data: comment, error: fetchError } = await txasupabase.supabase .from('comments') .select('*') .eq('id', commentId) .maybeSingle(); if (fetchError || !comment) { return new Response(JSON.stringify({ error: 'Bình luận không tồn tại!' }), { status: 404, headers: { 'Content-Type': 'application/json' } }); } let likes = comment.likes || []; let dislikes = comment.dislikes || []; const username = user.username; if (action === 'like') { if (likes.includes(username)) { // Tắt like likes = likes.filter(u => u !== username); } else { // Bật like, tắt dislike likes.push(username); dislikes = dislikes.filter(u => u !== username); } } else if (action === 'dislike') { if (dislikes.includes(username)) { // Tắt dislike dislikes = dislikes.filter(u => u !== username); } else { // Bật dislike, tắt like dislikes.push(username); likes = likes.filter(u => u !== username); } } const { data: updatedComment, error: updateError } = await txasupabase.supabase .from('comments') .update({ likes, dislikes }) .eq('id', commentId) .select() .single(); if (updateError) throw updateError; return new Response(JSON.stringify({ message: 'Tương tác thành công!', likesCount: updatedComment.likes?.length || 0, dislikesCount: updatedComment.dislikes?.length || 0, likes: updatedComment.likes || [], dislikes: updatedComment.dislikes || [] }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (error) { return new Response(JSON.stringify({ error: 'Lỗi xử lý tương tác: ' + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } } // 4. XÓA BÌNH LUẬN export async function DELETE({ request, cookies }) { const token = cookies.get('auth_token')?.value; let user = null; if (token) { try { user = JSON.parse(Buffer.from(token, 'base64').toString('utf-8')); } catch (e) {} } if (!user) { return new Response(JSON.stringify({ error: 'Vui lòng đăng nhập!' }), { status: 401, headers: { 'Content-Type': 'application/json' } }); } try { const { commentId } = await request.json(); if (!commentId) { return new Response(JSON.stringify({ error: 'Thiếu ID bình luận cần xóa!' }), { status: 400, headers: { 'Content-Type': 'application/json' } }); } // Lấy thông tin bình luận const { data: comment, error: fetchError } = await txasupabase.supabase .from('comments') .select('*') .eq('id', commentId) .maybeSingle(); if (fetchError || !comment) { return new Response(JSON.stringify({ error: 'Bình luận không tồn tại!' }), { status: 404, headers: { 'Content-Type': 'application/json' } }); } // Kiểm tra quyền xóa: Phải là chủ nhân bình luận hoặc là Admin if (comment.username !== user.username && user.role !== 'admin') { return new Response(JSON.stringify({ error: 'Bạn không có quyền xóa bình luận này!' }), { status: 403, headers: { 'Content-Type': 'application/json' } }); } const { error: deleteError = null } = await txasupabase.supabase .from('comments') .delete() .eq('id', commentId); if (deleteError) throw deleteError; return new Response(JSON.stringify({ message: 'Xóa bình luận thành công!' }), { status: 200, headers: { 'Content-Type': 'application/json' } }); } catch (error) { return new Response(JSON.stringify({ error: 'Lỗi xóa bình luận: ' + error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } }); } }