From bbc378dc956f3933e49d128ef0b589fc5389eeb0 Mon Sep 17 00:00:00 2001 From: Boris D Date: Tue, 28 Oct 2025 16:35:26 +0200 Subject: [PATCH] feat: add isPublic field to Query entity and implement QueryPublicGuard for public query access control --- src/query/base/base-query.controller.ts | 51 +++++++++++++++++++------ src/query/entities/query.entity.ts | 3 ++ src/query/guards/query-public.ts | 44 +++++++++++++++++++++ 3 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 src/query/guards/query-public.ts diff --git a/src/query/base/base-query.controller.ts b/src/query/base/base-query.controller.ts index 859ebf5..0680dff 100644 --- a/src/query/base/base-query.controller.ts +++ b/src/query/base/base-query.controller.ts @@ -19,6 +19,7 @@ import { TLogType } from "../logger/logger.types"; import { QueryResponse } from "src/vm/vm.constants"; import { Query } from "../entities/query.entity"; import { Token } from "src/api/entities/token.entity"; +import { QueryPublicGuard } from "../guards/query-public"; @UseGuards(ApiTokenGuard) export abstract class BaseQueryController { @@ -36,7 +37,8 @@ export abstract class BaseQueryController { @Post("create") async createQuery( @Req() req: Request & { apiToken: Token }, - @Body() queryData: { source: string; isTypescript?: number } + @Body() + queryData: { source: string; isTypescript?: number; isPublic?: number } ) { return this.queryHandlerService.createQuery( { @@ -50,21 +52,24 @@ export abstract class BaseQueryController { @Post("update/:id") @UseGuards(QueryGuard) async updateQuery( - @Body() updateData: Partial<{ source: string; isTypescript?: number }>, + @Body() + updateData: Partial<{ + source: string; + isTypescript?: number; + isPublic?: number; + }>, @Param("id") id: string ) { return this.queryHandlerService.updateQuery(id, updateData); } - @Post("/run/:id") - @UseGuards(QueryGuard) - async runQuery( - @Param("id") id: string, - @Body() query: Record, - @Headers() headers: Record, - @Res() res: Response, - @Req() req: Request & { query: Query } - ) { + private async run( + id: string, + query: Record, + headers: Record, + res: Response, + req: Request & { query: Query } + ): Promise { let queryResult: QueryResponse; const loggerTraceId = headers["x-trace-id"] || LoggerService.generateTraceId(); @@ -143,6 +148,30 @@ export abstract class BaseQueryController { res.send(queryResult?.response || null); } + @Post("/run-public/:id") + @UseGuards(QueryPublicGuard) + async runPublicQuery( + @Param("id") id: string, + @Body() query: Record, + @Headers() headers: Record, + @Res() res: Response, + @Req() req: Request & { query: Query } + ) { + return this.run(id, query, headers, res, req); + } + + @Post("/run/:id") + @UseGuards(QueryGuard) + async runQuery( + @Param("id") id: string, + @Body() query: Record, + @Headers() headers: Record, + @Res() res: Response, + @Req() req: Request & { query: Query } + ) { + return this.run(id, query, headers, res, req); + } + @Delete("/delete/:id") @UseGuards(QueryGuard) async deleteQuery(@Param("id") id: string) { diff --git a/src/query/entities/query.entity.ts b/src/query/entities/query.entity.ts index 0173888..68557a0 100644 --- a/src/query/entities/query.entity.ts +++ b/src/query/entities/query.entity.ts @@ -30,4 +30,7 @@ export class Query { @Column({ type: "tinyint", default: 0 }) isTypescript: number; + + @Column({ type: "tinyint", default: 0 }) + isPublic: number; } diff --git a/src/query/guards/query-public.ts b/src/query/guards/query-public.ts new file mode 100644 index 0000000..03d1089 --- /dev/null +++ b/src/query/guards/query-public.ts @@ -0,0 +1,44 @@ +import { + CanActivate, + ExecutionContext, + Inject, + Injectable, + UnauthorizedException, +} from "@nestjs/common"; +import { QueryHandlerService } from "src/query/handler/query.handler.service"; + +@Injectable() +export class QueryPublicGuard implements CanActivate { + constructor( + @Inject(QueryHandlerService) + private readonly queryHandlerService: QueryHandlerService + ) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + + const queryId = request.params?.id; + + if (!queryId) { + throw new UnauthorizedException("Query ID is required"); + } + + const query = await this.queryHandlerService.getQueryById(queryId); + + if (!query) { + throw new UnauthorizedException("Query not found"); + } + + if (!query.isActive) { + throw new UnauthorizedException("Query is inactive"); + } + + if (query.isPublic !== 1) { + throw new UnauthorizedException("Query is not public"); + } + + request.query = query; + + return true; + } +}