Spaces:
Running
Running
| import { NextResponse } from 'next/server' | |
| import fs from 'fs' | |
| import path from 'path' | |
| // Use /tmp for temporary message storage | |
| // Messages will reset on rebuilds, which is fine for a global chat | |
| const dataFilePath = path.join('/tmp', 'messages.json') | |
| // Simple bad word list (expand as needed) | |
| const BAD_WORDS = [ | |
| // English | |
| 'badword', 'spam', 'toxic', 'hate', 'violence', 'kill', 'stupid', 'idiot', | |
| 'fuck', 'fuxk', 'fck', 'shit', 'sh!t', 'bitch', 'asshole', 'damn', 'hell', | |
| 'crap', 'piss', 'dick', 'cock', 'pussy', 'bastard', 'slut', 'whore', | |
| // Spanish | |
| 'puto', 'mierda', 'coño', 'cabron', 'pendejo', 'joder', | |
| // French | |
| 'merde', 'putain', 'connard', 'salope', 'encule', | |
| // German | |
| 'scheisse', 'arschloch', 'schlampe', 'fotze', | |
| // Italian | |
| 'cazzo', 'merda', 'vaffanculo', 'stronzo', | |
| // Portuguese | |
| 'porra', 'caralho', 'merda', 'puta', | |
| // Russian (transliterated) | |
| 'cyka', 'blyat', 'nahui', 'pizda', | |
| // Hindi (transliterated) | |
| 'madarchod', 'bhenchod', 'chutiya', 'kutta', 'kamina' | |
| ] | |
| interface Message { | |
| id: string | |
| text: string | |
| sender: string | |
| userId: string | |
| timestamp: number | |
| } | |
| const getMessages = (): Message[] => { | |
| try { | |
| if (!fs.existsSync(dataFilePath)) { | |
| fs.writeFileSync(dataFilePath, '[]', 'utf8') | |
| return [] | |
| } | |
| const fileData = fs.readFileSync(dataFilePath, 'utf8') | |
| return JSON.parse(fileData) | |
| } catch (error) { | |
| console.error('Error reading messages:', error) | |
| return [] | |
| } | |
| } | |
| const saveMessages = (messages: Message[]) => { | |
| try { | |
| fs.writeFileSync(dataFilePath, JSON.stringify(messages, null, 2), 'utf8') | |
| } catch (error) { | |
| console.error('Error saving messages:', error) | |
| } | |
| } | |
| export async function GET() { | |
| const messages = getMessages() | |
| return NextResponse.json(messages, { | |
| headers: { 'Content-Type': 'application/json; charset=utf-8' } | |
| }) | |
| } | |
| export async function POST(request: Request) { | |
| try { | |
| const body = await request.json() | |
| const { text, sender, userId } = body | |
| // 1. Validation: Length | |
| if (!text || text.length > 200) { | |
| return NextResponse.json({ error: 'Message too long (max 200 chars)' }, { status: 400 }) | |
| } | |
| if (!text.trim()) { | |
| return NextResponse.json({ error: 'Message cannot be empty' }, { status: 400 }) | |
| } | |
| // 2. Validation: Toxicity (whole word matching only) | |
| const lowerText = text.toLowerCase() | |
| const containsBadWord = BAD_WORDS.some(word => { | |
| // Use word boundaries to match whole words only | |
| const regex = new RegExp(`\\b${word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i') | |
| return regex.test(text) | |
| }) | |
| if (containsBadWord) { | |
| return NextResponse.json({ error: 'Message contains inappropriate content' }, { status: 400 }) | |
| } | |
| const messages = getMessages() | |
| const now = Date.now() | |
| // 3. Validation: Spam (Rate Limiting) | |
| // Check if this user sent a message in the last 2 seconds | |
| const lastMessageFromUser = messages | |
| .filter(m => m.userId === userId) | |
| .sort((a, b) => b.timestamp - a.timestamp)[0] | |
| if (lastMessageFromUser && (now - lastMessageFromUser.timestamp) < 2000) { | |
| return NextResponse.json({ error: 'You are sending messages too fast. Please wait.' }, { status: 429 }) | |
| } | |
| // Check for duplicate message from same user | |
| if (lastMessageFromUser && lastMessageFromUser.text === text) { | |
| return NextResponse.json({ error: 'Do not send duplicate messages.' }, { status: 400 }) | |
| } | |
| const newMessage: Message = { | |
| id: Math.random().toString(36).substring(2, 15), | |
| text: text.trim(), | |
| sender: sender || 'Anonymous', | |
| userId: userId, | |
| timestamp: now | |
| } | |
| // Keep only last 100 messages to prevent file from growing too large | |
| const updatedMessages = [...messages, newMessage].slice(-100) | |
| saveMessages(updatedMessages) | |
| return NextResponse.json(updatedMessages, { | |
| headers: { 'Content-Type': 'application/json; charset=utf-8' } | |
| }) | |
| } catch (error) { | |
| console.error('Error processing message:', error) | |
| return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }) | |
| } | |
| } | |