feat: enhance DatabaseManagerService and QueryExecuterService with timeout settings, add AxiosPlugin for HTTP requests, and update DatabasePlugin to use query method

This commit is contained in:
Boris D
2025-10-09 17:20:33 +03:00
parent 0631e99886
commit 5b30b876e5
7 changed files with 104 additions and 13 deletions

View File

@ -79,6 +79,8 @@ export class DatabaseManagerService extends DatabaseEncryptionService {
user: queryUser ? database.q_username : database.c_username,
password: this.decryptPassword(database.password),
database: database.database,
idleTimeout: 150e3,
connectTimeout: 2e3,
};
}

View File

@ -61,7 +61,7 @@ export class QueryExecuterService {
{
removeOnComplete: true,
removeOnFail: true,
attempts: 3,
attempts: 0,
}
);
@ -130,6 +130,8 @@ export class QueryExecuterService {
const vm = new Vm({
memoryLimit: 128,
timeLimit: BigInt(100e9),
cpuTimeLimit: BigInt(5e9),
modules: modules,
plugins: plugins,
});

View File

@ -0,0 +1,54 @@
import axios from "axios";
import { Plugin } from "../plugin.class";
export class AxiosPlugin extends Plugin {
constructor(name: string) {
super(name, ["get", "post", "put", "delete", "head", "options", "patch"]);
}
static init(name: string): AxiosPlugin {
return new AxiosPlugin(name);
}
private configure(config) {
if (config.timeout > 10e3) {
config.timeout = 10e3;
} else {
config.timeout = 5e3;
}
return config;
}
async get(url: string, config?: any): Promise<any> {
return await axios.get(url, this.configure(config));
}
async post(url: string, data?: any, config?: any): Promise<any> {
return await axios.post(url, data, this.configure(config));
}
async put(url: string, data?: any, config?: any): Promise<any> {
return await axios.put(url, data, this.configure(config));
}
async delete(url: string, config?: any): Promise<any> {
return await axios.delete(url, this.configure(config));
}
async head(url: string, config?: any): Promise<any> {
return await axios.head(url, this.configure(config));
}
async options(url: string, config?: any): Promise<any> {
return await axios.options(url, this.configure(config));
}
async patch(url: string, data?: any, config?: any): Promise<any> {
return await axios.patch(url, data, this.configure(config));
}
onFinish() {
// No resources to clean up
}
}

View File

@ -9,7 +9,7 @@ export class DatabasePlugin extends Plugin {
);
}
super(name, ["execute"]);
super(name, ["query"]);
}
static async init(
@ -20,6 +20,8 @@ export class DatabasePlugin extends Plugin {
user: string;
password: string;
database: string;
idleTimeout: number;
connectTimeout: number;
}
): Promise<DatabasePlugin> {
const dbConnection = await mysql.createConnection({
@ -28,19 +30,18 @@ export class DatabasePlugin extends Plugin {
port: config.port,
password: config.password,
database: config.database,
idleTimeout: config.idleTimeout,
connectTimeout: config.connectTimeout,
enableKeepAlive: true,
});
await dbConnection.query("SET SESSION MAX_EXECUTION_TIME=2000;");
return new DatabasePlugin(name, dbConnection);
}
async execute(query): Promise<any> {
const [rows, fields] = await this.dbConnection.query(query);
return {
rows: rows,
fields: fields ?? [],
};
async query(query): Promise<any> {
return await this.dbConnection.query(query);
}
onFinish() {

View File

@ -10,15 +10,21 @@ export class Vm {
private jail: any;
private plugins: Plugin[];
private isolate: ivm.Isolate;
private timeLimit?: bigint;
private cpuTimeLimit?: bigint;
constructor(configs: {
memoryLimit: number;
timeLimit?: bigint;
cpuTimeLimit?: bigint;
modules: VModule[];
plugins: Plugin[];
}) {
this.memoryLimit = configs.memoryLimit;
this.modules = configs.modules;
this.plugins = configs.plugins;
this.timeLimit = configs.timeLimit;
this.cpuTimeLimit = configs.cpuTimeLimit;
}
async init(): Promise<Vm> {
@ -111,11 +117,23 @@ export class Vm {
const compiledScript = await this.isolate.compileScript(scriptWithResult);
const interval = setInterval(() => {
if (
this.isolate.cpuTime > this.cpuTimeLimit ||
this.isolate.wallTime > this.timeLimit
) {
this.isolate.dispose();
rejectPromise(new Error("Script execution timed out"));
}
}, 500);
compiledScript.run(this.context);
try {
return await resultPromise;
} finally {
clearInterval(interval);
this.onFinish();
}
}

View File

@ -2,6 +2,7 @@ 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";
import { AxiosPlugin } from "./plugins/axios.plugin";
export const registeredPlugins = {
db: async (service: QueryExecuterService, query: Query) => {
@ -17,6 +18,9 @@ export const registeredPlugins = {
return DatabasePlugin.init("db", databaseConnection);
},
axios: async () => {
return AxiosPlugin.init("axios");
},
query: async (service: QueryExecuterService, query: Query) => {
return QueryPlugin.init(query, service);
},

View File

@ -3,6 +3,7 @@
import "module/squel";
import "plugin/db";
import "plugin/axios";
function createSQL(id) {
return squel.select().from("test").where("id = ?", id).toString();
@ -11,13 +12,22 @@ function createSQL(id) {
async function main(input, headers) {
const sql = createSQL(input.id);
await db.execute("START TRANSACTION");
await db.query("START TRANSACTION");
// log(await db.execute('insert into test (name) values ("Test")'));
// log(await db.query('insert into test (name) values ("Test")'));
const res = await db.execute(sql);
log(new Date().toISOString());
// const a = await axios.get("https://httpbin.dev/delay/10", {
// timeout: 50000000,
// });
log(res);
// log(a);
const res = await db.query(`
SELECT SLEEP(10000);
`);
log(new Date().toISOString());
return {
response: {