Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
611 changes: 611 additions & 0 deletions server/src/admin/webServer/AdminWebServer.cpp

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions server/src/admin/webServer/AdminWebServer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
** EPITECH PROJECT, 2026
** R-Type
** File description:
** AdminWebServer
*/

#pragma once
#include <algorithm>
#include <atomic>
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <functional>
#include <httplib.h>
#include <memory>
#include <nlohmann/json.hpp>
#include <optional>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

#ifndef _WIN32
#include <arpa/inet.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#endif

#include "ISessionManager.hpp"
#include "RoomManager.hpp"

namespace Net::Admin
{
/**
* @class AdminWebServer
* @brief A simple administrative web server for managing sessions and rooms.
Comment thread
ann7415 marked this conversation as resolved.
*/
class AdminWebServer {
public:
/**
* @brief Constructs a new AdminWebServer object.
* @param sessions Shared pointer to the session manager.
* @param rooms Shared pointer to the room manager.
* @param onShutdown Callback function to be called on server shutdown.
* @param port Port number for the web server (default is 8082).
* @param token Authentication token for accessing admin endpoints (default is "admin").
*/
AdminWebServer(std::shared_ptr<Server::ISessionManager> sessions, std::shared_ptr<Engine::RoomManager> rooms,
std::function<void()> onShutdown, int port = 8082, std::string token = "admin");
Comment thread
ann7415 marked this conversation as resolved.

/**
* @brief Destroys the AdminWebServer object and stops the server if running.
*/
~AdminWebServer();

/**
* @brief Starts the admin web server in a separate thread.
*/
void start();

/**
* @brief Stops the admin web server.
*/
void stop() noexcept;

/**
* @brief Gets the port number the server is running on.
* @return The port number.
*/
[[nodiscard]] int port() const noexcept;

private:
void run(); ///> Thread function to run the web server

std::shared_ptr<Server::ISessionManager> _sessions; ///> Session manager
std::shared_ptr<Engine::RoomManager> _rooms; ///> Room manager
std::function<void()> _onShutdown; ///> Shutdown callback
std::unique_ptr<httplib::Server> _srv; ///> HTTP server instance

int _port = 8082; ///> Server port
std::string _token; ///> Authentication token

std::atomic<bool> _running{false}; ///> Flag indicating if the server is running
std::thread _thread; ///> Thread for the web server
};
} // namespace Net::Admin
73 changes: 73 additions & 0 deletions server/src/admin/webServer/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
function authHeaders() {
const t = document.getElementById("token").value.trim();
return { "Authorization": "Bearer " + t };
}

async function apiGet(path) {
const r = await fetch(path, { headers: authHeaders() });
if (!r.ok) throw new Error(await r.text());
return r.json();
}

async function apiPost(path, body) {
const r = await fetch(path, {
method: "POST",
headers: { ...authHeaders(), "Content-Type": "application/json" },
body: JSON.stringify(body || {})
});
if (!r.ok) throw new Error(await r.text());
return r.json();
}

function renderTable(el, cols, rows) {
const t = document.getElementById(el);
t.innerHTML = "";
const thead = document.createElement("tr");
cols.forEach(c => { const th = document.createElement("th"); th.textContent = c; thead.appendChild(th); });
t.appendChild(thead);

rows.forEach(r => {
const tr = document.createElement("tr");
cols.forEach(c => {
const td = document.createElement("td");
td.appendChild(r[c] instanceof Node ? r[c] : document.createTextNode(String(r[c] ?? "")));
tr.appendChild(td);
});
t.appendChild(tr);
});
}

async function refreshAll() {
try {
const status = await apiGet("/api/status");
document.getElementById("status").textContent = JSON.stringify(status, null, 2);
const rooms = await apiGet("/api/rooms");
renderTable("rooms", ["roomId","name","players","maxPlayers"], rooms);
const sessions = await apiGet("/api/sessions");
const rows = sessions.map(s => {
const kick = document.createElement("button");
kick.textContent = "Kick";
kick.onclick = async () => { await apiPost("/api/kick", { who: String(s.id) }); await refreshAll(); };

const ban = document.createElement("button");
ban.textContent = "Ban 60m";
ban.onclick = async () => { await apiPost("/api/ban", { who: String(s.id), minutes: 60 }); await refreshAll(); };

return {
id: s.id,
username: s.username,
authed: s.authed,
roomId: s.roomId,
kick: kick,
ban: ban
};
});
renderTable("sessions", ["id","username","authed","roomId","kick","ban"], rows);

const bans = await apiGet("/api/bans");
renderTable("bans", ["ip","remainingSeconds"], bans);

Comment thread
ann7415 marked this conversation as resolved.
} catch (e) {
alert("Error: " + e.message);
}
}
Comment thread
ann7415 marked this conversation as resolved.
31 changes: 31 additions & 0 deletions server/src/admin/webServer/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!doctype html>
<html>
Comment thread
ann7415 marked this conversation as resolved.
<head>
<meta charset="utf-8" />
<title>R-Type Admin</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>R-Type Admin Dashboard</h1>

<section>
<label>Admin token:</label>
<input id="token" type="password" placeholder="R_TYPE_ADMIN_TOKEN" />
<button onclick="refreshAll()">Refresh</button>
</section>

<h2>Status</h2>
<pre id="status"></pre>

<h2>Sessions</h2>
<table id="sessions"></table>

<h2>Rooms</h2>
<table id="rooms"></table>

<h2>Bans</h2>
<table id="bans"></table>

Comment thread
ann7415 marked this conversation as resolved.
<script src="app.js"></script>
</body>
</html>
Comment thread
ann7415 marked this conversation as resolved.
6 changes: 6 additions & 0 deletions server/src/admin/webServer/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
body { font-family: sans-serif; margin: 20px; }
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
th, td { border: 1px solid #ccc; padding: 6px; }
th { background: #eee; text-align: left; }
button { margin-right: 6px; }
pre { background: #111; color: #0f0; padding: 10px; }
Comment thread
ann7415 marked this conversation as resolved.
4 changes: 4 additions & 0 deletions server/src/thread/ServerRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ void ServerRuntime::start()
requestStop();
});
_adminConsole->start();
_adminWeb = std::make_unique<Admin::AdminWebServer>(_sessionManager, _roomManager, [this]() {
requestStop();
});
_adminWeb->start();
_receiverThread = std::thread(&ServerRuntime::runReceiver, this);
_processorThread = std::thread(&ServerRuntime::runProcessor, this);
_snapshotThread = std::thread(&ServerRuntime::runSnapshot, this);
Expand Down
2 changes: 2 additions & 0 deletions server/src/thread/ServerRuntime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <memory>
#include <thread>
#include "AdminConsole.hpp"
#include "AdminWebServer.hpp"
#include "AuthService.hpp"
#include "GameServer.hpp"
#include "IServer.hpp"
Expand Down Expand Up @@ -134,6 +135,7 @@ namespace Net::Thread
std::shared_ptr<Server::ISessionManager> _sessionManager; ///> Manages client sessions
std::shared_ptr<Engine::RoomManager> _roomManager; ///> Manages game rooms
std::unique_ptr<Admin::AdminConsole> _adminConsole; ///> Text-mode admin dashboard
std::unique_ptr<Admin::AdminWebServer> _adminWeb; ///> Web-based admin dashboard

std::thread _receiverThread; ///> Thread for receiving packets
std::thread _processorThread; ///> Thread for processing packets
Expand Down
3 changes: 2 additions & 1 deletion vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"sqlite3",
"openssl",
"nlohmann-json",
"lz4"
"lz4",
"cpp-httplib"
]
}
Loading