mirror of
https://github.com/rmoren97/mc-manager.git
synced 2026-03-28 17:26:47 -07:00
151 lines
4.5 KiB
TypeScript
151 lines
4.5 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import { validateSession, hasServerPermission } from '@/lib/auth'
|
|
import connectToDatabase from '@/lib/mongodb'
|
|
import { Server } from '@/lib/models'
|
|
import { isValidObjectId } from '@/lib/input-validation'
|
|
import { getContainerByName } from '@/lib/docker'
|
|
|
|
// GET /api/servers/[id]/console — SSE log stream
|
|
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 })
|
|
}
|
|
|
|
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 })
|
|
}
|
|
|
|
const adminIds = server.admins.map((a) => a.toString())
|
|
if (!hasServerPermission(session, 'servers:console', adminIds)) {
|
|
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
|
|
}
|
|
|
|
const container = await getContainerByName(`mc-${server._id}`)
|
|
if (!container) {
|
|
return NextResponse.json({ error: 'Container not found' }, { status: 404 })
|
|
}
|
|
|
|
const logStream = await container.logs({
|
|
follow: true,
|
|
stdout: true,
|
|
stderr: true,
|
|
tail: 100,
|
|
timestamps: true,
|
|
})
|
|
|
|
const encoder = new TextEncoder()
|
|
|
|
const stream = new ReadableStream({
|
|
start(controller) {
|
|
logStream.on('data', (chunk: Buffer) => {
|
|
// Docker log lines have 8-byte header, strip it
|
|
const line = chunk.slice(8).toString('utf-8').trim()
|
|
if (line) {
|
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ line })}\n\n`))
|
|
}
|
|
})
|
|
|
|
logStream.on('end', () => {
|
|
controller.close()
|
|
})
|
|
|
|
logStream.on('error', () => {
|
|
controller.close()
|
|
})
|
|
},
|
|
cancel() {
|
|
(logStream as NodeJS.ReadableStream & { destroy?: () => void }).destroy?.()
|
|
},
|
|
})
|
|
|
|
return new Response(stream, {
|
|
headers: {
|
|
'Content-Type': 'text/event-stream',
|
|
'Cache-Control': 'no-cache',
|
|
Connection: 'keep-alive',
|
|
},
|
|
})
|
|
} catch (error) {
|
|
console.error('Console stream error:', error)
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
}
|
|
}
|
|
|
|
// POST /api/servers/[id]/console — Send command
|
|
export async function POST(
|
|
request: NextRequest,
|
|
{ params }: { params: Promise<{ id: string }> }
|
|
) {
|
|
try {
|
|
const session = await validateSession(request)
|
|
if (!session) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
}
|
|
|
|
const { id } = await params
|
|
if (!isValidObjectId(id)) {
|
|
return NextResponse.json({ error: 'Invalid server ID' }, { status: 400 })
|
|
}
|
|
|
|
const { command } = await request.json()
|
|
if (!command || typeof command !== 'string') {
|
|
return NextResponse.json({ error: 'Command is required' }, { status: 400 })
|
|
}
|
|
|
|
await connectToDatabase()
|
|
const server = await Server.findById(id)
|
|
if (!server) {
|
|
return NextResponse.json({ error: 'Server not found' }, { status: 404 })
|
|
}
|
|
|
|
const adminIds = server.admins.map((a) => a.toString())
|
|
if (!hasServerPermission(session, 'servers:console', adminIds)) {
|
|
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
|
|
}
|
|
|
|
if (server.status !== 'online') {
|
|
return NextResponse.json({ error: 'Server is not running' }, { status: 400 })
|
|
}
|
|
|
|
const container = await getContainerByName(`mc-${server._id}`)
|
|
if (!container) {
|
|
return NextResponse.json({ error: 'Container not found' }, { status: 404 })
|
|
}
|
|
|
|
// Send command to server stdin via mc-send-to-console
|
|
const exec = await container.exec({
|
|
Cmd: ['mc-send-to-console', command.trim()],
|
|
AttachStdout: true,
|
|
AttachStderr: true,
|
|
User: '1000',
|
|
})
|
|
|
|
const execStream = await exec.start({ Detach: false })
|
|
|
|
// Wait for exec to finish
|
|
await new Promise<void>((resolve) => {
|
|
execStream.on('end', resolve)
|
|
execStream.on('error', () => resolve())
|
|
setTimeout(resolve, 3000)
|
|
})
|
|
|
|
// Output will appear in server stdout → SSE stream picks it up
|
|
return NextResponse.json({ success: true })
|
|
} catch (error) {
|
|
console.error('Console command error:', error)
|
|
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
|
|
}
|
|
}
|