Compare commits

...

28 Commits
main ... hnr

Author SHA1 Message Date
81ff748795 Update src/app.service.ts 2025-08-14 08:08:24 +00:00
1b7e5569bc Update src/app.service.ts 2025-08-14 08:03:00 +00:00
b9f3210bc4 Update .env 2025-08-14 08:01:56 +00:00
f5290a59c3 Update src/app.service.ts 2025-08-14 07:53:18 +00:00
26465e1a46 Update src/app.service.ts 2025-08-14 07:35:48 +00:00
3185762359 Update src/app-config/config.dev.json 2025-08-14 07:27:42 +00:00
c19866f960 Update src/app-config/config.test.json 2025-08-14 07:27:09 +00:00
39097d2ecb Update src/app-config/config.prod.json 2025-08-14 07:26:32 +00:00
86ab856b1d Update .env 2025-08-14 07:23:17 +00:00
3e55616599 Update src/app-config/config.local.json 2025-08-14 07:19:12 +00:00
8cd523aaff Update src/app-config/config.json 2025-08-14 07:18:49 +00:00
be1ffc6aec Update src/app-config/config.json 2025-08-14 07:18:20 +00:00
1c35cc6116 Update src/app-config/config.local.json 2025-08-14 07:17:56 +00:00
381a806792 Update src/redis/redis.provider.ts 2025-08-14 07:12:57 +00:00
2d988ea5b5 Update src/app-config/config.json 2025-08-10 07:23:51 +00:00
90ca9ec298 Update src/app-config/config.local.json 2025-08-10 07:20:57 +00:00
49be465b01 Update src/app-config/config.local.json 2025-08-10 07:11:38 +00:00
929358e6d9 Update src/app-config/config.json 2025-08-10 07:10:56 +00:00
c37f6167f8 Update src/app-config/config.local.json 2025-08-10 06:59:00 +00:00
619b97ff92 Update src/app-config/config.json 2025-08-10 06:50:29 +00:00
harshithnrao
159f8a0988 auth fixes 2025-07-25 20:17:33 +05:30
harshithnrao
5c24732ae4 email to id updation 2025-04-11 13:43:36 +05:30
harshithnrao
0fceef8c44 added validationPipes 2025-04-11 13:14:06 +05:30
harshithnrao
90f378a877 added cast, event-analytics, event-categories, episodes, and watchlist 2025-04-10 23:25:52 +05:30
harshithnrao
75673ae96d minor bug fixes and validation dtos 2025-04-06 17:55:09 +05:30
harshithnrao
dab3b9dc61 google-signin 2025-03-29 20:07:22 +05:30
harshithnrao
a293cdc04c redis with ttl
socketio for bookticket
2025-03-25 17:10:22 +05:30
harshithnrao
7c3fdd2fda redis for cache 2025-03-19 17:03:51 +05:30
127 changed files with 6468 additions and 426 deletions

8
.env
View File

@ -1,8 +1,8 @@
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_HOST=192.168.0.113
POSTGRES_PORT=5431
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_PASSWORD=FAdDoCwhmi3BvkK4pgEGDc7EspmDtHYV4y93748Xz6VysJKPdlsaU3L9N9k7OD9t
POSTGRES_DATABASE=wastecare
PORT=3000
MODE=DEV
RUN_MIGRATIONS=false
RUN_MIGRATIONS=true

View File

