Commit inicial

This commit is contained in:
jaguerrau
2025-10-28 21:08:07 -06:00
parent 63cadcaf72
commit 82cd7c9a45
52 changed files with 5869 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
#Lines starting with '#' are comments.
#Backslash character at the end of line means that the command continues in the next line.
#DATABASE CONFIGURATION
DriverClass=com.mysql.jdbc.Driver
JdbcUrl=jdbc:mysql://localhost/$DB$?characterEncoding=utf8
User=root
Password=
#Java server port
ServerPort=17178
#example of MS SQL Server configuration:
#DriverClass=net.sourceforge.jtds.jdbc.Driver
#JdbcUrl=jdbc:jtds:sqlserver://<server address>/<database>
#example of postegres configuration:
#JdbcUrl=jdbc:postgresql://localhost/test
#DriverClass=org.postgresql.Driver
#SQL COMMANDS
sql.create_table=CREATE TABLE IF NOT EXISTS animals (\
id INTEGER PRIMARY KEY AUTO_INCREMENT,\
name CHAR(30) NOT NULL,\
image BLOB)
sql.insert_animal=INSERT INTO animals VALUES (null, ?,?)
sql.select_animal=SELECT name, image, id FROM animals WHERE id = ?;
sql.create_table=CREATE TABLE article (col1 numeric(10,4) ,col2 text);
sql.select=select * from article
sql.insert=INSERT INTO article VALUES(?, ?)

View File

@@ -0,0 +1,29 @@
#Lines starting with '#' are comments.
#Backslash character at the end of line means that the command continues in the next line.
#DATABASE CONFIGURATION
DriverClass=com.mysql.jdbc.Driver
JdbcUrl=jdbc:mysql://localhost/$DB$?characterEncoding=utf8
User=root
Password=
#Java server port
ServerPort=17178
#example of MS SQL Server configuration:
#DriverClass=net.sourceforge.jtds.jdbc.Driver
#JdbcUrl=jdbc:jtds:sqlserver://<server address>/<database>
#example of postegres configuration:
#JdbcUrl=jdbc:postgresql://localhost/test
#DriverClass=org.postgresql.Driver
#SQL COMMANDS
sql.create_table=CREATE TABLE IF NOT EXISTS animals (\
id INTEGER PRIMARY KEY AUTO_INCREMENT,\
name CHAR(30) NOT NULL,\
image BLOB)
sql.insert_animal=INSERT INTO animals VALUES (null, ?,?)
sql.select_animal=SELECT name, image, id FROM animals WHERE id = ?;
sql.create_table=CREATE TABLE article (col1 numeric(10,4) ,col2 text);
sql.select=select * from article
sql.insert=INSERT INTO article VALUES(?, ?)

View File

@@ -0,0 +1,29 @@
#Lines starting with '#' are comments.
#Backslash character at the end of line means that the command continues in the next line.
#DATABASE CONFIGURATION
DriverClass=com.mysql.jdbc.Driver
JdbcUrl=jdbc:mysql://localhost/$DB$?characterEncoding=utf8
User=root
Password=
#Java server port
ServerPort=17178
#example of MS SQL Server configuration:
#DriverClass=net.sourceforge.jtds.jdbc.Driver
#JdbcUrl=jdbc:jtds:sqlserver://<server address>/<database>
#example of postegres configuration:
#JdbcUrl=jdbc:postgresql://localhost/test
#DriverClass=org.postgresql.Driver
#SQL COMMANDS
sql.create_table=CREATE TABLE IF NOT EXISTS animals (\
id INTEGER PRIMARY KEY AUTO_INCREMENT,\
name CHAR(30) NOT NULL,\
image BLOB)
sql.insert_animal=INSERT INTO animals VALUES (null, ?,?)
sql.select_animal=SELECT name, image, id FROM animals WHERE id = ?;
sql.create_table=CREATE TABLE article (col1 numeric(10,4) ,col2 text);
sql.select=select * from article
sql.insert=INSERT INTO article VALUES(?, ?)

29
Files/config.properties Normal file
View File

