Refactor VM and Plugin Management

- Removed the `plugins.constants.ts` file as it was no longer needed.
- Updated the `vm.class.ts` to improve result handling and logging.
- Introduced `vm.constants.ts` to manage registered plugins and modules.
- Simplified the test payload in `case1-payload.js` by removing unnecessary insert logic.
- Enhanced `case1.ts` to include database and migration setup, improving test clarity.
- Deleted unused functions for adding modules and plugins, streamlining the codebase.
This commit is contained in:
lborv
2025-09-29 20:58:51 +03:00
parent 1b552e651f
commit 593acf2a9a
21 changed files with 5391 additions and 469 deletions

View File

@ -57,6 +57,31 @@ export class DatabaseManagerService extends DatabaseEncryptionService {
}
}
async getConnectionOptions(projectId: string, queryUser = true) {
const project = await this.projectService.findById(projectId);
if (!project) {
throw new Error("Project not found");
}
const database = await this.databaseRepository.findOne({
where: { project: { id: project.id } },
relations: ["node"],
});
if (!database) {
throw new Error("Database not found");
}
return {
host: database.node.host,
port: database.node.port,
user: queryUser ? database.q_username : database.c_username,
password: this.decryptPassword(database.password),
database: database.database,
};
}
async createDatabase(
databaseNodeId: string,
projectId: string

View File

@ -1,8 +1,8 @@
import { DatabaseManagerModule } from "src/databaseManager/database/database.manager.module";
import { forwardRef, Module } from "@nestjs/common";
import { MigrationService } from "./migration.service";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Migration } from "../entities/migration.entity";
import { DatabaseManagerModule } from "../database.manager.module";
@Module({
imports: [

View File

@ -1,59 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class Fix1759005139426 implements MigrationInterface {
name = "Fix1759005139426";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE \`database\` ADD \`projectId\` varchar(36) NULL`
);
await queryRunner.query(
`ALTER TABLE \`database\` ADD UNIQUE INDEX \`IDX_3b4af405edcc8df2198286c89d\` (\`projectId\`)`
);
await queryRunner.query(
`ALTER TABLE \`project\` ADD \`databaseId\` varchar(36) NULL`
);
await queryRunner.query(
`ALTER TABLE \`project\` ADD UNIQUE INDEX \`IDX_6212e562f65574a73b47251265\` (\`databaseId\`)`
);
await queryRunner.query(
`CREATE UNIQUE INDEX \`REL_3b4af405edcc8df2198286c89d\` ON \`database\` (\`projectId\`)`
);
await queryRunner.query(
`CREATE UNIQUE INDEX \`REL_6212e562f65574a73b47251265\` ON \`project\` (\`databaseId\`)`
);
await queryRunner.query(
`ALTER TABLE \`database\` ADD CONSTRAINT \`FK_3b4af405edcc8df2198286c89d2\` FOREIGN KEY (\`projectId\`) REFERENCES \`project\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
await queryRunner.query(
`ALTER TABLE \`project\` ADD CONSTRAINT \`FK_6212e562f65574a73b47251265b\` FOREIGN KEY (\`databaseId\`) REFERENCES \`database\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE \`project\` DROP FOREIGN KEY \`FK_6212e562f65574a73b47251265b\``
);
await queryRunner.query(
`ALTER TABLE \`database\` DROP FOREIGN KEY \`FK_3b4af405edcc8df2198286c89d2\``
);
await queryRunner.query(
`DROP INDEX \`REL_6212e562f65574a73b47251265\` ON \`project\``
);
await queryRunner.query(
`DROP INDEX \`REL_3b4af405edcc8df2198286c89d\` ON \`database\``
);
await queryRunner.query(
`ALTER TABLE \`project\` DROP INDEX \`IDX_6212e562f65574a73b47251265\``
);
await queryRunner.query(
`ALTER TABLE \`project\` DROP COLUMN \`databaseId\``
);
await queryRunner.query(
`ALTER TABLE \`database\` DROP INDEX \`IDX_3b4af405edcc8df2198286c89d\``
);
await queryRunner.query(
`ALTER TABLE \`database\` DROP COLUMN \`projectId\``
);
}
}

View File

@ -1,9 +1,12 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class Init1759002866941 implements MigrationInterface {
name = "Init1759002866941";
export class Init1759168062983 implements MigrationInterface {
name = "Init1759168062983";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE \`redisNode\` (\`id\` varchar(36) NOT NULL, \`host\` varchar(255) NOT NULL, \`port\` int NOT NULL, \`user\` varchar(255) NOT NULL, \`password\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
);
await queryRunner.query(
`CREATE TABLE \`token\` (\`token\` varchar(36) NOT NULL, \`isActive\` tinyint NOT NULL DEFAULT '0', \`projectId\` varchar(36) NULL, PRIMARY KEY (\`token\`)) ENGINE=InnoDB`
);
@ -14,79 +17,61 @@ export class Init1759002866941 implements MigrationInterface {
`CREATE TABLE \`databaseNode\` (\`id\` varchar(36) NOT NULL, \`host\` varchar(255) NOT NULL, \`port\` int NOT NULL, \`username\` varchar(255) NOT NULL, \`password\` varchar(255) NOT NULL, UNIQUE INDEX \`IDX_DATABASE_NODE_HOST_PORT\` (\`host\`, \`port\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
);
await queryRunner.query(
`CREATE TABLE \`database\` (\`id\` varchar(36) NOT NULL, \`q_username\` varchar(255) NOT NULL, \`c_username\` varchar(255) NOT NULL, \`password\` varchar(255) NOT NULL, \`database\` varchar(255) NOT NULL, \`nodeId\` varchar(36) NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
`CREATE TABLE \`database\` (\`id\` varchar(36) NOT NULL, \`q_username\` varchar(255) NOT NULL, \`c_username\` varchar(255) NOT NULL, \`password\` varchar(255) NOT NULL, \`database\` varchar(255) NOT NULL, \`projectId\` varchar(36) NULL, \`nodeId\` varchar(36) NULL, UNIQUE INDEX \`REL_3b4af405edcc8df2198286c89d\` (\`projectId\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
);
await queryRunner.query(
`CREATE TABLE \`project\` (\`id\` varchar(36) NOT NULL, \`name\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
);
await queryRunner.query(
`CREATE TABLE \`module\` (\`id\` varchar(36) NOT NULL, \`sourcePath\` varchar(255) NOT NULL, \`name\` varchar(255) NOT NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
);
await queryRunner.query(
`CREATE TABLE \`plugin\` (\`id\` varchar(36) NOT NULL, \`class\` varchar(255) NOT NULL, \`name\` varchar(255) NOT NULL, \`config\` varchar(255) NOT NULL, \`queryId\` varchar(36) NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
`CREATE TABLE \`project\` (\`id\` varchar(36) NOT NULL, \`name\` varchar(255) NOT NULL, \`databaseId\` varchar(36) NULL, UNIQUE INDEX \`REL_6212e562f65574a73b47251265\` (\`databaseId\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
);
await queryRunner.query(
`CREATE TABLE \`query\` (\`id\` varchar(36) NOT NULL, \`source\` longtext NOT NULL, \`isActive\` tinyint NOT NULL DEFAULT '1', \`projectId\` varchar(36) NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
);
await queryRunner.query(
`CREATE TABLE \`query_modules_module\` (\`queryId\` varchar(36) NOT NULL, \`moduleId\` varchar(36) NOT NULL, INDEX \`IDX_12121324c524e12538de4948ce\` (\`queryId\`), INDEX \`IDX_7fb42dbc85874aafbd4bfb1c10\` (\`moduleId\`), PRIMARY KEY (\`queryId\`, \`moduleId\`)) ENGINE=InnoDB`
);
await queryRunner.query(
`ALTER TABLE \`token\` ADD CONSTRAINT \`FK_1e287222c887ffb2c3302880588\` FOREIGN KEY (\`projectId\`) REFERENCES \`project\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
await queryRunner.query(
`ALTER TABLE \`migration\` ADD CONSTRAINT \`FK_72832cfef0fdac2f4a0e017a6b8\` FOREIGN KEY (\`databaseId\`) REFERENCES \`database\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
await queryRunner.query(
`ALTER TABLE \`database\` ADD CONSTRAINT \`FK_3b4af405edcc8df2198286c89d2\` FOREIGN KEY (\`projectId\`) REFERENCES \`project\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
await queryRunner.query(
`ALTER TABLE \`database\` ADD CONSTRAINT \`FK_d668eb5daf53aa3eb388953db08\` FOREIGN KEY (\`nodeId\`) REFERENCES \`databaseNode\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
await queryRunner.query(
`ALTER TABLE \`plugin\` ADD CONSTRAINT \`FK_5162d18c3653d35ff4d104dd940\` FOREIGN KEY (\`queryId\`) REFERENCES \`query\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
`ALTER TABLE \`project\` ADD CONSTRAINT \`FK_6212e562f65574a73b47251265b\` FOREIGN KEY (\`databaseId\`) REFERENCES \`database\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
await queryRunner.query(
`ALTER TABLE \`query\` ADD CONSTRAINT \`FK_a8e4452beec9f0228fb45de99fe\` FOREIGN KEY (\`projectId\`) REFERENCES \`project\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
await queryRunner.query(
`ALTER TABLE \`query_modules_module\` ADD CONSTRAINT \`FK_12121324c524e12538de4948cee\` FOREIGN KEY (\`queryId\`) REFERENCES \`query\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE`
);
await queryRunner.query(
`ALTER TABLE \`query_modules_module\` ADD CONSTRAINT \`FK_7fb42dbc85874aafbd4bfb1c101\` FOREIGN KEY (\`moduleId\`) REFERENCES \`module\`(\`id\`) ON DELETE NO ACTION ON UPDATE NO ACTION`
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE \`query_modules_module\` DROP FOREIGN KEY \`FK_7fb42dbc85874aafbd4bfb1c101\``
);
await queryRunner.query(
`ALTER TABLE \`query_modules_module\` DROP FOREIGN KEY \`FK_12121324c524e12538de4948cee\``
);
await queryRunner.query(
`ALTER TABLE \`query\` DROP FOREIGN KEY \`FK_a8e4452beec9f0228fb45de99fe\``
);
await queryRunner.query(
`ALTER TABLE \`plugin\` DROP FOREIGN KEY \`FK_5162d18c3653d35ff4d104dd940\``
`ALTER TABLE \`project\` DROP FOREIGN KEY \`FK_6212e562f65574a73b47251265b\``
);
await queryRunner.query(
`ALTER TABLE \`database\` DROP FOREIGN KEY \`FK_d668eb5daf53aa3eb388953db08\``
);
await queryRunner.query(
`ALTER TABLE \`database\` DROP FOREIGN KEY \`FK_3b4af405edcc8df2198286c89d2\``
);
await queryRunner.query(
`ALTER TABLE \`migration\` DROP FOREIGN KEY \`FK_72832cfef0fdac2f4a0e017a6b8\``
);
await queryRunner.query(
`ALTER TABLE \`token\` DROP FOREIGN KEY \`FK_1e287222c887ffb2c3302880588\``
);
await queryRunner.query(
`DROP INDEX \`IDX_7fb42dbc85874aafbd4bfb1c10\` ON \`query_modules_module\``
);
await queryRunner.query(
`DROP INDEX \`IDX_12121324c524e12538de4948ce\` ON \`query_modules_module\``
);
await queryRunner.query(`DROP TABLE \`query_modules_module\``);
await queryRunner.query(`DROP TABLE \`query\``);
await queryRunner.query(`DROP TABLE \`plugin\``);
await queryRunner.query(`DROP TABLE \`module\``);
await queryRunner.query(
`DROP INDEX \`REL_6212e562f65574a73b47251265\` ON \`project\``
);
await queryRunner.query(`DROP TABLE \`project\``);
await queryRunner.query(
`DROP INDEX \`REL_3b4af405edcc8df2198286c89d\` ON \`database\``
);
await queryRunner.query(`DROP TABLE \`database\``);
await queryRunner.query(
`DROP INDEX \`IDX_DATABASE_NODE_HOST_PORT\` ON \`databaseNode\``
@ -94,5 +79,6 @@ export class Init1759002866941 implements MigrationInterface {
await queryRunner.query(`DROP TABLE \`databaseNode\``);
await queryRunner.query(`DROP TABLE \`migration\``);
await queryRunner.query(`DROP TABLE \`token\``);
await queryRunner.query(`DROP TABLE \`redisNode\``);
}
}

View File

@ -1,17 +0,0 @@
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from "typeorm";
import { Query } from "./query.entity";
@Entity("module")
export class VMModule {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ type: "varchar", length: 255, nullable: false })
sourcePath: string;
@Column({ type: "varchar", length: 255 })
name: string;
@ManyToMany(() => Query, (query) => query.modules)
queries: Query[];
}

