Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2bc1830db5 | |||
| 595f6a2844 | |||
| d0d2bc8cd6 | |||
| 04c3c6ddbe | |||
| 4260da30b7 | |||
| f55624d78e | |||
| 880e3dc896 | |||
| 383c32db7c | |||
| d9e148a3a3 | |||
|
|
9603494c4a | ||
|
|
e3cec87578 |
@@ -35,7 +35,8 @@ enum UserRole {
|
|||||||
ADMIN
|
ADMIN
|
||||||
HR
|
HR
|
||||||
|
|
||||||
@@schema("usr")
|
@@schema("usr")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model Blog {
|
model Blog {
|
||||||
@@ -151,7 +152,6 @@ model KlcArchive {
|
|||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
deletedAt DateTime? @map("deleted_at")
|
deletedAt DateTime? @map("deleted_at")
|
||||||
|
|
||||||
@@map("klc_archive")
|
@@map("klc_archive")
|
||||||
@@schema("cnt")
|
@@schema("cnt")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { CaseStudiesModule } from './modules/case-studies/case-studies.module';
|
|||||||
import { KlcArchiveModule } from './modules/klc-archive/klc-archive.module';
|
import { KlcArchiveModule } from './modules/klc-archive/klc-archive.module';
|
||||||
import { ReadingMaterialsModule } from './modules/reading-materials/reading-materials.module';
|
import { ReadingMaterialsModule } from './modules/reading-materials/reading-materials.module';
|
||||||
import { TrainingMaterialsModule } from './modules/training-materials/training-materials.module';
|
import { TrainingMaterialsModule } from './modules/training-materials/training-materials.module';
|
||||||
|
import { WebcastsModule } from './modules/webcast/webcasts.module';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ import { AppService } from './app.service';
|
|||||||
KlcArchiveModule,
|
KlcArchiveModule,
|
||||||
ReadingMaterialsModule,
|
ReadingMaterialsModule,
|
||||||
TrainingMaterialsModule,
|
TrainingMaterialsModule,
|
||||||
|
WebcastsModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
|
|||||||
24
src/main.ts
24
src/main.ts
@@ -4,6 +4,8 @@ import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import helmet from 'helmet';
|
import helmet from 'helmet';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
import { writeFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
@@ -12,12 +14,20 @@ async function bootstrap() {
|
|||||||
// Security
|
// Security
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: configService.get('CORS_ORIGIN', 'http://localhost:3000'),
|
origin: [
|
||||||
|
'http://localhost:3000', // local Swagger UI
|
||||||
|
'http://localhost:3006', // local frontend url
|
||||||
|
'https://editor.swagger.io', // Swagger Editor online
|
||||||
|
'https://klc-admin.wdiprojects.com',
|
||||||
|
'https://admin-uat.klc.betadelivery.com'
|
||||||
|
],
|
||||||
|
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
|
||||||
credentials: true,
|
credentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Global prefix
|
// ✅ Global prefix
|
||||||
app.setGlobalPrefix(configService.get('API_PREFIX', 'api/v1'));
|
const globalPrefix = configService.get('API_PREFIX', 'api/v1');
|
||||||
|
app.setGlobalPrefix(globalPrefix);
|
||||||
|
|
||||||
// Request logging middleware
|
// Request logging middleware
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
@@ -36,6 +46,7 @@ async function bootstrap() {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
const port = configService.get('PORT', 3000);
|
||||||
|
|
||||||
// Swagger documentation (only in development)
|
// Swagger documentation (only in development)
|
||||||
if (configService.get('NODE_ENV') !== 'production') {
|
if (configService.get('NODE_ENV') !== 'production') {
|
||||||
@@ -44,13 +55,18 @@ async function bootstrap() {
|
|||||||
.setDescription('Professional NestJS serverless application with user CRUD and authentication')
|
.setDescription('Professional NestJS serverless application with user CRUD and authentication')
|
||||||
.setVersion(configService.get('API_VERSION', '1.0.0'))
|
.setVersion(configService.get('API_VERSION', '1.0.0'))
|
||||||
.addBearerAuth()
|
.addBearerAuth()
|
||||||
|
.addServer(`${process.env.SERVER_URL}/`, 'Local Server')
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
const document = SwaggerModule.createDocument(app, config);
|
const document = SwaggerModule.createDocument(app, config);
|
||||||
SwaggerModule.setup('api/docs', app, document);
|
SwaggerModule.setup('api/docs', app, document);
|
||||||
|
|
||||||
|
const outputPath = join(process.cwd(), 'swagger.json');
|
||||||
|
writeFileSync(outputPath, JSON.stringify(document, null, 2), 'utf8');
|
||||||
|
console.log(`✅ Swagger JSON file generated at: ${outputPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const port = configService.get('PORT', 3000);
|
// const port = configService.get('PORT', 3000);
|
||||||
await app.listen(port);
|
await app.listen(port);
|
||||||
|
|
||||||
console.log(`🚀 Application is running on: http://localhost:${port}`);
|
console.log(`🚀 Application is running on: http://localhost:${port}`);
|
||||||
|
|||||||
@@ -81,13 +81,13 @@ export class BlogsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
// @UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
@Roles('ADMIN', 'HR')
|
// @Roles('ADMIN', 'HR')
|
||||||
@ApiBearerAuth()
|
// @ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Update blog post' })
|
@ApiOperation({ summary: 'Update blog post' })
|
||||||
@ApiResponse({ status: 200, description: 'Blog updated successfully', type: BlogResponseDto })
|
@ApiResponse({ status: 200, description: 'Blog updated successfully', type: BlogResponseDto })
|
||||||
@ApiResponse({ status: 404, description: 'Blog not found' })
|
@ApiResponse({ status: 404, description: 'Blog not found' })
|
||||||
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
// @ApiResponse({ status: 401, description: 'Unauthorized' })
|
||||||
@ApiResponse({ status: 403, description: 'Forbidden' })
|
@ApiResponse({ status: 403, description: 'Forbidden' })
|
||||||
async update(
|
async update(
|
||||||
@Param('id', ParseIntPipe) id: number,
|
@Param('id', ParseIntPipe) id: number,
|
||||||
@@ -97,13 +97,13 @@ export class BlogsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
@UseGuards(JwtAuthGuard, RolesGuard)
|
// @UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
@Roles('ADMIN', 'HR')
|
// @Roles('ADMIN', 'HR')
|
||||||
@ApiBearerAuth()
|
// @ApiBearerAuth()
|
||||||
@ApiOperation({ summary: 'Delete blog post (soft delete)' })
|
@ApiOperation({ summary: 'Delete blog post (soft delete)' })
|
||||||
@ApiResponse({ status: 200, description: 'Blog deleted successfully' })
|
@ApiResponse({ status: 200, description: 'Blog deleted successfully' })
|
||||||
@ApiResponse({ status: 404, description: 'Blog not found' })
|
@ApiResponse({ status: 404, description: 'Blog not found' })
|
||||||
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
// @ApiResponse({ status: 401, description: 'Unauthorized' })
|
||||||
@ApiResponse({ status: 403, description: 'Forbidden' })
|
@ApiResponse({ status: 403, description: 'Forbidden' })
|
||||||
async remove(@Param('id', ParseIntPipe) id: number): Promise<{ message: string }> {
|
async remove(@Param('id', ParseIntPipe) id: number): Promise<{ message: string }> {
|
||||||
await this.blogsService.remove(id);
|
await this.blogsService.remove(id);
|
||||||
|
|||||||
@@ -44,5 +44,5 @@ export class CreateBlogDto {
|
|||||||
@ApiPropertyOptional({ description: 'Publication date' })
|
@ApiPropertyOptional({ description: 'Publication date' })
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
publishedAt?: Date;
|
publishedAt?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export class BlogsService {
|
|||||||
data: {
|
data: {
|
||||||
...createBlogDto,
|
...createBlogDto,
|
||||||
tags: createBlogDto.tags || [],
|
tags: createBlogDto.tags || [],
|
||||||
|
publishedAt: createBlogDto.publishedAt ? new Date(createBlogDto.publishedAt) : null
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
94
src/modules/webcast/controllers/webcasts.controller.ts
Normal file
94
src/modules/webcast/controllers/webcasts.controller.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Patch,
|
||||||
|
Param,
|
||||||
|
Delete,
|
||||||
|
Query,
|
||||||
|
ParseIntPipe,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
|
||||||
|
import { WebcastsService } from '../services/webcasts.service';
|
||||||
|
import { CreateWebcastDto } from '../dto/create-webcast.dto';
|
||||||
|
import { UpdateWebcastDto } from '../dto/update-webcast.dto';
|
||||||
|
import { WebcastResponseDto } from '../dto/webcast-response.dto';
|
||||||
|
import { PaginationDto } from '../../../common/dto/pagination.dto';
|
||||||
|
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
|
||||||
|
import { RolesGuard } from '../../../common/guards/roles.guard';
|
||||||
|
import { Roles } from '../../../common/decorators/roles.decorator';
|
||||||
|
|
||||||
|
@ApiTags('webcasts')
|
||||||
|
@Controller('webcasts')
|
||||||
|
export class WebcastsController {
|
||||||
|
constructor(private readonly webcastsService: WebcastsService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
// @UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
// @Roles('ADMIN', 'HR')
|
||||||
|
// @ApiBearerAuth()
|
||||||
|
@ApiOperation({ summary: 'Create a new webcast' })
|
||||||
|
@ApiResponse({ status: 201, description: 'Webcast created successfully', type: WebcastResponseDto })
|
||||||
|
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
||||||
|
@ApiResponse({ status: 403, description: 'Forbidden' })
|
||||||
|
async create(@Body() createWebcastDto: CreateWebcastDto): Promise<WebcastResponseDto> {
|
||||||
|
return this.webcastsService.create(createWebcastDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@ApiOperation({ summary: 'Get all webcasts with pagination' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Webcasts retrieved successfully' })
|
||||||
|
async findAll(@Query() paginationDto: PaginationDto) {
|
||||||
|
return this.webcastsService.findAll(paginationDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('tag/:tag')
|
||||||
|
@ApiOperation({ summary: 'Get webcasts by tag' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Webcasts retrieved successfully' })
|
||||||
|
async findByTag(
|
||||||
|
@Param('tag') tag: string,
|
||||||
|
@Query() paginationDto: PaginationDto,
|
||||||
|
) {
|
||||||
|
return this.webcastsService.findByTag(tag, paginationDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ summary: 'Get webcast by ID' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Webcast retrieved successfully', type: WebcastResponseDto })
|
||||||
|
@ApiResponse({ status: 404, description: 'Webcast not found' })
|
||||||
|
async findOne(@Param('id', ParseIntPipe) id: number): Promise<WebcastResponseDto> {
|
||||||
|
return this.webcastsService.findOne(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(':id')
|
||||||
|
// @UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
// @Roles('ADMIN', 'HR')
|
||||||
|
// @ApiBearerAuth()
|
||||||
|
@ApiOperation({ summary: 'Update webcast' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Webcast updated successfully', type: WebcastResponseDto })
|
||||||
|
@ApiResponse({ status: 404, description: 'Webcast not found' })
|
||||||
|
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
||||||
|
@ApiResponse({ status: 403, description: 'Forbidden' })
|
||||||
|
async update(
|
||||||
|
@Param('id', ParseIntPipe) id: number,
|
||||||
|
@Body() updateWebcastDto: UpdateWebcastDto,
|
||||||
|
): Promise<WebcastResponseDto> {
|
||||||
|
return this.webcastsService.update(id, updateWebcastDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
// @UseGuards(JwtAuthGuard, RolesGuard)
|
||||||
|
// @Roles('ADMIN', 'HR')
|
||||||
|
// @ApiBearerAuth()
|
||||||
|
@ApiOperation({ summary: 'Delete webcast (soft delete)' })
|
||||||
|
@ApiResponse({ status: 200, description: 'Webcast deleted successfully' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Webcast not found' })
|
||||||
|
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
||||||
|
@ApiResponse({ status: 403, description: 'Forbidden' })
|
||||||
|
async remove(@Param('id', ParseIntPipe) id: number): Promise<{ message: string }> {
|
||||||
|
await this.webcastsService.remove(id);
|
||||||
|
return { message: 'Webcast deleted successfully' };
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/modules/webcast/dto/create-webcast.dto.ts
Normal file
22
src/modules/webcast/dto/create-webcast.dto.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { IsString, IsOptional, IsArray } from 'class-validator';
|
||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class CreateWebcastDto {
|
||||||
|
@ApiProperty({ description: 'Webcast title' })
|
||||||
|
@IsString()
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Webcast description' })
|
||||||
|
@IsString()
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Webcast file URL' })
|
||||||
|
@IsString()
|
||||||
|
fileUrl: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Webcast tags', type: [String] })
|
||||||
|
@IsArray()
|
||||||
|
@IsString({ each: true })
|
||||||
|
@IsOptional()
|
||||||
|
tags?: string[];
|
||||||
|
}
|
||||||
4
src/modules/webcast/dto/update-webcast.dto.ts
Normal file
4
src/modules/webcast/dto/update-webcast.dto.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { CreateWebcastDto } from './create-webcast.dto';
|
||||||
|
|
||||||
|
export class UpdateWebcastDto extends PartialType(CreateWebcastDto) {}
|
||||||
24
src/modules/webcast/dto/webcast-response.dto.ts
Normal file
24
src/modules/webcast/dto/webcast-response.dto.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class WebcastResponseDto {
|
||||||
|
@ApiProperty({ description: 'Webcast ID' })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Webcast title' })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Webcast description' })
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Webcast file URL' })
|
||||||
|
fileUrl: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Webcast tags', type: [String] })
|
||||||
|
tags: string[];
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Creation date' })
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ description: 'Last update date' })
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
153
src/modules/webcast/services/webcasts.service.ts
Normal file
153
src/modules/webcast/services/webcasts.service.ts
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../../../common/database/prisma.service';
|
||||||
|
import { CreateWebcastDto } from '../dto/create-webcast.dto';
|
||||||
|
import { UpdateWebcastDto } from '../dto/update-webcast.dto';
|
||||||
|
import { WebcastResponseDto } from '../dto/webcast-response.dto';
|
||||||
|
import { PaginationDto } from '../../../common/dto/pagination.dto';
|
||||||
|
import { paginate } from '../../../common/utils/pagination.util';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WebcastsService {
|
||||||
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async create(createWebcastDto: CreateWebcastDto): Promise<WebcastResponseDto> {
|
||||||
|
const webcast = await this.prisma.webcast.create({
|
||||||
|
data: {
|
||||||
|
...createWebcastDto,
|
||||||
|
tags: createWebcastDto.tags || [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.mapToResponseDto(webcast);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(paginationDto: PaginationDto) {
|
||||||
|
const { page, limit } = paginationDto;
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
const [webcasts, total] = await Promise.all([
|
||||||
|
this.prisma.webcast.findMany({
|
||||||
|
where: {
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'desc',
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limit,
|
||||||
|
}),
|
||||||
|
this.prisma.webcast.count({
|
||||||
|
where: {
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const mappedWebcasts = webcasts.map(webcast => this.mapToResponseDto(webcast));
|
||||||
|
|
||||||
|
return paginate(mappedWebcasts, { page, limit }, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOne(id: number): Promise<WebcastResponseDto> {
|
||||||
|
const webcast = await this.prisma.webcast.findFirst({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!webcast) {
|
||||||
|
throw new NotFoundException(`Webcast with ID ${id} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.mapToResponseDto(webcast);
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(id: number, updateWebcastDto: UpdateWebcastDto): Promise<WebcastResponseDto> {
|
||||||
|
const existingWebcast = await this.prisma.webcast.findFirst({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingWebcast) {
|
||||||
|
throw new NotFoundException(`Webcast with ID ${id} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const webcast = await this.prisma.webcast.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
...updateWebcastDto,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.mapToResponseDto(webcast);
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(id: number): Promise<void> {
|
||||||
|
const existingWebcast = await this.prisma.webcast.findFirst({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingWebcast) {
|
||||||
|
throw new NotFoundException(`Webcast with ID ${id} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prisma.webcast.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
deletedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async findByTag(tag: string, paginationDto: PaginationDto) {
|
||||||
|
const { page, limit } = paginationDto;
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
|
const [webcasts, total] = await Promise.all([
|
||||||
|
this.prisma.webcast.findMany({
|
||||||
|
where: {
|
||||||
|
tags: {
|
||||||
|
has: tag,
|
||||||
|
},
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'desc',
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limit,
|
||||||
|
}),
|
||||||
|
this.prisma.webcast.count({
|
||||||
|
where: {
|
||||||
|
tags: {
|
||||||
|
has: tag,
|
||||||
|
},
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const mappedWebcasts = webcasts.map(webcast => this.mapToResponseDto(webcast));
|
||||||
|
|
||||||
|
return paginate(mappedWebcasts, { page, limit }, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapToResponseDto(webcast: any): WebcastResponseDto {
|
||||||
|
return {
|
||||||
|
id: webcast.id,
|
||||||
|
title: webcast.title,
|
||||||
|
description: webcast.description,
|
||||||
|
fileUrl: webcast.fileUrl,
|
||||||
|
tags: webcast.tags,
|
||||||
|
createdAt: webcast.createdAt,
|
||||||
|
updatedAt: webcast.updatedAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/modules/webcast/webcasts.module.ts
Normal file
12
src/modules/webcast/webcasts.module.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { WebcastsService } from './services/webcasts.service';
|
||||||
|
import { WebcastsController } from './controllers/webcasts.controller';
|
||||||
|
import { PrismaModule } from '../../common/database/prisma.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [PrismaModule],
|
||||||
|
controllers: [WebcastsController],
|
||||||
|
providers: [WebcastsService],
|
||||||
|
exports: [WebcastsService],
|
||||||
|
})
|
||||||
|
export class WebcastsModule {}
|
||||||
Reference in New Issue
Block a user