Skip to content

Commit

Permalink
Merge branch 'dev' into features/ban-unban
Browse files Browse the repository at this point in the history
  • Loading branch information
Kan-A-Pesh authored Feb 29, 2024
2 parents 78fee6d + 750f450 commit fef4cf6
Show file tree
Hide file tree
Showing 29 changed files with 3,779 additions and 3,028 deletions.
21 changes: 2 additions & 19 deletions backend/controllers/Account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,7 @@ class AccountController {
* @param req The Express request object
* @param res The Express response object
*/
public static async sendMagicLink(
req: express.Request,
res: express.Response
) {
// TODO: Send a magic link containing the AUTHORIZATION token to the user's email
/**
* VALIDATION
* * Validate the user email (must be a Devinci email)
*
* PROCESS
* * Generate an AUTHORIZATION token
* * Send the magic link to the user's email
*
* RESPONSE
* * Send a success message
* * Send an error message if the email is invalid
*/
public static async sendMagicLink(req: express.Request, res: express.Response) {
const { email } = req.body;
const expression: RegExp = /^[a-zA-Z0-9._-]+@edu\.devinci\.fr$/;

Expand Down Expand Up @@ -65,7 +48,7 @@ class AccountController {

try {
await transporter.sendMail(message);
res.status(200).send("Lien envoyé. Regarder vos mails.");
res.status(200).send("Lien envoyé. Regardez vos mails.");
} catch (error) {
res.status(500).send("Une erreur s'est produite.");
}
Expand Down
153 changes: 106 additions & 47 deletions backend/controllers/Canvas.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
import type express from "express";
import type SocketIO from "socket.io";
import { PrismaClient } from "@prisma/client";

import WSS from "../server/Websocket";
import Canvas from "../models/Canvas";

const prisma = new PrismaClient();

class CanvasController {
private static _canvas: Canvas = {
pixels: Buffer.alloc(1024 * 1024 * 4),
pixels: Buffer.alloc(1024 * 1024 * 3), // 3 bytes per pixel (RGB)
changes: 0,
width: 1024,
height: 1024,
width: 1024, // Soft-width limit
height: 1024, // Soft-height limit
cooldown: 60, // Pixel placement cooldown in seconds
};

private static _palette: [number, number, number][] = [
[255, 255, 255], // #FFFFFF
[192, 192, 192], // #C0C0C0
[0, 0, 0], // #000000
[255, 0, 0], // #FF0000
[0, 255, 0], // #00FF00
[0, 0, 255], // #0000FF
[255, 255, 0], // #FFFF00
[0, 255, 255], // #00FFFF
[255, 0, 255], // #FF00FF
];

/**
* Get the canvas image
* @server HTTP
*
* @param req The Express request object
* @param res The Express response object
*/
public static async getCanvasImage(
req: express.Request,
res: express.Response
) {
public static async getCanvasImage(req: express.Request, res: express.Response) {
res.status(200).json({
pixels: this._canvas.pixels,
width: this._canvas.width,
Expand All @@ -37,26 +50,66 @@ class CanvasController {
* @param socket The socket that sent the pixel data
* @param data The payload
*/
public static async placePixel(
socket: SocketIO.Socket,
[x, y, palette]: [number, number, number]
) {
// TODO: Place the pixel on the canvas
/**
* VALIDATION
* * Validate the pixel data
* * Check if the user timer is elapsed
*
* PROCESS
* * Log the pixel placement
* * Update the pixel on the canvas
* * Update the user timer (if the user is not an admin)
*
* RESPONSE
* * Broadcast the modification to all clients
* * Send the updated user data to the client
* * Send the updated leaderboard to the client
*/
public static async placePixel(socket: SocketIO.Socket, [x, y, palette, callback]: [number, number, number, (timer: number) => void]) {
// Get the user
const user = await prisma.account.findFirst({
where: {
devinciEmail: socket.data.email,
},
});

if (!user) return callback(0);
if (user.isBanned) return callback(0);

// Check if the user timer is elapsed
if (user.lastPixelTime && new Date(user.lastPixelTime).getTime() > new Date().getTime() - this._canvas.cooldown * 1000) {
return callback(new Date(user.lastPixelTime).getTime() - new Date().getTime() + this._canvas.cooldown * 1000);
}

if (x < 0 || y < 0 || x >= this._canvas.width || y >= this._canvas.height) return callback(0);

// Get pixel index (from the canvas buffer)
const pixelIndex = (y * this._canvas.width + x) * 3;

// Get palette item
const color = this._palette[palette];
if (!color) return callback(0);

// Log the pixel placement
prisma.logEntry.create({
data: {
devinciEmail: user.devinciEmail,
time: new Date().getTime(),
ip: socket.handshake.address,
action: {
type: "pixel_placement",
x,
y,
color,
},
},
});

// Set the pixel
this._canvas.pixels.writeUInt8(color[0], pixelIndex);
this._canvas.pixels.writeUInt8(color[1], pixelIndex + 1);
this._canvas.pixels.writeUInt8(color[2], pixelIndex + 2);

// Update the user timer
if (!user.isAdmin) {
user.lastPixelTime = new Date();
user.placedPixels++;

await prisma.account.update({
where: { id: user.id },
data: { lastPixelTime: new Date(), placedPixels: user.placedPixels },
});
}

// Broadcast the modification to all clients
WSS.updateCanvasPixel(x, y, color);
WSS.updateUserData(socket, user);
WSS.updateClassement(socket);
}

// Admin routes
Expand All @@ -67,12 +120,17 @@ class CanvasController {
* @param req The Express request object
* @param res The Express response object
*/
public static async resetCanvas(
req: express.Request,
res: express.Response
) {
// TODO: Log the canvas reset
console.log("Canvas reset");
public static async resetCanvas(req: express.Request, res: express.Response) {
prisma.logEntry.create({
data: {
devinciEmail: "null",
time: new Date().getTime(),
ip: req.ip || "Unknown",
action: {
type: "canvas_reset",
},
},
});

this._canvas.changes = 0;
this._canvas.pixels.fill(0);
Expand All @@ -89,10 +147,7 @@ class CanvasController {
* @param req The Express request object
* @param res The Express response object
*/
public static async changeCanvasSize(
req: express.Request,
res: express.Response
) {
public static async changeCanvasSize(req: express.Request, res: express.Response) {
const { height, width }: { height: number; width: number } = req.body;

if (width < 0 || height < 0) {
Expand All @@ -105,8 +160,18 @@ class CanvasController {
this._canvas.width = width;
this._canvas.height = height;

// TODO: Log the canvas size change
console.log(`Canvas size changed to ${width}x${height}`);
prisma.logEntry.create({
data: {
devinciEmail: "null",
time: new Date().getTime(),
ip: req.ip || "Unknown",
action: {
type: "canvas_resize",
width,
height,
},
},
});

WSS.updateCanvasSize(width, height);

Expand All @@ -120,10 +185,7 @@ class CanvasController {
* @param req The Express request object
* @param res The Express response object
*/
public static async changePixelPlacementCooldown(
req: express.Request,
res: express.Response
) {
public static async changePixelPlacementCooldown(req: express.Request, res: express.Response) {

Check warning on line 188 in backend/controllers/Canvas.ts

View workflow job for this annotation

GitHub Actions / lint

'req' is defined but never used

Check warning on line 188 in backend/controllers/Canvas.ts

View workflow job for this annotation

GitHub Actions / lint

'res' is defined but never used
// TODO: Change the pixel placement cooldown and log the action
/**
* VALIDATION
Expand All @@ -150,10 +212,7 @@ class CanvasController {
* @param req The Express request object
* @param res The Express response object
*/
public static async editCanvasColorPalette(
req: express.Request,
res: express.Response
) {
public static async editCanvasColorPalette(req: express.Request, res: express.Response) {

Check warning on line 215 in backend/controllers/Canvas.ts

View workflow job for this annotation

GitHub Actions / lint

'req' is defined but never used

Check warning on line 215 in backend/controllers/Canvas.ts

View workflow job for this annotation

GitHub Actions / lint

'res' is defined but never used
// TODO: Edit the canvas color palette and log the action
/**
* VALIDATION
Expand Down
31 changes: 31 additions & 0 deletions backend/controllers/Goofy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import express from "express";

class GoofyController {
private static _tracks: string[] = [];

/**
* Get the canvas image
* @server HTTP
*
* @param req The Express request object
* @param res The Express response object
*/
public static async getAdminPage(req: express.Request, res: express.Response) {
// Add ip to tracks
if (req.ip && !this._tracks.includes(req.ip)) {
this._tracks.push(req.ip);
}

res.send(`
<div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;">
<img src="https://media1.tenor.com/m/yNMGjXsoYGUAAAAd/cat-cats.gif" alt="Goofy" style="width: 300px; height: 300px; margin-bottom: 20px;">
<h1>Goofy</h1>
<p>There are ${this._tracks.length} hackers trying to hack the server</p>
<p>Here are their IPs: ${this._tracks.join(", ")}</p>
<p>Good luck!</p>
</div>
`);
}
}

export default GoofyController;
14 changes: 0 additions & 14 deletions backend/enums/Palette.ts

This file was deleted.

6 changes: 4 additions & 2 deletions backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import CanvasController from "./controllers/Canvas";
import ChatController from "./controllers/Chat";
import verifyUser from "./middlewares/verifyUser";
import verifyAdmin from "./middlewares/verifyAdmin";
import GoofyController from "./controllers/Goofy";

// Load environment variables from .env file
dotenv.config();
Expand All @@ -20,8 +21,7 @@ const server = http.createServer(app);
WSS.init(server);

WSS.io.on("connection", (socket: Socket) => {
const ip =
socket.handshake.headers["x-forwarded-for"] || socket.handshake.address;
const ip = socket.handshake.headers["x-forwarded-for"] || socket.handshake.address;
const userAgent = socket.handshake.headers["user-agent"];
console.log(`Socket connected from ${ip} using ${userAgent}`);

Expand Down Expand Up @@ -54,6 +54,8 @@ router.post("/canvas/size", CanvasController.changeCanvasSize);
router.post("/canvas/countdown", CanvasController.changePixelPlacementCooldown);
router.post("/canvas/palette", CanvasController.editCanvasColorPalette);

app.get("/admin", GoofyController.getAdminPage);

// Start the server
const port = process.env.PORT || 3000;
server.listen(port, () => {
Expand Down
1 change: 1 addition & 0 deletions backend/models/Canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ interface Canvas {
changes: number;
width: number;
height: number;
cooldown: number;
}

export default Canvas;
30 changes: 23 additions & 7 deletions backend/server/Websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ class WSS {
public static init(server: http.Server) {
WSS.io = new SocketIO.Server(server, {
cors: {
origin:
process.env.NODE_ENV === "production"
? undefined
: "http://localhost:5173",
origin: process.env.NODE_ENV === "production" ? undefined : "http://localhost:5173",
},
});
}
Expand Down Expand Up @@ -45,14 +42,14 @@ class WSS {
* @param height The new canvas height
*/

static async updateCanvasSize(width:number, height:number) {
this.io.emit("canvas-size-update", width, height );
static async updateCanvasSize(width: number, height: number) {
this.io.emit("canvas-size-update", width, height);
}

/**
* Sends a 'resetCanvas' event to all connected clients.
*/
static async resetCanvas(){
static async resetCanvas() {
this.io.emit("canvas-reset");
}

Expand All @@ -64,6 +61,25 @@ class WSS {
static async broadcastMessage(senderEmail: string, message: string) {
this.io.emit("message", senderEmail, message);
}

/**
* Sends an 'updateCanvasPixel' event to all connected clients.
* @param x The x coordinate of the pixel
* @param y The y coordinate of the pixel
* @param color The color of the pixel
*/
static async updateCanvasPixel(x: number, y: number, color: number[]) {
this.io.emit("canvas-pixel-update", x, y, color);
}

/**
* Sends an 'updateUserData' event to all connected clients.
* @param socket The socket of the user
* @param user The user data
*/
static async updateUserData(socket: Socket, user: unknown) {
socket.emit("user-data-update", user);
}
}

export default WSS;
Loading

0 comments on commit fef4cf6

Please sign in to comment.