mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-16 20:59:26 +00:00
add scalar api reference
This commit is contained in:
@@ -39,6 +39,7 @@
|
|||||||
"@nestjs/serve-static": "^4.0.0",
|
"@nestjs/serve-static": "^4.0.0",
|
||||||
"@nestjs/swagger": "^7.1.16",
|
"@nestjs/swagger": "^7.1.16",
|
||||||
"@prisma/client": "4.16.0",
|
"@prisma/client": "4.16.0",
|
||||||
|
"@scalar/nestjs-api-reference": "^0.2.37",
|
||||||
"@types/passport-local": "^1.0.38",
|
"@types/passport-local": "^1.0.38",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"bcrypt": "5.1.0",
|
"bcrypt": "5.1.0",
|
||||||
|
|||||||
2318
pnpm-lock.yaml
generated
2318
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
19
src/infrastructure/common/helpers/api-schema.ts
Normal file
19
src/infrastructure/common/helpers/api-schema.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
type Constructor<T = object> = new (...args: any[]) => T
|
||||||
|
type Wrapper<T = object> = { new (): T & any; prototype: T }
|
||||||
|
type DecoratorOptions = { name: string }
|
||||||
|
type ApiSchemaDecorator = <T extends Constructor>(
|
||||||
|
options: DecoratorOptions
|
||||||
|
) => (constructor: T) => Wrapper<T>
|
||||||
|
|
||||||
|
export const ApiSchema: ApiSchemaDecorator = ({ name }) => {
|
||||||
|
return constructor => {
|
||||||
|
const wrapper = class extends constructor {}
|
||||||
|
|
||||||
|
Object.defineProperty(wrapper, 'name', {
|
||||||
|
value: name,
|
||||||
|
writable: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/main.ts
28
src/main.ts
@@ -1,6 +1,7 @@
|
|||||||
import { Logger, VersioningType } from '@nestjs/common'
|
import { Logger, VersioningType } from '@nestjs/common'
|
||||||
import { NestFactory } from '@nestjs/core'
|
import { NestFactory } from '@nestjs/core'
|
||||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
|
||||||
|
import { apiReference } from '@scalar/nestjs-api-reference'
|
||||||
import * as cookieParser from 'cookie-parser'
|
import * as cookieParser from 'cookie-parser'
|
||||||
|
|
||||||
import { AppModule } from './app.module'
|
import { AppModule } from './app.module'
|
||||||
@@ -22,6 +23,7 @@ async function bootstrap() {
|
|||||||
})
|
})
|
||||||
const config = new DocumentBuilder()
|
const config = new DocumentBuilder()
|
||||||
.setTitle('Flashcards')
|
.setTitle('Flashcards')
|
||||||
|
.addBearerAuth()
|
||||||
.setDescription('Flashcards API')
|
.setDescription('Flashcards API')
|
||||||
.setVersion('1.0')
|
.setVersion('1.0')
|
||||||
.addServer('https://api.flashcards.andrii.es')
|
.addServer('https://api.flashcards.andrii.es')
|
||||||
@@ -36,6 +38,32 @@ async function bootstrap() {
|
|||||||
customJs: '/swagger-ui.js',
|
customJs: '/swagger-ui.js',
|
||||||
customCssUrl: '/swagger-themes/dark.css',
|
customCssUrl: '/swagger-themes/dark.css',
|
||||||
})
|
})
|
||||||
|
app.use(
|
||||||
|
'/reference',
|
||||||
|
apiReference({
|
||||||
|
spec: {
|
||||||
|
content: document,
|
||||||
|
},
|
||||||
|
authentication: {
|
||||||
|
preferredSecurityScheme: 'bearer',
|
||||||
|
http: {
|
||||||
|
basic: {
|
||||||
|
username: 'Basic',
|
||||||
|
password: 'Basic',
|
||||||
|
},
|
||||||
|
bearer: {
|
||||||
|
token:
|
||||||
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJmMmJlOTViOS00ZDA3LTQ3NTEtYTc3NS1iZDYxMmZjOTU1M2EiLCJkYXRlIjoiMjAyMy0wOC0wNVQxMTowMjoxNC42MjFaIiwiaWF0IjoxNjkxMjMzMzM0LCJleHAiOjIwMDY4MDkzMzR9.PGTRcsf34VFaS-Hz7_PUnWR8bBuVK7pdteBWUUYHXfw',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
theme: 'deepSpace',
|
||||||
|
metaData: {
|
||||||
|
title: 'Flashcards API Reference',
|
||||||
|
ogTitle: 'Flashcards API Reference',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
pipesSetup(app)
|
pipesSetup(app)
|
||||||
app.useGlobalFilters(new HttpExceptionFilter())
|
app.useGlobalFilters(new HttpExceptionFilter())
|
||||||
await app.listen(process.env.PORT || 3333)
|
await app.listen(process.env.PORT || 3333)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { CommandBus } from '@nestjs/cqrs'
|
|||||||
import { FileFieldsInterceptor } from '@nestjs/platform-express'
|
import { FileFieldsInterceptor } from '@nestjs/platform-express'
|
||||||
import {
|
import {
|
||||||
ApiBadRequestResponse,
|
ApiBadRequestResponse,
|
||||||
|
ApiBearerAuth,
|
||||||
ApiBody,
|
ApiBody,
|
||||||
ApiConsumes,
|
ApiConsumes,
|
||||||
ApiNoContentResponse,
|
ApiNoContentResponse,
|
||||||
@@ -63,6 +64,7 @@ export class AuthController {
|
|||||||
@ApiUnauthorizedResponse({ description: 'Not logged in' })
|
@ApiUnauthorizedResponse({ description: 'Not logged in' })
|
||||||
@ApiBadRequestResponse({ description: 'User not found' })
|
@ApiBadRequestResponse({ description: 'User not found' })
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
@Get('me')
|
@Get('me')
|
||||||
async getUserData(@Request() req): Promise<UserEntity> {
|
async getUserData(@Request() req): Promise<UserEntity> {
|
||||||
const userId = req.user.id
|
const userId = req.user.id
|
||||||
@@ -77,6 +79,7 @@ export class AuthController {
|
|||||||
@UseInterceptors(FileFieldsInterceptor([{ name: 'avatar', maxCount: 1 }]))
|
@UseInterceptors(FileFieldsInterceptor([{ name: 'avatar', maxCount: 1 }]))
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Patch('me')
|
@Patch('me')
|
||||||
|
@ApiBearerAuth()
|
||||||
async updateUserData(
|
async updateUserData(
|
||||||
@Request() req,
|
@Request() req,
|
||||||
@UploadedFiles()
|
@UploadedFiles()
|
||||||
@@ -115,7 +118,7 @@ export class AuthController {
|
|||||||
secure: true,
|
secure: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
return { accessToken: req.user.data.accessToken }
|
return { accessToken: req.user.data.accessToken, refreshToken: req.user.data.refreshToken }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation({ description: 'Create a new user account', summary: 'Create a new user account' })
|
@ApiOperation({ description: 'Create a new user account', summary: 'Create a new user account' })
|
||||||
@@ -155,6 +158,7 @@ export class AuthController {
|
|||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Post('logout')
|
@Post('logout')
|
||||||
|
@ApiBearerAuth()
|
||||||
async logout(
|
async logout(
|
||||||
@Cookies('accessToken') accessToken: string,
|
@Cookies('accessToken') accessToken: string,
|
||||||
@Res({ passthrough: true }) res: ExpressResponse
|
@Res({ passthrough: true }) res: ExpressResponse
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { IsUUID } from 'class-validator'
|
import { IsUUID } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'EmailVerificationRequest' })
|
||||||
export class EmailVerificationDto {
|
export class EmailVerificationDto {
|
||||||
@IsUUID('4')
|
@IsUUID('4')
|
||||||
code: string
|
code: string
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { IsBoolean, IsEmail, IsOptional, Length } from 'class-validator'
|
import { IsBoolean, IsEmail, IsOptional, Length } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'LoginRequest' })
|
||||||
export class LoginDto {
|
export class LoginDto {
|
||||||
@Length(3, 30)
|
@Length(3, 30)
|
||||||
password: string
|
password: string
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger'
|
import { ApiProperty } from '@nestjs/swagger'
|
||||||
import { IsEmail, IsOptional, IsString } from 'class-validator'
|
import { IsEmail, IsOptional, IsString } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'RecoverPasswordRequest' })
|
||||||
export class RecoverPasswordDto {
|
export class RecoverPasswordDto {
|
||||||
/** User's email address */
|
/** User's email address */
|
||||||
@IsEmail()
|
@IsEmail()
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger'
|
import { ApiProperty } from '@nestjs/swagger'
|
||||||
import { IsBoolean, IsEmail, IsOptional, IsString, Length } from 'class-validator'
|
import { IsBoolean, IsEmail, IsOptional, IsString, Length } from 'class-validator'
|
||||||
|
|
||||||
export class RegistrationDto {
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
@Length(3, 30)
|
|
||||||
@IsOptional()
|
|
||||||
name?: string
|
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'RegistrationRequest' })
|
||||||
|
export class RegistrationDto {
|
||||||
@Length(3, 30)
|
@Length(3, 30)
|
||||||
password: string
|
password: string
|
||||||
|
|
||||||
@IsEmail()
|
@IsEmail()
|
||||||
email: string
|
email: string
|
||||||
|
|
||||||
|
@Length(3, 30)
|
||||||
|
@IsOptional()
|
||||||
|
name?: string
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: `HTML template to be sent in the email;\n ##name## will be replaced with the user's name; \n ##token## will be replaced with the password recovery token`,
|
description: `HTML template to be sent in the email;\n ##name## will be replaced with the user's name; \n ##token## will be replaced with the password recovery token`,
|
||||||
example: `<b>Hello, ##name##!</b><br/>Please confirm your email by clicking on the link below:<br/><a href="http://localhost:3000/confirm-email/##token##">Confirm email</a>. If it doesn't work, copy and paste the following link in your browser:<br/>http://localhost:3000/confirm-email/##token##`,
|
example: `<b>Hello, ##name##!</b><br/>Please confirm your email by clicking on the link below:<br/><a href="http://localhost:3000/confirm-email/##token##">Confirm email</a>. If it doesn't work, copy and paste the following link in your browser:<br/>http://localhost:3000/confirm-email/##token##`,
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger'
|
import { ApiProperty } from '@nestjs/swagger'
|
||||||
import { IsOptional, IsString, IsUUID } from 'class-validator'
|
import { IsOptional, IsString, IsUUID } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'ResendVerificationEmailRequest' })
|
||||||
export class ResendVerificationEmailDto {
|
export class ResendVerificationEmailDto {
|
||||||
@IsUUID()
|
@IsUUID()
|
||||||
userId: string
|
userId: string
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Length } from 'class-validator'
|
import { Length } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'ResetPasswordRequest' })
|
||||||
export class ResetPasswordDto {
|
export class ResetPasswordDto {
|
||||||
@Length(3, 30)
|
@Length(3, 30)
|
||||||
password: string
|
password: string
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { IsNumber, IsString, Max, Min } from 'class-validator'
|
import { IsNumber, IsString, Max, Min } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'SaveGradeRequest' })
|
||||||
export class SaveGradeDto {
|
export class SaveGradeDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
cardId: string
|
cardId: string
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { PartialType, PickType } from '@nestjs/swagger'
|
import { PartialType, PickType } from '@nestjs/swagger'
|
||||||
import { IsOptional } from 'class-validator'
|
import { IsOptional } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
import { User } from '../entities/auth.entity'
|
import { User } from '../entities/auth.entity'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'UpdateUserRequest' })
|
||||||
export class UpdateUserDataDto extends PartialType(PickType(User, ['name', 'avatar'] as const)) {
|
export class UpdateUserDataDto extends PartialType(PickType(User, ['name', 'avatar'] as const)) {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
avatar?: string
|
avatar?: string
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { ApiProperty, OmitType } from '@nestjs/swagger'
|
import { ApiProperty, OmitType } from '@nestjs/swagger'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
export class User {
|
export class User {
|
||||||
id: string
|
id: string
|
||||||
email: string
|
email: string
|
||||||
@@ -14,6 +16,8 @@ export class User {
|
|||||||
|
|
||||||
export class LoginResponse {
|
export class LoginResponse {
|
||||||
accessToken: string
|
accessToken: string
|
||||||
|
refreshToken: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'User' })
|
||||||
export class UserEntity extends OmitType(User, ['password']) {}
|
export class UserEntity extends OmitType(User, ['password']) {}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common'
|
import { Inject, Injectable } from '@nestjs/common'
|
||||||
import { PassportStrategy } from '@nestjs/passport'
|
import { PassportStrategy } from '@nestjs/passport'
|
||||||
import { Request } from 'express'
|
import { Request } from 'express'
|
||||||
import { Strategy } from 'passport-jwt'
|
import { ExtractJwt, Strategy } from 'passport-jwt'
|
||||||
|
|
||||||
import { AppSettings } from '../../../settings/app-settings'
|
import { AppSettings } from '../../../settings/app-settings'
|
||||||
import { UsersService } from '../../users/services/users.service'
|
import { UsersService } from '../../users/services/users.service'
|
||||||
@@ -24,7 +24,10 @@ export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'jwt-refresh'
|
|||||||
private userService: UsersService
|
private userService: UsersService
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
jwtFromRequest: cookieExtractor,
|
jwtFromRequest: ExtractJwt.fromExtractors([
|
||||||
|
cookieExtractor,
|
||||||
|
ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
]),
|
||||||
ignoreExpiration: true,
|
ignoreExpiration: true,
|
||||||
secretOrKey: appSettings.auth.REFRESH_JWT_SECRET_KEY,
|
secretOrKey: appSettings.auth.REFRESH_JWT_SECRET_KEY,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,13 +5,11 @@ import { ExtractJwt, Strategy } from 'passport-jwt'
|
|||||||
|
|
||||||
import { AppSettings } from '../../../settings/app-settings'
|
import { AppSettings } from '../../../settings/app-settings'
|
||||||
import { UsersService } from '../../users/services/users.service'
|
import { UsersService } from '../../users/services/users.service'
|
||||||
import { AuthService } from '../auth.service'
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(AppSettings.name) private readonly appSettings: AppSettings,
|
@Inject(AppSettings.name) private readonly appSettings: AppSettings,
|
||||||
private authService: AuthService,
|
|
||||||
private userService: UsersService
|
private userService: UsersService
|
||||||
) {
|
) {
|
||||||
super({
|
super({
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
import { CommandBus } from '@nestjs/cqrs'
|
import { CommandBus } from '@nestjs/cqrs'
|
||||||
import { FileFieldsInterceptor } from '@nestjs/platform-express'
|
import { FileFieldsInterceptor } from '@nestjs/platform-express'
|
||||||
import {
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
ApiConsumes,
|
ApiConsumes,
|
||||||
ApiNoContentResponse,
|
ApiNoContentResponse,
|
||||||
ApiNotFoundResponse,
|
ApiNotFoundResponse,
|
||||||
@@ -38,6 +39,7 @@ export class CardsController {
|
|||||||
@ApiOperation({ summary: 'Get card by id', description: 'Get card by id' })
|
@ApiOperation({ summary: 'Get card by id', description: 'Get card by id' })
|
||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@ApiNotFoundResponse({ description: 'Card not found' })
|
@ApiNotFoundResponse({ description: 'Card not found' })
|
||||||
|
@ApiBearerAuth()
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
findOne(@Param('id') id: string): Promise<CardWithGrade> {
|
findOne(@Param('id') id: string): Promise<CardWithGrade> {
|
||||||
return this.commandBus.execute(new GetDeckByIdCommand(id))
|
return this.commandBus.execute(new GetDeckByIdCommand(id))
|
||||||
@@ -48,6 +50,7 @@ export class CardsController {
|
|||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@ApiNotFoundResponse({ description: 'Card not found' })
|
@ApiNotFoundResponse({ description: 'Card not found' })
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
@UseInterceptors(
|
@UseInterceptors(
|
||||||
FileFieldsInterceptor([
|
FileFieldsInterceptor([
|
||||||
{ name: 'questionImg', maxCount: 1 },
|
{ name: 'questionImg', maxCount: 1 },
|
||||||
@@ -69,6 +72,7 @@ export class CardsController {
|
|||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Delete card by id', description: 'Delete card by id' })
|
@ApiOperation({ summary: 'Delete card by id', description: 'Delete card by id' })
|
||||||
@ApiNoContentResponse({ description: 'New tokens generated successfully' })
|
@ApiNoContentResponse({ description: 'New tokens generated successfully' })
|
||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { IsOptional, Length } from 'class-validator'
|
import { IsOptional, Length } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'CreateCardRequest' })
|
||||||
export class CreateCardDto {
|
export class CreateCardDto {
|
||||||
@Length(3, 500)
|
@Length(3, 500)
|
||||||
question: string
|
question: string
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import { PartialType } from '@nestjs/mapped-types'
|
|||||||
import { ApiProperty } from '@nestjs/swagger'
|
import { ApiProperty } from '@nestjs/swagger'
|
||||||
import { IsOptional, Length } from 'class-validator'
|
import { IsOptional, Length } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
import { CreateCardDto } from './create-card.dto'
|
import { CreateCardDto } from './create-card.dto'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'UpdateCardRequest' })
|
||||||
export class UpdateCardDto extends PartialType(CreateCardDto) {
|
export class UpdateCardDto extends PartialType(CreateCardDto) {
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Length(3, 500)
|
@Length(3, 500)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
import { CommandBus } from '@nestjs/cqrs'
|
import { CommandBus } from '@nestjs/cqrs'
|
||||||
import { FileFieldsInterceptor } from '@nestjs/platform-express'
|
import { FileFieldsInterceptor } from '@nestjs/platform-express'
|
||||||
import {
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
ApiConsumes,
|
ApiConsumes,
|
||||||
ApiNoContentResponse,
|
ApiNoContentResponse,
|
||||||
ApiNotFoundResponse,
|
ApiNotFoundResponse,
|
||||||
@@ -61,10 +62,7 @@ import {
|
|||||||
@ApiTags('Decks')
|
@ApiTags('Decks')
|
||||||
@Controller('decks')
|
@Controller('decks')
|
||||||
export class DecksController {
|
export class DecksController {
|
||||||
constructor(
|
constructor(private commandBus: CommandBus) {}
|
||||||
private commandBus: CommandBus,
|
|
||||||
private decksRepository: DecksRepository
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@HttpCode(HttpStatus.PARTIAL_CONTENT)
|
@HttpCode(HttpStatus.PARTIAL_CONTENT)
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
@@ -74,6 +72,7 @@ export class DecksController {
|
|||||||
})
|
})
|
||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
@Get()
|
@Get()
|
||||||
findAllV1(@Query() query: GetAllDecksDto, @Req() req): Promise<PaginatedDecksWithMaxCardsCount> {
|
findAllV1(@Query() query: GetAllDecksDto, @Req() req): Promise<PaginatedDecksWithMaxCardsCount> {
|
||||||
const finalQuery = Pagination.getPaginationData(query)
|
const finalQuery = Pagination.getPaginationData(query)
|
||||||
@@ -86,6 +85,7 @@ export class DecksController {
|
|||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Version('2')
|
@Version('2')
|
||||||
|
@ApiBearerAuth()
|
||||||
@Get()
|
@Get()
|
||||||
findAllV2(@Query() query: GetAllDecksDto, @Req() req): Promise<PaginatedDecks> {
|
findAllV2(@Query() query: GetAllDecksDto, @Req() req): Promise<PaginatedDecks> {
|
||||||
const finalQuery = Pagination.getPaginationData(query)
|
const finalQuery = Pagination.getPaginationData(query)
|
||||||
@@ -98,6 +98,7 @@ export class DecksController {
|
|||||||
description: 'Retrieve the minimum and maximum amount of cards in a deck.',
|
description: 'Retrieve the minimum and maximum amount of cards in a deck.',
|
||||||
summary: 'Minimum and maximum amount of cards in a deck',
|
summary: 'Minimum and maximum amount of cards in a deck',
|
||||||
})
|
})
|
||||||
|
@ApiBearerAuth()
|
||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Version('2')
|
@Version('2')
|
||||||
@@ -110,6 +111,7 @@ export class DecksController {
|
|||||||
@ApiOperation({ description: 'Create a deck', summary: 'Create a deck' })
|
@ApiOperation({ description: 'Create a deck', summary: 'Create a deck' })
|
||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
@UseInterceptors(FileFieldsInterceptor([{ name: 'cover', maxCount: 1 }]))
|
@UseInterceptors(FileFieldsInterceptor([{ name: 'cover', maxCount: 1 }]))
|
||||||
@Post()
|
@Post()
|
||||||
create(
|
create(
|
||||||
@@ -131,6 +133,7 @@ export class DecksController {
|
|||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
@ApiBearerAuth()
|
||||||
findOne(@Param('id') id: string): Promise<DeckWithAuthor> {
|
findOne(@Param('id') id: string): Promise<DeckWithAuthor> {
|
||||||
return this.commandBus.execute(new GetDeckByIdCommand(id))
|
return this.commandBus.execute(new GetDeckByIdCommand(id))
|
||||||
}
|
}
|
||||||
@@ -142,6 +145,7 @@ export class DecksController {
|
|||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@UseInterceptors(FileFieldsInterceptor([{ name: 'cover', maxCount: 1 }]))
|
@UseInterceptors(FileFieldsInterceptor([{ name: 'cover', maxCount: 1 }]))
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
|
@ApiBearerAuth()
|
||||||
update(
|
update(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@UploadedFiles()
|
@UploadedFiles()
|
||||||
@@ -162,6 +166,7 @@ export class DecksController {
|
|||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@ApiNotFoundResponse({ description: 'Deck not found' })
|
@ApiNotFoundResponse({ description: 'Deck not found' })
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@ApiBearerAuth()
|
||||||
remove(@Param('id') id: string, @Req() req): Promise<Deck> {
|
remove(@Param('id') id: string, @Req() req): Promise<Deck> {
|
||||||
return this.commandBus.execute(new DeleteDeckByIdCommand(id, req.user.id))
|
return this.commandBus.execute(new DeleteDeckByIdCommand(id, req.user.id))
|
||||||
}
|
}
|
||||||
@@ -170,6 +175,7 @@ export class DecksController {
|
|||||||
description: 'Retrieve paginated cards in a deck',
|
description: 'Retrieve paginated cards in a deck',
|
||||||
summary: 'Retrieve cards in a deck',
|
summary: 'Retrieve cards in a deck',
|
||||||
})
|
})
|
||||||
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get(':id/cards')
|
@Get(':id/cards')
|
||||||
findCardsInDeck(
|
findCardsInDeck(
|
||||||
@@ -187,6 +193,7 @@ export class DecksController {
|
|||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@ApiNotFoundResponse({ description: 'Deck not found' })
|
@ApiNotFoundResponse({ description: 'Deck not found' })
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@ApiBearerAuth()
|
||||||
@UseInterceptors(
|
@UseInterceptors(
|
||||||
FileFieldsInterceptor([
|
FileFieldsInterceptor([
|
||||||
{ name: 'questionImg', maxCount: 1 },
|
{ name: 'questionImg', maxCount: 1 },
|
||||||
@@ -213,6 +220,7 @@ export class DecksController {
|
|||||||
description: 'Retrieve a random card in a deck. The cards priority is based on the grade',
|
description: 'Retrieve a random card in a deck. The cards priority is based on the grade',
|
||||||
summary: 'Retrieve a random card',
|
summary: 'Retrieve a random card',
|
||||||
})
|
})
|
||||||
|
@ApiBearerAuth()
|
||||||
@Get(':id/learn')
|
@Get(':id/learn')
|
||||||
findRandomCardInDeck(
|
findRandomCardInDeck(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@@ -224,6 +232,7 @@ export class DecksController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||||
@ApiNotFoundResponse({ description: 'Card not found' })
|
@ApiNotFoundResponse({ description: 'Card not found' })
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'
|
|||||||
import { Transform } from 'class-transformer'
|
import { Transform } from 'class-transformer'
|
||||||
import { IsBoolean, IsOptional, Length } from 'class-validator'
|
import { IsBoolean, IsOptional, Length } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'CreateDeckRequest' })
|
||||||
export class CreateDeckDto {
|
export class CreateDeckDto {
|
||||||
@Length(3, 30)
|
@Length(3, 30)
|
||||||
name: string
|
name: string
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ import { PartialType } from '@nestjs/mapped-types'
|
|||||||
import { ApiProperty } from '@nestjs/swagger'
|
import { ApiProperty } from '@nestjs/swagger'
|
||||||
import { IsBoolean } from 'class-validator'
|
import { IsBoolean } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators'
|
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators'
|
||||||
|
|
||||||
import { CreateDeckDto } from './create-deck.dto'
|
import { CreateDeckDto } from './create-deck.dto'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'UpdateDeckRequest' })
|
||||||
export class UpdateDeckDto extends PartialType(CreateDeckDto) {
|
export class UpdateDeckDto extends PartialType(CreateDeckDto) {
|
||||||
@IsOptionalOrEmptyString()
|
@IsOptionalOrEmptyString()
|
||||||
name?: string
|
name?: string
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Length, Matches } from 'class-validator'
|
import { Length, Matches } from 'class-validator'
|
||||||
|
|
||||||
|
import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema'
|
||||||
|
|
||||||
|
@ApiSchema({ name: 'CreateUserRequest' })
|
||||||
export class CreateUserDto {
|
export class CreateUserDto {
|
||||||
@Length(3, 10)
|
@Length(3, 10)
|
||||||
name: string
|
name: string
|
||||||
|
|||||||
Reference in New Issue
Block a user