@ -93,7 +93,6 @@ CREATE TABLE "rules_details_ref" (
"deletedAt" DATE,
"version" NUMERIC
);
CREATE TABLE "users" (
"id" BIGSERIAL PRIMARY KEY NOT NULL,
"email" TEXT UNIQUE NOT NULL,
@ -102,6 +101,8 @@ CREATE TABLE "users" (
"name" TEXT,
"phoneNumber" TEXT,
"primaryRole" TEXT,
"resetCode" TEXT,
"resetCodeExpires" TIMESTAMP,
"status" TEXT,
"validFrom" DATE,
"validTill" DATE,
@ -183,7 +184,7 @@ CREATE TABLE "theatre_additional_details" (
CREATE TABLE "events" (
"id" BIGSERIAL PRIMARY KEY,
"org_email" TEXT NOT NULL,
"organizer_id" TEXT NOT NULL,
"event_name" TEXT,
"start_date" DATE,
"end_date" DATE,
@ -280,7 +281,7 @@ CREATE TABLE "tickets" (
"price" NUMERIC,
"seat_number" TEXT,
"qr_code" TEXT,
"buyer_email" TEXT NOT NULL,
"buyer_id" NUMERIC NOT NULL,
"booking_date" DATE,
"payment_status" TEXT,
"rescheduled_at" DATE,
@ -299,7 +300,7 @@ CREATE TABLE "tickets" (
CREATE TABLE "reviews" (
"id" BIGSERIAL PRIMARY KEY,
"event_id" NUMERIC NOT NULL,
"buyer_email" TEXT NOT NULL,
"buyer_id" TEXT NOT NULL,
"rating" NUMERIC,
"comment" TEXT,
"status" TEXT,
@ -332,11 +333,11 @@ CREATE TABLE "seats" (
CREATE TABLE "payouts" (
"id" BIGSERIAL PRIMARY KEY,
"payee_email" TEXT NOT NULL,
"payee_id" TEXT NOT NULL,
"amount" NUMERIC,
"payment_method" TEXT,
"transaction_id" TEXT,
"paid_to_email" TEXT,
"paid_to_id" TEXT,
"payout_date" DATE,
"status" TEXT,
"validFrom" DATE,
@ -369,6 +370,7 @@ CREATE TABLE "refunds" (
CREATE TABLE "push_notification" (
"id" BIGSERIAL PRIMARY KEY,
"user_id" NUMERIC NOT NULL,
"endpoint" TEXT NOT NULL,
"p256dh" TEXT,
"auth" TEXT,
@ -382,3 +384,113 @@ CREATE TABLE "push_notification" (
"deletedAt" DATE,
"version" NUMERIC
);
CREATE TABLE "promotions" (
"id" BIGSERIAL PRIMARY KEY,
"event_id" NUMERIC NOT NULL,
"start_date" DATE,
"end_date" DATE,
"location" TEXT,
"type" TEXT,
"description" TEXT,
"status" TEXT,
"validFrom" DATE,
"validTill" DATE,
"createdAt" DATE,
"updatedAt" DATE,
"createdBy" TEXT,
"modifiedBy" TEXT,
"deletedAt" DATE,
"version" NUMERIC
);
CREATE TABLE "event_episodes" (
"id" BIGSERIAL PRIMARY KEY,
"event_id" NUMERIC NOT NULL,
"episode_name" TEXT,
"episode_description" TEXT,
"episode_start_date" DATE,
"episode_end_date" DATE,
"episode_location" TEXT,
"episode_type" TEXT,
"images" JSON,
"status" TEXT,
"validFrom" DATE,
"validTill" DATE,
"createdAt" DATE,
"updatedAt" DATE,
"createdBy" TEXT,
"modifiedBy" TEXT,
"deletedAt" DATE,
"version" NUMERIC
);
CREATE TABLE "cast_members" (
"id" BIGSERIAL PRIMARY KEY,
"event_id" NUMERIC NOT NULL,
"cast_name" TEXT,
"role" TEXT,
"images" JSON,
"status" TEXT,
"validFrom" DATE,
"validTill" DATE,
"createdAt" DATE,
"updatedAt" DATE,
"createdBy" TEXT,
"modifiedBy" TEXT,
"deletedAt" DATE,
"version" NUMERIC
);
CREATE TABLE "user_watchlist" (
"id" BIGSERIAL PRIMARY KEY,
"user_id" NUMERIC NOT NULL,
"event_id" NUMERIC NOT NULL,
"status" TEXT,
"validFrom" DATE,
"validTill" DATE,
"createdAt" DATE,
"updatedAt" DATE,
"createdBy" TEXT,
"modifiedBy" TEXT,
"deletedAt" DATE,
"version" NUMERIC
);
CREATE TABLE "event_analytics" (
"id" BIGSERIAL PRIMARY KEY,
"event_id" NUMERIC NOT NULL,
"views" NUMERIC,
"ticket_sales" NUMERIC,
"engagement_rate" NUMERIC,
"promotion_views" NUMERIC,
"promotion_sales" NUMERIC,
"status" TEXT,
"validFrom" DATE,
"validTill" DATE,
"createdAt" DATE,
"updatedAt" DATE,
"createdBy" TEXT,
"modifiedBy" TEXT,
"deletedAt" DATE,
"version" NUMERIC
);
CREATE TABLE "locations" (
"id" BIGSERIAL PRIMARY KEY NOT NULL,
"name" TEXT,
"code" TEXT,
"images" TEXT[],
"status" TEXT,
"validFrom" DATE,
"validTill" DATE,
"createdAt" DATE,
"updatedAt" DATE,
"createdBy" TEXT,
"modifiedBy" TEXT,
"deletedAt" DATE,
"version" NUMERIC
);

830
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -24,25 +24,35 @@
},
"dependencies": {
"@nestjs/axios": "^4.0.0",
"@nestjs/common": "^10.0.0",
"@nestjs/common": "^10.4.15",
"@nestjs/config": "^3.3.0",
"@nestjs/core": "^10.0.0",
"@nestjs/core": "^10.4.15",
"@nestjs/jwt": "^11.0.0",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/platform-express": "^10.4.15",
"@nestjs/platform-socket.io": "^10.4.15",
"@nestjs/swagger": "^8.1.1",
"@nestjs/typeorm": "^10.0.1",
"@nestjs/websockets": "^10.4.15",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"cookie-parser": "^1.4.7",
"dotenv": "^16.3.1",
"handlebars": "^4.7.8",
"ioredis": "^5.6.0",
"moment": "^2.30.1",
"multer": "^1.4.5-lts.2",
"nodemailer": "^6.9.9",
"otp-generator": "^4.0.1",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1",
"pg": "^8.14.1",
"reflect-metadata": "^0.1.14",
"rxjs": "^7.8.1",
"sequelize": "^6.35.2",
"sequelize-typescript": "^2.1.6",
"socket.io": "^4.8.1",
"typeorm": "^0.3.17",
"web-push": "^3.6.7"
},
@ -56,6 +66,7 @@
"@types/node": "^20.10.6",
"@types/nodemailer": "^6.4.14",
"@types/passport": "^1.0.17",
"@types/passport-google-oauth20": "^2.0.16",
"@types/passport-jwt": "^4.0.1",
"@types/supertest": "^2.0.12",
"@types/validator": "^13.11.7",

View File

@ -1,4 +1,4 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common';
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { ActionsService } from './actions.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
@ -50,6 +50,7 @@ export class ActionsController {
}
@Post()
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() actions: Actions, @Res() res: Response) {
if(!actions) {
const response = new GenericResponse({

View File

@ -25,7 +25,7 @@ export class ActionsService {
return Actions.destroy({ where: { id: id } });
}
async upsert(actions: Actions, insertIfNotFound: boolean): Promise<Actions | [affectedCount: number]> {
async upsert(actions: any, insertIfNotFound: boolean): Promise<Actions | [affectedCount: number]> {
if (actions.id) {
const existingActions = await this.findByPk(actions.id);
if (existingActions) {

View File

@ -49,4 +49,16 @@ export class AppConfigService {
getVapidConfig(){
return configMaster[this.defaultEnv].vapidConfig;
}
getRedisConfig(){
return configMaster[this.defaultEnv].redisConfig;
}
getSwaggerConfig(){
return configMaster[this.defaultEnv].swaggerConfig;
}
getGoogleOauthConfig(){
return configMaster[this.defaultEnv].googleOauthConfig;
}
}

View File

@ -4,10 +4,10 @@
"port": 3003
},
"dbConfig": {
"host": "localhost",
"port": 5432,
"host": "192.168.0.113",
"port": 5431,
"user": "postgres",
"password": "postgres",
"password": "FAdDoCwhmi3BvkK4pgEGDc7EspmDtHYV4y93748Xz6VysJKPdlsaU3L9N9k7OD9t",
"database": "epr",
"MODE": "DEV",
"loggerEnabled": true

View File

@ -4,10 +4,10 @@
"port": 3000
},
"dbConfig": {
"host": "localhost",
"port": 5432,
"host": "192.168.0.113",
"port": 5431,
"user": "postgres",
"password": "postgres",
"password": "FAdDoCwhmi3BvkK4pgEGDc7EspmDtHYV4y93748Xz6VysJKPdlsaU3L9N9k7OD9t",
"database": "ticket_booking_db",
"MODE": "DEV",
"loggerEnabled": true
@ -53,8 +53,24 @@
},
"vapidConfig": {
"email": "mailto:example@domain.com",
"publicKey": "BPcIQx6jQlD0m2WA5qzXnHtKtsBhvIf_aRf6foXx3ESiw2Tks6b8tVzzX3hHwerGtWy4togFEiJtk5X-Sq36uVQ",
"privateKey": "v-pRrypniFnPn6UOyDNIKatxoHCI6aqsXCM86L7aTfY"
"publicKey": "BJnRhhooOB7unX8yfpHXgbUVqR03A7zsAppguA7JfHMNmhgZ1-RqbA-y70neaPrtbfO8Q1GsipYIM05_SpLDUP0",
"privateKey": "7OAq3PXC3VCNPus6Mua9rxSO66Vd0j5J5Mv8g7hMbYc"
},
"redisConfig": {
"host": "192.168.0.113",
"port": 6379,
"db": 0,
"username": "default",
"password":"oRYKRJKppvVqmSvTCiGeqVF4DDiFc22g6zodJQwbZCxNvKI34UYoQ8bMqdfDtmqd"
},
"googleOauthConfig": {
"clientId": "361418022886-q8inm4gh7aqhopitpsl87m0vcgces320.apps.googleusercontent.com",
"clientSecret": "GOCSPX-TwyERXKjx-8_U1X68ayJpCssfY",
"callbackURL": "http://localhost:3000/auth/google-redirect",
"scope": [
"email",
"profile"
]
}
}
}

View File

@ -4,10 +4,10 @@
"port": 3000
},
"dbConfig": {
"host": "localhost",
"port": 5432,
"host": "192.168.0.113",
"port": 5431,
"user": "postgres",
"password": "postgres",
"password": "FAdDoCwhmi3BvkK4pgEGDc7EspmDtHYV4y93748Xz6VysJKPdlsaU3L9N9k7OD9t",
"database": "ticket_booking_db",
"MODE": "DEV",
"loggerEnabled": true
@ -53,8 +53,24 @@
},
"vapidConfig": {
"email": "mailto:example@domain.com",
"publicKey": "BPcIQx6jQlD0m2WA5qzXnHtKtsBhvIf_aRf6foXx3ESiw2Tks6b8tVzzX3hHwerGtWy4togFEiJtk5X-Sq36uVQ",
"privateKey": "v-pRrypniFnPn6UOyDNIKatxoHCI6aqsXCM86L7aTfY"
"publicKey": "BJnRhhooOB7unX8yfpHXgbUVqR03A7zsAppguA7JfHMNmhgZ1-RqbA-y70neaPrtbfO8Q1GsipYIM05_SpLDUP0",
"privateKey": "7OAq3PXC3VCNPus6Mua9rxSO66Vd0j5J5Mv8g7hMbYc"
},
"redisConfig": {
"host": "192.168.0.113",
"port": 6379,
"db": 0,
"username": "default",
"password":"oRYKRJKppvVqmSvTCiGeqVF4DDiFc22g6zodJQwbZCxNvKI34UYoQ8bMqdfDtmqd"
},
"googleOauthConfig": {
"clientId": "361418022886-q8inm4gh7aqhopitpsl87m0vcgces320.apps.googleusercontent.com",
"clientSecret": "GOCSPX-TwyERXKjx-8_U1X68ayJpCssfY",
"callbackURL": "http://localhost:3000/auth/google-redirect",
"scope": [
"email",
"profile"
]
}
}
}

View File

@ -4,10 +4,10 @@
"port": 3003
},
"dbConfig": {
"host": "127.0.0.1",
"port": 5432,
"host": "192.168.0.113",
"port": 5431,
"user": "postgres",
"password": "postgres",
"password": "FAdDoCwhmi3BvkK4pgEGDc7EspmDtHYV4y93748Xz6VysJKPdlsaU3L9N9k7OD9t",
"database": "epr",
"MODE": "PROD",
"loggerEnabled": true

View File

@ -4,10 +4,10 @@
"port": 3003
},
"dbConfig": {
"host": "127.0.0.1",
"port": 5432,
"host": "192.168.0.113",
"port": 5431,
"user": "postgres",
"password": "postgres",
"password": "FAdDoCwhmi3BvkK4pgEGDc7EspmDtHYV4y93748Xz6VysJKPdlsaU3L9N9k7OD9t",
"database": "epr",
"MODE": "TEST",
"loggerEnabled": true

View File

@ -21,6 +21,9 @@ import { TicketModule } from './ticket/ticket.module';
import { TicketPricingModule } from './ticketPricing/ticketPricing.module';
import { TimeSlotModule } from './timeSlot/timeSlot.module';
import { PushSubscriptionModule } from './push-subscription/push-subscription.module';
import { RedisModule } from './redis/redis.module';
import { BookingGatewayModule } from './booking-gateway/booking-gateway.module';
import { LocationsModule } from './locations/locations.module';
@Module({
imports: [
@ -40,7 +43,10 @@ import { PushSubscriptionModule } from './push-subscription/push-subscription.mo
TicketPricingModule,
TimeSlotModule,
UserModule,
PushSubscriptionModule
PushSubscriptionModule,
RedisModule,
BookingGatewayModule,
LocationsModule
],
controllers: [AppController, AppConfigController],

View File

@ -18,20 +18,24 @@ export class AppService {
return 'Hello World!';
}
initializeSequelize() {
async initializeSequelize() {
this.getModels();
const dbConfig = this.configService.getDbConfig();
if (this.modelFilePaths.length > 0) {
this.commonService.sequelize = new Sequelize({
database: dbConfig.database,
database: "ticket_booking_db",
host: "192.168.0.113", // <-- IP or hostname of your DB
port: 5431,
dialect: 'postgres',
username: dbConfig.user,
password: dbConfig.password,
username: "postgres",
password: "FAdDoCwhmi3BvkK4pgEGDc7EspmDtHYV4y93748Xz6VysJKPdlsaU3L9N9k7OD9t",
models: this.modelFilePaths,
modelMatch: (filename, member) => {
return filename.substring(0, filename.indexOf('.entity')) === member.toLowerCase();
},
});
await this.commonService.sequelize.sync({ alter: true });
Utility.sequelize = this.commonService.sequelize;
}
const fileConfig = this.configService.initializeFileSystem();
@ -45,9 +49,22 @@ export class AppService {
const jwtConfig = this.configService.getJwtConfig();
this.commonService.jwtConfig = jwtConfig;
Utility.jwtConfig = jwtConfig;
// const urlConfig = this.configService.getUrlConfig();
// this.commonService.urlConfig = urlConfig;
// Utility.urlConfig = urlConfig;
const vapidConfig = this.configService.getVapidConfig();
this.commonService.vapidConfig = vapidConfig;
Utility.vapidConfig = vapidConfig;
const swaggerConfig = this.configService.getSwaggerConfig();
this.commonService.swaggerConfig = swaggerConfig;
Utility.swaggerConfig = swaggerConfig;
const redisConfig = this.configService.getRedisConfig();
this.commonService.redisConfig = redisConfig;
Utility.redisConfig = redisConfig;
const googleOauthConfig = this.configService.getGoogleOauthConfig();
this.commonService.googleOauthConfig = googleOauthConfig;
Utility.googleOauthConfig = googleOauthConfig;
}
getModels() {

View File

@ -1,12 +1,18 @@
import { Body, Controller, Delete, Post, Res } from '@nestjs/common';
import { Body, Controller, Delete, Get, Post, Req, Res, UseGuards } from '@nestjs/common';
import { GenericResponse } from 'src/common/GenericResponse.model';
import { AuthService } from './auth.service';
import { Response } from 'express';
import { Request, Response } from 'express';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { GoogleOauthGuard } from 'src/google-oauth/google-oauth.guard';
import { User } from 'src/user/user.entity';
import { UserService } from 'src/user/user.service';
import { ForgotPasswordDto, LoginDto, ResetPasswordDto, SignupDto, VerifyCodeDto } from './auth.dto';
import { MailService } from 'src/mail/mail.service';
@ApiTags('Auth')
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) { }
constructor(private authService: AuthService, private userService: UserService, private mailService: MailService) { }
@Post('login')
@ApiOperation({ summary: 'Login' })
@ -27,7 +33,7 @@ export class AuthController {
"data": null
}
})
async login(@Body() userDto: { email: string; password: string }, @Res() res: Response) {
async login(@Body() userDto: LoginDto, @Res() res: Response) {
const user = await this.authService.validateUser(userDto);
if (!user) {
const errorResponse = new GenericResponse({
@ -39,34 +45,140 @@ export class AuthController {
res.status(404).send(errorResponse);
}
const tokens = await this.authService.login(user);
const httpResponse = new GenericResponse(null, tokens);
res.header('Authorization', 'Bearer ' + tokens.access_token);
res.cookie('refresh_token', tokens.refresh_token, {
httpOnly: true,
secure: false,
sameSite: 'lax',
path: '/'
});
const httpResponse = new GenericResponse(null, {
// id: user.id,
email: user.email,
name: user.name,
userTypeCode: user.userTypeCode,
});
res.status(200).send(httpResponse);
}
@Post('refresh')
async refresh(@Body() body: { refresh_token: string }, @Res() res: Response) {
const newToken = await this.authService.refreshAccessToken(body.refresh_token);
// console.log("new token is",newToken);
if (!newToken) {
@Post('signup')
async signup(@Body() userDto: SignupDto, @Res() res: Response) {
const user = await this.userService.findByEmail(userDto.email);
if (user) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'Token not found'
}, null)
res.status(404).send(errorResponse);
stackTrace: 'User already exists'
}, null);
res.status(401).send(errorResponse);
}
const httpResponse = new GenericResponse(null, newToken);
res.status(201).send(httpResponse);
const userCreated = await this.userService.upsert(userDto, true);
const tokens = await this.authService.login(userCreated);
res.header('Authorization', 'Bearer ' + tokens.access_token);
res.cookie('refresh_token', tokens.refresh_token, {
httpOnly: true,
secure: false,
sameSite: 'lax',
path: '/'
})
const httpResponse = new GenericResponse(null,
{
email: tokens.email,
name: tokens.name,
userTypeCode: tokens.userTypeCode,
}
);
res.status(200).send(httpResponse);
}
@Post('refresh')
async refresh(@Req() req: Request, @Res() res: Response) {
console.log("req.cookies");
console.log(req.cookies);
console.log('headers:', req.headers.cookie);
const refreshToken = req.cookies['refresh_token'];
if (!refreshToken) {
return res.status(401).json({ message: 'Refresh token missing' });
}
try {
const newToken = await this.authService.refreshAccessToken(refreshToken);
res.header('Authorization', 'Bearer ' + newToken.access_token);
return res.status(200).json(new GenericResponse(null, {
email: newToken.email,
name: newToken.name,
userTypeCode: newToken.userTypeCode
}));
} catch (err) {
return res.status(403).json({
message: 'Invalid or expired refresh token',
error: err.message,
});
}
}
@Post('forgot-password')
@ApiOperation({ summary: 'Send reset code to email' })
async forgotPassword(@Body() dto: ForgotPasswordDto, @Res() res: Response) {
await this.authService.sendResetCode(dto.email);
return res.status(200).send(new GenericResponse(null, {
message: 'If your email is registered, a code has been sent.',
}));
}
@Post('verify-reset-code')
@ApiOperation({ summary: 'Verify 4-digit reset code' })
async verifyCode(@Body() dto: VerifyCodeDto, @Res() res: Response) {
const isValid = await this.authService.verifyResetCode(dto.email, dto.code);
if (!isValid) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_OR_EXPIRED_CODE',
stackTrace: 'Code is invalid or expired',
}, null));
}
return res.status(200).send(new GenericResponse(null, {
message: 'Code verified successfully.',
}));
}
@Post('reset-password')
@ApiOperation({ summary: 'Reset password using code' })
async resetPassword(@Body() dto: ResetPasswordDto, @Res() res: Response) {
const success = await this.authService.resetPasswordWithCode(dto.email, dto.code, dto.password);
if (!success) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_OR_EXPIRED_CODE',
stackTrace: 'Code is invalid or expired',
}, null));
}
return res.status(200).send(new GenericResponse(null, {
message: 'Password has been reset successfully.',
}));
}
@Delete('logout')
delete(@Body() body: { refresh_token: string }, @Res() res: Response) {
const deleteToken = this.authService.logout(body.refresh_token);
delete(@Res() res: Response, @Req() req: Request) {
const refreshToken = req.cookies['refresh_token'];
const deleteToken = this.authService.logout(refreshToken);
res.status(200).send(deleteToken);
}
@Post('verifyAccess')
// @Post('verifyAccess')
async verifyAccessToken(@Body() body: { access_token: string }, @Res() res: Response) {
const token = await this.authService.verifyAccessToken(body.access_token);
if (token) {
@ -83,7 +195,7 @@ export class AuthController {
}
}
@Post('verifyRefresh')
// @Post('verifyRefresh')
async verifyRefreshToken(@Body() body: { refresh_token: string }, @Res() res: Response) {
const token = await this.authService.verifyRefreshToken(body.refresh_token);
if (token) {
@ -100,4 +212,26 @@ export class AuthController {
}
}
//google oauth implementation
@Get()
@UseGuards(GoogleOauthGuard)
async googleLogin(@Res() res: Response) {
console.log("inside google login");
} //basically this is used to call googleLogin for login credentials
@Get('google-redirect')
@UseGuards(GoogleOauthGuard)
async googleOauthRedirect(@Req() req: Request, @Res() res: Response) {
console.log("inside google redirect");
const httpResponse = await this.authService.googleOauthRedirect(req.user);
res.header('Authorization', 'Bearer ' + httpResponse.access_token);
res.cookie('refresh_token', httpResponse.refresh_token, {
httpOnly: true,
secure: false,
sameSite: 'lax',
path: '/'
})
return res.status(httpResponse.statusCode).send();
}
}

101
src/auth/auth.dto.ts Normal file
View File

@ -0,0 +1,101 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, MinLength } from 'class-validator';
export class SignupDto {
@ApiProperty({
description: 'User email address',
example: 'user@example.com',
})
@IsEmail()
@IsNotEmpty()
email: string;
@ApiProperty({
description: 'User phone number in international format',
example: '+1234567890',
})
@IsPhoneNumber(null)
@IsOptional()
@IsNotEmpty()
phoneNumber?: string;
@ApiPropertyOptional({
description: 'User full name (optional)',
example: 'John Doe',
})
@IsOptional()
@IsString()
name?: string;
@ApiProperty({
// description: 'Password with a minimum of 6 characters',
example: 'securePassword123',
})
@IsString()
// @MinLength(6)
@IsNotEmpty()
password: string;
@ApiProperty({
description: 'User type code',
example: 'ADMIN',
})
@IsString()
@IsNotEmpty()
userTypeCode: string
}
export class LoginDto {
@ApiProperty({
description: 'User email address',
example: 'user@example.com',
})
@IsEmail()
@IsNotEmpty()
email: string;
@ApiProperty({
// description: 'Password with a minimum of 6 characters',
example: 'securePassword123',
})
@IsString()
// @MinLength(6)
@IsNotEmpty()
password: string;
}
export class ForgotPasswordDto {
@ApiProperty({ example: 'user@example.com' })
@IsEmail()
@IsNotEmpty()
email: string;
}
export class VerifyCodeDto {
@ApiProperty({ example: 'user@example.com' })
@IsEmail()
@IsNotEmpty()
email: string;
@ApiProperty({ example: '1234' })
@IsString()
code: string;
}
export class ResetPasswordDto {
@ApiProperty({ example: 'user@example.com' })
@IsEmail()
@IsNotEmpty()
email: string;
@ApiProperty({ example: '1234' })
@IsString()
code: string;
@ApiProperty({ example: 'newStrongPassword123' })
@IsString()
@MinLength(6)
password: string;
}

View File

@ -7,6 +7,8 @@ import { UserModule } from 'src/user/user.module';
import { JwtStrategy } from 'src/jwt/jwt.strategy';
import { Utility } from 'src/common/Utility';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { GoogleStrategy } from 'src/google-oauth/google.strategy';
import { MailModule } from 'src/mail/mail.module';
@Module({
imports: [
@ -24,9 +26,10 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
inject: [ConfigService],
}),
UserModule,
MailModule
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
providers: [AuthService, JwtStrategy,GoogleStrategy],
exports: [AuthService],
})
export class AuthModule {}

View File

@ -1,18 +1,25 @@
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Response } from 'express';
import { Utility } from 'src/common/Utility';
import JwtPayload from 'src/jwt/jwt-payload.dto';
import RefreshToken from 'src/jwt/refresh-token.entity';
import { User } from 'src/user/user.entity';
import { UserService } from 'src/user/user.service';
import { Request } from 'express';
import * as crypto from 'crypto';
import { MailService } from 'src/mail/mail.service';
import { instanceToPlain } from 'class-transformer';
@Injectable()
export class AuthService {
constructor(private userService: UserService, private jwtService: JwtService) { }
private signToken(payload: any, type: 'accessToken' | 'refreshToken'): string {
console.log("yav type andre", type)
constructor(private userService: UserService, private jwtService: JwtService, private mailService: MailService) { }
private signToken(payload: any, type: 'accessToken' | 'refreshToken') {
const config = Utility.jwtConfig[type];
console.log("yav expiry andre", config.expiresIn)
return this.jwtService.sign(payload, {
secret: config.secretOrKey,
expiresIn: config.expiresIn,
@ -26,25 +33,39 @@ export class AuthService {
secret: config.secretOrKey,
});
} catch (error) {
console.log(`${type} token is invalid`, error);
return null;
}
}
async validateUser(payload: JwtPayload) {
return this.userService.findByEmail(payload.email);
async validateUser(payload: any) {
return this.userService.findByEmailWithPassword(payload.email, payload.password);
}
async login(user: any) {
const payload: JwtPayload = { email: user.email, password: user.password };
console.log("illig bandu nilthu", payload)
// const plainUser = instanceToPlain(user);
const payload = {
id: user.id,
email: user.email,
userTypeCode: user.userTypeCode,
};
const accessToken = this.signToken(payload, 'accessToken');
console.log("illig bandu nilthu", accessToken)
const refreshToken = this.signToken(payload, 'refreshToken');
await RefreshToken.create({ email: user.email, token: refreshToken, type: 'jwt' });
return {
name: user.name,
email: user.email,
userTypeCode: user.userTypeCode,
access_token: accessToken,
refresh_token: refreshToken,
};
}
await RefreshToken.create({ email: user.email, token: refreshToken });
async signup(user: any) {
const { password, ...rest } = user;
const payload = { rest };
const accessToken = this.signToken(payload, 'accessToken');
const refreshToken = this.signToken(payload, 'refreshToken');
await RefreshToken.create({ email: user.email, token: refreshToken, type: 'jwt' });
return {
access_token: accessToken,
refresh_token: refreshToken,
@ -56,38 +77,117 @@ export class AuthService {
if (!payload) {
throw new Error('Invalid refresh token');
}
console.log(refreshToken);
console.log(payload);
const user = await this.userService.findByEmail(payload.email);
if (!user) {
throw new Error('User not found');
}
console.log(user)
const accessToken = this.signToken({
email: payload.email
}, 'accessToken');
console.log(accessToken)
return { access_token: accessToken };
const newPayload = {
id: user.id,
email: user.email,
userTypeCode: user.userTypeCode,
};
const accessToken = this.signToken(newPayload, 'accessToken');
return {
name: user.name,
email: user.email,
userTypeCode: user.userTypeCode,
access_token: accessToken
};
}
async verifyRefreshToken(refreshToken: string) {
const payload = this.verifyToken(refreshToken, 'refreshToken');
if (payload) {
console.log("Refresh token is valid", payload);
}
return payload;
}
async verifyAccessToken(accessToken: string) {
const payload = this.verifyToken(accessToken, 'accessToken');
if (payload) {
console.log("Access token is valid", payload);
}
return payload;
}
async logout(refreshToken: string) {
return RefreshToken.destroy({ where: { token: refreshToken } });
}
async sendResetCode(email: string): Promise<void> {
const user = await this.userService.findByEmail(email);
if (!user) return;
const plainUser = user.get({ plain: true });
console.log("user", plainUser);
const code = Math.floor(1000 + Math.random() * 9000).toString();
const expires = new Date(Date.now() + 10 * 60 * 1000);
// console.log("code", code);
// console.log("expires", expires);
user.resetCode = code;
user.resetCodeExpires = expires;
const updatedUser = await this.userService.update(plainUser);
console.log("updatedUser", updatedUser);
const subject = 'Your Password Reset Code';
const html = `<p>Your reset code is <strong>${code}</strong>. It will expire in 10 minutes.</p>`;
await this.mailService.sendMail(email, subject, `Code: ${code}`, html);
}
async resetPasswordWithCode(email: string, code: string, password: string): Promise<boolean> {
const userObj = await this.userService.findByEmail(email);
const user = userObj.get({ plain: true });
if (!user || user.resetCode != code) return false;
user.password = this.encryptPassword(password);
user.resetCode = null;
user.resetCodeExpires = null;
await this.userService.update(user);
return true;
}
async verifyResetCode(email: string, code: string): Promise<boolean> {
const user = await this.userService.findByEmail(email);
let plainUser = user.get({ plain: true });
console.log("plainUser", plainUser);
if (!plainUser || !plainUser.resetCode || !plainUser.resetCodeExpires) return false;
return plainUser.resetCode == code.toString();
}
//google services
async googleOauthRedirect(user) {
console.log("user in service is", user);
if (!user.email) {
return {
statusCode: 400,
message: 'User not found'
}
}
console.log("user.email in service is", user.email);
// let existingUser = await User.findOne({ where: { email: user.email } });
let existingUser = await this.userService.findByEmail(user.email);
if (!existingUser) {
existingUser = await User.create({
email: user.email,
name: user.name,
userTypeCode: 'user'
});
}
const newuser = await this.userService.findByEmail(user.email);
const rest = {
email: newuser.email,
name: newuser.name,
userTypeCode: newuser.userTypeCode,
};
const payload = existingUser.get({ plain: true });
// const { } = payload
const accessToken = this.signToken(rest, 'accessToken');
const refreshToken = this.signToken(rest, 'refreshToken');
await RefreshToken.create({ email: payload.email, token: refreshToken, type: 'jwt' });
return {
access_token: accessToken,
refresh_token: refreshToken
}
}
encryptPassword(password: string) {
return Buffer.from(password).toString('base64');
}
}

View File

@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { BookingGateway } from './booking.gateway';
import { BookingService } from './booking.service';
import { SeatService } from 'src/seat/seat.service';
import { TicketService } from 'src/ticket/ticket.service';
import { HttpModule, HttpService } from '@nestjs/axios';
@Module({
imports: [HttpModule],
providers: [BookingService, BookingGateway, SeatService, TicketService],
exports: [BookingService, BookingGateway],
})
export class BookingGatewayModule { }

View File

@ -0,0 +1,53 @@
import { WebSocketServer, OnGatewayConnection, OnGatewayDisconnect, SubscribeMessage, MessageBody, WebSocketGateway } from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { BookingService } from './booking.service';
import Ticket from 'src/ticket/ticket.entity';
@WebSocketGateway()
export class BookingGateway implements OnGatewayConnection, OnGatewayDisconnect {
constructor(private readonly bookingService: BookingService) { }
@WebSocketServer()
server: Server;
private connectedClients = 0;
handleConnection(client: Socket) {
this.connectedClients++;
console.log(`Client connected: ${client.id}`);
this.server.emit('clientsUpdated', this.connectedClients);
}
handleDisconnect(client: Socket) {
this.connectedClients--;
console.log(`Client disconnected: ${client.id}`);
this.server.emit('clientsUpdated', this.connectedClients);
}
@SubscribeMessage('bookTicket')
async handleBookTicket(@MessageBody() ticket: Ticket) {
const response = await this.bookingService.bookTicket(ticket);
this.server.to(ticket.eventId.toString() ).emit('ticketBooked', response);
this.server.emit('ticketBooked', response);
}
@SubscribeMessage('checkSeatAvailability')
async handleCheckSeatAvailability(@MessageBody() data: { event_id: number, seatNumber: string }) {
const { event_id: eventId, seatNumber } = data;
const isAvailable = await this.bookingService.isSeatAvailable(eventId, seatNumber);
this.server.emit('seatAvailability', { isAvailable });
}
@SubscribeMessage('joinRoom')
async handleJoinEvent(client: Socket,@MessageBody() data: { roomId: string }) {
client.join(data.roomId);
console.log(`Client joined room: ${data.roomId}`);
this.server.to(data.roomId).emit('clientJoinedRoom');
}
@SubscribeMessage('getBookedSeats')
async handleGetBookedSeats(@MessageBody() movieId: string) {
const bookedSeats = await this.bookingService.getBookedSeats(movieId);
this.server.emit('bookedSeatsList', bookedSeats);
}
}

View File

@ -0,0 +1,31 @@
import { Inject, Injectable } from '@nestjs/common';
import { BookingGateway } from './booking.gateway';
import { SeatService } from 'src/seat/seat.service';
import { TicketService } from 'src/ticket/ticket.service';
import Ticket from 'src/ticket/ticket.entity';
import Seat from 'src/seat/seat.entity';
@Injectable()
export class BookingService {
constructor(
// @Inject('BookingGateway') private readonly bookingGateway: BookingGateway,
private readonly seatService: SeatService,
private readonly ticketService: TicketService
) { }
async bookTicket(ticket: Ticket) {
this.ticketService.upsert(ticket, true);
this.seatService.bookSeat(ticket.eventId, ticket.seatNumber);
}
async getBookedSeats(event_id) {
const bookedSeats = await this.seatService.filter(new Seat({ eventId: event_id, available: 'booked' }));
return bookedSeats;
}
async isSeatAvailable(event_id: number, seatNumber: string) {
const seat = await this.seatService.findOne(new Seat({ eventId: event_id, seatNumber: seatNumber }));
if (seat.available === 'booked') {
return false;
}
return true;
}
}

312
src/cast/cast.controller.ts Normal file
View File

@ -0,0 +1,312 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { CastService } from './cast.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import Cast from './cast.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { CastDTO, CastUpdateDTO } from './cast.dto';
@ApiTags('cast')
@Controller('cast')
export class CastController {
constructor(private castService: CastService) { }
@Get("/all")
@ApiOperation({ summary: 'Get all casts' })
@ApiResponse({
status: 200,
description: 'Successfully retrieved all casts',
})
@ApiResponse({
status: 404,
description: 'No casts found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "No casts found"
},
"data": null
}
})
async getAllCasts(@Res() res: Response) {
const response = await this.castService.findAll() || [];
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No casts found'
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.send(httpResponse);
}
@Get(':id')
@ApiOperation({ summary: 'Get cast by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Cast ID' })
@ApiResponse({
status: 400,
description: 'ID is required',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NO_ID_REQ",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'Cast not found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "Cast not found"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully retrieved cast by ID',
})
async findById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.castService.findByPk(id) || {};
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Cast with ID ${id} not found`
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
@Post('/filter')
@ApiOperation({ summary: 'Filter casts based on criteria' })
@ApiBody({ type: Cast, description: 'Filter criteria for casts' })
@ApiResponse({
status: 400,
description: 'Invalid filter criteria',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.INVALID_CRITERIA",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'No casts found based on filter criteria',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "No casts found based on the filter criteria"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully filtered casts',
})
async filter(@Body() cast: Cast, @Res() res: Response) {
if (!cast) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_CRITERIA',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.castService.filter(cast) || [];
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No casts found based on the filter criteria'
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
@Post()
@ApiOperation({ summary: 'Insert a new cast' })
@ApiBody({ type: CastDTO, description: 'Cast data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid cast data',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.INVALID_DATA",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 201,
description: 'Successfully created a cast',
})
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() cast: CastDTO, @Res() res: Response) {
if (!cast) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_DATA',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.castService.upsert(cast, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
}
@Put()
@ApiOperation({ summary: 'Update an existing cast' })
@ApiBody({ type: CastUpdateDTO, description: 'Cast data to update' })
@ApiResponse({
status: 400,
description: 'Invalid cast data or ID missing',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.INVALID_DATA",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'Cast not found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "Cast not found"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully updated cast',
})
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() cast: CastUpdateDTO, @Res() res: Response) {
if (!cast || !cast.id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.castService.upsert(cast, false);
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Cast with ID ${cast.id} not found`
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete a cast by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Cast ID to delete' })
@ApiResponse({
status: 400,
description: 'ID parameter is required',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NO_ID_REQ",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'Cast not found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "Cast not found"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully deleted cast',
})
async deleteById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.castService.remove(id) || {};
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Cast with ID ${id} not found`
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
}

79
src/cast/cast.dto.ts Normal file
View File

@ -0,0 +1,79 @@
import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class CastDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
eventId: number;
@ApiProperty({ type: String })
@IsString()
@IsOptional()
castName: string;
@ApiProperty({ type: String })
@IsString()
@IsOptional()
role: string;
@ApiProperty({ type: Object })
@IsOptional()
images: object;
@ApiProperty({ type: String })
@IsString()
@IsOptional()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@IsOptional()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@IsOptional()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class CastUpdateDTO extends CastDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number;
}

58
src/cast/cast.entity.ts Normal file
View File

@ -0,0 +1,58 @@
import { Table, Column, Model, DataType } from 'sequelize-typescript';
import { ApiProperty } from '@nestjs/swagger';
@Table({ tableName: 'cast_members', paranoid: true })
export default class Cast extends Model {
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, allowNull: false, field: 'event_id' })
eventId: number;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'cast_name' })
castName: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'role' })
role: string;
@ApiProperty({ type: Object })
@Column({ type: DataType.JSON, field: 'images' })
images: object;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'status' })
status: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validFrom' })
validFrom: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validTill' })
validTill: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'createdAt' })
createdAt: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'updatedAt' })
updatedAt: Date;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'createdBy' })
createdBy: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'modifiedBy' })
modifiedBy: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'deletedAt' })
deletedAt: Date;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'version' })
version: number;
}

11
src/cast/cast.module.ts Normal file
View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { CastController } from './cast.controller';
import { CastService } from './cast.service';
import { HttpModule } from '@nestjs/axios';
@Module({
imports: [HttpModule],
providers: [CastService],
controllers: [CastController],
})
export class CastModule { }

41
src/cast/cast.service.ts Normal file
View File

@ -0,0 +1,41 @@
import { Injectable } from '@nestjs/common';
import Cast from './cast.entity';
import { HttpService } from '@nestjs/axios';
import { Utility } from 'src/common/Utility';
@Injectable()
export class CastService {
constructor(private readonly httpService: HttpService) { }
async findAll(): Promise<{ rows: Cast[], count: number }> {
return Cast.findAndCountAll();
}
async findByPk(id: number): Promise<Cast> {
return Cast.findByPk(id);
}
findOne(cast: Cast): Promise<Cast> {
return Cast.findOne({ where: cast as any });
}
filter(cast: Cast): Promise<Cast[]> {
return Cast.findAll({ where: cast as any });
}
async remove(id: number): Promise<number> {
return Cast.destroy({ where: { id: id } });
}
async upsert(cast: any, insertIfNotFound: boolean): Promise<Cast | [affectedCount: number]> {
if (cast.id) {
const existingCast = await this.findByPk(cast.id);
if (existingCast) {
return Cast.update(cast, { where: { id: cast.id } });
}
}
if (insertIfNotFound) {
return Cast.create(cast as any);
}
}
}

View File

@ -23,4 +23,6 @@ export class Utility {
};
static urlConfig:any;
static vapidConfig: any;
static redisConfig: any;
static googleOauthConfig: any;
}

View File

@ -10,6 +10,9 @@ export class CommonService {
urlConfig:any;
jwtConfig:any;
vapidConfig:any;
redisConfig:any;
swaggerConfig:any;
googleOauthConfig:any;
constructor() {
}

View File

@ -0,0 +1,63 @@
import { Injectable } from '@nestjs/common';
import { diskStorage } from 'multer';
import { extname, join } from 'path';
import * as fs from 'fs';
@Injectable()
export class FilesService {
// Method to configure Multer's storage
getMulterStorage() {
return diskStorage({
destination: (req, file, cb) => {
// Define the upload path
const uploadPath = './uploads';
// Create the upload directory if it doesn't exist
if (!fs.existsSync(uploadPath)) {
fs.mkdirSync(uploadPath, { recursive: true });
}
// Set the destination directory for file storage
cb(null, uploadPath);
},
filename: (req, file, cb) => {
// Generate a unique file name based on the current timestamp
const fileName = Date.now() + extname(file.originalname);
cb(null, fileName);
},
});
}
// Method to filter file types based on MIME type and extension
fileFilter(req, file, cb) {
const allowedImageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/jpg'];
const allowedVideoTypes = ['video/mp4', 'video/avi', 'video/mov'];
// Check if the file is an image or a video
if (allowedImageTypes.includes(file.mimetype) || allowedVideoTypes.includes(file.mimetype)) {
return cb(null, true); // Allow the file
} else {
return cb(new Error('Only images (jpg, png, gif) and videos (mp4, avi, mov) are allowed!'), false); // Reject the file
}
}
// Helper method to categorize the uploaded files into images and videos
categorizeFiles(files: Express.Multer.File[]): { image: string[]; video: string[] } {
const categorizedFiles = {
image: [],
video: [],
};
// Categorize files based on their extensions
files.forEach(file => {
const ext = extname(file.originalname).toLowerCase();
if (['.jpg', '.jpeg', '.png', '.gif'].includes(ext)) {
categorizedFiles.image.push(file.path); // Categorize as image
} else if (['.mp4', '.avi', '.mov'].includes(ext)) {
categorizedFiles.video.push(file.path); // Categorize as video
}
});
return categorizedFiles;
}
}

View File

@ -0,0 +1,287 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { EventAnalyticsService } from './event-analytics.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import EventAnalytics from './event-analytics.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { EventAnalyticsDTO, EventAnalyticsUpdateDTO } from './event-analytics.dto';
@ApiTags('event-analytics')
@Controller('event-analytics')
export class EventAnalyticsController {
constructor(private eventAnalyticsService: EventAnalyticsService) {}
@Get('/all')
@ApiOperation({ summary: 'Get all event analytics' })
@ApiResponse({ status: 200, description: 'Successfully retrieved all event analytics' })
@ApiResponse({
status: 404,
description: 'No event analytics found',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event analytics found'
},
data: null
}
})
async getAll(@Res() res: Response) {
const response = await this.eventAnalyticsService.findAll();
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event analytics found'
}, null);
return res.status(404).send(errorResponse);
}
res.send(new GenericResponse(null, response));
}
@Get(':id')
@ApiOperation({ summary: 'Get event analytics by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Event analytics ID' })
@ApiResponse({
status: 400,
description: 'ID is required',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
},
data: null
}
})
@ApiResponse({
status: 404,
description: 'Event analytics not found',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'Event analytics not found'
},
data: null
}
})
@ApiResponse({ status: 200, description: 'Successfully retrieved event analytics by ID' })
async findById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null));
}
const response = await this.eventAnalyticsService.findByPk(id);
if (!response) {
return res.status(404).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Event analytics with ID ${id} not found`
}, null));
}
res.status(200).send(new GenericResponse(null, response));
}
@Post('/filter')
@ApiOperation({ summary: 'Filter event analytics based on criteria' })
@ApiBody({ type: EventAnalytics, description: 'Filter criteria for event analytics' })
@ApiResponse({
status: 400,
description: 'Invalid filter criteria',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_CRITERIA',
stackTrace: 'Request'
},
data: null
}
})
@ApiResponse({
status: 404,
description: 'No event analytics found based on filter criteria',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event analytics found based on the filter criteria'
},
data: null
}
})
@ApiResponse({ status: 200, description: 'Successfully filtered event analytics' })
async filter(@Body() analytics: EventAnalytics, @Res() res: Response) {
if (!analytics) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_CRITERIA',
stackTrace: 'Request'
}, null));
}
const response = await this.eventAnalyticsService.filter(analytics) || [];
if (!response.length) {
return res.status(404).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event analytics found based on the filter criteria'
}, null));
}
res.status(200).send(new GenericResponse(null, response));
}
@Post()
@ApiOperation({ summary: 'Insert a new event analytics record' })
@ApiBody({ type: EventAnalyticsDTO, description: 'Event analytics data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid event analytics data',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_DATA',
stackTrace: 'Request'
},
data: null
}
})
@ApiResponse({ status: 201, description: 'Successfully created event analytics record' })
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() analytics: EventAnalyticsDTO, @Res() res: Response) {
if (!analytics) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_DATA',
stackTrace: 'Request'
}, null));
}
const response = await this.eventAnalyticsService.upsert(analytics, true);
res.status(201).send(new GenericResponse(null, response));
}
@Put()
@ApiOperation({ summary: 'Update an existing event analytics record' })
@ApiBody({ type: EventAnalyticsUpdateDTO, description: 'Event analytics data to update' })
@ApiResponse({
status: 400,
description: 'Invalid event analytics data or ID missing',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
},
data: null
}
})
@ApiResponse({
status: 404,
description: 'Event analytics record not found',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'Event analytics not found'
},
data: null
}
})
@ApiResponse({ status: 200, description: 'Successfully updated event analytics record' })
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() analytics: EventAnalyticsUpdateDTO, @Res() res: Response) {
if (!analytics || !analytics.id) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null));
}
const response = await this.eventAnalyticsService.upsert(analytics, false);
if (!response) {
return res.status(404).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Event analytics with ID ${analytics.id} not found`
}, null));
}
res.status(200).send(new GenericResponse(null, response));
}
@Delete(':id')
@ApiOperation({ summary: 'Delete event analytics by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Event analytics ID to delete' })
@ApiResponse({
status: 400,
description: 'ID parameter is required',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
},
data: null
}
})
@ApiResponse({
status: 404,
description: 'Event analytics record not found',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'Event analytics not found'
},
data: null
}
})
@ApiResponse({ status: 200, description: 'Successfully deleted event analytics record' })
async deleteById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null));
}
const response = await this.eventAnalyticsService.remove(id);
if (!response) {
return res.status(404).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Event analytics with ID ${id} not found`
}, null));
}
res.status(200).send(new GenericResponse(null, response));
}
}

