From 39c40013b28717c50f89aa60155d69b624dbc21f Mon Sep 17 00:00:00 2001 From: Boris D Date: Mon, 6 Oct 2025 11:06:02 +0300 Subject: [PATCH] feat: implement ApiTokenGuard for authentication and apply it to relevant controllers --- src/api/api.module.ts | 4 +- src/api/guards/api-token.guard.ts | 41 +++++++++++++++++++ .../database.manager.module.ts | 2 + .../database/database.manager.controller.ts | 4 +- src/project/project.controller.ts | 4 +- src/project/project.module.ts | 5 ++- src/query/command/command.controller.ts | 3 ++ .../query.controller.ts} | 25 ++++++++++- src/query/handler/query.handler.controller.ts | 25 ----------- src/query/query.module.ts | 11 ++--- 10 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 src/api/guards/api-token.guard.ts rename src/query/{executer/query.executer.controller.ts => handler/query.controller.ts} (55%) delete mode 100644 src/query/handler/query.handler.controller.ts diff --git a/src/api/api.module.ts b/src/api/api.module.ts index 55a94f5..dd97f9e 100644 --- a/src/api/api.module.ts +++ b/src/api/api.module.ts @@ -5,10 +5,12 @@ import { ProjectModule } from "../project/project.module"; import { ApiService } from "./api.service"; import { ApiController } from "./api.controller"; import { Project } from "../project/entities/project.entity"; +import { ApiTokenGuard } from "./guards/api-token.guard"; @Module({ imports: [ProjectModule, TypeOrmModule.forFeature([Token, Project])], controllers: [ApiController], - providers: [ApiService], + providers: [ApiService, ApiTokenGuard], + exports: [ApiTokenGuard, TypeOrmModule], }) export class ApiModule {} diff --git a/src/api/guards/api-token.guard.ts b/src/api/guards/api-token.guard.ts new file mode 100644 index 0000000..15e6ace --- /dev/null +++ b/src/api/guards/api-token.guard.ts @@ -0,0 +1,41 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; +import { Token } from "../entities/token.entity"; + +@Injectable() +export class ApiTokenGuard implements CanActivate { + constructor( + @InjectRepository(Token) + private readonly tokenRepository: Repository + ) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const token = request.params?.token || request.headers?.["x-api-token"]; + + if (!token) { + throw new UnauthorizedException("API token is required"); + } + + const tokenEntity = await this.tokenRepository.findOne({ + where: { token }, + relations: ["project"], + }); + + if (!tokenEntity) { + throw new UnauthorizedException("Invalid API token"); + } + + if (!tokenEntity.isActive) { + throw new UnauthorizedException("API token is inactive"); + } + + return true; + } +} diff --git a/src/databaseManager/database.manager.module.ts b/src/databaseManager/database.manager.module.ts index b1f52cb..406dbed 100644 --- a/src/databaseManager/database.manager.module.ts +++ b/src/databaseManager/database.manager.module.ts @@ -8,11 +8,13 @@ import { Project } from "src/project/entities/project.entity"; import { DatabaseManagerController } from "./database/database.manager.controller"; import { DatabaseManagerService } from "./database/database.manager.service"; import { DatabaseNodeService } from "./databaseNode/database.node.service"; +import { ApiModule } from "src/api/api.module"; @Module({ imports: [ forwardRef(() => ProjectModule), forwardRef(() => MigrationModule), + forwardRef(() => ApiModule), TypeOrmModule.forFeature([Database, DatabaseNode, Project]), ], controllers: [DatabaseManagerController], diff --git a/src/databaseManager/database/database.manager.controller.ts b/src/databaseManager/database/database.manager.controller.ts index b59e564..c2b685f 100644 --- a/src/databaseManager/database/database.manager.controller.ts +++ b/src/databaseManager/database/database.manager.controller.ts @@ -1,9 +1,11 @@ -import { Controller, Get, Post, Body, Param } from "@nestjs/common"; +import { Controller, Get, Post, Body, Param, UseGuards } from "@nestjs/common"; import { DatabaseManagerService } from "./database.manager.service"; import { DatabaseNodeService } from "../databaseNode/database.node.service"; import { MigrationService } from "../migration/migration.service"; +import { ApiTokenGuard } from "src/api/guards/api-token.guard"; @Controller("database") +@UseGuards(ApiTokenGuard) export class DatabaseManagerController { constructor( private readonly databaseManagerService: DatabaseManagerService, diff --git a/src/project/project.controller.ts b/src/project/project.controller.ts index 6956661..71ad45a 100644 --- a/src/project/project.controller.ts +++ b/src/project/project.controller.ts @@ -1,7 +1,9 @@ -import { Body, Controller, Inject, Put } from "@nestjs/common"; +import { Body, Controller, Inject, Put, UseGuards } from "@nestjs/common"; import { ProjectService } from "./project.service"; +import { ApiTokenGuard } from "src/api/guards/api-token.guard"; @Controller("project") +@UseGuards(ApiTokenGuard) export class ProjectController { constructor( @Inject(ProjectService) diff --git a/src/project/project.module.ts b/src/project/project.module.ts index 4b08204..a31a948 100644 --- a/src/project/project.module.ts +++ b/src/project/project.module.ts @@ -1,11 +1,12 @@ -import { Module } from "@nestjs/common"; +import { forwardRef, Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; import { Project } from "./entities/project.entity"; import { ProjectService } from "./project.service"; import { ProjectController } from "./project.controller"; +import { ApiModule } from "src/api/api.module"; @Module({ - imports: [TypeOrmModule.forFeature([Project])], + imports: [forwardRef(() => ApiModule), TypeOrmModule.forFeature([Project])], controllers: [ProjectController], providers: [ProjectService], exports: [ProjectService], diff --git a/src/query/command/command.controller.ts b/src/query/command/command.controller.ts index b1ae1f2..83d76cf 100644 --- a/src/query/command/command.controller.ts +++ b/src/query/command/command.controller.ts @@ -6,12 +6,15 @@ import { Param, Post, Res, + UseGuards, } from "@nestjs/common"; import { QueryHandlerService } from "../handler/query.handler.service"; import { QueryExecuterService } from "../executer/query.executer.service"; import { Response } from "express"; +import { ApiTokenGuard } from "src/api/guards/api-token.guard"; @Controller("command") +@UseGuards(ApiTokenGuard) export class CommandController { constructor( @Inject(QueryHandlerService) diff --git a/src/query/executer/query.executer.controller.ts b/src/query/handler/query.controller.ts similarity index 55% rename from src/query/executer/query.executer.controller.ts rename to src/query/handler/query.controller.ts index 2cbb3c9..c96907f 100644 --- a/src/query/executer/query.executer.controller.ts +++ b/src/query/handler/query.controller.ts @@ -6,17 +6,38 @@ import { Param, Post, Res, + UseGuards, } from "@nestjs/common"; import { Response } from "express"; -import { QueryExecuterService } from "./query.executer.service"; +import { QueryHandlerService } from "./query.handler.service"; +import { ApiTokenGuard } from "src/api/guards/api-token.guard"; +import { QueryExecuterService } from "../executer/query.executer.service"; @Controller("query") -export class QueryExecuterController { +@UseGuards(ApiTokenGuard) +export class QueryController { constructor( + @Inject(QueryHandlerService) + private readonly queryHandlerService: QueryHandlerService, @Inject(QueryExecuterService) private readonly queryExecuterService: QueryExecuterService ) {} + @Post("create") + async createQuery( + @Body() queryData: { projectToken: string; source: string } + ) { + return this.queryHandlerService.createQuery(queryData); + } + + @Post("update/:id") + async updateQuery( + @Body() updateData: Partial<{ source: string }>, + @Inject("id") id: string + ) { + return this.queryHandlerService.updateQuery(id, updateData); + } + @Post("/run/:token") async runQuery( @Param("token") token: string, diff --git a/src/query/handler/query.handler.controller.ts b/src/query/handler/query.handler.controller.ts deleted file mode 100644 index 1e15443..0000000 --- a/src/query/handler/query.handler.controller.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Body, Controller, Inject, Post } from "@nestjs/common"; -import { QueryHandlerService } from "./query.handler.service"; - -@Controller("query") -export class QueryHandlerController { - constructor( - @Inject(QueryHandlerService) - private readonly queryHandlerService: QueryHandlerService - ) {} - - @Post("create") - async createQuery( - @Body() queryData: { projectToken: string; source: string } - ) { - return this.queryHandlerService.createQuery(queryData); - } - - @Post("update/:id") - async updateQuery( - @Body() updateData: Partial<{ source: string }>, - @Inject("id") id: string - ) { - return this.queryHandlerService.updateQuery(id, updateData); - } -} diff --git a/src/query/query.module.ts b/src/query/query.module.ts index 0002585..493bc69 100644 --- a/src/query/query.module.ts +++ b/src/query/query.module.ts @@ -1,25 +1,22 @@ import { forwardRef, Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; import { Query } from "./entities/query.entity"; -import { QueryExecuterController } from "./executer/query.executer.controller"; -import { QueryHandlerController } from "./handler/query.handler.controller"; +import { QueryController } from "./handler/query.controller"; import { QueryExecuterService } from "./executer/query.executer.service"; import { QueryHandlerService } from "./handler/query.handler.service"; import { ProjectModule } from "src/project/project.module"; import { DatabaseManagerModule } from "src/databaseManager/database.manager.module"; import { CommandController } from "./command/command.controller"; +import { ApiModule } from "src/api/api.module"; @Module({ imports: [ forwardRef(() => ProjectModule), forwardRef(() => DatabaseManagerModule), + forwardRef(() => ApiModule), TypeOrmModule.forFeature([Query]), ], - controllers: [ - QueryExecuterController, - QueryHandlerController, - CommandController, - ], + controllers: [QueryController, CommandController], providers: [QueryExecuterService, QueryHandlerService], }) export class QueryModule {}