mirror of
https://github.com/khoaliber/dockhand.git
synced 2026-03-03 13:18:56 +00:00
155 lines
4.2 KiB
TypeScript
155 lines
4.2 KiB
TypeScript
import { json } from '@sveltejs/kit';
|
|
import type { RequestHandler } from './$types';
|
|
import { authorize } from '$lib/server/authorize';
|
|
import {
|
|
listContainers,
|
|
inspectContainer,
|
|
stopContainer,
|
|
removeContainer,
|
|
createContainer,
|
|
pullImage
|
|
} from '$lib/server/docker';
|
|
import { auditContainer } from '$lib/server/audit';
|
|
|
|
export interface BatchUpdateResult {
|
|
containerId: string;
|
|
containerName: string;
|
|
success: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
/**
|
|
* Batch update containers by recreating them with latest images.
|
|
* Expects JSON body: { containerIds: string[] }
|
|
*/
|
|
export const POST: RequestHandler = async (event) => {
|
|
const { url, cookies, request } = event;
|
|
const auth = await authorize(cookies);
|
|
|
|
const envId = url.searchParams.get('env');
|
|
const envIdNum = envId ? parseInt(envId) : undefined;
|
|
|
|
// Need create permission to recreate containers
|
|
if (auth.authEnabled && !await auth.can('containers', 'create', envIdNum)) {
|
|
return json({ error: 'Permission denied' }, { status: 403 });
|
|
}
|
|
|
|
try {
|
|
const body = await request.json();
|
|
const { containerIds } = body as { containerIds: string[] };
|
|
|
|
if (!containerIds || !Array.isArray(containerIds) || containerIds.length === 0) {
|
|
return json({ error: 'containerIds array is required' }, { status: 400 });
|
|
}
|
|
|
|
const results: BatchUpdateResult[] = [];
|
|
|
|
// Process containers sequentially to avoid resource conflicts
|
|
for (const containerId of containerIds) {
|
|
try {
|
|
const containers = await listContainers(true, envIdNum);
|
|
const container = containers.find(c => c.id === containerId);
|
|
|
|
if (!container) {
|
|
results.push({
|
|
containerId,
|
|
containerName: 'unknown',
|
|
success: false,
|
|
error: 'Container not found'
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// Get full container config
|
|
const inspectData = await inspectContainer(containerId, envIdNum) as any;
|
|
const wasRunning = inspectData.State.Running;
|
|
const config = inspectData.Config;
|
|
const hostConfig = inspectData.HostConfig;
|
|
const imageName = config.Image;
|
|
const containerName = container.name;
|
|
|
|
// Pull latest image first
|
|
try {
|
|
await pullImage(imageName, undefined, envIdNum);
|
|
} catch (pullError: any) {
|
|
results.push({
|
|
containerId,
|
|
containerName,
|
|
success: false,
|
|
error: `Pull failed: ${pullError.message}`
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// Stop container if running
|
|
if (wasRunning) {
|
|
await stopContainer(containerId, envIdNum);
|
|
}
|
|
|
|
// Remove old container
|
|
await removeContainer(containerId, true, envIdNum);
|
|
|
|
// Prepare port bindings
|
|
const ports: { [key: string]: { HostPort: string } } = {};
|
|
if (hostConfig.PortBindings) {
|
|
for (const [containerPort, bindings] of Object.entries(hostConfig.PortBindings)) {
|
|
if (bindings && (bindings as any[]).length > 0) {
|
|
ports[containerPort] = { HostPort: (bindings as any[])[0].HostPort || '' };
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create new container
|
|
const newContainer = await createContainer({
|
|
name: containerName,
|
|
image: imageName,
|
|
ports,
|
|
volumeBinds: hostConfig.Binds || [],
|
|
env: config.Env || [],
|
|
labels: config.Labels || {},
|
|
cmd: config.Cmd || undefined,
|
|
restartPolicy: hostConfig.RestartPolicy?.Name || 'no',
|
|
networkMode: hostConfig.NetworkMode || undefined
|
|
}, envIdNum);
|
|
|
|
// Start if was running
|
|
if (wasRunning) {
|
|
await newContainer.start();
|
|
}
|
|
|
|
// Audit log
|
|
await auditContainer(event, 'update', newContainer.id, containerName, envIdNum, { batchUpdate: true });
|
|
|
|
results.push({
|
|
containerId: newContainer.id,
|
|
containerName,
|
|
success: true
|
|
});
|
|
} catch (error: any) {
|
|
results.push({
|
|
containerId,
|
|
containerName: 'unknown',
|
|
success: false,
|
|
error: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
const successCount = results.filter(r => r.success).length;
|
|
const failCount = results.filter(r => !r.success).length;
|
|
|
|
return json({
|
|
success: failCount === 0,
|
|
results,
|
|
summary: {
|
|
total: results.length,
|
|
success: successCount,
|
|
failed: failCount
|
|
}
|
|
});
|
|
} catch (error: any) {
|
|
console.error('Error in batch update:', error);
|
|
return json({ error: 'Failed to batch update containers', details: error.message }, { status: 500 });
|
|
}
|
|
};
|