Add blog categories and default SUPER_ADMIN store scope to Mana Collection.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
3da8a5d4a2
commit
20e81ade19
@@ -0,0 +1,35 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "BlogCategory" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"storeId" TEXT NOT NULL,
|
||||||
|
"parentId" TEXT,
|
||||||
|
"nameFa" TEXT NOT NULL,
|
||||||
|
"nameEn" TEXT NOT NULL DEFAULT '',
|
||||||
|
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
CONSTRAINT "BlogCategory_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "BlogPost" ADD COLUMN "categoryId" TEXT;
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "BlogCategory_storeId_idx" ON "BlogCategory"("storeId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "BlogCategory_parentId_idx" ON "BlogCategory"("parentId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "BlogCategory_sortOrder_idx" ON "BlogCategory"("sortOrder");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "BlogPost_categoryId_idx" ON "BlogPost"("categoryId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "BlogCategory" ADD CONSTRAINT "BlogCategory_storeId_fkey" FOREIGN KEY ("storeId") REFERENCES "Store"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "BlogCategory" ADD CONSTRAINT "BlogCategory_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "BlogCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "BlogPost" ADD CONSTRAINT "BlogPost_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "BlogCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
+29
-8
@@ -126,6 +126,7 @@ model Store {
|
|||||||
|
|
||||||
products Product[]
|
products Product[]
|
||||||
categories Category[]
|
categories Category[]
|
||||||
|
blogCategories BlogCategory[]
|
||||||
brands Brand[]
|
brands Brand[]
|
||||||
blogs BlogPost[]
|
blogs BlogPost[]
|
||||||
orderItems OrderItem[]
|
orderItems OrderItem[]
|
||||||
@@ -219,24 +220,44 @@ model ProductVariant {
|
|||||||
@@index([productId])
|
@@index([productId])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model BlogCategory {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
storeId String
|
||||||
|
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||||||
|
parentId String?
|
||||||
|
parent BlogCategory? @relation("BlogCategoryTree", fields: [parentId], references: [id], onDelete: SetNull)
|
||||||
|
children BlogCategory[] @relation("BlogCategoryTree")
|
||||||
|
nameFa String
|
||||||
|
nameEn String @default("")
|
||||||
|
sortOrder Int @default(0)
|
||||||
|
blogs BlogPost[]
|
||||||
|
|
||||||
|
@@index([storeId])
|
||||||
|
@@index([parentId])
|
||||||
|
@@index([sortOrder])
|
||||||
|
}
|
||||||
|
|
||||||
model BlogPost {
|
model BlogPost {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
storeId String
|
storeId String
|
||||||
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
|
||||||
|
categoryId String?
|
||||||
|
category BlogCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
||||||
title String
|
title String
|
||||||
summary String @default("")
|
summary String @default("")
|
||||||
body String @default("")
|
body String @default("")
|
||||||
author String @default("")
|
author String @default("")
|
||||||
imageUrl String?
|
imageUrl String?
|
||||||
imageKey String?
|
imageKey String?
|
||||||
published Boolean @default(true)
|
published Boolean @default(true)
|
||||||
metaTitle String?
|
metaTitle String?
|
||||||
metaDescription String?
|
metaDescription String?
|
||||||
slug String?
|
slug String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
@@index([storeId])
|
@@index([storeId])
|
||||||
|
@@index([categoryId])
|
||||||
@@index([published])
|
@@index([published])
|
||||||
@@index([slug])
|
@@index([slug])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { AuthModule } from './auth/auth.module';
|
import { AuthModule } from './auth/auth.module';
|
||||||
|
import { BlogCategoriesModule } from './blog-categories/blog-categories.module';
|
||||||
import { BlogsModule } from './blogs/blogs.module';
|
import { BlogsModule } from './blogs/blogs.module';
|
||||||
import { BrandsModule } from './brands/brands.module';
|
import { BrandsModule } from './brands/brands.module';
|
||||||
import { CatalogModule } from './catalog/catalog.module';
|
import { CatalogModule } from './catalog/catalog.module';
|
||||||
@@ -29,6 +30,7 @@ import { StoresModule } from './stores/stores.module';
|
|||||||
StoresModule,
|
StoresModule,
|
||||||
ProductsModule,
|
ProductsModule,
|
||||||
CategoriesModule,
|
CategoriesModule,
|
||||||
|
BlogCategoriesModule,
|
||||||
BrandsModule,
|
BrandsModule,
|
||||||
BlogsModule,
|
BlogsModule,
|
||||||
CustomersModule,
|
CustomersModule,
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { UserRole } from '@prisma/client';
|
||||||
|
import { CurrentUser } from '../auth/decorators/current-user.decorator';
|
||||||
|
import { Roles } from '../auth/decorators/roles.decorator';
|
||||||
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||||
|
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||||
|
import type { AuthUser } from '../auth/auth.types';
|
||||||
|
import {
|
||||||
|
CreateBlogCategoryDto,
|
||||||
|
UpdateBlogCategoryDto,
|
||||||
|
} from './dto/blog-category.dto';
|
||||||
|
import { BlogCategoriesService } from './blog-categories.service';
|
||||||
|
|
||||||
|
@Controller('blog-categories')
|
||||||
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
@Roles(UserRole.STORE_ADMIN, UserRole.SUPER_ADMIN)
|
||||||
|
export class BlogCategoriesController {
|
||||||
|
constructor(private readonly categories: BlogCategoriesService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
list(
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
@Query('storeId') storeId?: string,
|
||||||
|
) {
|
||||||
|
return this.categories.list(user, storeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
create(@CurrentUser() user: AuthUser, @Body() dto: CreateBlogCategoryDto) {
|
||||||
|
return this.categories.create(user, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
update(
|
||||||
|
@CurrentUser() user: AuthUser,
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Body() dto: UpdateBlogCategoryDto,
|
||||||
|
) {
|
||||||
|
return this.categories.update(user, id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
remove(@CurrentUser() user: AuthUser, @Param('id') id: string) {
|
||||||
|
return this.categories.remove(user, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { BlogCategoriesController } from './blog-categories.controller';
|
||||||
|
import { BlogCategoriesService } from './blog-categories.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [BlogCategoriesController],
|
||||||
|
providers: [BlogCategoriesService],
|
||||||
|
exports: [BlogCategoriesService],
|
||||||
|
})
|
||||||
|
export class BlogCategoriesModule {}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
|
import { UserRole } from '@prisma/client';
|
||||||
|
import { AuthUser } from '../auth/auth.types';
|
||||||
|
import { StoreScopeService } from '../common/store-scope.service';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import {
|
||||||
|
CreateBlogCategoryDto,
|
||||||
|
UpdateBlogCategoryDto,
|
||||||
|
} from './dto/blog-category.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BlogCategoriesService {
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly storeScope: StoreScopeService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async list(actor: AuthUser, storeIdQuery?: string) {
|
||||||
|
const storeId =
|
||||||
|
actor.role === UserRole.SUPER_ADMIN
|
||||||
|
? storeIdQuery
|
||||||
|
: (await this.storeScope.requireActiveStore(actor)).id;
|
||||||
|
|
||||||
|
const categories = await this.prisma.blogCategory.findMany({
|
||||||
|
where: storeId ? { storeId } : undefined,
|
||||||
|
orderBy: [{ sortOrder: 'asc' }, { nameFa: 'asc' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.buildTree(categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(actor: AuthUser, dto: CreateBlogCategoryDto) {
|
||||||
|
const store = await this.storeScope.requireActiveStore(
|
||||||
|
actor,
|
||||||
|
dto.storeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.prisma.blogCategory.create({
|
||||||
|
data: {
|
||||||
|
storeId: store.id,
|
||||||
|
nameFa: dto.nameFa,
|
||||||
|
nameEn: dto.nameEn ?? '',
|
||||||
|
parentId: dto.parentId,
|
||||||
|
sortOrder: dto.sortOrder ?? 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(actor: AuthUser, id: string, dto: UpdateBlogCategoryDto) {
|
||||||
|
const existing = await this.prisma.blogCategory.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
if (!existing) {
|
||||||
|
throw new NotFoundException('دسته بلاگ یافت نشد');
|
||||||
|
}
|
||||||
|
await this.assertStoreAccess(actor, existing.storeId);
|
||||||
|
|
||||||
|
return this.prisma.blogCategory.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
...(dto.nameFa !== undefined ? { nameFa: dto.nameFa } : {}),
|
||||||
|
...(dto.nameEn !== undefined ? { nameEn: dto.nameEn } : {}),
|
||||||
|
...(dto.parentId !== undefined ? { parentId: dto.parentId } : {}),
|
||||||
|
...(dto.sortOrder !== undefined ? { sortOrder: dto.sortOrder } : {}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(actor: AuthUser, id: string) {
|
||||||
|
const existing = await this.prisma.blogCategory.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
if (!existing) {
|
||||||
|
throw new NotFoundException('دسته بلاگ یافت نشد');
|
||||||
|
}
|
||||||
|
await this.assertStoreAccess(actor, existing.storeId);
|
||||||
|
await this.prisma.blogCategory.delete({ where: { id } });
|
||||||
|
return { ok: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildTree<T extends { id: string; parentId: string | null }>(
|
||||||
|
items: T[],
|
||||||
|
) {
|
||||||
|
const byParent = new Map<string | null, T[]>();
|
||||||
|
for (const item of items) {
|
||||||
|
const key = item.parentId;
|
||||||
|
const list = byParent.get(key) ?? [];
|
||||||
|
list.push(item);
|
||||||
|
byParent.set(key, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
const walk = (
|
||||||
|
parentId: string | null,
|
||||||
|
): Array<T & { children: unknown[] }> =>
|
||||||
|
(byParent.get(parentId) ?? []).map((node) => ({
|
||||||
|
...node,
|
||||||
|
children: walk(node.id),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return walk(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async assertStoreAccess(actor: AuthUser, storeId: string) {
|
||||||
|
if (actor.role === UserRole.SUPER_ADMIN) return;
|
||||||
|
const store = await this.storeScope.requireActiveStore(actor);
|
||||||
|
if (store.id !== storeId) {
|
||||||
|
throw new NotFoundException('دسته بلاگ یافت نشد');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import {
|
||||||
|
IsInt,
|
||||||
|
IsOptional,
|
||||||
|
IsString,
|
||||||
|
Min,
|
||||||
|
MinLength,
|
||||||
|
} from 'class-validator';
|
||||||
|
|
||||||
|
export class CreateBlogCategoryDto {
|
||||||
|
@IsString()
|
||||||
|
@MinLength(1)
|
||||||
|
nameFa!: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
nameEn?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
parentId?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
|
@Min(0)
|
||||||
|
sortOrder?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
storeId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateBlogCategoryDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
@MinLength(1)
|
||||||
|
nameFa?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
nameEn?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
parentId?: string | null;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsInt()
|
||||||
|
@Min(0)
|
||||||
|
sortOrder?: number;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
import { UserRole } from '@prisma/client';
|
import { UserRole } from '@prisma/client';
|
||||||
import { AuthUser } from '../auth/auth.types';
|
import { AuthUser, displayName } from '../auth/auth.types';
|
||||||
import { StoreScopeService } from '../common/store-scope.service';
|
import { StoreScopeService } from '../common/store-scope.service';
|
||||||
import { slugify } from '../common/slugify';
|
import { slugify } from '../common/slugify';
|
||||||
import { PrismaService } from '../prisma/prisma.service';
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
@@ -21,12 +21,20 @@ export class BlogsService {
|
|||||||
|
|
||||||
return this.prisma.blogPost.findMany({
|
return this.prisma.blogPost.findMany({
|
||||||
where: storeId ? { storeId } : undefined,
|
where: storeId ? { storeId } : undefined,
|
||||||
|
include: {
|
||||||
|
category: { select: { id: true, nameFa: true, nameEn: true } },
|
||||||
|
},
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(actor: AuthUser, id: string) {
|
async findOne(actor: AuthUser, id: string) {
|
||||||
const post = await this.prisma.blogPost.findUnique({ where: { id } });
|
const post = await this.prisma.blogPost.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
category: { select: { id: true, nameFa: true, nameEn: true } },
|
||||||
|
},
|
||||||
|
});
|
||||||
if (!post) {
|
if (!post) {
|
||||||
throw new NotFoundException('مطلب یافت نشد');
|
throw new NotFoundException('مطلب یافت نشد');
|
||||||
}
|
}
|
||||||
@@ -46,7 +54,8 @@ export class BlogsService {
|
|||||||
title: dto.title,
|
title: dto.title,
|
||||||
summary: dto.summary ?? '',
|
summary: dto.summary ?? '',
|
||||||
body: dto.body ?? '',
|
body: dto.body ?? '',
|
||||||
author: dto.author ?? '',
|
author: dto.author?.trim() || displayName(actor),
|
||||||
|
categoryId: dto.categoryId,
|
||||||
imageUrl: dto.imageUrl,
|
imageUrl: dto.imageUrl,
|
||||||
imageKey: dto.imageKey,
|
imageKey: dto.imageKey,
|
||||||
published: dto.published ?? true,
|
published: dto.published ?? true,
|
||||||
@@ -54,6 +63,9 @@ export class BlogsService {
|
|||||||
metaDescription: dto.metaDescription,
|
metaDescription: dto.metaDescription,
|
||||||
slug: dto.slug ?? (slugify(dto.title) || undefined),
|
slug: dto.slug ?? (slugify(dto.title) || undefined),
|
||||||
},
|
},
|
||||||
|
include: {
|
||||||
|
category: { select: { id: true, nameFa: true, nameEn: true } },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,6 +83,9 @@ export class BlogsService {
|
|||||||
...(dto.summary !== undefined ? { summary: dto.summary } : {}),
|
...(dto.summary !== undefined ? { summary: dto.summary } : {}),
|
||||||
...(dto.body !== undefined ? { body: dto.body } : {}),
|
...(dto.body !== undefined ? { body: dto.body } : {}),
|
||||||
...(dto.author !== undefined ? { author: dto.author } : {}),
|
...(dto.author !== undefined ? { author: dto.author } : {}),
|
||||||
|
...(dto.categoryId !== undefined
|
||||||
|
? { categoryId: dto.categoryId }
|
||||||
|
: {}),
|
||||||
...(dto.imageUrl !== undefined ? { imageUrl: dto.imageUrl } : {}),
|
...(dto.imageUrl !== undefined ? { imageUrl: dto.imageUrl } : {}),
|
||||||
...(dto.imageKey !== undefined ? { imageKey: dto.imageKey } : {}),
|
...(dto.imageKey !== undefined ? { imageKey: dto.imageKey } : {}),
|
||||||
...(dto.published !== undefined ? { published: dto.published } : {}),
|
...(dto.published !== undefined ? { published: dto.published } : {}),
|
||||||
@@ -80,6 +95,9 @@ export class BlogsService {
|
|||||||
: {}),
|
: {}),
|
||||||
...(dto.slug !== undefined ? { slug: dto.slug } : {}),
|
...(dto.slug !== undefined ? { slug: dto.slug } : {}),
|
||||||
},
|
},
|
||||||
|
include: {
|
||||||
|
category: { select: { id: true, nameFa: true, nameEn: true } },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ export class CreateBlogDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
author?: string;
|
author?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
categoryId?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
imageUrl?: string;
|
imageUrl?: string;
|
||||||
@@ -69,6 +73,10 @@ export class UpdateBlogDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
author?: string;
|
author?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
categoryId?: string | null;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
imageUrl?: string | null;
|
imageUrl?: string | null;
|
||||||
|
|||||||
@@ -13,19 +13,19 @@ export class StoreScopeService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* STORE_ADMIN must own an active store for product/catalog CRUD.
|
* STORE_ADMIN must own an active store for product/catalog CRUD.
|
||||||
* SUPER_ADMIN may pass an explicit storeId.
|
* SUPER_ADMIN may pass an explicit storeId; otherwise defaults to
|
||||||
|
* the Mana Collection flagship store (slug: mana-collection).
|
||||||
*/
|
*/
|
||||||
async requireActiveStore(
|
async requireActiveStore(
|
||||||
actor: AuthUser,
|
actor: AuthUser,
|
||||||
storeId?: string,
|
storeId?: string,
|
||||||
): Promise<Store> {
|
): Promise<Store> {
|
||||||
if (actor.role === UserRole.SUPER_ADMIN) {
|
if (actor.role === UserRole.SUPER_ADMIN) {
|
||||||
if (!storeId) {
|
const store = storeId
|
||||||
throw new ForbiddenException('شناسه فروشگاه الزامی است');
|
? await this.prisma.store.findUnique({ where: { id: storeId } })
|
||||||
}
|
: await this.prisma.store.findUnique({
|
||||||
const store = await this.prisma.store.findUnique({
|
where: { slug: 'mana-collection' },
|
||||||
where: { id: storeId },
|
});
|
||||||
});
|
|
||||||
if (!store) {
|
if (!store) {
|
||||||
throw new NotFoundException('فروشگاه یافت نشد');
|
throw new NotFoundException('فروشگاه یافت نشد');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user