mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-05-13 02:45:04 +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:
@@ -42,9 +42,91 @@ void SQLiteDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameM
|
||||
ExecuteUpdate("UPDATE accounts SET gm_level = ? WHERE id = ?;", static_cast<int32_t>(gmLevel), accountId);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdateAccountPlayKey(const uint32_t accountId, const uint32_t playKeyId) {
|
||||
ExecuteUpdate("UPDATE accounts SET play_key_id = ? WHERE id = ?;", playKeyId, accountId);
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetAccountCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM accounts;");
|
||||
if (res.eof()) return 0;
|
||||
|
||||
return res.getIntField("count");
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetBannedAccountCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM accounts WHERE banned = 1;");
|
||||
if (res.eof()) return 0;
|
||||
|
||||
return res.getIntField("count");
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetLockedAccountCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM accounts WHERE locked = 1;");
|
||||
if (res.eof()) return 0;
|
||||
|
||||
return res.getIntField("count");
|
||||
}
|
||||
|
||||
std::vector<IAccounts::ListInfo> SQLiteDatabase::GetAllAccounts() {
|
||||
std::vector<IAccounts::ListInfo> out;
|
||||
auto [stmt, res] = ExecuteSelect("SELECT id, name, gm_level, banned, locked, mute_expire, play_key_id FROM accounts ORDER BY id ASC;");
|
||||
|
||||
while (!res.eof()) {
|
||||
IAccounts::ListInfo info;
|
||||
info.id = res.getIntField("id");
|
||||
info.name = res.getStringField("name");
|
||||
info.gm_level = static_cast<eGameMasterLevel>(res.getIntField("gm_level"));
|
||||
info.banned = res.getIntField("banned");
|
||||
info.locked = res.getIntField("locked");
|
||||
info.mute_expire = static_cast<uint64_t>(res.getInt64Field("mute_expire"));
|
||||
info.play_key_id = res.getIntField("play_key_id");
|
||||
out.push_back(info);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdateAccountLock(const uint32_t accountId, const bool locked) {
|
||||
ExecuteUpdate("UPDATE accounts SET locked = ? WHERE id = ?;", locked, accountId);
|
||||
}
|
||||
|
||||
std::optional<IAccounts::DetailedInfo> SQLiteDatabase::GetAccountById(const uint32_t accountId) {
|
||||
auto [_, result] = ExecuteSelect(
|
||||
"SELECT id, name, email, gm_level, banned, locked, mute_expire, play_key_id, created_at FROM accounts WHERE id = ? LIMIT 1;",
|
||||
accountId
|
||||
);
|
||||
|
||||
if (result.eof()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
IAccounts::DetailedInfo info;
|
||||
info.id = result.getIntField("id");
|
||||
info.name = result.getStringField("name");
|
||||
info.email = result.getStringField("email");
|
||||
info.gm_level = static_cast<eGameMasterLevel>(result.getIntField("gm_level"));
|
||||
info.banned = result.getIntField("banned") != 0;
|
||||
info.locked = result.getIntField("locked") != 0;
|
||||
info.mute_expire = static_cast<uint64_t>(result.getInt64Field("mute_expire"));
|
||||
info.play_key_id = result.getIntField("play_key_id");
|
||||
info.created_at = static_cast<uint64_t>(result.getInt64Field("created_at"));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdateAccountEmail(const uint32_t accountId, const std::string_view email) {
|
||||
ExecuteUpdate("UPDATE accounts SET email = ? WHERE id = ?;", email, accountId);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::DeleteAccount(const uint32_t accountId) {
|
||||
// Delete all associated data first
|
||||
ExecuteDelete("DELETE FROM char_info WHERE account_id = ?;", accountId);
|
||||
ExecuteDelete("DELETE FROM accounts WHERE id = ?;", accountId);
|
||||
}
|
||||
|
||||
std::vector<IAccounts::SessionInfo> SQLiteDatabase::GetAccountSessions(const uint32_t accountId, uint32_t limit) {
|
||||
// account_sessions table doesn't exist in the current schema
|
||||
// Session tracking would need to be implemented separately
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -4,3 +4,63 @@ void SQLiteDatabase::UpdateActivityLog(const LWOOBJID characterId, const eActivi
|
||||
ExecuteInsert("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);",
|
||||
characterId, static_cast<uint32_t>(activityType), static_cast<uint32_t>(time(NULL)), mapId);
|
||||
}
|
||||
|
||||
std::vector<IActivityLog::Entry> SQLiteDatabase::GetRecentActivity(const uint32_t limit) {
|
||||
std::vector<IActivityLog::Entry> out;
|
||||
|
||||
auto [stmt, res] = ExecuteSelect("SELECT character_id, activity, time, map_id FROM activity_log ORDER BY time DESC LIMIT ?;", limit);
|
||||
|
||||
while (!res.eof()) {
|
||||
IActivityLog::Entry e;
|
||||
e.characterId = static_cast<LWOOBJID>(res.getInt64Field("character_id"));
|
||||
e.activity = static_cast<eActivityType>(res.getIntField("activity"));
|
||||
e.timestamp = static_cast<uint32_t>(res.getIntField("time"));
|
||||
e.mapId = static_cast<LWOMAPID>(res.getIntField("map_id"));
|
||||
out.push_back(e);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetActivityLogCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM activity_log;");
|
||||
if (res.eof()) return 0;
|
||||
return res.getIntField("count");
|
||||
}
|
||||
|
||||
std::vector<IActivityLog::Entry> SQLiteDatabase::GetActivityLogPaginated(
|
||||
uint32_t offset,
|
||||
uint32_t limit,
|
||||
const std::string& orderColumn,
|
||||
const std::string& orderDir
|
||||
) {
|
||||
std::vector<IActivityLog::Entry> out;
|
||||
|
||||
// Validate orderColumn to prevent SQL injection
|
||||
std::string validColumn = "time";
|
||||
if (orderColumn == "character_id" || orderColumn == "activity" || orderColumn == "map_id" || orderColumn == "time") {
|
||||
validColumn = orderColumn;
|
||||
}
|
||||
|
||||
// Validate orderDir
|
||||
std::string validDir = (orderDir == "ASC" || orderDir == "asc") ? "ASC" : "DESC";
|
||||
|
||||
// Build query - can't use prepared statement for ORDER BY clause
|
||||
std::string query = "SELECT character_id, activity, time, map_id FROM activity_log ORDER BY " +
|
||||
validColumn + " " + validDir + " LIMIT ? OFFSET ?;";
|
||||
|
||||
auto [stmt, res] = ExecuteSelect(query, limit, offset);
|
||||
|
||||
while (!res.eof()) {
|
||||
IActivityLog::Entry e;
|
||||
e.characterId = static_cast<LWOOBJID>(res.getInt64Field("character_id"));
|
||||
e.activity = static_cast<eActivityType>(res.getIntField("activity"));
|
||||
e.timestamp = static_cast<uint32_t>(res.getIntField("time"));
|
||||
e.mapId = static_cast<LWOMAPID>(res.getIntField("map_id"));
|
||||
out.push_back(e);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -4,3 +4,127 @@ void SQLiteDatabase::InsertNewBugReport(const IBugReports::Info& info) {
|
||||
ExecuteInsert("INSERT INTO `bug_reports`(body, client_version, other_player_id, selection, reporter_id) VALUES (?, ?, ?, ?, ?)",
|
||||
info.body, info.clientVersion, info.otherPlayer, info.selection, info.characterId);
|
||||
}
|
||||
|
||||
std::vector<IBugReports::DetailedInfo> SQLiteDatabase::GetAllBugReports() {
|
||||
std::vector<IBugReports::DetailedInfo> out;
|
||||
auto [stmt, res] = ExecuteSelect(
|
||||
"SELECT id, body, client_version, other_player_id, selection, reporter_id, "
|
||||
"strftime('%s', submitted) as submitted, strftime('%s', resolved_time) as resolved_time, "
|
||||
"resolved_by_id, resolution FROM bug_reports ORDER BY id DESC;"
|
||||
);
|
||||
|
||||
while (!res.eof()) {
|
||||
IBugReports::DetailedInfo info;
|
||||
info.id = static_cast<uint64_t>(res.getInt64Field("id"));
|
||||
info.body = res.getStringField("body");
|
||||
info.clientVersion = res.getStringField("client_version");
|
||||
info.otherPlayer = res.getStringField("other_player_id");
|
||||
info.selection = res.getStringField("selection");
|
||||
info.characterId = static_cast<LWOOBJID>(res.getInt64Field("reporter_id"));
|
||||
info.submitted = static_cast<uint64_t>(res.getInt64Field("submitted"));
|
||||
info.resolved_time = res.fieldIsNull("resolved_time") ? 0 : static_cast<uint64_t>(res.getInt64Field("resolved_time"));
|
||||
info.resolved_by_id = res.fieldIsNull("resolved_by_id") ? 0 : res.getIntField("resolved_by_id");
|
||||
info.resolution = res.getStringField("resolution");
|
||||
out.push_back(info);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<IBugReports::DetailedInfo> SQLiteDatabase::GetUnresolvedBugReports() {
|
||||
std::vector<IBugReports::DetailedInfo> out;
|
||||
auto [stmt, res] = ExecuteSelect(
|
||||
"SELECT id, body, client_version, other_player_id, selection, reporter_id, "
|
||||
"strftime('%s', submitted) as submitted FROM bug_reports WHERE resolved_time IS NULL ORDER BY id DESC;"
|
||||
);
|
||||
|
||||
while (!res.eof()) {
|
||||
IBugReports::DetailedInfo info;
|
||||
info.id = static_cast<uint64_t>(res.getInt64Field("id"));
|
||||
info.body = res.getStringField("body");
|
||||
info.clientVersion = res.getStringField("client_version");
|
||||
info.otherPlayer = res.getStringField("other_player_id");
|
||||
info.selection = res.getStringField("selection");
|
||||
info.characterId = static_cast<LWOOBJID>(res.getInt64Field("reporter_id"));
|
||||
info.submitted = static_cast<uint64_t>(res.getInt64Field("submitted"));
|
||||
info.resolved_time = 0;
|
||||
info.resolved_by_id = 0;
|
||||
info.resolution = "";
|
||||
out.push_back(info);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<IBugReports::DetailedInfo> SQLiteDatabase::GetResolvedBugReports() {
|
||||
std::vector<IBugReports::DetailedInfo> out;
|
||||
auto [stmt, res] = ExecuteSelect(
|
||||
"SELECT id, body, client_version, other_player_id, selection, reporter_id, "
|
||||
"strftime('%s', submitted) as submitted, strftime('%s', resolved_time) as resolved_time, "
|
||||
"resolved_by_id, resolution FROM bug_reports WHERE resolved_time IS NOT NULL ORDER BY id DESC;"
|
||||
);
|
||||
|
||||
while (!res.eof()) {
|
||||
IBugReports::DetailedInfo info;
|
||||
info.id = static_cast<uint64_t>(res.getInt64Field("id"));
|
||||
info.body = res.getStringField("body");
|
||||
info.clientVersion = res.getStringField("client_version");
|
||||
info.otherPlayer = res.getStringField("other_player_id");
|
||||
info.selection = res.getStringField("selection");
|
||||
info.characterId = static_cast<LWOOBJID>(res.getInt64Field("reporter_id"));
|
||||
info.submitted = static_cast<uint64_t>(res.getInt64Field("submitted"));
|
||||
info.resolved_time = static_cast<uint64_t>(res.getInt64Field("resolved_time"));
|
||||
info.resolved_by_id = res.getIntField("resolved_by_id");
|
||||
info.resolution = res.getStringField("resolution");
|
||||
out.push_back(info);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<IBugReports::DetailedInfo> SQLiteDatabase::GetBugReportById(const uint64_t reportId) {
|
||||
auto [_, result] = ExecuteSelect(
|
||||
"SELECT id, body, client_version, other_player_id, selection, reporter_id, "
|
||||
"strftime('%s', submitted) as submitted, strftime('%s', resolved_time) as resolved_time, "
|
||||
"resolved_by_id, resolution FROM bug_reports WHERE id = ? LIMIT 1;",
|
||||
reportId
|
||||
);
|
||||
|
||||
if (result.eof()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
IBugReports::DetailedInfo info;
|
||||
info.id = static_cast<uint64_t>(result.getInt64Field("id"));
|
||||
info.body = result.getStringField("body");
|
||||
info.clientVersion = result.getStringField("client_version");
|
||||
info.otherPlayer = result.getStringField("other_player_id");
|
||||
info.selection = result.getStringField("selection");
|
||||
info.characterId = static_cast<LWOOBJID>(result.getInt64Field("reporter_id"));
|
||||
info.submitted = static_cast<uint64_t>(result.getInt64Field("submitted"));
|
||||
info.resolved_time = result.fieldIsNull("resolved_time") ? 0 : static_cast<uint64_t>(result.getInt64Field("resolved_time"));
|
||||
info.resolved_by_id = result.fieldIsNull("resolved_by_id") ? 0 : result.getIntField("resolved_by_id");
|
||||
info.resolution = result.getStringField("resolution");
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::ResolveBugReport(const uint64_t reportId, const uint32_t resolvedById, const std::string_view resolution) {
|
||||
ExecuteUpdate(
|
||||
"UPDATE bug_reports SET resolved_time = datetime('now'), resolved_by_id = ?, resolution = ? WHERE id = ?;",
|
||||
resolvedById, resolution, reportId
|
||||
);
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetBugReportCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM bug_reports;");
|
||||
return res.eof() ? 0 : res.getIntField("count");
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetUnresolvedBugReportCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM bug_reports WHERE resolved_time IS NULL;");
|
||||
return res.eof() ? 0 : res.getIntField("count");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ set(DDATABASES_DATABASES_SQLITE_TABLES_SOURCES
|
||||
"CharInfo.cpp"
|
||||
"CharXml.cpp"
|
||||
"CommandLog.cpp"
|
||||
"DashboardAuditLog.cpp"
|
||||
"DashboardConfig.cpp"
|
||||
"Friends.cpp"
|
||||
"IgnoreList.cpp"
|
||||
"Leaderboard.cpp"
|
||||
|
||||
@@ -83,3 +83,117 @@ bool SQLiteDatabase::IsNameInUse(const std::string_view name) {
|
||||
|
||||
return !result.eof();
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetCharacterCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM charinfo;");
|
||||
if (res.eof()) return 0;
|
||||
return res.getIntField("count");
|
||||
}
|
||||
|
||||
std::vector<ICharInfo::Info> SQLiteDatabase::GetAllCharactersPaginated(
|
||||
uint32_t offset,
|
||||
uint32_t limit,
|
||||
const std::string& orderColumn,
|
||||
const std::string& orderDir
|
||||
) {
|
||||
std::vector<ICharInfo::Info> out;
|
||||
|
||||
// Validate orderColumn to prevent SQL injection
|
||||
std::string validColumn = "id";
|
||||
if (orderColumn == "name" || orderColumn == "account_id" || orderColumn == "id" || orderColumn == "last_login") {
|
||||
validColumn = orderColumn;
|
||||
}
|
||||
|
||||
// Validate orderDir
|
||||
std::string validDir = (orderDir == "ASC" || orderDir == "asc") ? "ASC" : "DESC";
|
||||
|
||||
// Build query
|
||||
std::string query = "SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo ORDER BY " +
|
||||
validColumn + " " + validDir + " LIMIT ? OFFSET ?;";
|
||||
|
||||
auto [stmt, res] = ExecuteSelect(query, limit, offset);
|
||||
|
||||
while (!res.eof()) {
|
||||
ICharInfo::Info info;
|
||||
info.id = res.getInt64Field("id");
|
||||
info.name = res.getStringField("name");
|
||||
info.pendingName = res.getStringField("pending_name");
|
||||
info.needsRename = res.getIntField("needs_rename");
|
||||
info.cloneId = res.getInt64Field("prop_clone_id");
|
||||
info.accountId = res.getIntField("account_id");
|
||||
info.permissionMap = static_cast<ePermissionMap>(res.getIntField("permission_map"));
|
||||
out.push_back(info);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<ICharInfo::Info> SQLiteDatabase::GetCharactersWithPendingNames() {
|
||||
std::vector<ICharInfo::Info> out;
|
||||
|
||||
auto [stmt, res] = ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE pending_name != '' ORDER BY id ASC;");
|
||||
|
||||
while (!res.eof()) {
|
||||
ICharInfo::Info info;
|
||||
info.id = res.getInt64Field("id");
|
||||
info.name = res.getStringField("name");
|
||||
info.pendingName = res.getStringField("pending_name");
|
||||
info.needsRename = res.getIntField("needs_rename");
|
||||
info.cloneId = res.getInt64Field("prop_clone_id");
|
||||
info.accountId = res.getIntField("account_id");
|
||||
info.permissionMap = static_cast<ePermissionMap>(res.getIntField("permission_map"));
|
||||
out.push_back(info);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// SQLite-specific implementations added: UpdateCharacterPermissions and SetCharacterNeedsRename
|
||||
void SQLiteDatabase::UpdateCharacterPermissions(const LWOOBJID characterId, const ePermissionMap permissions) {
|
||||
ExecuteUpdate("UPDATE charinfo SET permission_map = ? WHERE id = ?;", static_cast<uint64_t>(permissions), characterId);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::SetCharacterNeedsRename(const LWOOBJID characterId, const bool needsRename) {
|
||||
ExecuteUpdate("UPDATE charinfo SET needs_rename = ? WHERE id = ?;", needsRename ? 1 : 0, characterId);
|
||||
}
|
||||
|
||||
std::optional<ICharInfo::Stats> SQLiteDatabase::GetCharacterStats(const LWOOBJID characterId) {
|
||||
// char_stats table doesn't exist in the current schema
|
||||
// Stats would need to be parsed from charxml or a new table created
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<ICharInfo::InventoryItem> SQLiteDatabase::GetCharacterInventory(const LWOOBJID characterId) {
|
||||
// Inventory data is stored in charxml, not a separate table
|
||||
// Would need to parse the XML to extract inventory items
|
||||
// Returning empty for now - implement XML parsing if needed
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<ICharInfo::Activity> SQLiteDatabase::GetCharacterActivity(const LWOOBJID characterId, uint32_t limit) {
|
||||
auto [_, res] = ExecuteSelect(
|
||||
"SELECT time, activity, map_id FROM activity_log WHERE character_id = ? ORDER BY time DESC LIMIT ?;",
|
||||
characterId, limit
|
||||
);
|
||||
|
||||
std::vector<ICharInfo::Activity> activities;
|
||||
while (!res.eof()) {
|
||||
ICharInfo::Activity activity;
|
||||
activity.timestamp = res.getInt64Field("time");
|
||||
activity.activity = static_cast<eActivityType>(res.getIntField("activity"));
|
||||
activity.mapId = res.getIntField("map_id");
|
||||
activities.push_back(activity);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return activities;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::RescueCharacter(const LWOOBJID characterId, uint32_t zoneId) {
|
||||
// The rescue is now handled by the chat server API which kicks the player
|
||||
// and modifies the XML after the player data is saved
|
||||
// This database method is intentionally a no-op as the actual work
|
||||
// is done via DashboardHelpers::RescueCharacter()
|
||||
}
|
||||
|
||||
@@ -3,3 +3,20 @@
|
||||
void SQLiteDatabase::InsertSlashCommandUsage(const LWOOBJID characterId, const std::string_view command) {
|
||||
ExecuteInsert("INSERT INTO command_log (character_id, command) VALUES (?, ?);", characterId, command);
|
||||
}
|
||||
|
||||
std::vector<ICommandLog::Entry> SQLiteDatabase::GetCommandLogs(uint32_t limit) {
|
||||
auto [_, result] = ExecuteSelect("SELECT id, character_id, command FROM command_log ORDER BY id DESC LIMIT ?;", limit);
|
||||
|
||||
std::vector<ICommandLog::Entry> logs;
|
||||
while (!result.eof()) {
|
||||
ICommandLog::Entry entry;
|
||||
entry.timestamp = 0; // Timestamp column doesn't exist in command_log table
|
||||
entry.characterId = result.getInt64Field("character_id");
|
||||
entry.command = result.getStringField("command");
|
||||
entry.arguments = ""; // Arguments not currently stored in DB
|
||||
logs.push_back(entry);
|
||||
result.nextRow();
|
||||
}
|
||||
|
||||
return logs;
|
||||
}
|
||||
|
||||
67
dDatabase/GameDatabase/SQLite/Tables/DashboardAuditLog.cpp
Normal file
67
dDatabase/GameDatabase/SQLite/Tables/DashboardAuditLog.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "SQLiteDatabase.h"
|
||||
#include <ctime>
|
||||
|
||||
void SQLiteDatabase::InsertAuditLog(const std::string_view ip, const std::string_view endpoint,
|
||||
const std::string_view method, const std::string_view user_agent,
|
||||
int32_t response_code) {
|
||||
uint64_t now = static_cast<uint64_t>(std::time(nullptr));
|
||||
ExecuteInsert("INSERT INTO dashboard_audit_log (timestamp, ip_address, endpoint, method, user_agent, response_code) VALUES (?, ?, ?, ?, ?, ?);",
|
||||
now, ip, endpoint, method, user_agent, response_code);
|
||||
}
|
||||
|
||||
std::vector<IDashboardAuditLog::AuditLogEntry> SQLiteDatabase::GetRecentAuditLogs(uint32_t limit) {
|
||||
auto [_, result] = ExecuteSelect("SELECT id, timestamp, ip_address, endpoint, method, user_agent, response_code FROM dashboard_audit_log ORDER BY timestamp DESC LIMIT ?;", limit);
|
||||
|
||||
std::vector<IDashboardAuditLog::AuditLogEntry> logs;
|
||||
while (result.nextRow()) {
|
||||
IDashboardAuditLog::AuditLogEntry entry;
|
||||
entry.id = result.getInt64Field(0);
|
||||
entry.timestamp = result.getInt64Field(1);
|
||||
entry.ip_address = result.getStringField(2);
|
||||
entry.endpoint = result.getStringField(3);
|
||||
entry.method = result.getStringField(4);
|
||||
entry.user_agent = result.getStringField(5);
|
||||
entry.response_code = result.getIntField(6);
|
||||
logs.push_back(entry);
|
||||
}
|
||||
|
||||
return logs;
|
||||
}
|
||||
|
||||
std::vector<IDashboardAuditLog::AuditLogEntry> SQLiteDatabase::GetAuditLogsByIP(const std::string_view ip, uint32_t limit) {
|
||||
auto [_, result] = ExecuteSelect("SELECT id, timestamp, ip_address, endpoint, method, user_agent, response_code FROM dashboard_audit_log WHERE ip_address = ? ORDER BY timestamp DESC LIMIT ?;", ip, limit);
|
||||
|
||||
std::vector<IDashboardAuditLog::AuditLogEntry> logs;
|
||||
while (result.nextRow()) {
|
||||
IDashboardAuditLog::AuditLogEntry entry;
|
||||
entry.id = result.getInt64Field(0);
|
||||
entry.timestamp = result.getInt64Field(1);
|
||||
entry.ip_address = result.getStringField(2);
|
||||
entry.endpoint = result.getStringField(3);
|
||||
entry.method = result.getStringField(4);
|
||||
entry.user_agent = result.getStringField(5);
|
||||
entry.response_code = result.getIntField(6);
|
||||
logs.push_back(entry);
|
||||
}
|
||||
|
||||
return logs;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::CleanupOldAuditLogs(uint32_t days_to_keep) {
|
||||
uint64_t cutoff_time = static_cast<uint64_t>(std::time(nullptr)) - (days_to_keep * 86400);
|
||||
ExecuteDelete("DELETE FROM dashboard_audit_log WHERE timestamp < ?;", cutoff_time);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::InsertAdminActionLog(uint32_t adminAccountId, const std::string_view action,
|
||||
const std::string_view targetType, uint64_t targetId,
|
||||
const std::string_view details) {
|
||||
// dashboard_admin_action_log table doesn't exist
|
||||
// Admin actions could be logged to dashboard_audit_log or a new table needs to be created
|
||||
// For now, this is a no-op
|
||||
}
|
||||
|
||||
std::vector<IDashboardAuditLog::AdminActionLog> SQLiteDatabase::GetAuditLogs(uint32_t limit) {
|
||||
// dashboard_admin_action_log table doesn't exist
|
||||
// Would need to create a new table or use dashboard_audit_log with additional columns
|
||||
return {};
|
||||
}
|
||||
15
dDatabase/GameDatabase/SQLite/Tables/DashboardConfig.cpp
Normal file
15
dDatabase/GameDatabase/SQLite/Tables/DashboardConfig.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "SQLiteDatabase.h"
|
||||
|
||||
std::optional<std::string> SQLiteDatabase::GetDashboardConfig(const std::string_view key) {
|
||||
auto [_, result] = ExecuteSelect("SELECT config_value FROM dashboard_config WHERE config_key = ?;", key);
|
||||
|
||||
if (result.nextRow()) {
|
||||
return result.getStringField(0);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::SetDashboardConfig(const std::string_view key, const std::string_view value) {
|
||||
ExecuteInsert("INSERT OR REPLACE INTO dashboard_config (config_key, config_value, updated_at) VALUES (?, ?, strftime('%s', 'now'));", key, value);
|
||||
}
|
||||
@@ -24,3 +24,46 @@ std::optional<IPetNames::Info> SQLiteDatabase::GetPetNameInfo(const LWOOBJID& pe
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::vector<IPetNames::DetailedInfo> SQLiteDatabase::GetAllPetNames() {
|
||||
std::vector<IPetNames::DetailedInfo> out;
|
||||
auto [stmt, res] = ExecuteSelect("SELECT id, pet_name, approved, owner_id FROM pet_names ORDER BY id DESC;");
|
||||
|
||||
while (!res.eof()) {
|
||||
IPetNames::DetailedInfo info;
|
||||
info.id = static_cast<LWOOBJID>(res.getInt64Field("id"));
|
||||
info.petName = res.getStringField("pet_name");
|
||||
info.approvalStatus = res.getIntField("approved");
|
||||
info.ownerId = static_cast<LWOOBJID>(res.getInt64Field("owner_id"));
|
||||
out.push_back(info);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<IPetNames::DetailedInfo> SQLiteDatabase::GetPetNamesByStatus(int32_t status) {
|
||||
std::vector<IPetNames::DetailedInfo> out;
|
||||
auto [stmt, res] = ExecuteSelect("SELECT id, pet_name, approved, owner_id FROM pet_names WHERE approved = ? ORDER BY id DESC;", status);
|
||||
|
||||
while (!res.eof()) {
|
||||
IPetNames::DetailedInfo info;
|
||||
info.id = static_cast<LWOOBJID>(res.getInt64Field("id"));
|
||||
info.petName = res.getStringField("pet_name");
|
||||
info.approvalStatus = res.getIntField("approved");
|
||||
info.ownerId = static_cast<LWOOBJID>(res.getInt64Field("owner_id"));
|
||||
out.push_back(info);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::SetPetApprovalStatus(const LWOOBJID& petId, int32_t status) {
|
||||
ExecuteUpdate("UPDATE pet_names SET approved = ? WHERE id = ?;", status, petId);
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetPendingPetNamesCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM pet_names WHERE approved = 1;");
|
||||
return res.eof() ? 0 : res.getIntField("count");
|
||||
}
|
||||
|
||||
@@ -9,3 +9,118 @@ std::optional<bool> SQLiteDatabase::IsPlaykeyActive(const int32_t playkeyId) {
|
||||
|
||||
return keyCheckRes.getIntField("active");
|
||||
}
|
||||
|
||||
std::vector<IPlayKeys::Info> SQLiteDatabase::GetAllPlayKeys() {
|
||||
std::vector<IPlayKeys::Info> out;
|
||||
auto [stmt, res] = ExecuteSelect(
|
||||
"SELECT id, key_string, key_uses, times_used, active, notes, strftime('%s', created_at) as created_at "
|
||||
"FROM play_keys ORDER BY id DESC;"
|
||||
);
|
||||
|
||||
while (!res.eof()) {
|
||||
IPlayKeys::Info info;
|
||||
info.id = res.getIntField("id");
|
||||
info.key_string = res.getStringField("key_string");
|
||||
info.key_uses = res.getIntField("key_uses");
|
||||
info.times_used = res.getIntField("times_used");
|
||||
info.active = res.getIntField("active") != 0;
|
||||
info.notes = res.getStringField("notes");
|
||||
info.created_at = static_cast<uint64_t>(res.getInt64Field("created_at"));
|
||||
out.push_back(info);
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<IPlayKeys::Info> SQLiteDatabase::GetPlayKeyById(const uint32_t playkeyId) {
|
||||
auto [_, result] = ExecuteSelect(
|
||||
"SELECT id, key_string, key_uses, times_used, active, notes, strftime('%s', created_at) as created_at "
|
||||
"FROM play_keys WHERE id = ? LIMIT 1;",
|
||||
playkeyId
|
||||
);
|
||||
|
||||
if (result.eof()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
IPlayKeys::Info info;
|
||||
info.id = result.getIntField("id");
|
||||
info.key_string = result.getStringField("key_string");
|
||||
info.key_uses = result.getIntField("key_uses");
|
||||
info.times_used = result.getIntField("times_used");
|
||||
info.active = result.getIntField("active") != 0;
|
||||
info.notes = result.getStringField("notes");
|
||||
info.created_at = static_cast<uint64_t>(result.getInt64Field("created_at"));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
std::optional<IPlayKeys::Info> SQLiteDatabase::GetPlayKeyByString(const std::string_view key_string) {
|
||||
auto [_, result] = ExecuteSelect(
|
||||
"SELECT id, key_string, key_uses, times_used, active, notes, strftime('%s', created_at) as created_at "
|
||||
"FROM play_keys WHERE key_string = ? LIMIT 1;",
|
||||
key_string
|
||||
);
|
||||
|
||||
if (result.eof()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
IPlayKeys::Info info;
|
||||
info.id = result.getIntField("id");
|
||||
info.key_string = result.getStringField("key_string");
|
||||
info.key_uses = result.getIntField("key_uses");
|
||||
info.times_used = result.getIntField("times_used");
|
||||
info.active = result.getIntField("active") != 0;
|
||||
info.notes = result.getStringField("notes");
|
||||
info.created_at = static_cast<uint64_t>(result.getInt64Field("created_at"));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
bool SQLiteDatabase::ConsumePlayKeyUsage(const uint32_t playkeyId) {
|
||||
// Atomically check times_used < key_uses and increment times_used. If uses exhausted, deactivate.
|
||||
auto [stmt, res] = ExecuteSelect("SELECT key_uses, times_used, active FROM play_keys WHERE id = ? LIMIT 1;", playkeyId);
|
||||
if (res.eof()) return false;
|
||||
|
||||
int key_uses = res.getIntField("key_uses");
|
||||
int times_used = res.getIntField("times_used");
|
||||
int active = res.getIntField("active");
|
||||
|
||||
if (!active) return false;
|
||||
if (times_used >= key_uses) return false;
|
||||
|
||||
// Increment times_used
|
||||
ExecuteUpdate("UPDATE play_keys SET times_used = times_used + 1 WHERE id = ?;", playkeyId);
|
||||
|
||||
// If we've reached the limit, deactivate
|
||||
if (times_used + 1 >= key_uses) {
|
||||
ExecuteUpdate("UPDATE play_keys SET active = 0 WHERE id = ?;", playkeyId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SQLiteDatabase::CreatePlayKey(const std::string_view key_string, uint32_t uses, const std::string_view notes) {
|
||||
ExecuteInsert(
|
||||
"INSERT INTO play_keys (key_string, key_uses, times_used, active, notes) VALUES (?, ?, 0, 1, ?);",
|
||||
key_string, uses, notes
|
||||
);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::UpdatePlayKey(const uint32_t playkeyId, uint32_t uses, bool active, const std::string_view notes) {
|
||||
ExecuteUpdate(
|
||||
"UPDATE play_keys SET key_uses = ?, active = ?, notes = ? WHERE id = ?;",
|
||||
uses, active ? 1 : 0, notes, playkeyId
|
||||
);
|
||||
}
|
||||
|
||||
void SQLiteDatabase::DeletePlayKey(const uint32_t playkeyId) {
|
||||
ExecuteDelete("DELETE FROM play_keys WHERE id = ?;", playkeyId);
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetPlayKeyCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM play_keys;");
|
||||
return res.eof() ? 0 : res.getIntField("count");
|
||||
}
|
||||
|
||||
@@ -193,10 +193,53 @@ std::optional<IProperty::Info> SQLiteDatabase::GetPropertyInfo(const LWOOBJID id
|
||||
auto [_, propertyEntry] = ExecuteSelect(
|
||||
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved, performance_cost "
|
||||
"FROM properties WHERE id = ?;", id);
|
||||
|
||||
|
||||
if (propertyEntry.eof()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return ReadPropertyInfo(propertyEntry);
|
||||
}
|
||||
|
||||
std::vector<IProperty::Info> SQLiteDatabase::GetAllProperties() {
|
||||
std::vector<IProperty::Info> out;
|
||||
auto [stmt, res] = ExecuteSelect(
|
||||
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, "
|
||||
"last_updated, time_claimed, reputation, mod_approved, performance_cost "
|
||||
"FROM properties ORDER BY id DESC;"
|
||||
);
|
||||
|
||||
while (!res.eof()) {
|
||||
out.push_back(ReadPropertyInfo(res));
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<IProperty::Info> SQLiteDatabase::GetPropertiesByApprovalStatus(uint32_t approved) {
|
||||
std::vector<IProperty::Info> out;
|
||||
auto [stmt, res] = ExecuteSelect(
|
||||
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, "
|
||||
"last_updated, time_claimed, reputation, mod_approved, performance_cost "
|
||||
"FROM properties WHERE mod_approved = ? ORDER BY id DESC;",
|
||||
approved
|
||||
);
|
||||
|
||||
while (!res.eof()) {
|
||||
out.push_back(ReadPropertyInfo(res));
|
||||
res.nextRow();
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetPropertyCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM properties;");
|
||||
return res.eof() ? 0 : res.getIntField("count");
|
||||
}
|
||||
|
||||
uint32_t SQLiteDatabase::GetUnapprovedPropertyCount() {
|
||||
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM properties WHERE mod_approved = 0;");
|
||||
return res.eof() ? 0 : res.getIntField("count");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user