View File

@ -0,0 +1,92 @@
import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class EventAnalyticsDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
eventId: number;
@ApiProperty({ type: Number })
@IsOptional()
@IsNumber()
views: number;
@ApiProperty({ type: Number })
@IsOptional()
@IsNumber()
ticketSales: number;
@ApiProperty({ type: Number })
@IsOptional()
@IsNumber()
engagementRate: number;
@ApiProperty({ type: Number })
@IsOptional()
@IsNumber()
promotionViews: number;
@ApiProperty({ type: Number })
@IsOptional()
@IsNumber()
promotionSales: number;
@ApiProperty({ type: String })
@IsString()
@IsOptional()
status: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsOptional()
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsOptional()
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class EventAnalyticsUpdateDTO extends EventAnalyticsDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number;
}

View File

@ -0,0 +1,66 @@
import { Table, Column, Model, DataType } from 'sequelize-typescript';
import { ApiProperty } from '@nestjs/swagger';
@Table({ tableName: 'event_analytics', paranoid: true })
export default class EventAnalytics extends Model {
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, allowNull: false, field: 'event_id' })
eventId: number;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'views' })
views: number;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'ticket_sales' })
ticketSales: number;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'engagement_rate' })
engagementRate: number;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'promotion_views' })
promotionViews: number;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'promotion_sales' })
promotionSales: number;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'status' })
status: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validFrom' })
validFrom: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validTill' })
validTill: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'createdAt' })
createdAt: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'updatedAt' })
updatedAt: Date;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'createdBy' })
createdBy: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'modifiedBy' })
modifiedBy: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'deletedAt' })
deletedAt: Date;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'version' })
version: number;
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { EventAnalyticsController } from './event-analytics.controller';
import { EventAnalyticsService } from './event-analytics.service';
import { HttpModule } from '@nestjs/axios';
@Module({
imports: [HttpModule],
providers: [EventAnalyticsService],
controllers: [EventAnalyticsController],
})
export class EventAnalyticsModule { }

