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,113 @@
import { exec } from "child_process";
import type { Request, Response } from "express";
import { db } from "../db/index.js";
import UserModel from "../db/models/user.model.js";
export const getStats = async (_req: Request, res: Response) => {
try {
const usersRes = await db.query(`SELECT COUNT(*) FROM "user"`);
const gamesRes = await db.query(`SELECT COUNT(*) FROM "game"`);
const activeUsersRes = await db.query(
`SELECT COUNT(*) FROM "user" WHERE created_at > NOW() - INTERVAL '7 days'`
);
res.status(200).json({
totalUsers: parseInt(usersRes.rows[0].count),
totalGames: parseInt(gamesRes.rows[0].count),
newUsersThisWeek: parseInt(activeUsersRes.rows[0].count)
});
} catch (err) {
console.log(err);
res.status(500).end();
}
};
export const listUsers = async (req: Request, res: Response) => {
try {
const limit = parseInt(req.query.limit as string) || 50;
const offset = parseInt(req.query.offset as string) || 0;
const search = req.query.search as string | undefined;
const users = await UserModel.getAllUsers(limit, offset, search);
const countRes = await db.query(
search
? `SELECT COUNT(*) FROM "user" WHERE name ILIKE $1 OR email ILIKE $1`
: `SELECT COUNT(*) FROM "user"`,
search ? [`%${search}%`] : []
);
res.status(200).json({
users,
total: parseInt(countRes.rows[0].count),
limit,
offset
});
} catch (err) {
console.log(err);
res.status(500).end();
}
};
export const updateUser = async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
const { role, banned } = req.body;
// Prevent admin from banning themselves
if (req.session.user?.id === id && banned === true) {
res.status(400).json({ message: "Cannot ban yourself." });
return;
}
const updated = await UserModel.adminUpdate(id, { role, banned });
if (!updated) {
res.status(404).end();
return;
}
res.status(200).json(updated);
} catch (err) {
console.log(err);
res.status(500).end();
}
};
export const deleteUser = async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
if (req.session.user?.id === id) {
res.status(400).json({ message: "Cannot delete your own account." });
return;
}
const removed = await UserModel.remove(id);
if (!removed) {
res.status(404).end();
return;
}
res.status(200).json(removed);
} catch (err) {
console.log(err);
res.status(500).end();
}
};
export const triggerUpdate = async (_req: Request, res: Response) => {
try {
const workdir = process.env.APP_DIR || "/opt/michess";
exec(`cd ${workdir} && git pull origin main 2>&1`, (err, stdout, stderr) => {
if (err) {
res.status(500).json({ message: "Git pull failed", output: stderr || err.message });
return;
}
res.status(200).json({
message: "Update successful. Restart the container to apply changes.",
output: stdout
});
});
} catch (err) {
console.log(err);
res.status(500).end();
}
};

View File

