import { json } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; import { authorize } from '$lib/server/authorize'; import { listContainers, inspectContainer, checkImageUpdateAvailable } from '$lib/server/docker'; import { clearPendingContainerUpdates, addPendingContainerUpdate } from '$lib/server/db'; export interface UpdateCheckResult { containerId: string; containerName: string; imageName: string; hasUpdate: boolean; currentDigest?: string; newDigest?: string; error?: string; isLocalImage?: boolean; } /** * Check all containers for available image updates. * Returns all results at once after checking in parallel. */ export const POST: RequestHandler = async ({ url, cookies }) => { const auth = await authorize(cookies); const envId = url.searchParams.get('env'); const envIdNum = envId ? parseInt(envId) : undefined; // Need at least view permission if (auth.authEnabled && !await auth.can('containers', 'view', envIdNum)) { return json({ error: 'Permission denied' }, { status: 403 }); } try { // Clear existing pending updates for this environment before checking if (envIdNum) { await clearPendingContainerUpdates(envIdNum); } const containers = await listContainers(true, envIdNum); // Check container for updates const checkContainer = async (container: typeof containers[0]): Promise => { try { // Get container's image name from config const inspectData = await inspectContainer(container.id, envIdNum) as any; const imageName = inspectData.Config?.Image; const currentImageId = inspectData.Image; if (!imageName) { return { containerId: container.id, containerName: container.name, imageName: container.image, hasUpdate: false, error: 'Could not determine image name' }; } // Use shared update detection function const result = await checkImageUpdateAvailable(imageName, currentImageId, envIdNum); return { containerId: container.id, containerName: container.name, imageName, hasUpdate: result.hasUpdate, currentDigest: result.currentDigest, newDigest: result.registryDigest, error: result.error, isLocalImage: result.isLocalImage }; } catch (error: any) { return { containerId: container.id, containerName: container.name, imageName: container.image, hasUpdate: false, error: error.message }; } }; // Check all containers in parallel const results = await Promise.all(containers.map(checkContainer)); const updatesFound = results.filter(r => r.hasUpdate).length; // Save containers with updates to the database for persistence if (envIdNum) { for (const result of results) { if (result.hasUpdate) { await addPendingContainerUpdate( envIdNum, result.containerId, result.containerName, result.imageName ); } } } return json({ total: containers.length, updatesFound, results }); } catch (error: any) { console.error('Error checking for updates:', error); return json({ error: 'Failed to check for updates', details: error.message }, { status: 500 }); } };