server: add user decorator, add ping module

This commit is contained in:
Roland Schneider
2025-12-12 08:20:32 +01:00
parent 453d02612c
commit 57edf5b4a8
9 changed files with 90 additions and 3 deletions

View File

@@ -9,6 +9,11 @@ Content-Type: application/json
> {% client.global.set("auth_token", response.body.accessToken); %} > {% client.global.set("auth_token", response.body.accessToken); %}
### GET request with parameter
GET {{apiBaseUrl}}/ping/auth
Accept: application/json
Authorization: Bearer {{auth_token}}
### GET request with parameter ### GET request with parameter
GET {{apiBaseUrl}}/users GET {{apiBaseUrl}}/users
Accept: application/json Accept: application/json

View File

@@ -24,6 +24,7 @@ import { EventExceptionsModule } from './event-exception/event-exceptions.module
import { CalendarModule } from './calendar/calendar.module'; import { CalendarModule } from './calendar/calendar.module';
import { Booking } from './entity/booking.entity'; import { Booking } from './entity/booking.entity';
import { BookingsModule } from './booking/bookings.module'; import { BookingsModule } from './booking/bookings.module';
import { PingModule } from './ping/ping.module';
const moduleTypeOrm = TypeOrmModule.forRootAsync({ const moduleTypeOrm = TypeOrmModule.forRootAsync({
imports: [ConfigModule], imports: [ConfigModule],
@@ -69,6 +70,7 @@ const moduleTypeOrm = TypeOrmModule.forRootAsync({
EventExceptionsModule, EventExceptionsModule,
CalendarModule, CalendarModule,
BookingsModule, BookingsModule,
PingModule,
], ],
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService],

View File

@@ -0,0 +1,23 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import express from 'express';
import { AppUser, UserPayload } from '../types';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request: express.Request = ctx.switchToHttp().getRequest();
const user: UserPayload = request.user as UserPayload;
let result: AppUser;
if (user) {
result = {
anonymous: false,
user: user,
};
} else {
result = {
anonymous: true,
user: undefined,
};
}
return result;
},
);

View File

@@ -22,6 +22,8 @@ import { Role } from '../auth/role.enum';
import { CalendarCreateBookingDto } from './dto/create-booking.dto'; import { CalendarCreateBookingDto } from './dto/create-booking.dto';
import { CancelBookingDto } from './dto/cancel-booking.dto'; import { CancelBookingDto } from './dto/cancel-booking.dto';
import { ApiBody } from '@nestjs/swagger'; import { ApiBody } from '@nestjs/swagger';
import { User } from '../auth/user.decorator';
import * as types from '../types';
@Controller('calendar') @Controller('calendar')
@UseGuards(JwtAuthGuard, RolesGuard) @UseGuards(JwtAuthGuard, RolesGuard)
@@ -77,9 +79,11 @@ export class CalendarController {
@Post('events/:id/bookings') @Post('events/:id/bookings')
@ApiBody({ type: CalendarCreateBookingDto }) @ApiBody({ type: CalendarCreateBookingDto })
createBooking( createBooking(
@User() user: types.AppUser,
@Param('id', ParseIntPipe) eventId: number, @Param('id', ParseIntPipe) eventId: number,
@Body(new ValidationPipe()) createBookingDto: CalendarCreateBookingDto, @Body(new ValidationPipe()) createBookingDto: CalendarCreateBookingDto,
) { ) {
console.info('user user', user);
return this.calendarService.createBooking(eventId, createBookingDto); return this.calendarService.createBooking(eventId, createBookingDto);
} }

View File

@@ -16,6 +16,7 @@ export class CalendarCreateBookingDto {
@ApiProperty() @ApiProperty()
occurrenceStartTime: Date; occurrenceStartTime: Date;
@IsOptional()
@IsNotEmpty() @IsNotEmpty()
@IsInt() @IsInt()
@ApiProperty() @ApiProperty()

View File

@@ -15,9 +15,24 @@ async function bootstrap() {
.setDescription('The DV Booking API description') .setDescription('The DV Booking API description')
.setVersion('1.0') .setVersion('1.0')
.addTag('dvbooking') .addTag('dvbooking')
.addBearerAuth(
{
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
name: 'JWT',
description: 'Enter JWT token',
in: 'header',
},
'bearer',
)
.build(); .build();
const document = SwaggerModule.createDocument(app, config); const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document); SwaggerModule.setup('api', app, document, {
swaggerOptions: {
security: [{ bearer: [] }],
},
});
app.useGlobalPipes( app.useGlobalPipes(
new ValidationPipe({ new ValidationPipe({

View File

@@ -0,0 +1,20 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { User } from '../auth/user.decorator';
import * as types from '../types';
@Controller('ping')
export class PingController {
@Get()
ping() {
return 'pong';
}
@UseGuards(JwtAuthGuard, RolesGuard)
@Get('auth')
pingAuth(@User() user: types.AppUser) {
console.info('user', user);
return 'pong';
}
}

View File

@@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { PingController } from './ping.controller';
@Module({
controllers: [PingController],
})
export class PingModule {}

View File

@@ -1,4 +1,3 @@
export interface LoginRequest { export interface LoginRequest {
username: string; username: string;
password: string; password: string;
@@ -8,3 +7,14 @@ export interface LoginResponse {
accessToken: string; accessToken: string;
refreshToken: string; refreshToken: string;
} }
export interface AppUser {
anonymous: boolean;
user: UserPayload | undefined;
}
export interface UserPayload {
userId: number;
username: string;
roles: string[];
}