diff --git a/src/migrations/1759387204103-commands.ts b/src/migrations/1759387204103-commands.ts new file mode 100644 index 0000000..db1bf56 --- /dev/null +++ b/src/migrations/1759387204103-commands.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class Commands1759387204103 implements MigrationInterface { + name = "Commands1759387204103"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE \`query\` ADD \`isCommand\` tinyint NOT NULL DEFAULT '0'` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`query\` DROP COLUMN \`isCommand\``); + } +} diff --git a/src/query/command/command.controller.ts b/src/query/command/command.controller.ts new file mode 100644 index 0000000..3b46619 --- /dev/null +++ b/src/query/command/command.controller.ts @@ -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, + @Headers() headers: Record + ) { + return this.queryExecuterService.runQuery(token, query, headers); + } +} diff --git a/src/query/entities/query.entity.ts b/src/query/entities/query.entity.ts index 9135b62..e44fa0e 100644 --- a/src/query/entities/query.entity.ts +++ b/src/query/entities/query.entity.ts @@ -14,4 +14,7 @@ export class Query { @Column({ type: "tinyint", default: 1 }) isActive: number; + + @Column({ type: "tinyint", default: 0 }) + isCommand: number; } diff --git a/src/query/executer/query.executer.controller.ts b/src/query/executer/query.executer.controller.ts index df96cd2..3733b07 100644 --- a/src/query/executer/query.executer.controller.ts +++ b/src/query/executer/query.executer.controller.ts @@ -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 + @Body() query: Record, + @Headers() headers: Record ) { - return this.queryExecuterService.runQuery(token, query); + return this.queryExecuterService.runQuery(token, query, headers); } } diff --git a/src/query/executer/query.executer.service.ts b/src/query/executer/query.executer.service.ts index c3fcd6f..a6c47ce 100644 --- a/src/query/executer/query.executer.service.ts +++ b/src/query/executer/query.executer.service.ts @@ -34,7 +34,11 @@ export class QueryExecuterService { .trim(); } - async runQuery(token: string, queryData: any) { + async runQuery( + token: string, + queryData: any, + headers: Record = {} + ) { 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 }; diff --git a/src/query/handler/query.handler.service.ts b/src/query/handler/query.handler.service.ts index 3dfb95a..445dc2a 100644 --- a/src/query/handler/query.handler.service.ts +++ b/src/query/handler/query.handler.service.ts @@ -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); diff --git a/src/query/query.module.ts b/src/query/query.module.ts index 636424d..0002585 100644 --- a/src/query/query.module.ts +++ b/src/query/query.module.ts @@ -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 {} diff --git a/src/vm/plugins/query.plugin.ts b/src/vm/plugins/query.plugin.ts new file mode 100644 index 0000000..12fd6b7 --- /dev/null +++ b/src/vm/plugins/query.plugin.ts @@ -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 { + return await this.QueryExecuterService.runQuery(this.query.id, data); + } + + onFinish() { + // No resources to clean up + } +} diff --git a/src/vm/vm.class.ts b/src/vm/vm.class.ts index d8e2a64..aabbedb 100644 --- a/src/vm/vm.class.ts +++ b/src/vm/vm.class.ts @@ -47,7 +47,11 @@ export class Vm { this.jail.setSync(name, func); } - async runScript(script: string, args: Record): Promise { + async runScript( + script: string, + args: Record, + headers: Record + ): Promise { 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) diff --git a/src/vm/vm.constants.ts b/src/vm/vm.constants.ts index 3567856..a3ea2ee 100644 --- a/src/vm/vm.constants.ts +++ b/src/vm/vm.constants.ts @@ -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 = { diff --git a/tests/base/case1-payload.js b/tests/base/case1-payload.js index b702737..8b18790 100644 --- a/tests/base/case1-payload.js +++ b/tests/base/case1-payload.js @@ -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" }]] }; }