Backend: Match-Lobbys, Map-Veto, Echtzeit und CS2-Serveradapter
Express + Socket.IO + SQLite. Veto-Engine mit konfigurierbaren Map-Pools und validierten Abläufen, rollenbasierte Berechtigungen, Server-Lebenszyklus mit Demo-, Docker- und Static-Pool-Adapter (RCON), automatisches Herunterfahren.
This commit is contained in:
commit
f08297dd0d
27 files changed
+6355
No files matched your search
@@ -0,0 +1,66 @@
|
||||
import crypto from 'node:crypto';
|
||||
|
||||
// Ohne leicht verwechselbare Zeichen (0/O, 1/I/L).
|
||||
const CODE_ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
|
||||
|
||||
export function randomCode(length = 8) {
|
||||
const bytes = crypto.randomBytes(length);
|
||||
let out = '';
|
||||
for (let i = 0; i < length; i++) out += CODE_ALPHABET[bytes[i] % CODE_ALPHABET.length];
|
||||
return out;
|
||||
}
|
||||
|
||||
export function normalizeCode(input) {
|
||||
return String(input ?? '')
|
||||
.toUpperCase()
|
||||
.replace(/[^A-Z0-9]/g, '');
|
||||
}
|
||||
|
||||
export function formatCode(code) {
|
||||
return code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;
|
||||
}
|
||||
|
||||
export function randomToken() {
|
||||
return crypto.randomBytes(32).toString('base64url');
|
||||
}
|
||||
|
||||
export function randomId() {
|
||||
return crypto.randomBytes(9).toString('base64url');
|
||||
}
|
||||
|
||||
/** Alphanumerisches Passwort, sicher für Konsolenbefehle ohne Escaping. */
|
||||
export function randomPassword(length = 16) {
|
||||
const alphabet = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
||||
const bytes = crypto.randomBytes(length);
|
||||
let out = '';
|
||||
for (let i = 0; i < length; i++) out += alphabet[bytes[i] % alphabet.length];
|
||||
return out;
|
||||
}
|
||||
|
||||
export function hashToken(token) {
|
||||
return crypto.createHash('sha256').update(String(token)).digest('hex');
|
||||
}
|
||||
|
||||
export function safeEqual(a, b) {
|
||||
const ba = Buffer.from(String(a));
|
||||
const bb = Buffer.from(String(b));
|
||||
return ba.length === bb.length && crypto.timingSafeEqual(ba, bb);
|
||||
}
|
||||
|
||||
/** Einfacher In-Memory-Ratenbegrenzer (Fenster pro Schlüssel). */
|
||||
export function createRateLimiter({ windowMs, max }) {
|
||||
const hits = new Map();
|
||||
return function check(key) {
|
||||
const now = Date.now();
|
||||
const entry = hits.get(key);
|
||||
if (!entry || now - entry.start > windowMs) {
|
||||
hits.set(key, { start: now, count: 1 });
|
||||
return true;
|
||||
}
|
||||
entry.count += 1;
|
||||
if (hits.size > 10_000) {
|
||||
for (const [k, v] of hits) if (now - v.start > windowMs) hits.delete(k);
|
||||
}
|
||||
return entry.count <= max;
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user