View File

@ -1,20 +0,0 @@
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { Query } from "./query.entity";
@Entity("plugin")
export class VmPlugin {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ type: "varchar", length: 255, nullable: false })
class: string;
@Column({ type: "varchar", length: 255, nullable: false })
name: string;
@ManyToOne(() => Query, (query) => query.plugins)
query: Query;
@Column({ type: "varchar", length: 255 })
config: string;
}

View File

@ -1,15 +1,5 @@
import { Project } from "../../project/entities/project.entity";
import {
Column,
Entity,
ManyToMany,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
JoinTable,
} from "typeorm";
import { VMModule } from "./module.entity";
import { VmPlugin } from "./plugin.entity";
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
@Entity("query")
export class Query {
@ -24,15 +14,4 @@ export class Query {
@Column({ type: "tinyint", default: 1 })
isActive: number;
@ManyToMany(() => VMModule, (module) => module.queries)
@JoinTable({
name: "query_modules_module",
joinColumn: { name: "queryId", referencedColumnName: "id" },
inverseJoinColumn: { name: "moduleId", referencedColumnName: "id" },
})
modules: VMModule[];
@OneToMany(() => VmPlugin, (plugin) => plugin.query)
plugins: VmPlugin[];
}

View File

@ -3,20 +3,41 @@ import { InjectRepository } from "@nestjs/typeorm";
import { Query } from "../entities/query.entity";
import { Repository } from "typeorm";
import { Vm } from "../../vm/vm.class";
import { VModule } from "../../vm/module.class";
import { DatabasePlugin } from "src/vm/plugins/database.plugin";
import { VModule } from "src/vm/module.class";
import { registeredModules, registeredPlugins } from "src/vm/vm.constants";
import { DatabaseManagerService } from "src/databaseManager/database/database.manager.service";
@Injectable()
export class QueryExecuterService {
constructor(
@InjectRepository(Query)
private readonly queryRepository: Repository<Query>
readonly queryRepository: Repository<Query>,
readonly databaseManagerService: DatabaseManagerService
) {}
parseImports(source: string): string[] {
const importRegex =
/import\s+(?:[\w*\s{},]*\s+from\s+)?["']([^"']+)["'];?/g;
const imports: string[] = [];
let match: RegExpExecArray | null;
while ((match = importRegex.exec(source)) !== null) {
imports.push(match[1]);
}
return imports;
}
clearImports(source: string): string {
return source
.replace(/import\s+(?:[\w*\s{},]*\s+from\s+)?["']([^"']+)["'];?/g, "")
.trim();
}
async runQuery(token: string, queryData: any) {
const query = await this.queryRepository.findOne({
where: { id: token },
relations: ["modules", "plugins"],
relations: ["project"],
});
if (!query) {
@ -24,37 +45,51 @@ export class QueryExecuterService {
}
const vm = await this.createVm(query);
const result = await vm.runScript(query.source, queryData);
const result = await vm.runScript(
this.clearImports(query.source),
queryData
);
return { message: "Query executed", result, query: queryData };
}
private async createVm(query: Query) {
if (query.modules === undefined) {
query.modules = [];
}
const imports = this.parseImports(query.source);
const importsParsed = imports.map((imp) => {
const item = imp.split("/");
if (query.plugins === undefined) {
query.plugins = [];
return {
type: item[0],
name: item[1],
};
});
const moduleNames = importsParsed.filter((imp) => imp.type === "module");
const pluginNames = importsParsed.filter((imp) => imp.type === "plugin");
const modules = moduleNames.map((mod) => {
if (registeredModules[mod.name]) {
return new VModule(mod.name, registeredModules[mod.name]);
}
throw new Error(`Module ${mod.name} not found`);
});
const plugins = [];
for (const plugin of pluginNames) {
if (!registeredPlugins[plugin.name]) {
throw new Error(`Plugin ${plugin.name} not found`);
}
plugins.push(
await registeredPlugins[plugin.name].initMethod(this, query)
);
}
const vm = new Vm({
memoryLimit: 128,
modules: query.modules.map((module) => {
return new VModule(module.name, module.sourcePath);
}),
plugins: await Promise.all(
query.plugins.map((plugin) => {
switch (plugin.class) {
case "DATABASE": {
const config = JSON.parse(plugin.config);
return DatabasePlugin.init(plugin.name, config);
}
default:
throw new Error(`Unknown plugin class: ${plugin.class}`);
}
})
),
modules: modules,
plugins: plugins,
});
return await vm.init();

View File

@ -22,32 +22,4 @@ export class QueryHandlerController {
) {
return this.queryHandlerService.updateQuery(id, updateData);
}
@Post("module/create")
async createModule(@Body() moduleData: { name: string; sourcePath: string }) {
return this.queryHandlerService.createModule(moduleData);
}
@Post("plugin/create")
async createPlugin(
@Body() pluginData: { name: string; class: string; config: string }
) {
return this.queryHandlerService.createPlugin(pluginData);
}
@Post("module/add")
async addModuleToQuery(@Body() data: { queryId: string; moduleId: string }) {
return this.queryHandlerService.addModuleToQuery(
data.queryId,
data.moduleId
);
}
@Post("plugin/add")
async addPluginToQuery(@Body() data: { queryId: string; pluginId: string }) {
return this.queryHandlerService.addPluginToQuery(
data.queryId,
data.pluginId
);
}
}

View File

@ -1,8 +1,6 @@
import { Inject, Injectable } from "@nestjs/common";
import { Repository } from "typeorm";
import { Query } from "../entities/query.entity";
import { VMModule } from "../entities/module.entity";
import { VmPlugin } from "../entities/plugin.entity";
import { InjectRepository } from "@nestjs/typeorm";
import { ProjectService } from "src/project/project.service";
@ -11,111 +9,10 @@ export class QueryHandlerService {
constructor(
@InjectRepository(Query)
private readonly queryRepository: Repository<Query>,
@InjectRepository(VMModule)
private readonly moduleRepository: Repository<VMModule>,
@InjectRepository(VmPlugin)
private readonly pluginRepository: Repository<VmPlugin>,
@Inject(ProjectService)
private readonly projectService: ProjectService
) {}
private async createOrGetVmModule(
name: string,
sourcePath: string
): Promise<VMModule> {
const vmModule = await this.moduleRepository.findOne({
where: {
name,
},
});
if (vmModule) {
return vmModule;
}
const newModule = this.moduleRepository.create({
sourcePath,
name,
});
return this.moduleRepository.save(newModule);
}
private async createOrGetPlugin(
name: string,
className: string,
query: Query,
config: Record<string, any>
): Promise<VmPlugin> {
const plugin = await this.pluginRepository.findOne({
where: {
name,
query: {
id: query.id,
},
},
});
if (plugin) {
return plugin;
}
const newPlugin = this.pluginRepository.create({
name,
class: className,
config: JSON.stringify(config),
query,
});
return this.pluginRepository.save(newPlugin);
}
// TODO: make it nice
async createDefaults(query: Query): Promise<void> {
const asyncCallModule = await this.createOrGetVmModule(
"asyncCall",
"dist/vm/modules/async.js"
);
const squelModule = await this.createOrGetVmModule(
"query",
"node_modules/squel/dist/squel.min.js"
);
query.modules = [asyncCallModule, squelModule];
const dbPlugin = await this.createOrGetPlugin("db", "DATABASE", query, {
// TODO: take it from database handler
host: process.env.DB_HOST,
user: process.env.DB_USERNAME,
port: process.env.DB_PORT,
password: process.env.DB_PASSWORD,
database: "test",
});
query.plugins = [dbPlugin];
await this.queryRepository.save(query);
}
parseImports(source: string): string[] {
const importRegex =
/import\s+(?:[\w*\s{},]*\s+from\s+)?["']([^"']+)["'];?/g;
const imports: string[] = [];
let match: RegExpExecArray | null;
while ((match = importRegex.exec(source)) !== null) {
imports.push(match[1]);
}
return imports;
}
clearImports(source: string): string {
return source
.replace(/import\s+(?:[\w*\s{},]*\s+from\s+)?["']([^"']+)["'];?/g, "")
.trim();
}
async createQuery(queryData: { projectToken: string; source: string }) {
const project = await this.projectService.findById(queryData.projectToken);
@ -128,11 +25,6 @@ export class QueryHandlerService {
const query = this.queryRepository.create(queryData);
const imports = this.parseImports(query.source);
console.log("Parsed imports:", imports);
query.source = this.clearImports(query.source);
await this.queryRepository.save(query);
return query;
@ -148,62 +40,4 @@ export class QueryHandlerService {
Object.assign(query, updateData);
return this.queryRepository.save(query);
}
async createModule(moduleData: { name: string; sourcePath: string }) {
const module = this.moduleRepository.create(moduleData);
return this.moduleRepository.save(module);
}
async createPlugin(pluginData: {
name: string;
class: string;
config: string;
}) {
const plugin = this.pluginRepository.create(pluginData);
return this.pluginRepository.save(plugin);
}
async addModuleToQuery(queryId: string, moduleId: string) {
const query = await this.queryRepository.findOne({
where: { id: queryId },
relations: ["modules"],
});
if (!query) {
throw new Error("Query not found");
}
const module = await this.moduleRepository.findOne({
where: { id: moduleId },
});
if (!module) {
throw new Error("Module not found");
}
query.modules.push(module);
return this.queryRepository.save(query);
}
async addPluginToQuery(queryId: string, pluginId: string) {
const query = await this.queryRepository.findOne({
where: { id: queryId },
relations: ["plugins"],
});
if (!query) {
throw new Error("Query not found");
}
const plugin = await this.pluginRepository.findOne({
where: { id: pluginId },
});
if (!plugin) {
throw new Error("Plugin not found");
}
query.plugins.push(plugin);
return this.queryRepository.save(query);
}
}

View File

@ -1,18 +1,18 @@
import { forwardRef, Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Query } from "./entities/query.entity";
import { VMModule } from "./entities/module.entity";
import { QueryExecuterController } from "./executer/query.executer.controller";
import { QueryHandlerController } from "./handler/query.handler.controller";
import { QueryExecuterService } from "./executer/query.executer.service";
import { QueryHandlerService } from "./handler/query.handler.service";
import { VmPlugin } from "./entities/plugin.entity";
import { ProjectModule } from "src/project/project.module";
import { DatabaseManagerModule } from "src/databaseManager/database.manager.module";
@Module({
imports: [
forwardRef(() => ProjectModule),
TypeOrmModule.forFeature([Query, VMModule, VmPlugin]),
forwardRef(() => DatabaseManagerModule),
TypeOrmModule.forFeature([Query]),
],
controllers: [QueryExecuterController, QueryHandlerController],
providers: [QueryExecuterService, QueryHandlerService],

5214
src/vm/modules/squel.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +0,0 @@
import { DatabasePlugin } from "./plugins/database.plugin";
import { RedisPlugin } from "./plugins/redis.plugin";
export const PluginClass = {
DATABASE: DatabasePlugin,
REDIS: RedisPlugin,
};

View File

@ -56,10 +56,10 @@ export class Vm {
rejectPromise = reject;
});
this.setFunction("returnResult", (...args) => {
console.log("Script result:", args);
this.setFunction("returnResult", (res) => {
console.log("Returning result from VM:", res);
resolvePromise(args);
resolvePromise(JSON.parse(res));
});
// TODO: log
@ -77,7 +77,7 @@ export class Vm {
${script}
try {
const result = await main(${JSON.stringify(args)});
returnResult(result)
returnResult(JSON.stringify(result))
} catch (e) {
error(e)
}

26
src/vm/vm.constants.ts Normal file
View File

@ -0,0 +1,26 @@
import { QueryExecuterService } from "src/query/executer/query.executer.service";
import { DatabasePlugin } from "./plugins/database.plugin";
import { Query } from "src/query/entities/query.entity";
export const registeredPlugins = {
db: {
class: DatabasePlugin,
initMethod: async (service: QueryExecuterService, query: Query) => {
const databaseConnection =
await service.databaseManagerService.getConnectionOptions(
query.project.id
);
if (!databaseConnection) {
throw new Error("Database connection not found");
}
return DatabasePlugin.init("db", databaseConnection);
},
},
};
export const registeredModules = {
squel: "dist/vm/modules/squel.js",
asyncCall: "dist/vm/modules/async.js",
};

View File

@ -1,28 +1,17 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable no-undef */
async function insert() {
const res = await asyncCall(
db,
squel.insert().into("testTable").set("col", "test me now").toString()
);
return res;
}
import "module/squel";
import "module/asyncCall";
import "plugin/db";
function createSQL(id) {
return squel.select().from("testTable").where("id = ?", id).toString();
return squel.select().from("test").where("id = ?", id).toString();
}
async function main(input) {
const inserted = await insert();
log(inserted);
const sql = createSQL(inserted.rows.insertId);
const sql = createSQL(input.id);
const res = await asyncCall(db, sql);
log(res.rows);
return res;
return { test: 1, array: [1, 2, [{ id: 1, name: "Test" }]] };
}

View File

@ -1,13 +1,39 @@
import createMigration from "../functions/createMigration";
import createDatabase from "../functions/createDatabase";
import createDatabaseNode from "../functions/createDatabaseNode";
import createProject from "../functions/createProject";
import createQuery from "../functions/createQuery";
import databaseMigrationUp from "../functions/databaseMigrationUp";
import runQuery from "../functions/runQuery";
import * as fs from "fs";
import * as path from "path";
(async () => {
try {
const node = await createDatabaseNode("localhost", 3306, "root", "root");
console.log("Database node created:", node);
const project = await createProject("Test Project");
console.log("Project created:", project);
const db = await createDatabase(project.id, node.id);
console.log("Database created:", db);
const migration = await createMigration(
db.id,
"CREATE TABLE `test` (id INT AUTO_INCREMENT PRIMARY KEY, col1 VARCHAR(255))",
"DROP TABLE `test`"
);
console.log("Migration created:", migration);
const migrationResult = await databaseMigrationUp(db.id);
console.log("Migrations applied:", migrationResult);
const payloadPath = path.join(__dirname, "case1-payload.js");
const query = await createQuery(
project,
@ -16,10 +42,10 @@ import * as path from "path";
console.log(query);
const result = await runQuery(query.id, { name: "World" });
const result = await runQuery(query.id, { id: 1 });
console.log("Query Result:", result);
console.log("Query Result:", result.data);
} catch (error) {
console.error("Error during test execution:", error);
console.error("Error during test execution");
}
})();

View File

@ -1,15 +0,0 @@
import axios from "axios";
import { config } from "tests/config";
export default async (query: { id: string }, module: { id: string }) => {
try {
const response = await axios.post(`${config.url}/query/module/add`, {
queryId: query.id,
moduleId: module.id,
});
return response;
} catch (error) {
console.error("Error in adding module to query:", error);
}
};

View File

@ -1,15 +0,0 @@
import axios from "axios";
import { config } from "tests/config";
export default async (query: { id: string }, plugin: { id: string }) => {
try {
const response = await axios.post(`${config.url}/query/plugin/add`, {
queryId: query.id,
pluginId: plugin.id,
});
return response;
} catch (error) {
console.error("Error in adding plugin to query:", error);
}
};

View File

@ -1,15 +0,0 @@
import axios from "axios";
import { config } from "tests/config";
export default async (name: string, sourcePath: string) => {
try {
const response = await axios.post(`${config.url}/query/module/create`, {
name,
sourcePath,
});
return response;
} catch (error) {
console.error("Error in creating project or query:", error);
}
};

View File

@ -1,16 +0,0 @@
import axios from "axios";
import { config as appConfig } from "../config";
export default async (name: string, className: string, config: string) => {
try {
const response = await axios.post(`${appConfig.url}/query/plugin/create`, {
name,
class: className,
config,
});
return response;
} catch (error) {
console.error("Error in creating project or query:", error);
}
};