Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
PORT=5000
SALT=secret
DB_USER = test
DB_PASSWORD = test
DB_HOST=127.0.0.2
DB_PORT=27017
DB_NAME=test_db
JWT_SECRET = 'secret key'
UPLOAD_FILE_DIRECTORY='\src\uploads'
HOST='localhost'
STATIC_DIRECTORY_PATH = '\src\static'
14 changes: 14 additions & 0 deletions Workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ npm start

Советы и инструкции по внесению изменений в учебный репозиторий.

### Переменные окружения
Проект использует переменные окружения для конфигурации. Создайте файл `.env` в корне проекта на основе `.env.example`.

`PORT` -- Порт HTTP-сервера. По умолчанию - `3000`
`SALT` -- Соль для хеширования паролей
`DB_USER` -- Пользователь для подключения к БД
`DB_PASSWORD` -- Пароль пользователя БД
`DB_HOST` -- Адрес сервера базы данных
`DB_PORT` -- Порт базы данных
`DB_NAME` -- Имя базы данных
`JWT_SECRET` -- Секретный ключ для подписи JWT
`UPLOAD_FILE_DIRECTORY` - Путь для загрузки файлов пользователями
`HOST` -- Хост сервера
`STATIC_DIRECTORY_PATH` -- Путь к директории со статикой
### Остальное

Все остальные файлы в проекте являются служебными. Пожалуйста, не удаляйте и не изменяйте их самовольно. Только если того требует задание или наставник.
2 changes: 1 addition & 1 deletion custom.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TokenPayload } from "./src/shared/modules/auth/types/TokenPayload.js";
import { TokenPayload } from "./src/shared/modules/auth/types/token-payload.ts";

