From 99114aff672c2acf7e4536594b15488a343fae89 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 18 May 2026 19:10:30 +0300 Subject: [PATCH 1/6] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B3=D0=BE=D1=82=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0?= =?UTF-8?q?=20=D0=BA=20=D0=B7=D0=B0=D1=89=D0=B8=D1=82=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rest/rest.application.ts | 8 ++- src/rest/rest.container.ts | 5 ++ src/shared/helpers/common.ts | 4 ++ src/shared/libs/config/rest.config.schema.ts | 14 ++++ .../controller/base-controller.abstract.ts | 12 +++- .../rest/middleware/upload-file.middleware.ts | 16 +++-- src/shared/libs/transform/path-transformer.ts | 62 ++++++++++++++++++ .../libs/transform/transformer.const.ts | 3 + src/shared/modules/comment/comment-service.ts | 7 +- src/shared/modules/offer/dto/offer-dto.ts | 2 +- src/shared/modules/offer/offer-controller.ts | 30 ++++++++- .../modules/offer/offer-service.interface.ts | 2 +- src/shared/modules/offer/offer-service.ts | 6 +- src/shared/modules/offer/offer.entity.ts | 8 +-- src/shared/modules/user/dto/user-dto.ts | 11 +--- src/shared/modules/user/user-controller.ts | 28 ++++++++ src/shared/modules/user/user-service.ts | 9 ++- src/shared/modules/user/user.constant.ts | 1 + src/shared/types/container.ts | 1 + src/static/default-avatar.jpg | Bin 0 -> 9099 bytes 20 files changed, 195 insertions(+), 34 deletions(-) create mode 100644 src/shared/libs/transform/path-transformer.ts create mode 100644 src/shared/libs/transform/transformer.const.ts create mode 100644 src/shared/modules/user/user.constant.ts create mode 100644 src/static/default-avatar.jpg diff --git a/src/rest/rest.application.ts b/src/rest/rest.application.ts index d23f675..7f43bc3 100644 --- a/src/rest/rest.application.ts +++ b/src/rest/rest.application.ts @@ -8,6 +8,7 @@ import express, { Express } from 'express'; import { Controller } from '../shared/libs/rest/controller/controller.interface.js'; import { ExceptionFilter } from '../shared/libs/rest/exception-filter/exception-filter.interface.js'; import { ParseTokenMiddleware } from '../shared/libs/rest/middleware/parse-token.middleware.js'; +import { getFullServerPath } from '../shared/helpers/common.js'; @injectable() export class RestApplication { @@ -57,6 +58,11 @@ export class RestApplication { '/upload', express.static(this.config.get('UPLOAD_FILE_DIRECTORY')), ); + + this.server.use( + '/static', + express.static(this.config.get('STATIC_DIRECTORY_PATH')), + ); } private async _initControllers() { @@ -91,7 +97,7 @@ export class RestApplication { await this.__initExceptionFilters(); this.logger.info( - `🚀 Server started on http://localhost:${this.config.get('PORT')}`, + `🚀 Server started on ${getFullServerPath(this.config.get('HOST'), this.config.get('PORT'))}`, ); this.logger.info('Database initialization'); diff --git a/src/rest/rest.container.ts b/src/rest/rest.container.ts index 2b31faa..80b213a 100644 --- a/src/rest/rest.container.ts +++ b/src/rest/rest.container.ts @@ -9,6 +9,7 @@ import { } from '../shared/libs/database-client/index.js'; import { ExceptionFilter } from '../shared/libs/rest/exception-filter/exception-filter.interface.js'; import { AppExceptionFilter } from '../shared/libs/rest/exception-filter/app-exception-filter.js'; +import { PathTransformer } from '../shared/libs/transform/path-transformer.js'; export function createRestContainer() { const restApplicationContainer = new Container(); @@ -32,6 +33,10 @@ export function createRestContainer() { .bind(Component.ExceptionFilter) .to(AppExceptionFilter) .inSingletonScope(); + restApplicationContainer + .bind(Component.PathTransformer) + .to(PathTransformer) + .inSingletonScope(); return restApplicationContainer; } diff --git a/src/shared/helpers/common.ts b/src/shared/helpers/common.ts index 1643ffa..ee06918 100644 --- a/src/shared/helpers/common.ts +++ b/src/shared/helpers/common.ts @@ -29,3 +29,7 @@ export function fillDTO(dto: ClassConstructor, plainObject: V) { export function createErrorObject(message: string) { return { error: message }; } + +export function getFullServerPath(host: string, port: number) { + return `http://${host}:${port}`; +} diff --git a/src/shared/libs/config/rest.config.schema.ts b/src/shared/libs/config/rest.config.schema.ts index a318da0..7fa9001 100644 --- a/src/shared/libs/config/rest.config.schema.ts +++ b/src/shared/libs/config/rest.config.schema.ts @@ -11,6 +11,8 @@ export type RestSchema = { DB_NAME: string; UPLOAD_FILE_DIRECTORY: string; JWT_SECRET: string; + HOST: string; + STATIC_DIRECTORY_PATH: string; }; convict.addFormats(validator); @@ -70,4 +72,16 @@ export const restSchema = convict({ env: 'JWT_SECRET', default: null, }, + HOST: { + doc: 'Host name', + format: String, + env: 'HOST', + default: null, + }, + STATIC_DIRECTORY_PATH: { + doc: 'Static directory path', + format: String, + env: 'STATIC_DIRECTORY_PATH', + default: null, + }, }); diff --git a/src/shared/libs/rest/controller/base-controller.abstract.ts b/src/shared/libs/rest/controller/base-controller.abstract.ts index d130324..427f8f1 100644 --- a/src/shared/libs/rest/controller/base-controller.abstract.ts +++ b/src/shared/libs/rest/controller/base-controller.abstract.ts @@ -1,10 +1,12 @@ -import { injectable } from 'inversify'; +import { inject, injectable } from 'inversify'; import { Controller } from './controller.interface.js'; import { Response, Router } from 'express'; import { Route } from '../types/route.interface.js'; import { StatusCodes } from 'http-status-codes'; import { Logger } from '../../logger/index.js'; import expressAsyncHandler from 'express-async-handler'; +import { Component } from '../../../types/container.js'; +import { PathTransformer } from '../../transform/path-transformer.js'; const DEFAULT_CONTENT_TYPE = 'application/json'; @@ -16,6 +18,9 @@ export abstract class BaseController implements Controller { this._router = Router(); } + @inject(Component.PathTransformer) + protected readonly pathTransformer: PathTransformer; + get router(): Router { return this._router; } @@ -37,7 +42,10 @@ export abstract class BaseController implements Controller { } public send(res: Response, statusCode: number, data: T) { - res.type(DEFAULT_CONTENT_TYPE).status(statusCode).json(data); + const modifiedData = this.pathTransformer.execute( + data as Record, + ); + res.type(DEFAULT_CONTENT_TYPE).status(statusCode).json(modifiedData); } public created(res: Response, data: T) { diff --git a/src/shared/libs/rest/middleware/upload-file.middleware.ts b/src/shared/libs/rest/middleware/upload-file.middleware.ts index 3d0de85..383e4de 100644 --- a/src/shared/libs/rest/middleware/upload-file.middleware.ts +++ b/src/shared/libs/rest/middleware/upload-file.middleware.ts @@ -1,16 +1,22 @@ import { NextFunction, Request, Response } from 'express'; -import { Middleware } from '../index.js'; import multer, { diskStorage } from 'multer'; import { extension } from 'mime-types'; import * as crypto from 'node:crypto'; +import { Middleware } from './middleware.interface.js'; export class UploadFileMiddleware implements Middleware { constructor( - private readonly fieldName: string, - private readonly uploadDirectory: string, + private uploadDirectory: string, + private fieldName: string, ) {} - execute(req: Request, res: Response, next: NextFunction): void { + public async execute( + req: Request, + res: Response, + next: NextFunction, + ): Promise { + console.log(this.uploadDirectory); + console.log(this.fieldName); const storage = diskStorage({ destination: this.uploadDirectory, filename: (_req, file, callback) => { @@ -20,6 +26,8 @@ export class UploadFileMiddleware implements Middleware { }, }); + console.log(storage); + const uploadSingleFileMiddleware = multer({ storage }).single( this.fieldName, ); diff --git a/src/shared/libs/transform/path-transformer.ts b/src/shared/libs/transform/path-transformer.ts new file mode 100644 index 0000000..2452170 --- /dev/null +++ b/src/shared/libs/transform/path-transformer.ts @@ -0,0 +1,62 @@ +import { inject, injectable } from 'inversify'; +import { Logger } from '../logger/index.js'; +import { Component } from '../../types/container.js'; +import { Config, RestSchema } from '../config/index.js'; +import { + DEFAULT_STATIC_IMAGES, + STATIC_RESOURCE_FIELDS, +} from './transformer.const.js'; +import { getFullServerPath } from '../../helpers/common.js'; + +function isObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +@injectable() +export class PathTransformer { + constructor( + @inject(Component.Logger) private readonly logger: Logger, + @inject(Component.Config) private readonly config: Config, + ) { + this.logger.info('PathTransformer was created'); + } + + private hasDefaultImage(value: string) { + return DEFAULT_STATIC_IMAGES.includes(value); + } + + private isStaticProperty(property: string) { + return STATIC_RESOURCE_FIELDS.includes(property); + } + + public execute(data: Record) { + const stack = [data]; + while (stack.length > 0) { + const current = stack.pop(); + for (const key in current) { + if (Object.hasOwn(current, key)) { + const value = current[key]; + + if (isObject(value)) { + stack.push(value); + continue; + } + + if (this.isStaticProperty(key) && typeof value === 'string') { + const staticPath = '/static'; + const uploadPath = '/upload'; + const serverHost = this.config.get('HOST'); + const serverPort = this.config.get('PORT'); + const rootPath = this.hasDefaultImage(value) + ? staticPath + : uploadPath; + current[key] = + `${getFullServerPath(serverHost, serverPort)}${rootPath}/${value}`; + } + } + } + } + + return data; + } +} diff --git a/src/shared/libs/transform/transformer.const.ts b/src/shared/libs/transform/transformer.const.ts new file mode 100644 index 0000000..b113565 --- /dev/null +++ b/src/shared/libs/transform/transformer.const.ts @@ -0,0 +1,3 @@ +export const DEFAULT_STATIC_IMAGES = ['default-avatar.jpg']; + +export const STATIC_RESOURCE_FIELDS = ['avatar', 'imagePreview', 'images']; diff --git a/src/shared/modules/comment/comment-service.ts b/src/shared/modules/comment/comment-service.ts index de1e4eb..b071eb1 100644 --- a/src/shared/modules/comment/comment-service.ts +++ b/src/shared/modules/comment/comment-service.ts @@ -31,7 +31,12 @@ export class CommentService implements CommentServiceInterface { public async findByOfferId( offerId: string, ): Promise[]> { - return this.commentModel.find({ offerId }).populate('author'); + return this.commentModel + .find({ offerId }) + .populate('author') + .limit(50) + .sort({ date: -1 }) + .exec(); } public async deleteByOfferId(offerId: string): Promise { diff --git a/src/shared/modules/offer/dto/offer-dto.ts b/src/shared/modules/offer/dto/offer-dto.ts index e9aa503..f630359 100644 --- a/src/shared/modules/offer/dto/offer-dto.ts +++ b/src/shared/modules/offer/dto/offer-dto.ts @@ -72,7 +72,7 @@ export class CreateOfferDto { @IsArray({ message: OfferDTOValidationMessage.goods.isArray }) goods!: Amenity[]; - @IsObject({ message: OfferDTOValidationMessage.host.isObject }) + @IsString({ message: OfferDTOValidationMessage.host.isObject }) host!: string; @IsInt({ message: OfferDTOValidationMessage.commentsCount.isInt }) diff --git a/src/shared/modules/offer/offer-controller.ts b/src/shared/modules/offer/offer-controller.ts index 0c67e7d..85b4369 100644 --- a/src/shared/modules/offer/offer-controller.ts +++ b/src/shared/modules/offer/offer-controller.ts @@ -62,6 +62,20 @@ export class OfferController extends BaseController { new DocumentExistsMiddleware('offerId', 'offer', this.offerService), ], }); + this.addRoute({ + path: '/premium', + method: HttpMethod.Get, + handler: this.showPremiumOffers, + }); + this.addRoute({ + path: '/:offerId', + method: HttpMethod.Get, + handler: this.getDetailOffers, + middlewares: [ + new ValidateObjectIdMiddleware('offerId'), + new DocumentExistsMiddleware('offerId', 'offer', this.offerService), + ], + }); this.addRoute({ path: '/:offerId/comments', method: HttpMethod.Get, @@ -74,7 +88,8 @@ export class OfferController extends BaseController { } public async index(req: Request, res: Response) { - const offers = await this.offerService.find(); + const limit = Number(req.params.limit) || 60; + const offers = await this.offerService.find(limit); let favoritesIds: string[] = []; if (req.tokenPayload) { @@ -105,11 +120,18 @@ export class OfferController extends BaseController { const result = await this.offerService.createOffer({ ...req.body, - userId: req.tokenPayload.id, + host: req.tokenPayload.id, }); this.created(res, fillDTO(OfferRDO, result)); } + public async getDetailOffers(req: Request, res: Response) { + const offer = await this.offerService.findOfferById( + req.params.offerId as string, + ); + this.ok(res, fillDTO(OfferRDO, offer)); + } + public async update(req: Request, res: Response) { const updateOffer = await this.offerService.updateById( req.params.id as string, @@ -138,13 +160,15 @@ export class OfferController extends BaseController { const city = req.params.city as City; const premiumOffers = await this.offerService.findPremiumOffersByCity(city); + console.log(premiumOffers); + this.ok(res, fillDTO(OfferRDO, premiumOffers)); } public async getComments(req: Request, res: Response) { const offerId = req.params.offerId; - const comments = this.commentService.findByOfferId(offerId as string); + const comments = await this.commentService.findByOfferId(offerId as string); this.ok(res, fillDTO(OfferRDO, comments)); } diff --git a/src/shared/modules/offer/offer-service.interface.ts b/src/shared/modules/offer/offer-service.interface.ts index 6f85822..7cc5e9b 100644 --- a/src/shared/modules/offer/offer-service.interface.ts +++ b/src/shared/modules/offer/offer-service.interface.ts @@ -15,5 +15,5 @@ export interface OfferServiceInterface { incCommentCount(offerId: string): Promise | null>; decCommentCount(offerId: string): Promise | null>; findPremiumOffersByCity(city: City): Promise[]>; - find(): Promise[]>; + find(limit: number): Promise[]>; } diff --git a/src/shared/modules/offer/offer-service.ts b/src/shared/modules/offer/offer-service.ts index 8ad7b2b..026f0c1 100644 --- a/src/shared/modules/offer/offer-service.ts +++ b/src/shared/modules/offer/offer-service.ts @@ -63,8 +63,8 @@ export class OfferService implements OfferServiceInterface, DocumentExists { .exec(); } - async find(): Promise[]> { - return this.offerModel.find().populate('host').exec(); + async find(limit: number): Promise[]> { + return this.offerModel.find().populate('host').limit(limit).exec(); } async findPremiumOffersByCity( @@ -72,6 +72,8 @@ export class OfferService implements OfferServiceInterface, DocumentExists { ): Promise[]> { return this.offerModel .find({ city, isPremium: true }) + .sort({ date: -1 }) + .limit(3) .populate('host') .exec(); } diff --git a/src/shared/modules/offer/offer.entity.ts b/src/shared/modules/offer/offer.entity.ts index b0df816..c397e12 100644 --- a/src/shared/modules/offer/offer.entity.ts +++ b/src/shared/modules/offer/offer.entity.ts @@ -5,7 +5,7 @@ import { prop, Ref, } from '@typegoose/typegoose'; -import { Amenity, City, HousingType } from '../../types/offer.js'; +import { Amenity, City, HousingType, Location } from '../../types/offer.js'; import { UserEntity } from '../user/user.entity.js'; // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging @@ -17,10 +17,6 @@ export interface OfferEntity extends defaultClasses.Base {} timestamps: true, }, }) -class Location { - public latitude!: number; - public longitude!: number; -} // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging export class OfferEntity extends defaultClasses.TimeStamps { @@ -104,7 +100,7 @@ export class OfferEntity extends defaultClasses.TimeStamps { @prop({ required: true }) public commentsCount!: number; - @prop({ _id: false, type: () => Location, required: true }) + @prop({ _id: false, required: true }) public location!: Location; } diff --git a/src/shared/modules/user/dto/user-dto.ts b/src/shared/modules/user/dto/user-dto.ts index 90906cc..a6da1f3 100644 --- a/src/shared/modules/user/dto/user-dto.ts +++ b/src/shared/modules/user/dto/user-dto.ts @@ -1,10 +1,4 @@ -import { - IsBoolean, - IsEmail, - IsString, - MaxLength, - MinLength, -} from 'class-validator'; +import { IsBoolean, IsEmail, MaxLength, MinLength } from 'class-validator'; import { UserDTOValidationMessage } from './user-dto.messages.js'; export class CreateUserDto { @@ -15,9 +9,6 @@ export class CreateUserDto { @IsEmail({}, { message: UserDTOValidationMessage.email.isEmail }) public email: string; - @IsString({ message: UserDTOValidationMessage.avatar.isString }) - public avatar: string; - @MinLength(6, { message: UserDTOValidationMessage.password.min }) @MaxLength(12, { message: UserDTOValidationMessage.password.max }) public password: string; diff --git a/src/shared/modules/user/user-controller.ts b/src/shared/modules/user/user-controller.ts index 6979ca3..978aedd 100644 --- a/src/shared/modules/user/user-controller.ts +++ b/src/shared/modules/user/user-controller.ts @@ -14,6 +14,7 @@ import { ValidateDTOMiddleware } from '../../libs/rest/middleware/validate-objec import { CreateUserDto, LoginUserDTO } from './dto/user-dto.js'; import { AuthServiceInterface, UserNotFoundException } from '../auth/index.js'; import { PrivateRouteMiddleware } from '../../libs/rest/middleware/private-route.middleware.js'; +import { UploadFileMiddleware } from '../../libs/rest/middleware/upload-file.middleware.js'; export class UserController extends BaseController { constructor( @@ -39,10 +40,23 @@ export class UserController extends BaseController { middlewares: [new ValidateDTOMiddleware(LoginUserDTO)], }); + this.addRoute({ + path: '/:userId/favorite', + method: HttpMethod.Get, + handler: this.getFavorites, + middlewares: [new PrivateRouteMiddleware()], + }); + this.addRoute({ path: '/:userId/avatar', method: HttpMethod.Post, handler: this.uploadAvatar, + middlewares: [ + new UploadFileMiddleware( + 'avatar', + this.config.get('UPLOAD_FILE_DIRECTORY'), + ), + ], }); this.addRoute({ @@ -70,6 +84,20 @@ export class UserController extends BaseController { this.created(res, fillDTO(UserRDO, result)); } + public async getFavorites(req: Request, res: Response) { + const user = await this.userService.findById(req.tokenPayload.id); + if (!user) { + throw new HttpError( + StatusCodes.UNAUTHORIZED, + 'Unauthorized', + 'UserController', + ); + } + const favorites = await this.userService.findFavoriteOffers(user.id); + + this.ok(res, favorites); + } + public async auth(req: Request, res: Response) { const user = await this.authService.verify(req.body as LoginUserDTO); diff --git a/src/shared/modules/user/user-service.ts b/src/shared/modules/user/user-service.ts index 9412d7f..df5ec79 100644 --- a/src/shared/modules/user/user-service.ts +++ b/src/shared/modules/user/user-service.ts @@ -6,6 +6,7 @@ import { inject, injectable } from 'inversify'; import { Component } from '../../types/container.js'; import { Logger } from '../../libs/logger/index.js'; import { OfferEntity } from '../offer/offer.entity.js'; +import { DEFAULT_AVATAR_FILE_NAME } from './user.constant.js'; @injectable() export class UserService implements UserServiceInterface { @@ -18,7 +19,7 @@ export class UserService implements UserServiceInterface { dto: CreateUserDto, salt: string, ): Promise> { - const user = new UserEntity(dto); + const user = new UserEntity({ ...dto, avatar: DEFAULT_AVATAR_FILE_NAME }); user.setPassword(dto.password, salt); const result = await UserModel.create(user); @@ -38,8 +39,10 @@ export class UserService implements UserServiceInterface { async findFavoriteOffers( userId: number, ): Promise[]> { - const user = await UserModel.findById(userId).populate('favorites').exec(); - + const user = await this.userModel + .findById(userId) + .populate('favorites') + .exec(); return (user?.favorites as DocumentType[]) ?? []; } diff --git a/src/shared/modules/user/user.constant.ts b/src/shared/modules/user/user.constant.ts new file mode 100644 index 0000000..29633af --- /dev/null +++ b/src/shared/modules/user/user.constant.ts @@ -0,0 +1 @@ +export const DEFAULT_AVATAR_FILE_NAME = 'default-avatar.jpg'; diff --git a/src/shared/types/container.ts b/src/shared/types/container.ts index 7125145..2abbcfa 100644 --- a/src/shared/types/container.ts +++ b/src/shared/types/container.ts @@ -15,4 +15,5 @@ export const Component = { CommentController: Symbol.for('CommentController'), AuthService: Symbol.for('AuthService'), AuthExceptionFilter: Symbol.for('AuthExceptionFilter'), + PathTransformer: Symbol.for('PathTransformer'), } as const; diff --git a/src/static/default-avatar.jpg b/src/static/default-avatar.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ef5daf07473922d53d59c0662a68044e5b17f8e2 GIT binary patch literal 9099 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 literal 0 HcmV?d00001 From 56954ccbbda8a33868c0fdef61a87f9954b9cc33 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 23 May 2026 20:50:24 +0300 Subject: [PATCH 2/6] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=BA=D0=BE=D0=B4=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../libs/rest/middleware/upload-file.middleware.ts | 14 +++++--------- src/shared/modules/comment/comment-service.ts | 4 ++-- src/shared/modules/comment/comment.const.ts | 1 + src/shared/modules/offer/offer-controller.ts | 5 ++++- src/shared/modules/offer/offer-service.ts | 3 ++- src/shared/modules/offer/offer.const.ts | 1 + src/shared/modules/user/user-controller.ts | 2 +- 7 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 src/shared/modules/comment/comment.const.ts create mode 100644 src/shared/modules/offer/offer.const.ts diff --git a/src/shared/libs/rest/middleware/upload-file.middleware.ts b/src/shared/libs/rest/middleware/upload-file.middleware.ts index 383e4de..eabe033 100644 --- a/src/shared/libs/rest/middleware/upload-file.middleware.ts +++ b/src/shared/libs/rest/middleware/upload-file.middleware.ts @@ -1,8 +1,8 @@ -import { NextFunction, Request, Response } from 'express'; -import multer, { diskStorage } from 'multer'; -import { extension } from 'mime-types'; -import * as crypto from 'node:crypto'; -import { Middleware } from './middleware.interface.js'; +import { NextFunction, Request, Response } from "express"; +import multer, { diskStorage } from "multer"; +import { extension } from "mime-types"; +import * as crypto from "node:crypto"; +import { Middleware } from "./middleware.interface.js"; export class UploadFileMiddleware implements Middleware { constructor( @@ -15,8 +15,6 @@ export class UploadFileMiddleware implements Middleware { res: Response, next: NextFunction, ): Promise { - console.log(this.uploadDirectory); - console.log(this.fieldName); const storage = diskStorage({ destination: this.uploadDirectory, filename: (_req, file, callback) => { @@ -26,8 +24,6 @@ export class UploadFileMiddleware implements Middleware { }, }); - console.log(storage); - const uploadSingleFileMiddleware = multer({ storage }).single( this.fieldName, ); diff --git a/src/shared/modules/comment/comment-service.ts b/src/shared/modules/comment/comment-service.ts index b071eb1..a9bd51f 100644 --- a/src/shared/modules/comment/comment-service.ts +++ b/src/shared/modules/comment/comment-service.ts @@ -6,6 +6,7 @@ import { CommentDto } from './dto/comment-dto.js'; import { CommentEntity } from './comment.entity.js'; import { DocumentType, types } from '@typegoose/typegoose'; import { OfferServiceInterface } from '../offer/offer-service.interface.js'; +import { DISPLAY_COMMENT_COUNT } from './comment.const.js'; @injectable() export class CommentService implements CommentServiceInterface { @@ -33,8 +34,7 @@ export class CommentService implements CommentServiceInterface { ): Promise[]> { return this.commentModel .find({ offerId }) - .populate('author') - .limit(50) + .limit(DISPLAY_COMMENT_COUNT) .sort({ date: -1 }) .exec(); } diff --git a/src/shared/modules/comment/comment.const.ts b/src/shared/modules/comment/comment.const.ts new file mode 100644 index 0000000..9f0214e --- /dev/null +++ b/src/shared/modules/comment/comment.const.ts @@ -0,0 +1 @@ +export const DISPLAY_COMMENT_COUNT = 50; diff --git a/src/shared/modules/offer/offer-controller.ts b/src/shared/modules/offer/offer-controller.ts index 85b4369..4a100b9 100644 --- a/src/shared/modules/offer/offer-controller.ts +++ b/src/shared/modules/offer/offer-controller.ts @@ -18,6 +18,7 @@ import { DocumentExistsMiddleware } from '../../libs/rest/middleware/document-ex import { DocumentExists } from '../../libs/rest/types/document-exists.interface.js'; import { PrivateRouteMiddleware } from '../../libs/rest/middleware/private-route.middleware.js'; import { UserServiceInterface } from '../user/user-service.interface.js'; +import { CommentRDO } from '../comment/rdo/comment-rdo.js'; export class OfferController extends BaseController { constructor( @@ -170,6 +171,8 @@ export class OfferController extends BaseController { const comments = await this.commentService.findByOfferId(offerId as string); - this.ok(res, fillDTO(OfferRDO, comments)); + console.log(comments); + + this.ok(res, fillDTO(CommentRDO, comments)); } } diff --git a/src/shared/modules/offer/offer-service.ts b/src/shared/modules/offer/offer-service.ts index 026f0c1..6fda718 100644 --- a/src/shared/modules/offer/offer-service.ts +++ b/src/shared/modules/offer/offer-service.ts @@ -9,6 +9,7 @@ import { DocumentType, types } from '@typegoose/typegoose'; import { CommentEntity } from '../comment/comment.entity.js'; import { Types } from 'mongoose'; import { DocumentExists } from '../../libs/rest/types/document-exists.interface.js'; +import { DISPLAY_PREMIUM_OFFERS_COUNT } from './offer.const.js'; @injectable() export class OfferService implements OfferServiceInterface, DocumentExists { @@ -73,7 +74,7 @@ export class OfferService implements OfferServiceInterface, DocumentExists { return this.offerModel .find({ city, isPremium: true }) .sort({ date: -1 }) - .limit(3) + .limit(DISPLAY_PREMIUM_OFFERS_COUNT) .populate('host') .exec(); } diff --git a/src/shared/modules/offer/offer.const.ts b/src/shared/modules/offer/offer.const.ts new file mode 100644 index 0000000..5df6342 --- /dev/null +++ b/src/shared/modules/offer/offer.const.ts @@ -0,0 +1 @@ +export const DISPLAY_PREMIUM_OFFERS_COUNT = 3; diff --git a/src/shared/modules/user/user-controller.ts b/src/shared/modules/user/user-controller.ts index 978aedd..38934b0 100644 --- a/src/shared/modules/user/user-controller.ts +++ b/src/shared/modules/user/user-controller.ts @@ -53,8 +53,8 @@ export class UserController extends BaseController { handler: this.uploadAvatar, middlewares: [ new UploadFileMiddleware( - 'avatar', this.config.get('UPLOAD_FILE_DIRECTORY'), + 'avatar', ), ], }); From 5c7cf086ae99a5c2bff99a5b14922d0164b95b77 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 23 May 2026 20:52:27 +0300 Subject: [PATCH 3/6] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../libs/rest/middleware/upload-file.middleware.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/shared/libs/rest/middleware/upload-file.middleware.ts b/src/shared/libs/rest/middleware/upload-file.middleware.ts index eabe033..2e7631f 100644 --- a/src/shared/libs/rest/middleware/upload-file.middleware.ts +++ b/src/shared/libs/rest/middleware/upload-file.middleware.ts @@ -1,8 +1,8 @@ -import { NextFunction, Request, Response } from "express"; -import multer, { diskStorage } from "multer"; -import { extension } from "mime-types"; -import * as crypto from "node:crypto"; -import { Middleware } from "./middleware.interface.js"; +import { NextFunction, Request, Response } from 'express'; +import multer, { diskStorage } from 'multer'; +import { extension } from 'mime-types'; +import * as crypto from 'node:crypto'; +import { Middleware } from './middleware.interface.js'; export class UploadFileMiddleware implements Middleware { constructor( From ce97411c6bafc16d389cf2ea1c04473620f79930 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 23 May 2026 20:57:19 +0300 Subject: [PATCH 4/6] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../libs/rest/middleware/upload-file.middleware.ts | 10 +++++----- src/shared/modules/offer/offer-controller.ts | 6 ------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/shared/libs/rest/middleware/upload-file.middleware.ts b/src/shared/libs/rest/middleware/upload-file.middleware.ts index eabe033..2e7631f 100644 --- a/src/shared/libs/rest/middleware/upload-file.middleware.ts +++ b/src/shared/libs/rest/middleware/upload-file.middleware.ts @@ -1,8 +1,8 @@ -import { NextFunction, Request, Response } from "express"; -import multer, { diskStorage } from "multer"; -import { extension } from "mime-types"; -import * as crypto from "node:crypto"; -import { Middleware } from "./middleware.interface.js"; +import { NextFunction, Request, Response } from 'express'; +import multer, { diskStorage } from 'multer'; +import { extension } from 'mime-types'; +import * as crypto from 'node:crypto'; +import { Middleware } from './middleware.interface.js'; export class UploadFileMiddleware implements Middleware { constructor( diff --git a/src/shared/modules/offer/offer-controller.ts b/src/shared/modules/offer/offer-controller.ts index 4a100b9..75398d9 100644 --- a/src/shared/modules/offer/offer-controller.ts +++ b/src/shared/modules/offer/offer-controller.ts @@ -160,9 +160,6 @@ export class OfferController extends BaseController { public async showPremiumOffers(req: Request, res: Response) { const city = req.params.city as City; const premiumOffers = await this.offerService.findPremiumOffersByCity(city); - - console.log(premiumOffers); - this.ok(res, fillDTO(OfferRDO, premiumOffers)); } @@ -170,9 +167,6 @@ export class OfferController extends BaseController { const offerId = req.params.offerId; const comments = await this.commentService.findByOfferId(offerId as string); - - console.log(comments); - this.ok(res, fillDTO(CommentRDO, comments)); } } From aabb36da965be1d99fbeec42e86877bcbf4e6ccc Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 23 May 2026 20:59:54 +0300 Subject: [PATCH 5/6] =?UTF-8?q?Revert=20"=D0=98=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ce97411c6bafc16d389cf2ea1c04473620f79930. --- .../libs/rest/middleware/upload-file.middleware.ts | 10 +++++----- src/shared/modules/offer/offer-controller.ts | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/shared/libs/rest/middleware/upload-file.middleware.ts b/src/shared/libs/rest/middleware/upload-file.middleware.ts index 2e7631f..eabe033 100644 --- a/src/shared/libs/rest/middleware/upload-file.middleware.ts +++ b/src/shared/libs/rest/middleware/upload-file.middleware.ts @@ -1,8 +1,8 @@ -import { NextFunction, Request, Response } from 'express'; -import multer, { diskStorage } from 'multer'; -import { extension } from 'mime-types'; -import * as crypto from 'node:crypto'; -import { Middleware } from './middleware.interface.js'; +import { NextFunction, Request, Response } from "express"; +import multer, { diskStorage } from "multer"; +import { extension } from "mime-types"; +import * as crypto from "node:crypto"; +import { Middleware } from "./middleware.interface.js"; export class UploadFileMiddleware implements Middleware { constructor( diff --git a/src/shared/modules/offer/offer-controller.ts b/src/shared/modules/offer/offer-controller.ts index 75398d9..4a100b9 100644 --- a/src/shared/modules/offer/offer-controller.ts +++ b/src/shared/modules/offer/offer-controller.ts @@ -160,6 +160,9 @@ export class OfferController extends BaseController { public async showPremiumOffers(req: Request, res: Response) { const city = req.params.city as City; const premiumOffers = await this.offerService.findPremiumOffersByCity(city); + + console.log(premiumOffers); + this.ok(res, fillDTO(OfferRDO, premiumOffers)); } @@ -167,6 +170,9 @@ export class OfferController extends BaseController { const offerId = req.params.offerId; const comments = await this.commentService.findByOfferId(offerId as string); + + console.log(comments); + this.ok(res, fillDTO(CommentRDO, comments)); } } From 779302874eb25ae4cc5ee1465b2a60f68a801dd3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 24 May 2026 14:03:29 +0300 Subject: [PATCH 6/6] =?UTF-8?q?=D0=9F=D0=BE=D0=B8=D1=81=D0=BA=20=D0=B8=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=81=D0=BE=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D1=81=D1=82?= =?UTF-8?q?=D0=B2=D0=B8=D1=8F=20=D0=A2=D0=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/modules/offer/offer-controller.ts | 7 ++--- src/shared/modules/offer/offer.const.ts | 1 + src/shared/modules/user/rdo/user.rdo.ts | 32 ++++++++++++++++++++ src/shared/modules/user/user-controller.ts | 8 +++-- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/shared/modules/offer/offer-controller.ts b/src/shared/modules/offer/offer-controller.ts index 4a100b9..174c26d 100644 --- a/src/shared/modules/offer/offer-controller.ts +++ b/src/shared/modules/offer/offer-controller.ts @@ -19,6 +19,7 @@ import { DocumentExists } from '../../libs/rest/types/document-exists.interface. import { PrivateRouteMiddleware } from '../../libs/rest/middleware/private-route.middleware.js'; import { UserServiceInterface } from '../user/user-service.interface.js'; import { CommentRDO } from '../comment/rdo/comment-rdo.js'; +import { MAX_DISPLAY_OFFERS_COUNT } from './offer.const.js'; export class OfferController extends BaseController { constructor( @@ -89,7 +90,7 @@ export class OfferController extends BaseController { } public async index(req: Request, res: Response) { - const limit = Number(req.params.limit) || 60; + const limit = Number(req.params.limit) || MAX_DISPLAY_OFFERS_COUNT; const offers = await this.offerService.find(limit); let favoritesIds: string[] = []; @@ -161,8 +162,6 @@ export class OfferController extends BaseController { const city = req.params.city as City; const premiumOffers = await this.offerService.findPremiumOffersByCity(city); - console.log(premiumOffers); - this.ok(res, fillDTO(OfferRDO, premiumOffers)); } @@ -171,8 +170,6 @@ export class OfferController extends BaseController { const comments = await this.commentService.findByOfferId(offerId as string); - console.log(comments); - this.ok(res, fillDTO(CommentRDO, comments)); } } diff --git a/src/shared/modules/offer/offer.const.ts b/src/shared/modules/offer/offer.const.ts index 5df6342..0071f44 100644 --- a/src/shared/modules/offer/offer.const.ts +++ b/src/shared/modules/offer/offer.const.ts @@ -1 +1,2 @@ export const DISPLAY_PREMIUM_OFFERS_COUNT = 3; +export const MAX_DISPLAY_OFFERS_COUNT = 60; diff --git a/src/shared/modules/user/rdo/user.rdo.ts b/src/shared/modules/user/rdo/user.rdo.ts index 95f044d..33c743c 100644 --- a/src/shared/modules/user/rdo/user.rdo.ts +++ b/src/shared/modules/user/rdo/user.rdo.ts @@ -17,6 +17,38 @@ export class UserRDO { favorites: string[]; } +export class UserFavoriteOfferRDO { + @Expose() + title: string; + + @Expose() + date: Date; + + @Expose() + city: string; + + @Expose() + type: string; + + @Expose() + price: number; + + @Expose() + isFavorite: boolean; + + @Expose() + imagePreview: string; + + @Expose() + isPremium: boolean; + + @Expose() + rating: number; + + @Expose() + commentsCount: number; +} + export class LoggedUserRDO { @Expose() public email: string; diff --git a/src/shared/modules/user/user-controller.ts b/src/shared/modules/user/user-controller.ts index 38934b0..febc222 100644 --- a/src/shared/modules/user/user-controller.ts +++ b/src/shared/modules/user/user-controller.ts @@ -7,7 +7,11 @@ import { Logger } from '../../libs/logger/index.js'; import { UserServiceInterface } from './user-service.interface.js'; import { StatusCodes } from 'http-status-codes'; import { Config, RestSchema } from '../../libs/config/index.js'; -import { LoggedUserRDO, UserRDO } from './rdo/user.rdo.js'; +import { + LoggedUserRDO, + UserFavoriteOfferRDO, + UserRDO, +} from './rdo/user.rdo.js'; import { fillDTO } from '../../helpers/common.js'; import { HttpError } from '../../libs/rest/errors/http-error.js'; import { ValidateDTOMiddleware } from '../../libs/rest/middleware/validate-object.middleware.js'; @@ -95,7 +99,7 @@ export class UserController extends BaseController { } const favorites = await this.userService.findFavoriteOffers(user.id); - this.ok(res, favorites); + this.ok(res, fillDTO(UserFavoriteOfferRDO, favorites)); } public async auth(req: Request, res: Response) {