View File

@ -0,0 +1,41 @@
import { Injectable } from '@nestjs/common';
import EventAnalytics from './event-analytics.entity';
import { HttpService } from '@nestjs/axios';
import { Utility } from 'src/common/Utility';
@Injectable()
export class EventAnalyticsService {
constructor(private readonly httpService: HttpService) {}
async findAll(): Promise<{ rows: EventAnalytics[], count: number }> {
return EventAnalytics.findAndCountAll();
}
async findByPk(id: number): Promise<EventAnalytics> {
return EventAnalytics.findByPk(id);
}
findOne(eventAnalytics: EventAnalytics): Promise<EventAnalytics> {
return EventAnalytics.findOne({ where: eventAnalytics as any });
}
filter(eventAnalytics: EventAnalytics): Promise<EventAnalytics[]> {
return EventAnalytics.findAll({ where: eventAnalytics as any });
}
async remove(id: number): Promise<number> {
return EventAnalytics.destroy({ where: { id: id } });
}
async upsert(eventAnalytics: any, insertIfNotFound: boolean): Promise<EventAnalytics | [affectedCount: number]> {
if (eventAnalytics.id) {
const existing = await this.findByPk(eventAnalytics.id);
if (existing) {
return EventAnalytics.update(eventAnalytics, { where: { id: eventAnalytics.id } });
}
}
if (insertIfNotFound) {
return EventAnalytics.create(eventAnalytics as any);
}
}
}

View File

