dok/app.js
2026-03-29 11:02:47 +01:00

272 lines
6.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 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]?.wood} Holz, ${nextLevel[0]?.stone} Stein, ${nextLevel[0]?.gold} 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" });
}
});
/* ========================
Body Parser
======================== */
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("/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}`);
});