diff --git a/.env.example b/.env.example index 176e132..28dabf9 100644 --- a/.env.example +++ b/.env.example @@ -10,3 +10,6 @@ SSL_CERT_FILE="" # API PREFIX="api/v1/" + +# Security +ADMIN_KEY="admin" diff --git a/src/modules/webtoon/admin/admin.controller.ts b/src/modules/webtoon/admin/admin.controller.ts index e3026f6..46476d8 100644 --- a/src/modules/webtoon/admin/admin.controller.ts +++ b/src/modules/webtoon/admin/admin.controller.ts @@ -1,13 +1,15 @@ -import {Body, Controller, Delete, Get, HttpCode, Post} from "@nestjs/common"; -import {ApiResponse, ApiTags} from "@nestjs/swagger"; +import {Body, Controller, Delete, Get, HttpCode, Post, UseGuards} from "@nestjs/common"; +import {ApiBearerAuth, ApiResponse, ApiTags} from "@nestjs/swagger"; import {DownloadManagerService} from "../webtoon/download-manager.service"; import {AddWebtoonToQueueDto} from "./models/dto/add-webtoon-to-queue.dto"; import {HttpStatusCode} from "axios"; import CachedWebtoonModel from "../webtoon/models/models/cached-webtoon.model"; +import {AdminGuard} from "./guard/admin.guard"; @Controller("admin") @ApiTags("Admin") +@UseGuards(AdminGuard) export class AdminController{ constructor( @@ -15,6 +17,7 @@ export class AdminController{ ){} @Post("queue") + @ApiBearerAuth() @ApiResponse({status: HttpStatusCode.Created, description: "Adds a webtoon to the download queue"}) @ApiResponse({status: HttpStatusCode.TooEarly, description: "Cache not loaded."}) async addWebtoonToQueue(@Body() addWebtoonToQueueDto: AddWebtoonToQueueDto): Promise{ @@ -22,6 +25,7 @@ export class AdminController{ } @Post("update/all") + @ApiBearerAuth() @ApiResponse({status: HttpStatusCode.Created, description: "Updates all webtoons in the database"}) @ApiResponse({status: HttpStatusCode.TooEarly, description: "Cache not loaded."}) async updateAllWebtoons(): Promise{ @@ -29,6 +33,7 @@ export class AdminController{ } @Get("current-download") + @ApiBearerAuth() @ApiResponse({status: HttpStatusCode.Ok, description: "Returns the current download"}) @ApiResponse({status: HttpStatusCode.NotFound, description: "No download in progress"}) async getCurrentDownload(): Promise{ @@ -36,6 +41,7 @@ export class AdminController{ } @Get("queue") + @ApiBearerAuth() @ApiResponse({status: HttpStatusCode.Ok, description: "Returns the current download queue"}) @ApiResponse({status: HttpStatusCode.NotFound, description: "No download in progress"}) async getQueue(): Promise{ @@ -43,6 +49,7 @@ export class AdminController{ } @Delete("current-download") + @ApiBearerAuth() @HttpCode(HttpStatusCode.NoContent) @ApiResponse({status: HttpStatusCode.NoContent, description: "Cancels the current download"}) async cancelCurrentDownload(): Promise{ @@ -50,6 +57,7 @@ export class AdminController{ } @Delete("queue") + @ApiBearerAuth() @HttpCode(HttpStatusCode.NoContent) @ApiResponse({status: HttpStatusCode.NoContent, description: "Clears the download queue"}) async clearQueue(): Promise{ diff --git a/src/modules/webtoon/admin/admin.module.ts b/src/modules/webtoon/admin/admin.module.ts index 13274fc..b3f2745 100644 --- a/src/modules/webtoon/admin/admin.module.ts +++ b/src/modules/webtoon/admin/admin.module.ts @@ -1,10 +1,11 @@ import {Module} from "@nestjs/common"; import {AdminController} from "./admin.controller"; import {WebtoonModule} from "../webtoon/webtoon.module"; +import {AdminGuard} from "./guard/admin.guard"; @Module({ imports: [WebtoonModule], controllers: [AdminController], - providers: [], + providers: [AdminGuard], }) export class AdminModule{} diff --git a/src/modules/webtoon/admin/guard/admin.guard.ts b/src/modules/webtoon/admin/guard/admin.guard.ts new file mode 100644 index 0000000..8780901 --- /dev/null +++ b/src/modules/webtoon/admin/guard/admin.guard.ts @@ -0,0 +1,20 @@ +import {CanActivate, ExecutionContext, Injectable, UnauthorizedException} from "@nestjs/common"; +import {ConfigService} from "@nestjs/config"; + +@Injectable() +export class AdminGuard implements CanActivate{ + constructor( + private readonly configService: ConfigService, + ){} + + async canActivate(context: ExecutionContext): Promise{ + const adminKey = this.configService.get("ADMIN_KEY"); + const request = context.switchToHttp().getRequest(); + const token = request.headers.authorization?.split(" ")[1]; + if(!token) + throw new UnauthorizedException("No token provided"); + if(token !== adminKey) + throw new UnauthorizedException("Invalid token"); + return true; + } +}