@ -0,0 +1,290 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { EventCategoryService } from './event-category.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import EventCategory from './event-category.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { EventCategoryDTO, EventCategoryUpdateDTO } from './event-category.dto';
@ApiTags('eventCategory')
@Controller('eventCategory')
export class EventCategoryController {
constructor(private eventCategoryService: EventCategoryService) {}
@Get('/all')
@ApiOperation({ summary: 'Get all event categories' })
@ApiResponse({ status: 200, description: 'Successfully retrieved all event categories' })
@ApiResponse({
status: 404,
description: 'No event categories found',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event categories found'
},
data: null
}
})
async getAll(@Res() res: Response) {
const response = await this.eventCategoryService.findAll();
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event categories found'
}, null);
return res.status(404).send(errorResponse);
}
res.send(new GenericResponse(null, response));
}
@Get(':id')
@ApiOperation({ summary: 'Get event category by ID' })
@ApiParam({ name: 'id', type: Number, description: 'EventCategory ID' })
@ApiResponse({
status: 200,
description: 'Successfully retrieved event category by ID',
})
@ApiResponse({
status: 400,
description: 'ID is required',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
},
data: null
}
})
@ApiResponse({
status: 404,
description: 'Event category not found',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'Event category not found'
},
data: null
}
})
async findById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null));
}
const response = await this.eventCategoryService.findByPk(id);
if (!response) {
return res.status(404).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Event category with ID ${id} not found`
}, null));
}
res.status(200).send(new GenericResponse(null, response));
}
@Post('/filter')
@ApiOperation({ summary: 'Filter event categories based on criteria' })
@ApiBody({ type: EventCategory, description: 'Filter criteria for event categories' })
@ApiResponse({ status: 200, description: 'Successfully filtered event categories' })
@ApiResponse({
status: 400,
description: 'Invalid filter criteria',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_CRITERIA',
stackTrace: 'Request'
},
data: null
}
})
@ApiResponse({
status: 404,
description: 'No event categories found based on filter criteria',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event categories found based on the filter criteria'
},
data: null
}
})
async filter(@Body() category: EventCategory, @Res() res: Response) {
if (!category) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_CRITERIA',
stackTrace: 'Request'
}, null));
}
const response = await this.eventCategoryService.filter(category);
if (!response) {
return res.status(404).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event categories found based on the filter criteria'
}, null));
}
res.status(200).send(new GenericResponse(null, response));
}
@Post()
@ApiOperation({ summary: 'Insert a new event category' })
@ApiBody({ type: EventCategoryDTO, description: 'Event category data to insert' })
@ApiResponse({ status: 201, description: 'Successfully created an event category' })
@ApiResponse({
status: 400,
description: 'Invalid event category data',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_DATA',
stackTrace: 'Request'
},
data: null
}
})
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() category: EventCategoryDTO, @Res() res: Response) {
if (!category) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_DATA',
stackTrace: 'Request'
}, null));
}
const response = await this.eventCategoryService.upsert(category, true);
res.status(201).send(new GenericResponse(null, response));
}
@Put()
@ApiOperation({ summary: 'Update an existing event category' })
@ApiBody({ type: EventCategoryUpdateDTO, description: 'Event category data to update' })
@ApiResponse({ status: 200, description: 'Successfully updated event category' })
@ApiResponse({
status: 400,
description: 'Invalid data or ID missing',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
},
data: null
}
})
@ApiResponse({
status: 404,
description: 'Event category not found',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'Event category not found'
},
data: null
}
})
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() category: EventCategoryUpdateDTO, @Res() res: Response) {
if (!category || !category.id) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null));
}
const response = await this.eventCategoryService.upsert(category, false);
if (!response) {
return res.status(404).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Event category with ID ${category.id} not found`
}, null));
}
res.status(200).send(new GenericResponse(null, response));
}
@Delete(':id')
@ApiOperation({ summary: 'Delete an event category by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Event category ID to delete' })
@ApiResponse({ status: 200, description: 'Successfully deleted event category' })
@ApiResponse({
status: 400,
description: 'ID parameter is required',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
},
data: null
}
})
@ApiResponse({
status: 404,
description: 'Event category not found',
example: {
notification: {
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'Event category not found'
},
data: null
}
})
async deleteById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
return res.status(400).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null));
}
const response = await this.eventCategoryService.remove(id);
if (!response) {
return res.status(404).send(new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Event category with ID ${id} not found`
}, null));
}
res.status(200).send(new GenericResponse(null, response));
}
}

View File

@ -0,0 +1,64 @@
import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class EventCategoryDTO {
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
categoryName: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class EventCategoryUpdateDTO extends EventCategoryDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number;
}

View File

@ -0,0 +1,46 @@
import { Table, Column, Model, DataType } from 'sequelize-typescript';
import { ApiProperty } from '@nestjs/swagger';
@Table({ tableName: 'event_categories', paranoid: true })
export default class EventCategory extends Model {
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'category_name' })
categoryName: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'status' })
status: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validFrom' })
validFrom: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validTill' })
validTill: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'createdAt' })
createdAt: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'updatedAt' })
updatedAt: Date;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'createdBy' })
createdBy: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'modifiedBy' })
modifiedBy: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'deletedAt' })
deletedAt: Date;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'version' })
version: number;
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { EventCategoryController } from './event-category.controller';
import { EventCategoryService } from './event-category.service';
import { HttpModule } from '@nestjs/axios';
@Module({
imports: [HttpModule],
providers: [EventCategoryService],
controllers: [EventCategoryController],
})
export class EventCategoryModule {}

View File

@ -0,0 +1,40 @@
import { Injectable } from '@nestjs/common';
import EventCategory from './event-category.entity';
import { HttpService } from '@nestjs/axios';
@Injectable()
export class EventCategoryService {
constructor(private readonly httpService: HttpService) {}
async findAll(): Promise<{ rows: EventCategory[]; count: number }> {
return EventCategory.findAndCountAll();
}
async findByPk(id: number): Promise<EventCategory> {
return EventCategory.findByPk(id);
}
findOne(category: EventCategory): Promise<EventCategory> {
return EventCategory.findOne({ where: category as any });
}
filter(category: EventCategory): Promise<EventCategory[]> {
return EventCategory.findAll({ where: category as any });
}
async remove(id: number): Promise<number> {
return EventCategory.destroy({ where: { id } });
}
async upsert(category: any, insertIfNotFound: boolean): Promise<EventCategory | [affectedCount: number]> {
if (category.id) {
const existingCategory = await this.findByPk(category.id);
if (existingCategory) {
return EventCategory.update(category, { where: { id: category.id } });
}
}
if (insertIfNotFound) {
return EventCategory.create(category as any);
}
}
}

View File

@ -0,0 +1,312 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { EventEpisodeService } from './event-episodes.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import EventEpisode from './event-episodes.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { EventEpisodeDTO, EventEpisodeUpdateDTO } from './event-episodes.dto';
@ApiTags('event-episode')
@Controller('event-episode')
export class EventEpisodeController {
constructor(private eventEpisodeService: EventEpisodeService) {}
@Get("/all")
@ApiOperation({ summary: 'Get all event episodes' })
@ApiResponse({
status: 200,
description: 'Successfully retrieved all event episodes',
})
@ApiResponse({
status: 404,
description: 'No event episodes found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "No event episodes found"
},
"data": null
}
})
async getAllEventEpisodes(@Res() res: Response) {
const response = await this.eventEpisodeService.findAll() || [];
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event episodes found'
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.send(httpResponse);
}
@Get(':id')
@ApiOperation({ summary: 'Get event episode by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Event Episode ID' })
@ApiResponse({
status: 400,
description: 'ID is required',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NO_ID_REQ",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'Event episode not found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "Event episode not found"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully retrieved event episode by ID',
})
async findById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.eventEpisodeService.findByPk(id) || {};
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Event episode with ID ${id} not found`
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
@Post('/filter')
@ApiOperation({ summary: 'Filter event episodes based on criteria' })
@ApiBody({ type: EventEpisode, description: 'Filter criteria for event episodes' })
@ApiResponse({
status: 400,
description: 'Invalid filter criteria',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.INVALID_CRITERIA",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'No event episodes found based on filter criteria',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "No event episodes found based on the filter criteria"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully filtered event episodes',
})
async filter(@Body() eventEpisode: EventEpisode, @Res() res: Response) {
if (!eventEpisode) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_CRITERIA',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.eventEpisodeService.filter(eventEpisode) || [];
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No event episodes found based on the filter criteria'
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
@Post()
@ApiOperation({ summary: 'Insert a new event episode' })
@ApiBody({ type: EventEpisodeDTO, description: 'Event episode data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid event episode data',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.INVALID_DATA",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 201,
description: 'Successfully created an event episode',
})
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() eventEpisode: EventEpisodeDTO, @Res() res: Response) {
if (!eventEpisode) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_DATA',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.eventEpisodeService.upsert(eventEpisode, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
}
@Put()
@ApiOperation({ summary: 'Update an existing event episode' })
@ApiBody({ type: EventEpisodeUpdateDTO, description: 'Event episode data to update' })
@ApiResponse({
status: 400,
description: 'Invalid event episode data or ID missing',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.INVALID_DATA",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'Event episode not found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "Event episode not found"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully updated event episode',
})
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() eventEpisode: EventEpisodeUpdateDTO, @Res() res: Response) {
if (!eventEpisode || !eventEpisode.id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.eventEpisodeService.upsert(eventEpisode, false);
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Event episode with ID ${eventEpisode.id} not found`
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete an event episode by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Event episode ID to delete' })
@ApiResponse({
status: 400,
description: 'ID parameter is required',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NO_ID_REQ",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'Event episode not found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "Event episode not found"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully deleted event episode',
})
async deleteById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.eventEpisodeService.remove(id) || {};
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Event episode with ID ${id} not found`
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
}

View File

@ -0,0 +1,99 @@
import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class EventEpisodeDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
eventId: number;
@ApiProperty({ type: String })
@IsString()
@IsOptional()
episodeName: string;
@ApiProperty({ type: String })
@IsString()
@IsOptional()
episodeDescription: string;
@ApiProperty({ type: Date })
@IsDate()
@IsOptional()
@Transform(({ value }) => new Date(value))
episodeStartDate: Date;
@ApiProperty({ type: Date })
@IsDate()
@IsOptional()
@Transform(({ value }) => new Date(value))
episodeEndDate: Date;
@ApiProperty({ type: String })
@IsString()
@IsOptional()
episodeLocation: string;
@ApiProperty({ type: String })
@IsString()
@IsOptional()
episodeType: string;
@ApiProperty({ type: Object })
@IsOptional()
images: object;
@ApiProperty({ type: String })
@IsString()
@IsOptional()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@IsOptional()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@IsOptional()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class EventEpisodeUpdateDTO extends EventEpisodeDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number;
}

View File

@ -0,0 +1,74 @@
import { Table, Column, Model, DataType } from 'sequelize-typescript';
import { ApiProperty } from '@nestjs/swagger';
@Table({ tableName: 'event_episodes', paranoid: true })
export default class EventEpisode extends Model {
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, allowNull: false, field: 'event_id' })
eventId: number;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'episode_name' })
episodeName: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'episode_description' })
episodeDescription: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'episode_start_date' })
episodeStartDate: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'episode_end_date' })
episodeEndDate: Date;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'episode_location' })
episodeLocation: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'episode_type' })
episodeType: string;
@ApiProperty({ type: Object })
@Column({ type: DataType.JSON, field: 'images' })
images: object;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'status' })
status: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validFrom' })
validFrom: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validTill' })
validTill: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'createdAt' })
createdAt: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'updatedAt' })
updatedAt: Date;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'createdBy' })
createdBy: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'modifiedBy' })
modifiedBy: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'deletedAt' })
deletedAt: Date;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'version' })
version: number;
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { EventEpisodeController } from './event-episodes.controller';
import { EventEpisodeService } from './event-episodes.service';
import { HttpModule } from '@nestjs/axios';
@Module({
imports: [HttpModule],
providers: [EventEpisodeService],
controllers: [EventEpisodeController],
})
export class EventEpisodeModule {}

View File

@ -0,0 +1,41 @@
import { Injectable } from '@nestjs/common';
import EventEpisode from './event-episodes.entity';
import { HttpService } from '@nestjs/axios';
import { Utility } from 'src/common/Utility';
@Injectable()
export class EventEpisodeService {
constructor(private readonly httpService: HttpService) { }
async findAll(): Promise<{ rows: EventEpisode[], count: number }> {
return EventEpisode.findAndCountAll();
}
async findByPk(id: number): Promise<EventEpisode> {
return EventEpisode.findByPk(id);
}
findOne(eventEpisode: EventEpisode): Promise<EventEpisode> {
return EventEpisode.findOne({ where: eventEpisode as any });
}
filter(eventEpisode: EventEpisode): Promise<EventEpisode[]> {
return EventEpisode.findAll({ where: eventEpisode as any });
}
async remove(id: number): Promise<number> {
return EventEpisode.destroy({ where: { id: id } });
}
async upsert(eventEpisode: any, insertIfNotFound: boolean): Promise<EventEpisode | [affectedCount: number]> {
if (eventEpisode.id) {
const existingEventEpisode = await this.findByPk(eventEpisode.id);
if (existingEventEpisode) {
return EventEpisode.update(eventEpisode, { where: { id: eventEpisode.id } });
}
}
if (insertIfNotFound) {
return EventEpisode.create(eventEpisode as any);
}
}
}

View File

@ -1,14 +1,18 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common';
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UploadedFile, UploadedFiles, UseInterceptors, UsePipes, ValidationPipe } from '@nestjs/common';
import { EventService } from './event.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import Event from './event.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import path from 'path';
import { EventDTO, EventUpdateDTO } from './event.dto';
@ApiTags('event')
@Controller('event')
export class EventController {
constructor(private eventService: EventService) {}
constructor(private eventService: EventService) { }
@Get("/all")
@ApiOperation({ summary: 'Get all events' })
@ -31,7 +35,7 @@ export class EventController {
})
async getAllEvents(@Res() res: Response) {
const response = await this.eventService.findAll() || [];
if(!response) {
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
@ -178,8 +182,34 @@ export class EventController {
status: 201,
description: 'Successfully created a event',
})
async insert(@Body() event: Event, @Res() res: Response) {
if (!event) {
@UseInterceptors(FilesInterceptor('files', 10, {
storage: diskStorage({
destination: './uploads',
filename: (req, file, callback) => {
console.log(file);
const filename = file.originalname;
callback(null, filename);
}
})
}))
@UsePipes(new ValidationPipe({ whitelist: true , skipMissingProperties: true }))
async insert(@Body() body: EventDTO, @Res() res: Response, @UploadedFiles() files?: Express.Multer.File[]) {
// const event = JSON.parse(body)
// console.log(body);
// if (!files) {
// console.log("No files");
// }
if (!body.organizer_id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.MISSING_ORGANIZER_ID',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
// console.log(files);
if (!body || !files) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
@ -188,8 +218,13 @@ export class EventController {
}, null);
return res.status(400).send(response);
}
delete event.id;
const response = await this.eventService.upsert(event, true);
const imageFiles = files.filter(file => file.mimetype.startsWith('image/')).map(file => file.filename);
const videoFiles = files.filter(file => file.mimetype.startsWith('video/')).map(file => file.filename);
body.images = {
images: imageFiles,
videos: videoFiles,
};
const response = await this.eventService.upsert(body, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
}
@ -227,7 +262,8 @@ export class EventController {
status: 200,
description: 'Successfully updated event',
})
async update(@Body() event: Event, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() event: EventUpdateDTO, @Res() res: Response) {
if (!event || !event.id) {
const response = new GenericResponse({
exception: true,

120
src/event/event.dto.ts Normal file
View File

@ -0,0 +1,120 @@
import { IsString, IsNumber, IsDate, IsOptional, IsObject } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class EventDTO {
@ApiProperty({ type: Number })
@IsNumber()
organizer_id: number;
@ApiProperty({ type: String })
@IsString()
eventName: string;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
startDate: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
endDate: Date;
@ApiProperty({ type: Number })
@IsNumber()
theatreId: number;
@ApiProperty({ type: String })
@IsString()
eventTime: string;
@ApiProperty({ type: String })
@IsString()
chiefGuests: string;
@ApiProperty({ type: String })
@IsString()
description: string;
@ApiProperty({ type: Object })
@IsObject()
images: object;
@ApiProperty({ type: String })
@IsString()
categories: string;
@ApiProperty({ type: Number })
@IsNumber()
ageRestriction: number;
@ApiProperty({ type: Number })
@IsNumber()
purchaseLimit: number;
@ApiProperty({ type: String })
@IsString()
cast: string;
@ApiProperty({ type: Number })
@IsNumber()
maxSeating: number;
@ApiProperty({ type: String })
@IsString()
eventType: string;
@ApiProperty({ type: String })
@IsString()
promoted: string;
@ApiProperty({ type: String })
@IsString()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
version: number;
}
export class EventUpdateDTO extends EventDTO {
@ApiProperty({ type: Number })
@IsNumber()
id: number;
}

View File

@ -4,9 +4,9 @@ import { ApiProperty } from '@nestjs/swagger';
@Table({ tableName: 'events', paranoid: true })
export default class Event extends Model {
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'org_email' })
orgEmail: string;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'organizer_id' })
organizerId: number;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'event_name' })

View File

@ -13,11 +13,7 @@ export class EventService {
}
async findByPk(id: number): Promise<Event> {
const event = Event.findByPk(id,)
// //const textbookExists = await this.checkTextbookExists((await event).textbookId);
return event
return Event.findByPk(id);
}
findOne(event: Event): Promise<Event> {
@ -32,7 +28,7 @@ export class EventService {
return Event.destroy({ where: { id: id } });
}
async upsert(event: Event, insertIfNotFound: boolean): Promise<Event | [affectedCount: number]> {
async upsert(event: any, insertIfNotFound: boolean): Promise<Event | [affectedCount: number]> {
if (event.id) {
const existingEvent = await this.findByPk(event.id);
if (existingEvent) {
@ -40,8 +36,6 @@ export class EventService {
}
}
if (insertIfNotFound) {
//const textbookExists = await this.checkTextbookExists(event.textbookId);
return Event.create(event as any)
}
}

View File

@ -1,9 +1,10 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common';
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { EventAdditionalDetailService } from './eventAdditionalDetail.service';
import { Response } from 'express';
import { GenericResponse } from '../../common/GenericResponse.model';
import EventAdditionalDetail from './eventAdditionalDetail.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { EventAdditionalDetailDTO, EventAdditionalDetailUpdateDTO } from './eventAdditionalDetail.dto';
@ApiTags('event/addl')
@Controller('event/addl/')
@ -160,7 +161,7 @@ export class EventAdditionalDetailController {
@Post()
@ApiOperation({ summary: 'Insert a new eventAdditionalDetail' })
@ApiBody({ type: EventAdditionalDetail, description: 'EventAdditionalDetail data to insert' })
@ApiBody({ type: EventAdditionalDetailDTO, description: 'EventAdditionalDetail data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid eventAdditionalDetail data',
@ -178,7 +179,8 @@ export class EventAdditionalDetailController {
status: 201,
description: 'Successfully created a eventAdditionalDetail',
})
async insert(@Body() eventAdditionalDetail: EventAdditionalDetail, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() eventAdditionalDetail: EventAdditionalDetailDTO, @Res() res: Response) {
if (!eventAdditionalDetail) {
const response = new GenericResponse({
exception: true,
@ -188,7 +190,6 @@ export class EventAdditionalDetailController {
}, null);
return res.status(400).send(response);
}
delete eventAdditionalDetail.id;
const response = await this.eventAdditionalDetailService.upsert(eventAdditionalDetail, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
@ -196,7 +197,7 @@ export class EventAdditionalDetailController {
@Put()
@ApiOperation({ summary: 'Update an existing eventAdditionalDetail' })
@ApiBody({ type: EventAdditionalDetail, description: 'EventAdditionalDetail data to update' })
@ApiBody({ type: EventAdditionalDetailUpdateDTO, description: 'EventAdditionalDetail data to update' })
@ApiResponse({
status: 400,
description: 'Invalid eventAdditionalDetail data or ID missing',
@ -227,7 +228,8 @@ export class EventAdditionalDetailController {
status: 200,
description: 'Successfully updated eventAdditionalDetail',
})
async update(@Body() eventAdditionalDetail: EventAdditionalDetail, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() eventAdditionalDetail: EventAdditionalDetailUpdateDTO, @Res() res: Response) {
if (!eventAdditionalDetail || !eventAdditionalDetail.id) {
const response = new GenericResponse({
exception: true,

View File

@ -0,0 +1,66 @@
import { IsString, IsNumber, IsDate, IsOptional } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class EventAdditionalDetailDTO {
@ApiProperty({ type: Number })
@IsNumber()
eventsId: number;
@ApiProperty({ type: String })
@IsString()
addlDataType: string;
@ApiProperty({ type: String })
@IsString()
addlDataName: string;
@ApiProperty({ type: String })
@IsString()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
version: number;
}
export class EventAdditionalDetailUpdateDTO extends EventAdditionalDetailDTO {
@ApiProperty({ type: Number })
@IsNumber()
id: number;
}

View File

@ -15,8 +15,6 @@ export class EventAdditionalDetailService {
async findByPk(id: number): Promise<EventAdditionalDetail> {
const eventAdditionalDetail = EventAdditionalDetail.findByPk(id,)
// //const textbookExists = await this.checkTextbookExists((await eventAdditionalDetail).textbookId);
return eventAdditionalDetail
}
@ -32,7 +30,7 @@ export class EventAdditionalDetailService {
return EventAdditionalDetail.destroy({ where: { id: id } });
}
async upsert(eventAdditionalDetail: EventAdditionalDetail, insertIfNotFound: boolean): Promise<EventAdditionalDetail | [affectedCount: number]> {
async upsert(eventAdditionalDetail: any, insertIfNotFound: boolean): Promise<EventAdditionalDetail | [affectedCount: number]> {
if (eventAdditionalDetail.id) {
const existingEventAdditionalDetail = await this.findByPk(eventAdditionalDetail.id);
if (existingEventAdditionalDetail) {
@ -40,8 +38,6 @@ export class EventAdditionalDetailService {
}
}
if (insertIfNotFound) {
//const textbookExists = await this.checkTextbookExists(eventAdditionalDetail.textbookId);
return EventAdditionalDetail.create(eventAdditionalDetail as any)
}
}

View File

@ -0,0 +1,12 @@
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { AuthGuard } from "@nestjs/passport";
@Injectable()
export class GoogleOauthGuard extends AuthGuard('google') {
constructor() {
super({
// accessType: 'offline',
});
}
}

View File

@ -0,0 +1,30 @@
import { Injectable } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy, VerifyCallback } from "passport-google-oauth20";
import { Utility } from "src/common/Utility";
@Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor() {
super({
clientID: Utility.googleOauthConfig.clientId,
clientSecret: Utility.googleOauthConfig.clientSecret,
callbackURL: Utility.googleOauthConfig.callbackURL,
scope: Utility.googleOauthConfig.scope,
});
}
async validate(accessToken: string, refreshToken: string, profile: any, done: VerifyCallback) {
const { name, emails } = profile;
const user = {
email: emails[0].value,
name: name.givenName,
accessToken,
refreshToken,
};
console.log("user object in validate is", user)
done(null, user);
}
}

View File

@ -11,4 +11,8 @@ export default class RefreshToken extends Model {
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT })
token: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT })
type: string;
}

View File

@ -0,0 +1,30 @@
import { ApiProperty } from '@nestjs/swagger';
export class CreateLocationDto {
@ApiProperty()
name: string;
@ApiProperty()
code: string;
@ApiProperty({ type: [String], required: false })
images?: string[];
@ApiProperty({ required: false })
status?: string;
@ApiProperty({ required: false, type: String, format: 'date' })
validFrom?: Date;
@ApiProperty({ required: false, type: String, format: 'date' })
validTill?: Date;
@ApiProperty({ required: false })
createdBy?: string;
@ApiProperty({ required: false })
modifiedBy?: string;
@ApiProperty({ required: false })
version?: number;
}

View File

@ -0,0 +1,7 @@
import { PartialType, ApiProperty } from '@nestjs/swagger';
import { CreateLocationDto } from './create-location.dto';
export class UpdateLocationDto extends PartialType(CreateLocationDto) {
@ApiProperty()
id: number;
}

View File

@ -0,0 +1,67 @@
import {
Table,
Column,
Model,
DataType,
PrimaryKey,
AutoIncrement,
Default,
} from 'sequelize-typescript';
import { ApiProperty } from '@nestjs/swagger';
@Table({ tableName: 'locations' })
export class Location extends Model {
@ApiProperty({ type: Number })
@PrimaryKey
@AutoIncrement
@Column({ type: DataType.BIGINT })
id: number;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT })
name: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT })
code: string;
@ApiProperty({ type: [String] })
@Column({ type: DataType.ARRAY(DataType.TEXT) })
images: string[];
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT })
status: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY })
validFrom: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY })
validTill: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE })
createdAt: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE })
updatedAt: Date;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT })
createdBy: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT })
modifiedBy: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE })
deletedAt: Date;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER })
version: number;
}

