Files
edubox/server/app/api/instances/route.ts
T
EduBox Dev b383b11ae2 feat(agent,server): v0.2.7 - mu-plugin WordPress robuste, réparation wp-config, proxy cookies/headers
- Agent: mu-plugin embarqué amélioré (HTTPS forcé, filtres URL, localhost:port)
- Agent: suppression des WP_HOME/WP_SITEURL hardcodés au démarrage des instances
- Server/proxy: envoi X-Forwarded-Port, réécriture headers/body élargie
- Server/proxy: sanitization des Set-Cookie (Secure, SameSite, Domain)
- Dashboard: version agent 0.2.7, action Supprimer complète
- Cleanup: binaires agent 0.2.3-0.2.6 remplacés par 0.2.7
2026-06-17 18:23:06 +00:00

133 lines
4.7 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { sendToNode } from "@/lib/websocket";
export async function GET(req: NextRequest) {
const { searchParams } = new URL(req.url);
const nodeId = searchParams.get("nodeId");
const establishmentId = searchParams.get("establishmentId");
let where: any = {};
if (nodeId) where.nodeId = nodeId;
if (establishmentId) {
const classes = await prisma.class.findMany({ where: { establishmentId }, select: { id: true } });
const students = await prisma.student.findMany({ where: { classId: { in: classes.map((c) => c.id) } }, select: { id: true } });
const nodes = await prisma.node.findMany({ where: { studentId: { in: students.map((s) => s.id) } }, select: { id: true } });
where.nodeId = { in: nodes.map((n) => n.id) };
}
const instances = await prisma.instance.findMany({
where,
include: {
node: {
include: {
student: {
include: {
class: {
include: {
establishment: true,
},
},
},
},
},
},
template: true,
},
orderBy: { createdAt: "desc" },
});
const enriched = instances.map((inst) => {
const domain = inst.node.student?.class.establishment?.domain;
const publicUrl = domain
? `https://${inst.id}.${domain}`
: null;
return {
...inst,
publicUrl,
};
});
return NextResponse.json(enriched);
}
export async function POST(req: NextRequest) {
const body = await req.json();
const { nodeId, templateId, port } = body;
const template = await prisma.template.findUnique({ where: { id: templateId } });
if (!template) return NextResponse.json({ error: "Template not found" }, { status: 404 });
const instance = await prisma.instance.create({
data: { nodeId, templateId, port: port || 8080, status: "stopped" },
});
const node = await prisma.node.findUnique({
where: { id: nodeId },
include: { student: { include: { class: { include: { establishment: true } } } } },
});
const domain = node?.student?.class.establishment?.domain;
const publicUrl = domain ? `https://${instance.id}.${domain}` : null;
const sent = sendToNode(nodeId, {
action: "start",
instanceId: instance.id,
type: template.type,
port: instance.port,
composeConfig: template.composeConfig
.replace(/{PORT}/g, String(instance.port))
.replace(/{INSTANCE_ID}/g, instance.id)
.replace(/{PUBLIC_URL}/g, publicUrl || `http://localhost:${instance.port}`)
.replace(/{PUBLIC_DOMAIN}/g, domain || "localhost"),
});
if (!sent) {
await prisma.instance.update({ where: { id: instance.id }, data: { status: "error" } });
}
return NextResponse.json(instance, { status: 201 });
}
export async function PATCH(req: NextRequest) {
const body = await req.json();
const { id, action } = body;
const instance = await prisma.instance.findUnique({ where: { id }, include: { template: true, node: { include: { student: { include: { class: { include: { establishment: true } } } } } } } });
if (!instance) return NextResponse.json({ error: "Not found" }, { status: 404 });
const domain = instance.node.student?.class.establishment?.domain;
const publicUrl = domain ? `https://${instance.id}.${domain}` : null;
if (action === "stop") {
sendToNode(instance.nodeId, { action: "delete", instanceId: instance.id });
await prisma.instance.update({ where: { id }, data: { status: "stopped" } });
} else if (action === "start") {
const sent = sendToNode(instance.nodeId, {
action: "start",
instanceId: instance.id,
type: instance.template.type,
port: instance.port,
composeConfig: instance.template.composeConfig
.replace(/{PORT}/g, String(instance.port))
.replace(/{INSTANCE_ID}/g, instance.id)
.replace(/{PUBLIC_URL}/g, publicUrl || `http://localhost:${instance.port}`)
.replace(/{PUBLIC_DOMAIN}/g, domain || "localhost"),
});
if (!sent) await prisma.instance.update({ where: { id }, data: { status: "error" } });
} else if (action === "reset") {
sendToNode(instance.nodeId, { action: "reset", instanceId: instance.id });
}
return NextResponse.json({ ok: true });
}
export async function DELETE(req: NextRequest) {
const { searchParams } = new URL(req.url);
const id = searchParams.get("id");
if (!id) return NextResponse.json({ error: "Missing id" }, { status: 400 });
const instance = await prisma.instance.findUnique({ where: { id } });
if (instance) sendToNode(instance.nodeId, { action: "delete", instanceId: instance.id });
await prisma.instance.delete({ where: { id } });
return NextResponse.json({ ok: true });
}