This commit is contained in:
cay 2026-04-10 16:58:46 +01:00
parent edf8a37ddc
commit 4810950d4e
3 changed files with 51 additions and 25 deletions

46
app.js
View File

@ -28,7 +28,6 @@ const { registerArenaHandlers } = require("./sockets/arena");
const { registerChatHandlers } = require("./sockets/chat");
const boosterRoutes = require("./routes/booster.route");
const pointsRoutes = require("./routes/points.route");
const shopRoutes = require("./routes/shop.route");
const compression = require("compression");
@ -58,14 +57,13 @@ app.use(
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "https://js.stripe.com"],
scriptSrc: ["'self'", "'unsafe-inline'"],
scriptSrcAttr: ["'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
imgSrc: ["'self'", "data:", "blob:", "https://*.stripe.com"],
connectSrc: ["'self'", "ws:", "wss:", "https://api.stripe.com"],
frameSrc: ["https://js.stripe.com", "https://hooks.stripe.com"],
frameAncestors: ["'self'"],
imgSrc: ["'self'", "data:", "blob:"],
connectSrc: ["'self'", "ws:", "wss:"],
frameAncestors: ["'self'"], // Erlaubt iframe von eigener Domain
},
},
}),
@ -80,6 +78,9 @@ app.use(limiter);
/* ========================
Lösung 2: Session Config
maxAge: 24h Sessions laufen
automatisch ab, auch wenn der
Browser einfach geschlossen wurde.
======================== */
app.use(
@ -90,7 +91,7 @@ app.use(
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
maxAge: 1000 * 60 * 60 * 24,
maxAge: 1000 * 60 * 60 * 24, // 24 Stunden
},
}),
);
@ -102,21 +103,30 @@ app.use(
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
/* Webhook braucht raw body alle anderen json */
app.use((req, res, next) => {
if (req.originalUrl === "/api/shop/webhook") {
express.raw({ type: "application/json" })(req, res, next);
} else {
express.json()(req, res, next);
}
});
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, "public")));
/* ========================
Login Middleware
Server Stats (öffentlich kein Login nötig)
Zählt online Spieler pro Server
via Socket.io Verbindungen
======================== */
app.get("/api/server-stats", (req, res) => {
const stats = {};
// Alle verbundenen Sockets durchgehen
const sockets = io.sockets.sockets;
sockets.forEach((socket) => {
if (socket.user && socket.serverId) {
stats[socket.serverId] = (stats[socket.serverId] || 0) + 1;
}
});
res.json(stats);
});
function requireLogin(req, res, next) {
if (!req.session.user) {
return res.status(401).json({ error: "Nicht eingeloggt" });
@ -325,7 +335,7 @@ app.get("/api/hud", requireLogin, async (req, res) => {
[userId],
);
const [[currency]] = await db.query(
"SELECT silver, gold, gems, wood, stone, iron FROM account_currency WHERE account_id = ?",
"SELECT silver, gold, gems, wood, stone FROM account_currency WHERE account_id = ?",
[userId],
);
res.json({
@ -335,7 +345,6 @@ app.get("/api/hud", requireLogin, async (req, res) => {
gems: currency?.gems || 0,
wood: currency?.wood || 0,
stone: currency?.stone || 0,
iron: currency?.iron || 0,
});
} catch (err) {
console.error(err);
@ -392,7 +401,6 @@ app.use("/arena", arenaRoutes);
app.use("/api", boosterRoutes);
app.use("/api", require("./routes/daily.route"));
app.use("/api/points", pointsRoutes);
app.use("/api", shopRoutes);
/* ========================
404 Handler

View File

@ -12,7 +12,7 @@ function registerChatHandlers(io, socket) {
/* ── Registrierung ── */
socket.on("register", async (username) => {
const [rows] = await db.query(
"SELECT ingame_name FROM accounts WHERE username = ?",
"SELECT ingame_name, server_id FROM accounts WHERE username = ?",
[username],
);
@ -20,6 +20,7 @@ function registerChatHandlers(io, socket) {
const ingameName = rows[0].ingame_name;
socket.user = ingameName;
socket.serverId = rows[0].server_id;
onlineUsers[ingameName] = socket.id;
});

View File

@ -52,17 +52,34 @@
<div class="server-status">
<% servers.forEach(server => { %>
<%= server.name %>:
<span class="online">Online</span><br>
<div class="server-status-row">
<span class="server-name"><%= server.name %></span>
<span class="online">Online</span>
<span class="server-players" id="players-<%= server.id %>"> Spieler online</span>
</div>
<% }) %>
<% extraServers.forEach(server => { %>
<%= server.name %>:
<span class="offline">Offline</span><br>
<div class="server-status-row">
<span class="server-name"><%= server.name %></span>
<span class="offline">Offline</span>
</div>
<% }) %>
</div>
<script>
fetch('/api/server-stats')
.then(r => r.json())
.then(stats => {
Object.entries(stats).forEach(([serverId, count]) => {
const el = document.getElementById('players-' + serverId);
if (el) el.textContent = ' ' + count + ' Spieler online';
});
})
.catch(() => {});
</script>
</div>
<% if (typeof error !== 'undefined' && error) { %>