View File

@ -0,0 +1,20 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LocationsController } from './locations.controller';
import { LocationsService } from './locations.service';
describe('LocationsController', () => {
let controller: LocationsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [LocationsController],
providers: [LocationsService],
}).compile();
controller = module.get<LocationsController>(LocationsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@ -0,0 +1,41 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { LocationsService } from './locations.service';
import { CreateLocationDto } from './dto/create-location.dto';
import { UpdateLocationDto } from './dto/update-location.dto';
@Controller('locations')
export class LocationsController {
constructor(private readonly locationsService: LocationsService) { }
@Post()
create(@Body() createLocationDto: CreateLocationDto) {
return this.locationsService.create(createLocationDto);
}
@Get()
findAll() {
return this.locationsService.findAll();
}
@Get('search')
async search(@Query('q') query: string) {
return this.locationsService.search(query);
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.locationsService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateLocationDto: UpdateLocationDto) {
return this.locationsService.update(+id, updateLocationDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.locationsService.remove(+id);
}
}

View File

@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { LocationsService } from './locations.service';
import { LocationsController } from './locations.controller';
@Module({
controllers: [LocationsController],
providers: [LocationsService],
})
export class LocationsModule {}

View File

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LocationsService } from './locations.service';
describe('LocationsService', () => {
let service: LocationsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [LocationsService],
}).compile();
service = module.get<LocationsService>(LocationsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,56 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateLocationDto } from './dto/create-location.dto';
import { UpdateLocationDto } from './dto/update-location.dto';
import { Location } from './entities/location.entity';
import { Op } from 'sequelize';
@Injectable()
export class LocationsService {
async create(createLocationDto: CreateLocationDto) {
return await Location.create(createLocationDto as any);
}
async findAll() {
return await Location.findAll();
}
async findOne(id: number) {
const location = await Location.findByPk(id);
if (!location) {
throw new NotFoundException(`Location with ID ${id} not found`);
}
return location;
}
async update(id: number, updateLocationDto: UpdateLocationDto) {
const location = await Location.findByPk(id);
if (!location) {
throw new NotFoundException(`Location with ID ${id} not found`);
}
await location.update(updateLocationDto);
return location;
}
async remove(id: number) {
const location = await Location.findByPk(id);
if (!location) {
throw new NotFoundException(`Location with ID ${id} not found`);
}
await location.destroy();
return { message: `Location with ID ${id} has been deleted.` };
}
async search(q: string) {
if (!q) return []; // no query, return empty list or all
const locations = await Location.findAll({
where: {
[Op.or]: [
{ name: { [Op.iLike]: `%${q}%` } },
{ code: { [Op.iLike]: `%${q}%` } }
],
},
});
return locations;
}
}

View File

@ -0,0 +1,17 @@
import { Body, Controller, Post } from '@nestjs/common';
import { MailService } from './mail.service';
@Controller('mail')
export class MailController {
constructor(private readonly mailService: MailService) {}
@Post()
async sendTestEmail(@Body() body: { email: string }) {
const { email } = body;
return this.mailService.sendMail(
email,
'Test Email',
'This is a test email.',
);
}
}

View File

@ -4,6 +4,7 @@ import { Module } from '@nestjs/common';
import { MailService } from './mail.service';
import { join } from 'path';
import { Utility } from 'src/common/Utility';
import { MailController } from './mail.controller';
@Module({
imports: [
@ -23,5 +24,6 @@ import { Utility } from 'src/common/Utility';
],
providers: [MailService],
exports: [MailService], // 👈 export for DI
controllers: [MailController],
})
export class MailModule {}
export class MailModule { }

View File

@ -1,19 +1,25 @@
// import { MailerService } from '@nestjs-modules/mailer'/;
import { Injectable } from '@nestjs/common';
import * as nodemailer from 'nodemailer';
import { Utility } from 'src/common/Utility';
@Injectable()
export class MailService {
// constructor(private mailerService: MailerService) { }
private transporter: nodemailer.Transporter;
constructor() {
this.transporter = nodemailer.createTransport(Utility.mailConfig.transport);
}
// async sendEmail(templateName: string, subject: string, context: any, toEmail: string, ccEmails?: string[], bccEmails?: string[]) {
// await this.mailerService.sendMail({
// to: toEmail,
// cc: ccEmails,
// bcc: bccEmails,
// subject: subject,
// template: templateName, // `.hbs` extension is appended automatically
// context,
// })
// }
async sendMail(to: string, subject: string, text: string, html?: string) {
const info = await this.transporter.sendMail({
from: Utility.mailConfig.defaults.from,
to,
subject,
text,
html,
});
console.log('Email sent:', info.messageId);
return info;
}
}

View File

@ -4,6 +4,8 @@ import * as bodyParser from 'body-parser';
import * as configMaster from './app-config/config.json';
import { Utility } from './common/Utility';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import * as cookieParser from 'cookie-parser';
async function bootstrap() {
Utility.appPort = configMaster.local.appConfig.port;
@ -11,6 +13,8 @@ async function bootstrap() {
Utility.fileConfig = configMaster.local.fileConfig;
Utility.swaggerConfig = configMaster.local.swaggerConfig;
Utility.vapidConfig = configMaster.local.vapidConfig;
Utility.redisConfig = configMaster.local.redisConfig;
Utility.googleOauthConfig = configMaster.local.googleOauthConfig;
const app = await NestFactory.create(AppModule, { cors: true });
@ -23,6 +27,14 @@ async function bootstrap() {
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
app.enableCors(
{
origin: true,
credentials: true,
}
);
app.use(cookieParser());
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
await app.listen(Utility.appPort);

View File

@ -1,14 +1,15 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common';
import { BadRequestException, Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { PayoutService } from './payout.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import Payout from './payout.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { PayoutDTO, PayoutUpdateDTO } from './payout.dto';
@ApiTags('payout')
@Controller('payout')
export class PayoutController {
constructor(private payoutService: PayoutService) {}
constructor(private payoutService: PayoutService) { }
@Get("/all")
@ApiOperation({ summary: 'Get all payouts' })
@ -30,8 +31,8 @@ export class PayoutController {
}
})
async getAllPayouts(@Res() res: Response) {
const response = await this.payoutService.findAll() || [];
if(!response) {
const response = await this.payoutService.findAll();
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
@ -87,7 +88,7 @@ export class PayoutController {
}, null);
return res.status(400).send(response);
}
const response = await this.payoutService.findByPk(id) || {};
const response = await this.payoutService.findByPk(id);
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
@ -178,17 +179,17 @@ export class PayoutController {
status: 201,
description: 'Successfully created a payout',
})
async insert(@Body() payout: Payout, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true }))
async insert(@Body() payout: PayoutDTO, @Res() res: Response) {
if (!payout) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_DATA',
stackTrace: 'Request'
stackTrace: 'Request body is invalid'
}, null);
return res.status(400).send(response);
}
delete payout.id;
const response = await this.payoutService.upsert(payout, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
@ -227,7 +228,8 @@ export class PayoutController {
status: 200,
description: 'Successfully updated payout',
})
async update(@Body() payout: Payout, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() payout: PayoutUpdateDTO, @Res() res: Response) {
if (!payout || !payout.id) {
const response = new GenericResponse({
exception: true,

88
src/payout/payout.dto.ts Normal file
View File

@ -0,0 +1,88 @@
import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty, IsEnum } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class PayoutDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
payeeId: number;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
amount: number;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
paymentMethod: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
transactionId: string;
@ApiProperty({ type: Number })
@IsNotEmpty()
@IsNumber()
paidToId: number;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
payoutDate: Date;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class PayoutUpdateDTO extends PayoutDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number
}

View File

@ -4,9 +4,9 @@ import { ApiProperty } from '@nestjs/swagger';
@Table({ tableName: 'payouts', paranoid: true })
export default class Payout extends Model {
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'payee_email' })
payeeEmail: string;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'payee_id' })
payeeId: number;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'amount' })
@ -21,8 +21,8 @@ export default class Payout extends Model {
transactionId: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'paid_to_email' })
paidToEmail: string;
@Column({ type: DataType.TEXT, field: 'paid_to_id' })
paidToId: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'payout_date' })

View File

