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;