feat: add isPublic field to Query entity and implement QueryPublicGuard for public query access control

This commit is contained in:
Boris D
2025-10-28 16:35:26 +02:00
parent 5d596832d6
commit bbc378dc95
3 changed files with 87 additions and 11 deletions

View File

@ -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<string, any>,
@Headers() headers: Record<string, any>,
@Res() res: Response,
@Req() req: Request & { query: Query }
) {
private async run(
id: string,
query: Record<string, any>,
headers: Record<string, any>,
res: Response,
req: Request & { query: Query }
): Promise<QueryResponse> {
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<string, any>,
@Headers() headers: Record<string, any>,
@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<string, any>,
@Headers() headers: Record<string, any>,
@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) {

View File

@ -30,4 +30,7 @@ export class Query {
@Column({ type: "tinyint", default: 0 })
isTypescript: number;
@Column({ type: "tinyint", default: 0 })
isPublic: number;
}

View File

@ -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<boolean> {
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;
}
}