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) } } }