forked from noxious/server
POC working new caching method - moved controllers folder, renamed assets to textures, fixed HTTP bug, formatted code
This commit is contained in:
123
src/controllers/assets.ts
Normal file
123
src/controllers/assets.ts
Normal file
@ -0,0 +1,123 @@
|
||||
import fs from 'fs'
|
||||
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
import { BaseController } from '#application/base/baseController'
|
||||
import Database from '#application/database'
|
||||
import Storage from '#application/storage'
|
||||
import { AssetData, UUID } from '#application/types'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
import SpriteRepository from '#repositories/spriteRepository'
|
||||
import TileRepository from '#repositories/tileRepository'
|
||||
|
||||
export class AssetsController extends BaseController {
|
||||
private readonly mapRepository = new MapRepository()
|
||||
private readonly spriteRepository = new SpriteRepository()
|
||||
private readonly tileRepository = new TileRepository()
|
||||
|
||||
/**
|
||||
* List tiles
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async listTiles(req: Request, res: Response) {
|
||||
const assets: AssetData[] = []
|
||||
const tiles = await this.tileRepository.getAll()
|
||||
|
||||
for (const tile of tiles) {
|
||||
assets.push({ key: tile.getId(), data: '/assets/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData)
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, assets)
|
||||
}
|
||||
|
||||
/**
|
||||
* List tiles by map
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async listTilesByMap(req: Request, res: Response) {
|
||||
const mapId = req.params.mapId as UUID
|
||||
|
||||
if (!mapId) {
|
||||
return this.sendError(res, 'Invalid map ID', 400)
|
||||
}
|
||||
|
||||
const map = await this.mapRepository.getById(mapId)
|
||||
if (!map) {
|
||||
return this.sendError(res, 'Map not found', 404)
|
||||
}
|
||||
|
||||
const assets: AssetData[] = []
|
||||
const tiles = await this.tileRepository.getByMapId(mapId)
|
||||
|
||||
for (const tile of tiles) {
|
||||
assets.push({ key: tile.getId(), data: '/assets/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData)
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, assets)
|
||||
}
|
||||
|
||||
/**
|
||||
* List sprite actions
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async listSpriteActions(req: Request, res: Response) {
|
||||
const spriteId = req.params.spriteId as UUID
|
||||
|
||||
if (!spriteId) {
|
||||
return this.sendError(res, 'Invalid sprite ID', 400)
|
||||
}
|
||||
|
||||
const sprite = await this.spriteRepository.getById(spriteId)
|
||||
if (!sprite) {
|
||||
return this.sendError(res, 'Sprite not found', 404)
|
||||
}
|
||||
|
||||
await this.spriteRepository.getEntityManager().populate(sprite, ['spriteActions'])
|
||||
|
||||
const assets: AssetData[] = sprite.getSpriteActions().map((spriteAction) => ({
|
||||
key: sprite.getId() + '-' + spriteAction.getAction(),
|
||||
data: '/assets/sprites/' + sprite.getId() + '/' + spriteAction.getAction() + '.png',
|
||||
group: spriteAction.getIsAnimated() ? 'sprite_animations' : 'sprites',
|
||||
updatedAt: sprite.getUpdatedAt(),
|
||||
originX: Number(spriteAction.getOriginX().toString()),
|
||||
originY: Number(spriteAction.getOriginY().toString()),
|
||||
isAnimated: spriteAction.getIsAnimated(),
|
||||
frameRate: spriteAction.getFrameRate(),
|
||||
frameWidth: spriteAction.getFrameWidth(),
|
||||
frameHeight: spriteAction.getFrameHeight(),
|
||||
frameCount: spriteAction.getSprites()?.length
|
||||
}))
|
||||
|
||||
return this.sendSuccess(res, assets)
|
||||
}
|
||||
|
||||
/**
|
||||
* Download asset
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async downloadAsset(req: Request, res: Response) {
|
||||
const { type, spriteId, file } = req.params
|
||||
|
||||
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}`)
|
||||
return this.sendError(res, 'Asset not found', 404)
|
||||
}
|
||||
|
||||
res.sendFile(assetPath, (err) => {
|
||||
if (err) {
|
||||
this.logger.error('Error sending file:' + err)
|
||||
this.sendError(res, 'Error downloading the asset', 500)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public async downloadCache(req: Request, res: Response) {
|
||||
|
||||
}
|
||||
}
|
98
src/controllers/auth.ts
Normal file
98
src/controllers/auth.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { Request, Response } from 'express'
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
import { BaseController } from '#application/base/baseController'
|
||||
import config from '#application/config'
|
||||
import { loginAccountSchema, registerAccountSchema, resetPasswordSchema, newPasswordSchema } from '#application/zodTypes'
|
||||
import UserService from '#services/userService'
|
||||
|
||||
export class AuthController extends BaseController {
|
||||
/**
|
||||
* Login user
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async login(req: Request, res: Response) {
|
||||
const { username, password } = req.body
|
||||
|
||||
try {
|
||||
loginAccountSchema.parse({ username, password })
|
||||
const user = await UserService.login(username, password)
|
||||
|
||||
if (user && typeof user !== 'boolean') {
|
||||
const token = jwt.sign({ id: user.getId() }, config.JWT_SECRET, { expiresIn: '4h' })
|
||||
return this.sendSuccess(res, { token })
|
||||
}
|
||||
|
||||
return this.sendError(res, 'Invalid credentials')
|
||||
} catch (error: any) {
|
||||
return this.sendError(res, error.errors?.[0]?.message || 'Validation error')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register user
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async register(req: Request, res: Response) {
|
||||
const { username, email, password } = req.body
|
||||
|
||||
try {
|
||||
registerAccountSchema.parse({ username, email, password })
|
||||
const user = await UserService.register(username, email, password)
|
||||
|
||||
if (user) {
|
||||
return this.sendSuccess(res, null, 'User registered successfully')
|
||||
}
|
||||
|
||||
return this.sendError(res, 'Failed to register user')
|
||||
} catch (error: any) {
|
||||
return this.sendError(res, error.errors?.[0]?.message || 'Validation error')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request password reset
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async requestPasswordReset(req: Request, res: Response) {
|
||||
const { email } = req.body
|
||||
|
||||
try {
|
||||
resetPasswordSchema.parse({ email })
|
||||
const sentEmail = await UserService.requestPasswordReset(email)
|
||||
|
||||
if (sentEmail) {
|
||||
return this.sendSuccess(res, null, 'Password reset email sent')
|
||||
}
|
||||
|
||||
return this.sendError(res, 'Failed to send password reset request')
|
||||
} catch (error: any) {
|
||||
return this.sendError(res, error.errors?.[0]?.message || 'Validation error')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset password
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async resetPassword(req: Request, res: Response) {
|
||||
const { urlToken, password } = req.body
|
||||
|
||||
try {
|
||||
newPasswordSchema.parse({ urlToken, password })
|
||||
const resetPassword = await UserService.resetPassword(urlToken, password)
|
||||
|
||||
if (resetPassword) {
|
||||
return this.sendSuccess(res, null, 'Password has been reset')
|
||||
}
|
||||
|
||||
return this.sendError(res, 'Failed to reset password')
|
||||
} catch (error: any) {
|
||||
return this.sendError(res, error.errors?.[0]?.message || 'Validation error')
|
||||
}
|
||||
}
|
||||
}
|
92
src/controllers/avatar.ts
Normal file
92
src/controllers/avatar.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import fs from 'fs'
|
||||
|
||||
import { Request, Response } from 'express'
|
||||
import sharp from 'sharp'
|
||||
|
||||
import { BaseController } from '#application/base/baseController'
|
||||
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: UUID
|
||||
characterHairId?: UUID
|
||||
}
|
||||
|
||||
export class AvatarController extends BaseController {
|
||||
private readonly characterTypeRepository = new CharacterTypeRepository()
|
||||
private readonly characterHairRepository = new CharacterHairRepository()
|
||||
private readonly characterRepository = new CharacterRepository()
|
||||
|
||||
/**
|
||||
* Get avatar by character
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async getByName(req: Request, res: Response) {
|
||||
const character = await this.characterRepository.getByName(req.params.characterName)
|
||||
if (!character?.characterType) {
|
||||
return this.sendError(res, 'Character or character type not found', 404)
|
||||
}
|
||||
|
||||
return this.generateAvatar(res, {
|
||||
characterTypeId: character.characterType.id,
|
||||
characterHairId: character.characterHair?.id
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get avatar by character type and hair
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async getByParams(req: Request, res: Response) {
|
||||
return this.generateAvatar(res, {
|
||||
characterTypeId: req.params.characterTypeId as UUID,
|
||||
characterHairId: req.params.characterHairId ? (req.params.characterHairId as UUID) : undefined
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate avatar
|
||||
* @param res
|
||||
* @param options
|
||||
* @private
|
||||
*/
|
||||
private async generateAvatar(res: Response, options: AvatarOptions) {
|
||||
try {
|
||||
const characterType = await this.characterTypeRepository.getById(options.characterTypeId)
|
||||
if (!characterType?.sprite?.id) {
|
||||
return this.sendError(res, 'Character type not found', 404)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
let avatar = sharp(bodySpritePath).extend({
|
||||
top: 2,
|
||||
bottom: 2,
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 }
|
||||
})
|
||||
|
||||
if (options.characterHairId) {
|
||||
const characterHair = await this.characterHairRepository.getById(options.characterHairId)
|
||||
if (characterHair?.sprite?.id) {
|
||||
const hairSpritePath = Storage.getPublicPath('sprites', characterHair.sprite.id, 'front.png')
|
||||
if (fs.existsSync(hairSpritePath)) {
|
||||
avatar = avatar.composite([{ input: hairSpritePath, gravity: 'north' }])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'image/png')
|
||||
return avatar.pipe(res)
|
||||
} catch (error) {
|
||||
return this.sendError(res, 'Error generating avatar', 500)
|
||||
}
|
||||
}
|
||||
}
|
92
src/controllers/cache.ts
Normal file
92
src/controllers/cache.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { Request, Response } from 'express'
|
||||
|
||||
import { BaseController } from '#application/base/baseController'
|
||||
import { AssetData, UUID } from '#application/types'
|
||||
import MapRepository from '#repositories/mapRepository'
|
||||
import SpriteRepository from '#repositories/spriteRepository'
|
||||
import TileRepository from '#repositories/tileRepository'
|
||||
|
||||
export class DataController extends BaseController {
|
||||
private readonly mapRepository = new MapRepository()
|
||||
private readonly spriteRepository = new SpriteRepository()
|
||||
private readonly tileRepository = new TileRepository()
|
||||
|
||||
/**
|
||||
* List tiles
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async tiles(req: Request, res: Response) {
|
||||
const assets: AssetData[] = []
|
||||
const tiles = await this.tileRepository.getAll()
|
||||
|
||||
for (const tile of tiles) {
|
||||
assets.push({ key: tile.getId(), data: '/assets/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData)
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, assets)
|
||||
}
|
||||
|
||||
/**
|
||||
* List tiles by map
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async listTilesByMap(req: Request, res: Response) {
|
||||
const mapId = req.params.mapId as UUID
|
||||
|
||||
if (!mapId) {
|
||||
return this.sendError(res, 'Invalid map ID', 400)
|
||||
}
|
||||
|
||||
const map = await this.mapRepository.getById(mapId)
|
||||
if (!map) {
|
||||
return this.sendError(res, 'Map not found', 404)
|
||||
}
|
||||
|
||||
const assets: AssetData[] = []
|
||||
const tiles = await this.tileRepository.getByMapId(mapId)
|
||||
|
||||
for (const tile of tiles) {
|
||||
assets.push({ key: tile.getId(), data: '/assets/tiles/' + tile.getId() + '.png', group: 'tiles', updatedAt: tile.getUpdatedAt() } as AssetData)
|
||||
}
|
||||
|
||||
return this.sendSuccess(res, assets)
|
||||
}
|
||||
|
||||
/**
|
||||
* List sprite actions
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
public async listSpriteActions(req: Request, res: Response) {
|
||||
const spriteId = req.params.spriteId as UUID
|
||||
|
||||
if (!spriteId) {
|
||||
return this.sendError(res, 'Invalid sprite ID', 400)
|
||||
}
|
||||
|
||||
const sprite = await this.spriteRepository.getById(spriteId)
|
||||
if (!sprite) {
|
||||
return this.sendError(res, 'Sprite not found', 404)
|
||||
}
|
||||
|
||||
await this.spriteRepository.getEntityManager().populate(sprite, ['spriteActions'])
|
||||
|
||||
const assets: AssetData[] = sprite.getSpriteActions().map((spriteAction) => ({
|
||||
key: sprite.getId() + '-' + spriteAction.getAction(),
|
||||
data: '/assets/sprites/' + sprite.getId() + '/' + spriteAction.getAction() + '.png',
|
||||
group: spriteAction.getIsAnimated() ? 'sprite_animations' : 'sprites',
|
||||
updatedAt: sprite.getUpdatedAt(),
|
||||
originX: Number(spriteAction.getOriginX().toString()),
|
||||
originY: Number(spriteAction.getOriginY().toString()),
|
||||
isAnimated: spriteAction.getIsAnimated(),
|
||||
frameRate: spriteAction.getFrameRate(),
|
||||
frameWidth: spriteAction.getFrameWidth(),
|
||||
frameHeight: spriteAction.getFrameHeight(),
|
||||
frameCount: spriteAction.getSprites()?.length
|
||||
}))
|
||||
|
||||
return this.sendSuccess(res, assets)
|
||||
}
|
||||
}
|
0
src/controllers/textures.ts
Normal file
0
src/controllers/textures.ts
Normal file
Reference in New Issue
Block a user