fix
This commit is contained in:
27
package.json
27
package.json
@@ -12,12 +12,7 @@
|
|||||||
"start:dev": "nest start --watch",
|
"start:dev": "nest start --watch",
|
||||||
"start:debug": "nest start --debug --watch",
|
"start:debug": "nest start --debug --watch",
|
||||||
"start:prod": "node dist/main",
|
"start:prod": "node dist/main",
|
||||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix"
|
||||||
"test": "jest",
|
|
||||||
"test:watch": "jest --watch",
|
|
||||||
"test:cov": "jest --coverage",
|
|
||||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
|
||||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^11.0.1",
|
"@nestjs/common": "^11.0.1",
|
||||||
@@ -40,10 +35,8 @@
|
|||||||
"@eslint/js": "^9.18.0",
|
"@eslint/js": "^9.18.0",
|
||||||
"@nestjs/cli": "^11.0.0",
|
"@nestjs/cli": "^11.0.0",
|
||||||
"@nestjs/schematics": "^11.0.0",
|
"@nestjs/schematics": "^11.0.0",
|
||||||
"@nestjs/testing": "^11.0.1",
|
|
||||||
"@prisma/client": "^6.14.0",
|
"@prisma/client": "^6.14.0",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/jest": "^30.0.0",
|
|
||||||
"@types/multer": "^2.0.0",
|
"@types/multer": "^2.0.0",
|
||||||
"@types/node": "^22.10.7",
|
"@types/node": "^22.10.7",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
@@ -51,7 +44,6 @@
|
|||||||
"eslint-config-prettier": "^10.0.1",
|
"eslint-config-prettier": "^10.0.1",
|
||||||
"eslint-plugin-prettier": "^5.2.2",
|
"eslint-plugin-prettier": "^5.2.2",
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"jest": "^30.0.0",
|
|
||||||
"prettier": "^3.4.2",
|
"prettier": "^3.4.2",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"supertest": "^7.0.0",
|
"supertest": "^7.0.0",
|
||||||
@@ -61,22 +53,5 @@
|
|||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.20.0"
|
"typescript-eslint": "^8.20.0"
|
||||||
},
|
|
||||||
"jest": {
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"js",
|
|
||||||
"json",
|
|
||||||
"ts"
|
|
||||||
],
|
|
||||||
"rootDir": "src",
|
|
||||||
"testRegex": ".*\\.spec\\.ts$",
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
|
||||||
},
|
|
||||||
"collectCoverageFrom": [
|
|
||||||
"**/*.(t|j)s"
|
|
||||||
],
|
|
||||||
"coverageDirectory": "../coverage",
|
|
||||||
"testEnvironment": "node"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,17 @@
|
|||||||
import { BadRequestException, Body, Controller, Delete, 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';
|
||||||
@@ -11,8 +24,10 @@ import { unlink } from 'fs';
|
|||||||
@ApiTags('App')
|
@ApiTags('App')
|
||||||
@Controller('app')
|
@Controller('app')
|
||||||
export class AppController {
|
export class AppController {
|
||||||
constructor(private prisma: PrismaService, private socketService: AppGetaway) { }
|
constructor(
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private socketService: AppGetaway,
|
||||||
|
) {}
|
||||||
|
|
||||||
@Get('candidates')
|
@Get('candidates')
|
||||||
async getCandidateList() {
|
async getCandidateList() {
|
||||||
@@ -23,12 +38,12 @@ export class AppController {
|
|||||||
age: true,
|
age: true,
|
||||||
cityOrRegion: true,
|
cityOrRegion: true,
|
||||||
profileImage: true,
|
profileImage: true,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
data.forEach((candidate) => {
|
data.forEach((candidate) => {
|
||||||
candidate.profileImage = `${process.env.HOST_URL as string}/uploads/${candidate.profileImage}`;
|
candidate.profileImage = `${process.env.HOST_URL as string}/uploads/${candidate.profileImage}`;
|
||||||
})
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,21 +57,23 @@ export class AppController {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Post('register')
|
@Post('register')
|
||||||
@UseInterceptors(FileInterceptor('profileImage', {
|
@UseInterceptors(
|
||||||
|
FileInterceptor('profileImage', {
|
||||||
storage: diskStorage({
|
storage: diskStorage({
|
||||||
destination: 'assets/uploads',
|
destination: 'assets/uploads',
|
||||||
filename: (req, file, cb) => {
|
filename: (req, file, cb) => {
|
||||||
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
|
const uniqueSuffix =
|
||||||
|
Date.now() + '-' + Math.round(Math.random() * 1e9);
|
||||||
const ext = extname(file.originalname);
|
const ext = extname(file.originalname);
|
||||||
cb(null, `${file.fieldname}-${uniqueSuffix}${ext}`);
|
cb(null, `${file.fieldname}-${uniqueSuffix}${ext}`);
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
}))
|
}),
|
||||||
|
)
|
||||||
async register(
|
async register(
|
||||||
@UploadedFile() file: Express.Multer.File,
|
@UploadedFile() file: Express.Multer.File,
|
||||||
@Body() dto: RegisterDto
|
@Body() dto: RegisterDto,
|
||||||
) {
|
) {
|
||||||
const imagePath = file ? file.filename : null;
|
const imagePath = file ? file.filename : null;
|
||||||
const savedCandidate = await this.prisma.candidate.create({
|
const savedCandidate = await this.prisma.candidate.create({
|
||||||
@@ -72,27 +89,34 @@ export class AppController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Put('candidate/:id')
|
@Put('candidate/:id')
|
||||||
@UseInterceptors(FileInterceptor('profileImage', {
|
@UseInterceptors(
|
||||||
|
FileInterceptor('profileImage', {
|
||||||
storage: diskStorage({
|
storage: diskStorage({
|
||||||
destination: 'assets/uploads',
|
destination: 'assets/uploads',
|
||||||
filename: (req, file, cb) => {
|
filename: (req, file, cb) => {
|
||||||
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
|
const uniqueSuffix =
|
||||||
|
Date.now() + '-' + Math.round(Math.random() * 1e9);
|
||||||
const ext = extname(file.originalname);
|
const ext = extname(file.originalname);
|
||||||
cb(null, `${file.fieldname}-${uniqueSuffix}${ext}`);
|
cb(null, `${file.fieldname}-${uniqueSuffix}${ext}`);
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
}))
|
}),
|
||||||
|
)
|
||||||
async update(
|
async update(
|
||||||
@Param('id', ParseIntPipe) id: number,
|
@Param('id', ParseIntPipe) id: number,
|
||||||
@UploadedFile() file: Express.Multer.File,
|
@UploadedFile() file: Express.Multer.File,
|
||||||
@Body() dto: RegisterDto
|
@Body() dto: RegisterDto,
|
||||||
) {
|
) {
|
||||||
const candidate = await this.prisma.candidate.findFirst({ where: { id } });
|
const candidate = await this.prisma.candidate.findFirst({ where: { id } });
|
||||||
if (!candidate) throw new NotFoundException(`Candidate with id ${id} not found`);
|
if (!candidate)
|
||||||
|
throw new NotFoundException(`Candidate with id ${id} not found`);
|
||||||
|
|
||||||
const dayMilliseconds = 86400000;
|
const dayMilliseconds = 86400000;
|
||||||
const expirationDate = new Date(candidate.createdAt.valueOf() + 3 * dayMilliseconds);
|
const expirationDate = new Date(
|
||||||
if (expirationDate < new Date()) throw new BadRequestException(`Cannot edit candidate form after 3 days`);
|
candidate.createdAt.valueOf() + 3 * dayMilliseconds,
|
||||||
|
);
|
||||||
|
if (expirationDate < new Date())
|
||||||
|
throw new BadRequestException(`Cannot edit candidate form after 3 days`);
|
||||||
|
|
||||||
const profileImagePath = file ? file.filename : candidate.profileImage;
|
const profileImagePath = file ? file.filename : candidate.profileImage;
|
||||||
|
|
||||||
@@ -108,16 +132,21 @@ export class AppController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Delete('candidate/:id')
|
@Delete('candidate/:id')
|
||||||
async deleteCandidate(@Param('id', ParseIntPipe) id: number) {
|
async deleteCandidate(@Param('id', ParseIntPipe) id: number) {
|
||||||
const candidate = await this.prisma.candidate.findFirst({ where: { id } });
|
const candidate = await this.prisma.candidate.findFirst({ where: { id } });
|
||||||
if (!candidate) throw new NotFoundException(`Candidate with id ${id} not found`);
|
if (!candidate)
|
||||||
|
throw new NotFoundException(`Candidate with id ${id} not found`);
|
||||||
|
|
||||||
await this.prisma.candidate.delete({ where: { id } });
|
await this.prisma.candidate.delete({ where: { id } });
|
||||||
|
|
||||||
if (candidate.profileImage) {
|
if (candidate.profileImage) {
|
||||||
const filePath = join(process.cwd(), 'assets', 'uploads', candidate.profileImage);
|
const filePath = join(
|
||||||
|
process.cwd(),
|
||||||
|
'assets',
|
||||||
|
'uploads',
|
||||||
|
candidate.profileImage,
|
||||||
|
);
|
||||||
unlink(filePath, (err) => {
|
unlink(filePath, (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Failed to delete file:', err);
|
console.error('Failed to delete file:', err);
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
import { MessageBody, OnGatewayConnection, OnGatewayDisconnect, SubscribeMessage, WebSocketGateway, WebSocketServer } from "@nestjs/websockets";
|
import {
|
||||||
import { Server, Socket } from "socket.io";
|
OnGatewayConnection,
|
||||||
|
OnGatewayDisconnect,
|
||||||
|
WebSocketGateway,
|
||||||
|
WebSocketServer,
|
||||||
|
} from '@nestjs/websockets';
|
||||||
|
import { Server, Socket } from 'socket.io';
|
||||||
|
|
||||||
@WebSocketGateway({
|
@WebSocketGateway({
|
||||||
cors: true
|
cors: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
export class AppGetaway implements OnGatewayDisconnect, OnGatewayConnection {
|
export class AppGetaway implements OnGatewayDisconnect, OnGatewayConnection {
|
||||||
@WebSocketServer() server: Server
|
@WebSocketServer() server: Server;
|
||||||
|
|
||||||
handleDisconnect(client: Socket) {
|
handleDisconnect(client: Socket) {
|
||||||
console.log(`${client.id} disconnected`)
|
console.log(`${client.id} disconnected`);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnection(client: Socket, ...args: any[]) {
|
handleConnection(client: Socket /*...args: any[]*/) {
|
||||||
console.log(`${client.id} connected`)
|
console.log(`${client.id} connected`);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddCandidate(registrationData: any) {
|
onAddCandidate(registrationData: any) {
|
||||||
|
|||||||
@@ -14,14 +14,15 @@ import { ConfigModule } from '@nestjs/config';
|
|||||||
ServeStaticModule.forRoot(
|
ServeStaticModule.forRoot(
|
||||||
{
|
{
|
||||||
rootPath: join(__dirname, 'assets/client'),
|
rootPath: join(__dirname, 'assets/client'),
|
||||||
renderPath: '/'
|
renderPath: '/',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
rootPath: join(__dirname, 'assets/uploads'),
|
rootPath: join(__dirname, 'assets/uploads'),
|
||||||
serveRoot: '/uploads',
|
serveRoot: '/uploads',
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
controllers: [AppController, StatsController],
|
controllers: [AppController, StatsController],
|
||||||
providers: [PrismaService, StatsService, AppGetaway],
|
providers: [PrismaService, StatsService, AppGetaway],
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule {}
|
||||||
|
|||||||
14
src/main.ts
14
src/main.ts
@@ -15,19 +15,11 @@ async function bootstrap() {
|
|||||||
setupSwagger(app);
|
setupSwagger(app);
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: [true],
|
origin: [true],
|
||||||
methods: [
|
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE', 'OPTIONS'],
|
||||||
'GET',
|
|
||||||
'HEAD',
|
|
||||||
'PUT',
|
|
||||||
'PATCH',
|
|
||||||
'POST',
|
|
||||||
'DELETE',
|
|
||||||
'OPTIONS',
|
|
||||||
],
|
|
||||||
allowedHeaders: ['*'],
|
allowedHeaders: ['*'],
|
||||||
optionsSuccessStatus: 204,
|
optionsSuccessStatus: 204,
|
||||||
credentials: true,
|
credentials: true,
|
||||||
preflightContinue: false
|
preflightContinue: false,
|
||||||
});
|
});
|
||||||
await app.listen(process.env.PORT ?? 3000);
|
await app.listen(process.env.PORT ?? 3000);
|
||||||
}
|
}
|
||||||
@@ -43,5 +35,5 @@ function setupSwagger(app: INestApplication<any>) {
|
|||||||
SwaggerModule.setup('api', app, document);
|
SwaggerModule.setup('api', app, document);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from '@nestjs/common';
|
||||||
import { PrismaService } from "./prisma.service";
|
import { PrismaService } from './prisma.service';
|
||||||
import { AppGetaway } from "src/app.getaway";
|
import { AppGetaway } from 'src/app.getaway';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StatsService {
|
export class StatsService {
|
||||||
constructor(
|
constructor(
|
||||||
private prisma: PrismaService,
|
private prisma: PrismaService,
|
||||||
private socketService: AppGetaway
|
private socketService: AppGetaway,
|
||||||
) { }
|
) {}
|
||||||
|
|
||||||
async incrementVisits() {
|
async incrementVisits() {
|
||||||
const stats = await this.prisma.stats.update({
|
const stats = await this.prisma.stats.update({
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { StatsService } from './services/stats.service';
|
|||||||
|
|
||||||
@Controller('stats')
|
@Controller('stats')
|
||||||
export class StatsController {
|
export class StatsController {
|
||||||
constructor(private readonly statsService: StatsService) { }
|
constructor(private readonly statsService: StatsService) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
getStats() {
|
getStats() {
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
import { INestApplication } from '@nestjs/common';
|
|
||||||
import * as request from 'supertest';
|
|
||||||
import { App } from 'supertest/types';
|
|
||||||
import { AppModule } from './../src/app.module';
|
|
||||||
|
|
||||||
describe('AppController (e2e)', () => {
|
|
||||||
let app: INestApplication<App>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
||||||
imports: [AppModule],
|
|
||||||
}).compile();
|
|
||||||
|
|
||||||
app = moduleFixture.createNestApplication();
|
|
||||||
await app.init();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('/ (GET)', () => {
|
|
||||||
return request(app.getHttpServer())
|
|
||||||
.get('/')
|
|
||||||
.expect(200)
|
|
||||||
.expect('Hello World!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"moduleFileExtensions": ["js", "json", "ts"],
|
|
||||||
"rootDir": ".",
|
|
||||||
"testEnvironment": "node",
|
|
||||||
"testRegex": ".e2e-spec.ts$",
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user