code refractor
This commit is contained in:
parent
329c6597be
commit
56ae410fae
@ -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");
|
|
@ -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;
|
|
42
prisma/migrations/20240504233128_init/migration.sql
Normal file
42
prisma/migrations/20240504233128_init/migration.sql
Normal file
@ -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;
|
4
prisma/migrations/20240504233249_init/migration.sql
Normal file
4
prisma/migrations/20240504233249_init/migration.sql
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `User` MODIFY `position_x` INTEGER NULL,
|
||||||
|
MODIFY `position_y` INTEGER NULL,
|
||||||
|
MODIFY `rotation` INTEGER NULL;
|
12
prisma/migrations/20240504233358_init/migration.sql
Normal file
12
prisma/migrations/20240504233358_init/migration.sql
Normal file
@ -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;
|
@ -1,3 +1,3 @@
|
|||||||
# Please do not edit this file manually
|
# Please do not edit this file manually
|
||||||
# It should be added in your version-control system (i.e. Git)
|
# It should be added in your version-control system (i.e. Git)
|
||||||
provider = "sqlite"
|
provider = "mysql"
|
@ -9,12 +9,37 @@ generator client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "sqlite"
|
provider = "mysql"
|
||||||
url = "file:./dev.db"
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
username String @unique
|
username String @unique
|
||||||
password String
|
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])
|
||||||
}
|
}
|
28
src/helpers/http.ts
Normal file
28
src/helpers/http.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import UserService from '../services/User';
|
||||||
|
|
||||||
|
export async function registerUser(req: Request, res: Response): Promise<void> {
|
||||||
|
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<void> {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
5
src/helpers/prisma.ts
Normal file
5
src/helpers/prisma.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export default prisma;
|
@ -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<void> {
|
|
||||||
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<boolean> {
|
|
||||||
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<any> {
|
|
||||||
return prisma.user.findUnique({
|
|
||||||
where: {
|
|
||||||
username,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
15
src/repositories/Map.ts
Normal file
15
src/repositories/Map.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Map } from '@prisma/client';
|
||||||
|
import prisma from '../helpers/prisma'; // Import the global Prisma instance
|
||||||
|
|
||||||
|
class MapRepository {
|
||||||
|
async getFirst(): Promise<Map | null> {
|
||||||
|
try {
|
||||||
|
return await prisma.map.findFirst();
|
||||||
|
} catch (error) {
|
||||||
|
// Handle error
|
||||||
|
throw new Error(`Failed to get first map: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new MapRepository;
|
20
src/repositories/User.ts
Normal file
20
src/repositories/User.ts
Normal file
@ -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<User | null> {
|
||||||
|
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;
|
103
src/server.ts
103
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 express from 'express';
|
||||||
import { Server } from 'socket.io';
|
import { Server } from 'socket.io';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import {createUser, getUser, validateUser} from './models/user';
|
|
||||||
import cors from 'cors';
|
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 app = express();
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
@ -16,46 +15,10 @@ const io = new Server(server, { cors: { origin: '*' } });
|
|||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
app.post('/register', async (req, res) => {
|
app.post('/login', loginUser);
|
||||||
const { username, password } = req.body;
|
app.post('/register', registerUser);
|
||||||
try {
|
|
||||||
await createUser(username, password);
|
|
||||||
res.status(201).send('User registered');
|
|
||||||
} catch (error) {
|
|
||||||
res.status(500).send('Error registering user');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post('/login', async (req, res) => {
|
async function handleSocketConnection(socket: any) {
|
||||||
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;
|
const {username, password} = socket.handshake.query;
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
@ -63,53 +26,41 @@ io.on('connection', (socket) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validateUser(<string> username, <string> password)) {
|
const user = UserRepository.getByUsername(username);
|
||||||
|
|
||||||
|
if (!user || !await UserService.validateUserCredentials(username, password)) {
|
||||||
socket.disconnect(true);
|
socket.disconnect(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('User connected:', username);
|
console.log('User connected:', username);
|
||||||
|
|
||||||
if (!map.players.find((player: any) => player.username === username)) {
|
socket.user = user;
|
||||||
map.players.push({
|
|
||||||
username,
|
|
||||||
coords: {
|
|
||||||
x: 0,
|
|
||||||
y: 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
socket.user = getUser(username);
|
|
||||||
|
|
||||||
// send a message to the client
|
|
||||||
socket.emit('message', 'Welcome to the server!');
|
socket.emit('message', 'Welcome to the server!');
|
||||||
|
|
||||||
// join the room
|
const map = await MapRepository.getFirst();
|
||||||
socket.join(map.name);
|
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', () => {
|
socket.on('get_map', () => {
|
||||||
|
console.log('Sending map to user:', username);
|
||||||
socket.emit('map', map);
|
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', () => {
|
socket.on('move', (coords: any) => {
|
||||||
console.log('Socket disconnected');
|
// 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;
|
const PORT = process.env.PORT || 3000;
|
||||||
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));
|
server.listen(PORT, () => console.log(`Server running on port ${PORT}`));
|
6
src/services/Map.ts
Normal file
6
src/services/Map.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
class MapService {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MapService;
|
35
src/services/User.ts
Normal file
35
src/services/User.ts
Normal file
@ -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<void> {
|
||||||
|
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<boolean> {
|
||||||
|
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;
|
Loading…
x
Reference in New Issue
Block a user