const crypto = require("node:crypto") const logger = require("../modules/logger") const database = require("../modules/database") const { DefaultError } = require("../errors/errors") async function addPropertyToPlayer(key, value, uuid) { try { const sql = `INSERT INTO playersProperties (name, value, uuid) VALUES (?, ?, ?)` const result = await database.query(sql, [key, value, uuid]) if (result.affectedRows > 0) { return { code: 200, key, value, uuid } } else { throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } catch (error) { logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function deletePropertyToPlayer(key, uuid) { try { const sql = `DELETE FROM playersProperties WHERE name = ? AND uuid = ?` const result = await database.query(sql, [key, uuid]) if (result.affectedRows > 0) { return { code: 200, key, uuid } } else { throw new DefaultError(500, "Property not found for this user/key combination.") } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function updatePropertyToPlayer(key, value, uuid) { try { const sql = `UPDATE playersProperties SET value = ? WHERE name = ? AND uuid = ?` const result = await database.query(sql, [value, key, uuid]) if (result.affectedRows > 0) { return { code: 200, key, value, uuid } } else { throw new DefaultError(404, "Property not found for this user/key combination") } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function getPlayerProperties(uuid) { try { const sql = `SELECT * FROM playersProperties WHERE uuid = ?` const rows = await database.query(sql, [uuid]) if (rows.length === 0) { throw new DefaultError(404, "Properties not found for this user") } return { code: 200, properties: rows.map(property => ({ name: property.name, value: property.value })) } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function getPlayerProperty(key, uuid) { try { const sql = `SELECT * FROM playersProperties WHERE name = ? AND uuid = ?` const rows = await database.query(sql, [key, uuid]) const property = rows[0] if (!property) { throw new DefaultError(404, "Property not found for this user/key combination") } return { code: 200, property } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function getPlayerSettingsSchema() { const RAW_SCHEMA_CACHE = { privileges: {}, preferences: {} } try { const privilegesRows = await database.query("DESCRIBE playersPrivileges") const preferencesRows = await database.query("DESCRIBE playersPreferences") RAW_SCHEMA_CACHE.privileges = privilegesRows.map(c => c.Field).filter(n => n !== "uuid") RAW_SCHEMA_CACHE.preferences = preferencesRows.map(c => c.Field).filter(n => n !== "uuid") return RAW_SCHEMA_CACHE } catch (err) { logger.log("Database Schema Error: " + err.toString(), ["MariaDB", "red"]) throw new DefaultError(500, "Internal Server Error", "Database Schema Error") } } async function updatePlayerPreferences(uuid, updates) { try { const keys = Object.keys(updates) if (keys.length === 0) { throw new DefaultError(400, "No fields provided for update.") } const setClause = keys.map(key => `\`${key}\` = ?`).join(', ') const sql = `UPDATE playersPreferences SET ${setClause} WHERE uuid = ?` const values = keys.map(key => updates[key]) values.push(uuid) const result = await database.query(sql, values) if (result.affectedRows > 0) { return { code: 200, message: "Preferences updated successfully." } } else { throw new DefaultError(404, "Player preferences not found or no changes made.") } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function getPlayerPreferences(uuid) { try { const sql = `SELECT profanityFilter FROM playersPreferences WHERE uuid = ?` const rows = await database.query(sql, [uuid]) const data = rows[0] if (data) { return { code: 200, message: "Preferences retrieved successfully.", data: data } } else { throw new DefaultError(404, "Preferences not found for this UUID.") } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function getPlayerPrivileges(uuid) { try { const sql = ` SELECT onlineChat, multiplayerServer, multiplayerRealms, telemetry FROM playersPrivileges WHERE uuid = ? ` const rows = await database.query(sql, [uuid]) const data = rows[0] if (data) { return { code: 200, message: "Privileges retrieved successfully.", data: data } } else { throw new DefaultError(404, "Privileges not found for this UUID.") } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function updatePlayerPrivileges(uuid, updates) { try { const keys = Object.keys(updates) if (keys.length === 0) { throw new DefaultError(404, "No fields provided for update.") } const setClause = keys.map(key => `\`${key}\` = ?`).join(', ') const sql = `UPDATE playersPrivileges SET ${setClause} WHERE uuid = ?` const values = keys.map(key => updates[key]) values.push(uuid) const result = await database.query(sql, values) if (result.affectedRows > 0) { return { code: 200, message: "Privileges updated successfully." } } else { throw new DefaultError(404, "Player privileges not found or no changes made.") } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function banUser(uuid, { reasonKey, reasonMessage, expires = null }) { try { if (!uuid || !reasonKey) { throw new DefaultError(400, "Missing uuid or reasonKey.") } let reasonId const reasonRows = await database.query("SELECT id FROM banReasons WHERE reason_key = ?", [reasonKey]) if (reasonRows.length > 0) { reasonId = reasonRows[0].id } else { const insertReason = await database.query("INSERT INTO banReasons (reason_key) VALUES (?)", [reasonKey]) reasonId = insertReason.insertId } const banId = crypto.randomUUID() const insertSql = ` INSERT INTO bans (banId, uuid, reason, reasonMessage, expires) VALUES (?, ?, ?, ?, ?) ` const result = await database.query(insertSql, [banId, uuid, reasonId, reasonMessage || "Banned by operator", expires]) if (result.affectedRows > 0) { return { code: 200, message: "User successfully banned.", banId: banId } } else { throw new DefaultError(500, "Failed to ban user.") } } catch (error) { if (error instanceof DefaultError) throw error if (error.code === "ER_NO_REFERENCED_ROW_2" || error.toString().includes("foreign key constraint")) { throw new DefaultError(404, "User not found (cannot ban a ghost).") } logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Internal Server Error", error.toString()) } } async function unbanUser(uuid) { try { if (!uuid) { throw new DefaultError(400, "Missing uuid.") } const sql = "DELETE FROM bans WHERE uuid = ?" const result = await database.query(sql, [uuid]) if (result.affectedRows > 0) { return { code: 200, message: "User successfully unbanned.", count: result.affectedRows } } else { throw new DefaultError(404, "User was not banned.") } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } async function getPlayerBans(uuid) { try { const sql = ` SELECT b.banId, b.expires, b.reasonMessage, r.reason_key as reason FROM bans b JOIN banReasons r ON b.reason = r.id WHERE b.uuid = ? ORDER BY b.expires ASC ` const rows = await database.query(sql, [uuid]) if (rows.length > 0) { return { code: 200, bans: rows } } else { return { code: 204 } } } catch (error) { if (error instanceof DefaultError) throw error logger.log("Internal Server Error".bold + " : " + error.toString(), ["MariaDB", "yellow"]) throw new DefaultError(500, "Please contact an administrator.", "InternalServerError") } } module.exports = { banUser, unbanUser, getPlayerBans, getPlayerProperty, getPlayerPrivileges, getPlayerProperties, addPropertyToPlayer, getPlayerPreferences, updatePlayerPrivileges, deletePropertyToPlayer, updatePropertyToPlayer, getPlayerSettingsSchema, updatePlayerPreferences, }