feat: implement command functionality with new CommandController, update Query entity, and enhance query execution with headers support

This commit is contained in:
lborv
2025-10-02 10:02:44 +03:00
parent e53e3cf92c
commit efbb9f5c21
11 changed files with 122 additions and 11 deletions

View File

@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class Commands1759387204103 implements MigrationInterface {
name = "Commands1759387204103";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE \`query\` ADD \`isCommand\` tinyint NOT NULL DEFAULT '0'`
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE \`query\` DROP COLUMN \`isCommand\``);
}
}

View File

@ -0,0 +1,37 @@
import { Body, Controller, Headers, Inject, Param, Post } from "@nestjs/common";
import { QueryHandlerService } from "../handler/query.handler.service";
import { QueryExecuterService } from "../executer/query.executer.service";
@Controller("command")
export class CommandController {
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, true);
}
@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,
@Body() query: Record<string, any>,
@Headers() headers: Record<string, any>
) {
return this.queryExecuterService.runQuery(token, query, headers);
}
}

View File

@ -14,4 +14,7 @@ export class Query {
@Column({ type: "tinyint", default: 1 })
isActive: number;
@Column({ type: "tinyint", default: 0 })
isCommand: number;
}

View File

@ -1,4 +1,4 @@
import { Body, Controller, Inject, Param, Post } from "@nestjs/common";
import { Body, Controller, Headers, Inject, Param, Post } from "@nestjs/common";
import { QueryExecuterService } from "./query.executer.service";
@Controller("query")
@ -11,8 +11,9 @@ export class QueryExecuterController {
@Post("/run/:token")
async runQuery(
@Param("token") token: string,
@Body() query: Record<string, any>
@Body() query: Record<string, any>,
@Headers() headers: Record<string, any>
) {
return this.queryExecuterService.runQuery(token, query);
return this.queryExecuterService.runQuery(token, query, headers);
}
}

View File

@ -34,7 +34,11 @@ export class QueryExecuterService {
.trim();
}
async runQuery(token: string, queryData: any) {
async runQuery(
token: string,
queryData: any,
headers: Record<string, any> = {}
) {
const query = await this.queryRepository.findOne({
where: { id: token },
relations: ["project"],
@ -47,7 +51,8 @@ export class QueryExecuterService {
const vm = await this.createVm(query);
const result = await vm.runScript(
this.clearImports(query.source),
queryData
queryData,
headers
);
return { message: "Query executed", result, query: queryData };

View File

@ -13,7 +13,10 @@ export class QueryHandlerService {
private readonly projectService: ProjectService
) {}
async createQuery(queryData: { projectToken: string; source: string }) {
async createQuery(
queryData: { projectToken: string; source: string },
isCommand = false
) {
const project = await this.projectService.findById(queryData.projectToken);
if (!project) {
@ -21,6 +24,7 @@ export class QueryHandlerService {
}
queryData["project"] = project;
queryData["isCommand"] = isCommand ? 1 : 0;
delete queryData.projectToken;
const query = this.queryRepository.create(queryData);

View File

@ -7,6 +7,7 @@ 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";
@Module({
imports: [
@ -14,7 +15,11 @@ import { DatabaseManagerModule } from "src/databaseManager/database.manager.modu
forwardRef(() => DatabaseManagerModule),
TypeOrmModule.forFeature([Query]),
],
controllers: [QueryExecuterController, QueryHandlerController],
controllers: [
QueryExecuterController,
QueryHandlerController,
CommandController,
],
providers: [QueryExecuterService, QueryHandlerService],
})
export class QueryModule {}

View File

@ -0,0 +1,25 @@
import { QueryExecuterService } from "../../query/executer/query.executer.service";
import { Query } from "../../query/entities/query.entity";
import { Plugin } from "../plugin.class";
export class QueryPlugin extends Plugin {
constructor(
name: string,
private query: Query,
private QueryExecuterService: QueryExecuterService
) {
super(name);
}
static async init(query: Query, queryExecuterService: QueryExecuterService) {
return new QueryPlugin("query", query, queryExecuterService);
}
async run(data): Promise<any> {
return await this.QueryExecuterService.runQuery(this.query.id, data);
}
onFinish() {
// No resources to clean up
}
}

View File

@ -47,7 +47,11 @@ export class Vm {
this.jail.setSync(name, func);
}
async runScript(script: string, args: Record<string, any>): Promise<any> {
async runScript(
script: string,
args: Record<string, any>,
headers: Record<string, any>
): Promise<any> {
let resolvePromise: (value: any) => void;
let rejectPromise: (reason?: any) => void;
@ -76,7 +80,9 @@ export class Vm {
(async () => {
${script}
try {
const result = await main(${JSON.stringify(args)});
const result = await main(${JSON.stringify(
args
)}, ${JSON.stringify(headers)});
returnResult(JSON.stringify(result))
} catch (e) {
error(e)

View File

@ -1,12 +1,14 @@
import { QueryExecuterService } from "src/query/executer/query.executer.service";
import { DatabasePlugin } from "./plugins/database.plugin";
import { Query } from "src/query/entities/query.entity";
import { QueryPlugin } from "./plugins/query.plugin";
export const registeredPlugins = {
db: async (service: QueryExecuterService, query: Query) => {
const databaseConnection =
await service.databaseManagerService.getConnectionOptions(
query.project.id
query.project.id,
query.isCommand == 0
);
if (!databaseConnection) {
@ -15,6 +17,12 @@ export const registeredPlugins = {
return DatabasePlugin.init("db", databaseConnection);
},
query: async (service: QueryExecuterService, query: Query) => {
return QueryPlugin.init(query, service);
},
command: async (service: QueryExecuterService, query: Query) => {
return QueryPlugin.init(query, service);
},
};
export const registeredModules = {

View File

@ -9,9 +9,11 @@ function createSQL(id) {
return squel.select().from("test").where("id = ?", id).toString();
}
async function main(input) {
async function main(input, headers) {
const sql = createSQL(input.id);
const res = await asyncCall(db, sql);
log(headers);
return { test: 1, array: [1, 2, [{ id: 1, name: "Test" }]] };
}