Files
patchmon.net/backend/src/routes/integrationRoutes.js
Muhammad Ibrahim 5457a1e9bc Docker implementation
Profile fixes
Hostgroup fixes
TFA fixes
2025-10-31 15:24:53 +00:00

357 lines
10 KiB
JavaScript

const express = require("express");
const { getPrismaClient } = require("../config/prisma");
const { v4: uuidv4 } = require("uuid");
const prisma = getPrismaClient();
const router = express.Router();
// POST /api/v1/integrations/docker - Docker data collection endpoint
router.post("/docker", async (req, res) => {
try {
const apiId = req.headers["x-api-id"];
const apiKey = req.headers["x-api-key"];
const {
containers,
images,
volumes,
networks,
updates,
daemon_info: _daemon_info,
hostname,
machine_id,
agent_version: _agent_version,
} = req.body;
console.log(
`[Docker Integration] Received data from ${hostname || machine_id}`,
);
// Validate API credentials
const host = await prisma.hosts.findFirst({
where: { api_id: apiId, api_key: apiKey },
});
if (!host) {
console.warn("[Docker Integration] Invalid API credentials");
return res.status(401).json({ error: "Invalid API credentials" });
}
console.log(
`[Docker Integration] Processing for host: ${host.friendly_name}`,
);
const now = new Date();
// Helper function to validate and parse dates
const parseDate = (dateString) => {
if (!dateString) return now;
const date = new Date(dateString);
return Number.isNaN(date.getTime()) ? now : date;
};
let containersProcessed = 0;
let imagesProcessed = 0;
let volumesProcessed = 0;
let networksProcessed = 0;
let updatesProcessed = 0;
// Process containers
if (containers && Array.isArray(containers)) {
console.log(
`[Docker Integration] Processing ${containers.length} containers`,
);
for (const containerData of containers) {
const containerId = uuidv4();
// Find or create image
let imageId = null;
if (containerData.image_repository && containerData.image_tag) {
const image = await prisma.docker_images.upsert({
where: {
repository_tag_image_id: {
repository: containerData.image_repository,
tag: containerData.image_tag,
image_id: containerData.image_id || "unknown",
},
},
update: {
last_checked: now,
updated_at: now,
},
create: {
id: uuidv4(),
repository: containerData.image_repository,
tag: containerData.image_tag,
image_id: containerData.image_id || "unknown",
source: containerData.image_source || "docker-hub",
created_at: parseDate(containerData.created_at),
updated_at: now,
},
});
imageId = image.id;
}
// Upsert container
await prisma.docker_containers.upsert({
where: {
host_id_container_id: {
host_id: host.id,
container_id: containerData.container_id,
},
},
update: {
name: containerData.name,
image_id: imageId,
image_name: containerData.image_name,
image_tag: containerData.image_tag || "latest",
status: containerData.status,
state: containerData.state || containerData.status,
ports: containerData.ports || null,
started_at: containerData.started_at
? parseDate(containerData.started_at)
: null,
updated_at: now,
last_checked: now,
},
create: {
id: containerId,
host_id: host.id,
container_id: containerData.container_id,
name: containerData.name,
image_id: imageId,
image_name: containerData.image_name,
image_tag: containerData.image_tag || "latest",
status: containerData.status,
state: containerData.state || containerData.status,
ports: containerData.ports || null,
created_at: parseDate(containerData.created_at),
started_at: containerData.started_at
? parseDate(containerData.started_at)
: null,
updated_at: now,
},
});
containersProcessed++;
}
}
// Process standalone images
if (images && Array.isArray(images)) {
console.log(`[Docker Integration] Processing ${images.length} images`);
for (const imageData of images) {
await prisma.docker_images.upsert({
where: {
repository_tag_image_id: {
repository: imageData.repository,
tag: imageData.tag,
image_id: imageData.image_id,
},
},
update: {
size_bytes: imageData.size_bytes
? BigInt(imageData.size_bytes)
: null,
digest: imageData.digest || null,
last_checked: now,
updated_at: now,
},
create: {
id: uuidv4(),
repository: imageData.repository,
tag: imageData.tag,
image_id: imageData.image_id,
digest: imageData.digest,
size_bytes: imageData.size_bytes
? BigInt(imageData.size_bytes)
: null,
source: imageData.source || "docker-hub",
created_at: parseDate(imageData.created_at),
updated_at: now,
},
});
imagesProcessed++;
}
}
// Process volumes
if (volumes && Array.isArray(volumes)) {
console.log(`[Docker Integration] Processing ${volumes.length} volumes`);
for (const volumeData of volumes) {
await prisma.docker_volumes.upsert({
where: {
host_id_volume_id: {
host_id: host.id,
volume_id: volumeData.volume_id,
},
},
update: {
name: volumeData.name,
driver: volumeData.driver || "local",
mountpoint: volumeData.mountpoint || null,
renderer: volumeData.renderer || null,
scope: volumeData.scope || "local",
labels: volumeData.labels || null,
options: volumeData.options || null,
size_bytes: volumeData.size_bytes
? BigInt(volumeData.size_bytes)
: null,
ref_count: volumeData.ref_count || 0,
updated_at: now,
last_checked: now,
},
create: {
id: uuidv4(),
host_id: host.id,
volume_id: volumeData.volume_id,
name: volumeData.name,
driver: volumeData.driver || "local",
mountpoint: volumeData.mountpoint || null,
renderer: volumeData.renderer || null,
scope: volumeData.scope || "local",
labels: volumeData.labels || null,
options: volumeData.options || null,
size_bytes: volumeData.size_bytes
? BigInt(volumeData.size_bytes)
: null,
ref_count: volumeData.ref_count || 0,
created_at: parseDate(volumeData.created_at),
updated_at: now,
},
});
volumesProcessed++;
}
}
// Process networks
if (networks && Array.isArray(networks)) {
console.log(
`[Docker Integration] Processing ${networks.length} networks`,
);
for (const networkData of networks) {
await prisma.docker_networks.upsert({
where: {
host_id_network_id: {
host_id: host.id,
network_id: networkData.network_id,
},
},
update: {
name: networkData.name,
driver: networkData.driver,
scope: networkData.scope || "local",
ipv6_enabled: networkData.ipv6_enabled || false,
internal: networkData.internal || false,
attachable:
networkData.attachable !== undefined
? networkData.attachable
: true,
ingress: networkData.ingress || false,
config_only: networkData.config_only || false,
labels: networkData.labels || null,
ipam: networkData.ipam || null,
container_count: networkData.container_count || 0,
updated_at: now,
last_checked: now,
},
create: {
id: uuidv4(),
host_id: host.id,
network_id: networkData.network_id,
name: networkData.name,
driver: networkData.driver,
scope: networkData.scope || "local",
ipv6_enabled: networkData.ipv6_enabled || false,
internal: networkData.internal || false,
attachable:
networkData.attachable !== undefined
? networkData.attachable
: true,
ingress: networkData.ingress || false,
config_only: networkData.config_only || false,
labels: networkData.labels || null,
ipam: networkData.ipam || null,
container_count: networkData.container_count || 0,
created_at: networkData.created_at
? parseDate(networkData.created_at)
: null,
updated_at: now,
},
});
networksProcessed++;
}
}
// Process updates
if (updates && Array.isArray(updates)) {
console.log(`[Docker Integration] Processing ${updates.length} updates`);
for (const updateData of updates) {
// Find the image by repository and image_id
const image = await prisma.docker_images.findFirst({
where: {
repository: updateData.repository,
tag: updateData.current_tag,
image_id: updateData.image_id,
},
});
if (image) {
// Store digest info in changelog_url field as JSON
const digestInfo = JSON.stringify({
method: "digest_comparison",
current_digest: updateData.current_digest,
available_digest: updateData.available_digest,
});
// Upsert the update record
await prisma.docker_image_updates.upsert({
where: {
image_id_available_tag: {
image_id: image.id,
available_tag: updateData.available_tag,
},
},
update: {
updated_at: now,
changelog_url: digestInfo,
severity: "digest_changed",
},
create: {
id: uuidv4(),
image_id: image.id,
current_tag: updateData.current_tag,
available_tag: updateData.available_tag,
severity: "digest_changed",
changelog_url: digestInfo,
updated_at: now,
},
});
updatesProcessed++;
}
}
}
console.log(
`[Docker Integration] Successfully processed: ${containersProcessed} containers, ${imagesProcessed} images, ${volumesProcessed} volumes, ${networksProcessed} networks, ${updatesProcessed} updates`,
);
res.json({
message: "Docker data collected successfully",
containers_received: containersProcessed,
images_received: imagesProcessed,
volumes_received: volumesProcessed,
networks_received: networksProcessed,
updates_found: updatesProcessed,
});
} catch (error) {
console.error("[Docker Integration] Error collecting Docker data:", error);
console.error("[Docker Integration] Error stack:", error.stack);
res.status(500).json({
error: "Failed to collect Docker data",
message: error.message,
details: process.env.NODE_ENV === "development" ? error.stack : undefined,
});
}
});
module.exports = router;