Files
dockhand/routes/api/containers/check-updates/+server.ts
Jarek Krochmalski 62e3c6439e Initial commit
2025-12-28 21:16:03 +01:00

112 lines
3.1 KiB
TypeScript

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<UpdateCheckResult> => {
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 });
}
};