This commit is contained in:
Иван 2025-12-14 18:38:31 +03:00
parent 72ccebd570
commit 980d0956cf
22 changed files with 258 additions and 174 deletions

View File

@ -1,14 +1,14 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { AppService } from './app.service'; import { AppService } from './app.service';
import { YachtModule } from './yacht/yacht.module'; import { YachtsModule } from './yachts/yachts.module';
import { CatalogModule } from './catalog/catalog.module'; import { CatalogModule } from './catalog/catalog.module';
import { AuthModule } from './auth/auth.module'; import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module'; import { UsersModule } from './users/users.module';
import { FilesModule } from './files/files.module'; import { FilesModule } from './files/files.module';
@Module({ @Module({
imports: [YachtModule, CatalogModule, AuthModule, UsersModule, FilesModule], imports: [YachtsModule, CatalogModule, AuthModule, UsersModule, FilesModule],
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService],
}) })

View File

@ -1,5 +1,6 @@
import { Injectable, Logger } from '@nestjs/common'; 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'; import { JwtService } from '@nestjs/jwt';
@Injectable() @Injectable()

View File

@ -2,7 +2,7 @@ import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport'; import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common'; import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import type { User } from 'src/users/users.service'; import type { User } from 'src/users/user.entity';
@Injectable() @Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) { export class LocalStrategy extends PassportStrategy(Strategy) {

View File

@ -6,7 +6,6 @@ import { CatalogResponseDto } from './dto/catalog-response.dto';
@Injectable() @Injectable()
export class CatalogService { export class CatalogService {
private catalogItems: CatalogItemDto[] = [ private catalogItems: CatalogItemDto[] = [
// SAMPLE DATA
{ {
id: 1, id: 1,
name: 'Item 1', name: 'Item 1',
@ -15,6 +14,9 @@ export class CatalogService {
minCost: 50, minCost: 50,
hasQuickRent: true, hasQuickRent: true,
pictureUrl: 'https://example.com/item1.jpg', pictureUrl: 'https://example.com/item1.jpg',
isFeatured: true,
mainImageUrl: '',
galleryUrls: [],
}, },
{ {
id: 2, id: 2,
@ -24,6 +26,9 @@ export class CatalogService {
minCost: 50, minCost: 50,
hasQuickRent: false, hasQuickRent: false,
pictureUrl: 'https://example.com/item1.jpg', pictureUrl: 'https://example.com/item1.jpg',
isFeatured: false,
mainImageUrl: '',
galleryUrls: [],
}, },
{ {
id: 3, id: 3,
@ -33,6 +38,9 @@ export class CatalogService {
minCost: 50, minCost: 50,
hasQuickRent: true, hasQuickRent: true,
pictureUrl: 'https://example.com/item1.jpg', pictureUrl: 'https://example.com/item1.jpg',
isFeatured: false,
mainImageUrl: '',
galleryUrls: [],
}, },
{ {
id: 4, id: 4,
@ -42,6 +50,33 @@ export class CatalogService {
minCost: 50, minCost: 50,
hasQuickRent: true, hasQuickRent: true,
pictureUrl: 'https://example.com/item1.jpg', 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: [],
}, },
]; ];

View File

@ -6,4 +6,7 @@ export class CatalogItemDto {
minCost: number; minCost: number;
hasQuickRent: boolean; hasQuickRent: boolean;
pictureUrl: string; pictureUrl: string;
isFeatured: boolean;
mainImageUrl: string;
galleryUrls: string[];
} }

View File

@ -11,6 +11,15 @@ async function bootstrap() {
app.setGlobalPrefix(''); 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() const config = new DocumentBuilder()
.setTitle('Travelmarine backend') .setTitle('Travelmarine backend')
.setDescription('Backend API for Travelmarine service') .setDescription('Backend API for Travelmarine service')

View File

@ -1,4 +1,4 @@
import { Yacht } from '../yacht/yacht.entity'; import { Yacht } from '../yachts/yacht.entity';
export type User = { export type User = {
userId: number; userId: number;

View File

@ -1,13 +1,11 @@
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { YachtService } from '../yacht/yacht.service'; import { YachtsService } from '../yachts/yachts.service';
import { User } from './user.entity'; import { User } from './user.entity';
@Injectable() @Injectable()
export class UsersService { export class UsersService {
private readonly logger = new Logger(UsersService.name); private readonly logger = new Logger(UsersService.name);
constructor(private readonly yachtsService: YachtService) {}
private readonly users: User[] = [ private readonly users: User[] = [
{ {
userId: 1, userId: 1,
@ -16,7 +14,6 @@ export class UsersService {
phone: '+79009009090', phone: '+79009009090',
email: 'email@email.com', email: 'email@email.com',
password: 'admin', password: 'admin',
yachts: [],
}, },
{ {
userId: 2, userId: 2,
@ -25,19 +22,18 @@ export class UsersService {
phone: '+79009009090', phone: '+79009009090',
email: 'email1@email.com', email: 'email1@email.com',
password: 'admin', password: 'admin',
yachts: [],
}, },
]; ];
async findOne( async findOne(
email: string, email: string,
includeYachts = false, includeYachts: boolean = false,
): Promise<User | undefined> { ): Promise<User | undefined> {
this.logger.log({ email });
const user = this.users.find((user) => user.email === email); const user = this.users.find((user) => user.email === email);
if (user && includeYachts) { if (user && includeYachts) {
user.yachts = await this.yachtsService.findByUserId(user.userId); // Fetch yachts for this user
user.yachts = await [];
} }
return user; return user;
@ -45,37 +41,30 @@ export class UsersService {
async findById( async findById(
userId: number, userId: number,
includeYachts = false, includeYachts: boolean = false,
): Promise<User | undefined> { ): Promise<User | undefined> {
const user = this.users.find((user) => user.userId === userId); const user = this.users.find((user) => user.userId === userId);
if (user && includeYachts) { if (user && includeYachts) {
user.yachts = await this.yachtsService.findByUserId(user.userId); user.yachts = [];
} }
return user; return user;
} }
async findAll(includeYachts = false): Promise<User[]> { async findAll(includeYachts: boolean = false): Promise<User[]> {
const users = [...this.users]; if (!includeYachts) {
return this.users;
if (includeYachts) {
for (const user of users) {
user.yachts = await this.yachtsService.findByUserId(user.userId);
}
} }
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<User, 'userId'>): Promise<User> { return usersWithYachts;
const newUser: User = {
userId: this.users.length + 1,
...userData,
};
this.users.push(newUser);
this.logger.log(`Created new user: ${newUser.email}`);
return newUser;
} }
} }

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -1,8 +0,0 @@
import { Module } from '@nestjs/common';
import { YachtService } from './yacht.service';
@Module({
providers: [YachtService],
exports: [YachtService],
})
export class YachtsModule {}

View File

@ -1,64 +0,0 @@
import { Injectable } from '@nestjs/common';
import { Yacht } from './yacht.entity';
@Injectable()
export class YachtService {
private yachts: Yacht[] = [];
async findAll(): Promise<Yacht[]> {
return this.yachts;
}
async findById(yachtId: number): Promise<Yacht | undefined> {
return this.yachts.find((yacht) => yacht.id === yachtId);
}
async findByUserId(userId: number): Promise<Yacht[]> {
return this.yachts.filter((yacht) => yacht.userId === userId);
}
async create(
yachtData: Omit<Yacht, 'yachtId' | 'createdAt' | 'updatedAt'>,
): Promise<Yacht> {
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<Yacht>,
): Promise<Yacht | undefined> {
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<boolean> {
const index = this.yachts.findIndex((yacht) => yacht.id === yachtId);
if (index === -1) {
return false;
}
this.yachts.splice(index, 1);
return true;
}
}

View File

@ -0,0 +1,7 @@
export class CreateYachtDto {
name: string;
model: string;
year: number;
length: number;
userId: number;
}

View File

@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateYachtDto } from './create-yacht.dto';
export class UpdateYachtDto extends PartialType(CreateYachtDto) {}

View File

@ -0,0 +1,10 @@
export type Yacht = {
yachtId: number;
name: string;
model: string;
year: number;
length: number;
userId: number;
createdAt: Date;
updatedAt: Date;
};

View File

@ -1,15 +1,15 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { YachtController } from './yacht.controller'; import { YachtsController } from './yacht.controller';
describe('YachtController', () => { describe('YachtController', () => {
let controller: YachtController; let controller: YachtsController;
beforeEach(async () => { beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
controllers: [YachtController], controllers: [YachtsController],
}).compile(); }).compile();
controller = module.get<YachtController>(YachtController); controller = module.get<YachtsController>(YachtsController);
}); });
it('should be defined', () => { it('should be defined', () => {

View File

@ -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);
}
}

View File

@ -0,0 +1,8 @@
import { Module } from '@nestjs/common';
import { YachtsService } from './yachts.service';
@Module({
providers: [YachtsService],
exports: [YachtsService],
})
export class YachtsModule {}

View File

@ -1,15 +1,15 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { YachtService } from './yacht.service'; import { YachtsService } from './yachts.service';
describe('YachtService', () => { describe('YachtsService', () => {
let service: YachtService; let service: YachtsService;
beforeEach(async () => { beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
providers: [YachtService], providers: [YachtsService],
}).compile(); }).compile();
service = module.get<YachtService>(YachtService); service = module.get<YachtsService>(YachtsService);
}); });
it('should be defined', () => { it('should be defined', () => {

View File

@ -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<Yacht[]> {
return this.yachts;
}
async findById(yachtId: number): Promise<Yacht> {
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<Yacht[]> {
return this.yachts.filter((y) => y.userId === userId);
}
async create(createYachtDto: CreateYachtDto): Promise<Yacht> {
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<Yacht> {
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<void> {
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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB