feat: mejoras al dashboard y selector de UUID

- Dashboard: agregar opciones de 50 pestañas deterministas (lotes 1-4)
- Dashboard: botón para cerrar salas individuales y expulsar jugadores
- Dashboard: selector de variante G por sala individual en tabla
- Nuevo selector moderno de UUID en página principal
- Mostrar nombres de jugadores en selector de UUID
- Búsqueda por UUID o nombre de jugador
- Redireccionar /missing-uuid a selector principal
- Endpoints para obtener UUIDs con nombres y cerrar/cambiar variante de salas
This commit is contained in:
2025-08-15 22:56:07 -06:00
parent f214174bab
commit ace9f8ee50
5 changed files with 581 additions and 12 deletions

View File

@@ -114,6 +114,48 @@ adminRouter.post("/rooms/:roomId/kick/:playerId", async (req: Request, res: Resp
}
});
adminRouter.post("/rooms/:roomId/close", async (req: Request, res: Response) => {
try {
const { roomId } = req.params;
const rooms = await matchMaker.query({ roomId });
if (rooms.length === 0) {
return res.status(404).json({ error: "Room not found" });
}
await matchMaker.remoteRoomCall(roomId, "executeAdminCommand", ["sendToLobby"]);
res.json({ success: true, message: `Room ${roomId} closed and players sent to lobby` });
} catch (error) {
console.error(`[AdminAPI] Error closing room ${req.params.roomId}:`, error);
res.status(500).json({ error: "Failed to close room" });
}
});
adminRouter.post("/rooms/:roomId/variant", async (req: Request, res: Response) => {
try {
const { roomId } = req.params;
const { variant } = req.body;
if (!variant || !['G1', 'G2', 'G3', 'G4', 'G5'].includes(variant)) {
return res.status(400).json({ error: "Invalid variant. Must be one of: G1, G2, G3, G4, G5" });
}
const rooms = await matchMaker.query({ roomId });
if (rooms.length === 0) {
return res.status(404).json({ error: "Room not found" });
}
await matchMaker.remoteRoomCall(roomId, "executeAdminCommand", ["setVariant", variant]);
res.json({ success: true, message: `Room ${roomId} variant changed to ${variant}` });
} catch (error) {
console.error(`[AdminAPI] Error changing variant for room ${req.params.roomId}:`, error);
res.status(500).json({ error: "Failed to change room variant" });
}
});
adminRouter.get("/stats", async (req: Request, res: Response) => {
try {
const stats = await matchMaker.stats.fetchAll();
@@ -461,6 +503,28 @@ adminRouter.get("/admin/uuids", async (req: Request, res: Response) => {
}
});
// UUID with names endpoint
adminRouter.get("/admin/uuids-with-names", async (req: Request, res: Response) => {
try {
const uuids = listAllowedUuids();
const nameManager = NameManager.getInstance();
const uuidsWithInfo = uuids.map(uuid => {
const name = nameManager.getPlayerName(uuid);
return {
uuid,
name: name || null,
hasName: !!name
};
});
res.json({ uuids: uuidsWithInfo });
} catch (error) {
console.error("[AdminAPI] Error fetching UUIDs with names:", error);
res.status(500).json({ error: "Failed to fetch UUIDs with names" });
}
});
// SSE endpoint for real-time dashboard updates
adminRouter.get("/dashboard-stream", (req: Request, res: Response) => {
// Set SSE headers