feat: add class-transformer dependency for better data transformation
refactor(prisma): update User model to include banned status and ban details refactor(prisma): modify Account model to include new authentication fields refactor(prisma): enhance Session model with impersonation tracking refactor(prisma): adjust Verification model to include value field feat(auth): integrate admin plugin and enhance email verification process refactor(category): enforce ownership checks in category service methods refactor(item): update item service to validate category ownership feat(item): add ability to fetch items by category refactor(restaurant): implement ownership checks and admin routes in restaurant controller fix(user): restrict user access to admin routes feat(exception): implement global exception filter for consistent error handling
This commit is contained in:
+15
-2
@@ -1,7 +1,7 @@
|
||||
import { betterAuth } from 'better-auth';
|
||||
import { prismaAdapter } from 'better-auth/adapters/prisma';
|
||||
// If your Prisma file is located elsewhere, you can change the path
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { admin } from 'better-auth/plugins';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@@ -9,12 +9,25 @@ export const auth = betterAuth({
|
||||
database: prismaAdapter(prisma, {
|
||||
provider: 'postgresql',
|
||||
}),
|
||||
plugins: [admin()],
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
// requireEmailVerification: true,
|
||||
requireEmailVerification: true,
|
||||
sendResetPassword: async ({ user, token }) => {
|
||||
console.log(`Send reset password email to ${user.email}: ${token}`);
|
||||
},
|
||||
},
|
||||
emailVerification: {
|
||||
sendVerificationEmail: async ({ user, token }) => {
|
||||
console.log(`Send verification email to ${user.email}: ${token}`);
|
||||
},
|
||||
sendOnSignIn: true,
|
||||
autoSignInAfterVerification: true,
|
||||
},
|
||||
trustedOrigins: ['http://localhost:5173', 'http://localhost:5174'],
|
||||
socialProviders: {
|
||||
google: {
|
||||
prompt: 'select_account',
|
||||
clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||
},
|
||||
|
||||
@@ -8,17 +8,17 @@ import {
|
||||
Put,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { AuthGuard, Session } from '@thallesp/nestjs-better-auth';
|
||||
import type { UserSession } from '@thallesp/nestjs-better-auth';
|
||||
import { CategoryService } from './category.service';
|
||||
import { CreateCategoryDto } from './dto/create-category.dto';
|
||||
import { UpdateCategoryDto } from './dto/update-category.dto';
|
||||
import { AuthGuard, Session } from '@thallesp/nestjs-better-auth';
|
||||
import type { UserSession } from '@thallesp/nestjs-better-auth';
|
||||
|
||||
@Controller('categories')
|
||||
@UseGuards(AuthGuard)
|
||||
export class CategoryController {
|
||||
constructor(private readonly service: CategoryService) {}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Post()
|
||||
async create(
|
||||
@Body() dto: CreateCategoryDto,
|
||||
@@ -28,8 +28,11 @@ export class CategoryController {
|
||||
}
|
||||
|
||||
@Get('restaurant/:restaurantId')
|
||||
async findAllForRestaurant(@Param('restaurantId') restaurantId: string) {
|
||||
return await this.service.findAllForRestaurant(restaurantId);
|
||||
async findAllForRestaurant(
|
||||
@Param('restaurantId') restaurantId: string,
|
||||
@Session() session: UserSession,
|
||||
) {
|
||||
return await this.service.findAllForRestaurant(restaurantId, session.user.id);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@@ -37,7 +40,6 @@ export class CategoryController {
|
||||
return await this.service.findOne(id);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Put(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@@ -47,9 +49,8 @@ export class CategoryController {
|
||||
return await this.service.update(id, dto, session.user.id);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete(':id')
|
||||
async remove(@Param('id') id: string, @Session() session: UserSession) {
|
||||
await this.service.remove(id, session.user.id);
|
||||
return await this.service.remove(id, session.user.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { RestaurantService } from 'src/restaurant/restaurant.service';
|
||||
import { CreateCategoryDto } from './dto/create-category.dto';
|
||||
import { UpdateCategoryDto } from './dto/update-category.dto';
|
||||
import { RestaurantService } from 'src/restaurant/restaurant.service';
|
||||
import { CategoryDto } from './dto/category.dto';
|
||||
|
||||
@Injectable()
|
||||
@@ -14,19 +14,25 @@ export class CategoryService {
|
||||
|
||||
async create(dto: CreateCategoryDto, userId: string) {
|
||||
await this.restaurantService.verifyOwnership(userId, dto.restaurantId);
|
||||
return this.prisma.category.create({
|
||||
|
||||
const category = await this.prisma.category.create({
|
||||
data: {
|
||||
name: dto.name,
|
||||
restaurantId: dto.restaurantId,
|
||||
},
|
||||
});
|
||||
|
||||
return CategoryDto.fromEntity(category);
|
||||
}
|
||||
|
||||
async findAllForRestaurant(restaurantId: string) {
|
||||
async findAllForRestaurant(restaurantId: string, userId: string) {
|
||||
await this.restaurantService.verifyOwnership(userId, restaurantId);
|
||||
|
||||
const categories = await this.prisma.category.findMany({
|
||||
where: { restaurantId },
|
||||
include: { items: true },
|
||||
});
|
||||
|
||||
return categories.map(CategoryDto.fromEntity);
|
||||
}
|
||||
|
||||
@@ -35,19 +41,20 @@ export class CategoryService {
|
||||
where: { id },
|
||||
include: { items: true },
|
||||
});
|
||||
|
||||
if (!category) throw new NotFoundException('Category not found');
|
||||
|
||||
|
||||
|
||||
return CategoryDto.fromEntity(category);
|
||||
}
|
||||
|
||||
async update(id: string, dto: UpdateCategoryDto, userId: string) {
|
||||
const category = await this.findOne(id);
|
||||
if (!category) {
|
||||
throw new NotFoundException('Category not found');
|
||||
}
|
||||
|
||||
await this.restaurantService.verifyOwnership(userId, category.restaurantId);
|
||||
|
||||
const savedCategory = await this.prisma.category.update({
|
||||
const updated = await this.prisma.category.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: dto.name,
|
||||
@@ -55,17 +62,14 @@ export class CategoryService {
|
||||
},
|
||||
});
|
||||
|
||||
return CategoryDto.fromEntity(savedCategory);
|
||||
return CategoryDto.fromEntity(updated);
|
||||
}
|
||||
|
||||
async remove(id: string, userId: string) {
|
||||
const category = await this.findOne(id);
|
||||
|
||||
if (!category) {
|
||||
throw new NotFoundException('Category not found');
|
||||
}
|
||||
await this.restaurantService.verifyOwnership(userId, category.restaurantId);
|
||||
|
||||
this.prisma.category.delete({ where: { id } });
|
||||
return this.prisma.category.delete({ where: { id } });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { IsNotEmpty, IsString, IsUUID } from 'class-validator';
|
||||
import { IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
|
||||
|
||||
export class CreateCategoryDto {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
image?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsUUID()
|
||||
restaurantId: string;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
ExceptionFilter,
|
||||
Catch,
|
||||
ArgumentsHost,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { Response } from 'express';
|
||||
|
||||
@Catch()
|
||||
export class AllExceptionsFilter implements ExceptionFilter {
|
||||
catch(exception: unknown, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse<Response>();
|
||||
|
||||
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
let message = 'Internal server error';
|
||||
|
||||
if (exception instanceof HttpException) {
|
||||
status = exception.getStatus();
|
||||
const res = exception.getResponse();
|
||||
if (typeof res === 'string') message = res;
|
||||
else if (typeof res === 'object') message = (res as any).message || res;
|
||||
}
|
||||
|
||||
return response.status(status).json({
|
||||
success: false,
|
||||
status,
|
||||
message,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,7 @@ export class CreateItemDto {
|
||||
@IsString()
|
||||
image?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNotEmpty()
|
||||
@IsUUID()
|
||||
categoryId?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsUUID()
|
||||
restaurantId: string;
|
||||
categoryId: string;
|
||||
}
|
||||
|
||||
+13
-13
@@ -8,48 +8,48 @@ import {
|
||||
Put,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { AuthGuard, Session } from '@thallesp/nestjs-better-auth';
|
||||
import type { UserSession } from '@thallesp/nestjs-better-auth';
|
||||
import { ItemService } from './item.service';
|
||||
import { CreateItemDto } from './dto/create-item.dto';
|
||||
import { UpdateItemDto } from './dto/update-item.dto';
|
||||
import { MenuItemDto } from './dto/menu-item.dto';
|
||||
import { AuthGuard, Session } from '@thallesp/nestjs-better-auth';
|
||||
import type { UserSession } from '@thallesp/nestjs-better-auth';
|
||||
|
||||
@Controller('items')
|
||||
@UseGuards(AuthGuard)
|
||||
export class ItemController {
|
||||
constructor(private readonly service: ItemService) {}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Post()
|
||||
async create(@Body() dto: CreateItemDto, @Session() session: UserSession) {
|
||||
const entity = await this.service.create(dto, session.user.id);
|
||||
return MenuItemDto.fromEntity(entity);
|
||||
return await this.service.create(dto, session.user.id);
|
||||
}
|
||||
|
||||
@Get('/category/:categoryId')
|
||||
async findAllByCategory(@Param('categoryId') categoryId: string) {
|
||||
const items = await this.service.findAllByCategory(categoryId);
|
||||
return items.map(MenuItemDto.fromEntity);
|
||||
}
|
||||
|
||||
@Get('restaurant/:restaurantId')
|
||||
async findAllForRestaurant(@Param('restaurantId') restaurantId: string) {
|
||||
const items = await this.service.findAllForRestaurant(restaurantId);
|
||||
return items.map(MenuItemDto.fromEntity);
|
||||
return await this.service.findAllForRestaurant(restaurantId);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id') id: string) {
|
||||
const entity = await this.service.findOne(id);
|
||||
return MenuItemDto.fromEntity(entity);
|
||||
return await this.service.findOne(id);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Put(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() dto: UpdateItemDto,
|
||||
@Session() session: UserSession,
|
||||
) {
|
||||
const entity = await this.service.update(id, dto, session.user.id);
|
||||
return MenuItemDto.fromEntity(entity);
|
||||
return await this.service.update(id, dto, session.user.id);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete(':id')
|
||||
async remove(@Param('id') id: string, @Session() session: UserSession) {
|
||||
await this.service.remove(id, session.user.id);
|
||||
|
||||
@@ -3,9 +3,10 @@ import { ItemController } from './item.controller';
|
||||
import { ItemService } from './item.service';
|
||||
import { RestaurantModule } from 'src/restaurant/restaurant.module';
|
||||
import { PrismaModule } from 'src/prisma/prisma.module';
|
||||
import { CategoryModule } from 'src/category/category.module';
|
||||
|
||||
@Module({
|
||||
imports: [RestaurantModule, PrismaModule],
|
||||
imports: [RestaurantModule, CategoryModule, PrismaModule],
|
||||
controllers: [ItemController],
|
||||
providers: [ItemService],
|
||||
})
|
||||
|
||||
+28
-19
@@ -1,32 +1,45 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { RestaurantService } from 'src/restaurant/restaurant.service';
|
||||
import { CreateItemDto } from './dto/create-item.dto';
|
||||
import { UpdateItemDto } from './dto/update-item.dto';
|
||||
import { RestaurantService } from 'src/restaurant/restaurant.service';
|
||||
import { MenuItemDto } from './dto/menu-item.dto';
|
||||
import { CategoryService } from 'src/category/category.service';
|
||||
|
||||
@Injectable()
|
||||
export class ItemService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private readonly restaurantService: RestaurantService,
|
||||
private readonly categoryService: CategoryService,
|
||||
) {}
|
||||
|
||||
async create(dto: CreateItemDto, userId: string) {
|
||||
await this.restaurantService.verifyOwnership(userId, dto.restaurantId);
|
||||
const category = await this.categoryService.findOne(dto.categoryId);
|
||||
await this.restaurantService.verifyOwnership(userId, category.restaurantId);
|
||||
|
||||
const item = await this.prisma.menuItem.create({
|
||||
data: {
|
||||
name: dto.name,
|
||||
description: dto.description,
|
||||
price: dto.price,
|
||||
image: dto.image,
|
||||
categoryId: dto.categoryId ?? null,
|
||||
restaurantId: dto.restaurantId,
|
||||
categoryId: category.id,
|
||||
restaurantId: category.restaurantId,
|
||||
},
|
||||
});
|
||||
|
||||
return MenuItemDto.fromEntity(item);
|
||||
}
|
||||
|
||||
async findAllByCategory(categoryId: string) {
|
||||
const items = await this.prisma.menuItem.findMany({
|
||||
where: { categoryId },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
|
||||
return items.map(MenuItemDto.fromEntity);
|
||||
}
|
||||
async findAllForRestaurant(restaurantId: string) {
|
||||
const items = await this.prisma.menuItem.findMany({
|
||||
where: { restaurantId },
|
||||
@@ -38,43 +51,39 @@ export class ItemService {
|
||||
|
||||
async findOne(id: string) {
|
||||
const item = await this.prisma.menuItem.findUnique({ where: { id } });
|
||||
|
||||
if (!item) throw new NotFoundException('Menu item not found');
|
||||
|
||||
return MenuItemDto.fromEntity(item);
|
||||
}
|
||||
|
||||
async update(id: string, dto: UpdateItemDto, userId: string) {
|
||||
const item = await this.findOne(id);
|
||||
|
||||
if (!item) {
|
||||
throw new NotFoundException('Menu item not found');
|
||||
}
|
||||
|
||||
await this.restaurantService.verifyOwnership(userId, item.restaurantId);
|
||||
|
||||
const updatedItem = await this.prisma.menuItem.update({
|
||||
const updated = await this.prisma.menuItem.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: dto.name,
|
||||
description: dto.description,
|
||||
price: dto.price,
|
||||
image: dto.image,
|
||||
categoryId: dto.categoryId ?? undefined,
|
||||
restaurantId: dto.restaurantId ?? undefined,
|
||||
categoryId: dto.categoryId
|
||||
? (await this.categoryService.findOne(dto.categoryId)).id
|
||||
: item.categoryId,
|
||||
},
|
||||
});
|
||||
|
||||
return MenuItemDto.fromEntity(updatedItem);
|
||||
return MenuItemDto.fromEntity(updated);
|
||||
}
|
||||
|
||||
async remove(id: string, userId: string) {
|
||||
const item = await this.findOne(id);
|
||||
|
||||
if (!item) {
|
||||
throw new NotFoundException('Menu item not found');
|
||||
}
|
||||
|
||||
await this.restaurantService.verifyOwnership(userId, item.restaurantId);
|
||||
|
||||
return this.prisma.menuItem.delete({ where: { id } });
|
||||
return await this.restaurantService.verifyOwnership(
|
||||
userId,
|
||||
item.restaurantId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+35
-3
@@ -1,10 +1,42 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { AuthService } from '@thallesp/nestjs-better-auth';
|
||||
import { toNodeHandler } from 'better-auth/node';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule, {
|
||||
bodyParser: false,
|
||||
// Disable NestJS's built-in body parser so we can control ordering
|
||||
const app = await NestFactory.create(AppModule, { bodyParser: false });
|
||||
|
||||
app.enableCors({
|
||||
origin: ['http://localhost:5173', 'http://localhost:5174'],
|
||||
credentials: true,
|
||||
});
|
||||
await app.listen(process.env.PORT ?? 3000);
|
||||
|
||||
// Access Express instance
|
||||
const expressApp = app.getHttpAdapter().getInstance();
|
||||
|
||||
// Access BetterAuth instance from AuthService
|
||||
const authService = app.get<AuthService>(AuthService);
|
||||
|
||||
// Mount BetterAuth before body parsers
|
||||
expressApp.all(
|
||||
/^\/api\/auth\/.*/,
|
||||
toNodeHandler(authService.instance.handler),
|
||||
);
|
||||
|
||||
// Re-enable Nest's JSON body parser AFTER mounting BetterAuth
|
||||
expressApp.use(require('express').json());
|
||||
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true,
|
||||
forbidNonWhitelisted: true,
|
||||
transform: true,
|
||||
}),
|
||||
);
|
||||
|
||||
app.setGlobalPrefix('api');
|
||||
await app.listen(3000);
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
@@ -6,62 +6,71 @@ import {
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Request,
|
||||
UseGuards,
|
||||
Session,
|
||||
} from '@nestjs/common';
|
||||
import { RestaurantService } from './restaurant.service';
|
||||
import {
|
||||
AllowAnonymous,
|
||||
AuthGuard,
|
||||
Roles,
|
||||
Session as UserSession,
|
||||
} from '@thallesp/nestjs-better-auth';
|
||||
|
||||
import type { UserSession as SessionType } from '@thallesp/nestjs-better-auth';
|
||||
import { CreateRestaurantDto } from './dto/create-restaurant.dto';
|
||||
import { UpdateRestaurantDto } from './dto/update-restaurant.dto';
|
||||
import { AuthGuard, Session } from '@thallesp/nestjs-better-auth';
|
||||
import type { UserSession } from '@thallesp/nestjs-better-auth';
|
||||
import { RestaurantService } from './restaurant.service';
|
||||
|
||||
@Controller('restaurants')
|
||||
@UseGuards(AuthGuard)
|
||||
export class RestaurantController {
|
||||
constructor(private readonly service: RestaurantService) {}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Post()
|
||||
async create(
|
||||
@Body() dto: CreateRestaurantDto,
|
||||
@Session() session: UserSession,
|
||||
@Session() session: SessionType,
|
||||
) {
|
||||
const ownerId = session.user.id;
|
||||
return await this.service.create(ownerId, dto);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('my')
|
||||
async myRestaurants(@Session() session) {
|
||||
return await this.service.findByOwner(session.user.id);
|
||||
return await this.service.create(session.user.id, dto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
async findAll() {
|
||||
return await this.service.findAll();
|
||||
async findOwnerRestaurants(@Session() session: SessionType) {
|
||||
return await this.service.findByOwner(session.user.id);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@Param('id') id: string) {
|
||||
return await this.service.findOne(id);
|
||||
async findOne(@Param('id') id: string, @Session() session: SessionType) {
|
||||
return await this.service.findMyRestaurant(id, session.user.id);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Put(':id')
|
||||
async update(
|
||||
@Param('id') id: string,
|
||||
@Body() dto: UpdateRestaurantDto,
|
||||
@Session() session: UserSession,
|
||||
@Session() session: SessionType,
|
||||
) {
|
||||
return await this.service.update(id, dto, session.user.id);
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Delete(':id')
|
||||
async remove(@Param('id') id: string, @Session() session: UserSession) {
|
||||
await this.service.remove(id, session.user.id);
|
||||
async remove(@Param('id') id: string, @Session() session: SessionType) {
|
||||
return await this.service.remove(id, session.user.id);
|
||||
}
|
||||
|
||||
// public menu by slug
|
||||
@Roles(['admin'])
|
||||
@Get('/admin/all')
|
||||
async adminFindAll() {
|
||||
return await this.service.findAll();
|
||||
}
|
||||
|
||||
@Roles(['admin'])
|
||||
@Get('/admin/:id')
|
||||
async adminFindOne(@Param('id') id: string) {
|
||||
return await this.service.findOne(id);
|
||||
}
|
||||
|
||||
@AllowAnonymous()
|
||||
@Get('/public/:slug')
|
||||
async publicBySlug(@Param('slug') slug: string) {
|
||||
return await this.service.findBySlug(slug);
|
||||
|
||||
@@ -3,9 +3,9 @@ import {
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { CreateRestaurantDto } from './dto/create-restaurant.dto';
|
||||
import { UpdateRestaurantDto } from './dto/update-restaurant.dto';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { RestaurantDto } from './dto/restaurant.dto';
|
||||
|
||||
@Injectable()
|
||||
@@ -13,11 +13,11 @@ export class RestaurantService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async verifyOwnership(userId: string, restaurantId: string) {
|
||||
const restaurant = await this.prisma.restaurant.findFirst({
|
||||
const owned = await this.prisma.restaurant.findFirst({
|
||||
where: { id: restaurantId, ownerId: userId },
|
||||
});
|
||||
|
||||
if (!restaurant) {
|
||||
if (!owned) {
|
||||
throw new ForbiddenException('You do not own this restaurant');
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,6 @@ export class RestaurantService {
|
||||
|
||||
async findAll() {
|
||||
const restaurants = await this.prisma.restaurant.findMany();
|
||||
|
||||
return restaurants.map(RestaurantDto.fromEntity);
|
||||
}
|
||||
|
||||
@@ -55,11 +54,19 @@ export class RestaurantService {
|
||||
const restaurant = await this.prisma.restaurant.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
if (!restaurant) throw new NotFoundException('Restaurant not found');
|
||||
|
||||
if (!restaurant) {
|
||||
throw new NotFoundException('Restaurant not found');
|
||||
}
|
||||
|
||||
return RestaurantDto.fromEntity(restaurant);
|
||||
}
|
||||
|
||||
// find by slug with nested categories + items + template
|
||||
async findMyRestaurant(id: string, userId: string) {
|
||||
await this.verifyOwnership(userId, id);
|
||||
return this.findOne(id);
|
||||
}
|
||||
|
||||
async findBySlug(slug: string) {
|
||||
const restaurant = await this.prisma.restaurant.findUnique({
|
||||
where: { slug },
|
||||
@@ -70,19 +77,18 @@ export class RestaurantService {
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!restaurant) throw new NotFoundException('Restaurant not found');
|
||||
return RestaurantDto.fromEntity(restaurant);
|
||||
}
|
||||
|
||||
async update(id: string, dto: UpdateRestaurantDto, userId: string) {
|
||||
const restaurant = await this.findOne(id);
|
||||
if (!restaurant) {
|
||||
throw new NotFoundException('Restaurant not found');
|
||||
}
|
||||
|
||||
return RestaurantDto.fromEntity(restaurant);
|
||||
}
|
||||
|
||||
async update(id: string, dto: UpdateRestaurantDto, userId: string) {
|
||||
await this.verifyOwnership(userId, id);
|
||||
|
||||
return this.prisma.restaurant.update({
|
||||
const updated = await this.prisma.restaurant.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: dto.name,
|
||||
@@ -92,15 +98,12 @@ export class RestaurantService {
|
||||
template: dto.template ?? undefined,
|
||||
},
|
||||
});
|
||||
|
||||
return RestaurantDto.fromEntity(updated);
|
||||
}
|
||||
|
||||
async remove(id: string, userId: string) {
|
||||
const restaurant = await this.findOne(id);
|
||||
|
||||
if (!restaurant) throw new NotFoundException('Restaurant not found');
|
||||
|
||||
await this.verifyOwnership(userId, id);
|
||||
|
||||
return this.prisma.restaurant.delete({ where: { id } });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { Controller, Get, Param, Delete, UseGuards } from '@nestjs/common';
|
||||
import { AuthGuard, Session } from '@thallesp/nestjs-better-auth';
|
||||
import {
|
||||
AuthGuard,
|
||||
Session,
|
||||
AllowAnonymous,
|
||||
Roles,
|
||||
} from '@thallesp/nestjs-better-auth';
|
||||
import type { UserSession } from '@thallesp/nestjs-better-auth';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@@ -8,11 +13,13 @@ import { UserService } from './user.service';
|
||||
export class UserController {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
// @AllowAnonymous()
|
||||
@Get()
|
||||
async getAllUsers() {
|
||||
return this.userService.getAllUsers();
|
||||
}
|
||||
|
||||
@Roles(['admin'])
|
||||
@Get(':id')
|
||||
async getUserById(@Param('id') id: string) {
|
||||
return this.userService.getUserById(id);
|
||||
|
||||
Reference in New Issue
Block a user