feat: Authentication with backend
This commit is contained in:
4
backend/.gitignore
vendored
4
backend/.gitignore
vendored
@@ -1,3 +1,5 @@
|
||||
swagger-spec.json
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
@@ -55,4 +57,4 @@ pids
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
*.sqlite
|
||||
*.sqlite
|
||||
|
||||
10
backend/package-lock.json
generated
10
backend/package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "0.0.1",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@nanogiants/nestjs-swagger-api-exception-decorator": "^1.6.11",
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
@@ -1610,6 +1611,15 @@
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz",
|
||||
"integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug=="
|
||||
},
|
||||
"node_modules/@nanogiants/nestjs-swagger-api-exception-decorator": {
|
||||
"version": "1.6.11",
|
||||
"resolved": "https://registry.npmjs.org/@nanogiants/nestjs-swagger-api-exception-decorator/-/nestjs-swagger-api-exception-decorator-1.6.11.tgz",
|
||||
"integrity": "sha512-F2Jvj52BDFvKo0I5LFj+kSjwLQecqrs+ibDWokq6Xkod/wrT6gxGia1H/z7ENGk9XwwXfQL9rZt4W/+Vwp0ZhQ==",
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^7.6.0 || ^8.0.0 || ^9.0.0 || ^10.0.0",
|
||||
"@nestjs/swagger": "^4.8.1 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/cli": {
|
||||
"version": "10.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.3.2.tgz",
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
"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"
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||
"openapi:schema": "ts-node src/generate-openapi.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nanogiants/nestjs-swagger-api-exception-decorator": "^1.6.11",
|
||||
"@nestjs/common": "^10.0.0",
|
||||
"@nestjs/core": "^10.0.0",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Post,
|
||||
UseGuards,
|
||||
Request,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { AuthGuard } from './auth.guard';
|
||||
import { SignInDto } from '../user/user.dto';
|
||||
import {
|
||||
ApiOkResponse,
|
||||
ApiProperty,
|
||||
ApiUnauthorizedResponse,
|
||||
} from '@nestjs/swagger';
|
||||
import { ApiException } from '@nanogiants/nestjs-swagger-api-exception-decorator';
|
||||
|
||||
export class SignInResponse {
|
||||
@ApiProperty()
|
||||
accessToken: string;
|
||||
}
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private authService: AuthService) {}
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post()
|
||||
async signIn(@Body() signInDto: { name: string; password: string }) {
|
||||
@ApiOkResponse({ description: 'User found', type: SignInResponse })
|
||||
@ApiException(() => UnauthorizedException)
|
||||
async signIn(@Body() signInDto: SignInDto) {
|
||||
const { token } = await this.authService.signIn(
|
||||
signInDto.name,
|
||||
signInDto.password,
|
||||
@@ -25,10 +36,4 @@ export class AuthController {
|
||||
accessToken: token,
|
||||
};
|
||||
}
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Get('profile')
|
||||
getProfile(@Request() req) {
|
||||
return req.user;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,12 @@ import { AccessTokenPayload } from './auth.service';
|
||||
import { User } from '../user/user.entity';
|
||||
import { UserService } from '../user/user.service';
|
||||
|
||||
export const GetUser = createParamDecorator((data, req): User => {
|
||||
return req.user;
|
||||
});
|
||||
export const GetUser = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext): User => {
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
return request.user;
|
||||
},
|
||||
);
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuard implements CanActivate {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { AuthController } from './auth.controller';
|
||||
import { AuthService } from './auth.service';
|
||||
import { UserModule } from '../user/user.module';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { JWT_SECRET } from 'src/consts';
|
||||
import { JWT_SECRET } from '../consts';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
||||
@@ -6,14 +6,13 @@ import * as fs from 'fs';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
app.setGlobalPrefix('api');
|
||||
|
||||
const config = new DocumentBuilder().build();
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config, {
|
||||
deepScanRoutes: true,
|
||||
});
|
||||
SwaggerModule.setup('openapi', app, document);
|
||||
SwaggerModule.setup('openapi', app, document, {});
|
||||
fs.writeFileSync('./swagger-spec.json', JSON.stringify(document));
|
||||
}
|
||||
bootstrap();
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as fs from 'fs';
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
app.setGlobalPrefix('api');
|
||||
|
||||
app.enableCors();
|
||||
const config = new DocumentBuilder().build();
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config, {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { AuthGuard, GetUser } from '../auth/auth.guard';
|
||||
import { ApiNotFoundResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { CreateUserDto, UserDto } from './user.dto';
|
||||
import { User } from './user.entity';
|
||||
import { ApiException } from '@nanogiants/nestjs-swagger-api-exception-decorator';
|
||||
|
||||
@ApiTags('user')
|
||||
@Controller('user')
|
||||
@@ -22,9 +23,11 @@ export class UserController {
|
||||
|
||||
@UseGuards(AuthGuard)
|
||||
@Get()
|
||||
@ApiNotFoundResponse({ description: 'User not found' })
|
||||
@ApiOkResponse({ description: 'User found', type: UserDto })
|
||||
@ApiException(() => NotFoundException, { description: 'User not found' })
|
||||
async getProfile(@GetUser() user: User): Promise<UserDto> {
|
||||
console.log(user);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
@@ -35,7 +38,7 @@ export class UserController {
|
||||
@UseGuards(AuthGuard)
|
||||
@Get(':id')
|
||||
@ApiOkResponse({ description: 'User found', type: UserDto })
|
||||
@ApiNotFoundResponse({ description: 'User not found' })
|
||||
@ApiException(() => NotFoundException, { description: 'User not found' })
|
||||
async findById(
|
||||
@Param('id') id: string,
|
||||
@GetUser() callerUser: User,
|
||||
|
||||
@@ -19,3 +19,5 @@ export class CreateUserDto extends PickType(User, [
|
||||
] as const) {}
|
||||
|
||||
export class UpdateUserDto extends OmitType(User, ['id'] as const) {}
|
||||
|
||||
export class SignInDto extends PickType(User, ['name', 'password'] as const) {}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UserService } from './user.service';
|
||||
import { userProviders } from './user.providers';
|
||||
import { DatabaseModule } from 'src/database/database.module';
|
||||
import { UserController } from './user.controller';
|
||||
import { DatabaseModule } from '../database/database.module';
|
||||
|
||||
@Module({
|
||||
imports: [DatabaseModule],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DATA_SOURCE } from 'src/database/database.providers';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { User } from './user.entity';
|
||||
import { DATA_SOURCE } from '../database/database.providers';
|
||||
|
||||
export const USER_REPOSITORY = 'USER_REPOSITORY';
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"openapi":"3.0.0","paths":{"/api/user":{"get":{"operationId":"UserController_getProfile","parameters":[],"responses":{"200":{"description":"User found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"404":{"description":"User not found"}},"tags":["user"]},"post":{"operationId":"UserController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserDto"}}}},"responses":{"200":{"description":""}},"tags":["user"]}},"/api/user/{id}":{"get":{"operationId":"UserController_findById","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"User found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"404":{"description":"User not found"}},"tags":["user"]}},"/api/auth":{"post":{"operationId":"AuthController_signIn","parameters":[],"responses":{"200":{"description":""}}}},"/api/auth/profile":{"get":{"operationId":"AuthController_getProfile","parameters":[],"responses":{"200":{"description":""}}}},"/api":{"get":{"operationId":"AppController_getHello","parameters":[],"responses":{"200":{"description":""}}}}},"info":{"title":"","description":"","version":"1.0.0","contact":{}},"tags":[],"servers":[],"components":{"schemas":{"SonarrSettings":{"type":"object","properties":{"apiKey":{"type":"string"},"baseUrl":{"type":"string"},"qualityProfileId":{"type":"number"},"rootFolderPath":{"type":"string"},"languageProfileId":{"type":"number"}},"required":["apiKey","baseUrl","qualityProfileId","rootFolderPath","languageProfileId"]},"RadarrSettings":{"type":"object","properties":{"apiKey":{"type":"string"},"baseUrl":{"type":"string"},"qualityProfileId":{"type":"number"},"rootFolderPath":{"type":"string"}},"required":["apiKey","baseUrl","qualityProfileId","rootFolderPath"]},"JellyfinSettings":{"type":"object","properties":{"apiKey":{"type":"string"},"baseUrl":{"type":"string"},"userId":{"type":"string"}},"required":["apiKey","baseUrl","userId"]},"Settings":{"type":"object","properties":{"autoplayTrailers":{"type":"boolean"},"language":{"type":"string"},"animationDuration":{"type":"number"},"sonarr":{"$ref":"#/components/schemas/SonarrSettings"},"radarr":{"$ref":"#/components/schemas/RadarrSettings"},"jellyfin":{"$ref":"#/components/schemas/JellyfinSettings"}},"required":["autoplayTrailers","language","animationDuration","sonarr","radarr","jellyfin"]},"UserDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"isAdmin":{"type":"boolean"},"settings":{"$ref":"#/components/schemas/Settings"}},"required":["id","name","isAdmin","settings"]},"CreateUserDto":{"type":"object","properties":{"name":{"type":"string"},"password":{"type":"string"},"isAdmin":{"type":"boolean"}},"required":["name","password","isAdmin"]}}}}
|
||||
Reference in New Issue
Block a user