feat: full de-Angularization with Bootstrap 5 migration

- Removed AngularJS 1.4.8 completely — no more framework dependency
- Extracted core RCON logic into pure vanilla JS (js/rcon.js)
- Added lightweight hash-based SPA router (js/router.js)
- Rewrote all 6 pages as vanilla JS modules in js/pages/
- Migrated Bootstrap 3 → Bootstrap 5 (cards instead of panels, bootstrap-icons instead of glyphicons)
- Removed jQuery dependency (Bootstrap 5 is vanilla JS)
- Purged all Angular templates from html/
- Removed npm artifacts (package.json, node_modules)
- Added nginx.example.conf for static hosting
- Updated rcon.css dark theme for BS5 variables
- Static file only — no Node.js backend required

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-06-13 17:51:50 +02:00
co-authored by Copilot
parent 0fd94044a8
commit 0a178d17b9
32 changed files with 1242 additions and 1702 deletions
+181
View File
@@ -0,0 +1,181 @@
/**
* Rust WebSocket RCON Client — pure vanilla JS, zero dependencies.
*
* Protocol:
* - Connect: ws://host:port/password
* - Outgoing: { Identifier: int, Message: string, Name: "WebRcon" }
* - Identifier > 1000 → request/response (callback tracked)
* - Identifier <= 1000 → unsolicited server push (Generic/Log/Error/Warning/Chat)
*
* Usage:
* Rcon.on('connected', () => { ... });
* Rcon.on('disconnected', (ev) => { ... });
* Rcon.on('message', (msg) => { ... });
* Rcon.connect('192.168.1.100:28016', 'mypassword');
* Rcon.request('serverinfo', (msg) => { console.log(JSON.parse(msg.Message)); });
* Rcon.command('say Hello world');
* Rcon.getPlayers((players) => { ... });
*/
var Rcon = (function () {
'use strict';
var ConnectionStatus = {
CONNECTING: 0,
OPEN: 1,
CLOSING: 2,
CLOSED: 3
};
var socket = null;
var address = null;
var callbacks = {};
var lastIndex = 1001;
var listeners = {};
/**
* Simple pub/sub event emitter.
*/
function emit(event, data) {
var cbs = listeners[event];
if (cbs) {
for (var i = 0; i < cbs.length; i++) {
cbs[i](data);
}
}
}
/**
* Connect to a Rust server's WebSocket RCON.
* @param {string} addr - "host:port"
* @param {string} pass - RCON password
*/
function connect(addr, pass) {
disconnect();
address = addr;
socket = new WebSocket('ws://' + addr + '/' + pass);
socket.onopen = function () {
emit('connected');
};
socket.onclose = function (ev) {
emit('disconnected', ev);
};
socket.onerror = function (ev) {
emit('error', ev);
};
socket.onmessage = function (e) {
var data = JSON.parse(e.data);
// Targeted response — match to pending callback
if (data.Identifier > 1000) {
var cb = callbacks[data.Identifier];
if (cb) {
cb(data);
}
delete callbacks[data.Identifier];
return;
}
// Unsolicited server push (console, chat, etc.)
emit('message', data);
};
}
/**
* Disconnect from the server.
*/
function disconnect() {
callbacks = {};
if (socket) {
socket.close();
socket = null;
}
address = null;
}
/**
* Send a fire-and-forget command (no response expected).
* @param {string} msg - RCON command
* @param {number} [identifier] - Defaults to -1
*/
function command(msg, identifier) {
if (!socket || !isConnected()) return;
if (identifier === undefined || identifier === null) {
identifier = -1;
}
socket.send(JSON.stringify({
Identifier: identifier,
Message: msg,
Name: 'WebRcon'
}));
}
/**
* Send a command and invoke callback with the response.
* @param {string} msg - RCON command
* @param {function} cb - callback(response)
*/
function request(msg, cb) {
lastIndex++;
callbacks[lastIndex] = cb;
command(msg, lastIndex);
}
/**
* Returns true if the WebSocket is open.
*/
function isConnected() {
return socket !== null && socket.readyState === ConnectionStatus.OPEN;
}
/**
* Get the current connected address (host:port).
*/
function getAddress() {
return address;
}
/**
* Convenience: fetch the player list.
* @param {function} cb - callback(playersArray)
*/
function getPlayers(cb) {
request('playerlist', function (response) {
cb(JSON.parse(response.Message));
});
}
/**
* Register an event listener.
* Events: 'connected', 'disconnected', 'error', 'message'
*/
function on(event, cb) {
if (!listeners[event]) listeners[event] = [];
listeners[event].push(cb);
}
/**
* Remove an event listener.
*/
function off(event, cb) {
var cbs = listeners[event];
if (cbs) {
listeners[event] = cbs.filter(function (f) { return f !== cb; });
}
}
return {
connect: connect,
disconnect: disconnect,
command: command,
request: request,
isConnected: isConnected,
getAddress: getAddress,
getPlayers: getPlayers,
on: on,
off: off
};
})();