@ -3,6 +3,7 @@ import Payout from './payout.entity';
import { firstValueFrom } from 'rxjs';
import { HttpService } from "@nestjs/axios";
import { Utility } from 'src/common/Utility';
import { PayoutDTO, PayoutUpdateDTO } from './payout.dto';
@Injectable()
export class PayoutService {
@ -13,11 +14,7 @@ export class PayoutService {
}
async findByPk(id: number): Promise<Payout> {
const payout = Payout.findByPk(id,)
// //const textbookExists = await this.checkTextbookExists((await payout).textbookId);
return payout
return Payout.findByPk(id);
}
findOne(payout: Payout): Promise<Payout> {
@ -32,7 +29,7 @@ export class PayoutService {
return Payout.destroy({ where: { id: id } });
}
async upsert(payout: Payout, insertIfNotFound: boolean): Promise<Payout | [affectedCount: number]> {
async upsert(payout: any, insertIfNotFound: boolean): Promise<Payout | [affectedCount: number]> {
if (payout.id) {
const existingPayout = await this.findByPk(payout.id);
if (existingPayout) {
@ -40,8 +37,6 @@ export class PayoutService {
}
}
if (insertIfNotFound) {
//const textbookExists = await this.checkTextbookExists(payout.textbookId);
return Payout.create(payout as any)
}
}

View File

@ -25,7 +25,7 @@ export class PolicyService {
return Policy.destroy({ where: { id: id } });
}
async upsert(policy: Policy, insertIfNotFound: boolean): Promise<Policy | [affectedCount: number]> {
async upsert(policy: any, insertIfNotFound: boolean): Promise<Policy | [affectedCount: number]> {
if (policy.id) {
const existingPolicy = await this.findByPk(policy.id);
if (existingPolicy) {

View File

@ -0,0 +1,312 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { PromotionService } from './promotions.service'; // Updated service
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import Promotion from './promotions.entity'; // Updated entity
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { PromotionDTO, PromotionUpdateDTO } from './promotions.dto';
@ApiTags('promotion')
@Controller('promotion')
export class PromotionController {
constructor(private promotionService: PromotionService) {}
@Get("/all")
@ApiOperation({ summary: 'Get all promotions' })
@ApiResponse({
status: 200,
description: 'Successfully retrieved all promotions',
})
@ApiResponse({
status: 404,
description: 'No promotions found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "No promotions found"
},
"data": null
}
})
async getAllPromotions(@Res() res: Response) {
const response = await this.promotionService.findAll() || [];
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No promotions found'
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.send(httpResponse);
}
@Get(':id')
@ApiOperation({ summary: 'Get promotion by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Promotion ID' })
@ApiResponse({
status: 400,
description: 'ID is required',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NO_ID_REQ",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'Promotion not found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "Promotion not found"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully retrieved promotion by ID',
})
async findById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.promotionService.findByPk(id) || {};
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Promotion with ID ${id} not found`
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
@Post('/filter')
@ApiOperation({ summary: 'Filter promotions based on criteria' })
@ApiBody({ type: Promotion, description: 'Filter criteria for promotions' })
@ApiResponse({
status: 400,
description: 'Invalid filter criteria',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.INVALID_CRITERIA",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'No promotions found based on filter criteria',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "No promotions found based on the filter criteria"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully filtered promotions',
})
async filter(@Body() promotion: Promotion, @Res() res: Response) {
if (!promotion) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_CRITERIA',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.promotionService.filter(promotion) || [];
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: 'No promotions found based on the filter criteria'
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
@Post()
@ApiOperation({ summary: 'Insert a new promotion' })
@ApiBody({ type: PromotionDTO, description: 'Promotion data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid promotion data',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.INVALID_DATA",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 201,
description: 'Successfully created a promotion',
})
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() promotion: PromotionDTO, @Res() res: Response) {
if (!promotion) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.INVALID_DATA',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.promotionService.upsert(promotion, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
}
@Put()
@ApiOperation({ summary: 'Update an existing promotion' })
@ApiBody({ type: PromotionUpdateDTO, description: 'Promotion data to update' })
@ApiResponse({
status: 400,
description: 'Invalid promotion data or ID missing',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.INVALID_DATA",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'Promotion not found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "Promotion not found"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully updated promotion',
})
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() promotion: PromotionUpdateDTO, @Res() res: Response) {
if (!promotion || !promotion.id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.promotionService.upsert(promotion, false);
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Promotion with ID ${promotion.id} not found`
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
@Delete(':id')
@ApiOperation({ summary: 'Delete a promotion by ID' })
@ApiParam({ name: 'id', type: Number, description: 'Promotion ID to delete' })
@ApiResponse({
status: 400,
description: 'ID parameter is required',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NO_ID_REQ",
"stackTrace": "Request"
},
"data": null
}
})
@ApiResponse({
status: 404,
description: 'Promotion not found',
example: {
"notification": {
"exception": true,
"exceptionSeverity": "HIGH",
"exceptionMessage": "ERR.NOT_FOUND",
"stackTrace": "Promotion not found"
},
"data": null
}
})
@ApiResponse({
status: 200,
description: 'Successfully deleted promotion',
})
async deleteById(@Param('id') id: number, @Res() res: Response) {
if (!id) {
const response = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NO_ID_REQ',
stackTrace: 'Request'
}, null);
return res.status(400).send(response);
}
const response = await this.promotionService.remove(id) || {};
if (!response) {
const errorResponse = new GenericResponse({
exception: true,
exceptionSeverity: 'HIGH',
exceptionMessage: 'ERR.NOT_FOUND',
stackTrace: `Promotion with ID ${id} not found`
}, null);
return res.status(404).send(errorResponse);
}
const httpResponse = new GenericResponse(null, response);
res.status(200).send(httpResponse);
}
}

View File

@ -0,0 +1,91 @@
import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class PromotionDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
eventId: number;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
startDate: Date;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
endDate: Date;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
location: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
type: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
description: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class PromotionUpdateDTO extends PromotionDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number
}

View File

@ -0,0 +1,66 @@
import { Table, Column, Model, DataType } from 'sequelize-typescript';
import { ApiProperty } from '@nestjs/swagger';
@Table({ tableName: 'promotions', paranoid: true })
export default class Promotion extends Model {
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, allowNull: false, field: 'event_id' })
eventId: number;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'start_date' })
startDate: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'end_date' })
endDate: Date;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'location' })
location: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'type' })
type: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'description' })
description: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'status' })
status: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validFrom' })
validFrom: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATEONLY, field: 'validTill' })
validTill: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'createdAt' })
createdAt: Date;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'updatedAt' })
updatedAt: Date;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'createdBy' })
createdBy: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'modifiedBy' })
modifiedBy: string;
@ApiProperty({ type: Date })
@Column({ type: DataType.DATE, field: 'deletedAt' })
deletedAt: Date;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'version' })
version: number;
}

View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { PromotionController } from './promotions.controller';
import { PromotionService } from './promotions.service';
import { HttpModule } from '@nestjs/axios';
@Module({
imports: [HttpModule],
providers: [PromotionService],
controllers: [PromotionController],
})
export class PromotionModule { }

View File

@ -0,0 +1,41 @@
import { Injectable } from '@nestjs/common';
import Promotion from './promotions.entity';
import { HttpService } from '@nestjs/axios';
import { Utility } from 'src/common/Utility';
@Injectable()
export class PromotionService {
constructor(private readonly httpService: HttpService) { }
async findAll(): Promise<{ rows: Promotion[], count: number }> {
return Promotion.findAndCountAll();
}
async findByPk(id: number): Promise<Promotion> {
return Promotion.findByPk(id);
}
findOne(promotion: Promotion): Promise<Promotion> {
return Promotion.findOne({ where: promotion as any });
}
filter(promotion: Promotion): Promise<Promotion[]> {
return Promotion.findAll({ where: promotion as any });
}
async remove(id: number): Promise<number> {
return Promotion.destroy({ where: { id: id } });
}
async upsert(promotion: any, insertIfNotFound: boolean): Promise<Promotion | [affectedCount: number]> {
if (promotion.id) {
const existingPromotion = await this.findByPk(promotion.id);
if (existingPromotion) {
return Promotion.update(promotion, { where: { id: promotion.id } });
}
}
if (insertIfNotFound) {
return Promotion.create(promotion as any);
}
}
}

View File

@ -1,18 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { PushSubscriptionController } from './push-subscription.controller';
describe('PushSubscriptionController', () => {
let controller: PushSubscriptionController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [PushSubscriptionController],
}).compile();
controller = module.get<PushSubscriptionController>(PushSubscriptionController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});

View File

