diff --git a/package-lock.json b/package-lock.json index e3c6998..f69a98f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -213,9 +213,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.3", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.3.tgz", - "integrity": "sha512-KOzM7MhcBFlmnlr/fzISFF5vGWVSvN6fTd4T+ExOt08bA/dA5kpSzY52nMsI1KDFmUREpJelPYyuslLRSjjgCg==", + "version": "4.19.5", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", + "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", "dev": true, "license": "MIT", "dependencies": { @@ -250,9 +250,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", - "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "version": "20.14.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", + "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -1257,9 +1257,9 @@ } }, "node_modules/nodemon": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.3.tgz", - "integrity": "sha512-m4Vqs+APdKzDFpuaL9F9EVOF85+h070FnkHVEoU4+rmT6Vw0bmNl7s61VEkY/cJkL7RCv1p4urnUDUMrS5rk2w==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz", + "integrity": "sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1330,10 +1330,13 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1824,9 +1827,9 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/prisma/migrations/20240614234637_init/migration.sql b/prisma/migrations/20240621191014_init/migration.sql similarity index 53% rename from prisma/migrations/20240614234637_init/migration.sql rename to prisma/migrations/20240621191014_init/migration.sql index ade3ac0..aa985ad 100644 --- a/prisma/migrations/20240614234637_init/migration.sql +++ b/prisma/migrations/20240621191014_init/migration.sql @@ -1,3 +1,37 @@ +-- CreateTable +CREATE TABLE `ObjectGroup` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `name` VARCHAR(191) NOT NULL, + `description` VARCHAR(191) NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `Objects` ( + `id` VARCHAR(191) NOT NULL, + `objectGroupId` INTEGER NOT NULL, + `name` VARCHAR(191) NOT NULL, + `origin_x` INTEGER NOT NULL DEFAULT 0, + `origin_y` INTEGER NOT NULL DEFAULT 0, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` DATETIME(3) NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `Item` ( + `id` VARCHAR(191) NOT NULL, + `name` VARCHAR(191) NOT NULL, + `description` VARCHAR(191) NOT NULL, + `stackable` BOOLEAN NOT NULL DEFAULT false, + `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `updatedAt` DATETIME(3) NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + -- CreateTable CREATE TABLE `User` ( `id` INTEGER NOT NULL AUTO_INCREMENT, @@ -27,6 +61,16 @@ CREATE TABLE `Character` ( PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +-- CreateTable +CREATE TABLE `CharacterItem` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `characterId` INTEGER NOT NULL, + `itemId` VARCHAR(191) NOT NULL, + `quantity` INTEGER NOT NULL, + + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + -- CreateTable CREATE TABLE `Zone` ( `id` INTEGER NOT NULL AUTO_INCREMENT, @@ -42,10 +86,10 @@ CREATE TABLE `Zone` ( ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable -CREATE TABLE `ZoneDecoration` ( +CREATE TABLE `ZoneObject` ( `id` INTEGER NOT NULL AUTO_INCREMENT, `zoneId` INTEGER NOT NULL, - `type` INTEGER NOT NULL, + `objectId` VARCHAR(191) NOT NULL, `position_x` INTEGER NOT NULL, `position_y` INTEGER NOT NULL, @@ -63,6 +107,9 @@ CREATE TABLE `Chat` ( PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +-- AddForeignKey +ALTER TABLE `Objects` ADD CONSTRAINT `Objects_objectGroupId_fkey` FOREIGN KEY (`objectGroupId`) REFERENCES `ObjectGroup`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + -- AddForeignKey ALTER TABLE `Character` ADD CONSTRAINT `Character_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; @@ -70,7 +117,16 @@ ALTER TABLE `Character` ADD CONSTRAINT `Character_userId_fkey` FOREIGN KEY (`use ALTER TABLE `Character` ADD CONSTRAINT `Character_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE `ZoneDecoration` ADD CONSTRAINT `ZoneDecoration_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE `CharacterItem` ADD CONSTRAINT `CharacterItem_characterId_fkey` FOREIGN KEY (`characterId`) REFERENCES `Character`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `CharacterItem` ADD CONSTRAINT `CharacterItem_itemId_fkey` FOREIGN KEY (`itemId`) REFERENCES `Item`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `ZoneObject` ADD CONSTRAINT `ZoneObject_zoneId_fkey` FOREIGN KEY (`zoneId`) REFERENCES `Zone`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE `ZoneObject` ADD CONSTRAINT `ZoneObject_objectId_fkey` FOREIGN KEY (`objectId`) REFERENCES `Objects`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE `Chat` ADD CONSTRAINT `Chat_characterId_fkey` FOREIGN KEY (`characterId`) REFERENCES `Character`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index fbbf7a9..68d3990 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -19,6 +19,26 @@ datasource db { url = env("DATABASE_URL") } +model Objects { + id String @id @default(uuid()) + name String + origin_x Int @default(0) + origin_y Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + ZoneObject ZoneObject[] +} + +model Item { + id String @id @default(uuid()) + name String + description String + stackable Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + characters CharacterItem[] +} + model User { id Int @id @default(autoincrement()) username String @unique @@ -27,42 +47,53 @@ model User { } model Character { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) userId Int - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - name String @unique - hitpoints Int @default(100) - mana Int @default(100) - level Int @default(1) - experience Int @default(0) - role String @default("player") + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + name String @unique + hitpoints Int @default(100) + mana Int @default(100) + level Int @default(1) + experience Int @default(0) + role String @default("player") position_x Int position_y Int rotation Int zoneId Int - zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade) + zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade) chats Chat[] + items CharacterItem[] +} + +model CharacterItem { + id Int @id @default(autoincrement()) + characterId Int + character Character @relation(fields: [characterId], references: [id], onDelete: Cascade) + itemId String + item Item @relation(fields: [itemId], references: [id], onDelete: Cascade) + quantity Int } model Zone { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) name String width Int height Int tiles Json walls Json - decorations ZoneDecoration[] + zoneObjects ZoneObject[] characters Character[] chats Chat[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } -model ZoneDecoration { - id Int @id @default(autoincrement()) +model ZoneObject { + id Int @id @default(autoincrement()) zoneId Int - zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade) - type Int + zone Zone @relation(fields: [zoneId], references: [id], onDelete: Cascade) + objectId String + object Objects @relation(fields: [objectId], references: [id]) position_x Int position_y Int } diff --git a/public/.gitignore b/public/.gitignore new file mode 100644 index 0000000..782bfe1 --- /dev/null +++ b/public/.gitignore @@ -0,0 +1,2 @@ +!.gitignore +** \ No newline at end of file diff --git a/src/app/events/CharacterConnect.ts b/src/app/events/character/CharacterConnect.ts similarity index 84% rename from src/app/events/CharacterConnect.ts rename to src/app/events/character/CharacterConnect.ts index 556754c..0af0a03 100644 --- a/src/app/events/CharacterConnect.ts +++ b/src/app/events/character/CharacterConnect.ts @@ -1,6 +1,6 @@ import { Socket, Server } from "socket.io"; -import {TSocket} from "../utilities/Types"; -import CharacterRepository from "../repositories/CharacterRepository"; +import {TSocket} from "../../utilities/Types"; +import CharacterRepository from "../../repositories/CharacterRepository"; import {Character, User} from "@prisma/client"; type SocketResponseT = { diff --git a/src/app/events/CharacterCreate.ts b/src/app/events/character/CharacterCreate.ts similarity index 87% rename from src/app/events/CharacterCreate.ts rename to src/app/events/character/CharacterCreate.ts index ef351d5..3ce2c54 100644 --- a/src/app/events/CharacterCreate.ts +++ b/src/app/events/character/CharacterCreate.ts @@ -1,8 +1,8 @@ import { Server } from "socket.io"; -import {TSocket} from "../utilities/Types"; +import {TSocket} from "../../utilities/Types"; import {Character} from "@prisma/client"; -import CharacterRepository from "../repositories/CharacterRepository"; -import {ZCharacterCreate} from "../utilities/ZodTypes"; +import CharacterRepository from "../../repositories/CharacterRepository"; +import {ZCharacterCreate} from "../../utilities/ZodTypes"; export default function (socket: TSocket, io: Server) { socket.on('character:create', async (data: any) => { diff --git a/src/app/events/CharacterDelete.ts b/src/app/events/character/CharacterDelete.ts similarity index 79% rename from src/app/events/CharacterDelete.ts rename to src/app/events/character/CharacterDelete.ts index c3ffd3e..6b265a7 100644 --- a/src/app/events/CharacterDelete.ts +++ b/src/app/events/character/CharacterDelete.ts @@ -1,8 +1,8 @@ import {Server} from "socket.io"; -import {TSocket} from "../utilities/Types"; +import {TSocket} from "../../utilities/Types"; import {Character} from "@prisma/client"; -import CharacterRepository from "../repositories/CharacterRepository"; -import {ZCharacterDelete} from "../utilities/ZodTypes"; +import CharacterRepository from "../../repositories/CharacterRepository"; +import {ZCharacterDelete} from "../../utilities/ZodTypes"; export default function (socket: TSocket, io: Server) { socket.on('character:delete', async (data: any) => { diff --git a/src/app/events/CharacterList.ts b/src/app/events/character/CharacterList.ts similarity index 83% rename from src/app/events/CharacterList.ts rename to src/app/events/character/CharacterList.ts index cf8bc5e..b721fd4 100644 --- a/src/app/events/CharacterList.ts +++ b/src/app/events/character/CharacterList.ts @@ -1,7 +1,7 @@ import { Socket, Server } from "socket.io"; -import {TSocket} from "../utilities/Types"; +import {TSocket} from "../../utilities/Types"; import {Character} from "@prisma/client"; -import CharacterRepository from "../repositories/CharacterRepository"; +import CharacterRepository from "../../repositories/CharacterRepository"; export default function CharacterList(socket: TSocket, io: Server) { socket.on('character:list', async (data: any) => { diff --git a/src/app/events/CharacterMove.ts b/src/app/events/character/CharacterMove.ts similarity index 85% rename from src/app/events/CharacterMove.ts rename to src/app/events/character/CharacterMove.ts index 28e5537..669e14d 100644 --- a/src/app/events/CharacterMove.ts +++ b/src/app/events/character/CharacterMove.ts @@ -1,7 +1,7 @@ import { Server } from "socket.io"; -import {TSocket} from "../utilities/Types"; -import CharacterRepository from "../repositories/CharacterRepository"; -import ZoneManager from "../ZoneManager"; +import {TSocket} from "../../utilities/Types"; +import CharacterRepository from "../../repositories/CharacterRepository"; +import ZoneManager from "../../ZoneManager"; type SocketResponseT = { position_x: number, diff --git a/src/app/events/CharacterZoneLeave.ts b/src/app/events/character/CharacterZoneLeave.ts similarity index 88% rename from src/app/events/CharacterZoneLeave.ts rename to src/app/events/character/CharacterZoneLeave.ts index c41cdfe..69dae87 100644 --- a/src/app/events/CharacterZoneLeave.ts +++ b/src/app/events/character/CharacterZoneLeave.ts @@ -1,7 +1,7 @@ import { Server } from "socket.io"; -import {TSocket} from "../utilities/Types"; -import ZoneRepository from "../repositories/ZoneRepository"; -import ZoneManager from "../ZoneManager"; +import {TSocket} from "../../utilities/Types"; +import ZoneRepository from "../../repositories/ZoneRepository"; +import ZoneManager from "../../ZoneManager"; import {Character, Zone} from "@prisma/client"; /** diff --git a/src/app/events/CharacterZoneRequest.ts b/src/app/events/character/CharacterZoneRequest.ts similarity index 83% rename from src/app/events/CharacterZoneRequest.ts rename to src/app/events/character/CharacterZoneRequest.ts index febd33a..76f1383 100644 --- a/src/app/events/CharacterZoneRequest.ts +++ b/src/app/events/character/CharacterZoneRequest.ts @@ -1,10 +1,10 @@ import { Server } from "socket.io"; -import {TSocket} from "../utilities/Types"; -import ZoneRepository from "../repositories/ZoneRepository"; -import ZoneManager from "../ZoneManager"; +import {TSocket} from "../../utilities/Types"; +import ZoneRepository from "../../repositories/ZoneRepository"; +import ZoneManager from "../../ZoneManager"; import {Character, Zone} from "@prisma/client"; -interface IZoneLoad { +interface IPayload { zoneId: number; } @@ -14,7 +14,7 @@ interface IZoneLoad { * @param io */ export default function (socket: TSocket, io: Server) { - socket.on('character:zone:request', async (data: IZoneLoad) => { + socket.on('character:zone:request', async (data: IPayload) => { console.log(`---User ${socket.character?.id} has requested zone.`); if (!data.zoneId) { diff --git a/src/app/events/gm/GmTileList.ts b/src/app/events/gm/GmTileList.ts new file mode 100644 index 0000000..c068e0f --- /dev/null +++ b/src/app/events/gm/GmTileList.ts @@ -0,0 +1,37 @@ +import { Server } from "socket.io"; +import {TSocket} from "../../utilities/Types"; +import fs from 'fs'; + +interface IPayload { +} + +/** + * Handle game master list tiles event + * @param socket + * @param io + */ +export default function (socket: TSocket, io: Server) { + socket.on('gm:tile:list', async (data: any, callback: (response: string[]) => void) => { + + // get root path + const root_folder = process.cwd(); + const folder = `${root_folder}/public/tiles`; + + // list the files in the folder + let tiles: string[] = []; + + fs.readdir(folder, (err, files) => { + if (err) { + console.log(err); + return; + } + + files.forEach(file => { + tiles.push(file); + }); + + // send over the list of tiles to the socket + callback(tiles); + }); + }); +} \ No newline at end of file diff --git a/src/app/events/gm/GmTileUpload.ts b/src/app/events/gm/GmTileUpload.ts new file mode 100644 index 0000000..eb1dc49 --- /dev/null +++ b/src/app/events/gm/GmTileUpload.ts @@ -0,0 +1,33 @@ +import { Server } from "socket.io"; +import {TSocket} from "../../utilities/Types"; +import {writeFile} from "node:fs"; +import {randomUUID} from "node:crypto"; + + +interface IPayload { +} + +/** + * Handle game master upload tile event + * @param socket + * @param io + */ +export default function (socket: TSocket, io: Server) { + socket.on('gm:tile:upload', async (data: any) => { + + // get root path + const root_folder = process.cwd(); + const public_folder = `${root_folder}/public`; + + for (const key in data) { + const filename = randomUUID(); + const path = `${public_folder}/tiles/${filename}.png`; + const tile = data[key]; + + // save the tile to the disk, for example + writeFile(path, tile, (err) => { + // pajeet INC + }); + } + }); +} \ No newline at end of file diff --git a/src/app/events/GmZoneEditorZoneRequest.ts b/src/app/events/gm/GmZoneEditorZoneRequest.ts similarity index 73% rename from src/app/events/GmZoneEditorZoneRequest.ts rename to src/app/events/gm/GmZoneEditorZoneRequest.ts index 267ad41..519487d 100644 --- a/src/app/events/GmZoneEditorZoneRequest.ts +++ b/src/app/events/gm/GmZoneEditorZoneRequest.ts @@ -1,10 +1,10 @@ import { Server } from "socket.io"; -import {TSocket} from "../utilities/Types"; -import ZoneRepository from "../repositories/ZoneRepository"; -import ZoneManager from "../ZoneManager"; +import {TSocket} from "../../utilities/Types"; +import ZoneRepository from "../../repositories/ZoneRepository"; +import ZoneManager from "../../ZoneManager"; import {Character, Zone} from "@prisma/client"; -interface IZoneLoad { +interface IPayload { zoneId: number; } @@ -14,7 +14,7 @@ interface IZoneLoad { * @param io */ export default function (socket: TSocket, io: Server) { - socket.on('gm:zone_editor:zone:request', async (data: IZoneLoad) => { + socket.on('gm:zone_editor:zone:request', async (data: IPayload) => { console.log(`---GM ${socket.character?.id} has requested zone via zone editor.`); if (!data.zoneId) { diff --git a/src/app/events/GmZoneEditorZoneSave.ts b/src/app/events/gm/GmZoneEditorZoneSave.ts similarity index 81% rename from src/app/events/GmZoneEditorZoneSave.ts rename to src/app/events/gm/GmZoneEditorZoneSave.ts index d8b41e5..c25fbcb 100644 --- a/src/app/events/GmZoneEditorZoneSave.ts +++ b/src/app/events/gm/GmZoneEditorZoneSave.ts @@ -1,10 +1,10 @@ import { Server } from "socket.io"; -import {TSocket} from "../utilities/Types"; -import ZoneRepository from "../repositories/ZoneRepository"; -import ZoneManager from "../ZoneManager"; +import {TSocket} from "../../utilities/Types"; +import ZoneRepository from "../../repositories/ZoneRepository"; +import ZoneManager from "../../ZoneManager"; import {Character, Zone} from "@prisma/client"; -interface IZoneLoad { +interface IPayload { zoneId: number; name: string; width: number; @@ -19,7 +19,7 @@ interface IZoneLoad { * @param io */ export default function (socket: TSocket, io: Server) { - socket.on('gm:zone_editor:zone:save', async (data: IZoneLoad) => { + socket.on('gm:zone_editor:zone:save', async (data: IPayload) => { console.log(`---GM ${socket.character?.id} has saved zone via zone editor.`); console.log(data); diff --git a/src/app/utilities/Http.ts b/src/app/utilities/Http.ts index 428bdee..3ef41af 100644 --- a/src/app/utilities/Http.ts +++ b/src/app/utilities/Http.ts @@ -8,8 +8,33 @@ import UserService from '../services/UserService'; import jwt from "jsonwebtoken"; import config from "./Config"; import {loginAccountSchema, registerAccountSchema} from "./ZodTypes"; +import path from "path"; + +function isValidAsset(assetName: string) { + const assetPath = path.join(__dirname, 'public', 'assets', assetName); + return assetPath.startsWith(path.join(__dirname, 'public', 'assets')); +} async function addAuthRoutes(app: Application) { + app.get('/assets/:asset', (req: Request, res: Response) => { + const assetName = req.params.asset; + + if (!isValidAsset(assetName)) { + return res.status(400).send('Invalid asset name'); + } + + const options = { + root: path.join(__dirname, 'public', 'assets'), + }; + + res.sendFile(assetName, options, (err) => { + if (err) { + console.error('Error sending file:', err); + res.status(500).send('Error downloading the asset'); + } + }); + }); + app.post('/login', async (req: Request, res: Response) => { const { username, password } = req.body; diff --git a/src/server.ts b/src/server.ts index 74d5fc2..20672ee 100644 --- a/src/server.ts +++ b/src/server.ts @@ -12,6 +12,7 @@ import ZoneManager from "./app/ZoneManager"; import UserManager from "./app/UserManager"; import {Authentication} from "./app/middleware/Authentication"; import CommandManager from "./app/CommandManager"; +import {Dirent} from "node:fs"; export class Server { @@ -76,15 +77,26 @@ export class Server private async handleConnection(socket: TSocket) { const eventsPath = path.join(__dirname, 'app', 'events'); try { - const files: string[] = await fs.promises.readdir(eventsPath); - for (const file of files) { - const module = await import(path.join(eventsPath, file)); - module.default(socket, this.io); - } + await this.loadEventHandlers(eventsPath, socket); } catch (error: any) { throw new Error('[❌] Failed to load event handlers: ' + error.message); } } + + private async loadEventHandlers(dir: string, socket: TSocket) { + const files: Dirent[] = await fs.promises.readdir(dir, { withFileTypes: true }); + + for (const file of files) { + const fullPath = path.join(dir, file.name); + + if (file.isDirectory()) { + await this.loadEventHandlers(fullPath, socket); + } else if (file.isFile()) { + const module = await import(fullPath); + module.default(socket, this.io); + } + } + } } // Start the server