Ajout endpoint géolocalisation
This commit is contained in:
parent
1ae6b6634c
commit
86427bb41b
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `accuracy` to the `Geolocation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `altitude` to the `Geolocation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `altitudeAccuracy` to the `Geolocation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `speed` to the `Geolocation` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Geolocation" ADD COLUMN "accuracy" DOUBLE PRECISION NOT NULL,
|
||||||
|
ADD COLUMN "altitude" DOUBLE PRECISION NOT NULL,
|
||||||
|
ADD COLUMN "altitudeAccuracy" DOUBLE PRECISION NOT NULL,
|
||||||
|
ADD COLUMN "speed" DOUBLE PRECISION NOT NULL;
|
@ -21,11 +21,15 @@ model User {
|
|||||||
|
|
||||||
model Geolocation {
|
model Geolocation {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
userId Int
|
userId Int
|
||||||
longitude Float
|
longitude Float
|
||||||
latitude Float
|
latitude Float
|
||||||
|
speed Float
|
||||||
|
accuracy Float
|
||||||
|
altitude Float
|
||||||
|
altitudeAccuracy Float
|
||||||
timestamp DateTime
|
timestamp DateTime
|
||||||
user User @relation(fields: [userId], references: [id])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model Challenge {
|
model Challenge {
|
||||||
@ -38,17 +42,18 @@ model Challenge {
|
|||||||
|
|
||||||
model ChallengeAction {
|
model ChallengeAction {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
userId Int
|
userId Int
|
||||||
|
challenge Challenge @relation(fields: [challengeId], references: [id])
|
||||||
challengeId Int @unique
|
challengeId Int @unique
|
||||||
active Boolean @default(false)
|
active Boolean @default(false)
|
||||||
success Boolean @default(false)
|
success Boolean @default(false)
|
||||||
challenge Challenge @relation(fields: [challengeId], references: [id])
|
|
||||||
user User @relation(fields: [userId], references: [id])
|
|
||||||
moneyUpdate MoneyUpdate?
|
moneyUpdate MoneyUpdate?
|
||||||
}
|
}
|
||||||
|
|
||||||
model TrainTrip {
|
model TrainTrip {
|
||||||
id String @id
|
id String @id
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
userId Int
|
userId Int
|
||||||
distance Float
|
distance Float
|
||||||
from String
|
from String
|
||||||
@ -58,20 +63,19 @@ model TrainTrip {
|
|||||||
infoJson Json
|
infoJson Json
|
||||||
geometry String
|
geometry String
|
||||||
moneyUpdate MoneyUpdate?
|
moneyUpdate MoneyUpdate?
|
||||||
user User @relation(fields: [userId], references: [id])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model MoneyUpdate {
|
model MoneyUpdate {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
userId Int
|
userId Int
|
||||||
before Int
|
before Int
|
||||||
after Int
|
after Int
|
||||||
reason MoneyUpdateType
|
reason MoneyUpdateType
|
||||||
actionId Int? @unique
|
|
||||||
tripId String? @unique
|
|
||||||
action ChallengeAction? @relation(fields: [actionId], references: [id])
|
action ChallengeAction? @relation(fields: [actionId], references: [id])
|
||||||
|
actionId Int? @unique
|
||||||
trip TrainTrip? @relation(fields: [tripId], references: [id])
|
trip TrainTrip? @relation(fields: [tripId], references: [id])
|
||||||
user User @relation(fields: [userId], references: [id])
|
tripId String? @unique
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MoneyUpdateType {
|
enum MoneyUpdateType {
|
||||||
|
@ -3,9 +3,10 @@ import { PrismaService } from './prisma/prisma.service'
|
|||||||
import { PrismaModule } from './prisma/prisma.module'
|
import { PrismaModule } from './prisma/prisma.module'
|
||||||
import { UsersModule } from './users/users.module'
|
import { UsersModule } from './users/users.module'
|
||||||
import { AuthModule } from './auth/auth.module'
|
import { AuthModule } from './auth/auth.module'
|
||||||
|
import { GeolocationsModule } from './geolocations/geolocations.module'
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule, UsersModule, AuthModule],
|
imports: [PrismaModule, UsersModule, AuthModule, GeolocationsModule],
|
||||||
providers: [PrismaService],
|
providers: [PrismaService],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { Injectable } from '@nestjs/common'
|
import { Injectable } from '@nestjs/common'
|
||||||
import { AuthGuard } from '@nestjs/passport'
|
import { AuthGuard } from '@nestjs/passport'
|
||||||
|
import { User } from '@prisma/client'
|
||||||
|
import { Request } from 'express'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class JwtAuthGuard extends AuthGuard('jwt') {}
|
export class JwtAuthGuard extends AuthGuard('jwt') {}
|
||||||
|
|
||||||
|
export type AuthenticatedRequest = Request & { user: User }
|
||||||
|
24
server/src/geolocations/dto/create-geolocation.dto.ts
Normal file
24
server/src/geolocations/dto/create-geolocation.dto.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger"
|
||||||
|
|
||||||
|
export class CreateGeolocationDto {
|
||||||
|
@ApiProperty({description: "Longitude en degrés"})
|
||||||
|
longitude: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Latitude en degrés"})
|
||||||
|
latitude: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Vitesse en mètres par seconde"})
|
||||||
|
speed: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Précision en mètres de la position obtenue"})
|
||||||
|
accuracy: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Altitude en mètres"})
|
||||||
|
altitude: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Précision de l'altitude en mètres"})
|
||||||
|
altitudeAccuracy: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Date et heure de capture de la géolocalisation"})
|
||||||
|
timestamp: Date
|
||||||
|
}
|
35
server/src/geolocations/entities/geolocation.entity.ts
Normal file
35
server/src/geolocations/entities/geolocation.entity.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger"
|
||||||
|
import { Geolocation } from "@prisma/client"
|
||||||
|
|
||||||
|
export class GeolocationEntity implements Geolocation {
|
||||||
|
constructor(partial: Partial<GeolocationEntity>) {
|
||||||
|
Object.assign(this, partial)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiProperty({description: "Identifiant unique"})
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Utilisateur⋅rice ayant émis la géolocalisation"})
|
||||||
|
userId: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Longitude en degrés"})
|
||||||
|
longitude: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Latitude en degrés"})
|
||||||
|
latitude: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Vitesse en mètres par seconde"})
|
||||||
|
speed: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Précision en mètres de la position obtenue"})
|
||||||
|
accuracy: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Altitude en mètres"})
|
||||||
|
altitude: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Précision de l'altitude en mètres"})
|
||||||
|
altitudeAccuracy: number
|
||||||
|
|
||||||
|
@ApiProperty({description: "Date et heure de capture de la géolocalisation"})
|
||||||
|
timestamp: Date
|
||||||
|
}
|
20
server/src/geolocations/geolocations.controller.spec.ts
Normal file
20
server/src/geolocations/geolocations.controller.spec.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing'
|
||||||
|
import { GeolocationsController } from './geolocations.controller'
|
||||||
|
import { GeolocationsService } from './geolocations.service'
|
||||||
|
|
||||||
|
describe('GeolocationsController', () => {
|
||||||
|
let controller: GeolocationsController
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [GeolocationsController],
|
||||||
|
providers: [GeolocationsService],
|
||||||
|
}).compile()
|
||||||
|
|
||||||
|
controller = module.get<GeolocationsController>(GeolocationsController)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
63
server/src/geolocations/geolocations.controller.ts
Normal file
63
server/src/geolocations/geolocations.controller.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { Controller, Get, Post, Body, Patch, Param, Delete, ParseIntPipe, UseGuards, HttpCode, Req, NotFoundException } from '@nestjs/common'
|
||||||
|
import { GeolocationsService } from './geolocations.service'
|
||||||
|
import { CreateGeolocationDto } from './dto/create-geolocation.dto'
|
||||||
|
import { AuthenticatedRequest, JwtAuthGuard } from 'src/auth/jwt-auth.guard'
|
||||||
|
import { ApiBearerAuth, ApiCreatedResponse, ApiForbiddenResponse, ApiNoContentResponse, ApiNotFoundResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger'
|
||||||
|
import { GeolocationEntity } from './entities/geolocation.entity'
|
||||||
|
|
||||||
|
@Controller('geolocations')
|
||||||
|
export class GeolocationsController {
|
||||||
|
constructor(private readonly geolocationsService: GeolocationsService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@HttpCode(201)
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiCreatedResponse({ type: GeolocationEntity, description: "Objet créé avec succès" })
|
||||||
|
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
|
||||||
|
@ApiForbiddenResponse({ description: "Permission refusée" })
|
||||||
|
@ApiNotFoundResponse({ description: "Object non trouvé" })
|
||||||
|
async create(@Req() request: AuthenticatedRequest, @Body() createGeolocationDto: CreateGeolocationDto): Promise<GeolocationEntity> {
|
||||||
|
const user = request.user
|
||||||
|
const geolocation = await this.geolocationsService.create(user, createGeolocationDto)
|
||||||
|
return new GeolocationEntity(geolocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({ type: GeolocationEntity, isArray: true })
|
||||||
|
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
|
||||||
|
@ApiForbiddenResponse({ description: "Permission refusée" })
|
||||||
|
@ApiNotFoundResponse({ description: "Objet non trouvé" })
|
||||||
|
async findAll(): Promise<GeolocationEntity[]> {
|
||||||
|
const geolocations = await this.geolocationsService.findAll()
|
||||||
|
return geolocations.map(geolocation => new GeolocationEntity(geolocation))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiOkResponse({ type: GeolocationEntity })
|
||||||
|
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
|
||||||
|
@ApiForbiddenResponse({ description: "Permission refusée" })
|
||||||
|
@ApiNotFoundResponse({ description: "Objet non trouvé" })
|
||||||
|
async findOne(@Param('id', ParseIntPipe) id: number): Promise<GeolocationEntity> {
|
||||||
|
const geolocation = await this.geolocationsService.findOne(+id)
|
||||||
|
if (!geolocation)
|
||||||
|
throw new NotFoundException(`Géolocalisation inexistante avec l'identifiant ${id}`)
|
||||||
|
return new GeolocationEntity(geolocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
@HttpCode(204)
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiNoContentResponse({ description: "Objet supprimé avec succès" })
|
||||||
|
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
|
||||||
|
@ApiForbiddenResponse({ description: "Permission refusée" })
|
||||||
|
@ApiNotFoundResponse({ description: "Objet non trouvé" })
|
||||||
|
async remove(@Param('id', ParseIntPipe) id: number): Promise<void> {
|
||||||
|
await this.geolocationsService.remove(+id)
|
||||||
|
}
|
||||||
|
}
|
11
server/src/geolocations/geolocations.module.ts
Normal file
11
server/src/geolocations/geolocations.module.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Module } from '@nestjs/common'
|
||||||
|
import { GeolocationsService } from './geolocations.service'
|
||||||
|
import { GeolocationsController } from './geolocations.controller'
|
||||||
|
import { PrismaModule } from 'src/prisma/prisma.module'
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [GeolocationsController],
|
||||||
|
providers: [GeolocationsService],
|
||||||
|
imports: [PrismaModule],
|
||||||
|
})
|
||||||
|
export class GeolocationsModule {}
|
18
server/src/geolocations/geolocations.service.spec.ts
Normal file
18
server/src/geolocations/geolocations.service.spec.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing'
|
||||||
|
import { GeolocationsService } from './geolocations.service'
|
||||||
|
|
||||||
|
describe('GeolocationsService', () => {
|
||||||
|
let service: GeolocationsService
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [GeolocationsService],
|
||||||
|
}).compile()
|
||||||
|
|
||||||
|
service = module.get<GeolocationsService>(GeolocationsService)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
27
server/src/geolocations/geolocations.service.ts
Normal file
27
server/src/geolocations/geolocations.service.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Injectable } from '@nestjs/common'
|
||||||
|
import { CreateGeolocationDto } from './dto/create-geolocation.dto'
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service'
|
||||||
|
import { Geolocation, User } from '@prisma/client'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GeolocationsService {
|
||||||
|
constructor(private prisma: PrismaService) { }
|
||||||
|
|
||||||
|
async create(authenticatedUser: User, createGeolocationDto: CreateGeolocationDto): Promise<Geolocation> {
|
||||||
|
const data = { ...createGeolocationDto, userId: authenticatedUser.id }
|
||||||
|
return await this.prisma.geolocation.create({ data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(): Promise<Geolocation[]> {
|
||||||
|
return await this.prisma.geolocation.findMany()
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: number): Promise<Geolocation> {
|
||||||
|
return await this.prisma.geolocation.findUnique({ where: { id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async remove(id: number): Promise<Geolocation> {
|
||||||
|
return await this.prisma.geolocation.delete({ where: { id } })
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,10 @@
|
|||||||
import { Body, Controller, Get, HttpCode, NotFoundException, Param, ParseIntPipe, Patch, Post, Req, UseGuards } from '@nestjs/common'
|
import { Body, Controller, Get, HttpCode, NotFoundException, Param, ParseIntPipe, Patch, Req, UseGuards } from '@nestjs/common'
|
||||||
import { UsersService } from './users.service'
|
import { UsersService } from './users.service'
|
||||||
import { ApiBadRequestResponse, ApiBearerAuth, ApiForbiddenResponse, ApiNoContentResponse, ApiNotFoundResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger'
|
import { ApiBadRequestResponse, ApiBearerAuth, ApiForbiddenResponse, ApiNoContentResponse, ApiNotFoundResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger'
|
||||||
import { UserEntity } from './entities/user.entity'
|
import { UserEntity } from './entities/user.entity'
|
||||||
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard'
|
import { AuthenticatedRequest, JwtAuthGuard } from 'src/auth/jwt-auth.guard'
|
||||||
import { User } from '@prisma/client'
|
|
||||||
import { UpdatePasswordDto } from './dto/user_password.dto'
|
import { UpdatePasswordDto } from './dto/user_password.dto'
|
||||||
|
|
||||||
export type AuthenticatedRequest = Request & { user: User }
|
|
||||||
|
|
||||||
@Controller('users')
|
@Controller('users')
|
||||||
export class UsersController {
|
export class UsersController {
|
||||||
constructor(private readonly usersService: UsersService) {}
|
constructor(private readonly usersService: UsersService) {}
|
||||||
@ -45,8 +42,8 @@ export class UsersController {
|
|||||||
@ApiBadRequestResponse({description: "Erreur dans la saisie du nouveau mot de passe."})
|
@ApiBadRequestResponse({description: "Erreur dans la saisie du nouveau mot de passe."})
|
||||||
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
|
@ApiUnauthorizedResponse({ description: "Non authentifié⋅e" })
|
||||||
@ApiForbiddenResponse({ description: "Permission refusée" })
|
@ApiForbiddenResponse({ description: "Permission refusée" })
|
||||||
async updatePassword(@Req() request: AuthenticatedRequest, @Body() { password }: UpdatePasswordDto) {
|
async updatePassword(@Req() request: AuthenticatedRequest, @Body() body: UpdatePasswordDto) {
|
||||||
const user = request.user
|
const user = request.user
|
||||||
await this.usersService.updatePassword(user, password)
|
await this.usersService.updatePassword(user, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'
|
|||||||
import { User } from '@prisma/client'
|
import { User } from '@prisma/client'
|
||||||
import { PrismaService } from 'src/prisma/prisma.service'
|
import { PrismaService } from 'src/prisma/prisma.service'
|
||||||
import * as bcrypt from 'bcrypt'
|
import * as bcrypt from 'bcrypt'
|
||||||
|
import { UpdatePasswordDto } from './dto/user_password.dto'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersService {
|
export class UsersService {
|
||||||
@ -15,7 +16,7 @@ export class UsersService {
|
|||||||
return await this.prisma.user.findUnique({ where: { id } })
|
return await this.prisma.user.findUnique({ where: { id } })
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePassword(user: User, password: string) {
|
async updatePassword(user: User, { password }: UpdatePasswordDto) {
|
||||||
const hashedPassword = await bcrypt.hash(password, 10)
|
const hashedPassword = await bcrypt.hash(password, 10)
|
||||||
await this.prisma.user.update({
|
await this.prisma.user.update({
|
||||||
where: { id: user.id },
|
where: { id: user.id },
|
||||||
|
Loading…
Reference in New Issue
Block a user