@ -7,14 +7,14 @@ export class PushSubscriptionController {
constructor(private readonly pushSubscriptionService: PushSubscriptionService) { }
@Post('subscribe')
async subscribe(@Body() body: any) {
async subscribe(@Body() body: {endpoint: string, p256dh: string, auth: string}) {
console.log(body);
await this.pushSubscriptionService.saveSubscription(body);
return 'subscribed';
}
@Post('send-notification')
async sendNotification(@Body() body: any) {
async sendNotification(@Body() body: { title: string, message: string }) {
await this.pushSubscriptionService.sendPushNotification(body.title, body.message);
return 'notification sent';
}

View File

@ -1,6 +1,6 @@
import * as webPush from 'web-push';
import { Injectable } from '@nestjs/common';
import PushSubscription from './push-subscription.entity';
import PushSubscription from './push-subscription.entity';
import { Utility } from 'src/common/Utility';
@Injectable()
@ -19,25 +19,24 @@ export class PushSubscriptionService {
return 'saved subscription';
}
async sendPushNotification(title: any, message: any) {
const subs = await PushSubscription.findAll();
var pushSub: any;
for (const sub of subs) {
pushSub = {
async sendPushNotification(title: string, message: string) {
const subscriptions = await PushSubscription.findAll();
const payload = JSON.stringify({ title, message });
for (const sub of subscriptions) {
const pushSub = {
endpoint: sub.endpoint,
keys: {
p256dh: sub.p256dh,
auth: sub.auth,
},
};
}
const payload = JSON.stringify({ title, message })
try {
await webPush.sendPushNotification(pushSub, payload)
} catch (err) {
console.log("error occurred", err)
try {
await webPush.sendNotification(pushSub, payload);
} catch (err) {
console.error('Error sending push notification', err);
}
}
}
}

View File

@ -0,0 +1,66 @@
import { Controller, Get, Param, Post, Body, Delete } from '@nestjs/common';
import { RedisService } from './redis.service';
import { BadRequestException, InternalServerErrorException } from '@nestjs/common';
import { Logger } from '@nestjs/common';
@Controller('redis')
export class RedisController {
private readonly logger = new Logger(RedisController.name);
constructor(private readonly redisService: RedisService) {}
@Post('set')
async setValue(@Body() body: { key: string; value: string; ttl?: number }) {
if (body.ttl && body.ttl <= 0) {
throw new BadRequestException('TTL must be a positive number');
}
try {
if (body.ttl) {
await this.redisService.setTimed(body.key, body.value, body.ttl);
} else {
await this.redisService.set(body.key, body.value);
}
return { success: true, message: 'Value set in Redis' };
} catch (error) {
this.logger.error('Failed to set value in Redis', error.stack);
throw new InternalServerErrorException('Failed to set value in Redis');
}
}
@Get('get/:key')
async getValue(@Param('key') key: string) {
try {
const value = await this.redisService.get(key);
if (value === null) {
throw new BadRequestException('Key not found');
}
return { success: true, value };
} catch (error) {
this.logger.error('Failed to get value from Redis', error.stack);
throw new InternalServerErrorException('Failed to get value from Redis');
}
}
@Delete('del/:key')
async deleteValue(@Param('key') key: string) {
try {
await this.redisService.del(key);
return { success: true, message: 'Value deleted from Redis' };
} catch (error) {
this.logger.error('Failed to delete value from Redis', error.stack);
throw new InternalServerErrorException('Failed to delete value from Redis');
}
}
@Get('flushall')
async flushAll() {
try {
await this.redisService.flushall();
return { success: true, message: 'Redis flushed' };
} catch (error) {
this.logger.error('Failed to flush Redis', error.stack);
throw new InternalServerErrorException('Failed to flush Redis');
}
}
}

11
src/redis/redis.module.ts Normal file
View File

@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { RedisProvider } from './redis.provider';
import { RedisController } from './redis.controller';
import { RedisService } from './redis.service';
@Module({
controllers: [RedisController],
providers: [RedisService, RedisProvider],
exports: [RedisProvider, RedisService],
})
export class RedisModule { }

View File

@ -0,0 +1,27 @@
import { Injectable, Global } from '@nestjs/common';
import Redis from 'ioredis';
import { Utility } from 'src/common/Utility';
@Global()
@Injectable()
export class RedisProvider {
private redisClient: Redis;
constructor() {
this.redisClient = new Redis({
host: Utility.redisConfig.host,
port: Utility.redisConfig.port,
db: Utility.redisConfig.db,
username: Utility.redisConfig.username || 'default', // For Redis >= 6 with ACLs
password: Utility.redisConfig.password
});
this.redisClient.on('connect', () => console.log('connected to redis'));
this.redisClient.on('error', (err) => console.error('error:', err));
}
get client() {
return this.redisClient;
}
}

View File

@ -0,0 +1,32 @@
import { Injectable } from '@nestjs/common';
import { RedisProvider } from './redis.provider';
@Injectable()
export class RedisService {
constructor(private readonly redisProvider: RedisProvider) {}
async set(key: string, value: any): Promise<void> {
const client = this.redisProvider.client;
await client.set(key, value, );
}
async del(key: string): Promise<void> {
const client = this.redisProvider.client;
await client.del(key);
}
async flushall(): Promise<void> {
const client = this.redisProvider.client;
await client.flushall();
}
async setTimed(key: string, value: any, ttl: number): Promise<void> {
const client = this.redisProvider.client;
await client.set(key, value, 'EX', ttl);
}
async get(key: string): Promise<string> {
const client = this.redisProvider.client;
return client.get(key);
}
}

View File

@ -1,9 +1,10 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common';
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { RefundService } from './refund.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import Refund from './refund.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { RefundDTO, RefundUpdateDTO } from './refund.dto';
@ApiTags('refund')
@Controller('refund')
@ -160,7 +161,7 @@ export class RefundController {
@Post()
@ApiOperation({ summary: 'Insert a new refund' })
@ApiBody({ type: Refund, description: 'Refund data to insert' })
@ApiBody({ type: RefundDTO, description: 'Refund data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid refund data',
@ -178,7 +179,8 @@ export class RefundController {
status: 201,
description: 'Successfully created a refund',
})
async insert(@Body() refund: Refund, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() refund: RefundDTO, @Res() res: Response) {
if (!refund) {
const response = new GenericResponse({
exception: true,
@ -188,7 +190,6 @@ export class RefundController {
}, null);
return res.status(400).send(response);
}
delete refund.id;
const response = await this.refundService.upsert(refund, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
@ -196,7 +197,7 @@ export class RefundController {
@Put()
@ApiOperation({ summary: 'Update an existing refund' })
@ApiBody({ type: Refund, description: 'Refund data to update' })
@ApiBody({ type: RefundUpdateDTO, description: 'Refund data to update' })
@ApiResponse({
status: 400,
description: 'Invalid refund data or ID missing',
@ -227,7 +228,8 @@ export class RefundController {
status: 200,
description: 'Successfully updated refund',
})
async update(@Body() refund: Refund, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() refund: RefundUpdateDTO, @Res() res: Response) {
if (!refund || !refund.id) {
const response = new GenericResponse({
exception: true,

83
src/refund/refund.dto.ts Normal file
View File

@ -0,0 +1,83 @@
import { IsString, IsNumber, IsDate, IsOptional, IsNotEmpty, IsEmail } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class RefundDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
ticketId: number;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
refundAmount: number;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
refundPercentage: number;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
refundReason: string;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
refundDate: Date;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class RefundUpdateDTO extends RefundDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number
}

View File

@ -13,11 +13,7 @@ export class RefundService {
}
async findByPk(id: number): Promise<Refund> {
const refund = Refund.findByPk(id,)
// //const textbookExists = await this.checkTextbookExists((await refund).textbookId);
return refund
return Refund.findByPk(id);
}
findOne(refund: Refund): Promise<Refund> {
@ -32,7 +28,7 @@ export class RefundService {
return Refund.destroy({ where: { id: id } });
}
async upsert(refund: Refund, insertIfNotFound: boolean): Promise<Refund | [affectedCount: number]> {
async upsert(refund: any, insertIfNotFound: boolean): Promise<Refund | [affectedCount: number]> {
if (refund.id) {
const existingRefund = await this.findByPk(refund.id);
if (existingRefund) {
@ -40,8 +36,6 @@ export class RefundService {
}
}
if (insertIfNotFound) {
//const textbookExists = await this.checkTextbookExists(refund.textbookId);
return Refund.create(refund as any)
}
}

View File

@ -25,7 +25,7 @@ export class ResourcesService {
return Resources.destroy({ where: { id: id } });
}
async upsert(resources: Resources, insertIfNotFound: boolean): Promise<Resources | [affectedCount: number]> {
async upsert(resources: any, insertIfNotFound: boolean): Promise<Resources | [affectedCount: number]> {
if (resources.id) {
const existingResources = await this.findByPk(resources.id);
if (existingResources) {

View File

@ -1,9 +1,10 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common';
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { ReviewService } from './review.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import Review from './review.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { ReviewDTO, ReviewUpdateDTO } from './review.dto';
@ApiTags('review')
@Controller('review')
@ -160,7 +161,7 @@ export class ReviewController {
@Post()
@ApiOperation({ summary: 'Insert a new review' })
@ApiBody({ type: Review, description: 'Review data to insert' })
@ApiBody({ type: ReviewDTO, description: 'Review data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid review data',
@ -178,7 +179,8 @@ export class ReviewController {
status: 201,
description: 'Successfully created a review',
})
async insert(@Body() review: Review, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() review: ReviewDTO, @Res() res: Response) {
if (!review) {
const response = new GenericResponse({
exception: true,
@ -188,7 +190,6 @@ export class ReviewController {
}, null);
return res.status(400).send(response);
}
delete review.id;
const response = await this.reviewService.upsert(review, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
@ -196,7 +197,7 @@ export class ReviewController {
@Put()
@ApiOperation({ summary: 'Update an existing review' })
@ApiBody({ type: Review, description: 'Review data to update' })
@ApiBody({ type: ReviewUpdateDTO, description: 'Review data to update' })
@ApiResponse({
status: 400,
description: 'Invalid review data or ID missing',
@ -227,7 +228,8 @@ export class ReviewController {
status: 200,
description: 'Successfully updated review',
})
async update(@Body() review: Review, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() review: ReviewUpdateDTO, @Res() res: Response) {
if (!review || !review.id) {
const response = new GenericResponse({
exception: true,

79
src/review/review.dto.ts Normal file
View File

@ -0,0 +1,79 @@
import { IsString, IsNumber, IsDate, IsNotEmpty, IsOptional } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class ReviewDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
eventId: number;
@ApiProperty({ type: Number })
@IsNotEmpty()
@IsNumber()
buyerId: number;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
rating: number;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
comment: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class ReviewUpdateDTO extends ReviewDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number;
}

View File

@ -9,8 +9,8 @@ export default class Review extends Model {
eventId: number;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'buyer_email' })
buyerEmail: string;
@Column({ type: DataType.TEXT, field: 'buyer_id' })
buyerId: string;
@ApiProperty({ type: Number })
@Column({ type: DataType.NUMBER, field: 'rating' })

View File

@ -13,11 +13,7 @@ export class ReviewService {
}
async findByPk(id: number): Promise<Review> {
const review = Review.findByPk(id,)
// //const textbookExists = await this.checkTextbookExists((await review).textbookId);
return review
return Review.findByPk(id);
}
findOne(review: Review): Promise<Review> {
@ -32,7 +28,7 @@ export class ReviewService {
return Review.destroy({ where: { id: id } });
}
async upsert(review: Review, insertIfNotFound: boolean): Promise<Review | [affectedCount: number]> {
async upsert(review: any, insertIfNotFound: boolean): Promise<Review | [affectedCount: number]> {
if (review.id) {
const existingReview = await this.findByPk(review.id);
if (existingReview) {
@ -40,8 +36,6 @@ export class ReviewService {
}
}
if (insertIfNotFound) {
//const textbookExists = await this.checkTextbookExists(review.textbookId);
return Review.create(review as any)
}
}

View File

@ -1,14 +1,16 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common';
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { SeatService } from './seat.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import Seat from './seat.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { RedisService } from 'src/redis/redis.service';
import { SeatDTO, SeatUpdateDTO } from './seat.dto';
@ApiTags('seat')
@Controller('seat')
export class SeatController {
constructor(private seatService: SeatService) {}
constructor(private seatService: SeatService, private redisService: RedisService) {}
@Get("/all")
@ApiOperation({ summary: 'Get all seats' })
@ -30,7 +32,12 @@ export class SeatController {
}
})
async getAllSeats(@Res() res: Response) {
const response = await this.seatService.findAll() || [];
// const cacheKey = 'all_seats';
// let response = await this.redisService.get(cacheKey);
// if (!response) {
const response = await this.seatService.findAll() || [];
// await this.redisService.setTimed(cacheKey, seats,300);
// }
if(!response) {
const errorResponse = new GenericResponse({
exception: true,
@ -160,7 +167,7 @@ export class SeatController {
@Post()
@ApiOperation({ summary: 'Insert a new seat' })
@ApiBody({ type: Seat, description: 'Seat data to insert' })
@ApiBody({ type: SeatDTO, description: 'Seat data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid seat data',
@ -178,7 +185,8 @@ export class SeatController {
status: 201,
description: 'Successfully created a seat',
})
async insert(@Body() seat: Seat, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() seat: SeatDTO, @Res() res: Response) {
if (!seat) {
const response = new GenericResponse({
exception: true,
@ -188,7 +196,6 @@ export class SeatController {
}, null);
return res.status(400).send(response);
}
delete seat.id;
const response = await this.seatService.upsert(seat, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
@ -196,7 +203,7 @@ export class SeatController {
@Put()
@ApiOperation({ summary: 'Update an existing seat' })
@ApiBody({ type: Seat, description: 'Seat data to update' })
@ApiBody({ type: SeatUpdateDTO, description: 'Seat data to update' })
@ApiResponse({
status: 400,
description: 'Invalid seat data or ID missing',
@ -227,7 +234,8 @@ export class SeatController {
status: 200,
description: 'Successfully updated seat',
})
async update(@Body() seat: Seat, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() seat: SeatUpdateDTO, @Res() res: Response) {
if (!seat || !seat.id) {
const response = new GenericResponse({
exception: true,

84
src/seat/seat.dto.ts Normal file
View File

@ -0,0 +1,84 @@
import { IsString, IsNumber, IsDate, IsNotEmpty, IsOptional } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class SeatDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
eventId: number;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
seatNumber: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
row: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
column: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
available: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class SeatUpdateDTO extends SeatDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number;
}

View File

@ -20,6 +20,10 @@ export default class Seat extends Model {
@Column({ type: DataType.TEXT, field: 'column' })
column: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'available' })
available: string;
@ApiProperty({ type: String })
@Column({ type: DataType.TEXT, field: 'status' })
status: string;

View File

@ -2,9 +2,13 @@ import { Module } from '@nestjs/common';
import { SeatController } from './seat.controller';
import { SeatService } from './seat.service';
import { HttpModule } from '@nestjs/axios';
// import { RedisService } from 'src/redis/redis.service';
// import { RedisProvider } from 'src/redis/redis.provider';
// import Redis from 'ioredis';
import { RedisModule } from 'src/redis/redis.module';
@Module({
imports: [HttpModule],
imports: [HttpModule, RedisModule],
providers: [SeatService],
controllers: [SeatController],
})

View File

@ -13,11 +13,7 @@ export class SeatService {
}
async findByPk(id: number): Promise<Seat> {
const seat = Seat.findByPk(id,)
// //const textbookExists = await this.checkTextbookExists((await seat).textbookId);
return seat
return Seat.findByPk(id);
}
findOne(seat: Seat): Promise<Seat> {
@ -32,7 +28,11 @@ export class SeatService {
return Seat.destroy({ where: { id: id } });
}
async upsert(seat: Seat, insertIfNotFound: boolean): Promise<Seat | [affectedCount: number]> {
async bookSeat(eventId: number, seatNumber: string) {
return Seat.update({ available: 'booked' }, { where: { eventId: eventId, seatNumber: seatNumber } });
}
async upsert(seat: any, insertIfNotFound: boolean): Promise<Seat | [affectedCount: number]> {
if (seat.id) {
const existingSeat = await this.findByPk(seat.id);
if (existingSeat) {
@ -40,8 +40,6 @@ export class SeatService {
}
}
if (insertIfNotFound) {
//const textbookExists = await this.checkTextbookExists(seat.textbookId);
return Seat.create(seat as any)
}
}

View File

@ -1,9 +1,10 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common';
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { TheatreService } from './theatre.service';
import { Response } from 'express';
import { GenericResponse } from '../common/GenericResponse.model';
import Theatre from './theatre.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { TheatreDTO, TheatreUpdateDTO } from './theatre.dto';
@ApiTags('theatre')
@Controller('theatre')
@ -160,7 +161,7 @@ export class TheatreController {
@Post()
@ApiOperation({ summary: 'Insert a new theatre' })
@ApiBody({ type: Theatre, description: 'Theatre data to insert' })
@ApiBody({ type: TheatreDTO, description: 'Theatre data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid theatre data',
@ -178,7 +179,8 @@ export class TheatreController {
status: 201,
description: 'Successfully created a theatre',
})
async insert(@Body() theatre: Theatre, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() theatre: TheatreDTO, @Res() res: Response) {
if (!theatre) {
const response = new GenericResponse({
exception: true,
@ -188,7 +190,6 @@ export class TheatreController {
}, null);
return res.status(400).send(response);
}
delete theatre.id;
const response = await this.theatreService.upsert(theatre, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
@ -196,7 +197,7 @@ export class TheatreController {
@Put()
@ApiOperation({ summary: 'Update an existing theatre' })
@ApiBody({ type: Theatre, description: 'Theatre data to update' })
@ApiBody({ type: TheatreUpdateDTO, description: 'Theatre data to update' })
@ApiResponse({
status: 400,
description: 'Invalid theatre data or ID missing',
@ -227,7 +228,8 @@ export class TheatreController {
status: 200,
description: 'Successfully updated theatre',
})
async update(@Body() theatre: Theatre, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() theatre: TheatreUpdateDTO, @Res() res: Response) {
if (!theatre || !theatre.id) {
const response = new GenericResponse({
exception: true,

View File

@ -0,0 +1,94 @@
import { IsString, IsNumber, IsDate, IsNotEmpty, IsOptional, IsObject } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
export class TheatreDTO {
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
theatreName: string;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
rows: number;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
columns: number;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
address: string;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
city: string;
@ApiProperty({ type: Object })
@IsObject()
@IsNotEmpty()
images: object;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
totalSeats: number;
@ApiProperty({ type: String })
@IsString()
@IsNotEmpty()
status: string;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validFrom: Date;
@ApiProperty({ type: Date })
@IsDate()
@IsNotEmpty()
@Transform(({ value }) => new Date(value))
validTill: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
createdAt: Date;
@ApiProperty({ type: Date })
@IsDate()
@Transform(({ value }) => new Date(value))
updatedAt: Date;
@ApiProperty({ type: String })
@IsString()
createdBy: string;
@ApiProperty({ type: String })
@IsString()
modifiedBy: string;
@ApiProperty({ type: Date })
@IsOptional()
@IsDate()
@Transform(({ value }) => value ? new Date(value) : null)
deletedAt: Date;
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
version: number;
}
export class TheatreUpdateDTO extends TheatreDTO {
@ApiProperty({ type: Number })
@IsNumber()
@IsNotEmpty()
id: number
}

View File

@ -13,11 +13,7 @@ export class TheatreService {
}
async findByPk(id: number): Promise<Theatre> {
const theatre = Theatre.findByPk(id,)
// //const textbookExists = await this.checkTextbookExists((await theatre).textbookId);
return theatre
return Theatre.findByPk(id);
}
findOne(theatre: Theatre): Promise<Theatre> {
@ -32,7 +28,7 @@ export class TheatreService {
return Theatre.destroy({ where: { id: id } });
}
async upsert(theatre: Theatre, insertIfNotFound: boolean): Promise<Theatre | [affectedCount: number]> {
async upsert(theatre: any, insertIfNotFound: boolean): Promise<Theatre | [affectedCount: number]> {
if (theatre.id) {
const existingTheatre = await this.findByPk(theatre.id);
if (existingTheatre) {
@ -40,8 +36,6 @@ export class TheatreService {
}
}
if (insertIfNotFound) {
//const textbookExists = await this.checkTextbookExists(theatre.textbookId);
return Theatre.create(theatre as any)
}
}

View File

@ -1,9 +1,10 @@
import { Body, Controller, Delete, Get, Param, Post, Put, Res } from '@nestjs/common';
import { Body, Controller, Delete, Get, Param, Post, Put, Res, UsePipes, ValidationPipe } from '@nestjs/common';
import { TheatreAdditionalDetailsService } from './theatreAdditionalDetails.service';
import { Response } from 'express';
import { GenericResponse } from '../../common/GenericResponse.model';
import TheatreAdditionalDetails from './theatreAdditionalDetails.entity';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { TheatreAdditionalDetailsDTO, TheatreAdditionalDetailsUpdateDTO } from './theatreAdditionalDetails.dto';
@ApiTags('theatreAdditionalDetails')
@Controller('theatreAdditionalDetails')
@ -160,7 +161,7 @@ export class TheatreAdditionalDetailsController {
@Post()
@ApiOperation({ summary: 'Insert a new theatreAdditionalDetails' })
@ApiBody({ type: TheatreAdditionalDetails, description: 'TheatreAdditionalDetails data to insert' })
@ApiBody({ type: TheatreAdditionalDetailsDTO, description: 'TheatreAdditionalDetails data to insert' })
@ApiResponse({
status: 400,
description: 'Invalid theatreAdditionalDetails data',
@ -178,7 +179,8 @@ export class TheatreAdditionalDetailsController {
status: 201,
description: 'Successfully created a theatreAdditionalDetails',
})
async insert(@Body() theatreAdditionalDetails: TheatreAdditionalDetails, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true}))
async insert(@Body() theatreAdditionalDetails: TheatreAdditionalDetailsDTO, @Res() res: Response) {
if (!theatreAdditionalDetails) {
const response = new GenericResponse({
exception: true,
@ -188,7 +190,6 @@ export class TheatreAdditionalDetailsController {
}, null);
return res.status(400).send(response);
}
delete theatreAdditionalDetails.id;
const response = await this.theatreAdditionalDetailsService.upsert(theatreAdditionalDetails, true);
const httpResponse = new GenericResponse(null, response);
res.status(201).send(httpResponse);
@ -196,7 +197,7 @@ export class TheatreAdditionalDetailsController {
@Put()
@ApiOperation({ summary: 'Update an existing theatreAdditionalDetails' })
@ApiBody({ type: TheatreAdditionalDetails, description: 'TheatreAdditionalDetails data to update' })
@ApiBody({ type: TheatreAdditionalDetailsUpdateDTO, description: 'TheatreAdditionalDetails data to update' })
@ApiResponse({
status: 400,
description: 'Invalid theatreAdditionalDetails data or ID missing',
@ -227,7 +228,8 @@ export class TheatreAdditionalDetailsController {
status: 200,
description: 'Successfully updated theatreAdditionalDetails',
})
async update(@Body() theatreAdditionalDetails: TheatreAdditionalDetails, @Res() res: Response) {
@UsePipes(new ValidationPipe({ whitelist: true, skipMissingProperties: true }))
async update(@Body() theatreAdditionalDetails: TheatreAdditionalDetailsUpdateDTO, @Res() res: Response) {
if (!theatreAdditionalDetails || !theatreAdditionalDetails.id) {
const response = new GenericResponse({
exception: true,

Some files were not shown because too many files have changed in this diff Show More