Skip to content

Commit ed10148

Browse files
committed
feat(express): fastify added
1 parent 6dbe286 commit ed10148

24 files changed

+1588
-1330
lines changed

package.json

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,64 +21,66 @@
2121
"prepare": "husky install"
2222
},
2323
"dependencies": {
24-
"@nestjs/cache-manager": "^2.3.0",
25-
"@nestjs/common": "^10.0.0",
26-
"@nestjs/config": "^3.2.2",
27-
"@nestjs/core": "^10.0.0",
28-
"@nestjs/passport": "^11.0.4",
29-
"@nestjs/platform-express": "^10.4.13",
30-
"@nestjs/swagger": "^7.3.1",
31-
"@prisma/client": "^6.1.0",
24+
"@fastify/cookie": "^11.0.2",
25+
"@fastify/cors": "^10.0.2",
26+
"@fastify/session": "^11.1.0",
27+
"@fastify/static": "^8.1.1",
28+
"@mgcrea/fastify-session-redis-store": "^1.0.0",
29+
"@nestjs/cache-manager": "^3.0.0",
30+
"@nestjs/common": "^11.0.10",
31+
"@nestjs/config": "^4.0.0",
32+
"@nestjs/core": "^11.0.10",
33+
"@nestjs/passport": "^11.0.5",
34+
"@nestjs/platform-fastify": "^11.0.10",
35+
"@nestjs/swagger": "^11.0.4",
36+
"@prisma/client": "^6.4.1",
37+
"@types/ioredis": "^5.0.0",
3238
"argon2": "^0.41.1",
3339
"auth": "^1.2.3",
3440
"axios": "^1.7.9",
35-
"cache-manager": "^6.3.2",
41+
"cache-manager": "^6.4.0",
3642
"class-transformer": "^0.5.1",
3743
"class-validator": "^0.14.1",
3844
"config": "^3.3.12",
3945
"connect-redis": "^8.0.1",
4046
"cookie-parser": "^1.4.7",
41-
"express": "^4.21.1",
42-
"express-session": "^1.18.1",
43-
"ioredis": "^5.4.2",
44-
"pg": "^8.11.5",
47+
"ioredis": "^5.5.0",
48+
"pg": "^8.13.3",
4549
"postgres": "^3.4.4",
46-
"prisma": "^6.1.0",
50+
"prisma": "^6.4.1",
4751
"redis": "^4.7.0",
4852
"reflect-metadata": "^0.2.2",
49-
"rxjs": "^7.8.1",
53+
"rxjs": "^7.8.2",
5054
"sequelize": "^6.37.3",
5155
"sequelize-cli": "^6.6.2",
52-
"uuid": "^9.0.1"
56+
"uuid": "^11.1.0"
5357
},
5458
"devDependencies": {
55-
"@commitlint/cli": "^19.6.1",
56-
"@commitlint/config-conventional": "^19.6.0",
57-
"@nestjs/cli": "^10.0.0",
58-
"@nestjs/schematics": "^10.2.3",
59-
"@nestjs/testing": "^10.0.0",
60-
"@types/express": "^4.17.17",
61-
"@types/express-session": "^1.18.1",
59+
"@commitlint/cli": "^19.7.1",
60+
"@commitlint/config-conventional": "^19.7.1",
61+
"@nestjs/cli": "^11.0.4",
62+
"@nestjs/schematics": "^11.0.1",
63+
"@nestjs/testing": "^11.0.10",
6264
"@types/jest": "^29.5.2",
63-
"@types/node": "^20.3.1",
65+
"@types/node": "^22.13.5",
6466
"@types/passport": "^1.0.17",
65-
"@types/supertest": "^2.0.12",
66-
"@typescript-eslint/eslint-plugin": "^8.22.0",
67-
"@typescript-eslint/parser": "^8.22.0",
67+
"@types/supertest": "^6.0.2",
68+
"@typescript-eslint/eslint-plugin": "^8.24.1",
69+
"@typescript-eslint/parser": "^8.24.1",
6870
"commitizen": "^4.3.1",
69-
"commitlint": "^19.6.1",
71+
"commitlint": "^19.7.1",
7072
"cz-conventional-changelog": "^3.3.0",
71-
"eslint": "^9.19.0",
72-
"eslint-config-prettier": "^9.0.0",
73+
"eslint": "^9.21.0",
74+
"eslint-config-prettier": "^10.0.1",
7375
"eslint-plugin-import": "^2.31.0",
7476
"eslint-plugin-prettier": "^5.0.0",
75-
"husky": "^8.0.0",
77+
"husky": "^9.1.7",
7678
"jest": "^29.5.0",
77-
"lint-staged": "^15.2.11",
78-
"prettier": "^3.0.0",
79+
"lint-staged": "^15.4.3",
80+
"prettier": "^3.5.2",
7981
"source-map-support": "^0.5.21",
80-
"supertest": "^6.3.3",
81-
"ts-jest": "^29.1.0",
82+
"supertest": "^7.0.0",
83+
"ts-jest": "^29.2.6",
8284
"ts-loader": "^9.4.3",
8385
"ts-node": "^10.9.1",
8486
"tsconfig-paths": "^4.2.0",

src/auth/auth/auth.controller.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import { AuthService } from './auth.service'
1414
import { AuthDto } from './dto'
1515
import { AtGuard } from './guards'
1616
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'
17-
import { Request, Response } from 'express'
18-
17+
import { FastifyRequest } from 'fastify'
18+
import { FastifyReply } from 'fastify'
1919
@Controller('auth')
2020
export class AuthController {
2121
constructor(private authService: AuthService) {}
@@ -24,7 +24,10 @@ export class AuthController {
2424
@ApiOperation({ summary: 'Register new account' })
2525
@Post('register')
2626
@HttpCode(HttpStatus.CREATED)
27-
async signupLocal(@Body() dto: AuthDto, @Req() req: Request): Promise<any> {
27+
async signupLocal(
28+
@Body() dto: AuthDto,
29+
@Req() req: FastifyRequest,
30+
): Promise<any> {
2831
return await this.authService.signupLocal(dto, req)
2932
}
3033

@@ -35,8 +38,8 @@ export class AuthController {
3538
@HttpCode(HttpStatus.OK)
3639
async signinLocal(
3740
@Body() dto: AuthDto,
38-
@Req() req: Request,
39-
@Res() res: Response,
41+
@Req() req: FastifyRequest,
42+
@Res() res: FastifyReply,
4043
): Promise<any> {
4144
await this.authService.signinLocal(dto, req)
4245
return res.redirect('/auth/status')
@@ -48,7 +51,7 @@ export class AuthController {
4851
@Get('status')
4952
@UseGuards(AtGuard)
5053
@HttpCode(200)
51-
async status(@Req() req: Request) {
54+
async status(@Req() req: FastifyRequest) {
5255
return req.session.user
5356
}
5457

@@ -58,7 +61,7 @@ export class AuthController {
5861
@Get('logout')
5962
@UseGuards(AtGuard)
6063
@HttpCode(HttpStatus.OK)
61-
async logout(@Req() req: Request): Promise<boolean> {
64+
async logout(@Req() req: FastifyRequest): Promise<boolean> {
6265
return await this.authService.logout(req)
6366
}
6467
}

src/auth/auth/auth.service.ts

Lines changed: 65 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,113 +6,120 @@ import {
66
import * as argon from 'argon2'
77
import { AuthDto } from './dto'
88
import { PrismaService } from 'src/prisma/prisma.service'
9-
import { Request } from 'express'
9+
import { FastifyRequest } from 'fastify'
1010
import { Users } from '@prisma/client'
1111

1212
@Injectable()
1313
export class AuthService {
1414
constructor(private prisma: PrismaService) {}
1515

16-
async signupLocal(dto: AuthDto, req: Request): Promise<Users> {
17-
const User = await this.prisma.users.findUnique({
18-
where: {
19-
username: dto.username,
20-
email: dto.email,
21-
},
16+
async signupLocal(dto: AuthDto, req: FastifyRequest): Promise<Users> {
17+
// Проверка, существует ли уже пользователь
18+
const existingUser = await this.prisma.users.findUnique({
19+
where: { username: dto.username, email: dto.email },
2220
})
21+
if (existingUser) {
22+
throw new BadRequestException('User already exists')
23+
}
2324

24-
if (User) throw { message: 'User already exists' }
25-
25+
// Хэширование пароля
2626
const hash = await argon.hash(dto.password)
2727
dto.password = undefined
2828

29-
await this.saveSession(req, User)
30-
31-
return this.prisma.users.create({
32-
data: {
33-
...dto,
34-
hash: hash,
35-
},
29+
// Создание нового пользователя в базе данных
30+
const newUser = await this.prisma.users.create({
31+
data: { ...dto, hash: hash },
3632
})
33+
34+
// Сохранение сессии с данными нового пользователя
35+
await this.saveSession(req, newUser)
36+
37+
return newUser
3738
}
3839

39-
async ValidateUser(dto: AuthDto): Promise<any> {
40-
const User = await this.prisma.users.findUnique({
40+
async ValidateUser(dto: AuthDto): Promise<Users> {
41+
const user = await this.prisma.users.findUnique({
4142
where: { username: dto.username },
4243
})
44+
if (!user) throw new ForbiddenException('Access Denied')
4345

44-
if (!User) throw new ForbiddenException('Access Denied')
45-
46-
const passwordMatches = await argon.verify(User.hash, dto.password)
46+
const passwordMatches = await argon.verify(user.hash, dto.password)
4747
if (!passwordMatches) throw new ForbiddenException('Access Denied')
4848

49-
return User
49+
return user
5050
}
5151

52-
async signinLocal(dto: AuthDto, req: Request): Promise<Users> {
53-
const User = await this.ValidateUser(dto)
54-
55-
await this.saveSession(req, User, dto.remember)
56-
57-
return User
52+
async signinLocal(dto: AuthDto, req: FastifyRequest): Promise<Users> {
53+
const user = await this.ValidateUser(dto)
54+
await this.saveSession(req, user, dto.remember)
55+
return user
5856
}
5957

60-
async logout(req: Request): Promise<boolean> {
58+
async logout(req: FastifyRequest): Promise<boolean> {
6159
await this.destroySession(req)
6260
return true
6361
}
6462

65-
async ValidateOAuthUser(dto: AuthDto, req: Request): Promise<any> {
66-
const User = await this.prisma.users.findUnique({
67-
where: {
68-
username: dto.username,
69-
email: dto.email,
70-
},
63+
async ValidateOAuthUser(dto: AuthDto, req: FastifyRequest): Promise<Users> {
64+
const user = await this.prisma.users.findUnique({
65+
where: { username: dto.username, email: dto.email },
7166
})
72-
73-
if (User) {
74-
await this.saveSession(req, User, true)
75-
return User
67+
if (user) {
68+
await this.saveSession(req, user, true)
69+
return user
7670
}
7771

72+
// Если пользователь не найден, создаем нового
7873
const hash = await argon.hash(dto.password)
7974
dto.password = undefined
80-
81-
const user = await this.prisma.users.create({
82-
data: {
83-
...dto,
84-
hash: hash,
85-
},
75+
const newUser = await this.prisma.users.create({
76+
data: { ...dto, hash: hash },
8677
})
8778

88-
if (user) {
89-
await this.saveSession(req, User, true)
90-
return user
79+
if (newUser) {
80+
await this.saveSession(req, newUser, true)
81+
return newUser
9182
}
92-
return new BadRequestException()
83+
throw new BadRequestException('OAuth validation failed')
9384
}
9485

9586
private async saveSession(
96-
req: Request,
87+
req: FastifyRequest,
9788
user: Users,
9889
remember?: boolean,
9990
): Promise<boolean> {
10091
return new Promise((resolve, reject) => {
101-
if (!remember) req.session.cookie.maxAge = 1000
92+
// Установка времени жизни cookie в зависимости от флага remember
93+
if (remember) {
94+
req.session.cookie.maxAge = 1000 * 60 * 60 * 24 * 30 // 30 дней
95+
} else {
96+
req.session.cookie.maxAge = 1000 * 60 * 10 // 10 минут
97+
}
98+
10299
req.session.user = { ...user }
103100
req.session.save((err) => {
104-
if (err) reject(err)
105-
else resolve(true)
101+
if (err) {
102+
reject(err)
103+
} else {
104+
resolve(true)
105+
}
106106
})
107107
})
108108
}
109109

110-
private async destroySession(req: Request): Promise<boolean> {
110+
private async destroySession(req: FastifyRequest): Promise<boolean> {
111111
return new Promise((resolve, reject) => {
112-
req.session.destroy((err) => {
113-
if (err) reject(err)
114-
else resolve(true)
115-
})
112+
if (req.session) {
113+
req.session.destroy((err) => {
114+
if (err) {
115+
reject(err)
116+
} else {
117+
resolve(true)
118+
}
119+
})
120+
} else {
121+
reject(new Error('Session handling is not available'))
122+
}
116123
})
117124
}
118125
}

src/auth/auth/guards/at.guard.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
UnauthorizedException,
55
} from '@nestjs/common'
66
import { Reflector } from '@nestjs/core'
7+
import { FastifyRequest } from 'fastify'
78

89
@Injectable()
910
export class AtGuard {
@@ -16,8 +17,7 @@ export class AtGuard {
1617
])
1718
if (isPublic) return true
1819

19-
const request = context.switchToHttp().getRequest()
20-
20+
const request = context.switchToHttp().getRequest<FastifyRequest>()
2121
const user = request.session.user
2222
if (!user) {
2323
throw new UnauthorizedException('User not found in session')
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Controller, Get, Query, Req, Res } from '@nestjs/common'
22
import { GithubOAuthService } from './github.service'
3-
import { Request, Response } from 'express'
43
import { ApiOperation, ApiTags } from '@nestjs/swagger'
4+
import { FastifyRequest, FastifyReply } from 'fastify'
55

66
@Controller('auth/github')
77
export class GithubController {
@@ -10,7 +10,7 @@ export class GithubController {
1010
@ApiTags('OAuth2-github')
1111
@ApiOperation({ summary: 'login with github account' })
1212
@Get('login')
13-
async githubLogin(@Res() res: Response) {
13+
async githubLogin(@Res() res: FastifyReply) {
1414
const url = `https://github.com/login/oauth/authorize?client_id=${process.env.GITHUB_CLIENT_ID}&redirect_uri=${process.env.GITHUB_CALLBACK_URL}&scope=user:email`
1515
return res.redirect(url)
1616
}
@@ -20,15 +20,17 @@ export class GithubController {
2020
@Get('callback')
2121
async githubCallback(
2222
@Query('code') code: string,
23-
@Req() req: Request,
24-
@Res() res: Response,
23+
@Req() req: FastifyRequest,
24+
@Res() res: FastifyReply,
2525
) {
2626
try {
27+
// Обрабатываем авторизацию через GitHub
2728
await this.githubOAuthService.authenticate(code, req)
28-
return res.redirect('/auth/status')
29+
return res.redirect('/auth/status') // переадресация на статус
2930
} catch (error) {
3031
console.error('GitHub OAuth callback error:', error)
31-
return res.status(error.status || 500).json({ message: error.message })
32+
// Обработка ошибки, если что-то пошло не так
33+
return res.status(error.status || 500).send({ message: error.message })
3234
}
3335
}
3436
}

0 commit comments

Comments
 (0)