223 lines
5.6 KiB
TypeScript
Raw Normal View History

2025-04-12 22:05:42 +02:00
import { NextResponse, NextRequest } from "next/server";
2025-04-13 21:10:17 +02:00
import { prisma } from "@/lib/prisma";
2025-04-12 22:05:42 +02:00
2025-04-13 21:10:17 +02:00
interface Node {
id: string;
type: string;
data: {
label: string;
[key: string]: any;
};
position: { x: number; y: number };
style: React.CSSProperties;
2025-04-14 21:48:09 +02:00
draggable?: boolean;
selectable?: boolean;
zIndex?: number;
2025-04-13 21:10:17 +02:00
}
interface Edge {
id: string;
source: string;
target: string;
type: string;
style: {
stroke: string;
strokeWidth: number;
};
}
interface Server {
id: number;
name: string;
ip: string;
}
interface Application {
id: number;
name: string;
localURL: string;
serverId: number;
}
2025-04-12 22:05:42 +02:00
const NODE_WIDTH = 220;
const NODE_HEIGHT = 60;
2025-04-14 21:48:09 +02:00
const APP_NODE_WIDTH = 160;
const APP_NODE_HEIGHT = 40;
2025-04-12 22:05:42 +02:00
const HORIZONTAL_SPACING = 280;
2025-04-14 21:48:09 +02:00
const VERTICAL_SPACING = 60;
2025-04-12 22:05:42 +02:00
const START_Y = 120;
const ROOT_NODE_WIDTH = 300;
2025-04-14 21:48:09 +02:00
const CONTAINER_PADDING = 40;
2025-04-12 22:05:42 +02:00
export async function GET() {
try {
const [servers, applications] = await Promise.all([
prisma.server.findMany({
orderBy: { id: "asc" },
2025-04-13 21:10:17 +02:00
}) as Promise<Server[]>,
2025-04-12 22:05:42 +02:00
prisma.application.findMany({
orderBy: { serverId: "asc" },
2025-04-13 21:10:17 +02:00
}) as Promise<Application[]>,
2025-04-12 22:05:42 +02:00
]);
2025-04-14 21:48:09 +02:00
// Root Node
2025-04-13 21:10:17 +02:00
const rootNode: Node = {
2025-04-12 22:05:42 +02:00
id: "root",
type: "infrastructure",
data: { label: "My Infrastructure" },
2025-04-14 21:48:09 +02:00
position: { x: 0, y: 0 },
2025-04-12 22:05:42 +02:00
style: {
background: "#ffffff",
color: "#0f0f0f",
border: "2px solid #e6e4e1",
borderRadius: "8px",
padding: "16px",
width: ROOT_NODE_WIDTH,
height: NODE_HEIGHT,
fontSize: "1.2rem",
fontWeight: "bold",
},
};
2025-04-14 21:48:09 +02:00
// Server Nodes
2025-04-13 21:10:17 +02:00
const serverNodes: Node[] = servers.map((server, index) => {
2025-04-12 22:05:42 +02:00
const xPos =
index * HORIZONTAL_SPACING -
((servers.length - 1) * HORIZONTAL_SPACING) / 2;
return {
id: `server-${server.id}`,
type: "server",
data: {
label: `${server.name}\n${server.ip}`,
...server,
},
position: { x: xPos, y: START_Y },
style: {
background: "#ffffff",
color: "#0f0f0f",
border: "2px solid #e6e4e1",
borderRadius: "4px",
padding: "8px",
width: NODE_WIDTH,
height: NODE_HEIGHT,
fontSize: "0.9rem",
lineHeight: "1.2",
whiteSpace: "pre-wrap",
},
};
});
2025-04-14 21:48:09 +02:00
// Application Nodes
2025-04-13 21:10:17 +02:00
const appNodes: Node[] = [];
2025-04-12 22:05:42 +02:00
servers.forEach((server) => {
2025-04-14 21:48:09 +02:00
const serverNode = serverNodes.find((n) => n.id === `server-${server.id}`);
const serverX = serverNode?.position.x || 0;
const xOffset = (NODE_WIDTH - APP_NODE_WIDTH) / 2;
2025-04-12 22:05:42 +02:00
applications
.filter((app) => app.serverId === server.id)
.forEach((app, appIndex) => {
appNodes.push({
id: `app-${app.id}`,
type: "application",
data: {
label: `${app.name}\n${app.localURL}`,
...app,
},
position: {
2025-04-14 21:48:09 +02:00
x: serverX + xOffset,
y: START_Y + NODE_HEIGHT + 30 + appIndex * VERTICAL_SPACING,
2025-04-12 22:05:42 +02:00
},
style: {
2025-04-14 21:48:09 +02:00
background: "#f5f5f5",
2025-04-12 22:05:42 +02:00
color: "#0f0f0f",
border: "2px solid #e6e4e1",
borderRadius: "4px",
2025-04-14 21:48:09 +02:00
padding: "6px",
width: APP_NODE_WIDTH,
height: APP_NODE_HEIGHT,
fontSize: "0.8rem",
lineHeight: "1.1",
2025-04-12 22:05:42 +02:00
whiteSpace: "pre-wrap",
},
});
});
});
2025-04-14 21:48:09 +02:00
// Connections
2025-04-13 21:10:17 +02:00
const connections: Edge[] = [
2025-04-12 22:05:42 +02:00
...servers.map((server) => ({
id: `conn-root-${server.id}`,
source: "root",
target: `server-${server.id}`,
type: "straight",
style: {
stroke: "#94a3b8",
strokeWidth: 2,
},
})),
...applications.map((app) => ({
id: `conn-${app.serverId}-${app.id}`,
source: `server-${app.serverId}`,
target: `app-${app.id}`,
type: "straight",
style: {
stroke: "#60a5fa",
strokeWidth: 2,
},
})),
];
2025-04-14 21:48:09 +02:00
// Container Box
const allNodes = [rootNode, ...serverNodes, ...appNodes];
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
allNodes.forEach((node) => {
const width = parseInt(node.style.width?.toString() || "0", 10);
const height = parseInt(node.style.height?.toString() || "0", 10);
minX = Math.min(minX, node.position.x);
maxX = Math.max(maxX, node.position.x + width);
minY = Math.min(minY, node.position.y);
maxY = Math.max(maxY, node.position.y + height);
});
const containerNode: Node = {
id: 'container',
type: 'container',
data: { label: '' },
position: {
x: minX - CONTAINER_PADDING,
y: minY - CONTAINER_PADDING
},
style: {
width: maxX - minX + 2 * CONTAINER_PADDING,
height: maxY - minY + 2 * CONTAINER_PADDING,
background: 'transparent',
border: '2px dashed #e2e8f0',
borderRadius: '8px',
zIndex: 0,
},
draggable: false,
selectable: false,
zIndex: -1,
};
2025-04-12 22:05:42 +02:00
return NextResponse.json({
2025-04-14 21:48:09 +02:00
nodes: [containerNode, ...allNodes],
2025-04-12 22:05:42 +02:00
edges: connections,
});
2025-04-13 21:10:17 +02:00
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
2025-04-12 22:05:42 +02:00
return NextResponse.json(
{
2025-04-13 21:10:17 +02:00
error: `Error fetching flowchart: ${errorMessage}`,
2025-04-12 22:05:42 +02:00
},
{ status: 500 }
);
}
2025-04-13 21:10:17 +02:00
}