@@ -0,0 +1,192 @@
import type { Request, Response } from "express";
import { db } from "../db/index.js";
import UserModel from "../db/models/user.model.js";
export const sendRequest = async (req: Request, res: Response) => {
try {
const fromId = req.session.user?.id as number;
const { username } = req.body;
if (!username) {
res.status(400).json({ message: "Username required." });
return;
}
const targets = await UserModel.findByNameEmail({ name: username, email: username });
if (!targets || !targets.length) {
res.status(404).json({ message: "User not found." });
return;
}
const toId = targets[0].id as number;
if (toId === fromId) {
res.status(400).json({ message: "Cannot add yourself." });
return;
}
// Check already friends
const existing = await db.query(
`SELECT id FROM "friendship" WHERE (user_id_1=$1 AND user_id_2=$2) OR (user_id_1=$2 AND user_id_2=$1)`,
[fromId, toId]
);
if (existing.rowCount) {
res.status(409).json({ message: "Already friends." });
return;
}
// Check existing request
const existingReq = await db.query(
`SELECT id, status FROM "friend_request" WHERE (from_id=$1 AND to_id=$2) OR (from_id=$2 AND to_id=$1)`,
[fromId, toId]
);
if (existingReq.rowCount) {
res.status(409).json({ message: "Request already exists." });
return;
}
const result = await db.query(
`INSERT INTO "friend_request"(from_id, to_id) VALUES($1, $2) RETURNING id`,
[fromId, toId]
);
res.status(201).json({ id: result.rows[0].id, toName: targets[0].name });
} catch (err) {
console.log(err);
res.status(500).end();
}
};
export const getRequests = async (req: Request, res: Response) => {
try {
const userId = req.session.user?.id as number;
const incoming = await db.query(
`SELECT fr.id, fr.from_id, fr.to_id, fr.status, fr.created_at,
u.name as from_name
FROM "friend_request" fr
JOIN "user" u ON u.id = fr.from_id
WHERE fr.to_id=$1 AND fr.status='pending'
ORDER BY fr.created_at DESC`,
[userId]
);
const outgoing = await db.query(
`SELECT fr.id, fr.from_id, fr.to_id, fr.status, fr.created_at,
u.name as to_name
FROM "friend_request" fr
JOIN "user" u ON u.id = fr.to_id
WHERE fr.from_id=$1 AND fr.status='pending'
ORDER BY fr.created_at DESC`,
[userId]
);
res.status(200).json({
incoming: incoming.rows,
outgoing: outgoing.rows
});
} catch (err) {
console.log(err);
res.status(500).end();
}
};
export const respondToRequest = async (req: Request, res: Response) => {
try {
const userId = req.session.user?.id as number;
const requestId = parseInt(req.params.id);
const { action } = req.body; // "accept" | "reject"
if (action !== "accept" && action !== "reject") {
res.status(400).json({ message: "Invalid action." });
return;
}
const reqRow = await db.query(
`SELECT * FROM "friend_request" WHERE id=$1 AND to_id=$2 AND status='pending'`,
[requestId, userId]
);
if (!reqRow.rowCount) {
res.status(404).json({ message: "Request not found." });
return;
}
const { from_id, to_id } = reqRow.rows[0];
await db.query(`UPDATE "friend_request" SET status=$1 WHERE id=$2`, [
action === "accept" ? "accepted" : "rejected",
requestId
]);
if (action === "accept") {
const [a, b] = from_id < to_id ? [from_id, to_id] : [to_id, from_id];
await db.query(
`INSERT INTO "friendship"(user_id_1, user_id_2) VALUES($1, $2) ON CONFLICT DO NOTHING`,
[a, b]
);
}
res.status(200).json({ message: action === "accept" ? "Friend added!" : "Request rejected." });
} catch (err) {
console.log(err);
res.status(500).end();
}
};
export const getFriends = async (req: Request, res: Response) => {
try {
const userId = req.session.user?.id as number;
const result = await db.query(
`SELECT
f.id,
CASE WHEN f.user_id_1=$1 THEN f.user_id_2 ELSE f.user_id_1 END as friend_id,
u.name as friend_name,
u.wins, u.losses, u.draws,
f.created_at
FROM "friendship" f
JOIN "user" u ON u.id = CASE WHEN f.user_id_1=$1 THEN f.user_id_2 ELSE f.user_id_1 END
WHERE f.user_id_1=$1 OR f.user_id_2=$1
ORDER BY u.name ASC`,
[userId]
);
res.status(200).json(result.rows);
} catch (err) {
console.log(err);
res.status(500).end();
}
};
export const removeFriend = async (req: Request, res: Response) => {
try {
const userId = req.session.user?.id as number;
const friendId = parseInt(req.params.id);
await db.query(
`DELETE FROM "friendship" WHERE (user_id_1=$1 AND user_id_2=$2) OR (user_id_1=$2 AND user_id_2=$1)`,
[userId, friendId]
);
res.status(200).json({ message: "Friend removed." });
} catch (err) {
console.log(err);
res.status(500).end();
}
};
export const searchUsers = async (req: Request, res: Response) => {
try {
const q = req.query.q as string;
if (!q || q.length < 2) {
res.status(400).json({ message: "Query too short." });
return;
}
const users = await UserModel.searchByName(q, 10);
res.status(200).json(users);
} catch (err) {
console.log(err);
res.status(500).end();
}
};

View File

