diff --git a/.gitignore b/.gitignore index 22239d80a5439734168dc992e57cc02191f8010c..acb19f9f8addf6ff983d7ec622ae1a6c4ef40428 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,7 @@ lerna-debug.log* .env ormconfig.json *.providers.ts + +# uploads +images/* +!images/default.jpeg diff --git a/images/default.jpeg b/images/default.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..7e5e656834bfa61b0fc135c94d1fb9089596f2f6 Binary files /dev/null and b/images/default.jpeg differ diff --git a/package-lock.json b/package-lock.json index 01d2eb7272357854133cd7fe3325d9234a86c4cb..ce42b1efb531ab17de2f16ab7c76349893a398af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3125,7 +3125,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3527,7 +3528,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3582,6 +3584,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3625,12 +3628,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/src/app.controller.ts b/src/app.controller.ts index cce879ee622146012901c9adb47ef40c0fd3a555..045e5b1ae398cd994ada21cd7f9512b475af30ec 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get } from '@nestjs/common'; +import { Controller, Get, Param, Res } from '@nestjs/common'; import { AppService } from './app.service'; @Controller() @@ -9,4 +9,9 @@ export class AppController { getHello(): string { return this.appService.getHello(); } + + @Get('images/:id') + returnImage(@Param('id') id, @Res() res) { + return; + } } diff --git a/src/game/game.controller.ts b/src/game/game.controller.ts index 092f484ffe1576be36a4a6e74d27bb28fbf0e4dc..b79699c3e50130bc9a9537257c0130182ef278ea 100644 --- a/src/game/game.controller.ts +++ b/src/game/game.controller.ts @@ -10,12 +10,17 @@ import { UseInterceptors, ClassSerializerInterceptor, Delete, + UploadedFile, + Res, } from '@nestjs/common'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { diskStorage } from 'multer'; +import { extname } from 'path'; import { GameService } from './game.service'; import { AuthGuard } from '../shared/auth.guard'; import { User } from '../user/user.decorator'; -import { GameDTO, FlagboxEventDTO, GameStateDTO } from './game.dto'; +import { GameDTO, FlagboxEventDTO, GameStateDTO, newGameDTO } from './game.dto'; import { ValidationPipe } from '../shared/validation.pipe'; import { Roles, GameStates } from '../shared/guard.decorator'; import { GameEntity } from './game.entity'; @@ -36,7 +41,7 @@ export class GameController { @Post('new') @UseGuards(new AuthGuard()) @UsePipes(new ValidationPipe()) - async newGame(@User('id') person, @Body() body: GameDTO) { + async newGame(@User('id') person, @Body() body: newGameDTO) { return this.gameservice.createNewGame(person, body); } @@ -101,4 +106,30 @@ export class GameController { async flagboxEvent(@Param('id') id: string, @Body() data: FlagboxEventDTO) { return this.gameservice.flagboxEvent(id, data); } + + @Post('upload') + @UseInterceptors( + FileInterceptor('image', { + storage: diskStorage({ + destination: './images', + filename: (req, file, cb) => { + // Generating a 32 random chars long string + const randomName = Array(32) + .fill(null) + .map(() => Math.round(Math.random() * 16).toString(16)) + .join(''); + //Calling the callback passing the random name generated with the original extension name + cb(null, `${randomName}${extname(file.originalname)}`); + }, + }), + }), + ) + uploadImage(@UploadedFile() image) { + return image; + } + + @Get('images/:img') + returnImage(@Param('img') image, @Res() res) { + return res.sendFile(image, { root: 'images' }); + } } diff --git a/src/game/game.dto.ts b/src/game/game.dto.ts index 1809314fb6cfcab6b7618f5b42add114505e157a..e6d99869c101eedc77d7050275c2de4d09b9540f 100644 --- a/src/game/game.dto.ts +++ b/src/game/game.dto.ts @@ -62,15 +62,19 @@ export class newGameDTO { @IsNotEmpty() @Length(1, 255) desc: string; - @IsNotEmpty() - @Validate(CenterJSON) - center: JSON; + @ValidateNested() + @Type(() => CenterDTO) + center: CenterDTO; @IsDateString() @IsNotEmpty() startdate: string; @IsDateString() @IsNotEmpty() enddate: string; + @Length(0, 65) + image: string; + @Allow() + map?: JSON; } export class GameStateDTO { diff --git a/src/game/game.entity.ts b/src/game/game.entity.ts index ccc6ea24bbeef81f6a899e862406e1cc6a43d174..56f3f3f6e2ffe341e6677e574bebf4b98b2b7be7 100644 --- a/src/game/game.entity.ts +++ b/src/game/game.entity.ts @@ -28,6 +28,7 @@ export class GameEntity { @Column('text') state: string; @Column('timestamp') startdate: Timestamp; @Column('timestamp') enddate: Timestamp; + @Column('text') image: string; @OneToMany(type => FactionEntity, factions => factions.game) factions: FactionEntity[]; diff --git a/src/game/game.module.ts b/src/game/game.module.ts index 6fffbb0132fd6085fa82adfa93d9bec083559cb2..2c5ba6add51069313c81227f2f95039db5a9f432 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -16,6 +16,7 @@ import { NotificationModule } from '../notifications/notifications.module'; import { TickService } from './tick.service'; import { ScoreService } from '../score/score.service'; import { ScoreEntity } from '../score/score.entity'; +import { MulterModule } from '@nestjs/platform-express'; ///////////////////////////////////////////////////////////////////// /// Game /// @@ -34,6 +35,9 @@ import { ScoreEntity } from '../score/score.entity'; ScoreEntity, ]), NotificationModule, + MulterModule.register({ + dest: './images', + }), ], controllers: [GameController], providers: [GameService, TickService, ScoreService], diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 4863cf067d2133cc428da74bb80b1ed6448bff41..9d0244a4c523d1759818439d5f915f206af4aebe 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -8,7 +8,7 @@ import { ObjectivePointEntity, ObjectivePoint_HistoryEntity, } from './game.entity'; -import { GameDTO, FlagboxEventDTO, GameStateDTO } from './game.dto'; +import { GameDTO, FlagboxEventDTO, GameStateDTO, newGameDTO } from './game.dto'; import { PersonEntity } from '../user/user.entity'; import { FactionEntity } from '../faction/faction.entity'; import { NotificationGateway } from '../notifications/notifications.gateway'; @@ -34,7 +34,7 @@ export class GameService { private tickService: TickService, ) {} // create a new game - async createNewGame(personId: PersonEntity, gameData: GameDTO) { + async createNewGame(personId: PersonEntity, gameData: newGameDTO) { // checks if a game with the same name exists already if (await this.gameRepository.findOne({ name: gameData.name })) { throw new HttpException('Game already exists', HttpStatus.BAD_REQUEST); diff --git a/src/tracking/tracking.service.ts b/src/tracking/tracking.service.ts index c40d5f78cda46a69127463431a2abfb6032a3793..1a772eec2d71e9c22eebc5fe49d12cf6d828779a 100644 --- a/src/tracking/tracking.service.ts +++ b/src/tracking/tracking.service.ts @@ -70,35 +70,54 @@ export class TrackingService { playerdata.push( await this.trackingrepository.find({ where: { faction: gameperson.faction }, - relations: ['faction', 'gamepersonId'], + relations: ['faction', 'gamepersonId', 'gamepersonId.person'], }), ); } else { let factions = await this.factionRepository.find({ game: gameId }); playerdata = await Promise.all( factions.map(async faction => { - return await this.trackingrepository.find({ + let rawdata = await this.trackingrepository.find({ where: { faction: faction.factionId }, - relations: ['faction', 'gamepersonId'], + relations: ['faction', 'gamepersonId', 'gamepersonId.person'], }); + let groups = { + 'infantry.svg': [], + 'recon.svg': [], + 'mechanized.svg': [], + }; + rawdata.forEach(async player => { + groups[player.icon].push(player); + }); + return groups; }), ); } // parse data + // create an array for each faction + // inside faction create an array for each icon type + // insisde icon arrays include all players with same icon const currentdata = await Promise.all( await playerdata.map(async faction => { - return await Promise.all( - faction.map(async player => { - return await { - gamepersonId: player['gamepersonId']['gamepersonId'], - gamepersonRole: player['gamepersonId']['role'], - factionId: player['faction']['factionId'], - factionColour: player['faction']['colour'], - icon: player['icon'], - coordinates: player['data'].pop(), - }; - }), - ); + let data = []; + for (let group in faction) { + data.push( + await Promise.all( + faction[group].map(async player => { + return await { + username: player['gamepersonId']['person']['name'], + gamepersonId: player['gamepersonId']['gamepersonId'], + gamepersonRole: player['gamepersonId']['role'], + factionId: player['faction']['factionId'], + factionColour: player['faction']['colour'], + icon: player['icon'], + coordinates: player['data'].pop(), + }; + }), + ), + ); + } + return data; }), ); return currentdata;