From 56ae410faeb55dc536ad65f561b1e7e0faecb7df Mon Sep 17 00:00:00 2001 From: Dennis Postma Date: Sun, 5 May 2024 02:58:36 +0200 Subject: [PATCH] code refractor --- .../20240502213424_init/migration.sql | 9 -- .../migration.sql | 22 ---- .../20240504233128_init/migration.sql | 42 +++++++ .../20240504233249_init/migration.sql | 4 + .../20240504233358_init/migration.sql | 12 ++ prisma/migrations/migration_lock.toml | 2 +- prisma/schema.prisma | 29 ++++- src/helpers/http.ts | 28 +++++ src/helpers/prisma.ts | 5 + src/models/user.ts | 35 ------ src/repositories/Map.ts | 15 +++ src/repositories/User.ts | 20 ++++ src/server.ts | 109 +++++------------- src/services/Map.ts | 6 + src/services/User.ts | 35 ++++++ 15 files changed, 225 insertions(+), 148 deletions(-) delete mode 100644 prisma/migrations/20240502213424_init/migration.sql delete mode 100644 prisma/migrations/20240502214036_remove_email_field/migration.sql create mode 100644 prisma/migrations/20240504233128_init/migration.sql create mode 100644 prisma/migrations/20240504233249_init/migration.sql create mode 100644 prisma/migrations/20240504233358_init/migration.sql create mode 100644 src/helpers/http.ts create mode 100644 src/helpers/prisma.ts delete mode 100644 src/models/user.ts create mode 100644 src/repositories/Map.ts create mode 100644 src/repositories/User.ts create mode 100644 src/services/Map.ts create mode 100644 src/services/User.ts diff --git a/prisma/migrations/20240502213424_init/migration.sql b/prisma/migrations/20240502213424_init/migration.sql deleted file mode 100644 index a826aa4..0000000 --- a/prisma/migrations/20240502213424_init/migration.sql +++ /dev/null @@ -1,9 +0,0 @@ --- CreateTable -CREATE TABLE "User" ( - "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - "email" TEXT NOT NULL, - "name" TEXT -); - --- CreateIndex -CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); diff --git a/prisma/migrations/20240502214036_remove_email_field/migration.sql b/prisma/migrations/20240502214036_remove_email_field/migration.sql deleted file mode 100644 index 4593494..0000000 --- a/prisma/migrations/20240502214036_remove_email_field/migration.sql +++ /dev/null @@ -1,22 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `email` on the `User` table. All the data in the column will be lost. - - You are about to drop the column `name` on the `User` table. All the data in the column will be lost. - - Added the required column `password` to the `User` table without a default value. This is not possible if the table is not empty. - - Added the required column `username` to the `User` table without a default value. This is not possible if the table is not empty. - -*/ --- RedefineTables -PRAGMA foreign_keys=OFF; -CREATE TABLE "new_User" ( - "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - "username" TEXT NOT NULL, - "password" TEXT NOT NULL -); -INSERT INTO "new_User" ("id") SELECT "id" FROM "User"; -DROP TABLE "User"; -ALTER TABLE "new_User" RENAME TO "User"; -CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); -PRAGMA foreign_key_check; -PRAGMA foreign_keys=ON; diff --git a/prisma/migrations/20240504233128_init/migration.sql b/prisma/migrations/20240504233128_init/migration.sql new file mode 100644 index 0000000..5a1de25 --- /dev/null +++ b/prisma/migrations/20240504233128_init/migration.sql @@ -0,0 +1,42 @@ +-- CreateTable +CREATE TABLE `User` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `username` VARCHAR(191) NOT NULL, + `password` VARCHAR(191) NOT NULL, + `position_x` INTEGER NOT NULL, + `position_y` INTEGER NOT NULL, + `rotation` INTEGER NOT NULL, + `mapId` INTEGER NULL, + + UNIQUE INDEX `User_username_key`(`username`), + UNIQUE INDEX `User_mapId_key`(`mapId`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `Map` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `name` VARCHAR(191) NOT NULL, + `tiles` VARCHAR(191) NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `Chatlogs` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `userId` INTEGER NOT NULL, + `message` VARCHAR(191) NOT NULL, + `mapId` INTEGER NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- AddForeignKey +ALTER TABLE `User` ADD CONSTRAINT `User_mapId_fkey` FOREIGN KEY (`mapId`) REFERENCES `Map`(`id`) ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Chatlogs` ADD CONSTRAINT `Chatlogs_mapId_fkey` FOREIGN KEY (`mapId`) REFERENCES `Map`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `Chatlogs` ADD CONSTRAINT `Chatlogs_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20240504233249_init/migration.sql b/prisma/migrations/20240504233249_init/migration.sql new file mode 100644 index 0000000..c8e0b46 --- /dev/null +++ b/prisma/migrations/20240504233249_init/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE `User` MODIFY `position_x` INTEGER NULL, + MODIFY `position_y` INTEGER NULL, + MODIFY `rotation` INTEGER NULL; diff --git a/prisma/migrations/20240504233358_init/migration.sql b/prisma/migrations/20240504233358_init/migration.sql new file mode 100644 index 0000000..7bf27c6 --- /dev/null +++ b/prisma/migrations/20240504233358_init/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - You are about to alter the column `tiles` on the `Map` table. The data in that column could be lost. The data in that column will be cast from `VarChar(191)` to `Json`. + - Added the required column `height` to the `Map` table without a default value. This is not possible if the table is not empty. + - Added the required column `width` to the `Map` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE `Map` ADD COLUMN `height` INTEGER NOT NULL, + ADD COLUMN `width` INTEGER NOT NULL, + MODIFY `tiles` JSON NOT NULL; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml index e5e5c47..e5a788a 100644 --- a/prisma/migrations/migration_lock.toml +++ b/prisma/migrations/migration_lock.toml @@ -1,3 +1,3 @@ # Please do not edit this file manually # It should be added in your version-control system (i.e. Git) -provider = "sqlite" \ No newline at end of file +provider = "mysql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 29a7821..7de53ba 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -9,12 +9,37 @@ generator client { } datasource db { - provider = "sqlite" - url = "file:./dev.db" + provider = "mysql" + url = env("DATABASE_URL") } model User { id Int @id @default(autoincrement()) username String @unique password String + position_x Int? + position_y Int? + rotation Int? + mapId Int? @unique + map Map? @relation(fields: [mapId], references: [id]) + chatlogs Chatlogs[] +} + +model Map { + id Int @id @default(autoincrement()) + name String + width Int + height Int + tiles Json + users User[] // One-to-many relation: A map can have multiple users + chatlogs Chatlogs[] +} + +model Chatlogs { + id Int @id @default(autoincrement()) + userId Int + message String + mapId Int + map Map @relation(fields: [mapId], references: [id]) + user User @relation(fields: [userId], references: [id]) } \ No newline at end of file diff --git a/src/helpers/http.ts b/src/helpers/http.ts new file mode 100644 index 0000000..08bdebb --- /dev/null +++ b/src/helpers/http.ts @@ -0,0 +1,28 @@ +import { Request, Response } from 'express'; +import UserService from '../services/User'; + +export async function registerUser(req: Request, res: Response): Promise { + const { username, password } = req.body; + try { + await UserService.createUser(username, password); + res.status(201).send('User registered'); + } catch (error) { + console.error('Error registering user:', error); + res.status(500).send('Error registering user'); + } +} + +export async function loginUser(req: Request, res: Response): Promise { + const { username, password } = req.body; + try { + const isValid = await UserService.validateUserCredentials(username, password); + if (isValid) { + res.send('Login successful'); + } else { + res.status(401).send('Invalid credentials'); + } + } catch (error) { + console.error('Error validating credentials:', error); + res.status(500).send('Error validating credentials'); + } +} diff --git a/src/helpers/prisma.ts b/src/helpers/prisma.ts new file mode 100644 index 0000000..b904402 --- /dev/null +++ b/src/helpers/prisma.ts @@ -0,0 +1,5 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +export default prisma; \ No newline at end of file diff --git a/src/models/user.ts b/src/models/user.ts deleted file mode 100644 index c05d908..0000000 --- a/src/models/user.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import bcrypt from 'bcryptjs'; - -const prisma = new PrismaClient(); - -export async function createUser(username: string, password: string): Promise { - const salt = bcrypt.genSaltSync(10); - const hash = bcrypt.hashSync(password, salt); - await prisma.user.create({ - data: { - username, - password: hash - } - }); -} - -export async function validateUser(username: string, password: string): Promise { - const user = await prisma.user.findUnique({ - where: { - username, - }, - }); - - if (!user) return false; - - return bcrypt.compareSync(password, user.password); -} - -export async function getUser(username: string): Promise { - return prisma.user.findUnique({ - where: { - username, - }, - }); -} \ No newline at end of file diff --git a/src/repositories/Map.ts b/src/repositories/Map.ts new file mode 100644 index 0000000..c204b6f --- /dev/null +++ b/src/repositories/Map.ts @@ -0,0 +1,15 @@ +import { Map } from '@prisma/client'; +import prisma from '../helpers/prisma'; // Import the global Prisma instance + +class MapRepository { + async getFirst(): Promise { + try { + return await prisma.map.findFirst(); + } catch (error) { + // Handle error + throw new Error(`Failed to get first map: ${error.message}`); + } + } +} + +export default new MapRepository; \ No newline at end of file diff --git a/src/repositories/User.ts b/src/repositories/User.ts new file mode 100644 index 0000000..0ee9967 --- /dev/null +++ b/src/repositories/User.ts @@ -0,0 +1,20 @@ +import prisma from '../helpers/prisma'; // Import the global Prisma instance +import { User } from '@prisma/client'; +import bcrypt from 'bcryptjs'; + +class UserRepository { + async getByUsername(username: string): Promise { + try { + return await prisma.user.findUnique({ + where: { + username, + }, + }); + } catch (error) { + // Handle error + throw new Error(`Failed to get user by username: ${error.message}`); + } + } +} + +export default new UserRepository; \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index b6a39d0..3cda215 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,13 +1,12 @@ -/** - * Resources: - * https://deepinder.me/creating-a-real-time-chat-app-with-vue-socket-io-and-nodejs-2 - * https://socket.io/docs/v4/server-api/ - */ import express from 'express'; import { Server } from 'socket.io'; import http from 'http'; -import {createUser, getUser, validateUser} from './models/user'; import cors from 'cors'; +import UserRepository from "./repositories/User"; +import UserService from "./services/User"; +import MapRepository from "./repositories/Map"; +import prisma from "./helpers/prisma"; +import { registerUser, loginUser } from './helpers/http'; const app = express(); const server = http.createServer(app); @@ -16,100 +15,52 @@ const io = new Server(server, { cors: { origin: '*' } }); app.use(cors()); app.use(express.json()); -app.post('/register', async (req, res) => { - const { username, password } = req.body; - try { - await createUser(username, password); - res.status(201).send('User registered'); - } catch (error) { - res.status(500).send('Error registering user'); - } -}); +app.post('/login', loginUser); +app.post('/register', registerUser); -app.post('/login', async (req, res) => { - const { username, password } = req.body; - if (await validateUser(username, password)) { - res.send('Login successful'); - } else { - res.status(401).send('Invalid credentials'); - } -}); - -// this is a room in socket.io -const map: any = { - name: 'Test Map', - width: 10, - height: 10, - data: [ - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 ], - [ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 ], - [ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 ], - [ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 ], - [ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 ], - [ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 ], - [ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 ], - [ 0, 1, 1, 1, 1, 1, 1, 1, 1, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - ], - players: [] -} - -io.on('connection', (socket) => { - const { username, password } = socket.handshake.query; +async function handleSocketConnection(socket: any) { + const {username, password} = socket.handshake.query; if (!username || !password) { socket.disconnect(true); return; } - if (!validateUser( username, password)) { + const user = UserRepository.getByUsername(username); + + if (!user || !await UserService.validateUserCredentials(username, password)) { socket.disconnect(true); return; } console.log('User connected:', username); - if (!map.players.find((player: any) => player.username === username)) { - map.players.push({ - username, - coords: { - x: 0, - y: 0 - } - }); - } + socket.user = user; - // @ts-ignore - socket.user = getUser(username); - - // send a message to the client socket.emit('message', 'Welcome to the server!'); - // join the room + const map = await MapRepository.getFirst(); socket.join(map.name); + socket.emit('message', 'You have joined the room: ' + map.name); - // send a message to the client - socket.emit('message', 'You have joined the room!'); - - // send the map to the client socket.on('get_map', () => { + console.log('Sending map to user:', username); socket.emit('map', map); - }) - - // update map when a player moves - socket.on('move', (coords) => { - // @ts-ignore - const player = map.players.find(p => p.username === socket.user.username); - if (!player) return; - player.coords = coords; - io.to(map.name).emit('player_moved', map); }); -}) -io.on('disconnect', () => { - console.log('Socket disconnected'); -}); + socket.on('move', (coords: any) => { + // const player = map.players.find((p: any) => p.username === socket.user.username); + // if (!player) return; + // player.coords = coords; + // io.to(map.name).emit('player_moved', map); + }); + + socket.on('disconnect', () => { + console.log('User disconnected:', username); + }); +} + +io.on('connection', handleSocketConnection); const PORT = process.env.PORT || 3000; -server.listen(PORT, () => console.log(`Server running on port ${PORT}`)); \ No newline at end of file +server.listen(PORT, () => console.log(`Server running on port ${PORT}`)); diff --git a/src/services/Map.ts b/src/services/Map.ts new file mode 100644 index 0000000..a6853c8 --- /dev/null +++ b/src/services/Map.ts @@ -0,0 +1,6 @@ + +class MapService { + +} + +export default MapService; \ No newline at end of file diff --git a/src/services/User.ts b/src/services/User.ts new file mode 100644 index 0000000..50a50ed --- /dev/null +++ b/src/services/User.ts @@ -0,0 +1,35 @@ +import bcrypt from "bcryptjs"; +import prisma from "../helpers/prisma"; +import UserRepository from "../repositories/User"; + +class UserService { + async createUser(username: string, password: string): Promise { + try { + const hashedPassword = await bcrypt.hash(password, 10); + await prisma.user.create({ + data: { + username, + password: hashedPassword, + }, + }); + } catch (error) { + // Handle error + throw new Error(`Failed to create user: ${error.message}`); + } + } + + async validateUserCredentials(username: string, password: string): Promise { + try { + const user = await UserRepository.getByUsername(username); + + if (!user) return false; + + return bcrypt.compareSync(password, user.password); + } catch (error) { + // Handle error + throw new Error(`Failed to validate user credentials: ${error.message}`); + } + } +} + +export default new UserService; \ No newline at end of file