@@ -0,0 +1,127 @@
import { spawn, type ChildProcessWithoutNullStreams } from "child_process";
export interface StockfishLevel {
level: number;
name: string;
skillLevel: number;
depth: number;
moveTime: number; // ms
}
export const AI_LEVELS: StockfishLevel[] = [
{ level: 1, name: "Anfänger", skillLevel: 0, depth: 1, moveTime: 100 },
{ level: 2, name: "Leicht", skillLevel: 3, depth: 3, moveTime: 200 },
{ level: 3, name: "Mittel", skillLevel: 8, depth: 5, moveTime: 500 },
{ level: 4, name: "Fortgeschritten",skillLevel: 14, depth: 10, moveTime: 1000 },
{ level: 5, name: "Experte", skillLevel: 18, depth: 15, moveTime: 2000 },
{ level: 6, name: "Meister", skillLevel: 20, depth: 20, moveTime: 3000 },
];
export class StockfishEngine {
private process: ChildProcessWithoutNullStreams | null = null;
private buffer = "";
private resolvers: Map<string, (move: string) => void> = new Map();
private ready = false;
constructor() {
this.start();
}
private start() {
// Try system stockfish first, then fallback to npm stockfish
const binary = process.env.STOCKFISH_PATH || "stockfish";
try {
this.process = spawn(binary, [], { stdio: ["pipe", "pipe", "pipe"] });
} catch {
console.error("Failed to start stockfish binary, trying node-stockfish fallback");
return;
}
this.process.stdout.on("data", (data: Buffer) => {
this.buffer += data.toString();
const lines = this.buffer.split("\n");
this.buffer = lines.pop() || "";
for (const line of lines) {
this.handleLine(line.trim());
}
});
this.process.stderr.on("data", (data: Buffer) => {
console.error("Stockfish stderr:", data.toString());
});
this.process.on("exit", (code) => {
console.log("Stockfish exited with code", code);
this.ready = false;
});
this.send("uci");
}
private send(cmd: string) {
if (this.process?.stdin.writable) {
this.process.stdin.write(cmd + "\n");
}
}
private handleLine(line: string) {
if (line === "uciok") {
this.ready = true;
this.send("isready");
}
if (line === "readyok") {
// engine ready
}
if (line.startsWith("bestmove ")) {
const parts = line.split(" ");
const move = parts[1];
// resolve any pending resolver
const [key] = this.resolvers.entries().next().value ?? [];
if (key) {
this.resolvers.get(key)?.(move === "(none)" ? "" : move);
this.resolvers.delete(key);
}
}
}
async getBestMove(fen: string, level: StockfishLevel): Promise<string> {
return new Promise((resolve) => {
if (!this.process || !this.ready) {
resolve("");
return;
}
const id = `${Date.now()}-${Math.random()}`;
this.resolvers.set(id, resolve);
this.send("ucinewgame");
this.send(`setoption name Skill Level value ${level.skillLevel}`);
this.send(`position fen ${fen}`);
this.send(`go depth ${level.depth} movetime ${level.moveTime}`);
// Timeout safety
setTimeout(() => {
if (this.resolvers.has(id)) {
this.resolvers.delete(id);
resolve("");
}
}, level.moveTime + 5000);
});
}
destroy() {
this.send("quit");
this.process?.kill();
this.process = null;
}
}
// Singleton engine instance
let engineInstance: StockfishEngine | null = null;
export const getEngine = (): StockfishEngine => {
if (!engineInstance) {
engineInstance = new StockfishEngine();
}
return engineInstance;
};

View File

