mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-06-06 23:04:21 +00:00
Add dashboard audit log and configuration management
- Implemented dashboard audit logging with InsertAuditLog, GetRecentAuditLogs, GetAuditLogsByIP, and CleanupOldAuditLogs methods. - Created dashboard configuration management with GetDashboardConfig and SetDashboardConfig methods. - Added new tables for dashboard_audit_log and dashboard_config in both MySQL and SQLite migrations. - Updated CMakeLists to include Crow and ASIO for dashboard server functionality. - Enhanced existing database classes to support new dashboard features, including character, play key, and property management. - Added new methods for retrieving and managing play keys, properties, and pet names. - Updated TestSQLDatabase to include stubs for new dashboard-related methods. - Modified shared and dashboard configuration files for new settings.
This commit is contained in:
@@ -1,59 +1,153 @@
|
||||
#include "DashboardWeb.h"
|
||||
#include "DashboardShared.h"
|
||||
|
||||
// Blueprint includes
|
||||
#include "blueprints/AuthBlueprint.h"
|
||||
#include "blueprints/ApiBlueprint.h"
|
||||
#include "blueprints/PageBlueprint.h"
|
||||
#include "blueprints/PlayKeysBlueprint.h"
|
||||
#include "blueprints/CharactersBlueprint.h"
|
||||
#include "blueprints/MailBlueprint.h"
|
||||
#include "blueprints/BugReportsBlueprint.h"
|
||||
#include "blueprints/ModerationBlueprint.h"
|
||||
|
||||
// Crow headers - must come before ASIO to avoid conflicts
|
||||
#include "crow.h"
|
||||
#include "crow/middlewares/session.h"
|
||||
|
||||
// thanks bill gates
|
||||
#ifdef _WIN32
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
#include "inja.hpp"
|
||||
|
||||
#include "eHTTPMethod.h"
|
||||
|
||||
|
||||
// simple home page with inja
|
||||
void HandleHTTPHomeRequest(HTTPReply& reply, std::string body) {
|
||||
try {
|
||||
inja::Environment env;
|
||||
env.set_trim_blocks(true);
|
||||
env.set_lstrip_blocks(true);
|
||||
|
||||
nlohmann::json data;
|
||||
data["title"] = "Darkflame Universe Dashboard";
|
||||
data["header"] = "Welcome to the Darkflame Universe Dashboard";
|
||||
data["message"] = "This is a simple dashboard page served using Inja templating engine.";
|
||||
|
||||
const std::string template_str = R"(
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ title }}</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ header }}</h1>
|
||||
<p>{{ message }}</p>
|
||||
</body>
|
||||
</html>
|
||||
)";
|
||||
|
||||
std::string rendered = env.render(template_str, data);
|
||||
reply.message = rendered;
|
||||
reply.status = eHTTPStatusCode::OK;
|
||||
reply.contentType = ContentType::HTML;
|
||||
} catch (const std::exception& e) {
|
||||
reply.status = eHTTPStatusCode::INTERNAL_SERVER_ERROR;
|
||||
reply.message = "Internal Server Error";
|
||||
reply.contentType = ContentType::PLAIN;
|
||||
}
|
||||
}
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
namespace DashboardWeb {
|
||||
void RegisterRoutes() {
|
||||
Game::web.RegisterHTTPRoute({
|
||||
.path = "/",
|
||||
.method = eHTTPMethod::GET,
|
||||
.handle = HandleHTTPHomeRequest
|
||||
});
|
||||
|
||||
using Session = crow::SessionMiddleware<crow::InMemoryStore>;
|
||||
|
||||
static crow::App<crow::CookieParser, Session> g_App {
|
||||
Session{
|
||||
// cookie config: use "session" cookie name, 24h max_age
|
||||
crow::CookieParser::Cookie("session").max_age(24 * 60 * 60).path("/"),
|
||||
// session id length
|
||||
32,
|
||||
// storage backend (InMemoryStore)
|
||||
crow::InMemoryStore{}
|
||||
}
|
||||
};
|
||||
|
||||
static std::future<void> g_ServerFuture;
|
||||
static bool g_Running = false;
|
||||
static bool g_Initialized = false;
|
||||
|
||||
void SetupRoutes() {
|
||||
static bool setupCalled = false;
|
||||
if (setupCalled) {
|
||||
std::cerr << "WARNING: SetupRoutes() called multiple times!" << std::endl;
|
||||
return;
|
||||
}
|
||||
setupCalled = true;
|
||||
|
||||
std::cerr << "Setting up dashboard routes..." << std::endl;
|
||||
|
||||
// Set mustache template base directory
|
||||
crow::mustache::set_base("./templates");
|
||||
|
||||
// Setup all blueprint routes
|
||||
try {
|
||||
std::cerr << " - Setting up AuthBlueprint..." << std::endl;
|
||||
AuthBlueprint::Setup(g_App);
|
||||
|
||||
std::cerr << " - Setting up ApiBlueprint..." << std::endl;
|
||||
ApiBlueprint::Setup(g_App);
|
||||
|
||||
std::cerr << " - Setting up PageBlueprint..." << std::endl;
|
||||
PageBlueprint::Setup(g_App);
|
||||
|
||||
std::cerr << " - Setting up PlayKeysBlueprint..." << std::endl;
|
||||
PlayKeysBlueprint::Setup(g_App);
|
||||
|
||||
std::cerr << " - Setting up CharactersBlueprint..." << std::endl;
|
||||
CharactersBlueprint::Setup(g_App);
|
||||
|
||||
std::cerr << " - Setting up MailBlueprint..." << std::endl;
|
||||
MailBlueprint::Setup(g_App);
|
||||
|
||||
std::cerr << " - Setting up BugReportsBlueprint..." << std::endl;
|
||||
BugReportsBlueprint::Setup(g_App);
|
||||
|
||||
std::cerr << " - Setting up ModerationBlueprint..." << std::endl;
|
||||
ModerationBlueprint::Setup(g_App);
|
||||
|
||||
std::cerr << "All routes set up successfully!" << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
// Print to stderr since LOG might not be available
|
||||
std::cerr << "Error setting up routes: " << e.what() << std::endl;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Initialize(uint32_t port) {
|
||||
// Only allow initialization once per process lifetime
|
||||
// Crow apps cannot be restarted once stopped
|
||||
if (g_Initialized) {
|
||||
std::cerr << "Dashboard web server already initialized. Cannot reinitialize." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Setup routes (only happens once)
|
||||
SetupRoutes();
|
||||
|
||||
// Configure Crow app
|
||||
g_App.loglevel(crow::LogLevel::Info); // Changed to Info to see startup messages
|
||||
|
||||
// Start the server in a separate thread
|
||||
g_ServerFuture = std::async(std::launch::async, [port]() {
|
||||
try {
|
||||
g_App.port(port).multithreaded().run();
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error running Crow server: " << e.what() << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
g_Running = true;
|
||||
g_Initialized = true;
|
||||
|
||||
// Give the server a moment to start
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error initializing dashboard web server: " << e.what() << std::endl;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void Update() {
|
||||
// Crow runs in its own thread, nothing to update here
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
if (!g_Running) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_App.stop();
|
||||
|
||||
// Wait for the server thread to finish (with timeout)
|
||||
if (g_ServerFuture.valid()) {
|
||||
auto status = g_ServerFuture.wait_for(std::chrono::seconds(5));
|
||||
if (status == std::future_status::timeout) {
|
||||
std::cerr << "Warning: Dashboard web server did not stop gracefully" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
g_Running = false;
|
||||
}
|
||||
|
||||
} // namespace DashboardWeb
|
||||
|
||||
Reference in New Issue
Block a user