feat: add isPublic field to Query entity and implement QueryPublicGuard for public query access control
This commit is contained in:
@ -19,6 +19,7 @@ import { TLogType } from "../logger/logger.types";
|
|||||||
import { QueryResponse } from "src/vm/vm.constants";
|
import { QueryResponse } from "src/vm/vm.constants";
|
||||||
import { Query } from "../entities/query.entity";
|
import { Query } from "../entities/query.entity";
|
||||||
import { Token } from "src/api/entities/token.entity";
|
import { Token } from "src/api/entities/token.entity";
|
||||||
|
import { QueryPublicGuard } from "../guards/query-public";
|
||||||
|
|
||||||
@UseGuards(ApiTokenGuard)
|
@UseGuards(ApiTokenGuard)
|
||||||
export abstract class BaseQueryController {
|
export abstract class BaseQueryController {
|
||||||
@ -36,7 +37,8 @@ export abstract class BaseQueryController {
|
|||||||
@Post("create")
|
@Post("create")
|
||||||
async createQuery(
|
async createQuery(
|
||||||
@Req() req: Request & { apiToken: Token },
|
@Req() req: Request & { apiToken: Token },
|
||||||
@Body() queryData: { source: string; isTypescript?: number }
|
@Body()
|
||||||
|
queryData: { source: string; isTypescript?: number; isPublic?: number }
|
||||||
) {
|
) {
|
||||||
return this.queryHandlerService.createQuery(
|
return this.queryHandlerService.createQuery(
|
||||||
{
|
{
|
||||||
@ -50,21 +52,24 @@ export abstract class BaseQueryController {
|
|||||||
@Post("update/:id")
|
@Post("update/:id")
|
||||||
@UseGuards(QueryGuard)
|
@UseGuards(QueryGuard)
|
||||||
async updateQuery(
|
async updateQuery(
|
||||||
@Body() updateData: Partial<{ source: string; isTypescript?: number }>,
|
@Body()
|
||||||
|
updateData: Partial<{
|
||||||
|
source: string;
|
||||||
|
isTypescript?: number;
|
||||||
|
isPublic?: number;
|
||||||
|
}>,
|
||||||
@Param("id") id: string
|
@Param("id") id: string
|
||||||
) {
|
) {
|
||||||
return this.queryHandlerService.updateQuery(id, updateData);
|
return this.queryHandlerService.updateQuery(id, updateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post("/run/:id")
|
private async run(
|
||||||
@UseGuards(QueryGuard)
|
id: string,
|
||||||
async runQuery(
|
query: Record<string, any>,
|
||||||
@Param("id") id: string,
|
headers: Record<string, any>,
|
||||||
@Body() query: Record<string, any>,
|
res: Response,
|
||||||
@Headers() headers: Record<string, any>,
|
req: Request & { query: Query }
|
||||||
@Res() res: Response,
|
): Promise<QueryResponse> {
|
||||||
@Req() req: Request & { query: Query }
|
|
||||||
) {
|
|
||||||
let queryResult: QueryResponse;
|
let queryResult: QueryResponse;
|
||||||
const loggerTraceId =
|
const loggerTraceId =
|
||||||
headers["x-trace-id"] || LoggerService.generateTraceId();
|
headers["x-trace-id"] || LoggerService.generateTraceId();
|
||||||
@ -143,6 +148,30 @@ export abstract class BaseQueryController {
|
|||||||
res.send(queryResult?.response || null);
|
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")
|
@Delete("/delete/:id")
|
||||||
@UseGuards(QueryGuard)
|
@UseGuards(QueryGuard)
|
||||||
async deleteQuery(@Param("id") id: string) {
|
async deleteQuery(@Param("id") id: string) {
|
||||||
|
|||||||
@ -30,4 +30,7 @@ export class Query {
|
|||||||
|
|
||||||
@Column({ type: "tinyint", default: 0 })
|
@Column({ type: "tinyint", default: 0 })
|
||||||
isTypescript: number;
|
isTypescript: number;
|
||||||
|
|
||||||
|
@Column({ type: "tinyint", default: 0 })
|
||||||
|
isPublic: number;
|
||||||
}
|
}
|
||||||
|
|||||||
44
src/query/guards/query-public.ts
Normal file
44
src/query/guards/query-public.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user