@@ -6,11 +6,13 @@ export const INIT_TABLES = /* sql */ `
CREATE TABLE IF NOT EXISTS "user" (
id SERIAL PRIMARY KEY,
name VARCHAR(128) UNIQUE NOT NULL,
email VARCHAR(128),
email VARCHAR(128) UNIQUE,
password TEXT,
role VARCHAR(16) DEFAULT 'user',
wins INTEGER DEFAULT 0,
losses INTEGER DEFAULT 0,
draws INTEGER DEFAULT 0,
banned BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS "game" (
@@ -22,7 +24,28 @@ export const INIT_TABLES = /* sql */ `
white_name VARCHAR(32),
black_id INT REFERENCES "user",
black_name VARCHAR(32),
vs_ai BOOLEAN DEFAULT FALSE,
ai_level INTEGER,
started_at TIMESTAMP NOT NULL,
ended_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS "friend_request" (
id SERIAL PRIMARY KEY,
from_id INT REFERENCES "user"(id) ON DELETE CASCADE,
to_id INT REFERENCES "user"(id) ON DELETE CASCADE,
status VARCHAR(16) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(from_id, to_id)
);
CREATE TABLE IF NOT EXISTS "friendship" (
id SERIAL PRIMARY KEY,
user_id_1 INT REFERENCES "user"(id) ON DELETE CASCADE,
user_id_2 INT REFERENCES "user"(id) ON DELETE CASCADE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id_1, user_id_2)
);
ALTER TABLE "user" ADD COLUMN IF NOT EXISTS role VARCHAR(16) DEFAULT 'user';
ALTER TABLE "user" ADD COLUMN IF NOT EXISTS banned BOOLEAN DEFAULT FALSE;
ALTER TABLE "game" ADD COLUMN IF NOT EXISTS vs_ai BOOLEAN DEFAULT FALSE;
ALTER TABLE "game" ADD COLUMN IF NOT EXISTS ai_level INTEGER;
`;

View File

@@ -8,7 +8,7 @@ export const create = async (user: User, password: string) => {
try {
const res = await db.query(
`INSERT INTO "user"(name, email, password) VALUES($1, $2, $3) RETURNING id, name, email, wins, losses, draws`,
`INSERT INTO "user"(name, email, password) VALUES($1, $2, $3) RETURNING id, name, email, wins, losses, draws, role, banned`,
[user.name, user.email || null, password]
);
return res.rows[0] as User;
@@ -24,11 +24,11 @@ export const findById = async (id: number) => {
}
try {
const res = await db.query(
`SELECT id, name, email, wins, losses, draws FROM "user" WHERE id=$1`,
`SELECT id, name, email, wins, losses, draws, role, banned FROM "user" WHERE id=$1`,
[id]
);
if (res.rowCount) {
return res.rows[0] as User;
return res.rows[0] as User & { banned?: boolean };
} else return null;
} catch (err: unknown) {
console.log(err);
@@ -37,14 +37,13 @@ export const findById = async (id: number) => {
};
export const findByNameEmail = async (user: User, includePassword = false, limit?: number) => {
// if user is not specified, get all users
if (!user) {
try {
const res = await db.query(
`SELECT id, name, email, wins, losses, draws FROM "user" LIMIT $1`,
`SELECT id, name, email, wins, losses, draws, role, banned FROM "user" LIMIT $1`,
[limit ?? 10]
);
return res.rows as (User & { password?: string })[];
return res.rows as (User & { password?: string; banned?: boolean })[];
} catch (err: unknown) {
console.log(err);
return null;
@@ -53,12 +52,25 @@ export const findByNameEmail = async (user: User, includePassword = false, limit
try {
const res = await db.query(
`SELECT id, name, email, wins, losses, draws${
`SELECT id, name, email, wins, losses, draws, role, banned${
includePassword ? `, password` : ""
} FROM "user" WHERE name=$1 OR email=$2 LIMIT $3`,
[user.name, user.email, limit ?? 1]
);
return res.rows as (User & { password?: string })[];
return res.rows as (User & { password?: string; banned?: boolean })[];
} catch (err: unknown) {
console.log(err);
return null;
}
};
export const searchByName = async (query: string, limit = 10) => {
try {
const res = await db.query(
`SELECT id, name, wins, losses, draws FROM "user" WHERE name ILIKE $1 LIMIT $2`,
[`%${query}%`, limit]
);
return res.rows as User[];
} catch (err: unknown) {
console.log(err);
return null;
@@ -71,11 +83,11 @@ export const update = async (id: number, updatedUser: User & { password?: string
}
try {
let query = `UPDATE "user" SET name=$1, email=$2 WHERE id=$3 RETURNING id, name, email, wins, losses, draws`;
let values = [updatedUser.name, updatedUser.email, id];
let query = `UPDATE "user" SET name=$1, email=$2 WHERE id=$3 RETURNING id, name, email, wins, losses, draws, role, banned`;
let values: (string | number | undefined)[] = [updatedUser.name, updatedUser.email, id];
if (updatedUser.password) {
query = `UPDATE "user" SET name=$1, email=$2, password=$3 WHERE id=$4 RETURNING id, name, email, wins, losses, draws`;
query = `UPDATE "user" SET name=$1, email=$2, password=$3 WHERE id=$4 RETURNING id, name, email, wins, losses, draws, role, banned`;
values = [updatedUser.name, updatedUser.email, updatedUser.password, id];
}
const res = await db.query(query, values);
@@ -86,6 +98,58 @@ export const update = async (id: number, updatedUser: User & { password?: string
}
};
export const adminUpdate = async (id: number, fields: { role?: string; banned?: boolean }) => {
try {
const setClauses: string[] = [];
const values: (string | boolean | number)[] = [];
let paramIndex = 1;
if (fields.role !== undefined) {
setClauses.push(`role=$${paramIndex++}`);
values.push(fields.role);
}
if (fields.banned !== undefined) {
setClauses.push(`banned=$${paramIndex++}`);
values.push(fields.banned);
}
if (!setClauses.length) return null;
values.push(id);
const res = await db.query(
`UPDATE "user" SET ${setClauses.join(", ")} WHERE id=$${paramIndex} RETURNING id, name, email, role, banned`,
values
);
return res.rows[0] as User & { banned?: boolean };
} catch (err: unknown) {
console.log(err);
return null;
}
};
export const getAllUsers = async (limit = 50, offset = 0, search?: string) => {
try {
let query = `SELECT id, name, email, wins, losses, draws, role, banned, created_at FROM "user"`;
const values: (string | number)[] = [];
if (search) {
query += ` WHERE name ILIKE $1 OR email ILIKE $1`;
values.push(`%${search}%`);
query += ` ORDER BY created_at DESC LIMIT $${values.length + 1} OFFSET $${values.length + 2}`;
values.push(limit, offset);
} else {
query += ` ORDER BY created_at DESC LIMIT $1 OFFSET $2`;
values.push(limit, offset);
}
const res = await db.query(query, values);
return res.rows as (User & { banned?: boolean; created_at?: string })[];
} catch (err: unknown) {
console.log(err);
return null;
}
};
export const remove = async (id: number) => {
if (id === 0) {
return null;
@@ -106,7 +170,10 @@ const UserModel = {
create,
findById,
findByNameEmail,
searchByName,
update,
adminUpdate,
getAllUsers,
remove
};

View File

@@ -0,0 +1,21 @@
import type { NextFunction, Request, Response } from "express";
export const requireAuth = (req: Request, res: Response, next: NextFunction) => {
if (!req.session.user?.id || typeof req.session.user.id === "string") {
res.status(401).json({ message: "Not authenticated." });
return;
}
next();
};
export const requireAdmin = (req: Request, res: Response, next: NextFunction) => {
if (!req.session.user?.id || typeof req.session.user.id === "string") {
res.status(401).json({ message: "Not authenticated." });
return;
}
if (req.session.user.role !== "admin") {
res.status(403).json({ message: "Admin access required." });
return;
}
next();
};

View File

@@ -27,7 +27,7 @@ const sessionMiddleware = session({
secret: process.env.SESSION_SECRET || "make sure to change this!",
resave: false,
saveUninitialized: false,
name: "chessu",
name: "michess",
proxy: true,
cookie: {
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days

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;

View File

@@ -20,11 +20,25 @@ const server = createServer(app);
// database
await db.connect();
db.query(INIT_TABLES, (err) => {
db.query(INIT_TABLES, async (err) => {
if (err) {
console.error(err);
} else {
console.log("Tables initialized");
console.log("Michess tables initialized");
// Set admin role for ADMIN_EMAIL if configured
const adminEmail = process.env.ADMIN_EMAIL;
if (adminEmail) {
try {
await db.query(
`UPDATE "user" SET role='admin' WHERE email=$1 AND role='user'`,
[adminEmail]
);
console.log(`Admin role ensured for: ${adminEmail}`);
} catch (e) {
console.error("Failed to set admin role:", e);
}
}
}
});
@@ -33,6 +47,20 @@ app.use(cors(corsConfig));
app.use(express.json());
app.set("trust proxy", 1);
app.use(session);
// Ban check middleware
app.use("/v1", (req: Request, res: Response, next: NextFunction) => {
if (req.session.user?.id && typeof req.session.user.id === "number") {
const user = req.session.user as { banned?: boolean };
if (user.banned) {
req.session.destroy(() => {});
res.status(403).json({ message: "Your account has been banned." });
return;
}
}
next();
});
app.use("/v1", routes);
// socket.io
@@ -41,8 +69,8 @@ io.use((socket, next) => {
session(socket.request as Request, {} as Response, next as NextFunction);
});
io.use((socket, next) => {
const session = socket.request.session;
if (session && session.user) {
const sess = socket.request.session;
if (sess && sess.user) {
next();
} else {
console.log("io.use: no session");
@@ -53,5 +81,5 @@ initSocket();
const port = process.env.PORT || 3001;
server.listen(port, () => {
console.log(`chessu api server listening on :${port}`);
console.log(`Michess API server listening on :${port}`);
});