diff --git a/.env b/.env index 2a09a9c..082c5f8 100644 --- a/.env +++ b/.env @@ -9,4 +9,6 @@ DB_NAME=dok OFFLINE_SERVER_1=Test Server Alpha OFFLINE_SERVER_2=Test Server Beta -APP_URL=https://spiel.dynastyofknights.com \ No newline at end of file +APP_URL=https://spiel.dynastyofknights.com + +SESSION_SECRET=irgendein_langer_geheimer_zufallstext_123! \ No newline at end of file diff --git a/app.js b/app.js index 01993f0..70c53e3 100644 --- a/app.js +++ b/app.js @@ -22,6 +22,8 @@ const equip = require("./routes/equip"); const equipment = require("./routes/equipment"); const blackmarket = require("./routes/blackmarket"); +const compression = require("compression"); + const app = express(); app.set("trust proxy", 1); const PORT = process.env.PORT || 3000; @@ -33,6 +35,12 @@ const PORT = process.env.PORT || 3000; const server = http.createServer(app); const io = new Server(server); +/* ======================== + Compression +======================== */ + +app.use(compression()); + /* ======================== Security Middleware ======================== */ @@ -60,9 +68,14 @@ app.use(limiter); app.use( session({ - secret: "dynastyofknights_secret", + secret: process.env.SESSION_SECRET || "dynastyofknights_secret", resave: false, saveUninitialized: false, + cookie: { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + maxAge: 1000 * 60 * 60 * 24, + }, }), ); @@ -204,12 +217,6 @@ app.use((req, res) => { res.status(404).send("Seite nicht gefunden"); }); -/* ======================== - Webseite beschleunigen -======================== */ -const compression = require("compression"); -app.use(compression()); - /* ======================== Chat System ======================== */ diff --git a/public/css/building.css b/public/css/building.css index d854eb0..ad4da2e 100644 --- a/public/css/building.css +++ b/public/css/building.css @@ -474,7 +474,7 @@ body { filter: brightness(1.3); } -.equip-slot:empty { +.equip-slot:not(:has(img)) { opacity: 0.7; } @@ -507,11 +507,24 @@ body { #item-tooltip { position: fixed; - background: #111; - border: 1px solid #555; - padding: 6px; - color: white; + pointer-events: none; + + background: rgba(0, 0, 0, 0.85); + color: #fff; + + padding: 8px 12px; + border-radius: 6px; + + font-size: 14px; + display: none; + z-index: 9999; + + max-width: 200px; + + border: 1px solid #555; + + box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); } /* ========================= @@ -533,14 +546,6 @@ body { /* ========================= Schwarzmarkt ========================= */ -#market-pages { - display: flex; - flex-direction: column; - - gap: 8px; - - margin-top: 15px; -} .market-page { padding: 8px 12px; @@ -622,6 +627,8 @@ body { justify-content: center; margin-top: 15px; + + /* Ehemals doppelt definiert – zusammengeführt */ } .bag-icon { diff --git a/public/js/buildings/schwarzmarkt.js b/public/js/buildings/schwarzmarkt.js index e04352c..11d567b 100644 --- a/public/js/buildings/schwarzmarkt.js +++ b/public/js/buildings/schwarzmarkt.js @@ -42,18 +42,22 @@ Geheimhandel } async function loadPages() { - const res = await fetch("/api/blackmarket/pages"); - const data = await res.json(); + try { + const res = await fetch("/api/blackmarket/pages"); - const container = document.getElementById("market-pages"); + if (!res.ok) throw new Error("API Fehler"); - let html = ""; + const data = await res.json(); - for (let i = 1; i <= data.maxPages; i++) { - const price = data.prices.find((p) => p.page === i); + const container = document.getElementById("market-pages"); - if (data.ownedPages.includes(i)) { - html += ` + let html = ""; + + for (let i = 1; i <= data.maxPages; i++) { + const price = data.prices.find((p) => p.page === i); + + if (data.ownedPages.includes(i)) { + html += `
@@ -64,8 +68,8 @@ async function loadPages() {
`; - } else if (price) { - html += ` + } else if (price) { + html += `
@@ -80,8 +84,8 @@ Kaufen
`; - } else { - html += ` + } else { + html += `
@@ -92,10 +96,13 @@ Kaufen
`; + } } - } - container.innerHTML = html; + container.innerHTML = html; + } catch (err) { + console.error("Schwarzmarkt Fehler:", err); + } } /* Kaufen */ @@ -109,22 +116,29 @@ document.addEventListener("click", async (e) => { const page = slot.dataset.page; - const res = await fetch("/api/blackmarket/buy-page", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ page }), - }); + try { + const res = await fetch("/api/blackmarket/buy-page", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ page }), + }); - const data = await res.json(); + if (!res.ok) throw new Error("API Fehler"); - if (data.error) { - alert(data.error); - return; + const data = await res.json(); + + if (data.error) { + alert(data.error); + return; + } + + loadPages(); + } catch (err) { + console.error("Kauf Fehler:", err); + alert("Fehler beim Kauf. Bitte erneut versuchen."); } - - loadPages(); }); /* Tabs */ diff --git a/public/js/buildings/wohnhaus.js b/public/js/buildings/wohnhaus.js index e0c6683..23f42f1 100644 --- a/public/js/buildings/wohnhaus.js +++ b/public/js/buildings/wohnhaus.js @@ -159,8 +159,15 @@ function initInventoryButtons() { }; document.getElementById("inv-right").onclick = () => { - inventoryPage++; - renderInventory(); + const totalPages = Math.max( + 1, + Math.ceil(inventoryItems.length / slotsPerPage), + ); + + if (inventoryPage < totalPages - 1) { + inventoryPage++; + renderInventory(); + } }; } diff --git a/public/js/map-ui.js b/public/js/map-ui.js index 0fff700..07647da 100644 --- a/public/js/map-ui.js +++ b/public/js/map-ui.js @@ -4,6 +4,8 @@ const popup = document.getElementById("building-popup"); const title = document.getElementById("popup-title"); const tooltip = document.getElementById("map-tooltip"); +const tooltipCache = {}; + const buildingModules = { 11: loadWohnhaus, 12: loadSchwarzmarkt, @@ -136,11 +138,15 @@ document.querySelectorAll(".building").forEach((building) => { try { const id = building.dataset.id; - const res = await fetch("/api/building/" + id); + if (!tooltipCache[id]) { + const res = await fetch("/api/building/" + id); - if (!res.ok) throw new Error("API Fehler"); + if (!res.ok) throw new Error("API Fehler"); - const data = await res.json(); + tooltipCache[id] = await res.json(); + } + + const data = tooltipCache[id]; tooltip.innerHTML = ` ${data.name}
diff --git a/routes/equip.js b/routes/equip.js index 162db6b..568553f 100644 --- a/routes/equip.js +++ b/routes/equip.js @@ -1,25 +1,35 @@ const express = require("express"); const router = express.Router(); const db = require("../database/database"); +const auth = require("../middleware/auth"); -router.post("/", async (req, res) => { +router.post("/", auth, async (req, res) => { const userId = req.session.user.id; const { slot, itemId, itemLevelId } = req.body; - await db.query( - ` - INSERT INTO avatar_equipment - (user_id,slot,item_id,item_level_id) - VALUES (?,?,?,?) - ON DUPLICATE KEY UPDATE - item_id = VALUES(item_id), - item_level_id = VALUES(item_level_id) - `, - [userId, slot, itemId, itemLevelId], - ); + if (!slot) { + return res.status(400).json({ error: "Slot fehlt" }); + } - res.json({ success: true }); + try { + await db.query( + ` + INSERT INTO avatar_equipment + (user_id,slot,item_id,item_level_id) + VALUES (?,?,?,?) + ON DUPLICATE KEY UPDATE + item_id = VALUES(item_id), + item_level_id = VALUES(item_level_id) + `, + [userId, slot, itemId || null, itemLevelId || null], + ); + + res.json({ success: true }); + } catch (err) { + console.error("Equip Fehler:", err); + res.status(500).json({ error: "DB Fehler" }); + } }); module.exports = router; diff --git a/routes/equipment.js b/routes/equipment.js index ae9edbc..0e055f3 100644 --- a/routes/equipment.js +++ b/routes/equipment.js @@ -1,23 +1,33 @@ const express = require("express"); const router = express.Router(); const db = require("../database/database"); +const auth = require("../middleware/auth"); -router.get("/", async (req, res) => { +router.get("/", auth, async (req, res) => { const userId = req.session.user.id; - const [rows] = await db.query( - ` + try { + const [rows] = await db.query( + ` SELECT ae.slot, -items.icon +ae.item_id, +ae.item_level_id, +items.icon, +item_levels.level AS item_level FROM avatar_equipment ae LEFT JOIN items ON items.id = ae.item_id +LEFT JOIN item_levels ON item_levels.id = ae.item_level_id WHERE ae.user_id=? `, - [userId], - ); + [userId], + ); - res.json(rows); + res.json(rows); + } catch (err) { + console.error("Equipment Fehler:", err); + res.status(500).json({ error: "DB Fehler" }); + } }); module.exports = router; diff --git a/routes/inventory.js b/routes/inventory.js index a6b28db..871ca87 100644 --- a/routes/inventory.js +++ b/routes/inventory.js @@ -1,12 +1,14 @@ const express = require("express"); const router = express.Router(); const db = require("../database/database"); +const auth = require("../middleware/auth"); -router.get("/", async (req, res) => { - const userId = 1; +router.get("/", auth, async (req, res) => { + const userId = req.session.user.id; - const [items] = await db.query( - ` + try { + const [items] = await db.query( + ` SELECT items.id, items.name, @@ -19,10 +21,14 @@ JOIN items ON items.id=user_inventory.item_id LEFT JOIN item_levels ON item_levels.id=user_inventory.item_level_id WHERE user_inventory.user_id=? `, - [userId], - ); + [userId], + ); - res.json(items); + res.json(items); + } catch (err) { + console.error("Inventory Fehler:", err); + res.status(500).json({ error: "DB Fehler" }); + } }); module.exports = router;