declare module "express-serve-static-core" {
export interface Request {
Expand Down
25 changes: 16 additions & 9 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
version: '3.9'

services:
db:
image: mongo:4.2
restart: always
container_name: six-cities-mongodb
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: test
MONGO_INITDB_ROOT_USERNAME: ${DB_USER:-admin}
MONGO_INITDB_ROOT_PASSWORD: ${DB_PASSWORD:-test}
ports:
- 27017:27017
- "${DB_PORT:-27017}:27017"
volumes:
- six-cities_data:/data/db
networks:
- app-network

db_ui:
image: mongo-express
restart: always
container_name: six-cities-mongo-express
ports:
- 8081:8081
- "${MONGO_EXPRESS_PORT:-8081}:8081"
environment:
ME_CONFIG_BASICAUTH_USERNAME: admin
ME_CONFIG_BASICAUTH_PASSWORD: test
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: test
ME_CONFIG_MONGODB_URL: mongodb://admin:test@db:27017/
ME_CONFIG_BASICAUTH_USERNAME: ${ME_USER:-admin}
ME_CONFIG_BASICAUTH_PASSWORD: ${ME_PASSWORD:-test}
ME_CONFIG_MONGODB_ADMINUSERNAME: ${DB_USER:-admin}
ME_CONFIG_MONGODB_ADMINPASSWORD: ${DB_PASSWORD:-test}
ME_CONFIG_MONGODB_URL: mongodb://${DB_USER:-admin}:${DB_PASSWORD:-test}@db:27017/
depends_on:
- db
networks:
- app-network

volumes:
six-cities_data:
1 change: 0 additions & 1 deletion queries.http

This file was deleted.

File renamed without changes.
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'reflect-metadata';
import { CliApplication } from './cli/cli-application.js';
import { Command } from './cli/command.interfaсe.js';
import { Command } from './cli/command.interface.js';
import { GenerateCommand } from './cli/generate.command.js';
import { HelpCommand } from './cli/help.command.js';
import { ImportCommand } from './cli/import.command.js';
Expand Down
2 changes: 1 addition & 1 deletion src/cli/cli-application.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CommandParser } from './command-parser.js';
import { Command } from './command.interfaсe.js';
import { Command } from './command.interface.js';

type CollectionCommand = Record<string, Command>;

Expand Down
File renamed without changes.
3 changes: 1 addition & 2 deletions src/cli/generate.command.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import got from 'got';
import { MockServerData } from '../shared/types/mock-server.js';
import { Command } from './command.interfaсe.js';
import { Command } from './command.interface.js';
import { appendFile } from 'node:fs/promises';
import { TSVOfferGenerator } from '../shared/libs/offer-generator/offer-generator.js';

Expand Down Expand Up @@ -29,7 +29,6 @@ export class GenerateCommand implements Command {
}

async execute(...parameters: string[]): Promise<void> {
console.log(parameters);
const [count, path, url] = parameters;
const offerCount = parseInt(count, 10);

Expand Down
2 changes: 1 addition & 1 deletion src/cli/help.command.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from './command.interfaсe.js';
import { Command } from './command.interface.js';

export class HelpCommand implements Command {
getName(): string {
Expand Down
41 changes: 28 additions & 13 deletions src/cli/import.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@
import { OfferService } from '../shared/modules/offer/offer-service.js';
import { UserService } from '../shared/modules/user/user-service.js';
import { Offer } from '../shared/types/offer.js';
import { Command } from './command.interfaсe.js';
import { DEFAULT_DB_PORT, DEFAULT_USER_PASSWORD } from './const.cli.js';
import { OfferEntity } from '../shared/modules/offer/offer.entity.js';
import { UserEntity } from '../shared/modules/user/user.entity.js';
import { CommentEntity } from '../shared/modules/comment/comment.entity.js';
import {
OfferEntity,
OfferModel,
} from '../shared/modules/offer/offer.entity.js';
import { UserEntity, UserModel } from '../shared/modules/user/user.entity.js';
import {
CommentEntity,
CommentModel,
} from '../shared/modules/comment/comment.entity.js';
import { Command } from './command.interface.js';

export class ImportCommand implements Command {
private salt: string;
Expand All @@ -29,17 +35,24 @@

constructor() {
this.logger = new ConsoleLogger();
this.databaseClient = new MongoDatabaseClient(this.logger);
}

public getName(): string {
return '--import';
}

private initModels() {
this.userModel = UserModel;
this.offerModel = OfferModel;
this.commentModel = CommentModel;

this.userService = new UserService(this.logger, this.userModel);
this.offerService = new OfferService(
this.logger,
this.offerModel,
this.commentModel,
);
this.databaseClient = new MongoDatabaseClient(this.logger);
}

public getName(): string {
return '--import';
}

private async onImportedLine(line: string, resolve: () => void) {
Expand All @@ -48,17 +61,17 @@
resolve();
}

private onCompletedImport(count: number) {
console.log(`Imported ${count} offers`);
private onCompletedImport() {
this.databaseClient.disconnect();
}

private async saveOffer(offer: Offer) {
const { id: _id, host, ...offerData } = offer;

Check warning on line 69 in src/cli/import.command.ts

View workflow job for this annotation

GitHub Actions / Check

'_id' is assigned a value but never used
const user = await this.userService.register(
{ ...offer.host, password: DEFAULT_USER_PASSWORD },
{ ...host, password: DEFAULT_USER_PASSWORD },
this.salt,
);
await this.offerService.createOffer({ ...offer, host: user.id });
await this.offerService.createOffer({ ...offerData, host: user.id });
}

public async execute(
Expand All @@ -74,6 +87,8 @@

await this.databaseClient.connect(dbURI);

this.initModels();

const fileReader = new TSVFileReader(filename.trim());

fileReader.on('line', this.onImportedLine.bind(this));
Expand Down
11 changes: 5 additions & 6 deletions src/cli/version.command.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { Command } from './command.interfaсe.js';
import { Command } from './command.interface.js';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';

type PackageJsonConfig = {
version: string;
};

const isPackageJsonConfig = (value: unknown): value is PackageJsonConfig => (
const isPackageJsonConfig = (value: unknown): value is PackageJsonConfig =>
typeof value === 'object' &&
value !== null &&
!Array.isArray(value) &&
Object.hasOwn(value, 'version')
);
value !== null &&
!Array.isArray(value) &&
Object.hasOwn(value, 'version');

export class VersionCommand implements Command {
constructor(private filePath: string = './package.json') {}
Expand Down
Empty file removed src/main.ts
Empty file.
11 changes: 5 additions & 6 deletions src/mock/mock.tsv
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
id title description postDate city previewImage images isPremium isFavorite rating type bedrooms maxAdults price goods hostId hostName hostEmail hostAvatar hostPassword hostIsPro locationLat locationLng commentsCount
1 Cozy apartment Уютная квартира в центре с прекрасным видом на город 2026-01-20 Paris https://example.com/preview1.jpg https://example.com/img1.jpg|https://example.com/img2.jpg|https://example.com/img3.jpg|https://example.com/img4.jpg|https://example.com/img5.jpg|https://example.com/img6.jpg true false 4.7 apartment 2 4 5000 Breakfast|Air conditioning|Laptop friendly workspace 1 John Doe john@example.com https://example.com/avatar1.jpg john-password true 48.8566 2.3522 12
2 Luxury villa Премиум вилла с бассейном и садом для больших компаний 2026-01-18 Amsterdam https://example.com/preview2.jpg https://example.com/img7.jpg|https://example.com/img8.jpg|https://example.com/img9.jpg|https://example.com/img10.jpg|https://example.com/img11.jpg|https://example.com/img12.jpg true true 4.9 house 4 8 25000 Breakfast|Washer|Towels|Fridge 2 Maria Schmidt maria@example.com https://example.com/avatar2.jpg maria-password false 52.3702 4.8952 8
3 Student room Бюджетная комната для студентов рядом с университетом 2026-01-22 Cologne https://example.com/preview3.jpg https://example.com/img13.jpg|https://example.com/img14.jpg|https://example.com/img15.jpg|https://example.com/img16.jpg|https://example.com/img17.jpg|https://example.com/img18.jpg false false 3.8 room 1 2 1200 Laptop friendly workspace 3 Ahmed Ali ahmed@example.com https://example.com/avatar3.jpg ahmed-password true 50.9375 6.9603 5
4 Hotel suite Современный номер в отеле с видом на канал 2026-01-15 Brussels https://example.com/preview4.jpg https://example.com/img19.jpg|https://example.com/img20.jpg|https://example.com/img21.jpg|https://example.com/img22.jpg|https://example.com/img23.jpg|https://example.com/img24.jpg false true 4.2 hotel 1 3 8000 Air conditioning|Baby seat|Towels 4 Emma Dubois emma@example.com https://example.com/avatar4.jpg emma-password true 50.8503 4.3517 23
5 Family house Семейный дом в тихом районе с детской комнатой 2026-01-19 Hamburg https://example.com/preview5.jpg https://example.com/img25.jpg|https://example.com/img26.jpg|https://example.com/img27.jpg|https://example.com/img28.jpg|https://example.com/img29.jpg|https://example.com/img30.jpg true false 4.5 house 3 6 15000 Breakfast|Baby seat|Washer|Fridge 5 Sophie Müller sophie@example.com https://example.com/avatar5.jpg sophie-password false 53.5511 9.9937 17
1 Cozy apartment Уютная квартира в центре с прекрасным видом на город и удобной планировкой 2026-01-20 Paris https://example.com/preview1.jpg https://example.com/img1.jpg|https://example.com/img2.jpg|https://example.com/img3.jpg|https://example.com/img4.jpg|https://example.com/img5.jpg|https://example.com/img6.jpg true false 4.7 apartment 2 4 5000 Breakfast|Air conditioning|Laptop friendly workspace 1 John Doe john@example.com https://example.com/avatar1.jpg john12 true 48.8566 2.3522 12
2 Luxury villa Премиум вилла с бассейном и садом для больших компаний и длительного отдыха 2026-01-18 Amsterdam https://example.com/preview2.jpg https://example.com/img7.jpg|https://example.com/img8.jpg|https://example.com/img9.jpg|https://example.com/img10.jpg|https://example.com/img11.jpg|https://example.com/img12.jpg true true 4.9 house 4 8 25000 Breakfast|Washer|Towels|Fridge 2 Maria Schmidt maria@example.com https://example.com/avatar2.jpg maria12 false 52.3702 4.8952 8
3 Student room Бюджетная комната для студентов рядом с университетом и общественным транспортом 2026-01-22 Cologne https://example.com/preview3.jpg https://example.com/img13.jpg|https://example.com/img14.jpg|https://example.com/img15.jpg|https://example.com/img16.jpg|https://example.com/img17.jpg|https://example.com/img18.jpg false false 4.0 room 1 2 1200 Laptop friendly workspace 3 Ahmed Ali ahmed@example.com https://example.com/avatar3.jpg ahmed12 true 50.9375 6.9603 5
4 Hotel suite Современный номер в отеле с видом на канал и завтраком по расписанию 2026-01-15 Brussels https://example.com/preview4.jpg https://example.com/img19.jpg|https://example.com/img20.jpg|https://example.com/img21.jpg|https://example.com/img22.jpg|https://example.com/img23.jpg|https://example.com/img24.jpg false true 4.2 hotel 1 3 8000 Air conditioning|Baby seat|Towels 4 Emma Dubois emma@example.com https://example.com/avatar4.jpg emma12 true 50.8503 4.3517 23
5 Family house Семейный дом в тихом районе с детской комнатой и просторной кухней 2026-01-19 Hamburg https://example.com/preview5.jpg https://example.com/img25.jpg|https://example.com/img26.jpg|https://example.com/img27.jpg|https://example.com/img28.jpg|https://example.com/img29.jpg|https://example.com/img30.jpg true false 4.5 house 3 6 15000 Breakfast|Baby seat|Washer|Fridge 5 Sophie Müller sophie@example.com https://example.com/avatar5.jpg soph12 false 53.5511 9.9937 17
4 changes: 2 additions & 2 deletions src/shared/helpers/hash.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as crypto from 'node:crypto';

export const createSha256 = (line: string, solt: string) => {
const shaHasher = crypto.createHmac('sha256', solt);
export const createSha256 = (line: string, salt: string) => {
const shaHasher = crypto.createHmac('sha256', salt);
return shaHasher.update(line).digest('hex');
};
3 changes: 2 additions & 1 deletion src/shared/helpers/offer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Amenity, City, HousingType, Offer } from '../types/offer.js';

export const createOffer = (offerData: string): Offer => {
const [
// id,
id,
title,
description,
postDate,
Expand All @@ -29,6 +29,7 @@ export const createOffer = (offerData: string): Offer => {
] = offerData.replace('\n', '').split('\t');

return {
id: id,
title: title,
description: description,
date: new Date(postDate),
Expand Down
15 changes: 8 additions & 7 deletions src/shared/libs/logger/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { Logger } from './logger.interface.js';
import { Logger as PinoInstance, pino, transport } from 'pino';
import { injectable } from 'inversify';

const LOG_FILE_PATH = 'logs/rest.log';

@injectable()
export class PinoLogger implements Logger {
private readonly logger: PinoInstance;

constructor() {
const modulePath = getCurrentModulePath();
const logFilePath = 'logs/rest.log';
const destination = resolve(modulePath,'../../../', logFilePath);
const destination = resolve(modulePath, '../../../', LOG_FILE_PATH);

const multiTransport = transport({
targets: [
Expand All @@ -23,26 +24,26 @@ export class PinoLogger implements Logger {
},
{
target: 'pino-pretty',
}
},
],
});

this.logger = pino({}, multiTransport);
}

info(message: string, ...args: unknown[]): void {
this.logger.info({meta: args}, message);
this.logger.info({ meta: args }, message);
}

debug(message: string, ...args: unknown[]): void {
this.logger.debug({meta: args}, message);
this.logger.debug({ meta: args }, message);
}

warn(message: string, ...args: unknown[]): void {
this.logger.warn({meta: args}, message);
this.logger.warn({ meta: args }, message);
}

error(message: string, ...args: unknown[]): void {
this.logger.error({meta: args}, message);
this.logger.error({ meta: args }, message);
}
}
52 changes: 52 additions & 0 deletions src/shared/libs/rest/middleware/offer-owner.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Middleware } from './middleware.interface.js';
import { OfferServiceInterface } from '../../../modules/offer/offer-service.interface.js';
import { Request, Response, NextFunction } from 'express';
import { StatusCodes } from 'http-status-codes';
import { HttpError } from '../errors/index.js';

export class IsOfferOwnerMiddleware implements Middleware {
constructor(private readonly service: OfferServiceInterface) {}

async execute(
req: Request,
_res: Response,
next: NextFunction,
): Promise<void> {
const userId = req.tokenPayload.id;
const { offerId } = req.params;

const offer = await this.service.findOfferById(offerId as string);

if (!offer) {
return next(
new HttpError(
StatusCodes.NOT_FOUND,
'Offer not found',
'IsOfferOwnerMiddleware',
),
);
}

if (!userId) {
return next(
new HttpError(
StatusCodes.UNAUTHORIZED,
'Unauthorized',
'IsOfferOwnerMiddleware',
),
);
}

if (offer.host.toString() !== userId) {
return next(
new HttpError(
StatusCodes.FORBIDDEN,
'You are not the owner of this offer',
'IsOfferOwnerMiddleware',
),
);
}

return next();
}
}
Loading
Loading