From 8fe1d398a6777f3379a004528b39eb5a1583be26 Mon Sep 17 00:00:00 2001 From: mahyargdz Date: Sun, 4 May 2025 11:31:53 +0330 Subject: [PATCH] update: add slug field to the danak service entity --- prometheus/prometheus.yml | 14 ---------- src/app.module.ts | 6 ++-- src/common/enums/message.enum.ts | 5 ++++ src/configs/typeorm.config.ts | 2 +- src/modules/blogs/providers/blogs.service.ts | 5 +++- .../danak-services/DTO/create-service.dto.ts | 7 +++++ .../entities/danak-service.entity.ts | 11 ++++++-- .../providers/danak-services.service.ts | 28 +++++++++++++++++-- .../discounts/providers/discounts.service.ts | 1 + 9 files changed, 55 insertions(+), 24 deletions(-) delete mode 100644 prometheus/prometheus.yml diff --git a/prometheus/prometheus.yml b/prometheus/prometheus.yml deleted file mode 100644 index 1c783bd..0000000 --- a/prometheus/prometheus.yml +++ /dev/null @@ -1,14 +0,0 @@ -global: - scrape_interval: 15s - evaluation_interval: 15s - -scrape_configs: - - job_name: "nestjs" - static_configs: - - targets: ["localhost:3000"] - metrics_path: "/metrics" - scrape_interval: 5s - - - job_name: "prometheus" - static_configs: - - targets: ["localhost:9090"] diff --git a/src/app.module.ts b/src/app.module.ts index 40d7251..443884e 100755 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,13 +7,13 @@ import { ConfigModule } from "@nestjs/config"; import { ThrottlerModule } from "@nestjs/throttler"; import { TypeOrmModule } from "@nestjs/typeorm"; import { MailerModule } from "@nestjs-modules/mailer"; -// import { TelegrafModule } from "nestjs-telegraf"; +import { TelegrafModule } from "nestjs-telegraf"; import { bullMqConfig } from "./configs/bullmq.config"; import { cacheConfig } from "./configs/cache.config"; import { mailerConfig } from "./configs/mailer.config"; import { rateLimitConfig } from "./configs/rateLimit.config"; -// import { telegrafConfig } from "./configs/telegraf.config"; +import { telegrafConfig } from "./configs/telegraf.config"; import { databaseConfigs } from "./configs/typeorm.config"; import { HTTPLogger } from "./core/middlewares/logger.middleware"; import { AddressModule } from "./modules/address/address.module"; @@ -43,7 +43,7 @@ import { MonitoringModule } from "./monitoring/monitoring.module"; @Module({ imports: [ - // TelegrafModule.forRootAsync(telegrafConfig()), + TelegrafModule.forRootAsync(telegrafConfig()), MailerModule.forRootAsync(mailerConfig()), BullModule.forRootAsync(bullMqConfig()), ThrottlerModule.forRootAsync(rateLimitConfig()), diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 0313c90..dd63f8a 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -198,6 +198,10 @@ export const enum ServiceMessage { SHOW_IN_SLIDER_BOOLEAN = "وضعیت نمایش در اسلایدر سرویس باید یک بولین باشد", COVER_URL_REQUIRED = "آدرس عکس کاور سرویس مورد نیاز است", COVER_URL_SHOULD_BE_URL = "آدرس عکس کاور سرویس باید یک آدرس یو آر ال باشد", + SLUG_REQUIRED = "شناسه منحصر به فرد سرویس مورد نیاز است", + SLUG_STRING = "شناسه منحصر به فرد سرویس باید یک رشته باشد", + SLUG_LENGTH = "شناسه منحصر به فرد سرویس باید بین ۳ تا ۱۵۰ کاراکتر باشد", + SERVICE_ALREADY_EXISTS_WITH_THIS_SLUG = "سرویسی با این اسلاگ قبلا ثبت شده است", } export const enum AnnouncementMessage { @@ -731,6 +735,7 @@ export const enum BlogMessage { SLUG_REQUIRED = "اسلاگ بلاگ الزامی است", SLUG_MAX_LENGTH = "حداکثر طول اسلاگ بلاگ باید ۲۵۵ کاراکتر باشد", CATEGORY_HAS_BLOGS = "دسته بندی مورد نظر دارای بلاگ می‌باشد", + BLOG_ALREADY_EXISTS_WITH_THIS_SLUG = "بلاگی با این اسلاگ قبلا ثبت شده است", } export const enum SliderMessage { diff --git a/src/configs/typeorm.config.ts b/src/configs/typeorm.config.ts index 9391ab2..8d8378f 100755 --- a/src/configs/typeorm.config.ts +++ b/src/configs/typeorm.config.ts @@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions { username: configService.getOrThrow("DB_USER"), password: configService.getOrThrow("DB_PASS"), autoLoadEntities: true, - synchronize: configService.getOrThrow("NODE_ENV") == "production" ? false : false, + synchronize: configService.getOrThrow("NODE_ENV") == "production" ? false : true, logging: configService.getOrThrow("NODE_ENV") == "production" ? false : true, migrationsTableName: "typeorm_migrations", migrationsRun: false, diff --git a/src/modules/blogs/providers/blogs.service.ts b/src/modules/blogs/providers/blogs.service.ts index 4897d5e..61715da 100755 --- a/src/modules/blogs/providers/blogs.service.ts +++ b/src/modules/blogs/providers/blogs.service.ts @@ -111,6 +111,9 @@ export class BlogsService { const category = await this.findCategoryById(createBlogDto.categoryId); + const existSlug = await this.blogsRepository.findOne({ where: { slug: createBlogDto.slug } }); + if (existSlug) throw new BadRequestException(BlogMessage.BLOG_ALREADY_EXISTS_WITH_THIS_SLUG); + const slug = this.generateSlug(createBlogDto.slug); const blog = this.blogsRepository.create({ @@ -145,7 +148,7 @@ export class BlogsService { if (updateBlogDto.slug) { const existBlog = await this.blogsRepository.findOne({ where: { slug: updateBlogDto.slug, id: Not(id) } }); - if (existBlog) throw new BadRequestException(BlogMessage.BLOG_ALREADY_EXISTS); + if (existBlog) throw new BadRequestException(BlogMessage.BLOG_ALREADY_EXISTS_WITH_THIS_SLUG); } await this.blogsRepository.save({ diff --git a/src/modules/danak-services/DTO/create-service.dto.ts b/src/modules/danak-services/DTO/create-service.dto.ts index c42cc26..6f82adb 100755 --- a/src/modules/danak-services/DTO/create-service.dto.ts +++ b/src/modules/danak-services/DTO/create-service.dto.ts @@ -103,4 +103,11 @@ export class CreateServiceDto { @IsUrl({ protocols: ["http", "https"], require_protocol: true }, { message: ServiceMessage.COVER_URL_SHOULD_BE_URL }) @ApiProperty({ description: "The cover url of the service", example: "https://example.com/cover.png" }) coverUrl?: string; + + @IsOptional() + @IsNotEmpty({ message: ServiceMessage.SLUG_REQUIRED }) + @IsString({ message: ServiceMessage.SLUG_STRING }) + @Length(3, 150, { message: ServiceMessage.SLUG_LENGTH }) + @ApiProperty({ description: "The slug of the service", example: "service-slug" }) + slug?: string; } diff --git a/src/modules/danak-services/entities/danak-service.entity.ts b/src/modules/danak-services/entities/danak-service.entity.ts index a9242f1..bfe7411 100755 --- a/src/modules/danak-services/entities/danak-service.entity.ts +++ b/src/modules/danak-services/entities/danak-service.entity.ts @@ -1,4 +1,4 @@ -import { Column, DeleteDateColumn, Entity, ManyToOne, OneToMany } from "typeorm"; +import { Column, DeleteDateColumn, Entity, Index, ManyToOne, OneToMany } from "typeorm"; import { DanakServiceCategory } from "./danak-service-category.entity"; import { DanakServiceImage } from "./danak-service-image.entity"; @@ -13,10 +13,15 @@ import { ServicesLanguage } from "../enums/services-language.enum"; @Entity() export class DanakService extends BaseEntity { @Column({ type: "varchar", length: 150, nullable: false, unique: true }) + @Index() name: string; @Column({ type: "varchar", length: 150, nullable: true }) - title: string; + title: string | null; + + @Column({ type: "varchar", length: 150, nullable: true, unique: true }) + @Index() + slug: string | null; @Column({ type: "boolean", nullable: false, default: false }) isDanakSuggest: boolean; @@ -55,7 +60,7 @@ export class DanakService extends BaseEntity { icon: string; @Column({ type: "varchar", length: 255, nullable: true }) - coverUrl: string; + coverUrl: string | null; //------------------------------------- @ManyToOne(() => DanakServiceCategory, (danakServiceCategory) => danakServiceCategory.danakServices, { nullable: false, diff --git a/src/modules/danak-services/providers/danak-services.service.ts b/src/modules/danak-services/providers/danak-services.service.ts index 37111c3..6cb75a3 100755 --- a/src/modules/danak-services/providers/danak-services.service.ts +++ b/src/modules/danak-services/providers/danak-services.service.ts @@ -271,11 +271,13 @@ export class DanakServicesService { const existService = await this.danakServicesRepository.findOneByName(createDto.name); if (existService) throw new BadRequestException(ServiceMessage.NAME_EXIST); + const slug = this.generateSlug(createDto.slug ?? createDto.title); + const images = createDto.images.map((image) => { return { imageUrl: image }; }); - const service = this.danakServicesRepository.create({ ...createDto, category, images }); + const service = this.danakServicesRepository.create({ ...createDto, category, images, slug }); await this.danakServicesRepository.save(service); return { message: CommonMessage.CREATED, @@ -314,7 +316,17 @@ export class DanakServicesService { images = newImages; } - await this.danakServicesRepository.save({ ...service, ...updateDto, images: images.length ? images : service.images }); + if (updateDto.slug) { + const existSlug = await this.danakServicesRepository.findOne({ where: { slug: updateDto.slug, id: Not(serviceId) } }); + if (existSlug) throw new BadRequestException(ServiceMessage.SERVICE_ALREADY_EXISTS_WITH_THIS_SLUG); + } + + await this.danakServicesRepository.save({ + ...service, + ...updateDto, + slug: this.generateSlug(updateDto.slug ?? service.slug ?? service.name), + images: images.length ? images : service.images, + }); return { message: CommonMessage.UPDATE_SUCCESS, service, @@ -555,6 +567,18 @@ export class DanakServicesService { } /******************************************** */ + private generateSlug(slug: string): string { + if (!slug) return ""; + + // Replace spaces and special characters with hyphens, preserve Farsi characters + return slug + .trim() + .replace(/[&\\#,+()$~%.'":*?<>{}]/g, "") // Remove special characters + .replace(/\s+/g, "-") // Replace spaces with hyphens + .replace(/-+/g, "-") // Replace multiple hyphens with single hyphen + .trim(); + } + private async checkUserPurchasedService(userId: string, serviceId: string) { const subscription = await this.danakServicesRepository .createQueryBuilder() diff --git a/src/modules/discounts/providers/discounts.service.ts b/src/modules/discounts/providers/discounts.service.ts index 2fa708b..7478846 100755 --- a/src/modules/discounts/providers/discounts.service.ts +++ b/src/modules/discounts/providers/discounts.service.ts @@ -175,6 +175,7 @@ export class DiscountsService { count, }; } + /** * Soft delete a discount */