wip
BIN
assets/uploads/profileImage-1756051504425-576080142.png
Normal file
|
After Width: | Height: | Size: 449 KiB |
BIN
assets/uploads/profileImage-1756051558334-70000459.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
assets/uploads/profileImage-1756052029006-61410254.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/uploads/profileImage-1756052103150-110638977.png
Normal file
|
After Width: | Height: | Size: 440 KiB |
BIN
assets/uploads/profileImage-1756052162400-772820453.png
Normal file
|
After Width: | Height: | Size: 387 KiB |
BIN
assets/uploads/profileImage-1756052194078-183457847.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
assets/uploads/profileImage-1756052231467-661374958.png
Normal file
|
After Width: | Height: | Size: 445 KiB |
BIN
assets/uploads/profileImage-1756052427617-312691193.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
assets/uploads/profileImage-1756053237450-897610114.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
assets/uploads/profileImage-1756059946876-912349692.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
assets/uploads/profileImage-1756212925924-35299885.png
Normal file
|
After Width: | Height: | Size: 449 KiB |
BIN
assets/uploads/profileImage-1756212996591-498695492.png
Normal file
|
After Width: | Height: | Size: 387 KiB |
BIN
assets/uploads/profileImage-1756213111781-8673126.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
assets/uploads/profileImage-1756213893152-387543064.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
8
prisma/migrations/20250824170509_init/migration.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "public"."Stats" (
|
||||||
|
"id" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
"totalVisits" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"totalClicks" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
CONSTRAINT "Stats_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
@@ -26,3 +26,9 @@ model Candidate {
|
|||||||
profileImage String?
|
profileImage String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model Stats {
|
||||||
|
id Int @id @default(1)
|
||||||
|
totalVisits Int @default(0)
|
||||||
|
totalClicks Int @default(0)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BadRequestException, Body, Controller, Get, NotFoundException, Param, ParseIntPipe, Post, Put, UploadedFile, UseInterceptors } from '@nestjs/common';
|
import { BadRequestException, Body, Controller, Delete, Get, NotFoundException, Param, ParseIntPipe, Post, Put, UploadedFile, UseInterceptors } from '@nestjs/common';
|
||||||
import { RegisterDto } from './dto/register.dto';
|
import { RegisterDto } from './dto/register.dto';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { PrismaService } from './services/prisma.service';
|
import { PrismaService } from './services/prisma.service';
|
||||||
@@ -31,6 +31,7 @@ export class AppController {
|
|||||||
return this.prisma.candidate.findFirst({ where: { id: id } });
|
return this.prisma.candidate.findFirst({ where: { id: id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Post('register')
|
@Post('register')
|
||||||
@UseInterceptors(FileInterceptor('profileImage', {
|
@UseInterceptors(FileInterceptor('profileImage', {
|
||||||
storage: diskStorage({
|
storage: diskStorage({
|
||||||
@@ -95,4 +96,17 @@ export class AppController {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Delete('candidate/:id')
|
||||||
|
async deleteCandidate(@Param('id', ParseIntPipe) id: number) {
|
||||||
|
const candidate = await this.prisma.candidate.findFirst({ where: { id } });
|
||||||
|
if (!candidate) throw new NotFoundException(`Candidate with id ${id} not found`);
|
||||||
|
|
||||||
|
await this.prisma.candidate.delete({ where: { id } });
|
||||||
|
|
||||||
|
this.socketService.onDeleteCandidate(id);
|
||||||
|
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,4 +23,8 @@ export class AppGetaway implements OnGatewayDisconnect, OnGatewayConnection {
|
|||||||
onUpdateCandidate(updatedData: any) {
|
onUpdateCandidate(updatedData: any) {
|
||||||
this.server.emit('candidateUpdated', updatedData);
|
this.server.emit('candidateUpdated', updatedData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDeleteCandidate(deletedId: number) {
|
||||||
|
this.server.emit('candidateDeleted', deletedId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import { ServeStaticModule } from '@nestjs/serve-static';
|
|||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { PrismaService } from './services/prisma.service';
|
import { PrismaService } from './services/prisma.service';
|
||||||
import { AppGetaway } from './app.getaway';
|
import { AppGetaway } from './app.getaway';
|
||||||
|
import { StatsController } from './stats.controller';
|
||||||
|
import { StatsService } from './services/stats.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -17,7 +19,7 @@ import { AppGetaway } from './app.getaway';
|
|||||||
serveRoot: '/uploads',
|
serveRoot: '/uploads',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController, StatsController],
|
||||||
providers: [PrismaService, AppGetaway],
|
providers: [PrismaService, StatsService, AppGetaway],
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|||||||
34
src/services/stats.service.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { PrismaService } from "./prisma.service";
|
||||||
|
import { AppGetaway } from "src/app.getaway";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class StatsService {
|
||||||
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private socketService: AppGetaway
|
||||||
|
) { }
|
||||||
|
|
||||||
|
async incrementVisits() {
|
||||||
|
const stats = await this.prisma.stats.update({
|
||||||
|
where: { id: 1 },
|
||||||
|
data: { totalVisits: { increment: 1 } },
|
||||||
|
});
|
||||||
|
this.socketService.server.emit('statsUpdated', stats);
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
async incrementClicks() {
|
||||||
|
const stats = await this.prisma.stats.update({
|
||||||
|
where: { id: 1 },
|
||||||
|
data: { totalClicks: { increment: 1 } },
|
||||||
|
});
|
||||||
|
this.socketService.server.emit('statsUpdated', stats);
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStats() {
|
||||||
|
return this.prisma.stats.findUnique({ where: { id: 1 } });
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/stats.controller.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Controller, Get, Post } from '@nestjs/common';
|
||||||
|
import { StatsService } from './services/stats.service';
|
||||||
|
|
||||||
|
@Controller('stats')
|
||||||
|
export class StatsController {
|
||||||
|
constructor(private readonly statsService: StatsService) { }
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getStats() {
|
||||||
|
return this.statsService.getStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('visit')
|
||||||
|
incrementVisit() {
|
||||||
|
return this.statsService.incrementVisits();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('click')
|
||||||
|
incrementClick() {
|
||||||
|
return this.statsService.incrementClicks();
|
||||||
|
}
|
||||||
|
}
|
||||||