import { Application, Request, Response } from 'express' import UserService from '../services/userService' import jwt from 'jsonwebtoken' import config from './config' import { loginAccountSchema, registerAccountSchema, resetPasswordSchema, newPasswordSchema } from './zodTypes' import fs from 'fs' import { httpLogger } from './logger' import { getPublicPath } from './storage' import TileRepository from '../repositories/tileRepository' import { AssetData } from './types' import ZoneRepository from '../repositories/zoneRepository' import SpriteRepository from '../repositories/spriteRepository' async function addHttpRoutes(app: Application) { /** * Login * @param req * @param res */ app.post('/login', async (req: Request, res: Response) => { const { username, password } = req.body try { loginAccountSchema.parse({ username, password }) } catch (error: any) { return res.status(400).json({ message: error.errors[0]?.message }) } const userService = new UserService() const user = await userService.login(username, password) if (user && typeof user !== 'boolean') { const token = jwt.sign({ id: user.id }, config.JWT_SECRET, { expiresIn: '4h' }) return res.status(200).json({ token }) } return res.status(400).json({ message: 'Failed to login' }) }) /** * Register * @param req * @param res */ app.post('/register', async (req: Request, res: Response) => { const { username, email, password } = req.body try { registerAccountSchema.parse({ username, email, password }) } catch (error: any) { return res.status(400).json({ message: error.errors[0]?.message }) } const userService = new UserService() const user = await userService.register(username, email, password) if (user) { return res.status(200).json({ message: 'User registered' }) } return res.status(400).json({ message: 'Failed to register user' }) }) /** * Reset password * @param req * @param res */ app.post('/reset-password', async (req: Request, res: Response) => { const { email } = req.body try { resetPasswordSchema.parse({ email }) } catch (error: any) { return res.status(400).json({ message: error.errors[0]?.message }) } const userService = new UserService() const sentEmail = await userService.requestPasswordReset(email) if (sentEmail) { return res.status(200).json({ message: 'Email has been sent' }) } return res.status(400).json({ message: 'Failed to send password reset request' }) }) /** * New password * @param req * @param res */ app.post('/new-password', async (req: Request, res: Response) => { const { urlToken, password } = req.body try { newPasswordSchema.parse({ urlToken, password }) } catch (error: any) { return res.status(400).json({ message: error.errors[0]?.message }) } const userService = new UserService() const resetPassword = await userService.resetPassword(urlToken, password) if (resetPassword) { return res.status(200).json({ message: 'Password has been reset' }) } return res.status(400).json({ message: 'Failed to set new password' }) }) /** * Get all tiles from a zone as an array of ids * @param req * @param res */ app.get('/assets/list_tiles', async (req: Request, res: Response) => { // Get all tiles let assets: AssetData[] = [] const tiles = await TileRepository.getAll() for (const tile of tiles) { assets.push({ key: tile.id, data: '/assets/tiles/' + tile.id + '.png', group: 'tiles', updatedAt: tile.updatedAt } as AssetData) } // Return the array res.json(assets) }) /** * Get all tiles from a zone and serve as AssetData array * @param req * @param res */ app.get('/assets/list_tiles/:zoneId', async (req: Request, res: Response) => { const zoneId = req.params.zoneId // Check if zoneId is valid number if (!zoneId || parseInt(zoneId) === 0) { return res.status(400).json({ message: 'Invalid zone ID' }) } // Get zone by id const zone = await ZoneRepository.getById(parseInt(zoneId)) if (!zone) { return res.status(404).json({ message: 'Zone not found' }) } // Get all tiles let assets: AssetData[] = [] const tiles = await TileRepository.getByZoneId(parseInt(zoneId)) for (const tile of tiles) { assets.push({ key: tile.id, data: '/assets/tiles/' + tile.id + '.png', group: 'tiles', updatedAt: tile.updatedAt } as AssetData) } // Return the array res.json(assets) }) app.get('/assets/list_sprite_actions/:spriteId', async (req: Request, res: Response) => { const spriteId = req.params.spriteId // Check if spriteId is valid number if (!spriteId || parseInt(spriteId) === 0) { return res.status(400).json({ message: 'Invalid sprite ID' }) } // Get sprite by id const sprite = await SpriteRepository.getById(spriteId) if (!sprite) { return res.status(404).json({ message: 'Sprite not found' }) } let assets: AssetData[] = [] sprite.spriteActions.forEach((spriteAction) => { assets.push({ key: sprite.id + '-' + spriteAction.action, data: '/assets/sprites/' + sprite.id + '/' + spriteAction.action + '.png', group: spriteAction.isAnimated ? 'sprite_animations' : 'sprites', updatedAt: sprite.updatedAt, isAnimated: spriteAction.isAnimated, frameCount: JSON.parse(JSON.stringify(spriteAction.sprites)).length, frameWidth: spriteAction.frameWidth, frameHeight: spriteAction.frameHeight }) }) // Return the array res.json(assets) }) /** * Download asset file * @param req * @param res */ app.get('/assets/:type/:spriteId?/:file', (req: Request, res: Response) => { const assetType = req.params.type const spriteId = req.params.spriteId const fileName = req.params.file let assetPath if (assetType === 'sprites' && spriteId) { assetPath = getPublicPath(assetType, spriteId, fileName) } else { assetPath = getPublicPath(assetType, fileName) } if (!fs.existsSync(assetPath)) { httpLogger.error(`File not found: ${assetPath}`) return res.status(404).send('Asset not found') } res.sendFile(assetPath, (err) => { if (err) { httpLogger.error('Error sending file:', err) res.status(500).send('Error downloading the asset') } }) }) httpLogger.info('Web routes added') } export { addHttpRoutes }