feat: implement AdminGuard and QueryGuard for enhanced access control; refactor API and query handling; add deleteQuery method in QueryHandlerService; update QueryResponse type for improved response handling

This commit is contained in:
Boris D
2025-10-10 11:54:30 +03:00
parent ca134414b0
commit 5a15833080
8 changed files with 71 additions and 9 deletions

View File

@ -1,7 +1,18 @@
import { Body, Controller, Delete, Inject, Post } from "@nestjs/common";
import {
Body,
Controller,
Delete,
Inject,
Post,
UseGuards,
} from "@nestjs/common";
import { ApiService } from "./api.service";
import { AdminGuard } from "./guards/admin.guard";
import { ApiTokenGuard } from "./guards/api-token.guard";
@Controller("api")
@UseGuards(ApiTokenGuard)
@UseGuards(AdminGuard)
export class ApiController {
constructor(
@Inject(ApiService)

View File

@ -7,7 +7,7 @@ import { ApiController } from "./api.controller";
import { Project } from "../project/entities/project.entity";
import { ApiTokenGuard } from "./guards/api-token.guard";
import { RedisModule } from "src/redis/redis.module";
import { QueryGuard } from "./guards/query.guard";
import { QueryGuard } from "../query/guards/query.guard";
import { QueryModule } from "src/query/query.module";
@Module({

View File

@ -9,6 +9,9 @@ export class Token {
@Column({ type: "tinyint", default: 1 })
isActive: boolean;
@Column({ type: "tinyint", default: 0 })
isAdmin: boolean;
@ManyToOne(() => Project, (project) => project.apiTokens)
project: Project;
}

View File

@ -0,0 +1,27 @@
import {
CanActivate,
ExecutionContext,
Inject,
Injectable,
UnauthorizedException,
} from "@nestjs/common";
import { ApiService } from "../api.service";
@Injectable()
export class AdminGuard implements CanActivate {
constructor(
@Inject(ApiService)
private readonly apiService: ApiService
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const apiToken = request.apiToken;
if (!apiToken || !apiToken.isAdmin) {
throw new UnauthorizedException("Admin privileges are required");
}
return true;
}
}

View File

@ -1,5 +1,6 @@
import {
Body,
Delete,
Headers,
Inject,
Param,
@ -11,7 +12,7 @@ import { Response } from "express";
import { QueryHandlerService } from "../handler/query.handler.service";
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
import { QueryExecuterService } from "../executer/query.executer.service";
import { QueryGuard } from "src/api/guards/query.guard";
import { QueryGuard } from "src/query/guards/query.guard";
@UseGuards(ApiTokenGuard)
export abstract class BaseQueryController {
@ -57,11 +58,12 @@ export abstract class BaseQueryController {
res.status(queryResult?.statusCode || 200);
if (
queryResult?.statusCode === 302 &&
queryResult?.redirect ||
(queryResult?.statusCode === 302 &&
queryResult?.headers &&
queryResult?.headers["Location"]
queryResult?.headers["Location"])
) {
res.redirect(queryResult?.headers["Location"]);
res.redirect(queryResult?.redirect || queryResult?.headers["Location"]);
return;
}
@ -71,4 +73,10 @@ export abstract class BaseQueryController {
res.send(queryResult?.response || null);
}
@Delete("/delete/:id")
@UseGuards(QueryGuard)
async deleteQuery(@Param("id") id: string) {
return this.queryHandlerService.deleteQuery(id);
}
}

View File

@ -68,4 +68,16 @@ export class QueryHandlerService {
Object.assign(query, updateData);
return this.queryRepository.save(query);
}
async deleteQuery(id: string) {
const query = await this.queryRepository.findOne({ where: { id } });
if (!query) {
throw new Error("Query not found");
}
await this.redisClient.del(`query_${id}`);
return this.queryRepository.remove(query);
}
}

View File

@ -46,7 +46,8 @@ export const registeredModules = {
};
export type QueryResponse = {
statusCode: number;
statusCode?: number;
response: any;
headers: Record<string, string>;
headers?: Record<string, string>;
redirect?: string;
};