@@ -0,0 +1,29 @@
#Lines starting with '#' are comments.
#Backslash character at the end of line means that the command continues in the next line.
#DATABASE CONFIGURATION
DriverClass=com.mysql.jdbc.Driver
JdbcUrl=jdbc:mysql://localhost/$DB$?characterEncoding=utf8
User=root
Password=
#Java server port
ServerPort=17178
#example of MS SQL Server configuration:
#DriverClass=net.sourceforge.jtds.jdbc.Driver
#JdbcUrl=jdbc:jtds:sqlserver://<server address>/<database>
#example of postegres configuration:
#JdbcUrl=jdbc:postgresql://localhost/test
#DriverClass=org.postgresql.Driver
#SQL COMMANDS
sql.create_table=CREATE TABLE IF NOT EXISTS animals (\
id INTEGER PRIMARY KEY AUTO_INCREMENT,\
name CHAR(30) NOT NULL,\
image BLOB)
sql.insert_animal=INSERT INTO animals VALUES (null, ?,?)
sql.select_animal=SELECT name, image, id FROM animals WHERE id = ?;
sql.create_table=CREATE TABLE article (col1 numeric(10,4) ,col2 text);
sql.select=select * from article
sql.insert=INSERT INTO article VALUES(?, ?)

View File

@@ -0,0 +1,9 @@
@rem Este script reinicia el proceso en PM2 del servidor de jRDC2
@rem estas lineas sirven para que el archivo bat corra en modo administrador.
set "params=%*"
cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && ""%~s0"" %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && exit /B )
pm2 restart BotSoporte_4.0
exit

View File

@@ -0,0 +1,9 @@
@rem Este script reinicia el proceso en PM2 del servidor de jRDC2
@rem estas lineas sirven para que el archivo bat corra en modo administrador.
set "params=%*"
cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && ""%~s0"" %params%", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && exit /B )
pm2 restart jRDC-Multi
exit

8
Files/start.bat Normal file
View File

@@ -0,0 +1,8 @@
@rem Este script mata el proceso del servidor y despues lo reinicia, necesita los archivos stop.bat y start2.bat
start cmd.exe /c stop.bat
timeout 2
start cmd.exe /c start2.bat %1
exit

3
Files/start2.bat Normal file
View File

@@ -0,0 +1,3 @@
@TITLE -== DBR Server %1 %2 ==-
"C:\Program Files (x86)\Java\jdk-14\bin\java.exe" -jar jRDC_Multi.jar

1
Files/stop.bat Normal file
View File

@@ -0,0 +1 @@
wmic Path win32_process Where "CommandLine Like '%%jRDC_Multi.jar%%'" Call Terminate

