From 93db3c10f0c57b293bb468beba11a04a56052e7a Mon Sep 17 00:00:00 2001 From: Anders Lehmann Pier Date: Sun, 22 Jun 2025 13:35:45 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=8C=20Add=20Docker=20API=20endpoints?= =?UTF-8?q?=20for=20container=20management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docker/containers/[id]/restart/route.js | 21 ++++++++++ app/api/docker/containers/[id]/start/route.js | 21 ++++++++++ app/api/docker/containers/[id]/stats/route.js | 41 +++++++++++++++++++ app/api/docker/containers/[id]/stop/route.js | 21 ++++++++++ app/api/docker/containers/route.js | 16 ++++++++ 5 files changed, 120 insertions(+) create mode 100644 app/api/docker/containers/[id]/restart/route.js create mode 100644 app/api/docker/containers/[id]/start/route.js create mode 100644 app/api/docker/containers/[id]/stats/route.js create mode 100644 app/api/docker/containers/[id]/stop/route.js create mode 100644 app/api/docker/containers/route.js diff --git a/app/api/docker/containers/[id]/restart/route.js b/app/api/docker/containers/[id]/restart/route.js new file mode 100644 index 0000000..acde547 --- /dev/null +++ b/app/api/docker/containers/[id]/restart/route.js @@ -0,0 +1,21 @@ +import { NextResponse } from 'next/server'; + +export async function POST(request, { params }) { + try { + const Docker = require('dockerode'); + const docker = new Docker({ socketPath: '/var/run/docker.sock' }); + const container = docker.getContainer(params.id); + await container.restart({ t: 10 }); // 10 second timeout + + return NextResponse.json({ + success: true, + message: 'Container restarted successfully' + }); + } catch (error) { + console.error('Failed to restart container:', error); + return NextResponse.json( + { error: error.message || 'Failed to restart container' }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/app/api/docker/containers/[id]/start/route.js b/app/api/docker/containers/[id]/start/route.js new file mode 100644 index 0000000..10597e4 --- /dev/null +++ b/app/api/docker/containers/[id]/start/route.js @@ -0,0 +1,21 @@ +import { NextResponse } from 'next/server'; + +export async function POST(request, { params }) { + try { + const Docker = require('dockerode'); + const docker = new Docker({ socketPath: '/var/run/docker.sock' }); + const container = docker.getContainer(params.id); + await container.start(); + + return NextResponse.json({ + success: true, + message: 'Container started successfully' + }); + } catch (error) { + console.error('Failed to start container:', error); + return NextResponse.json( + { error: error.message || 'Failed to start container' }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/app/api/docker/containers/[id]/stats/route.js b/app/api/docker/containers/[id]/stats/route.js new file mode 100644 index 0000000..16f05aa --- /dev/null +++ b/app/api/docker/containers/[id]/stats/route.js @@ -0,0 +1,41 @@ +import { NextResponse } from 'next/server'; + +export async function GET(request, { params }) { + try { + const Docker = require('dockerode'); + const docker = new Docker({ socketPath: '/var/run/docker.sock' }); + const container = docker.getContainer(params.id); + const stats = await container.stats({ stream: false }); + + // Calculate CPU and memory percentages + const cpuPercent = calculateCPUPercent(stats); + const memoryUsage = stats.memory_stats.usage || 0; + const memoryLimit = stats.memory_stats.limit || 0; + + return NextResponse.json({ + cpu: cpuPercent, + memory: Math.round(memoryUsage / 1024 / 1024), // Convert to MB + memoryLimit: Math.round(memoryLimit / 1024 / 1024) + }); + } catch (error) { + console.error('Failed to get container stats:', error); + return NextResponse.json( + { error: error.message || 'Failed to get container stats' }, + { status: 500 } + ); + } +} + +function calculateCPUPercent(stats) { + const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - + (stats.precpu_stats?.cpu_usage?.total_usage || 0); + const systemDelta = stats.cpu_stats.system_cpu_usage - + (stats.precpu_stats?.system_cpu_usage || 0); + + if (systemDelta > 0 && cpuDelta > 0) { + const cpuPercent = (cpuDelta / systemDelta) * + (stats.cpu_stats.online_cpus || 1) * 100; + return Math.round(cpuPercent * 100) / 100; + } + return 0; +} \ No newline at end of file diff --git a/app/api/docker/containers/[id]/stop/route.js b/app/api/docker/containers/[id]/stop/route.js new file mode 100644 index 0000000..0121a9b --- /dev/null +++ b/app/api/docker/containers/[id]/stop/route.js @@ -0,0 +1,21 @@ +import { NextResponse } from 'next/server'; + +export async function POST(request, { params }) { + try { + const Docker = require('dockerode'); + const docker = new Docker({ socketPath: '/var/run/docker.sock' }); + const container = docker.getContainer(params.id); + await container.stop({ t: 10 }); // 10 second timeout + + return NextResponse.json({ + success: true, + message: 'Container stopped successfully' + }); + } catch (error) { + console.error('Failed to stop container:', error); + return NextResponse.json( + { error: error.message || 'Failed to stop container' }, + { status: 500 } + ); + } +} \ No newline at end of file diff --git a/app/api/docker/containers/route.js b/app/api/docker/containers/route.js new file mode 100644 index 0000000..89fcbb0 --- /dev/null +++ b/app/api/docker/containers/route.js @@ -0,0 +1,16 @@ +import { NextResponse } from 'next/server'; + +export async function GET() { + try { + const Docker = require('dockerode'); + const docker = new Docker({ socketPath: '/var/run/docker.sock' }); + const containers = await docker.listContainers({ all: true }); + return NextResponse.json(containers); + } catch (error) { + console.error('Docker API Error:', error); + return NextResponse.json( + { error: 'Failed to fetch containers: ' + error.message }, + { status: 500 } + ); + } +} \ No newline at end of file