Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b77f7e42c | |||
| 9e79b44bdb | |||
| 7fad278d31 | |||
| 776d8a8187 | |||
| e4b29a918f | |||
| 4acd59b482 | |||
| 84c48dd482 | |||
| bbc378dc95 | |||
| 5d596832d6 | |||
| 3a1249615e | |||
| 0ac6b7db6f | |||
| 9080f193c1 | |||
| 1a2d7b20c0 | |||
| e1fce6d11d | |||
| 1d5160e60e | |||
| 038f2f8605 | |||
| 91ad421b8d | |||
| ee5ad66759 | |||
| f7b775f87b | |||
| 6992041429 | |||
| 0f0d082a17 | |||
| f046999828 | |||
| eb65eec9c0 | |||
| a139e957b1 | |||
| 96c560a691 | |||
| 9c924c525b | |||
| fce94e7ffd | |||
| 70f6fd68bf | |||
| 6e95a0c1a9 | |||
| ff664c2086 | |||
| aaa8680421 | |||
| 41f1c402ed | |||
| 93f12cd1d8 | |||
| 2671665e25 | |||
| 2885f0d74e | |||
| aab9ffa253 |
@ -1,15 +0,0 @@
|
||||
# Docker Environment Configuration
|
||||
NODE_ENV=development
|
||||
|
||||
# Application Configuration
|
||||
APP_PORT=3000
|
||||
|
||||
# Database Configuration
|
||||
DB_HOST=mariadb
|
||||
DB_PORT=3306
|
||||
DB_USERNAME=app_user
|
||||
DB_PASSWORD=app_password
|
||||
DB_DATABASE=low_code_engine
|
||||
DB_ROOT_PASSWORD=rootpassword
|
||||
|
||||
DB_PORT=3309
|
||||
@ -5,6 +5,9 @@ DB_USERNAME=root
|
||||
DB_PASSWORD=your_password_here
|
||||
DB_DATABASE=low_code_engine
|
||||
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
|
||||
# Application Configuration
|
||||
NODE_ENV=development
|
||||
PORT=3054
|
||||
|
||||
53
.gitea/workflows/deploy-testing.yml
Normal file
53
.gitea/workflows/deploy-testing.yml
Normal file
@ -0,0 +1,53 @@
|
||||
name: Deploy to Testing Server
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
name: Deploy to Testing Environment
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Deploy via SSH
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
host: ${{ secrets.SSH_HOST }}
|
||||
username: ${{ secrets.SSH_USERNAME }}
|
||||
key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
port: ${{ secrets.SSH_PORT || 22 }}
|
||||
script: |
|
||||
# Navigate to project directory
|
||||
cd ${{ secrets.PROJECT_PATH }}
|
||||
|
||||
# Pull latest code
|
||||
echo "🔄 Pulling latest code from repository..."
|
||||
git pull origin develop
|
||||
|
||||
# Install dependencies
|
||||
echo "📦 Installing dependencies with yarn..."
|
||||
yarn install --frozen-lockfile
|
||||
|
||||
# Run database migrations
|
||||
echo "🗄️ Running database migrations..."
|
||||
yarn migration:run
|
||||
|
||||
# Build the project
|
||||
echo "🏗️ Building the project..."
|
||||
yarn build
|
||||
|
||||
# Restart PM2 process
|
||||
echo "🔄 Restarting PM2 application..."
|
||||
pm2 restart ${{ secrets.PM2_APP_NAME || 'low-code-engine' }}
|
||||
|
||||
# Show PM2 status
|
||||
echo "✅ Deployment completed! PM2 status:"
|
||||
pm2 status
|
||||
|
||||
echo "🚀 Application deployed successfully!"
|
||||
@ -1,17 +0,0 @@
|
||||
name: Test Runner
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
|
||||
jobs:
|
||||
hello-world:
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
- name: Test multiple commands
|
||||
run: |
|
||||
echo "Step 1 complete ✅"
|
||||
echo "Step 2 complete ✅"
|
||||
echo "All good!"
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "lib/sdk"]
|
||||
path = lib/sdk
|
||||
url = http://192.168.0.16:3000/lborv/few-line-sdk.git
|
||||
64
Dockerfile
64
Dockerfile
@ -1,64 +0,0 @@
|
||||
# Multi-stage build for production optimization
|
||||
FROM node:18-alpine AS development
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json yarn.lock ./
|
||||
|
||||
# Install dependencies
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Development command
|
||||
CMD ["yarn", "start:dev"]
|
||||
|
||||
# Production build stage
|
||||
FROM node:18-alpine AS build
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json yarn.lock ./
|
||||
|
||||
# Install dependencies
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN yarn build
|
||||
|
||||
# Production stage
|
||||
FROM node:18-alpine AS production
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json yarn.lock ./
|
||||
|
||||
# Install only production dependencies
|
||||
RUN yarn install --frozen-lockfile --production && yarn cache clean
|
||||
|
||||
# Copy built application
|
||||
COPY --from=build /usr/src/app/dist ./dist
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs
|
||||
RUN adduser -S nestjs -u 1001
|
||||
|
||||
# Change ownership of the app directory
|
||||
USER nestjs
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Production command
|
||||
CMD ["node", "dist/main"]
|
||||
@ -1,70 +0,0 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
# MariaDB Database
|
||||
mariadb:
|
||||
image: mariadb:10.11
|
||||
container_name: low-code-engine-mariadb
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootpassword}
|
||||
MYSQL_DATABASE: ${DB_DATABASE:-low_code_engine}
|
||||
MYSQL_USER: ${DB_USERNAME:-app_user}
|
||||
MYSQL_PASSWORD: ${DB_PASSWORD:-app_password}
|
||||
ports:
|
||||
- "${DB_PORT:-3306}:3306"
|
||||
volumes:
|
||||
- mariadb_data:/var/lib/mysql
|
||||
- ./docker/mariadb/init:/docker-entrypoint-initdb.d
|
||||
networks:
|
||||
- app-network
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"mysqladmin",
|
||||
"ping",
|
||||
"-h",
|
||||
"localhost",
|
||||
"-u",
|
||||
"root",
|
||||
"-p${DB_ROOT_PASSWORD:-rootpassword}",
|
||||
]
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
interval: 10s
|
||||
|
||||
# NestJS Application
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
target: development
|
||||
container_name: low-code-engine-app
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NODE_ENV: ${NODE_ENV:-development}
|
||||
DB_HOST: mariadb
|
||||
DB_PORT: 3306
|
||||
DB_USERNAME: ${DB_USERNAME:-app_user}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-app_password}
|
||||
DB_DATABASE: ${DB_DATABASE:-low_code_engine}
|
||||
ports:
|
||||
- "${APP_PORT:-3000}:3000"
|
||||
volumes:
|
||||
- .:/usr/src/app
|
||||
- /usr/src/app/node_modules
|
||||
networks:
|
||||
- app-network
|
||||
depends_on:
|
||||
mariadb:
|
||||
condition: service_healthy
|
||||
command: yarn start:dev
|
||||
|
||||
volumes:
|
||||
mariadb_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
app-network:
|
||||
driver: bridge
|
||||
1
lib/sdk
Submodule
1
lib/sdk
Submodule
Submodule lib/sdk added at 4b8c6753ef
37
scripts/setup-docker.sh
Normal file
37
scripts/setup-docker.sh
Normal file
@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "📦 Updating system packages..."
|
||||
apt update -y
|
||||
apt upgrade -y
|
||||
|
||||
echo "🔧 Installing dependencies..."
|
||||
apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
|
||||
|
||||
echo "🔑 Adding Docker’s official GPG key..."
|
||||
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
|
||||
echo "📂 Adding Docker repository..."
|
||||
echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
|
||||
https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
|
||||
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
echo "📦 Installing Docker Engine and Compose..."
|
||||
apt update -y
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
|
||||
echo "⚙️ Enabling and starting Docker service..."
|
||||
systemctl enable docker
|
||||
systemctl start docker
|
||||
|
||||
echo "👤 Adding current user to docker group..."
|
||||
usermod -aG docker $USER
|
||||
|
||||
echo "✅ Docker and Docker Compose installation complete!"
|
||||
echo "➡️ Log out and log back in (or run 'newgrp docker') to use Docker without sudo."
|
||||
echo
|
||||
echo "💡 Docker version:"
|
||||
docker --version || echo "Docker not yet available in current shell"
|
||||
echo "💡 Docker Compose version:"
|
||||
docker compose version || echo "Docker Compose not yet available in current shell"
|
||||
@ -3,6 +3,7 @@ import {
|
||||
Controller,
|
||||
Delete,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
@ -28,8 +29,8 @@ export class ApiController {
|
||||
return this.apiService.generateToken(body.id);
|
||||
}
|
||||
|
||||
@Delete("token/revoke")
|
||||
revokeToken(@Body() body: { token: string }) {
|
||||
return this.apiService.revokeToken(body.token);
|
||||
@Delete("token/revoke/:token")
|
||||
revokeToken(@Param("token") token: string) {
|
||||
return this.apiService.revokeToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
21
src/migrations/1760377023483-projectSettings.ts
Normal file
21
src/migrations/1760377023483-projectSettings.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class ProjectSettings1760377023483 implements MigrationInterface {
|
||||
name = "ProjectSettings1760377023483";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE \`projectSetting\` (\`id\` varchar(36) NOT NULL, \`key\` varchar(255) NOT NULL, \`value\` text NOT NULL, \`projectId\` varchar(36) NULL, PRIMARY KEY (\`id\`)) ENGINE=InnoDB`
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`projectSetting\` ADD CONSTRAINT \`FK_8dfaf9c1ebbadb7af024e72e871\` FOREIGN KEY (\`projectId\`) REFERENCES \`project\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION`
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`projectSetting\` DROP FOREIGN KEY \`FK_8dfaf9c1ebbadb7af024e72e871\``
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE \`projectSetting\``);
|
||||
}
|
||||
}
|
||||
15
src/migrations/1760548310371-projectmeta.ts
Normal file
15
src/migrations/1760548310371-projectmeta.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class Projectmeta1760548310371 implements MigrationInterface {
|
||||
name = "Projectmeta1760548310371";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE \`project\` ADD \`meta\` longtext NULL`
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`project\` DROP COLUMN \`meta\``);
|
||||
}
|
||||
}
|
||||
11
src/project/constants.ts
Normal file
11
src/project/constants.ts
Normal file
@ -0,0 +1,11 @@
|
||||
type item = {
|
||||
id: string;
|
||||
path: string;
|
||||
};
|
||||
|
||||
export type TMeta = {
|
||||
migrations: item[];
|
||||
queries: item[];
|
||||
functions: item[];
|
||||
settings: item[];
|
||||
};
|
||||
@ -14,6 +14,8 @@ import { Database } from "../../databaseManager/entities/database.entity";
|
||||
import { FunctionEntity } from "../../query/entities/function.entity";
|
||||
import { RedisNode } from "../../redisManager/entities/redis.node.entity";
|
||||
import { Log } from "../../query/logger/entities/log.entity";
|
||||
import { ProjectSetting } from "../settings/entities/project.setting.entity";
|
||||
import { TMeta } from "../constants";
|
||||
|
||||
@Entity("project")
|
||||
export class Project {
|
||||
@ -39,6 +41,19 @@ export class Project {
|
||||
@OneToMany(() => FunctionEntity, (functionEntity) => functionEntity.project)
|
||||
functions: FunctionEntity[];
|
||||
|
||||
@OneToMany(() => ProjectSetting, (setting) => setting.project)
|
||||
settings: ProjectSetting[];
|
||||
|
||||
@Column({
|
||||
type: "longtext",
|
||||
nullable: true,
|
||||
transformer: {
|
||||
to: (value: TMeta) => JSON.stringify(value),
|
||||
from: (value: string) => JSON.parse(value) as TMeta,
|
||||
},
|
||||
})
|
||||
meta: TMeta;
|
||||
|
||||
@ManyToMany(() => RedisNode, (redisNode) => redisNode.projects)
|
||||
@JoinTable()
|
||||
redisNodes: RedisNode[];
|
||||
|
||||
@ -1,24 +1,87 @@
|
||||
import { Body, Controller, Inject, Put, UseGuards } from "@nestjs/common";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { ProjectService } from "./project.service";
|
||||
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
|
||||
import { AdminGuard } from "src/api/guards/admin.guard";
|
||||
import { Request } from "express";
|
||||
import { ProjectSettingService } from "./settings/project.setting.service";
|
||||
import { TMeta } from "./constants";
|
||||
|
||||
@Controller("project")
|
||||
@UseGuards(ApiTokenGuard)
|
||||
export class ProjectController {
|
||||
constructor(
|
||||
@Inject(ProjectService)
|
||||
private readonly projectService: ProjectService
|
||||
private readonly projectService: ProjectService,
|
||||
@Inject(ProjectSettingService)
|
||||
private readonly projectSettingService: ProjectSettingService
|
||||
) {}
|
||||
|
||||
@Put("create")
|
||||
@UseGuards(AdminGuard)
|
||||
createProject(@Body() body: { name: string }) {
|
||||
return this.projectService.create(body.name);
|
||||
}
|
||||
|
||||
@Get("details")
|
||||
getProjectDetails(
|
||||
@Req() req: Request & { apiToken: { project: { id: string } } }
|
||||
) {
|
||||
return this.projectService.getProjectDetails(req.apiToken.project.id);
|
||||
}
|
||||
|
||||
@Post("update/meta")
|
||||
updateProjectMeta(
|
||||
@Body() body: { meta: TMeta },
|
||||
@Req() req: Request & { apiToken: { project: { id: string } } }
|
||||
) {
|
||||
return this.projectService.updateMeta(req.apiToken.project.id, body.meta);
|
||||
}
|
||||
|
||||
@Put("create-without-db")
|
||||
@UseGuards(AdminGuard)
|
||||
createProjectWithoutDB(@Body() body: { name: string }) {
|
||||
return this.projectService.create(body.name, false);
|
||||
}
|
||||
|
||||
@Put("settings/create")
|
||||
createSetting(
|
||||
@Body() body: { key: string; value: string },
|
||||
@Req() req: Request & { apiToken: { id: string } }
|
||||
) {
|
||||
return this.projectSettingService.create(
|
||||
req.apiToken.id,
|
||||
body.key,
|
||||
body.value
|
||||
);
|
||||
}
|
||||
|
||||
@Delete("settings/delete/:key")
|
||||
deleteSetting(
|
||||
@Param("key") key: string,
|
||||
@Req() req: Request & { apiToken: { id: string } }
|
||||
) {
|
||||
return this.projectSettingService.delete(req.apiToken.id, key);
|
||||
}
|
||||
|
||||
@Get("settings")
|
||||
getAllSettings(@Req() req: Request & { apiToken: { id: string } }) {
|
||||
return this.projectSettingService.getAll(req.apiToken.id);
|
||||
}
|
||||
|
||||
@Get("api-tokens")
|
||||
@UseGuards(AdminGuard)
|
||||
getAllApiTokens(@Req() req: Request & { apiToken: { id: string } }) {
|
||||
return this.projectService.getAllApiTokens(req.apiToken.id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,16 +6,18 @@ import { ProjectController } from "./project.controller";
|
||||
import { ApiModule } from "src/api/api.module";
|
||||
import { RedisModule } from "src/redis/redis.module";
|
||||
import { DatabaseManagerModule } from "src/databaseManager/database.manager.module";
|
||||
import { ProjectSetting } from "./settings/entities/project.setting.entity";
|
||||
import { ProjectSettingService } from "./settings/project.setting.service";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
forwardRef(() => ApiModule),
|
||||
forwardRef(() => RedisModule),
|
||||
forwardRef(() => DatabaseManagerModule),
|
||||
TypeOrmModule.forFeature([Project]),
|
||||
TypeOrmModule.forFeature([Project, ProjectSetting]),
|
||||
],
|
||||
controllers: [ProjectController],
|
||||
providers: [ProjectService],
|
||||
exports: [ProjectService],
|
||||
providers: [ProjectService, ProjectSettingService],
|
||||
exports: [ProjectService, ProjectSettingService],
|
||||
})
|
||||
export class ProjectModule {}
|
||||
|
||||
@ -4,6 +4,8 @@ import { Repository } from "typeorm";
|
||||
import { Project } from "./entities/project.entity";
|
||||
import { RedisClient } from "src/redis/redis.service";
|
||||
import { DatabaseManagerService } from "src/databaseManager/database/database.manager.service";
|
||||
import { ProjectSettingService } from "./settings/project.setting.service";
|
||||
import { TMeta } from "./constants";
|
||||
|
||||
@Injectable()
|
||||
export class ProjectService {
|
||||
@ -13,9 +15,23 @@ export class ProjectService {
|
||||
@Inject(RedisClient)
|
||||
private readonly redisClient: RedisClient,
|
||||
@Inject(forwardRef(() => DatabaseManagerService))
|
||||
private readonly databaseManagerService: DatabaseManagerService
|
||||
private readonly databaseManagerService: DatabaseManagerService,
|
||||
@Inject(ProjectSettingService)
|
||||
private readonly projectSettingService: ProjectSettingService
|
||||
) {}
|
||||
|
||||
async createDefaultSettings(projectId: string) {
|
||||
const defaultSettings = [{ key: "sessionTTL", value: "3600" }];
|
||||
|
||||
await Promise.all(
|
||||
defaultSettings.map((setting) =>
|
||||
this.projectSettingService.create(projectId, setting.key, setting.value)
|
||||
)
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async create(name: string, createDatabase: boolean = true) {
|
||||
const project = this.projectRepository.create({ name });
|
||||
const projectSaved = await this.projectRepository.save(project);
|
||||
@ -24,9 +40,39 @@ export class ProjectService {
|
||||
await this.databaseManagerService.createDatabase(projectSaved.id);
|
||||
}
|
||||
|
||||
await this.createDefaultSettings(projectSaved.id);
|
||||
await this.redisClient.set(`project_${projectSaved.id}`, projectSaved, 300);
|
||||
|
||||
return projectSaved;
|
||||
}
|
||||
|
||||
async updateMeta(projectId: string, meta: TMeta) {
|
||||
return this.projectRepository.update({ id: projectId }, { meta });
|
||||
}
|
||||
|
||||
async getProjectDetails(projectId: string) {
|
||||
const project = await this.projectRepository.findOne({
|
||||
where: { id: projectId },
|
||||
relations: [
|
||||
"database",
|
||||
"database.migrations",
|
||||
"queries",
|
||||
"functions",
|
||||
"settings",
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
migrations: project?.database?.migrations || [],
|
||||
queries: project?.queries || [],
|
||||
functions: project?.functions || [],
|
||||
settings: project?.settings || [],
|
||||
meta: project?.meta || null,
|
||||
name: project?.name || "",
|
||||
id: project?.id || "",
|
||||
};
|
||||
}
|
||||
|
||||
async findById(id: string) {
|
||||
const cached = await this.redisClient.get(`project_${id}`);
|
||||
|
||||
@ -58,4 +104,21 @@ export class ProjectService {
|
||||
redisNodes: redisNodeId,
|
||||
});
|
||||
}
|
||||
|
||||
async getAllApiTokens(projectId: string) {
|
||||
const project = await this.projectRepository.findOne({
|
||||
where: { id: projectId },
|
||||
relations: ["apiTokens"],
|
||||
});
|
||||
return project?.apiTokens || [];
|
||||
}
|
||||
|
||||
async getProjectInfo(projectId: string) {
|
||||
const project = await this.projectRepository.findOne({
|
||||
where: { id: projectId },
|
||||
relations: ["queries", "apiTokens", "functions", "settings"],
|
||||
});
|
||||
|
||||
return project;
|
||||
}
|
||||
}
|
||||
|
||||
19
src/project/settings/entities/project.setting.entity.ts
Normal file
19
src/project/settings/entities/project.setting.entity.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { Project } from "../../entities/project.entity";
|
||||
|
||||
@Entity("projectSetting")
|
||||
export class ProjectSetting {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id: string;
|
||||
|
||||
@ManyToOne(() => Project, (project) => project.settings, {
|
||||
onDelete: "CASCADE",
|
||||
})
|
||||
project: Project;
|
||||
|
||||
@Column({ type: "varchar", length: 255, nullable: false })
|
||||
key: string;
|
||||
|
||||
@Column({ type: "text", nullable: false })
|
||||
value: string;
|
||||
}
|
||||
112
src/project/settings/project.setting.service.ts
Normal file
112
src/project/settings/project.setting.service.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import { ProjectSetting } from "./entities/project.setting.entity";
|
||||
import { RedisClient } from "src/redis/redis.service";
|
||||
|
||||
@Injectable()
|
||||
export class ProjectSettingService {
|
||||
constructor(
|
||||
@InjectRepository(ProjectSetting)
|
||||
private readonly projectSettingRepository: Repository<ProjectSetting>,
|
||||
@Inject(RedisClient)
|
||||
private readonly redisClient: RedisClient
|
||||
) {}
|
||||
|
||||
async updateCache(projectId: string) {
|
||||
const settings = await this.projectSettingRepository.find({
|
||||
where: { project: { id: projectId } },
|
||||
});
|
||||
|
||||
const settingsObject = settings.reduce((obj, setting) => {
|
||||
obj[setting.key] = setting.value;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
await this.redisClient.set(
|
||||
`project_settings_${projectId}`,
|
||||
settingsObject,
|
||||
300
|
||||
);
|
||||
|
||||
return settingsObject;
|
||||
}
|
||||
|
||||
async get(key: string, projectId: string) {
|
||||
const cached = await this.redisClient.get(`project_settings_${projectId}`);
|
||||
|
||||
if (cached && key in cached) {
|
||||
return cached[key];
|
||||
}
|
||||
|
||||
const setting = await this.projectSettingRepository.findOne({
|
||||
where: { project: { id: projectId }, key },
|
||||
});
|
||||
|
||||
if (setting) {
|
||||
return setting.value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async getAll(projectId: string) {
|
||||
const cached = await this.redisClient.get(`project_settings_${projectId}`);
|
||||
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const settings = await this.projectSettingRepository.find({
|
||||
where: { project: { id: projectId } },
|
||||
});
|
||||
|
||||
const settingsObject = settings.reduce((obj, setting) => {
|
||||
obj[setting.key] = setting.value;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
await this.redisClient.set(
|
||||
`project_settings_${projectId}`,
|
||||
settingsObject,
|
||||
300
|
||||
);
|
||||
|
||||
return settingsObject;
|
||||
}
|
||||
|
||||
async create(projectId: string, key: string, value: string) {
|
||||
const existingSetting = await this.projectSettingRepository.findOne({
|
||||
where: { project: { id: projectId }, key },
|
||||
});
|
||||
|
||||
if (existingSetting) {
|
||||
existingSetting.value = value;
|
||||
await this.projectSettingRepository.save(existingSetting);
|
||||
|
||||
return await this.updateCache(projectId);
|
||||
}
|
||||
|
||||
const newSetting = this.projectSettingRepository.create({
|
||||
key,
|
||||
value,
|
||||
project: { id: projectId },
|
||||
});
|
||||
|
||||
await this.projectSettingRepository.save(newSetting);
|
||||
|
||||
return await this.updateCache(projectId);
|
||||
}
|
||||
|
||||
async delete(projectId: string, key: string) {
|
||||
const setting = await this.projectSettingRepository.findOne({
|
||||
where: { project: { id: projectId }, key },
|
||||
});
|
||||
|
||||
if (setting) {
|
||||
await this.projectSettingRepository.remove(setting);
|
||||
}
|
||||
|
||||
return await this.updateCache(projectId);
|
||||
}
|
||||
}
|
||||
@ -18,8 +18,9 @@ import { LoggerService } from "../logger/logger.service";
|
||||
import { TLogType } from "../logger/logger.types";
|
||||
import { QueryResponse } from "src/vm/vm.constants";
|
||||
import { Query } from "../entities/query.entity";
|
||||
import { Token } from "src/api/entities/token.entity";
|
||||
import { QueryPublicGuard } from "../guards/query-public";
|
||||
|
||||
@UseGuards(ApiTokenGuard)
|
||||
export abstract class BaseQueryController {
|
||||
constructor(
|
||||
@Inject(QueryHandlerService)
|
||||
@ -33,30 +34,42 @@ export abstract class BaseQueryController {
|
||||
protected abstract getIsCommand(): boolean;
|
||||
|
||||
@Post("create")
|
||||
@UseGuards(ApiTokenGuard)
|
||||
async createQuery(
|
||||
@Body() queryData: { projectToken: string; source: string }
|
||||
@Req() req: Request & { apiToken: Token },
|
||||
@Body()
|
||||
queryData: { source: string; isTypescript?: number; isPublic?: number }
|
||||
) {
|
||||
return this.queryHandlerService.createQuery(queryData, this.getIsCommand());
|
||||
return this.queryHandlerService.createQuery(
|
||||
{
|
||||
...queryData,
|
||||
projectToken: req?.apiToken.project.id,
|
||||
},
|
||||
this.getIsCommand()
|
||||
);
|
||||
}
|
||||
|
||||
@Post("update/:id")
|
||||
@UseGuards(QueryGuard)
|
||||
@UseGuards(ApiTokenGuard, QueryGuard)
|
||||
async updateQuery(
|
||||
@Body() updateData: Partial<{ source: string }>,
|
||||
@Body()
|
||||
updateData: Partial<{
|
||||
source: string;
|
||||
isTypescript?: number;
|
||||
isPublic?: number;
|
||||
}>,
|
||||
@Param("id") id: string
|
||||
) {
|
||||
return this.queryHandlerService.updateQuery(id, updateData);
|
||||
}
|
||||
|
||||
@Post("/run/:id")
|
||||
@UseGuards(QueryGuard)
|
||||
async runQuery(
|
||||
@Param("id") id: string,
|
||||
@Body() query: Record<string, any>,
|
||||
@Headers() headers: Record<string, any>,
|
||||
@Res() res: Response,
|
||||
@Req() req: Request & { query: Query }
|
||||
) {
|
||||
private async run(
|
||||
id: string,
|
||||
query: Record<string, any>,
|
||||
headers: Record<string, any>,
|
||||
res: Response,
|
||||
req: Request & { query: Query }
|
||||
): Promise<QueryResponse> {
|
||||
let queryResult: QueryResponse;
|
||||
const loggerTraceId =
|
||||
headers["x-trace-id"] || LoggerService.generateTraceId();
|
||||
@ -135,8 +148,32 @@ export abstract class BaseQueryController {
|
||||
res.send(queryResult?.response || null);
|
||||
}
|
||||
|
||||
@Post("/run-public/:id")
|
||||
@UseGuards(QueryPublicGuard)
|
||||
async runPublicQuery(
|
||||
@Param("id") id: string,
|
||||
@Body() query: Record<string, any>,
|
||||
@Headers() headers: Record<string, any>,
|
||||
@Res() res: Response,
|
||||
@Req() req: Request & { query: Query }
|
||||
) {
|
||||
return this.run(id, query, headers, res, req);
|
||||
}
|
||||
|
||||
@Post("/run/:id")
|
||||
@UseGuards(ApiTokenGuard, QueryGuard)
|
||||
async runQuery(
|
||||
@Param("id") id: string,
|
||||
@Body() query: Record<string, any>,
|
||||
@Headers() headers: Record<string, any>,
|
||||
@Res() res: Response,
|
||||
@Req() req: Request & { query: Query }
|
||||
) {
|
||||
return this.run(id, query, headers, res, req);
|
||||
}
|
||||
|
||||
@Delete("/delete/:id")
|
||||
@UseGuards(QueryGuard)
|
||||
@UseGuards(ApiTokenGuard, QueryGuard)
|
||||
async deleteQuery(@Param("id") id: string) {
|
||||
return this.queryHandlerService.deleteQuery(id);
|
||||
}
|
||||
|
||||
@ -27,4 +27,10 @@ export class Query {
|
||||
|
||||
@Column({ type: "tinyint", default: 0 })
|
||||
isCommand: number;
|
||||
|
||||
@Column({ type: "tinyint", default: 0 })
|
||||
isTypescript: number;
|
||||
|
||||
@Column({ type: "tinyint", default: 0 })
|
||||
isPublic: number;
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ import { FunctionService } from "src/query/function/function.service";
|
||||
import { SessionService } from "../session/session.service";
|
||||
import { TLog, TLogType } from "../logger/logger.types";
|
||||
import { LoggerService } from "../logger/logger.service";
|
||||
import { ProjectSettingService } from "src/project/settings/project.setting.service";
|
||||
import ts from "typescript";
|
||||
|
||||
@Injectable()
|
||||
export class QueryExecuterService {
|
||||
@ -31,6 +33,7 @@ export class QueryExecuterService {
|
||||
readonly databaseManagerService: DatabaseManagerService,
|
||||
readonly redisNodeService: RedisNodeService,
|
||||
readonly sessionService: SessionService,
|
||||
readonly projectSettingService: ProjectSettingService,
|
||||
@InjectQueue(QUEUE_NAMES.QUERY) private queryQueue: Queue
|
||||
) {
|
||||
this.queueEvents = new QueueEvents(this.queryQueue.name);
|
||||
@ -87,6 +90,14 @@ export class QueryExecuterService {
|
||||
return result;
|
||||
}
|
||||
|
||||
private compileTypeScript(tsCode: string) {
|
||||
const jsCode = ts.transpileModule(tsCode, {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS },
|
||||
}).outputText;
|
||||
|
||||
return jsCode;
|
||||
}
|
||||
|
||||
async runQuery(
|
||||
token: string,
|
||||
queryData: any,
|
||||
@ -120,17 +131,19 @@ export class QueryExecuterService {
|
||||
type: TLogType.info,
|
||||
});
|
||||
|
||||
let script = this.clearImports(query.source);
|
||||
|
||||
if (query.isTypescript) {
|
||||
script = this.compileTypeScript(script);
|
||||
}
|
||||
|
||||
const vm = await this.createVm(
|
||||
query,
|
||||
log,
|
||||
callStack,
|
||||
cookies["x-session-id"]
|
||||
);
|
||||
const result = await vm.runScript(
|
||||
this.clearImports(query.source),
|
||||
queryData,
|
||||
headers
|
||||
);
|
||||
const result = await vm.runScript(script, queryData, headers);
|
||||
|
||||
if (!this.checkResponse(result)) {
|
||||
throw new Error(`Error initializing VM: ${JSON.stringify(result)}`);
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
import { Controller, Inject, Post, UseGuards } from "@nestjs/common";
|
||||
import {
|
||||
Controller,
|
||||
Delete,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
|
||||
import { FunctionService } from "./function.service";
|
||||
import { Token } from "src/api/entities/token.entity";
|
||||
|
||||
@Controller("functions")
|
||||
@UseGuards(ApiTokenGuard)
|
||||
@ -11,12 +20,19 @@ export class FunctionController {
|
||||
) {}
|
||||
|
||||
@Post("create")
|
||||
async createFunction(projectId: string, name: string, source: string) {
|
||||
return this.functionService.create(projectId, name, source);
|
||||
async createFunction(
|
||||
@Req() req: Request & { apiToken: Token },
|
||||
name: string,
|
||||
source: string
|
||||
) {
|
||||
return this.functionService.create(req.apiToken.project.id, name, source);
|
||||
}
|
||||
|
||||
@Post("delete")
|
||||
async deleteFunction(projectId: string, name: string) {
|
||||
return this.functionService.deleteFunction(projectId, name);
|
||||
@Delete("delete/:name")
|
||||
async deleteFunction(
|
||||
@Req() req: Request & { apiToken: Token },
|
||||
@Param("name") name: string
|
||||
) {
|
||||
return this.functionService.deleteFunction(req.apiToken.project.id, name);
|
||||
}
|
||||
}
|
||||
|
||||
44
src/query/guards/query-public.ts
Normal file
44
src/query/guards/query-public.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Inject,
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
} from "@nestjs/common";
|
||||
import { QueryHandlerService } from "src/query/handler/query.handler.service";
|
||||
|
||||
@Injectable()
|
||||
export class QueryPublicGuard implements CanActivate {
|
||||
constructor(
|
||||
@Inject(QueryHandlerService)
|
||||
private readonly queryHandlerService: QueryHandlerService
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
|
||||
const queryId = request.params?.id;
|
||||
|
||||
if (!queryId) {
|
||||
throw new UnauthorizedException("Query ID is required");
|
||||
}
|
||||
|
||||
const query = await this.queryHandlerService.getQueryById(queryId);
|
||||
|
||||
if (!query) {
|
||||
throw new UnauthorizedException("Query not found");
|
||||
}
|
||||
|
||||
if (!query.isActive) {
|
||||
throw new UnauthorizedException("Query is inactive");
|
||||
}
|
||||
|
||||
if (query.isPublic !== 1) {
|
||||
throw new UnauthorizedException("Query is not public");
|
||||
}
|
||||
|
||||
request.query = query;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,6 @@ export class QueryHandlerService {
|
||||
delete queryData.projectToken;
|
||||
|
||||
const query = this.queryRepository.create(queryData);
|
||||
|
||||
await this.queryRepository.save(query);
|
||||
|
||||
return query;
|
||||
@ -64,9 +63,9 @@ export class QueryHandlerService {
|
||||
}
|
||||
|
||||
await this.redisClient.del(`query_${id}`);
|
||||
|
||||
Object.assign(query, updateData);
|
||||
return this.queryRepository.save(query);
|
||||
|
||||
return await this.queryRepository.save(query);
|
||||
}
|
||||
|
||||
async deleteQuery(id: string) {
|
||||
|
||||
@ -5,11 +5,14 @@ import {
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { LoggerService } from "./logger.service";
|
||||
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
|
||||
import { QueryGuard } from "../guards/query.guard";
|
||||
import { Token } from "src/api/entities/token.entity";
|
||||
import { Query } from "../entities/query.entity";
|
||||
|
||||
@Controller("logger")
|
||||
@UseGuards(ApiTokenGuard)
|
||||
@ -19,14 +22,17 @@ export class LoggerController {
|
||||
private readonly loggerService: LoggerService
|
||||
) {}
|
||||
|
||||
@Get("/:id/:traceId")
|
||||
getByTraceId(@Param("traceId") traceId: string) {
|
||||
return this.loggerService.findByTraceId(traceId);
|
||||
@Get("/:traceId")
|
||||
getByTraceId(
|
||||
@Req() req: Request & { apiToken: Token },
|
||||
@Param("traceId") traceId: string
|
||||
) {
|
||||
return this.loggerService.findByTraceId(req.apiToken.project.id, traceId);
|
||||
}
|
||||
|
||||
@Post("/:id/findAll")
|
||||
@Post("/findAll")
|
||||
findAll(
|
||||
@Param("id") projectId: string,
|
||||
@Req() req: Request & { apiToken: Token },
|
||||
@Body()
|
||||
body: {
|
||||
traceId?: string;
|
||||
@ -37,13 +43,13 @@ export class LoggerController {
|
||||
offset: number;
|
||||
}
|
||||
) {
|
||||
return this.loggerService.findByProjectId(projectId, body);
|
||||
return this.loggerService.findByProjectId(req.apiToken.project.id, body);
|
||||
}
|
||||
|
||||
@Post("/:id/find")
|
||||
@Post("/find")
|
||||
@UseGuards(QueryGuard)
|
||||
find(
|
||||
@Param("id") _id: string,
|
||||
@Req() req: Request & { query: Query },
|
||||
@Body()
|
||||
body: {
|
||||
traceId?: string;
|
||||
@ -54,6 +60,6 @@ export class LoggerController {
|
||||
offset: number;
|
||||
}
|
||||
) {
|
||||
return this.loggerService.find(_id, body);
|
||||
return this.loggerService.find(req.query.id, body);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,8 +22,15 @@ export class LoggerService {
|
||||
return await this.logRepository.save(log);
|
||||
}
|
||||
|
||||
async findByTraceId(traceId: string): Promise<Log[]> {
|
||||
return await this.logRepository.find({ where: { traceId } });
|
||||
async findByTraceId(projectId: string, traceId: string): Promise<Log[]> {
|
||||
return await this.logRepository.find({
|
||||
where: {
|
||||
traceId,
|
||||
project: {
|
||||
id: projectId,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private prepareQuery(data: {
|
||||
|
||||
@ -18,6 +18,8 @@ import { SessionService } from "./session/session.service";
|
||||
import { Log } from "./logger/entities/log.entity";
|
||||
import { LoggerService } from "./logger/logger.service";
|
||||
import { LoggerController } from "./logger/logger.controller";
|
||||
import { ProjectSettingService } from "src/project/settings/project.setting.service";
|
||||
import { ProjectSetting } from "src/project/settings/entities/project.setting.entity";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -27,7 +29,7 @@ import { LoggerController } from "./logger/logger.controller";
|
||||
forwardRef(() => QueueModule),
|
||||
forwardRef(() => RedisModule),
|
||||
forwardRef(() => RedisManagerModule),
|
||||
TypeOrmModule.forFeature([Query, FunctionEntity, Log]),
|
||||
TypeOrmModule.forFeature([Query, FunctionEntity, Log, ProjectSetting]),
|
||||
],
|
||||
controllers: [
|
||||
QueryController,
|
||||
@ -41,12 +43,14 @@ import { LoggerController } from "./logger/logger.controller";
|
||||
LoggerService,
|
||||
QueryHandlerService,
|
||||
FunctionService,
|
||||
ProjectSettingService,
|
||||
],
|
||||
exports: [
|
||||
QueryExecuterService,
|
||||
TypeOrmModule,
|
||||
QueryHandlerService,
|
||||
LoggerService,
|
||||
ProjectSettingService,
|
||||
],
|
||||
})
|
||||
export class QueryModule {}
|
||||
|
||||
59
src/query/session/session.controller.ts
Normal file
59
src/query/session/session.controller.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Inject,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
Req,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { ApiTokenGuard } from "src/api/guards/api-token.guard";
|
||||
import { SessionService } from "./session.service";
|
||||
|
||||
@Controller("session")
|
||||
@UseGuards(ApiTokenGuard)
|
||||
export class SessionController {
|
||||
constructor(
|
||||
@Inject(SessionService)
|
||||
private readonly sessionService: SessionService
|
||||
) {}
|
||||
|
||||
@Get("get/:sessionId")
|
||||
getSession(
|
||||
@Param("sessionId") sessionId: string,
|
||||
@Req() req: Request & { apiToken: { project: { id: string } } }
|
||||
) {
|
||||
return this.sessionService.get(sessionId, req.apiToken.project.id);
|
||||
}
|
||||
|
||||
@Delete("delete/:sessionId")
|
||||
deleteSession(
|
||||
@Param("sessionId") sessionId: string,
|
||||
@Req() req: Request & { apiToken: { project: { id: string } } }
|
||||
) {
|
||||
return this.sessionService.delete(sessionId, req.apiToken.project.id);
|
||||
}
|
||||
|
||||
@Post("create")
|
||||
createSession(
|
||||
@Req() req: Request & { apiToken: { project: { id: string } } }
|
||||
) {
|
||||
return this.sessionService.create(req.apiToken.project.id);
|
||||
}
|
||||
|
||||
@Put("set/:sessionId")
|
||||
setSession(
|
||||
@Param("sessionId") sessionId: string,
|
||||
@Req() req: Request & { apiToken: { project: { id: string } } },
|
||||
@Body() body: { data: any }
|
||||
) {
|
||||
return this.sessionService.set(
|
||||
sessionId,
|
||||
req.apiToken.project.id,
|
||||
body.data
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,15 @@
|
||||
import { Inject, Injectable } from "@nestjs/common";
|
||||
import { ProjectSettingService } from "src/project/settings/project.setting.service";
|
||||
import { RedisClient } from "src/redis/redis.service";
|
||||
|
||||
@Injectable()
|
||||
export class SessionService {
|
||||
constructor(@Inject(RedisClient) private readonly redisClient: RedisClient) {}
|
||||
constructor(
|
||||
@Inject(RedisClient)
|
||||
private readonly redisClient: RedisClient,
|
||||
@Inject(ProjectSettingService)
|
||||
private readonly projectSettingService: ProjectSettingService
|
||||
) {}
|
||||
|
||||
private generateSessionId(length: number = 16): string {
|
||||
const characters =
|
||||
@ -16,6 +22,11 @@ export class SessionService {
|
||||
return `${result}_${new Date().getTime()}`;
|
||||
}
|
||||
|
||||
async getSessionTTL(projectId: string): Promise<number> {
|
||||
const ttl = await this.projectSettingService.get("sessionTTL", projectId);
|
||||
return ttl ? parseInt(ttl) : 3600;
|
||||
}
|
||||
|
||||
async create(prefix: string): Promise<{ sessionId: string }> {
|
||||
const sessionId = this.generateSessionId();
|
||||
await this.set(sessionId, prefix, {
|
||||
@ -33,7 +44,11 @@ export class SessionService {
|
||||
const data = await this.redisClient.get(`${prefix}:${sessionId}`);
|
||||
|
||||
if (data) {
|
||||
await this.redisClient.set(`${prefix}:${sessionId}`, data, 3600);
|
||||
await this.redisClient.set(
|
||||
`${prefix}:${sessionId}`,
|
||||
data,
|
||||
await this.getSessionTTL(prefix)
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -41,7 +56,22 @@ export class SessionService {
|
||||
}
|
||||
|
||||
async set(sessionId: string, prefix: string, data: any): Promise<void> {
|
||||
await this.redisClient.set(`${prefix}:${sessionId}`, data, 3600);
|
||||
const existingSession = await this.redisClient.get(
|
||||
`${prefix}:${sessionId}`
|
||||
);
|
||||
|
||||
if (!existingSession) {
|
||||
throw new Error("Session not found");
|
||||
}
|
||||
|
||||
await this.redisClient.set(
|
||||
`${prefix}:${sessionId}`,
|
||||
{
|
||||
...existingSession,
|
||||
...data,
|
||||
},
|
||||
await this.getSessionTTL(prefix)
|
||||
);
|
||||
}
|
||||
|
||||
async delete(sessionId: string, prefix: string): Promise<void> {
|
||||
|
||||
29
src/vm/plugins/settings.plugin.ts
Normal file
29
src/vm/plugins/settings.plugin.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Query } from "src/query/entities/query.entity";
|
||||
import { Plugin } from "../plugin.class";
|
||||
import { QueryExecuterService } from "src/query/executer/query.executer.service";
|
||||
|
||||
export class SettingsPlugin extends Plugin {
|
||||
constructor(
|
||||
name: string,
|
||||
private query: Query,
|
||||
private queryExecuterService: QueryExecuterService
|
||||
) {
|
||||
super(name, ["get"]);
|
||||
}
|
||||
|
||||
static async init(query: Query, queryExecuterService: QueryExecuterService) {
|
||||
return new SettingsPlugin("settings", query, queryExecuterService);
|
||||
}
|
||||
|
||||
async get(property: string): Promise<any> {
|
||||
const settings =
|
||||
await this.queryExecuterService.projectSettingService.getAll(
|
||||
this.query.project.id
|
||||
);
|
||||
return settings[property];
|
||||
}
|
||||
|
||||
onFinish() {
|
||||
// No resources to clean up
|
||||
}
|
||||
}
|
||||
@ -172,16 +172,20 @@ export class Vm {
|
||||
`;
|
||||
|
||||
const compiledScript = await this.isolate.compileScript(scriptWithResult);
|
||||
let timer = 0n;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (
|
||||
this.isolate.cpuTime > this.cpuTimeLimit ||
|
||||
this.isolate.wallTime > this.timeLimit
|
||||
this.isolate.wallTime > this.timeLimit ||
|
||||
timer > this.timeLimit
|
||||
) {
|
||||
this.isolate.dispose();
|
||||
|
||||
rejectPromise(new Error("Script execution timed out"));
|
||||
}
|
||||
|
||||
timer += 500000n;
|
||||
}, 500);
|
||||
|
||||
compiledScript.run(this.context);
|
||||
|
||||
@ -6,6 +6,7 @@ import { AxiosPlugin } from "./plugins/axios.plugin";
|
||||
import { RedisPlugin } from "./plugins/redis.plugin";
|
||||
import { SessionPlugin } from "./plugins/session.plugin";
|
||||
import { TLog } from "src/query/logger/logger.types";
|
||||
import { SettingsPlugin } from "./plugins/settings.plugin";
|
||||
|
||||
export const registeredPlugins = {
|
||||
db: async (service: QueryExecuterService, query: Query) => {
|
||||
@ -32,6 +33,9 @@ export const registeredPlugins = {
|
||||
|
||||
return RedisPlugin.init("redis", redisConnection, query.project.id);
|
||||
},
|
||||
settings: async (service: QueryExecuterService, query: Query) => {
|
||||
return SettingsPlugin.init(query, service);
|
||||
},
|
||||
session: async (
|
||||
service: QueryExecuterService,
|
||||
query: Query,
|
||||
|
||||
Reference in New Issue
Block a user