21
Files/www/login.html Normal file
View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Login jRDC Server</title>
<style>
body { font-family: sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f0f0; }
form { background: white; padding: 2em; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
input { display: block; margin-bottom: 1em; padding: 0.5em; width: 200px; }
button { padding: 0.7em; width: 100%; border: none; background-color: #007bff; color: white; cursor: pointer; border-radius: 4px; }
</style>
</head>
<body>
<form action="/dologin" method="post">
<h2>Acceso al Manager</h2>
<input type="text" name="username" placeholder="Usuario" required>
<input type="password" name="password" placeholder="Contraseña" required>
<button type="submit">Entrar</button>
</form>
</body>
</html>

392
Files/www/manager.html Normal file
View File

@@ -0,0 +1,392 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>jRDC2-Multi - Panel de Administración</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Helvetica, Arial, sans-serif;
background-color: #f8f9fa;
color: #212529;
margin: 0;
padding: 0;
}
.admin-menu {
background-color: #e9ecef;
padding: 1.5em 2em;
border-bottom: 1px solid #dee2e6;
}
.admin-menu h2 {
margin: 0 0 0.2em 0;
color: #343a40;
}
.admin-menu p {
margin: 0 0 1em 0;
color: #495057;
}
.admin-menu a {
color: #007bff;
text-decoration: none;
font-weight: 500;
margin-right: 0.5em;
padding-right: 0.5em;
border-right: 1px solid #ccc;
cursor: pointer;
}
.admin-menu a:last-child {
border-right: none;
}
.admin-menu a:hover {
text-decoration: underline;
}
.main-content {
padding: 2em;
}
#output-container {
background: #fff;
padding: 1em;
border: 1px solid #eee;
border-radius: 8px;
font-family: monospace;
white-space: pre-wrap;
word-wrap: break-word;
min-height: 200px;
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 1.5em;
font-family: sans-serif;
font-size: 0.9em;
}
th,
td {
padding: 10px 12px;
text-align: center;
border-bottom: 1px solid #dee2e6;
}
thead {
background-color: #007bff;
color: #fff;
}
th {
font-weight: 600;
cursor: help;
}
tbody td:first-child {
font-weight: bold;
color: #0056b3;
text-align: left;
}
/* --- ESTILOS PARA EL INDICADOR SSE --- */
.sse-status-container {
display: flex;
align-items: center;
margin-top: 1em;
font-size: 0.8em;
}
.sse-status {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-weight: bold;
margin-left: 10px;
min-width: 80px;
text-align: center;
}
.sse-connected {
background-color: #28a745;
color: white;
}
.sse-disconnected {
background-color: #dc3545;
color: white;
}
.sse-connecting {
background-color: #ffc107;
color: #212529;
}
.cell-update {
animation: flash 0.7s ease-out;
}
@keyframes flash {
0% {
background-color: #ffc107;
}
100% {
background-color: transparent;
}
}
</style>
</head>
<body>
<div class="admin-menu">
<h2>Panel de Administración jRDC</h2>
<p>Bienvenido, <strong>admin</strong></p>
<nav id="main-nav">
<a data-command="test">Test</a>
<a data-command="ping">Ping</a>
<a data-command="reload">Reload</a>
<a data-command="slowqueries">Queries Lentas</a>
<a data-command="getstats">Estadísticas Pool</a>
<a data-command="rpm2">Reiniciar (pm2)</a>
<a data-command="reviveBow">Revive Bow</a>
<a data-command="getconfiginfo">Info</a>
</nav>
<div class="sse-status-container">
<span>Estado de Estadísticas en Tiempo Real:</span>
<span id="sse-status" class="sse-status sse-disconnected"
>Desconectado</span
>
</div>
</div>
<div class="main-content">
<h1 id="content-title">Bienvenido</h1>
<div id="output-container">
<p style="font-family: sans-serif">
Selecciona una opción del menú para comenzar.
</p>
</div>
</div>
<script>
const outputContainer = document.getElementById("output-container");
const contentTitle = document.getElementById("content-title");
const sseStatus = document.getElementById("sse-status");
let sseConnection = null;
// --- CONFIGURACIÓN PARA LA TABLA DE ESTADÍSTICAS ---
const COLUMN_ORDER = [
"InitialPoolSize",
"MinPoolSize",
"MaxPoolSize",
"AcquireIncrement",
"TotalConnections",
"BusyConnections",
"IdleConnections",
"CheckoutTimeout",
"MaxIdleTime",
"MaxConnectionAge",
];
const HEADER_TOOLTIPS = {
InitialPoolSize:
"Número de conexiones que el pool intenta adquirir al arrancar.",
MinPoolSize: "Número mínimo de conexiones que el pool mantendrá.",
MaxPoolSize: "Número máximo de conexiones que el pool puede mantener.",
AcquireIncrement:
"Número de conexiones a adquirir cuando el pool se queda sin conexiones.",
TotalConnections: "Número total de conexiones (ocupadas + libres).",
BusyConnections: "Número de conexiones activamente en uso.",
IdleConnections: "Número de conexiones disponibles en el pool.",
CheckoutTimeout: "Tiempo máximo de espera por una conexión (ms).",
MaxIdleTime:
"Tiempo máximo que una conexión puede estar inactiva (segundos).",
MaxConnectionAge: "Tiempo máximo de vida de una conexión (segundos).",
};
// --- MANEJO DE LA CONEXIÓN SSE ---
function connectSSE() {
if (sseConnection && sseConnection.readyState !== EventSource.CLOSED) {
return; // Ya está conectado o conectando
}
outputContainer.innerHTML =
'<p style="font-family: sans-serif;">Esperando datos del pool de conexiones...</p>';
updateSSEStatus("connecting");
// La ruta debe coincidir con la que registraste en srvr.AddHandler
const SSE_ENDPOINT = "/stats-stream";
sseConnection = new EventSource(SSE_ENDPOINT);
sseConnection.onopen = () => {
console.log("Conexión SSE establecida.");
updateSSEStatus("connected");
};
// Escucha el evento específico "stats_update"
sseConnection.addEventListener("stats_update", (event) => {
try {
const data = JSON.parse(event.data);
renderOrUpdateStatsTable(data);
} catch (e) {
console.error("Error al parsear datos SSE:", e);
}
});
sseConnection.onerror = () => {
console.error("Error en la conexión SSE. Reintentando...");
updateSSEStatus("disconnected");
sseConnection.close();
// El navegador reintentará automáticamente la conexión
};
}
function disconnectSSE() {
if (sseConnection) {
sseConnection.close();
sseConnection = null;
console.log("Conexión SSE cerrada.");
updateSSEStatus("disconnected");
}
}
function updateSSEStatus(status) {
switch (status) {
case "connected":
sseStatus.textContent = "Conectado";
sseStatus.className = "sse-status sse-connected";
break;
case "connecting":
sseStatus.textContent = "Conectando";
sseStatus.className = "sse-status sse-connecting";
break;
case "disconnected":
sseStatus.textContent = "Desconectado";
sseStatus.className = "sse-status sse-disconnected";
break;
}
}
// --- RENDERIZADO Y ACTUALIZACIÓN DE LA TABLA ---
function renderOrUpdateStatsTable(data) {
const table = document.getElementById("stats-table");
// Si la tabla no existe, la crea
if (!table) {
outputContainer.innerHTML = createStatsTableHTML(data);
return;
}
// Si la tabla ya existe, solo actualiza las celdas
for (const dbKey in data) {
const poolData = data[dbKey];
COLUMN_ORDER.forEach((metric) => {
const cell = document.getElementById(`${dbKey}_${metric}`);
if (cell && cell.textContent != poolData[metric]) {
cell.textContent = poolData[metric];
cell.classList.add("cell-update");
setTimeout(() => cell.classList.remove("cell-update"), 700);
}
});
}
}
function createStatsTableHTML(data) {
let tableHtml = `<table id="stats-table"><thead><tr><th title="Nombre de la Base de Datos">DB Key</th>`;
COLUMN_ORDER.forEach((key) => {
tableHtml += `<th title="${HEADER_TOOLTIPS[key] || ""}">${key}</th>`;
});
tableHtml += `</tr></thead><tbody>`;
for (const dbKey in data) {
const poolData = data[dbKey];
tableHtml += `<tr><td>${dbKey}</td>`;
COLUMN_ORDER.forEach((metric) => {
// Se añade un ID único a cada celda: "DB1_TotalConnections"
tableHtml += `<td id="${dbKey}_${metric}">${
poolData[metric] ?? "N/A"
}</td>`;
});
tableHtml += `</tr>`;
}
tableHtml += `</tbody></table>`;
return tableHtml;
}
// --- MANEJO DE COMANDOS ESTÁTICOS (SIN CAMBIOS) ---
async function loadStaticContent(command) {
contentTitle.textContent = `Resultado del Comando: '${command}'`;
outputContainer.innerHTML = "Cargando...";
try {
const response = await fetch(`/manager?command=${command}`);
const responseText = await response.text();
if (!response.ok)
throw new Error(
`Error del servidor (${response.status}): ${responseText}`
);
const contentType = response.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
const data = JSON.parse(responseText);
if (command === "slowqueries") {
outputContainer.innerHTML = data.message
? `<p>${data.message}</p>`
: createTableFromJSON(data.data);
} else {
outputContainer.textContent = JSON.stringify(data, null, 2);
}
} else {
outputContainer.textContent = responseText;
}
} catch (error) {
outputContainer.textContent = `Error al procesar la respuesta:\n${error.message}`;
}
}
function createTableFromJSON(data) {
if (!data || data.length === 0)
return "<p>No se encontraron queries lentas.</p>";
const headers = Object.keys(data[0]);
let table = "<table><thead><tr>";
headers.forEach((h) => (table += `<th>${h.replace(/_/g, " ")}</th>`));
table += "</tr></thead><tbody>";
data.forEach((row) => {
table += "<tr>";
headers.forEach((h) => (table += `<td>${row[h]}</td>`));
table += "</tr>";
});
table += "</tbody></table>";
return table;
}
// --- EVENT LISTENER PRINCIPAL ---
document.getElementById("main-nav").addEventListener("click", (event) => {
if (event.target.tagName === "A") {
const command = event.target.dataset.command;
if (!command) return;
if (command === "reload") {
// Pedimos al usuario la DB Key. Si la deja vacía, se asume recarga total.
const dbKey = prompt(
"Ingrese la llave de la DB a recargar (ej: DB2, DB3). Deje vacío para recargar TODAS:",
""
);
if (dbKey === null) {
// El usuario presionó Cancelar o cerró la ventana. NO HACER NADA.
outputContainer.textContent = "Recarga cancelada por el usuario.";
contentTitle.textContent = "Administración";
return;
}
let finalCommand = "reload";
if (dbKey && dbKey.trim() !== "") {
// Si el usuario especificó una DB (ej. DB2), construimos el comando con el parámetro 'db'.
const key = dbKey.toUpperCase().trim();
finalCommand = `reload&db=${key}`;
outputContainer.innerHTML = `<p style="font-family: sans-serif;">Intentando recargar: <b>${key}</b> (Hot-Swap)...</p>`;
} else {
// Recarga total.
outputContainer.innerHTML = `<p style="font-family: sans-serif;">Intentando recargar: <b>TODAS</b> (Hot-Swap)...</p>`;
}
disconnectSSE();
// Llamamos a la función loadStaticContent con el comando completo (ej: 'reload&db=DB2' o 'reload')
loadStaticContent(finalCommand);
} else if (command === "getstats") {
contentTitle.textContent = `Estadísticas del Pool en Tiempo Real`;
connectSSE();
} else {
// Si se selecciona cualquier otro comando, se desconecta del SSE
disconnectSSE();
loadStaticContent(command);
}
}
});
</script>
</body>
</html>