mirror of
https://github.com/MrSedan/neuro-reply-website.git
synced 2026-01-14 20:49:42 +03:00
feat: auth
This commit is contained in:
@@ -7,3 +7,5 @@ DATABASE_PORT=5432
|
|||||||
SERVER_PORT=3000
|
SERVER_PORT=3000
|
||||||
|
|
||||||
REDIS_PASSWORD=123
|
REDIS_PASSWORD=123
|
||||||
|
|
||||||
|
JWT_SECRET=secret
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@
|
|||||||
**/.env
|
**/.env
|
||||||
**/.env.*
|
**/.env.*
|
||||||
!**/.env.example
|
!**/.env.example
|
||||||
|
node_modules
|
||||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
dist
|
||||||
15
.prettierrc
15
.prettierrc
@@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
"singleQuote": true,
|
"arrowParens": "always",
|
||||||
"trailingComma": "all",
|
"printWidth": 100,
|
||||||
"semi": true,
|
"singleQuote": false,
|
||||||
"tabWidth": 4,
|
"trailingComma": "all",
|
||||||
"printWidth": 150,
|
"tabWidth": 2,
|
||||||
"bracketSpacing": true,
|
"useTabs": false,
|
||||||
"endOfLine": "lf"
|
"semi": true,
|
||||||
|
"bracketSpacing": true
|
||||||
}
|
}
|
||||||
|
|||||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -10,6 +10,7 @@
|
|||||||
"**/node_modules": true
|
"**/node_modules": true
|
||||||
},
|
},
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
|
"editor.tabSize": 2,
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"[dockercompose]": {
|
"[dockercompose]": {
|
||||||
"editor.insertSpaces": true,
|
"editor.insertSpaces": true,
|
||||||
|
|||||||
BIN
backend/bun.lockb
Executable file
BIN
backend/bun.lockb
Executable file
Binary file not shown.
@@ -1,27 +1,30 @@
|
|||||||
import { config as configInit } from 'dotenv';
|
import { config as configInit } from "dotenv";
|
||||||
|
|
||||||
configInit({ path: '../.env' });
|
configInit({ path: "../.env" });
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
database: {
|
database: {
|
||||||
type: 'postgres',
|
type: "postgres",
|
||||||
host: process.env.DATABASE_HOST || 'localhost',
|
host: process.env.DATABASE_HOST || "localhost",
|
||||||
port: +process.env.DATABASE_PORT || 5432,
|
port: +process.env.DATABASE_PORT || 5432,
|
||||||
username: process.env.DATABASE_USERNAME || 'postgres',
|
username: process.env.DATABASE_USERNAME || "postgres",
|
||||||
password: process.env.DATABASE_PASSWORD || '',
|
password: process.env.DATABASE_PASSWORD || "",
|
||||||
database: process.env.DATABASE_DB || 'bot_db',
|
database: process.env.DATABASE_DB || "bot_db",
|
||||||
synchronize: true,
|
synchronize: true,
|
||||||
logging: false,
|
logging: false,
|
||||||
autoLoadEntities: true,
|
autoLoadEntities: true,
|
||||||
},
|
},
|
||||||
redis: {
|
redis: {
|
||||||
redis_host: process.env.REDIS_HOST || 'localhost',
|
redis_host: process.env.REDIS_HOST || "localhost",
|
||||||
redis_port: +process.env.REDIS_PORT || 6379,
|
redis_port: +process.env.REDIS_PORT || 6379,
|
||||||
redis_password: process.env.REDIS_PASSWORD || '',
|
redis_password: process.env.REDIS_PASSWORD || "",
|
||||||
redis_database: +process.env.REDIS_DB || 0,
|
redis_database: +process.env.REDIS_DB || 0,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: +process.env.SERVER_PORT || 8080,
|
port: +process.env.SERVER_PORT || 8080,
|
||||||
access_token: process.env.ACCESS_TOKEN || '',
|
access_token: process.env.ACCESS_TOKEN || "",
|
||||||
},
|
},
|
||||||
|
auth: {
|
||||||
|
jwt_secret: process.env.JWT_SECRET || "secret",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
16
backend/libs/database/web_user.entity.ts
Normal file
16
backend/libs/database/web_user.entity.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class WebUser {
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
public uuid: string;
|
||||||
|
|
||||||
|
@Column({ unique: true })
|
||||||
|
public login: string;
|
||||||
|
|
||||||
|
@Column({ nullable: false })
|
||||||
|
public password: string;
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
public telegram_id?: string;
|
||||||
|
}
|
||||||
@@ -1,14 +1,17 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from "@nestjs/common";
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
import { Admin } from './database/admin.entity';
|
import { Admin } from "./database/admin.entity";
|
||||||
import { Image } from './database/image.entity';
|
import { Image } from "./database/image.entity";
|
||||||
import { Payment } from './database/payment.entity';
|
import { Payment } from "./database/payment.entity";
|
||||||
import { Post } from './database/post.entity';
|
import { Post } from "./database/post.entity";
|
||||||
import { ProxyUser } from './database/proxy_user.entity';
|
import { ProxyUser } from "./database/proxy_user.entity";
|
||||||
import { BotSettings } from './database/settings.entity';
|
import { BotSettings } from "./database/settings.entity";
|
||||||
import { User } from './database/user.entity';
|
import { User } from "./database/user.entity";
|
||||||
|
import { WebUser } from "./database/web_user.entity";
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([User, Admin, Post, Image, Payment, ProxyUser, BotSettings])],
|
imports: [
|
||||||
exports: [TypeOrmModule],
|
TypeOrmModule.forFeature([User, Admin, Post, Image, Payment, ProxyUser, BotSettings, WebUser]),
|
||||||
|
],
|
||||||
|
exports: [TypeOrmModule],
|
||||||
})
|
})
|
||||||
export class LibsModule {}
|
export class LibsModule {}
|
||||||
|
|||||||
@@ -1,81 +1,83 @@
|
|||||||
{
|
{
|
||||||
"name": "backend",
|
"name": "backend",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
"start": "nest start",
|
"start": "nest start",
|
||||||
"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/src/main",
|
"start:prod": "node dist/src/main",
|
||||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"test:cov": "jest --coverage",
|
"test:cov": "jest --coverage",
|
||||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
"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"
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/cache-manager": "2.2.1",
|
||||||
|
"@nestjs/common": "^10.0.0",
|
||||||
|
"@nestjs/core": "^10.0.0",
|
||||||
|
"@nestjs/passport": "^10.0.3",
|
||||||
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
|
"@nestjs/swagger": "^7.1.16",
|
||||||
|
"@nestjs/typeorm": "^10.0.1",
|
||||||
|
"bcrypt": "^5.1.1",
|
||||||
|
"cache-manager": "^5.4.0",
|
||||||
|
"cache-manager-redis-store": "^3.0.1",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"passport": "^0.7.0",
|
||||||
|
"passport-jwt": "^4.0.1",
|
||||||
|
"pg": "^8.11.3",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"rxjs": "^7.8.1",
|
||||||
|
"typeorm": "^0.3.17"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^10.0.0",
|
||||||
|
"@nestjs/schematics": "^10.0.0",
|
||||||
|
"@nestjs/testing": "^10.0.0",
|
||||||
|
"@types/express": "^4.17.17",
|
||||||
|
"@types/jest": "^29.5.2",
|
||||||
|
"@types/node": "^20.3.1",
|
||||||
|
"@types/passport-http-bearer": "^1.0.41",
|
||||||
|
"@types/supertest": "^2.0.12",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
|
"eslint": "^8.42.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
|
"jest": "^29.5.0",
|
||||||
|
"prettier": "^3.0.0",
|
||||||
|
"source-map-support": "^0.5.21",
|
||||||
|
"supertest": "^6.3.3",
|
||||||
|
"ts-jest": "^29.1.0",
|
||||||
|
"ts-loader": "^9.4.3",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"tsconfig-paths": "^4.2.0",
|
||||||
|
"typescript": "^5.1.3",
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"json",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"rootDir": "src",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"collectCoverageFrom": [
|
||||||
"@nestjs/cache-manager": "2.2.1",
|
"**/*.(t|j)s"
|
||||||
"@nestjs/common": "^10.0.0",
|
],
|
||||||
"@nestjs/core": "^10.0.0",
|
"coverageDirectory": "../coverage",
|
||||||
"@nestjs/passport": "^10.0.3",
|
"testEnvironment": "node"
|
||||||
"@nestjs/platform-express": "^10.0.0",
|
}
|
||||||
"@nestjs/swagger": "^7.1.16",
|
|
||||||
"@nestjs/typeorm": "^10.0.1",
|
|
||||||
"cache-manager": "^5.4.0",
|
|
||||||
"cache-manager-redis-store": "^3.0.1",
|
|
||||||
"dotenv": "^16.3.1",
|
|
||||||
"passport": "^0.7.0",
|
|
||||||
"passport-http-bearer": "^1.0.1",
|
|
||||||
"pg": "^8.11.3",
|
|
||||||
"reflect-metadata": "^0.1.13",
|
|
||||||
"rxjs": "^7.8.1",
|
|
||||||
"typeorm": "^0.3.17"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@nestjs/cli": "^10.0.0",
|
|
||||||
"@nestjs/schematics": "^10.0.0",
|
|
||||||
"@nestjs/testing": "^10.0.0",
|
|
||||||
"@types/express": "^4.17.17",
|
|
||||||
"@types/jest": "^29.5.2",
|
|
||||||
"@types/node": "^20.3.1",
|
|
||||||
"@types/passport-http-bearer": "^1.0.41",
|
|
||||||
"@types/supertest": "^2.0.12",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
|
||||||
"eslint": "^8.42.0",
|
|
||||||
"eslint-config-prettier": "^9.0.0",
|
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
|
||||||
"jest": "^29.5.0",
|
|
||||||
"prettier": "^3.0.0",
|
|
||||||
"source-map-support": "^0.5.21",
|
|
||||||
"supertest": "^6.3.3",
|
|
||||||
"ts-jest": "^29.1.0",
|
|
||||||
"ts-loader": "^9.4.3",
|
|
||||||
"ts-node": "^10.9.1",
|
|
||||||
"tsconfig-paths": "^4.2.0",
|
|
||||||
"typescript": "^5.1.3"
|
|
||||||
},
|
|
||||||
"jest": {
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"js",
|
|
||||||
"json",
|
|
||||||
"ts"
|
|
||||||
],
|
|
||||||
"rootDir": "src",
|
|
||||||
"testRegex": ".*\\.spec\\.ts$",
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
|
||||||
},
|
|
||||||
"collectCoverageFrom": [
|
|
||||||
"**/*.(t|j)s"
|
|
||||||
],
|
|
||||||
"coverageDirectory": "../coverage",
|
|
||||||
"testEnvironment": "node"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
48
backend/src/modules/auth/auth.controller.ts
Normal file
48
backend/src/modules/auth/auth.controller.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Post,
|
||||||
|
Request,
|
||||||
|
UseGuards,
|
||||||
|
} from "@nestjs/common";
|
||||||
|
import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||||
|
import { JwtGuard } from "./auth.guard";
|
||||||
|
import { AuthService } from "./auth.service";
|
||||||
|
import { LoginDto } from "./dto/login.dto";
|
||||||
|
|
||||||
|
@ApiTags("auth")
|
||||||
|
@Controller("auth")
|
||||||
|
export class AuthController {
|
||||||
|
constructor(private authService: AuthService) {}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
description: "Login into system",
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Post("login")
|
||||||
|
signIn(@Body() signInDto: LoginDto) {
|
||||||
|
return this.authService.signIn(signInDto.username, signInDto.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
description: "Register into system",
|
||||||
|
})
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
@Post("register")
|
||||||
|
register(@Body() signInDto: LoginDto) {
|
||||||
|
return this.authService.register(signInDto.username, signInDto.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({
|
||||||
|
description: "Get user profile",
|
||||||
|
})
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
@Get("profile")
|
||||||
|
@ApiBearerAuth()
|
||||||
|
getProfile(@Request() req) {
|
||||||
|
return req.user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +1,31 @@
|
|||||||
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
|
import { Injectable } from "@nestjs/common";
|
||||||
import { Reflector } from '@nestjs/core';
|
import { AuthGuard } from "@nestjs/passport";
|
||||||
import { AuthService } from './auth.service';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthGuard implements CanActivate {
|
export class JwtGuard extends AuthGuard("jwt") {}
|
||||||
constructor(
|
// export class AuthGuard implements CanActivate {
|
||||||
private reflector: Reflector,
|
// constructor(private jwtService: JwtService) {}
|
||||||
private readonly authService: AuthService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
canActivate(context: ExecutionContext) {
|
// async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||||
const request = context.switchToHttp().getRequest();
|
// const request = context.switchToHttp().getRequest();
|
||||||
const allowUnauthorizedRequest = this.reflector.get<boolean>('allowUnauthorizedRequest', context.getHandler());
|
// const token = this.extractTokenFromHeader(request);
|
||||||
|
// if (!token) {
|
||||||
|
// throw new UnauthorizedException();
|
||||||
|
// }
|
||||||
|
// try {
|
||||||
|
// const payload = await this.jwtService.verifyAsync(token, {
|
||||||
|
// secret: config.auth.jwt_secret,
|
||||||
|
// });
|
||||||
|
|
||||||
let token = this.extractTokenFromHeader(request.headers);
|
// request["user"] = payload;
|
||||||
|
// } catch {
|
||||||
|
// throw new UnauthorizedException();
|
||||||
|
// }
|
||||||
|
|
||||||
if (!token) {
|
// return true;
|
||||||
token = request.query.access_token || request.body.access_token;
|
// }
|
||||||
}
|
|
||||||
if (allowUnauthorizedRequest || this.authService.authUserByToken(token)) return true;
|
|
||||||
throw new UnauthorizedException('Unathorized!');
|
|
||||||
}
|
|
||||||
|
|
||||||
private extractTokenFromHeader(headers: any): string | null {
|
// private extractTokenFromHeader(request: Request): string | undefined {
|
||||||
if (headers && headers.authorization) {
|
// const [type, token] = request.headers.authorization.split(" ") ?? [];
|
||||||
const authHeader = headers.authorization as string;
|
// return type === "Bearer" ? token : undefined;
|
||||||
const headerParts = authHeader.split(' ');
|
// }
|
||||||
|
// }
|
||||||
if (headerParts.length === 2 && headerParts[0].toLowerCase() === 'bearer') {
|
|
||||||
return headerParts[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from "@nestjs/common";
|
||||||
import { PassportModule } from '@nestjs/passport';
|
import { JwtModule } from "@nestjs/jwt";
|
||||||
import { AuthService } from './auth.service';
|
import { PassportModule } from "@nestjs/passport";
|
||||||
import { HttpBearerStrategy } from './http-bearer.strategy';
|
import { LibsModule } from "libs/libs.module";
|
||||||
|
import { config } from "../../../config";
|
||||||
|
import { AuthController } from "./auth.controller";
|
||||||
|
import { AuthService } from "./auth.service";
|
||||||
|
import { JwtStrategy } from "./strategy/jwt.strategy";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PassportModule.register({ defaultStrategy: 'bearer' })],
|
imports: [
|
||||||
providers: [HttpBearerStrategy, AuthService],
|
LibsModule,
|
||||||
exports: [HttpBearerStrategy, AuthService],
|
PassportModule,
|
||||||
|
JwtModule.register({
|
||||||
|
secret: config.auth.jwt_secret,
|
||||||
|
global: true,
|
||||||
|
signOptions: { expiresIn: 86000 },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
controllers: [AuthController],
|
||||||
|
providers: [AuthService, JwtStrategy],
|
||||||
|
exports: [AuthService],
|
||||||
})
|
})
|
||||||
export class AuthModule {}
|
export class AuthModule {}
|
||||||
|
|||||||
@@ -1,10 +1,51 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger, UnauthorizedException } from "@nestjs/common";
|
||||||
import { config } from 'config';
|
import { JwtService } from "@nestjs/jwt";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import * as bcrypt from "bcrypt";
|
||||||
|
import { WebUser } from "libs/database/web_user.entity";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
private readonly logger: Logger = new Logger(AuthService.name);
|
private readonly logger: Logger = new Logger(AuthService.name);
|
||||||
authUserByToken(token: string) {
|
constructor(
|
||||||
return token === config.server.access_token;
|
@InjectRepository(WebUser) private userRepository: Repository<WebUser>,
|
||||||
|
private jwtService: JwtService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async signIn(username: string, pass: string): Promise<{ access_token: string }> {
|
||||||
|
const user = await this.userRepository.findOne({ where: { login: username } });
|
||||||
|
if (!user) throw new UnauthorizedException("User not found");
|
||||||
|
const isMatch = await bcrypt.compare(pass, user.password);
|
||||||
|
if (!isMatch) throw new UnauthorizedException("Wrong password");
|
||||||
|
const payload = { sub: user.uuid, username: user.login };
|
||||||
|
this.logger.log(`User ${user.login} logged in`);
|
||||||
|
return { access_token: await this.jwtService.signAsync(payload) };
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateUserJwt(uuid: string): Promise<any> {
|
||||||
|
const user = await this.userRepository.findOneBy({ uuid: uuid });
|
||||||
|
if (!user) {
|
||||||
|
throw new UnauthorizedException("User not found");
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
login: user.login,
|
||||||
|
telegram_id: user.telegram_id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async register(username: string, password: string): Promise<{ access_token: string }> {
|
||||||
|
const user = await this.userRepository.findOne({ where: { login: username } });
|
||||||
|
if (user) throw new UnauthorizedException("User already exists");
|
||||||
|
const salt = await bcrypt.genSalt();
|
||||||
|
const hash = await bcrypt.hash(password, salt);
|
||||||
|
const newUser = this.userRepository.create({
|
||||||
|
login: username,
|
||||||
|
password: hash,
|
||||||
|
});
|
||||||
|
await this.userRepository.save(newUser);
|
||||||
|
this.logger.log(`User ${username} registered`);
|
||||||
|
const payload = { sub: newUser.uuid, username: newUser.login };
|
||||||
|
return { access_token: await this.jwtService.signAsync(payload) };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
backend/src/modules/auth/dto/login.dto.ts
Normal file
6
backend/src/modules/auth/dto/login.dto.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
|
||||||
|
export class LoginDto {
|
||||||
|
@ApiProperty({ description: "User name", example: "test" }) readonly username: string;
|
||||||
|
@ApiProperty({ description: "User password", example: "test" }) readonly password: string;
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
||||||
import { PassportStrategy } from '@nestjs/passport';
|
|
||||||
import { Strategy } from 'passport-http-bearer';
|
|
||||||
import { AuthService } from './auth.service';
|
|
||||||
@Injectable()
|
|
||||||
export class HttpBearerStrategy extends PassportStrategy(Strategy) {
|
|
||||||
constructor(private readonly authService: AuthService) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
async validate(token: string): Promise<boolean> {
|
|
||||||
const user = await this.authService.authUserByToken(token);
|
|
||||||
if (!user) {
|
|
||||||
throw new UnauthorizedException();
|
|
||||||
}
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
21
backend/src/modules/auth/strategy/jwt.strategy.ts
Normal file
21
backend/src/modules/auth/strategy/jwt.strategy.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { Injectable, UnauthorizedException } from "@nestjs/common";
|
||||||
|
import { PassportStrategy } from "@nestjs/passport";
|
||||||
|
import { ExtractJwt, Strategy } from "passport-jwt";
|
||||||
|
import { config } from "../../../../config";
|
||||||
|
import { AuthService } from "../auth.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||||
|
constructor(private authService: AuthService) {
|
||||||
|
super({
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
secretOrKey: config.auth.jwt_secret,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(payload: { iat: number; sub: string }): Promise<any> {
|
||||||
|
const user = await this.authService.validateUserJwt(payload.sub);
|
||||||
|
if (!user) throw new UnauthorizedException();
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
import { INestApplication } from '@nestjs/common';
|
import { INestApplication } from "@nestjs/common";
|
||||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
||||||
|
|
||||||
export function swagger(app: INestApplication): INestApplication {
|
export function swagger(app: INestApplication): INestApplication {
|
||||||
const config = new DocumentBuilder().setTitle('Neuro website').setDescription('Some description').setVersion('0.1').build();
|
const config = new DocumentBuilder()
|
||||||
const document = SwaggerModule.createDocument(app, config);
|
.setTitle("Neuro website")
|
||||||
SwaggerModule.setup('api', app, document);
|
.setDescription("Some description")
|
||||||
return app;
|
.setVersion("0.1")
|
||||||
|
.addBearerAuth()
|
||||||
|
.build();
|
||||||
|
const document = SwaggerModule.createDocument(app, config);
|
||||||
|
SwaggerModule.setup("api", app, document);
|
||||||
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,20 +49,19 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
restart: always
|
restart: always
|
||||||
|
# bot:
|
||||||
bot:
|
# container_name: neuro_bot
|
||||||
container_name: neuro_bot
|
# build: ./neuro-reply-bot-reworked
|
||||||
build: ./neuro-reply-bot-reworked
|
# environment:
|
||||||
environment:
|
# - API_URL=http://backend:3000
|
||||||
- API_URL=http://backend:3000
|
# - TZ=Europe/Moscow
|
||||||
- TZ=Europe/Moscow
|
# - REDIS_HOST=redis
|
||||||
- REDIS_HOST=redis
|
# networks:
|
||||||
networks:
|
# - labnet
|
||||||
- labnet
|
# depends_on:
|
||||||
depends_on:
|
# - backend
|
||||||
- backend
|
# - redis
|
||||||
- redis
|
# restart: always
|
||||||
restart: always
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
neuro_postgres_db:
|
neuro_postgres_db:
|
||||||
|
|||||||
1
package.json
Normal file
1
package.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{ "dependencies": { "@nestjs/jwt": "^10.2.0" } }
|
||||||
Reference in New Issue
Block a user