diff --git a/src/app.module.ts b/src/app.module.ts index 3c10b53..52f1508 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,14 +1,14 @@ import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { YachtModule } from './yacht/yacht.module'; +import { YachtsModule } from './yachts/yachts.module'; import { CatalogModule } from './catalog/catalog.module'; import { AuthModule } from './auth/auth.module'; import { UsersModule } from './users/users.module'; import { FilesModule } from './files/files.module'; @Module({ - imports: [YachtModule, CatalogModule, AuthModule, UsersModule, FilesModule], + imports: [YachtsModule, CatalogModule, AuthModule, UsersModule, FilesModule], controllers: [AppController], providers: [AppService], }) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index bb9aa95..39ec86a 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -1,5 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; -import { type User, UsersService } from '../users/users.service'; +import { UsersService } from '../users/users.service'; +import { User } from 'src/users/user.entity'; import { JwtService } from '@nestjs/jwt'; @Injectable() diff --git a/src/auth/local.strategy.ts b/src/auth/local.strategy.ts index 05f544c..b404888 100644 --- a/src/auth/local.strategy.ts +++ b/src/auth/local.strategy.ts @@ -2,7 +2,7 @@ import { Strategy } from 'passport-local'; import { PassportStrategy } from '@nestjs/passport'; import { Injectable, UnauthorizedException } from '@nestjs/common'; import { AuthService } from './auth.service'; -import type { User } from 'src/users/users.service'; +import type { User } from 'src/users/user.entity'; @Injectable() export class LocalStrategy extends PassportStrategy(Strategy) { diff --git a/src/catalog/catalog.service.ts b/src/catalog/catalog.service.ts index d180a1b..0620332 100644 --- a/src/catalog/catalog.service.ts +++ b/src/catalog/catalog.service.ts @@ -6,7 +6,6 @@ import { CatalogResponseDto } from './dto/catalog-response.dto'; @Injectable() export class CatalogService { private catalogItems: CatalogItemDto[] = [ - // SAMPLE DATA { id: 1, name: 'Item 1', @@ -15,6 +14,9 @@ export class CatalogService { minCost: 50, hasQuickRent: true, pictureUrl: 'https://example.com/item1.jpg', + isFeatured: true, + mainImageUrl: '', + galleryUrls: [], }, { id: 2, @@ -24,6 +26,9 @@ export class CatalogService { minCost: 50, hasQuickRent: false, pictureUrl: 'https://example.com/item1.jpg', + isFeatured: false, + mainImageUrl: '', + galleryUrls: [], }, { id: 3, @@ -33,6 +38,9 @@ export class CatalogService { minCost: 50, hasQuickRent: true, pictureUrl: 'https://example.com/item1.jpg', + isFeatured: false, + mainImageUrl: '', + galleryUrls: [], }, { id: 4, @@ -42,6 +50,33 @@ export class CatalogService { minCost: 50, hasQuickRent: true, pictureUrl: 'https://example.com/item1.jpg', + isFeatured: false, + mainImageUrl: '', + galleryUrls: [], + }, + { + id: 4, + name: 'Item 4', + length: 10, + speed: 100, + minCost: 50, + hasQuickRent: true, + pictureUrl: 'https://example.com/item1.jpg', + isFeatured: false, + mainImageUrl: '', + galleryUrls: [], + }, + { + id: 4, + name: 'Item 4', + length: 10, + speed: 100, + minCost: 50, + hasQuickRent: true, + pictureUrl: 'https://example.com/item1.jpg', + isFeatured: false, + mainImageUrl: '', + galleryUrls: [], }, ]; diff --git a/src/catalog/dto/catalog-item.dto.ts b/src/catalog/dto/catalog-item.dto.ts index 1dd3dcb..1558b29 100644 --- a/src/catalog/dto/catalog-item.dto.ts +++ b/src/catalog/dto/catalog-item.dto.ts @@ -6,4 +6,7 @@ export class CatalogItemDto { minCost: number; hasQuickRent: boolean; pictureUrl: string; + isFeatured: boolean; + mainImageUrl: string; + galleryUrls: string[]; } diff --git a/src/main.ts b/src/main.ts index 7d4d115..63faf11 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,15 @@ async function bootstrap() { app.setGlobalPrefix(''); + app.enableCors({ + origin: ['http://localhost:3000'], + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', + credentials: true, + allowedHeaders: 'Content-Type, Authorization, X-Requested-With', + exposedHeaders: 'Authorization', + maxAge: 86400, + }); + const config = new DocumentBuilder() .setTitle('Travelmarine backend') .setDescription('Backend API for Travelmarine service') diff --git a/src/users/user.entity.ts b/src/users/user.entity.ts index fee0c6c..82e1b0c 100644 --- a/src/users/user.entity.ts +++ b/src/users/user.entity.ts @@ -1,4 +1,4 @@ -import { Yacht } from '../yacht/yacht.entity'; +import { Yacht } from '../yachts/yacht.entity'; export type User = { userId: number; diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 7343b39..a4c24e7 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -1,13 +1,11 @@ import { Injectable, Logger } from '@nestjs/common'; -import { YachtService } from '../yacht/yacht.service'; +import { YachtsService } from '../yachts/yachts.service'; import { User } from './user.entity'; @Injectable() export class UsersService { private readonly logger = new Logger(UsersService.name); - constructor(private readonly yachtsService: YachtService) {} - private readonly users: User[] = [ { userId: 1, @@ -16,7 +14,6 @@ export class UsersService { phone: '+79009009090', email: 'email@email.com', password: 'admin', - yachts: [], }, { userId: 2, @@ -25,19 +22,18 @@ export class UsersService { phone: '+79009009090', email: 'email1@email.com', password: 'admin', - yachts: [], }, ]; async findOne( email: string, - includeYachts = false, + includeYachts: boolean = false, ): Promise { - this.logger.log({ email }); const user = this.users.find((user) => user.email === email); if (user && includeYachts) { - user.yachts = await this.yachtsService.findByUserId(user.userId); + // Fetch yachts for this user + user.yachts = await []; } return user; @@ -45,37 +41,30 @@ export class UsersService { async findById( userId: number, - includeYachts = false, + includeYachts: boolean = false, ): Promise { const user = this.users.find((user) => user.userId === userId); if (user && includeYachts) { - user.yachts = await this.yachtsService.findByUserId(user.userId); + user.yachts = []; } return user; } - async findAll(includeYachts = false): Promise { - const users = [...this.users]; - - if (includeYachts) { - for (const user of users) { - user.yachts = await this.yachtsService.findByUserId(user.userId); - } + async findAll(includeYachts: boolean = false): Promise { + if (!includeYachts) { + return this.users; } - return users; - } + // Fetch all users with their yachts + const usersWithYachts = await Promise.all( + this.users.map(async (user) => { + const yachts = []; + return { ...user, yachts }; + }), + ); - async create(userData: Omit): Promise { - const newUser: User = { - userId: this.users.length + 1, - ...userData, - }; - - this.users.push(newUser); - this.logger.log(`Created new user: ${newUser.email}`); - return newUser; + return usersWithYachts; } } diff --git a/src/yacht/yacht.controller.ts b/src/yacht/yacht.controller.ts deleted file mode 100644 index 3d99473..0000000 --- a/src/yacht/yacht.controller.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Patch, - Param, - Delete, - ParseIntPipe, -} from '@nestjs/common'; -import { YachtService } from './yacht.service'; -import { CreateYachtDto } from './dto/create-yacht.dto'; -import { UpdateYachtDto } from './dto/update-yacht.dto'; -import { Yacht } from './yacht.entity'; - -@Controller('yacht') // Routes will be /yacht -export class YachtController { - constructor(private readonly yachtService: YachtService) {} - - @Post() - create(@Body() createYachtDto: CreateYachtDto): Yacht { - return this.yachtService.create(createYachtDto); - } - - @Get() - getAll(): Yacht[] { - return this.yachtService.getAll(); - } - - @Get(':id') - getById(@Param('id', ParseIntPipe) id: number): Yacht { - return this.yachtService.getById(id); - } - - @Patch(':id') - update( - @Param('id', ParseIntPipe) id: number, - @Body() updateYachtDto: UpdateYachtDto, - ): Yacht { - return this.yachtService.update(id, updateYachtDto); - } - - @Delete(':id') - remove(@Param('id', ParseIntPipe) id: number): void { - return this.yachtService.remove(id); - } -} diff --git a/src/yacht/yacht.entity.ts b/src/yacht/yacht.entity.ts deleted file mode 100644 index 0dc527d..0000000 --- a/src/yacht/yacht.entity.ts +++ /dev/null @@ -1,12 +0,0 @@ -export class Yacht { - id?: number; - userId: number; - name: string; - length: number; - speed: number; - minCost: number; - hasQuickRent: boolean; - pictureUrl: string; - createdAt: Date; - updatedAt: Date; -} diff --git a/src/yacht/yacht.module.ts b/src/yacht/yacht.module.ts deleted file mode 100644 index a597ccc..0000000 --- a/src/yacht/yacht.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Module } from '@nestjs/common'; -import { YachtService } from './yacht.service'; - -@Module({ - providers: [YachtService], - exports: [YachtService], -}) -export class YachtsModule {} diff --git a/src/yacht/yacht.service.ts b/src/yacht/yacht.service.ts deleted file mode 100644 index a64b0aa..0000000 --- a/src/yacht/yacht.service.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Yacht } from './yacht.entity'; - -@Injectable() -export class YachtService { - private yachts: Yacht[] = []; - - async findAll(): Promise { - return this.yachts; - } - - async findById(yachtId: number): Promise { - return this.yachts.find((yacht) => yacht.id === yachtId); - } - - async findByUserId(userId: number): Promise { - return this.yachts.filter((yacht) => yacht.userId === userId); - } - - async create( - yachtData: Omit, - ): Promise { - const newYacht: Yacht = { - id: this.yachts.length + 1, - ...yachtData, - createdAt: new Date(), - updatedAt: new Date(), - }; - - this.yachts.push(newYacht); - return newYacht; - } - - async update( - yachtId: number, - updateData: Partial, - ): Promise { - const index = this.yachts.findIndex((yacht) => yacht.id === yachtId); - - if (index === -1) { - return undefined; - } - - this.yachts[index] = { - ...this.yachts[index], - ...updateData, - updatedAt: new Date(), - }; - - return this.yachts[index]; - } - - async delete(yachtId: number): Promise { - const index = this.yachts.findIndex((yacht) => yacht.id === yachtId); - - if (index === -1) { - return false; - } - - this.yachts.splice(index, 1); - - return true; - } -} diff --git a/src/yachts/dto/create-yacht.dto.ts b/src/yachts/dto/create-yacht.dto.ts new file mode 100644 index 0000000..0512bf6 --- /dev/null +++ b/src/yachts/dto/create-yacht.dto.ts @@ -0,0 +1,7 @@ +export class CreateYachtDto { + name: string; + model: string; + year: number; + length: number; + userId: number; +} diff --git a/src/yachts/dto/update-yacht.dto.ts b/src/yachts/dto/update-yacht.dto.ts new file mode 100644 index 0000000..702ca91 --- /dev/null +++ b/src/yachts/dto/update-yacht.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateYachtDto } from './create-yacht.dto'; + +export class UpdateYachtDto extends PartialType(CreateYachtDto) {} diff --git a/src/yachts/yacht.entity.ts b/src/yachts/yacht.entity.ts new file mode 100644 index 0000000..6eeedc1 --- /dev/null +++ b/src/yachts/yacht.entity.ts @@ -0,0 +1,10 @@ +export type Yacht = { + yachtId: number; + name: string; + model: string; + year: number; + length: number; + userId: number; + createdAt: Date; + updatedAt: Date; +}; diff --git a/src/yacht/yacht.controller.spec.ts b/src/yachts/yachts.controller.spec.ts similarity index 60% rename from src/yacht/yacht.controller.spec.ts rename to src/yachts/yachts.controller.spec.ts index e09d5e4..7eca733 100644 --- a/src/yacht/yacht.controller.spec.ts +++ b/src/yachts/yachts.controller.spec.ts @@ -1,15 +1,15 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { YachtController } from './yacht.controller'; +import { YachtsController } from './yacht.controller'; describe('YachtController', () => { - let controller: YachtController; + let controller: YachtsController; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - controllers: [YachtController], + controllers: [YachtsController], }).compile(); - controller = module.get(YachtController); + controller = module.get(YachtsController); }); it('should be defined', () => { diff --git a/src/yachts/yachts.controller.ts b/src/yachts/yachts.controller.ts new file mode 100644 index 0000000..7b64cf5 --- /dev/null +++ b/src/yachts/yachts.controller.ts @@ -0,0 +1,54 @@ +import { + Controller, + Get, + Post, + Put, + Delete, + Body, + Param, + Query, + ParseIntPipe, + HttpCode, + HttpStatus, +} from '@nestjs/common'; +import { YachtsService } from './yachts.service'; +import { CreateYachtDto } from './dto/create-yacht.dto'; +import { UpdateYachtDto } from './dto/update-yacht.dto'; + +@Controller('yachts') +export class YachtsController { + constructor(private readonly yachtsService: YachtsService) {} + + @Get() + async findAll(@Query('userId') userId?: string) { + if (userId) { + return this.yachtsService.findByUserId(parseInt(userId)); + } + return this.yachtsService.findAll(); + } + + @Get(':id') + async findOne(@Param('id', ParseIntPipe) id: number) { + return this.yachtsService.findById(id); + } + + @Post() + @HttpCode(HttpStatus.CREATED) + async create(@Body() createYachtDto: CreateYachtDto) { + return this.yachtsService.create(createYachtDto); + } + + @Put(':id') + async update( + @Param('id', ParseIntPipe) id: number, + @Body() updateYachtDto: UpdateYachtDto, + ) { + return this.yachtsService.update(id, updateYachtDto); + } + + @Delete(':id') + @HttpCode(HttpStatus.NO_CONTENT) + async delete(@Param('id', ParseIntPipe) id: number) { + return this.yachtsService.delete(id); + } +} diff --git a/src/yachts/yachts.module.ts b/src/yachts/yachts.module.ts new file mode 100644 index 0000000..61c35d5 --- /dev/null +++ b/src/yachts/yachts.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { YachtsService } from './yachts.service'; + +@Module({ + providers: [YachtsService], + exports: [YachtsService], +}) +export class YachtsModule {} diff --git a/src/yacht/yacht.service.spec.ts b/src/yachts/yachts.service.spec.ts similarity index 55% rename from src/yacht/yacht.service.spec.ts rename to src/yachts/yachts.service.spec.ts index ef6a604..2076132 100644 --- a/src/yacht/yacht.service.spec.ts +++ b/src/yachts/yachts.service.spec.ts @@ -1,15 +1,15 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { YachtService } from './yacht.service'; +import { YachtsService } from './yachts.service'; -describe('YachtService', () => { - let service: YachtService; +describe('YachtsService', () => { + let service: YachtsService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [YachtService], + providers: [YachtsService], }).compile(); - service = module.get(YachtService); + service = module.get(YachtsService); }); it('should be defined', () => { diff --git a/src/yachts/yachts.service.ts b/src/yachts/yachts.service.ts new file mode 100644 index 0000000..6b83d89 --- /dev/null +++ b/src/yachts/yachts.service.ts @@ -0,0 +1,95 @@ +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { Yacht } from './yacht.entity'; +import { CreateYachtDto } from './dto/create-yacht.dto'; +import { UpdateYachtDto } from './dto/update-yacht.dto'; + +@Injectable() +export class YachtsService { + private readonly logger = new Logger(YachtsService.name); + + private yachts: Yacht[] = [ + { + yachtId: 1, + name: 'Sea Dream', + model: 'Sunseeker 76', + year: 2020, + length: 76, + userId: 1, + createdAt: new Date('2023-01-15'), + updatedAt: new Date('2023-01-15'), + }, + { + yachtId: 2, + name: 'Ocean Breeze', + model: 'Princess 68', + year: 2021, + length: 68, + userId: 1, + createdAt: new Date('2023-02-20'), + updatedAt: new Date('2023-02-20'), + }, + { + yachtId: 3, + name: 'Wave Rider', + model: 'Ferretti 70', + year: 2019, + length: 70, + userId: 2, + createdAt: new Date('2023-03-10'), + updatedAt: new Date('2023-03-10'), + }, + ]; + + async findAll(): Promise { + return this.yachts; + } + + async findById(yachtId: number): Promise { + const yacht = this.yachts.find((y) => y.yachtId === yachtId); + if (!yacht) { + throw new NotFoundException(`Yacht with ID ${yachtId} not found`); + } + return yacht; + } + + async findByUserId(userId: number): Promise { + return this.yachts.filter((y) => y.userId === userId); + } + + async create(createYachtDto: CreateYachtDto): Promise { + const newYacht: Yacht = { + yachtId: this.yachts.length + 1, + ...createYachtDto, + createdAt: new Date(), + updatedAt: new Date(), + }; + this.yachts.push(newYacht); + return newYacht; + } + + async update( + yachtId: number, + updateYachtDto: UpdateYachtDto, + ): Promise { + const index = this.yachts.findIndex((y) => y.yachtId === yachtId); + if (index === -1) { + throw new NotFoundException(`Yacht with ID ${yachtId} not found`); + } + + this.yachts[index] = { + ...this.yachts[index], + ...updateYachtDto, + updatedAt: new Date(), + }; + + return this.yachts[index]; + } + + async delete(yachtId: number): Promise { + const index = this.yachts.findIndex((y) => y.yachtId === yachtId); + if (index === -1) { + throw new NotFoundException(`Yacht with ID ${yachtId} not found`); + } + this.yachts.splice(index, 1); + } +} diff --git a/uploads/1765725754099-656590480.jpg b/uploads/1765725754099-656590480.jpg new file mode 100644 index 0000000..0498d39 Binary files /dev/null and b/uploads/1765725754099-656590480.jpg differ diff --git a/uploads/1765725783913-362909271.jpg b/uploads/1765725783913-362909271.jpg new file mode 100644 index 0000000..0498d39 Binary files /dev/null and b/uploads/1765725783913-362909271.jpg differ