update: add slug field to the danak service entity
This commit is contained in:
@@ -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"]
|
|
||||||
+3
-3
@@ -7,13 +7,13 @@ import { ConfigModule } from "@nestjs/config";
|
|||||||
import { ThrottlerModule } from "@nestjs/throttler";
|
import { ThrottlerModule } from "@nestjs/throttler";
|
||||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
import { MailerModule } from "@nestjs-modules/mailer";
|
import { MailerModule } from "@nestjs-modules/mailer";
|
||||||
// import { TelegrafModule } from "nestjs-telegraf";
|
import { TelegrafModule } from "nestjs-telegraf";
|
||||||
|
|
||||||
import { bullMqConfig } from "./configs/bullmq.config";
|
import { bullMqConfig } from "./configs/bullmq.config";
|
||||||
import { cacheConfig } from "./configs/cache.config";
|
import { cacheConfig } from "./configs/cache.config";
|
||||||
import { mailerConfig } from "./configs/mailer.config";
|
import { mailerConfig } from "./configs/mailer.config";
|
||||||
import { rateLimitConfig } from "./configs/rateLimit.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 { databaseConfigs } from "./configs/typeorm.config";
|
||||||
import { HTTPLogger } from "./core/middlewares/logger.middleware";
|
import { HTTPLogger } from "./core/middlewares/logger.middleware";
|
||||||
import { AddressModule } from "./modules/address/address.module";
|
import { AddressModule } from "./modules/address/address.module";
|
||||||
@@ -43,7 +43,7 @@ import { MonitoringModule } from "./monitoring/monitoring.module";
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
// TelegrafModule.forRootAsync(telegrafConfig()),
|
TelegrafModule.forRootAsync(telegrafConfig()),
|
||||||
MailerModule.forRootAsync(mailerConfig()),
|
MailerModule.forRootAsync(mailerConfig()),
|
||||||
BullModule.forRootAsync(bullMqConfig()),
|
BullModule.forRootAsync(bullMqConfig()),
|
||||||
ThrottlerModule.forRootAsync(rateLimitConfig()),
|
ThrottlerModule.forRootAsync(rateLimitConfig()),
|
||||||
|
|||||||
@@ -198,6 +198,10 @@ export const enum ServiceMessage {
|
|||||||
SHOW_IN_SLIDER_BOOLEAN = "وضعیت نمایش در اسلایدر سرویس باید یک بولین باشد",
|
SHOW_IN_SLIDER_BOOLEAN = "وضعیت نمایش در اسلایدر سرویس باید یک بولین باشد",
|
||||||
COVER_URL_REQUIRED = "آدرس عکس کاور سرویس مورد نیاز است",
|
COVER_URL_REQUIRED = "آدرس عکس کاور سرویس مورد نیاز است",
|
||||||
COVER_URL_SHOULD_BE_URL = "آدرس عکس کاور سرویس باید یک آدرس یو آر ال باشد",
|
COVER_URL_SHOULD_BE_URL = "آدرس عکس کاور سرویس باید یک آدرس یو آر ال باشد",
|
||||||
|
SLUG_REQUIRED = "شناسه منحصر به فرد سرویس مورد نیاز است",
|
||||||
|
SLUG_STRING = "شناسه منحصر به فرد سرویس باید یک رشته باشد",
|
||||||
|
SLUG_LENGTH = "شناسه منحصر به فرد سرویس باید بین ۳ تا ۱۵۰ کاراکتر باشد",
|
||||||
|
SERVICE_ALREADY_EXISTS_WITH_THIS_SLUG = "سرویسی با این اسلاگ قبلا ثبت شده است",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum AnnouncementMessage {
|
export const enum AnnouncementMessage {
|
||||||
@@ -731,6 +735,7 @@ export const enum BlogMessage {
|
|||||||
SLUG_REQUIRED = "اسلاگ بلاگ الزامی است",
|
SLUG_REQUIRED = "اسلاگ بلاگ الزامی است",
|
||||||
SLUG_MAX_LENGTH = "حداکثر طول اسلاگ بلاگ باید ۲۵۵ کاراکتر باشد",
|
SLUG_MAX_LENGTH = "حداکثر طول اسلاگ بلاگ باید ۲۵۵ کاراکتر باشد",
|
||||||
CATEGORY_HAS_BLOGS = "دسته بندی مورد نظر دارای بلاگ میباشد",
|
CATEGORY_HAS_BLOGS = "دسته بندی مورد نظر دارای بلاگ میباشد",
|
||||||
|
BLOG_ALREADY_EXISTS_WITH_THIS_SLUG = "بلاگی با این اسلاگ قبلا ثبت شده است",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum SliderMessage {
|
export const enum SliderMessage {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions {
|
|||||||
username: configService.getOrThrow<string>("DB_USER"),
|
username: configService.getOrThrow<string>("DB_USER"),
|
||||||
password: configService.getOrThrow<string>("DB_PASS"),
|
password: configService.getOrThrow<string>("DB_PASS"),
|
||||||
autoLoadEntities: true,
|
autoLoadEntities: true,
|
||||||
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : false,
|
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
||||||
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
||||||
migrationsTableName: "typeorm_migrations",
|
migrationsTableName: "typeorm_migrations",
|
||||||
migrationsRun: false,
|
migrationsRun: false,
|
||||||
|
|||||||
@@ -111,6 +111,9 @@ export class BlogsService {
|
|||||||
|
|
||||||
const category = await this.findCategoryById(createBlogDto.categoryId);
|
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 slug = this.generateSlug(createBlogDto.slug);
|
||||||
|
|
||||||
const blog = this.blogsRepository.create({
|
const blog = this.blogsRepository.create({
|
||||||
@@ -145,7 +148,7 @@ export class BlogsService {
|
|||||||
|
|
||||||
if (updateBlogDto.slug) {
|
if (updateBlogDto.slug) {
|
||||||
const existBlog = await this.blogsRepository.findOne({ where: { slug: updateBlogDto.slug, id: Not(id) } });
|
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({
|
await this.blogsRepository.save({
|
||||||
|
|||||||
@@ -103,4 +103,11 @@ export class CreateServiceDto {
|
|||||||
@IsUrl({ protocols: ["http", "https"], require_protocol: true }, { message: ServiceMessage.COVER_URL_SHOULD_BE_URL })
|
@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" })
|
@ApiProperty({ description: "The cover url of the service", example: "https://example.com/cover.png" })
|
||||||
coverUrl?: string;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 { DanakServiceCategory } from "./danak-service-category.entity";
|
||||||
import { DanakServiceImage } from "./danak-service-image.entity";
|
import { DanakServiceImage } from "./danak-service-image.entity";
|
||||||
@@ -13,10 +13,15 @@ import { ServicesLanguage } from "../enums/services-language.enum";
|
|||||||
@Entity()
|
@Entity()
|
||||||
export class DanakService extends BaseEntity {
|
export class DanakService extends BaseEntity {
|
||||||
@Column({ type: "varchar", length: 150, nullable: false, unique: true })
|
@Column({ type: "varchar", length: 150, nullable: false, unique: true })
|
||||||
|
@Index()
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 150, nullable: true })
|
@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 })
|
@Column({ type: "boolean", nullable: false, default: false })
|
||||||
isDanakSuggest: boolean;
|
isDanakSuggest: boolean;
|
||||||
@@ -55,7 +60,7 @@ export class DanakService extends BaseEntity {
|
|||||||
icon: string;
|
icon: string;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255, nullable: true })
|
@Column({ type: "varchar", length: 255, nullable: true })
|
||||||
coverUrl: string;
|
coverUrl: string | null;
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
@ManyToOne(() => DanakServiceCategory, (danakServiceCategory) => danakServiceCategory.danakServices, {
|
@ManyToOne(() => DanakServiceCategory, (danakServiceCategory) => danakServiceCategory.danakServices, {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
|||||||
@@ -271,11 +271,13 @@ export class DanakServicesService {
|
|||||||
const existService = await this.danakServicesRepository.findOneByName(createDto.name);
|
const existService = await this.danakServicesRepository.findOneByName(createDto.name);
|
||||||
if (existService) throw new BadRequestException(ServiceMessage.NAME_EXIST);
|
if (existService) throw new BadRequestException(ServiceMessage.NAME_EXIST);
|
||||||
|
|
||||||
|
const slug = this.generateSlug(createDto.slug ?? createDto.title);
|
||||||
|
|
||||||
const images = createDto.images.map((image) => {
|
const images = createDto.images.map((image) => {
|
||||||
return { imageUrl: 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);
|
await this.danakServicesRepository.save(service);
|
||||||
return {
|
return {
|
||||||
message: CommonMessage.CREATED,
|
message: CommonMessage.CREATED,
|
||||||
@@ -314,7 +316,17 @@ export class DanakServicesService {
|
|||||||
images = newImages;
|
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 {
|
return {
|
||||||
message: CommonMessage.UPDATE_SUCCESS,
|
message: CommonMessage.UPDATE_SUCCESS,
|
||||||
service,
|
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) {
|
private async checkUserPurchasedService(userId: string, serviceId: string) {
|
||||||
const subscription = await this.danakServicesRepository
|
const subscription = await this.danakServicesRepository
|
||||||
.createQueryBuilder()
|
.createQueryBuilder()
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ export class DiscountsService {
|
|||||||
count,
|
count,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Soft delete a discount
|
* Soft delete a discount
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user