Storage class is now OOP
This commit is contained in:
parent
04e081c31a
commit
5982422e04
@ -3,7 +3,7 @@ import * as path from 'path'
|
||||
import { pathToFileURL } from 'url'
|
||||
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
import { getAppPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { Command } from '#application/types'
|
||||
|
||||
export class CommandRegistry {
|
||||
@ -15,7 +15,7 @@ export class CommandRegistry {
|
||||
}
|
||||
|
||||
public async loadCommands(): Promise<void> {
|
||||
const directory = getAppPath('commands')
|
||||
const directory = Storage.getAppPath('commands')
|
||||
this.logger.info(`Loading commands from: ${directory}`)
|
||||
|
||||
try {
|
||||
@ -32,7 +32,7 @@ export class CommandRegistry {
|
||||
|
||||
private async loadCommandFile(file: fs.Dirent): Promise<void> {
|
||||
try {
|
||||
const filePath = getAppPath('commands', file.name)
|
||||
const filePath = Storage.getAppPath('commands', file.name)
|
||||
const commandName = path.basename(file.name, path.extname(file.name))
|
||||
|
||||
const module = await import(pathToFileURL(filePath).href)
|
||||
|
@ -1,34 +1,71 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
import config from './config'
|
||||
import config from '#application/config'
|
||||
|
||||
export function getRootPath(folder: string, ...additionalSegments: string[]) {
|
||||
return path.join(process.cwd(), folder, ...additionalSegments)
|
||||
}
|
||||
class Storage {
|
||||
private readonly baseDir: string
|
||||
private readonly rootDir: string
|
||||
|
||||
export function getAppPath(folder: string, ...additionalSegments: string[]) {
|
||||
const baseDir = config.ENV === 'development' ? 'src' : 'dist'
|
||||
return path.join(process.cwd(), baseDir, folder, ...additionalSegments)
|
||||
}
|
||||
constructor() {
|
||||
this.rootDir = process.cwd()
|
||||
this.baseDir = config.ENV === 'development' ? 'src' : 'dist'
|
||||
}
|
||||
|
||||
export function getPublicPath(folder: string, ...additionalSegments: string[]) {
|
||||
return path.join(process.cwd(), 'public', folder, ...additionalSegments)
|
||||
}
|
||||
/**
|
||||
* Gets path relative to project root
|
||||
*/
|
||||
public getRootPath(folder: string, ...additionalSegments: string[]): string {
|
||||
return path.join(this.rootDir, folder, ...additionalSegments)
|
||||
}
|
||||
|
||||
export function doesPathExist(path: string) {
|
||||
try {
|
||||
fs.accessSync(path, fs.constants.F_OK)
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
/**
|
||||
* Gets path relative to app directory (src/dist)
|
||||
*/
|
||||
public getAppPath(folder: string, ...additionalSegments: string[]): string {
|
||||
return path.join(this.rootDir, this.baseDir, folder, ...additionalSegments)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets path relative to public directory
|
||||
*/
|
||||
public getPublicPath(folder: string, ...additionalSegments: string[]): string {
|
||||
return path.join(this.rootDir, 'public', folder, ...additionalSegments)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a path exists
|
||||
* @throws Error if path is empty or invalid
|
||||
*/
|
||||
public doesPathExist(pathToCheck: string): boolean {
|
||||
if (!pathToCheck) {
|
||||
throw new Error('Path cannot be empty')
|
||||
}
|
||||
|
||||
try {
|
||||
fs.accessSync(pathToCheck, fs.constants.F_OK)
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory and any necessary parent directories
|
||||
* @throws Error if directory creation fails
|
||||
*/
|
||||
public createDir(dirPath: string): void {
|
||||
if (!dirPath) {
|
||||
throw new Error('Directory path cannot be empty')
|
||||
}
|
||||
|
||||
try {
|
||||
fs.mkdirSync(dirPath, { recursive: true })
|
||||
} catch (error) {
|
||||
const typedError = error as Error
|
||||
throw new Error(`Failed to create directory: ${typedError.message}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createDir(path: string) {
|
||||
try {
|
||||
fs.mkdirSync(path, { recursive: true })
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
export default new Storage()
|
||||
|
@ -1,11 +1,10 @@
|
||||
import fs from 'fs'
|
||||
|
||||
import sharp from 'sharp'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import { BaseCommand } from '#application/base/baseCommand'
|
||||
import { CharacterGender, CharacterRace } from '#application/enums'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { UUID } from '#application/types'
|
||||
import { Character } from '#entities/character'
|
||||
import { CharacterHair } from '#entities/characterHair'
|
||||
@ -44,7 +43,7 @@ export default class InitCommand extends BaseCommand {
|
||||
}
|
||||
|
||||
private async importTiles(): Promise<void> {
|
||||
for (const tile of fs.readdirSync(getPublicPath('tiles'))) {
|
||||
for (const tile of fs.readdirSync(Storage.getPublicPath('tiles'))) {
|
||||
const newTile = new Tile()
|
||||
newTile.setId(tile.split('.')[0] as UUID).setName('New tile')
|
||||
|
||||
@ -53,18 +52,18 @@ export default class InitCommand extends BaseCommand {
|
||||
}
|
||||
|
||||
private async importObjects(): Promise<void> {
|
||||
for (const object of fs.readdirSync(getPublicPath('objects'))) {
|
||||
for (const object of fs.readdirSync(Storage.getPublicPath('objects'))) {
|
||||
const newMapObject = new MapObject()
|
||||
newMapObject
|
||||
.setId(object.split('.')[0] as UUID)
|
||||
.setName('New object')
|
||||
.setFrameWidth(
|
||||
(await sharp(getPublicPath('objects', object))
|
||||
(await sharp(Storage.getPublicPath('objects', object))
|
||||
.metadata()
|
||||
.then((metadata) => metadata.height)) ?? 0
|
||||
)
|
||||
.setFrameHeight(
|
||||
(await sharp(getPublicPath('objects', object))
|
||||
(await sharp(Storage.getPublicPath('objects', object))
|
||||
.metadata()
|
||||
.then((metadata) => metadata.width)) ?? 0
|
||||
)
|
||||
|
@ -3,12 +3,12 @@ import fs from 'fs'
|
||||
import sharp from 'sharp'
|
||||
|
||||
import { BaseCommand } from '#application/base/baseCommand'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
|
||||
export default class TilesCommand extends BaseCommand {
|
||||
public async execute(): Promise<void> {
|
||||
// Get all tiles
|
||||
const tilesDir = getPublicPath('tiles')
|
||||
const tilesDir = Storage.getPublicPath('tiles')
|
||||
const tiles = fs.readdirSync(tilesDir).filter((file) => file.endsWith('.png'))
|
||||
|
||||
// Create output directory if it doesn't exist
|
||||
@ -18,14 +18,14 @@ export default class TilesCommand extends BaseCommand {
|
||||
|
||||
for (const tile of tiles) {
|
||||
// Check if tile is already 66x34
|
||||
const metadata = await sharp(getPublicPath('tiles', tile)).metadata()
|
||||
const metadata = await sharp(Storage.getPublicPath('tiles', tile)).metadata()
|
||||
if (metadata.width === 66 && metadata.height === 34) {
|
||||
this.logger.info(`Tile ${tile} already processed`)
|
||||
continue
|
||||
}
|
||||
|
||||
const inputPath = getPublicPath('tiles', tile)
|
||||
const tempPath = getPublicPath('tiles', `temp_${tile}`)
|
||||
const inputPath = Storage.getPublicPath('tiles', tile)
|
||||
const tempPath = Storage.getPublicPath('tiles', `temp_${tile}`)
|
||||
|
||||
try {
|
||||
await sharp(inputPath)
|
||||
|
@ -4,7 +4,7 @@ import { Server } from 'socket.io'
|
||||
|
||||
import { gameLogger, gameMasterLogger } from '#application/logger'
|
||||
import prisma from '#application/prisma'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
|
||||
@ -38,10 +38,10 @@ export default class ObjectRemoveEvent {
|
||||
})
|
||||
|
||||
// get root path
|
||||
const public_folder = getPublicPath('objects')
|
||||
const public_folder = Storage.getPublicPath('objects')
|
||||
|
||||
// remove the tile from the disk
|
||||
const finalFilePath = getPublicPath('objects', data.object + '.png')
|
||||
const finalFilePath = Storage.getPublicPath('objects', data.object + '.png')
|
||||
fs.unlink(finalFilePath, (err) => {
|
||||
if (err) {
|
||||
gameMasterLogger.error(`Error deleting object ${data.object}: ${err.message}`)
|
||||
|
@ -6,7 +6,7 @@ import { Server } from 'socket.io'
|
||||
|
||||
import { gameMasterLogger } from '#application/logger'
|
||||
import prisma from '#application/prisma'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
|
||||
@ -32,7 +32,7 @@ export default class ObjectUploadEvent {
|
||||
if (character.role !== 'gm') {
|
||||
return callback(false)
|
||||
}
|
||||
const public_folder = getPublicPath('objects')
|
||||
const public_folder = Storage.getPublicPath('objects')
|
||||
|
||||
// Ensure the folder exists
|
||||
await fs.mkdir(public_folder, { recursive: true })
|
||||
@ -56,7 +56,7 @@ export default class ObjectUploadEvent {
|
||||
|
||||
const uuid = object.id
|
||||
const filename = `${uuid}.png`
|
||||
const finalFilePath = getPublicPath('objects', filename)
|
||||
const finalFilePath = Storage.getPublicPath('objects', filename)
|
||||
await writeFile(finalFilePath, objectData)
|
||||
|
||||
gameMasterLogger.info('gm:object:upload', `Object ${key} uploaded with id ${uuid}`)
|
||||
|
@ -3,7 +3,7 @@ import fs from 'fs/promises'
|
||||
import { Server } from 'socket.io'
|
||||
|
||||
import prisma from '#application/prisma'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
|
||||
@ -26,7 +26,7 @@ export default class SpriteCreateEvent {
|
||||
return callback(false)
|
||||
}
|
||||
|
||||
const public_folder = getPublicPath('sprites')
|
||||
const public_folder = Storage.getPublicPath('sprites')
|
||||
|
||||
// Ensure the folder exists
|
||||
await fs.mkdir(public_folder, { recursive: true })
|
||||
@ -39,7 +39,7 @@ export default class SpriteCreateEvent {
|
||||
const uuid = sprite.id
|
||||
|
||||
// Create folder with uuid
|
||||
const sprite_folder = getPublicPath('sprites', uuid)
|
||||
const sprite_folder = Storage.getPublicPath('sprites', uuid)
|
||||
await fs.mkdir(sprite_folder, { recursive: true })
|
||||
|
||||
callback(true)
|
||||
|
@ -4,7 +4,7 @@ import { Server } from 'socket.io'
|
||||
|
||||
import { gameMasterLogger } from '#application/logger'
|
||||
import prisma from '#application/prisma'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { TSocket } from '#application/types'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
|
||||
@ -19,7 +19,7 @@ export default class GMSpriteDeleteEvent {
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {
|
||||
this.public_folder = getPublicPath('sprites')
|
||||
this.public_folder = Storage.getPublicPath('sprites')
|
||||
}
|
||||
|
||||
public listen(): void {
|
||||
@ -45,7 +45,7 @@ export default class GMSpriteDeleteEvent {
|
||||
}
|
||||
|
||||
private async deleteSpriteFolder(spriteId: string): Promise<void> {
|
||||
const finalFilePath = getPublicPath('sprites', spriteId)
|
||||
const finalFilePath = Storage.getPublicPath('sprites', spriteId)
|
||||
|
||||
if (fs.existsSync(finalFilePath)) {
|
||||
await fs.promises.rmdir(finalFilePath, { recursive: true })
|
||||
|
@ -7,7 +7,7 @@ import type { Prisma, SpriteAction } from '@prisma/client'
|
||||
|
||||
import { gameMasterLogger } from '#application/logger'
|
||||
import prisma from '#application/prisma'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { TSocket } from '#application/types'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
|
||||
@ -314,13 +314,13 @@ export default class SpriteUpdateEvent {
|
||||
}
|
||||
|
||||
private async saveSpritesToDisk(id: string, actions: ProcessedSpriteAction[]): Promise<void> {
|
||||
const publicFolder = getPublicPath('sprites', id)
|
||||
const publicFolder = Storage.getPublicPath('sprites', id)
|
||||
await mkdir(publicFolder, { recursive: true })
|
||||
|
||||
await Promise.all(
|
||||
actions.map(async (action) => {
|
||||
const spritesheet = await this.createSpritesheet(action.buffersWithDimensions)
|
||||
await writeFile(getPublicPath('sprites', id, `${action.action}.png`), spritesheet)
|
||||
await writeFile(Storage.getPublicPath('sprites', id, `${action.action}.png`), spritesheet)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { Server } from 'socket.io'
|
||||
|
||||
import { gameMasterLogger } from '#application/logger'
|
||||
import prisma from '#application/prisma'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
|
||||
@ -19,7 +19,7 @@ export default class GMTileDeleteEvent {
|
||||
private readonly io: Server,
|
||||
private readonly socket: TSocket
|
||||
) {
|
||||
this.public_folder = getPublicPath('tiles')
|
||||
this.public_folder = Storage.getPublicPath('tiles')
|
||||
}
|
||||
|
||||
public listen(): void {
|
||||
@ -56,7 +56,7 @@ export default class GMTileDeleteEvent {
|
||||
}
|
||||
|
||||
private async deleteTileFile(tileId: string): Promise<void> {
|
||||
const finalFilePath = getPublicPath('tiles', `${tileId}.png`)
|
||||
const finalFilePath = Storage.getPublicPath('tiles', `${tileId}.png`)
|
||||
try {
|
||||
await fs.unlink(finalFilePath)
|
||||
} catch (error: any) {
|
||||
|
@ -5,7 +5,7 @@ import { Server } from 'socket.io'
|
||||
|
||||
import { gameMasterLogger } from '#application/logger'
|
||||
import prisma from '#application/prisma'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { TSocket } from '#application/types'
|
||||
import characterRepository from '#repositories/characterRepository'
|
||||
|
||||
@ -32,7 +32,7 @@ export default class TileUploadEvent {
|
||||
return
|
||||
}
|
||||
|
||||
const public_folder = getPublicPath('tiles')
|
||||
const public_folder = Storage.getPublicPath('tiles')
|
||||
|
||||
// Ensure the folder exists
|
||||
await fs.mkdir(public_folder, { recursive: true })
|
||||
@ -45,7 +45,7 @@ export default class TileUploadEvent {
|
||||
})
|
||||
const uuid = tile.id
|
||||
const filename = `${uuid}.png`
|
||||
const finalFilePath = getPublicPath('tiles', filename)
|
||||
const finalFilePath = Storage.getPublicPath('tiles', filename)
|
||||
await writeFile(finalFilePath, tileData)
|
||||
})
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { Request, Response } from 'express'
|
||||
|
||||
import { BaseController } from '#application/base/baseController'
|
||||
import Database from '#application/database'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { AssetData, UUID } from '#application/types'
|
||||
import SpriteRepository from '#repositories/spriteRepository'
|
||||
import TileRepository from '#repositories/tileRepository'
|
||||
@ -98,7 +98,7 @@ export class AssetsController extends BaseController {
|
||||
public async downloadAsset(req: Request, res: Response) {
|
||||
const { type, spriteId, file } = req.params
|
||||
|
||||
const assetPath = type === 'sprites' && spriteId ? getPublicPath(type, spriteId, file) : getPublicPath(type, file)
|
||||
const assetPath = type === 'sprites' && spriteId ? Storage.getPublicPath(type, spriteId, file) : Storage.getPublicPath(type, file)
|
||||
|
||||
if (!fs.existsSync(assetPath)) {
|
||||
this.logger.error(`File not found: ${assetPath}`)
|
||||
|
@ -4,14 +4,15 @@ import { Request, Response } from 'express'
|
||||
import sharp from 'sharp'
|
||||
|
||||
import { BaseController } from '#application/base/baseController'
|
||||
import { getPublicPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { UUID } from '#application/types'
|
||||
import CharacterHairRepository from '#repositories/characterHairRepository'
|
||||
import CharacterRepository from '#repositories/characterRepository'
|
||||
import CharacterTypeRepository from '#repositories/characterTypeRepository'
|
||||
|
||||
interface AvatarOptions {
|
||||
characterTypeId: number
|
||||
characterHairId?: number
|
||||
characterTypeId: UUID
|
||||
characterHairId?: UUID
|
||||
}
|
||||
|
||||
export class AvatarController extends BaseController {
|
||||
@ -57,7 +58,7 @@ export class AvatarController extends BaseController {
|
||||
return this.sendError(res, 'Character type not found', 404)
|
||||
}
|
||||
|
||||
const bodySpritePath = getPublicPath('sprites', characterType.sprite.id, 'idle_right_down.png')
|
||||
const bodySpritePath = Storage.getPublicPath('sprites', characterType.sprite.id, 'idle_right_down.png')
|
||||
if (!fs.existsSync(bodySpritePath)) {
|
||||
return this.sendError(res, 'Body sprite file not found', 404)
|
||||
}
|
||||
@ -71,7 +72,7 @@ export class AvatarController extends BaseController {
|
||||
if (options.characterHairId) {
|
||||
const characterHair = await CharacterHairRepository.getById(options.characterHairId)
|
||||
if (characterHair?.sprite?.id) {
|
||||
const hairSpritePath = getPublicPath('sprites', characterHair.sprite.id, 'front.png')
|
||||
const hairSpritePath = Storage.getPublicPath('sprites', characterHair.sprite.id, 'front.png')
|
||||
if (fs.existsSync(hairSpritePath)) {
|
||||
avatar = avatar.composite([{ input: hairSpritePath, gravity: 'north' }])
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import { Server as SocketServer } from 'socket.io'
|
||||
|
||||
import config from '#application/config'
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
import { getAppPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { TSocket } from '#application/types'
|
||||
import SocketManager from '#managers/socketManager'
|
||||
|
||||
@ -56,9 +56,9 @@ class QueueManager {
|
||||
const { jobName, params, socketId } = job.data
|
||||
|
||||
try {
|
||||
const jobsDir = getAppPath('jobs')
|
||||
const jobsDir = Storage.getAppPath('jobs')
|
||||
const extension = config.ENV === 'development' ? '.ts' : '.js'
|
||||
const jobPath = getAppPath('jobs', `${jobName}${extension}`)
|
||||
const jobPath = Storage.getAppPath('jobs', `${jobName}${extension}`)
|
||||
|
||||
if (!fs.existsSync(jobPath)) {
|
||||
this.logger.warn(`Job file not found: ${jobPath}`)
|
||||
|
@ -7,7 +7,7 @@ import { Server as SocketServer } from 'socket.io'
|
||||
|
||||
import config from '#application/config'
|
||||
import Logger, { LoggerType } from '#application/logger'
|
||||
import { getAppPath } from '#application/storage'
|
||||
import Storage from '#application/storage'
|
||||
import { TSocket } from '#application/types'
|
||||
import { Authentication } from '#middleware/authentication'
|
||||
|
||||
@ -61,11 +61,11 @@ class SocketManager {
|
||||
*/
|
||||
private async loadEventHandlers(baseDir: string, subDir: string, socket: TSocket): Promise<void> {
|
||||
try {
|
||||
const fullDir = getAppPath(baseDir, subDir)
|
||||
const fullDir = Storage.getAppPath(baseDir, subDir)
|
||||
const files = await fs.promises.readdir(fullDir, { withFileTypes: true })
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = getAppPath(baseDir, subDir, file.name)
|
||||
const filePath = Storage.getAppPath(baseDir, subDir, file.name)
|
||||
|
||||
if (file.isDirectory()) {
|
||||
await this.loadEventHandlers(baseDir, `${subDir}/${file.name}`, socket)
|
||||
|
Loading…
x
Reference in New Issue
Block a user