feat: Michess – Initiales Setup auf Basis von chessu

- Rebranding: chessu → Michess (Header, Footer, Metadata, Session-Cookie)
- Stockfish KI-Integration: 6 Schwierigkeitsgrade (Anfänger bis Meister)
- Admin-Panel: Nutzerverwaltung, Sperren/Entsperren, Git-Pull-Update
- Freundessystem: Anfragen, Freundesliste, Nutzersuche
- DB-Schema: role, banned, friend_request, friendship Tabellen
- Docker: NAS-optimiertes docker-compose.yml mit PostgreSQL Healthcheck
- Stockfish binary via apk im Alpine-Image installiert
- .env.example für einfaches Deployment
- scripts/update.sh für manuelles NAS-Update
- Brain.md + Plan.md als Projekt-Gedächtnis

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Michess
2026-04-13 10:02:52 +02:00
parent 29968f023d
commit 7dac042d73
28 changed files with 1760 additions and 89 deletions

View File

@@ -0,0 +1,15 @@
import express from "express";
import * as admin from "../controllers/admin.controller.js";
import { requireAdmin } from "../middleware/auth.js";
const router = express.Router();
router.use(requireAdmin);
router.get("/stats", admin.getStats);
router.get("/users", admin.listUsers);
router.patch("/users/:id", admin.updateUser);
router.delete("/users/:id", admin.deleteUser);
router.post("/update", admin.triggerUpdate);
export default router;

View File

@@ -0,0 +1,31 @@
import express from "express";
import type { Request, Response } from "express";
import { AI_LEVELS, getEngine } from "../controllers/stockfish.controller.js";
const router = express.Router();
router.post("/move", async (req: Request, res: Response) => {
try {
const { fen, level } = req.body;
if (!fen || typeof fen !== "string") {
res.status(400).json({ message: "FEN required." });
return;
}
const aiLevel = AI_LEVELS.find((l) => l.level === (level || 3)) || AI_LEVELS[2];
const engine = getEngine();
const move = await engine.getBestMove(fen, aiLevel);
res.status(200).json({ move: move || null });
} catch (err) {
console.error(err);
res.status(500).end();
}
});
router.get("/levels", (_req: Request, res: Response) => {
res.status(200).json(AI_LEVELS.map(({ level, name }) => ({ level, name })));
});
export default router;

View File

@@ -0,0 +1,15 @@
import express from "express";
import * as friends from "../controllers/friends.controller.js";
import { requireAuth } from "../middleware/auth.js";
const router = express.Router();
router.use(requireAuth);
router.get("/", friends.getFriends);
router.post("/request", friends.sendRequest);
router.get("/requests", friends.getRequests);
router.patch("/requests/:id", friends.respondToRequest);
router.delete("/:id", friends.removeFriend);
export default router;

View File

@@ -1,6 +1,9 @@
import { Router } from "express";
import admin from "./admin.route.js";
import ai from "./ai.route.js";
import auth from "./auth.route.js";
import friends from "./friends.route.js";
import games from "./games.route.js";
import users from "./users.route.js";
@@ -9,5 +12,8 @@ const router = Router();
router.use("/games", games);
router.use("/auth", auth);
router.use("/users", users);
router.use("/admin", admin);
router.use("/friends", friends);
router.use("/ai", ai);
export default router;

View File

@@ -1,8 +1,10 @@
import { Router } from "express";
import * as controller from "../controllers/users.controller.js";
import { searchUsers } from "../controllers/friends.controller.js";
const router = Router();
router.get("/search", searchUsers);
router.route("/:name").get(controller.getUserProfile);
export default router;