import { NextRequest, NextResponse } from 'next/server' import { validateSession, hasPermission } from '@/lib/auth' import connectToDatabase from '@/lib/mongodb' import { Server } from '@/lib/models' import { isValidObjectId } from '@/lib/input-validation' import { createAuditLog, getClientIP } from '@/lib/audit' import { getServerPath } from '@/lib/docker' import { readdir, stat, writeFile } from 'fs/promises' import path from 'path' // GET /api/servers/[id]/mods — List mods export async function GET( request: NextRequest, { params }: { params: Promise<{ id: string }> } ) { try { const session = await validateSession(request) if (!session) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } if (!hasPermission(session, 'mods:view')) { return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) } const { id } = await params if (!isValidObjectId(id)) { return NextResponse.json({ error: 'Invalid server ID' }, { status: 400 }) } await connectToDatabase() const server = await Server.findById(id) if (!server) { return NextResponse.json({ error: 'Server not found' }, { status: 404 }) } if (server.type !== 'forge' && server.type !== 'fabric') { return NextResponse.json({ error: 'Mods are only available for Forge/Fabric servers' }, { status: 400 }) } const modsDir = path.join(getServerPath(server._id.toString()), 'mods') const mods = [] try { const files = await readdir(modsDir) for (const file of files) { if (file.endsWith('.jar') || file.endsWith('.jar.disabled')) { const filePath = path.join(modsDir, file) const stats = await stat(filePath) const enabled = file.endsWith('.jar') && !file.endsWith('.jar.disabled') const name = file.replace(/\.jar(\.disabled)?$/, '') mods.push({ name, filename: file, size: stats.size, enabled, }) } } } catch { // Directory doesn't exist yet } return NextResponse.json({ success: true, data: mods }) } catch (error) { console.error('Fetch mods error:', error) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } } // POST /api/servers/[id]/mods — Upload a mod export async function POST( request: NextRequest, { params }: { params: Promise<{ id: string }> } ) { const clientIP = getClientIP(request) try { const session = await validateSession(request) if (!session) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } if (!hasPermission(session, 'mods:install')) { return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) } const { id } = await params if (!isValidObjectId(id)) { return NextResponse.json({ error: 'Invalid server ID' }, { status: 400 }) } await connectToDatabase() const server = await Server.findById(id) if (!server) { return NextResponse.json({ error: 'Server not found' }, { status: 404 }) } if (server.type !== 'forge' && server.type !== 'fabric') { return NextResponse.json({ error: 'Mods are only available for Forge/Fabric servers' }, { status: 400 }) } const formData = await request.formData() const file = formData.get('file') as File | null if (!file || !file.name.endsWith('.jar')) { return NextResponse.json({ error: 'A .jar file is required' }, { status: 400 }) } const modsDir = path.join(getServerPath(server._id.toString()), 'mods') const { execSync } = await import('child_process') execSync(`mkdir -p "${modsDir}"`) const buffer = Buffer.from(await file.arrayBuffer()) await writeFile(path.join(modsDir, file.name), buffer) await createAuditLog({ action: 'mod_installed', entityType: 'mod', entityName: file.name, userId: session._id, userName: session.username, userEmail: session.email, newValues: { serverId: server._id.toString(), filename: file.name }, clientIP, status: 'success', statusCode: 201, }) return NextResponse.json({ success: true, message: 'Mod uploaded. Restart the server to load it.' }, { status: 201 }) } catch (error) { console.error('Upload mod error:', error) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) } }