364 lines
9.9 KiB
JavaScript
364 lines
9.9 KiB
JavaScript
require("dotenv").config();
|
|
|
|
const express = require("express");
|
|
const path = require("path");
|
|
const helmet = require("helmet");
|
|
const rateLimit = require("express-rate-limit");
|
|
const http = require("http");
|
|
const { Server } = require("socket.io");
|
|
const db = require("./database/database");
|
|
|
|
const serverRoutes = require("./routes/servers");
|
|
const registerRoutes = require("./routes/register");
|
|
const verifyRoutes = require("./routes/verify");
|
|
const characterRoutes = require("./routes/character");
|
|
const session = require("express-session");
|
|
const loginRoutes = require("./routes/login");
|
|
const launcherRoutes = require("./routes/launcher");
|
|
const buildingRoutes = require("./routes/buildings");
|
|
const inventory = require("./routes/inventory");
|
|
const avatar = require("./routes/avatar");
|
|
const equip = require("./routes/equip");
|
|
const equipment = require("./routes/equipment");
|
|
const blackmarket = require("./routes/blackmarket");
|
|
const mineRoute = require("./routes/mine");
|
|
const carddeckRoutes = require("./routes/carddeck");
|
|
const arenaRoutes = require("./routes/arena");
|
|
const { registerArenaHandlers } = require("./sockets/arena");
|
|
const { registerChatHandlers } = require("./sockets/chat");
|
|
|
|
const compression = require("compression");
|
|
|
|
const app = express();
|
|
app.set("trust proxy", 1);
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
/* ========================
|
|
Chatserver
|
|
======================== */
|
|
|
|
const server = http.createServer(app);
|
|
const io = new Server(server);
|
|
|
|
/* ========================
|
|
Compression
|
|
======================== */
|
|
|
|
app.use(compression());
|
|
|
|
/* ========================
|
|
Security Middleware
|
|
======================== */
|
|
|
|
app.use(
|
|
helmet({
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
scriptSrc: ["'self'", "'unsafe-inline'"],
|
|
scriptSrcAttr: ["'unsafe-inline'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
|
|
fontSrc: ["'self'", "https://fonts.gstatic.com"],
|
|
imgSrc: ["'self'", "data:"],
|
|
connectSrc: ["'self'", "ws:", "wss:"],
|
|
},
|
|
},
|
|
}),
|
|
);
|
|
|
|
const limiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 5000,
|
|
});
|
|
|
|
app.use(limiter);
|
|
|
|
app.use(
|
|
session({
|
|
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,
|
|
},
|
|
}),
|
|
);
|
|
|
|
/* ========================
|
|
Express Settings
|
|
======================== */
|
|
|
|
app.set("view engine", "ejs");
|
|
app.set("views", path.join(__dirname, "views"));
|
|
|
|
/* ========================
|
|
Login Middleware
|
|
======================== */
|
|
|
|
function requireLogin(req, res, next) {
|
|
if (!req.session.user) {
|
|
return res.status(401).json({ error: "Nicht eingeloggt" });
|
|
}
|
|
next();
|
|
}
|
|
|
|
/* ========================
|
|
Route für Ajax für Gebäude
|
|
======================== */
|
|
|
|
app.get("/api/building/:id", requireLogin, async (req, res) => {
|
|
const buildingId = req.params.id;
|
|
const userId = req.session.user.id;
|
|
|
|
try {
|
|
const [userBuilding] = await db.query(
|
|
"SELECT level, points FROM user_buildings WHERE user_id=? AND building_id=?",
|
|
[userId, buildingId],
|
|
);
|
|
|
|
let building;
|
|
|
|
if (!userBuilding.length) {
|
|
await db.query(
|
|
"INSERT INTO user_buildings (user_id,building_id,level,points) VALUES (?,?,1,0)",
|
|
[userId, buildingId],
|
|
);
|
|
building = { level: 1, points: 0 };
|
|
} else {
|
|
building = userBuilding[0];
|
|
}
|
|
|
|
const [nextLevel] = await db.query(
|
|
"SELECT required_points, wood, stone, gold FROM building_levels WHERE building_id=? AND level=?",
|
|
[buildingId, building.level + 1],
|
|
);
|
|
|
|
const [info] = await db.query(
|
|
"SELECT name,description,history FROM buildings WHERE id=?",
|
|
[buildingId],
|
|
);
|
|
|
|
const buildingInfo = info[0] || {};
|
|
res.json({
|
|
name: buildingInfo.name || "Gebäude",
|
|
type: buildingId,
|
|
level: building.level,
|
|
points: building.points,
|
|
nextLevelPoints: nextLevel[0]?.required_points || null,
|
|
description: info[0].description,
|
|
history: info[0].history,
|
|
upgradeCost: nextLevel[0]
|
|
? `${nextLevel[0].wood} Holz, ${nextLevel[0].stone} Stein, ${nextLevel[0].gold} Gold`
|
|
: "Max Level erreicht",
|
|
upgradeWood: nextLevel[0]?.wood ?? null,
|
|
upgradeStone: nextLevel[0]?.stone ?? null,
|
|
upgradeGold: nextLevel[0]?.gold ?? null,
|
|
upgradeRequiredPoints: nextLevel[0]?.required_points ?? null, // NEU
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: "DB Fehler" });
|
|
}
|
|
});
|
|
|
|
/* ========================
|
|
Route für Gebäude Upgrade
|
|
======================== */
|
|
|
|
app.post("/api/building/:id/upgrade", requireLogin, async (req, res) => {
|
|
const buildingId = req.params.id;
|
|
const userId = req.session.user.id;
|
|
|
|
try {
|
|
// Aktuelles Level holen
|
|
const [[userBuilding]] = await db.query(
|
|
"SELECT id, level, points FROM user_buildings WHERE user_id = ? AND building_id = ?",
|
|
[userId, buildingId],
|
|
);
|
|
|
|
if (!userBuilding) {
|
|
return res.status(404).json({ error: "Gebäude nicht gefunden" });
|
|
}
|
|
|
|
const nextLevel = userBuilding.level + 1;
|
|
|
|
// Upgrade-Kosten für nächstes Level holen
|
|
const [[levelData]] = await db.query(
|
|
"SELECT required_points, wood, stone, gold FROM building_levels WHERE building_id = ? AND level = ?",
|
|
[buildingId, nextLevel],
|
|
);
|
|
|
|
if (!levelData) {
|
|
return res.status(400).json({ error: "Maximales Level bereits erreicht" });
|
|
}
|
|
|
|
// Punkte prüfen
|
|
if (userBuilding.points < levelData.required_points) {
|
|
return res.status(400).json({
|
|
error: `Nicht genügend Punkte. Benötigt: ${levelData.required_points}, Vorhanden: ${userBuilding.points}`,
|
|
});
|
|
}
|
|
|
|
// Ressourcen des Spielers prüfen
|
|
const [[currency]] = await db.query(
|
|
"SELECT wood, stone, gold FROM account_currency WHERE account_id = ?",
|
|
[userId],
|
|
);
|
|
|
|
if (!currency) {
|
|
return res.status(400).json({ error: "Keine Währungsdaten gefunden" });
|
|
}
|
|
|
|
if (currency.wood < levelData.wood || currency.stone < levelData.stone || currency.gold < levelData.gold) {
|
|
return res.status(400).json({
|
|
error: "Nicht genügend Ressourcen",
|
|
required: { wood: levelData.wood, stone: levelData.stone, gold: levelData.gold },
|
|
current: { wood: currency.wood, stone: currency.stone, gold: currency.gold },
|
|
});
|
|
}
|
|
|
|
// Ressourcen abziehen
|
|
await db.query(
|
|
"UPDATE account_currency SET wood = wood - ?, stone = stone - ?, gold = gold - ? WHERE account_id = ?",
|
|
[levelData.wood, levelData.stone, levelData.gold, userId],
|
|
);
|
|
|
|
// Level erhöhen, nur benötigte Punkte abziehen
|
|
await db.query(
|
|
"UPDATE user_buildings SET level = ?, points = points - ? WHERE id = ?",
|
|
[nextLevel, levelData.required_points, userBuilding.id],
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
newLevel: nextLevel,
|
|
cost: { wood: levelData.wood, stone: levelData.stone, gold: levelData.gold },
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: "DB Fehler" });
|
|
}
|
|
});
|
|
|
|
/* ========================
|
|
HUD API
|
|
======================== */
|
|
|
|
app.get("/api/hud", requireLogin, async (req, res) => {
|
|
const userId = req.session.user.id;
|
|
try {
|
|
const [[account]] = await db.query(
|
|
"SELECT ingame_name FROM accounts WHERE id = ?",
|
|
[userId],
|
|
);
|
|
const [[currency]] = await db.query(
|
|
"SELECT silver, gold, gems, wood, stone FROM account_currency WHERE account_id = ?",
|
|
[userId],
|
|
);
|
|
res.json({
|
|
name: account?.ingame_name || "Held",
|
|
silver: currency?.silver || 0,
|
|
gold: currency?.gold || 0,
|
|
gems: currency?.gems || 0,
|
|
wood: currency?.wood || 0,
|
|
stone: currency?.stone || 0,
|
|
});
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: "DB Fehler" });
|
|
}
|
|
});
|
|
|
|
app.get("/api/buildings", requireLogin, async (req, res) => {
|
|
const userId = req.session.user.id;
|
|
|
|
try {
|
|
const [rows] = await db.query(
|
|
`
|
|
SELECT
|
|
b.id,
|
|
b.name,
|
|
b.description,
|
|
b.history,
|
|
ub.level,
|
|
ub.points
|
|
FROM buildings b
|
|
LEFT JOIN user_buildings ub
|
|
ON ub.building_id = b.id AND ub.user_id = ?
|
|
`,
|
|
[userId],
|
|
);
|
|
|
|
res.json(rows);
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: "DB Fehler" });
|
|
}
|
|
});
|
|
|
|
}
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error(err);
|
|
res.status(500).json({ error: "DB Fehler" });
|
|
}
|
|
});
|
|
|
|
app.use(express.json());
|
|
app.use(express.urlencoded({ extended: true }));
|
|
|
|
/* ========================
|
|
Static Files
|
|
======================== */
|
|
|
|
app.use(express.static(path.join(__dirname, "public")));
|
|
|
|
/* ========================
|
|
Routes
|
|
======================== */
|
|
|
|
app.use("/", serverRoutes);
|
|
app.use("/register", registerRoutes);
|
|
app.use("/verify", verifyRoutes);
|
|
app.use("/create-character", characterRoutes);
|
|
app.use("/login", loginRoutes);
|
|
app.use("/launcher", launcherRoutes);
|
|
app.use("/", buildingRoutes);
|
|
app.use("/api/inventory", inventory);
|
|
app.use("/api/avatar", avatar);
|
|
app.use("/api/equip", equip);
|
|
app.use("/api/equipment", equipment);
|
|
app.use("/api/blackmarket", blackmarket);
|
|
app.use("/api/mine", mineRoute);
|
|
app.use("/api", carddeckRoutes);
|
|
app.use("/arena", arenaRoutes);
|
|
|
|
/* ========================
|
|
404 Handler
|
|
======================== */
|
|
|
|
app.use((req, res) => {
|
|
res.status(404).send("Seite nicht gefunden");
|
|
});
|
|
|
|
/* ========================
|
|
Socket.io Handler
|
|
======================== */
|
|
|
|
io.on("connection", (socket) => {
|
|
console.log("Spieler verbunden:", socket.id);
|
|
registerChatHandlers(io, socket);
|
|
registerArenaHandlers(io, socket);
|
|
});
|
|
|
|
/* ========================
|
|
Server Start
|
|
======================== */
|
|
|
|
server.listen(PORT, () => {
|
|
console.log(`Dynasty of Knights Server läuft auf http://localhost:${PORT}`);
|
|
});
|