diff --git a/Dockerfile b/Dockerfile index d6499ec1027ec8e3eaf63f279282628690d6f984..e3da96a638a495e9ea8c0b9f25845017e8955bbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,5 +4,5 @@ COPY package*.json ./ RUN npm install RUN npm ci --only=production COPY . . -EXPOSE 3000 +EXPOSE 5000 CMD [ "npm", "start", "--force" ] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c5b6dd6fc2c14b21ac27abfe4050131117f962d9..05c4c11f6797b7d67226fa03b30c6327b6fe9c69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -448,6 +448,41 @@ "uuid": "3.3.2" } }, + "@nestjs/jwt": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-6.1.1.tgz", + "integrity": "sha512-XZEYC+p69N+Accktjho0B98TkwAKCZNt91+t08eukw7Gwk6FvfJB+aBzHCmQEaWUiAOpAo4eObgac86P12XOkw==", + "requires": { + "@types/jsonwebtoken": "7.2.8", + "jsonwebtoken": "8.4.0" + }, + "dependencies": { + "@types/jsonwebtoken": { + "version": "7.2.8", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz", + "integrity": "sha512-XENN3YzEB8D6TiUww0O8SRznzy1v+77lH7UmuN54xq/IHIsyWjWOzZuFFTtoiRuaE782uAoRwBe/wwow+vQXZw==", + "requires": { + "@types/node": "*" + } + }, + "jsonwebtoken": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.4.0.tgz", + "integrity": "sha512-coyXjRTCy0pw5WYBpMvWOMN+Kjaik2MwTUIq9cna/W7NpO9E+iYbumZONAz3hcr+tXFJECoQVrtmIoC3Oz0gvg==", + "requires": { + "jws": "^3.1.5", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1" + } + } + } + }, "@nestjs/platform-express": { "version": "6.2.4", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-6.2.4.tgz", @@ -5879,6 +5914,20 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "passport-jwt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz", + "integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==", + "requires": { + "jsonwebtoken": "^8.2.0", + "passport-strategy": "^1.0.0" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", diff --git a/package.json b/package.json index cecd109950851c302849f33d7e0487f56736a661..eaf64ea6d6af75e1ef9ecb38237a0f2326895fc2 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "dependencies": { "@nestjs/common": "^6.0.0", "@nestjs/core": "^6.0.0", + "@nestjs/jwt": "^6.1.1", "@nestjs/platform-express": "^6.0.0", "@nestjs/platform-socket.io": "^6.3.1", "@nestjs/typeorm": "^6.1.1", @@ -31,6 +32,7 @@ "class-validator": "^0.9.1", "express-rate-limit": "^4.0.4", "jsonwebtoken": "^8.5.1", + "passport-jwt": "^4.0.0", "pg": "^7.11.0", "pg-hstore": "^2.3.2", "reflect-metadata": "^0.1.12", diff --git a/src/app.module.ts b/src/app.module.ts index f591eb0f2c4e6b6de14f8f1d3de17aa73bcf4dff..f593d727a4ed2ba97cc5f7735b104380de65c162 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,15 +1,17 @@ import { Module } from '@nestjs/common'; -import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core'; +import { APP_FILTER, APP_INTERCEPTOR, APP_GUARD } from '@nestjs/core'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { Connection } from 'typeorm'; import { UserModule } from './user/user.module'; + import { HttpErrorFilter } from './shared/http-error.filter'; import { LoggingInterceptor } from './shared/logging.interceptor'; import { MapMarkerModule } from './mapmarkers/mapmarkers.module'; import { NotificationModule } from './notifications/notifications.module'; import { GameModule } from './game/game.module'; +import { RolesGuard } from './shared/roles.guard'; @Module({ imports: [ @@ -30,6 +32,10 @@ import { GameModule } from './game/game.module'; provide: APP_INTERCEPTOR, useClass: LoggingInterceptor, }, + { + provide: APP_GUARD, + useClass: RolesGuard + } ], }) export class AppModule { diff --git a/src/game/coordinate.entity.ts b/src/game/coordinate.entity.ts index 9c5a38c2e91924e8a20e73c1802510069c8e43ed..65ad6545eb030813a796cb4fef836030bf247154 100644 --- a/src/game/coordinate.entity.ts +++ b/src/game/coordinate.entity.ts @@ -1,84 +1,30 @@ import { - Entity, - Column, - PrimaryGeneratedColumn, - OneToMany, - ManyToOne, - PrimaryColumn, -} from 'typeorm'; -import { - Game_PersonEntity, - ObjectivePointEntity, - GameEntity, -} from './game.entity'; -import { FactionEntity } from './faction.entity'; - -@Entity('Coordinate') -export class CoordinateEntity { - @PrimaryColumn('uuid') id: string; - - @OneToMany( - type => MapDrawingEntity, - mapDrawings => mapDrawings.mapDrawings_coordinates, - ) - mapDrawings: MapDrawingEntity[]; - @OneToMany( - type => Game_PersonEntity, - game_persons => game_persons.game_person_coordinates, - ) - game_persons: Game_PersonEntity[]; - @OneToMany( - type => ObjectivePointEntity, - objective_points => objective_points.coordinate, - ) - objective_points: ObjectivePointEntity[]; - @OneToMany(type => MapEntity, maps => maps.coordinate) - maps: ObjectivePointEntity[]; -} - -@Entity('Map') -export class MapEntity { - @PrimaryGeneratedColumn('uuid') mapId: string; - - @ManyToOne(type => CoordinateEntity, coordinate => coordinate.maps) - coordinate: CoordinateEntity; - @OneToMany(type => GameEntity, games => games.map) - games: GameEntity[]; - @OneToMany(type => MapDrawingEntity, mapDrawings => mapDrawings.map) - mapDrawings: MapDrawingEntity[]; -} - -@Entity('MapDrawing') -export class MapDrawingEntity { - @PrimaryGeneratedColumn('uuid') mapDrawingId: string; - @Column({ type: 'bool' }) drawingIsActive: boolean; - @Column({ type: 'time' }) drawingValidTill: string; - - @ManyToOne( - type => CoordinateEntity, - mapDrawings_coordinates => mapDrawings_coordinates.mapDrawings, - ) - mapDrawings_coordinates: CoordinateEntity; - @ManyToOne(type => MapEntity, map => map.mapDrawings) - map: MapEntity; - @ManyToOne( - type => MapDrawingTypeEntity, - mapDrawingType => mapDrawingType.mapDrawings, - ) - mapDrawingType: MapEntity; - @ManyToOne(type => FactionEntity, faction => faction.mapDrawings) - faction: FactionEntity; -} - -@Entity('MapDrawingType') -export class MapDrawingTypeEntity { - @PrimaryGeneratedColumn('uuid') mapDrawingTypeId: string; - @Column({ type: 'text' }) drawingTypeName: string; - @Column({ type: 'text' }) drawingTypeDescription: string; - - @OneToMany( - type => MapDrawingEntity, - mapDrawings => mapDrawings.mapDrawingType, - ) - mapDrawings: MapDrawingEntity[]; -} + Entity, + Column, + PrimaryGeneratedColumn, + OneToMany, + ManyToOne, + PrimaryColumn, + Timestamp, + } from 'typeorm'; + import { + Game_PersonEntity, + ObjectivePointEntity, + GameEntity, + } from './game.entity'; + import { FactionEntity } from './faction.entity'; + + @Entity('MapDrawing') + export class MapDrawingEntity { + @PrimaryGeneratedColumn('uuid') mapDrawingId: string; + @Column({ type: 'bool' }) drawingIsActive: boolean; + @Column({ type: 'time' }) drawingValidTill: string; + + @Column({ type: 'json', nullable: true }) data: JSON; + + @ManyToOne(type => FactionEntity, faction => faction.mapDrawings) + faction: FactionEntity; + @ManyToOne(type => GameEntity, gameEntity => gameEntity.id) + gameId: GameEntity; + } + \ No newline at end of file diff --git a/src/game/faction.entity.ts b/src/game/faction.entity.ts index 7caa5a57a008ed2075def6262b927dcc8de8b679..891421d3a41315549a9e589ef87ce1f22a9abc84 100644 --- a/src/game/faction.entity.ts +++ b/src/game/faction.entity.ts @@ -1,95 +1,95 @@ import { - Entity, - Column, - PrimaryGeneratedColumn, - OneToMany, - ManyToOne, - Timestamp, -} from 'typeorm'; -import { PersonEntity } from '../user/user.entity'; -import { GameEntity } from './game.entity'; -import { Game_PersonEntity } from './game.entity'; -import { MapDrawingEntity } from './coordinate.entity'; - -//Faction, PowerUp, Faction_powerUp, FP_History, Score, Task - -@Entity('Faction') -export class FactionEntity { - @PrimaryGeneratedColumn('uuid') factionId: string; - @Column({ type: 'text', unique: true }) factionName: string; - @Column({ type: 'text' }) factionPassword: string; - @Column({ type: 'float' }) multiplier: number; - - @OneToMany(type => MapDrawingEntity, mapDrawings => mapDrawings.faction) - mapDrawings: MapDrawingEntity[]; - @OneToMany(type => ScoreEntity, scores => scores.faction) - scores: ScoreEntity[]; - @OneToMany(type => PowerUpEntity, powerUps => powerUps.factions) - powerUps: Faction_PowerUpEntity[]; - @OneToMany(type => TaskEntity, tasks => tasks.faction) - tasks: TaskEntity[]; - @OneToMany(type => Game_PersonEntity, game_persons => game_persons.faction) - game_persons: Game_PersonEntity[]; - @ManyToOne(type => GameEntity, game => game.factions) - game: GameEntity; -} - -@Entity('PowerUp') -export class PowerUpEntity { - @PrimaryGeneratedColumn('uuid') powerUpId: string; - @Column({ type: 'text' }) powerUpName: string; - @Column({ type: 'text' }) powerUpDescription: string; - @Column({ type: 'int' }) amount: number; - @Column({ type: 'time' }) cooldown: string; - - @OneToMany(type => FactionEntity, factions => factions.powerUps) - factions: Faction_PowerUpEntity[]; -} - -@Entity('Faction_PowerUp') -export class Faction_PowerUpEntity { - @PrimaryGeneratedColumn('uuid') faction_powerUpId: string; - - @ManyToOne(type => FactionEntity, faction => faction.powerUps) - faction: FactionEntity; - @ManyToOne(type => PowerUpEntity, powerUp => powerUp.factions) - powerUp: PowerUpEntity; - @OneToMany(type => FP_HistoryEntity, histories => histories.faction_PowerUp) - histories: FP_HistoryEntity[]; -} - -@Entity('FP_History') -export class FP_HistoryEntity { - @PrimaryGeneratedColumn('uuid') historyId: string; - @Column({ type: 'timestamp' }) historyTimeStamp: Timestamp; - - @ManyToOne( - type => Faction_PowerUpEntity, - faction_PowerUp => faction_PowerUp.histories, - ) - faction_PowerUp: Faction_PowerUpEntity; -} - -@Entity('Score') -export class ScoreEntity { - @PrimaryGeneratedColumn('uuid') scoreId: string; - @Column({ type: 'float' }) score: number; - @Column({ type: 'timestamp' }) scoreTimeStamp: Timestamp; - - @ManyToOne(type => FactionEntity, factionName => factionName.scores) - faction: FactionEntity; -} - -@Entity('Task') -export class TaskEntity { - @PrimaryGeneratedColumn('uuid') taskId: string; - @Column({ type: 'text' }) taskName: string; - @Column({ type: 'text' }) taskDescription: string; - @Column({ type: 'text' }) taskWinner: string; - @Column({ type: 'bool' }) taskIsActive: boolean; - - @ManyToOne(type => FactionEntity, faction => faction.tasks) - faction: FactionEntity; - @ManyToOne(type => PersonEntity, person => person.tasks) - person: PersonEntity; -} + Entity, + Column, + PrimaryGeneratedColumn, + OneToMany, + ManyToOne, + Timestamp, + } from 'typeorm'; + import { GameEntity } from './game.entity'; + import { Game_PersonEntity } from './game.entity'; + import { MapDrawingEntity } from './coordinate.entity'; + + //Faction, PowerUp, Faction_powerUp, FP_History, Score, Task + + @Entity('Faction') + export class FactionEntity { + @PrimaryGeneratedColumn('uuid') factionId: string; + @Column({ type: 'text', unique: true }) factionName: string; + @Column({ type: 'text' }) factionPassword: string; + @Column({ type: 'float' }) multiplier: number; + + @OneToMany(type => MapDrawingEntity, mapDrawings => mapDrawings.faction) + mapDrawings: MapDrawingEntity[]; + @OneToMany(type => ScoreEntity, scores => scores.faction) + scores: ScoreEntity[]; + @OneToMany(type => PowerUpEntity, powerUps => powerUps.factions) + powerUps: Faction_PowerUpEntity[]; + @OneToMany(type => TaskEntity, tasks => tasks.faction) + tasks: TaskEntity[]; + @OneToMany(type => Game_PersonEntity, game_persons => game_persons.faction) + game_persons: Game_PersonEntity[]; + @ManyToOne(type => GameEntity, game => game.factions) + game: GameEntity; + } + + @Entity('PowerUp') + export class PowerUpEntity { + @PrimaryGeneratedColumn('uuid') powerUpId: string; + @Column({ type: 'text' }) powerUpName: string; + @Column({ type: 'text' }) powerUpDescription: string; + @Column({ type: 'int' }) amount: number; + @Column({ type: 'time' }) cooldown: string; + + @OneToMany(type => FactionEntity, factions => factions.powerUps) + factions: Faction_PowerUpEntity[]; + } + + @Entity('Faction_PowerUp') + export class Faction_PowerUpEntity { + @PrimaryGeneratedColumn('uuid') faction_powerUpId: string; + + @ManyToOne(type => FactionEntity, faction => faction.powerUps) + faction: FactionEntity; + @ManyToOne(type => PowerUpEntity, powerUp => powerUp.factions) + powerUp: PowerUpEntity; + @OneToMany(type => FP_HistoryEntity, histories => histories.faction_PowerUp) + histories: FP_HistoryEntity[]; + } + + @Entity('FP_History') + export class FP_HistoryEntity { + @PrimaryGeneratedColumn('uuid') historyId: string; + @Column({ type: 'timestamp' }) historyTimeStamp: Timestamp; + + @ManyToOne( + type => Faction_PowerUpEntity, + faction_PowerUp => faction_PowerUp.histories, + ) + faction_PowerUp: Faction_PowerUpEntity; + } + + @Entity('Score') + export class ScoreEntity { + @PrimaryGeneratedColumn('uuid') scoreId: string; + @Column({ type: 'float' }) score: number; + @Column({ type: 'timestamp' }) scoreTimeStamp: Timestamp; + + @ManyToOne(type => FactionEntity, factionName => factionName.scores) + faction: FactionEntity; + } + + @Entity('Task') + export class TaskEntity { + @PrimaryGeneratedColumn('uuid') taskId: string; + @Column({ type: 'text' }) taskName: string; + @Column({ type: 'text' }) taskDescription: string; + @Column({ type: 'text' }) taskWinner: string; + @Column({ type: 'bool' }) taskIsActive: boolean; + + @ManyToOne(type => FactionEntity, faction => faction.tasks) + faction: FactionEntity; +/* @ManyToOne(type => PersonEntity, person => person.tasks) + person: PersonEntity; */ + } + \ No newline at end of file diff --git a/src/game/game.controller.ts b/src/game/game.controller.ts index bba49166a663fa07bfa4bcb75fbd3239a401564f..9eb94eb6bf4ed09c6a359c72daf8cfdcb0634079 100644 --- a/src/game/game.controller.ts +++ b/src/game/game.controller.ts @@ -1,49 +1,81 @@ -import { Controller, Post, UseGuards, Body, Get, Param, UsePipes, Put, Delete } from '@nestjs/common'; +import { + Controller, + Post, + UseGuards, + Body, + Get, + Param, + UsePipes, + Put, +} from '@nestjs/common'; import { GameService } from './game.service'; import { AuthGuard } from '../shared/auth.guard'; import { User } from '../user/user.decorator'; -import { GameDTO } from './game.dto'; +import { GameDTO, GameGroupDTO } from './game.dto'; import { ValidationPipe } from '../shared/validation.pipe'; +import { Roles } from '../shared/roles.decorator'; @Controller('game') export class GameController { - constructor(private gameservice: GameService) { } + constructor(private gameservice: GameService) {} - @Post('new') - @UseGuards(new AuthGuard()) - @UsePipes(new ValidationPipe()) - async newGame(@User('id') person, @Body() body: GameDTO ) { - return this.gameservice.createNewGame(person, body); - } + @Post('new') + @UseGuards(new AuthGuard()) + @UsePipes(new ValidationPipe()) + async newGame(@User('id') person, @Body() body: GameDTO) { + return this.gameservice.createNewGame(person, body); + } - @Put(':id') - @UseGuards(new AuthGuard()) - @UsePipes(new ValidationPipe()) - async editGame(@Param('id') id: string, @Body() body: GameDTO) { - return this.gameservice.editGame(id, body); - } + @Put(':id') + @Roles('admin') + @UsePipes(new ValidationPipe()) + async editGame(@Param('id') id: string, @Body() body: GameDTO) { + return this.gameservice.editGame(id, body); + } - @Delete(':id') - @UseGuards(new AuthGuard()) - async deleteGame(@Param('id') id) { - return this.gameservice.deleteGame(id); - } + @Post(':id') + @UseGuards(new AuthGuard()) + @UsePipes(new ValidationPipe()) + async createGroup(@User('id') person, @Param('id') id: string, @Body() data: GameGroupDTO) { + try { + return this.gameservice.createGroup(person, id, data); + } catch (error) {} + } - @UseGuards(new AuthGuard()) - @UsePipes(new ValidationPipe()) - @Post(':id') - async joinGame(@User('id') person, @Param('id') id: string, @Body() password: string) { - return this.gameservice.joinGame(person, id, password); - } + @Get('get-groups') + async getGroups() { + return this.gameservice.showGroups(); + } - @Get('listgames') - async listGames() { - return this.gameservice.listGames(); - } + @Put('groups/:id') + @UseGuards(new AuthGuard()) + async joinGroup(@User('id') person, @Param('id') id) { + return this.gameservice.joinGroup(person, id) + } - @Get(':id') - async returnGameInfo(@Param('id') id: string) { - return this.gameservice.returnGameInfo(id); + @Put('joinfaction') + @UseGuards(new AuthGuard()) + joinFaction( + @User('id') person, + @Param('id') gameId, + @Param('faction') faction: string, + @Body() password: string, + ) { + try { + // return this.gameservice.joinFaction(person, gameId, faction, password); + } catch (error) { + return error; } + } + + @Get('listgames') + async listGames() { + return this.gameservice.listGames(); + } + + @Get(':id') + async returnGameInfo(@Param('id') id: string) { + return this.gameservice.returnGameInfo(id); + } } diff --git a/src/game/game.dto.ts b/src/game/game.dto.ts index 737a428ac63507bbe561724923e32db96b2a78d4..a5d43f5d624a7ff690d2a5e6fa6e2db4d45eaed1 100644 --- a/src/game/game.dto.ts +++ b/src/game/game.dto.ts @@ -22,14 +22,23 @@ export class GameDTO { @IsDate() @IsNotEmpty() - startDate: string; - - @IsDate() + @Length(1, 255) + desc: string; @IsNotEmpty() - endDate: string; - - @IsArray() + //@IsJSON() + center: JSON; + //@IsJSON() + // doesn't accept with IsJSON, WIP to get validation for map and center + // IsJSON checks with json.parse, expecting string + map?: JSON; + @IsDateString() @IsNotEmpty() + startdate: string; + @IsDateString() + @IsNotEmpty() + enddate: string; + // custom validation for array length (arr>min, arr<max) + //@Validate(ArrayLength, [4, 8]) factions?: FactionDTO[]; @IsArray() @@ -44,53 +53,15 @@ export class GameDTO { export class FactionDTO { @IsString() @IsNotEmpty() - @Length(3, 30) - factionName: string; - - @IsString() - @IsNotEmpty() - @Length(3, 255) - faction_Password?: string; -} - -/*export class PowerUpDTO { - @IsString() @IsNotEmpty() - powerUpName: string; - - @IsString() @IsNotEmpty() - powerUpDescription?: string; - - @IsInt() @IsNotEmpty() - amount: number; - - @IsNotEmpty() - cooldown: string; -}*/ - -export class ObjectivePointDTO { - @IsString() - @IsNotEmpty() - objectivePointDescription: string; - - @IsJSON() - @IsNotEmpty() - objectivePointCoordinates: JSON; - - @IsArray() - @IsNotEmpty() - objectivePointHistory: ObjectivePointHistoryDTO[]; + @Length(2, 15) + name: string; + id: string; + game: GameDTO; + password: string; } -export class ObjectivePointHistoryDTO { +export class GameGroupDTO { @IsString() - factionId: string; - - @IsNotEmpty() - @IsInt() - @Min(0) - @Max(2) - oP_HistoryStatus: number; - - @IsNotEmpty() - oP_HistoryTimestamp: Timestamp; + @Length(3, 31) + name: string; } diff --git a/src/game/game.entity.ts b/src/game/game.entity.ts index 9b499bb53fcb15dc7d8bc9019b347961066c0f9f..33b0a3acb8961a30dfad185fe42ca7903f2b2a47 100644 --- a/src/game/game.entity.ts +++ b/src/game/game.entity.ts @@ -5,16 +5,14 @@ import { ManyToOne, OneToMany, Timestamp, - PrimaryColumn, + OneToOne, + JoinColumn, } from 'typeorm'; -import { PersonEntity, PersonRoleEntity } from '../user/user.entity'; -import { FactionEntity } from '../game/faction.entity'; -import { - CoordinateEntity, - MapEntity, - MapDrawingEntity, - MapDrawingTypeEntity, -} from './coordinate.entity'; + +import { PersonEntity } from '../user/user.entity'; +import { GameGroupEntity } from './group.entity'; +import { FactionEntity } from './faction.entity'; +import { MapDrawingEntity } from './coordinate.entity'; // table that stores all created games @Entity('Game') @@ -23,22 +21,22 @@ export class GameEntity { @Column('text') name: string; @Column('text') desc: string; @Column('json') center: JSON; - @Column('json') map: JSON; + @Column({ type: 'json', nullable: true }) map: JSON; @Column('timestamp') startdate: Timestamp; @Column('timestamp') enddate: Timestamp; - - @ManyToOne(type => MapEntity, map => map.games) - gamemap: MapEntity; @OneToMany(type => FactionEntity, faction => faction.game) factions: FactionEntity[]; @OneToMany(type => Game_PersonEntity, game_persons => game_persons.game) game_persons: Game_PersonEntity[]; + @OneToMany(type => GameGroupEntity, group => group.game) + groups: GameGroupEntity[]; @OneToMany( type => ObjectivePointEntity, objective_points => objective_points.game, ) objective_points: ObjectivePointEntity[]; + gameObject() { const { id, name } = this; return { id, name }; @@ -48,20 +46,24 @@ export class GameEntity { // table that stores players associated with particular game @Entity('Game_Person') export class Game_PersonEntity { - @PrimaryGeneratedColumn('uuid') gameId: string; + @PrimaryGeneratedColumn('uuid') gamepersonId: string; + @Column({ type: 'text', nullable: true }) role: string; @ManyToOne(type => FactionEntity, faction => faction.game_persons) faction: FactionEntity; @ManyToOne(type => GameEntity, game => game.game_persons) game: GameEntity; @ManyToOne(type => PersonEntity, person => person.game_persons) person: PersonEntity; - @ManyToOne(type => PersonRoleEntity, person_role => person_role.game_persons) - person_role: PersonRoleEntity; - @ManyToOne( - type => CoordinateEntity, - game_person_coordinates => game_person_coordinates.game_persons, - ) - game_person_coordinates: CoordinateEntity; + @OneToOne(type => GameGroupEntity, group => group.leader, { + onDelete: 'CASCADE' + }) + @JoinColumn({name: 'leaderGroup'}) + leaderGroup: GameGroupEntity; + @ManyToOne(type => GameGroupEntity, group => group.players, { + onDelete: 'CASCADE' + }) + @JoinColumn({name: 'group'}) + group: GameGroupEntity; } @Entity('ObjectivePoint') @@ -70,11 +72,8 @@ export class ObjectivePointEntity { @Column({ type: 'text' }) objectivePointDescription: string; @Column({ type: 'float' }) objectivePointMultiplier: number; - @ManyToOne( - type => CoordinateEntity, - coordinate => coordinate.objective_points, - ) - coordinate: CoordinateEntity; + @ManyToOne(type => MapDrawingEntity, coordinate => coordinate.data) + coordinate: MapDrawingEntity; @ManyToOne(type => GameEntity, game => game.objective_points) game: GameEntity; @OneToMany( @@ -96,62 +95,3 @@ export class ObjectivePoint_HistoryEntity { ) objective_point: ObjectivePointEntity; } - -@Entity('PowerUp') -export class PowerUpEntity { - @PrimaryGeneratedColumn('uuid') powerUpId: string; - @Column({ type: 'text' }) powerUpName: string; - @Column({ type: 'text' }) powerUpDescription: string; - @Column({ type: 'int' }) amount: number; - @Column({ type: 'time' }) cooldown: string; - - @OneToMany(type => FactionEntity, factions => factions.powerUps) - factions: Faction_PowerUpEntity[]; -} - -@Entity('Faction_PowerUp') -export class Faction_PowerUpEntity { - @PrimaryGeneratedColumn('uuid') faction_powerUpId: string; - - @ManyToOne(type => FactionEntity, faction => faction.powerUps) - faction: FactionEntity; - @ManyToOne(type => PowerUpEntity, powerUp => powerUp.factions) - powerUp: PowerUpEntity; - @OneToMany(type => FP_HistoryEntity, histories => histories.faction_PowerUp) - histories: FP_HistoryEntity[]; -} - -@Entity('FP_History') -export class FP_HistoryEntity { - @PrimaryGeneratedColumn('uuid') historyId: string; - @Column({ type: 'timestamp' }) historyTimeStamp: Timestamp; - - @ManyToOne( - type => Faction_PowerUpEntity, - faction_PowerUp => faction_PowerUp.histories, - ) - faction_PowerUp: Faction_PowerUpEntity; -} -@Entity('Score') -export class ScoreEntity { - @PrimaryGeneratedColumn('uuid') scoreId: string; - @Column({ type: 'float' }) score: number; - @Column({ type: 'timestamp' }) scoreTimeStamp: Timestamp; - - @ManyToOne(type => FactionEntity, factionName => factionName.scores) - faction: FactionEntity; -} - -@Entity('Task') -export class TaskEntity { - @PrimaryGeneratedColumn('uuid') taskId: string; - @Column({ type: 'text' }) taskName: string; - @Column({ type: 'text' }) taskDescription: string; - @Column({ type: 'text' }) taskWinner: string; - @Column({ type: 'bool' }) taskIsActive: boolean; - - @ManyToOne(type => FactionEntity, faction => faction.tasks) - faction: FactionEntity; - @ManyToOne(type => PersonEntity, person => person.tasks) - person: PersonEntity; -} diff --git a/src/game/game.module.ts b/src/game/game.module.ts index f51261d980a9eee78ddacd910c2d5f0f7e2e49c8..7df189f348bf12543437ecdd85d892980115c7ae 100644 --- a/src/game/game.module.ts +++ b/src/game/game.module.ts @@ -4,18 +4,12 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { GameController } from './game.controller'; import { GameService } from './game.service'; import { GameEntity, Game_PersonEntity } from './game.entity'; -import { FactionEntity } from './faction.entity'; import { PersonEntity } from '../user/user.entity'; +import { GameGroupEntity } from './group.entity'; +import { FactionEntity } from './faction.entity'; @Module({ - imports: [ - TypeOrmModule.forFeature([ - GameEntity, - FactionEntity, - Game_PersonEntity, - PersonEntity, - ]), - ], + imports: [TypeOrmModule.forFeature([GameEntity, FactionEntity, Game_PersonEntity, PersonEntity, GameGroupEntity])], controllers: [GameController], providers: [GameService], }) diff --git a/src/game/game.service.ts b/src/game/game.service.ts index 58419ebc611c9afc8ac65d3f651dd83865043bc5..8dbc824e5d91b2ec9d9e1da54cbdd90a53e1ea96 100644 --- a/src/game/game.service.ts +++ b/src/game/game.service.ts @@ -1,11 +1,12 @@ -import { Injectable, Logger, HttpException, HttpStatus } from '@nestjs/common'; +import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, In } from 'typeorm'; +import { Repository, Not } from 'typeorm'; import { GameEntity, Game_PersonEntity } from './game.entity'; -import { FactionEntity } from './faction.entity'; import { GameDTO } from './game.dto'; import { PersonEntity } from '../user/user.entity'; +import { GameGroupEntity } from './group.entity'; +import { FactionEntity } from './faction.entity'; @Injectable() export class GameService { @@ -18,51 +19,77 @@ export class GameService { private personRepository: Repository<PersonEntity>, @InjectRepository(Game_PersonEntity) private game_PersonRepository: Repository<Game_PersonEntity>, + @InjectRepository(GameGroupEntity) + private game_GroupRepository: Repository<GameGroupEntity>, ) {} // create a new game - async createNewGame(personId: string, gameData: GameDTO) { + async createNewGame(personId: PersonEntity, gameData: GameDTO) { + try { + // checks if a game with the same name exists already + const { name } = gameData; + if (await this.gameRepository.findOne({ where: { name } })) { + throw new HttpException('Game already exists', HttpStatus.BAD_REQUEST); + } + // else add the game to the database + const game = await this.gameRepository.create({ + ...gameData, + factions: gameData.factions, + }); + await this.gameRepository.insert(game); + const gamePerson = await this.game_PersonRepository.create({ + faction: null, + game: game, + person: personId, + }); + gamePerson['role'] = 'admin'; + await this.game_PersonRepository.insert(gamePerson); + return { + message: 'New game added', + }; + } catch (error) { + return error.message; + } + } + + // edit already created game + async editGame(id: string, gameData: GameDTO) { // checks if a game with the same name exists already const { name } = gameData; - if (await this.gameRepository.findOne({ where: { name } })) { + if (await this.gameRepository.findOne({ name: name, id: Not(id) })) { throw new HttpException('Game already exists', HttpStatus.BAD_REQUEST); } - // else add the game to the database - const game = await this.gameRepository.create({ + // update game entry in db + const updatedGame = await this.gameRepository.create({ ...gameData, factions: gameData.factions, }); - await this.gameRepository.insert(game); - return { - message: 'New game added', - }; - } + updatedGame['id'] = id; + const gameId = await this.gameRepository.save(updatedGame); - // edit already created game - async editGame(id: string, gameData: Partial<GameDTO>) { - try { - // update game entry in db - let update = await this.gameRepository.create({ - ...gameData, - factions: gameData.factions, - }); - await this.gameRepository.update({ id }, update); - // add the factions to db - if (gameData.factions) { - gameData.factions.map(async faction => { + // get all the factions that are associated with the game to deny duplicate entries + const factions = await this.factionRepository.find({ + select: ['factionName'], + where: { game: gameId }, + }); + const factionNames = factions.map(({ factionName }) => factionName); + + // add the factions to db + if (gameData.factions) { + gameData.factions.map(async faction => { + if (!Object.values(factionNames).includes(faction.name)) { let name = await this.factionRepository.create({ ...faction, - game: gameData, + game: gameId, }); await this.factionRepository.insert(name); - }); - } - return { - message: 'Game updated', - }; - } catch (error) { - console.log(error); + } + }); } + + return { + message: 'Game updated', + }; } async deleteGame(id) { @@ -74,22 +101,76 @@ export class GameService { } // checks the password, creates an entry in GamePerson table with associated role&faction - async joinGame(person, gameId, json) { - const user = await this.personRepository.findOne({ - where: { id: person }, - }); - const game = await this.gameRepository.findOne({ where: { id: gameId } }); + async createGroup(person, gameId, groupData) { + try { + // check if the person already is in a group in this game + const checkDuplicate = await this.game_PersonRepository.findOne({ + person: person, + }); + if (checkDuplicate) { + throw new HttpException( + 'You already belong to a group!', + HttpStatus.BAD_REQUEST, + ); + } - //const index = game.passwords.indexOf(json.password); + // create a group entry and insert it to db + const group = await this.game_GroupRepository.create({ + ...groupData, + game: gameId, + }); + const gameGroup = await this.game_GroupRepository.insert(group); - // create game_Person entry - /* const gamePerson = await this.game_PersonRepository.create({ - faction, - gameId, - person, - }); - */ - return 'WIP'; + // create game_Person entry and insert it to db + const gamePerson = await this.game_PersonRepository.create({ + role: 'soldier', + faction: null, + game: gameId, + person: person, + leaderGroup: gameGroup.identifiers[0]['id'], + group: gameGroup.identifiers[0]['id'], + }); + await this.game_PersonRepository.insert(gamePerson); + + return { + message: 'created new group', + }; + } catch (e) { + return e; + } + } + + async showGroups() { + try { + return await this.game_GroupRepository.find({ + relations: ['leader', 'players', 'game'], + }); + } catch (e) { + return e; + } + } + + async joinGroup(person, groupId) { + try { + const gameData = await this.game_GroupRepository.findOne({ + where: { id: groupId }, + relations: ['players', 'game'], + }); + const gamePerson = await this.game_PersonRepository.create({ + role: 'soldier', + faction: null, + game: gameData.game, + person: person, + leaderGroup: null, + group: groupId, + }); + await this.game_PersonRepository.insert(gamePerson); + return { + message: 'Joined group', + }; + } catch (e) { + return e; + } } // returns name and id of each game diff --git a/src/game/group.entity.ts b/src/game/group.entity.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b9f00ed621b41b8515a77126f9564c14a02d74b --- /dev/null +++ b/src/game/group.entity.ts @@ -0,0 +1,28 @@ +import { + Entity, + Column, + PrimaryGeneratedColumn, + ManyToOne, + OneToMany, + OneToOne, + JoinColumn, + } from 'typeorm'; + +import { Game_PersonEntity, GameEntity } from './game.entity'; + +@Entity('GameGroup') +export class GameGroupEntity { + @PrimaryGeneratedColumn('uuid') id: string; + @Column('text') name: string; + @OneToOne(type => Game_PersonEntity, person => person.leaderGroup, { + onDelete: 'CASCADE' + }) + //@JoinColumn({name:'leader'}) + leader: Game_PersonEntity; + @OneToMany(type => Game_PersonEntity, person => person.group, { + onDelete: 'CASCADE' + }) + players: Game_PersonEntity[]; + @ManyToOne(type => GameEntity, game => game.groups) + game: GameEntity; +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 3c7cc4607bac085ff529775a441ef81e4e089a6a..1e3e4e147bc386a4ce15a36bf274face5fd14c1e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,11 @@ import * as rateLimit from 'express-rate-limit'; import { AppModule } from './app.module'; + +/* + Main.ts starts the server. +*/ + // due to a bug with newest release of express-rate-limit, call for rateLimit is broken // (rateLimit as any) works as a workaround for now // see https://github.com/nfriedly/express-rate-limit/issues/138 diff --git a/src/mapmarkers/mapmarker.entity.ts b/src/mapmarkers/mapmarker.entity.ts index bc6d33112149f23e52e493c374be19ca0f18e536..b83b1d9ad56c6de51563aa0e1a5fe024e5cf9665 100644 --- a/src/mapmarkers/mapmarker.entity.ts +++ b/src/mapmarkers/mapmarker.entity.ts @@ -1,19 +1,26 @@ -import { Entity, Column, PrimaryGeneratedColumn, Timestamp, ManyToOne } from 'typeorm'; - -import { PersonEntity } from '../user/user.entity' - -/* -Entity: MapMarker -- represents data that database contains on mapmarker -*/ - -@Entity('MapMarker') -export class MapMarkerEntity { +import { + Entity, + Column, + PrimaryGeneratedColumn, + Timestamp, + ManyToOne, + } from 'typeorm'; + + import { PersonEntity } from '../user/user.entity'; + + /* + Entity: MapMarker + - represents data that database contains on mapmarker + */ + + @Entity('MapMarker') + export class MapMarkerEntity { @PrimaryGeneratedColumn('uuid') id: string; - @Column({type: 'text'}) latitude: string; - @Column({type: 'text'}) longitude: string; - @Column({type: 'timestamp'}) timestamp: Timestamp; - @Column({type: 'json', nullable: true}) features: JSON; + @Column({ type: 'text', nullable: true }) latitude: string; + @Column({ type: 'text', nullable: true }) longitude: string; + @Column({ type: 'timestamp' }) timestamp: Timestamp; + @Column({ type: 'json', nullable: true }) features: JSON; @ManyToOne(type => PersonEntity, player => player.markers) player: PersonEntity; -} \ No newline at end of file + } + \ No newline at end of file diff --git a/src/mapmarkers/mapmarker.service.ts b/src/mapmarkers/mapmarker.service.ts index 9e0167075f46f6d68a34afa0f5bc421e21699d7e..1a948694810e6ba42b961bde7bd73de02ba24fb6 100644 --- a/src/mapmarkers/mapmarker.service.ts +++ b/src/mapmarkers/mapmarker.service.ts @@ -1,6 +1,6 @@ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; -import { Repository, In } from "typeorm"; -import { InjectRepository } from "@nestjs/typeorm"; +import { Repository, In } from 'typeorm'; +import { InjectRepository } from '@nestjs/typeorm'; import { MapMarkerEntity } from './mapmarker.entity'; import { MapMarkerDTO } from './mapmarker.dto'; @@ -9,41 +9,50 @@ import { userInfo } from 'os'; @Injectable() export class MapMarkerService { - constructor( - //create references to tables as repositories - @InjectRepository(MapMarkerEntity) private mapmarkerRepository: Repository<MapMarkerEntity>, - @InjectRepository(PersonEntity) private personRepository: Repository<PersonEntity> - ) { } + constructor( + //create references to tables as repositories + @InjectRepository(MapMarkerEntity) + private mapmarkerRepository: Repository<MapMarkerEntity>, + @InjectRepository(PersonEntity) + private personRepository: Repository<PersonEntity>, + ) {} - // insert markers - async insertLocation(personId: string, data: MapMarkerDTO) { - try { - - //get functions runtime as timestamp - data.timestamp = new Date(Date.now()).toLocaleString(); - //check from database for the user who uploads the data - const user = await this.personRepository.findOne({ where: { id: personId } }) - //create© entity properties - const location = await this.mapmarkerRepository.create({ ...data, player: user }); - // insert created entity NOTE: insert method doesn't check for duplicates. - await this.mapmarkerRepository.insert(location); - // return data and player id&name - return { ...data, player: location.player.nameObject() }; - } catch (error) { - return error; - } + // insert marker + async insertLocation(personId: string, data: MapMarkerDTO) { + try { + //get functions runtime as timestamp + data.timestamp = new Date(Date.now()).toLocaleString(); + //check from database for the user who uploads the data + const user = await this.personRepository.findOne({ + where: { id: personId }, + }); + //create© entity properties + const location = await this.mapmarkerRepository.create({ + ...data, + player: user, + }); + // insert created entity NOTE: insert method doesn't check for duplicates. + await this.mapmarkerRepository.insert(location); + // return data and player id&name + return { ...data, player: location.player.nameObject() }; + } catch (error) { + return error; } + } - // get all markers - async getAllMarkers() { - try { - // find all markers with specified player - const markers = await this.mapmarkerRepository.find({ relations: ['player'] }); - // return markers from database with said playerdata - return markers.map(marker => { return { ...marker, player: marker.player.nameObject() } }); - } catch (error) { - return error.message; - } - + // get all markers + async getAllMarkers() { + try { + // find all markers with specified player + const markers = await this.mapmarkerRepository.find({ + relations: ['player'], + }); + // return markers from database with said playerdata + return markers.map(marker => { + return { ...marker, player: marker.player.nameObject() }; + }); + } catch (error) { + return error.message; } -} \ No newline at end of file + } +} diff --git a/src/mapmarkers/mapmarkers.controller.ts b/src/mapmarkers/mapmarkers.controller.ts index 58c4035753abd25239ba44623fb227d628c3c4d9..fef971652de596196169251c202378c67902babd 100644 --- a/src/mapmarkers/mapmarkers.controller.ts +++ b/src/mapmarkers/mapmarkers.controller.ts @@ -23,6 +23,10 @@ export class MapMarkersController { // return all markers through service @Get('getall') async getAll(){ - return this.mapmarkerservice.getAllMarkers(); + try{ + return this.mapmarkerservice.getAllMarkers(); + }catch(error){ + return error.message; + } } } diff --git a/src/shared/auth.guard.ts b/src/shared/auth.guard.ts index 78d66f86757f1f5803338440839afeb3610ed1d7..ff2bdf406d5badffc467cdf22d0f61935aae0af8 100644 --- a/src/shared/auth.guard.ts +++ b/src/shared/auth.guard.ts @@ -1,30 +1,43 @@ -import { Injectable, ExecutionContext, CanActivate, HttpException, HttpStatus } from '@nestjs/common'; +import { + Injectable, + ExecutionContext, + CanActivate, + HttpException, + HttpStatus, +} from '@nestjs/common'; import * as jwt from 'jsonwebtoken'; @Injectable() export class AuthGuard implements CanActivate { + // check for logged in user + async canActivate(context: ExecutionContext): Promise<boolean> { + // get request + const request = context.switchToHttp().getRequest(); + // check for authorization header + if (!request.headers.authorization) { + return false; + } - async canActivate(context: ExecutionContext): Promise<boolean> { - const request = context.switchToHttp().getRequest(); - if (!request.headers.authorization) { - return false; - } + // validate token + request.user = await this.validateToken(request.headers.authorization); - request.user = await this.validateToken(request.headers.authorization); + return true; + } - return true; + // validate token + async validateToken(auth: string) { + // check if header contains Bearer + if (auth.split(' ')[0] !== 'Bearer') { + throw new HttpException('Invalid token', HttpStatus.FORBIDDEN); } - - async validateToken(auth: string) { - if (auth.split(" ")[0] !== 'Bearer') { - throw new HttpException('Invalid token', HttpStatus.FORBIDDEN); - } - const token = auth.split(" ")[1]; - try { - return await jwt.verify(token, process.env.SECRET); - } catch (err) { - const message = `Token error: ${err.message || err.name}` - throw new HttpException(message, HttpStatus.FORBIDDEN); - } + // get the token + const token = auth.split(' ')[1]; + try { + // return token. + return await jwt.verify(token, process.env.SECRET); + } catch (err) { + const message = `Token error: ${err.message || err.name}`; + throw new HttpException(message, HttpStatus.FORBIDDEN); } -} \ No newline at end of file + } +} diff --git a/src/shared/roles.decorator.ts b/src/shared/roles.decorator.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d14223c3ce5ee1002cf245fa16fd1b57749bae1 --- /dev/null +++ b/src/shared/roles.decorator.ts @@ -0,0 +1,3 @@ +import { SetMetadata } from '@nestjs/common'; + +export const Roles = (...roles: string[]) => SetMetadata('roles', roles); \ No newline at end of file diff --git a/src/shared/roles.guard.ts b/src/shared/roles.guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f05fc90d4c008277e96d543f8112b4581a91c47 --- /dev/null +++ b/src/shared/roles.guard.ts @@ -0,0 +1,51 @@ +import { + Injectable, + CanActivate, + ExecutionContext, + HttpException, + HttpStatus, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import * as jwt from 'jsonwebtoken'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { Game_PersonEntity } from '../game/game.entity'; + +@Injectable() +export class RolesGuard implements CanActivate { + constructor( + private readonly reflector: Reflector, + @InjectRepository(Game_PersonEntity) + private game_PersonRepository: Repository<Game_PersonEntity>, + ) {} + + async canActivate(context: ExecutionContext): Promise<boolean> { + // get roles that are allowed access, identified by @Roles('role') decorators in controllers + const roles = this.reflector.get<string[]>('roles', context.getHandler()); + if (!roles) { + return true; + } + const request = context.switchToHttp().getRequest(); + const gameId = request.params.id + const user = await this.getUserObject(request.headers.authorization); + const role = await this.game_PersonRepository.findOne({person: user['id'], game: gameId}) + // check that the role matches the criteria and that token is valid for this game + return role && roles.includes(role['role']); + } + + async getUserObject(auth: string) { + // check if header contains Bearer + if (auth.split(' ')[0] !== 'Bearer') { + throw new HttpException('Invalid token', HttpStatus.FORBIDDEN); + } + // get the token + const token = auth.split(' ')[1]; + try { + return await jwt.verify(token, process.env.SECRET) + } catch (err) { + const message = `Token error: ${err.message || err.name}`; + throw new HttpException(message, HttpStatus.FORBIDDEN); + } + } +} diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index 2910ed9c445715f94c698935428bf2c85ef6d717..4d4158d3f037850c8c5230975ef5f46ae5a75ede 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -1,4 +1,11 @@ -import { Controller, Post, Body, UsePipes, Get, UseGuards } from '@nestjs/common'; +import { + Controller, + Post, + Body, + UsePipes, + Get, + UseGuards, +} from '@nestjs/common'; import { UserService } from './user.service'; import { UserDTO } from './user.dto'; @@ -7,24 +14,24 @@ import { ValidationPipe } from '../shared/validation.pipe'; @Controller('user') export class UserController { - constructor(private userService: UserService) { } + constructor(private userService: UserService) {} - @Post('register') - @UsePipes(new ValidationPipe()) - createUser(@Body() data: UserDTO) { - return this.userService.register(data); - } + @Post('register') + @UsePipes(new ValidationPipe()) + createUser(@Body() data: UserDTO) { + return this.userService.register(data); + } - @Post('login') - @UsePipes(new ValidationPipe()) - loginUser(@Body() data: UserDTO) { - return this.userService.login(data); - } + @Post('login') + @UsePipes(new ValidationPipe()) + loginUser(@Body() data: UserDTO) { + return this.userService.login(data); + } - // verifies the token - @Get('verify') - @UseGuards(new AuthGuard()) - showMap() { - return true; - } + // verifies the token + @Get('verify') + @UseGuards(new AuthGuard()) + showMap() { + return true; + } } diff --git a/src/user/user.entity.ts b/src/user/user.entity.ts index a1a33a09e6dad6fad6979a1ae7cf9ed270e0453f..6323836fdcaf3fbd91a3be97268e9a659e142b41 100644 --- a/src/user/user.entity.ts +++ b/src/user/user.entity.ts @@ -1,4 +1,10 @@ -import { Entity, Column, PrimaryGeneratedColumn, BeforeInsert, OneToMany } from 'typeorm'; +import { + Entity, + Column, + PrimaryGeneratedColumn, + BeforeInsert, + OneToMany, +} from 'typeorm'; import * as bcrypt from 'bcryptjs'; import * as jwt from 'jsonwebtoken'; import { MapMarkerEntity } from 'src/mapmarkers/mapmarker.entity'; @@ -7,54 +13,45 @@ import {Game_PersonEntity} from '../game/game.entity' @Entity('Person') export class PersonEntity { - @PrimaryGeneratedColumn('uuid') id: string; - @Column({type: 'text', unique: true}) name: string; - @Column('text') password: string; - @OneToMany(type => MapMarkerEntity, marker => marker.player) - markers: MapMarkerEntity[]; - @OneToMany(type => TaskEntity, task => task.person) - tasks: TaskEntity[]; - @OneToMany(type => Game_PersonEntity, game_persons => game_persons.person) - game_persons: Game_PersonEntity[]; - - // hashes the password before inserting it to database - @BeforeInsert() - async hashPassword() { - this.password = await bcrypt.hash(this.password, 10); - } - - // returns username and associated token - tokenObject() { - const {name, token} = this; - return {name, token}; - } - - // returns username and the id - nameObject() { - const {id, name} = this; - return {id, name}; - } - - async comparePassword(attempt: string) { - return await bcrypt.compareSync(attempt, this.password); - } - - private get token() { - const {id, name} = this; - return jwt.sign({ - id, name - }, - process.env.SECRET, - { expiresIn: '7d'}, - ); - } + @PrimaryGeneratedColumn('uuid') id: string; + @Column({ type: 'text', unique: true }) name: string; + @Column('text') password: string; + @OneToMany(type => MapMarkerEntity, marker => marker.player) + markers: MapMarkerEntity[]; + @OneToMany(type => Game_PersonEntity, game_persons => game_persons.person) + game_persons: Game_PersonEntity[]; + + // hashes the password before inserting it to database + @BeforeInsert() + async hashPassword() { + this.password = await bcrypt.hash(this.password, 10); + } + + // returns username and associated token + tokenObject() { + const { name, token } = this; + return { name, token }; + } + + // returns username and the id + nameObject() { + const { id, name } = this; + return { id, name }; + } + + async comparePassword(attempt: string) { + return await bcrypt.compareSync(attempt, this.password); + } + + private get token() { + const { id, name } = this; + return jwt.sign( + { + id, + name, + }, + process.env.SECRET, + { expiresIn: '7d' }, + ); + } } - -@Entity('PersonRole') -export class PersonRoleEntity { - @PrimaryGeneratedColumn('uuid') person_roleId: string; - @Column({type: 'text'}) person_roleDescription: string; - - @OneToMany(type => Game_PersonEntity, game_persons => game_persons.person_role) - game_persons: Game_PersonEntity[]; -} \ No newline at end of file diff --git a/src/user/user.module.ts b/src/user/user.module.ts index cd43a16b6f27fc11b6c277617f859889021b0c5a..6cb77a7b735df532d710febc33a4df53f264563a 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -3,11 +3,12 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { UserController } from './user.controller'; import { UserService } from './user.service'; -import { PersonEntity } from './user.entity'; +import { PersonEntity} from './user.entity'; +import { GameEntity } from '../game/game.entity'; @Module({ - imports: [TypeOrmModule.forFeature([PersonEntity])], + imports: [TypeOrmModule.forFeature([PersonEntity, GameEntity])], controllers: [UserController], - providers: [UserService] + providers: [UserService], }) export class UserModule {} diff --git a/src/user/user.service.ts b/src/user/user.service.ts index 0b42bf9689f1cf7a697baa5c0093e02105794e6a..8d0a64d8ea0430d1633e67a17ee3fb782d21ce72 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -4,10 +4,17 @@ import { InjectRepository } from "@nestjs/typeorm"; import { PersonEntity } from './user.entity'; import { UserDTO } from './user.dto'; +import { GameEntity } from '../game/game.entity'; +import { GameDTO } from '../game/game.dto'; @Injectable() export class UserService { - constructor(@InjectRepository(PersonEntity) private userRepository: Repository<PersonEntity>){} + constructor( + @InjectRepository(PersonEntity) + private userRepository: Repository<PersonEntity>, + @InjectRepository(GameEntity) + private gameRepository: Repository<GameEntity> + ){} async register(data: UserDTO) { const { name } = data; @@ -21,14 +28,40 @@ export class UserService { } async login(data: UserDTO) { - const { name, password } = data; - const user = await this.userRepository.findOne({ where: { name }}); - if (!user || !(await user.comparePassword(password))) { - throw new HttpException( - 'Invalid username/password', - HttpStatus.BAD_REQUEST, - ); + try{ + const { name, password } = data; + const user = await this.userRepository.findOne({ where: { name }}); + if (!user || !(await user.comparePassword(password))) { + throw new HttpException( + 'Invalid username/password', + HttpStatus.BAD_REQUEST, + ); + } + return user.tokenObject(); + }catch(error){ + return error.message; + } + } + + // liitytään peliin + async joinGame(game: GameDTO, data: UserDTO){ + try { + // etsi peli valinnan mukaan ja otetaan käyttäjän rooli kyseisestä pelistä talteen + const role = await this.gameRepository.findOne(); + return role; + } catch (error) { + return error.message; } - return user.tokenObject(); } + + // liitytään factionii + async joinFaction(game: GameDTO, data: UserDTO): Promise<boolean>{ + try { + // tarkistetaan factionin salasana + return true; + } catch (error) { + return error; + } + } + }