feat: implement Database Manager module with encryption, CRUD operations, and migration management

This commit is contained in:
lborv
2025-09-27 18:06:50 +03:00
parent 2f848137ed
commit 0d5b2830ed
24 changed files with 541 additions and 303 deletions

View File

@ -7,3 +7,6 @@ DB_DATABASE=low_code_engine
# Application Configuration # Application Configuration
NODE_ENV=development NODE_ENV=development
PORT=3054
ENCRYPTION_KEY=12345678901234567890123456789012
IV_LENGTH=16

View File

@ -37,6 +37,7 @@
"@nestjs/typeorm": "^11.0.0", "@nestjs/typeorm": "^11.0.0",
"@types/ioredis": "^5.0.0", "@types/ioredis": "^5.0.0",
"axios": "^1.12.2", "axios": "^1.12.2",
"crypto": "^1.0.1",
"ioredis": "^5.7.0", "ioredis": "^5.7.0",
"isolated-vm": "^6.0.1", "isolated-vm": "^6.0.1",
"mariadb": "^3.4.5", "mariadb": "^3.4.5",

View File

@ -9,8 +9,8 @@ export class ApiController {
) {} ) {}
@Post("token/generate") @Post("token/generate")
generateToken(@Body() body: { token: string }) { generateToken(@Body() body: { id: string }) {
return this.apiService.generateToken(body.token); return this.apiService.generateToken(body.id);
} }
@Delete("token/revoke") @Delete("token/revoke")

View File

@ -14,9 +14,9 @@ export class ApiService {
private readonly projectRepository: Repository<Project> private readonly projectRepository: Repository<Project>
) {} ) {}
async generateToken(projectToken: string) { async generateToken(projectId: string) {
const project = await this.projectRepository.findOne({ const project = await this.projectRepository.findOne({
where: { token: projectToken }, where: { id: projectId },
}); });
if (!project) { if (!project) {

View File

@ -5,17 +5,17 @@ import { QueryModule } from "src/query/query.module";
import { ProjectModule } from "src/project/project.module"; import { ProjectModule } from "src/project/project.module";
import { RedisModule } from "src/redis/redis.module"; import { RedisModule } from "src/redis/redis.module";
import { DatabaseModule } from "src/database/database.module"; import { DatabaseModule } from "src/database/database.module";
import { MigrationModule } from "src/migration/migration.module"; import { DatabaseManagerModule } from "src/databaseManager/database/database.manager.module";
@Module({ @Module({
imports: [ imports: [
DatabaseModule, DatabaseModule,
RedisModule, RedisModule,
DatabaseManagerModule,
ApiModule, ApiModule,
ProjectModule, ProjectModule,
QueryModule, QueryModule,
TestModule, TestModule,
MigrationModule,
], ],
controllers: [], controllers: [],
providers: [], providers: [],

View File

@ -0,0 +1,31 @@
import * as crypto from "crypto";
export class DatabaseEncryptionService {
protected encryptPassword(password: string): string {
const iv = crypto.randomBytes(
process.env.IV_LENGTH ? parseInt(process.env.IV_LENGTH) : 16
);
const cipher = crypto.createCipheriv(
"aes-256-cbc",
process.env.ENCRYPTION_KEY,
iv
);
let encrypted = cipher.update(password, "utf8", "base64");
encrypted += cipher.final("base64");
return iv.toString("base64") + ":" + encrypted;
}
protected decryptPassword(encrypted: string): string {
const parts = encrypted.split(":");
const iv = Buffer.from(parts[0], "base64");
const encryptedText = parts[1];
const decipher = crypto.createDecipheriv(
"aes-256-cbc",
process.env.ENCRYPTION_KEY,
iv
);
let decrypted = decipher.update(encryptedText, "base64", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
}
}

View File

@ -0,0 +1,74 @@
import { Controller, Get, Inject, Post } from "@nestjs/common";
import { DatabaseManagerService } from "./database.manager.service";
import { DatabaseNodeService } from "../databaseNode/database.node.service";
import { MigrationService } from "../migration/migration.service";
@Controller("database")
export class DatabaseManagerController {
constructor(
@Inject("DatabaseService")
private readonly databaseManagerService: DatabaseManagerService,
@Inject("DatabaseNodeService")
private readonly databaseNodeService: DatabaseNodeService,
@Inject("MigrationService")
private readonly migrationService: MigrationService
) {}
@Post("create")
createDatabase(
@Inject("body") body: { projectId: string; databaseNodeId: string }
) {
return this.databaseManagerService.createDatabase(
body.projectId,
body.databaseNodeId
);
}
@Post("node/create")
addDatabaseNode(
@Inject("body")
body: {
host: string;
port: number;
username: string;
password: string;
}
) {
return this.databaseNodeService.create(
body.host,
body.port,
body.username,
body.password
);
}
@Get("migration/up/:databaseId")
migrateUp(@Inject("params") params: { databaseId: string }) {
return this.migrationService.up(params.databaseId);
}
@Get("migration/down/:databaseId")
migrateDown(@Inject("params") params: { databaseId: string }) {
return this.migrationService.down(params.databaseId);
}
@Post("migration/create")
createMigration(
@Inject("body")
body: {
up: string;
down: string;
databaseId: string;
}
) {
return this.migrationService.create(body.up, body.down, body.databaseId);
}
@Post("query/:databaseId")
runQuery(
@Inject("params") params: { databaseId: string },
@Inject("body") body: { query: string }
) {
return this.databaseManagerService.runQuery(params.databaseId, body.query);
}
}

View File

@ -0,0 +1,22 @@
import { forwardRef, Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Database } from "../entities/database.entity";
import { MigrationModule } from "../migration/migration.module";
import { ProjectModule } from "src/project/project.module";
import { DatabaseManagerController } from "./database.manager.controller";
import { DatabaseManagerService } from "./database.manager.service";
import { DatabaseNode } from "../entities/database.node.entity";
import { Project } from "src/project/entities/project.entity";
import { DatabaseNodeService } from "../databaseNode/database.node.service";
@Module({
imports: [
forwardRef(() => ProjectModule),
MigrationModule,
TypeOrmModule.forFeature([Database, DatabaseNode, Project]),
],
controllers: [DatabaseManagerController],
providers: [DatabaseManagerService, DatabaseNodeService],
exports: [DatabaseManagerService, DatabaseNodeService],
})
export class DatabaseManagerModule {}

View File

@ -0,0 +1,95 @@
import { Inject, Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Database } from "../entities/database.entity";
import { Repository } from "typeorm";
import { ProjectService } from "src/project/project.service";
import { DatabaseEncryptionService } from "../database.encryption.service";
import { DatabaseNodeService } from "../databaseNode/database.node.service";
import * as mysql from "mysql2/promise";
@Injectable()
export class DatabaseManagerService extends DatabaseEncryptionService {
constructor(
@InjectRepository(Database)
private databaseRepository: Repository<Database>,
@Inject(ProjectService)
private readonly projectService: ProjectService,
@Inject(DatabaseNodeService)
private readonly databaseNodeService: DatabaseNodeService
) {
super();
}
async findById(id: string): Promise<Database | null> {
return this.databaseRepository.findOne({ where: { id } });
}
async runQuery(databaseId: string, query: string) {
const database = await this.findById(databaseId);
if (!database) {
throw new Error("Database not found");
}
const dbConnection = await mysql.createConnection({
host: database.node.host,
port: database.node.port,
user: database.c_username,
password: this.decryptPassword(database.password),
database: database.database,
enableKeepAlive: true,
});
try {
const [results] = await dbConnection.execute(query);
return results;
} finally {
await dbConnection.end();
}
}
async createDatabase(
databaseNodeId: string,
projectId: string
): Promise<Database> {
const node = await this.databaseNodeService.findById(databaseNodeId);
if (!node) {
throw new Error("Database node not found");
}
const project = await this.projectService.findById(projectId);
if (!project) {
throw new Error("Project not found");
}
const c_username = `c_user_${Math.random().toString(36).substring(2, 8)}`;
const q_username = `q_user_${Math.random().toString(36).substring(2, 8)}`;
const databaseName = `db_${Math.random().toString(36).substring(2, 8)}`;
const password = this.encryptPassword(
Math.random().toString(36).substring(2, 10)
);
await this.databaseNodeService.initDatabase(
{
database: databaseName,
c_username,
q_username,
password: this.decryptPassword(password),
},
databaseNodeId
);
const database = this.databaseRepository.create({
q_username,
c_username,
database: databaseName,
password,
node,
project,
});
return await this.databaseRepository.save(database);
}
}

View File

@ -0,0 +1,88 @@
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DatabaseNode } from "../entities/database.node.entity";
import { Repository } from "typeorm";
import { DatabaseEncryptionService } from "../database.encryption.service";
import * as mysql from "mysql2/promise";
@Injectable()
export class DatabaseNodeService extends DatabaseEncryptionService {
constructor(
@InjectRepository(DatabaseNode)
private databaseNodeRepository: Repository<DatabaseNode>
) {
super();
}
async findById(id: string): Promise<DatabaseNode | null> {
return this.databaseNodeRepository.findOne({ where: { id } });
}
async initDatabase(
data: {
database: string;
c_username: string;
q_username: string;
password: string;
},
databaseNodeId: string
): Promise<void> {
try {
const dbConnection = await mysql.createConnection({
...(await this.getConnectionOptions(databaseNodeId)),
enableKeepAlive: true,
});
await dbConnection.execute(`CREATE DATABASE \`${data.database}\`;`);
await dbConnection.execute(
`CREATE USER '${data.c_username}'@'%' IDENTIFIED BY '${data.password}';`
);
await dbConnection.execute(
`CREATE USER '${data.q_username}'@'%' IDENTIFIED BY '${data.password}';`
);
await dbConnection.execute(
`GRANT ALL PRIVILEGES ON \`${data.database}\`.* TO '${data.c_username}'@'%';`
);
await dbConnection.execute(
`GRANT SELECT, SHOW VIEW ON \`${data.database}\`.* TO '${data.q_username}'@'%';`
);
await dbConnection.execute(`FLUSH PRIVILEGES;`);
await dbConnection.end();
} catch (error) {
console.error("Error initializing database:", error);
throw error;
}
}
async create(
host: string,
port: number,
username: string,
password: string
): Promise<DatabaseNode> {
const encryptedPassword = this.encryptPassword(password);
const databaseNode = this.databaseNodeRepository.create({
host,
port,
username,
password: encryptedPassword,
});
return this.databaseNodeRepository.save(databaseNode);
}
async getConnectionOptions(id: string) {
const node = await this.databaseNodeRepository.findOne({ where: { id } });
if (!node) {
throw new Error("Database node not found");
}
return {
host: node.host,
port: node.port,
username: node.username,
password: this.decryptPassword(node.password),
};
}
}

View File

@ -0,0 +1,38 @@
import {
Column,
Entity,
ManyToOne,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
} from "typeorm";
import { Migration } from "./migration.entity";
import { Project } from "src/project/entities/project.entity";
import { DatabaseNode } from "./database.node.entity";
@Entity("database")
export class Database {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ type: "varchar", length: 255 })
q_username: string;
@Column({ type: "varchar", length: 255 })
c_username: string;
@Column({ type: "varchar", length: 255 })
password: string;
@Column({ type: "varchar", length: 255 })
database: string;
@OneToMany(() => Migration, (migration) => migration.database)
migrations: Migration[];
@OneToOne(() => Project, (project) => project.database)
project: Project;
@ManyToOne(() => DatabaseNode, (node) => node.databases)
node: DatabaseNode;
}

View File

@ -0,0 +1,23 @@
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Database } from "./database.entity";
@Entity("databaseNode")
export class DatabaseNode {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ type: "varchar", length: 255 })
host: string;
@Column({ type: "int" })
port: number;
@Column({ type: "varchar", length: 255 })
username: string;
@Column({ type: "varchar", length: 255 })
password: string;
@OneToMany(() => Database, (database) => database.node)
databases: Database[];
}

View File

@ -0,0 +1,35 @@
import {
Column,
CreateDateColumn,
Entity,
ManyToOne,
PrimaryGeneratedColumn,
} from "typeorm";
import { Database } from "./database.entity";
@Entity("migration")
export class Migration {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
appliedAt: Date;
@Column({ type: "tinyint", default: 1 })
isApplied: number;
@Column({ type: "tinyint", default: 1 })
isValid: number;
@Column({ type: "longtext", nullable: false })
up: string;
@Column({ type: "longtext", nullable: false })
down: string;
@ManyToOne(() => Database, (database) => database.id)
database: Database;
@CreateDateColumn()
createdAt: Date;
}

View File

@ -0,0 +1,11 @@
import { forwardRef, Module } from "@nestjs/common";
import { MigrationService } from "./migration.service";
import { DatabaseModule } from "src/database/database.module";
@Module({
imports: [forwardRef(() => DatabaseModule)],
controllers: [],
providers: [MigrationService],
exports: [MigrationService],
})
export class MigrationModule {}

View File

@ -0,0 +1,95 @@
import { Inject, Injectable } from "@nestjs/common";
import { IsNull, Not, Repository } from "typeorm";
import { Migration } from "../entities/migration.entity";
import { InjectRepository } from "@nestjs/typeorm";
import { DatabaseManagerService } from "../database/database.manager.service";
@Injectable()
export class MigrationService {
constructor(
@InjectRepository(Migration)
private readonly migrationRepository: Repository<Migration>,
@Inject(DatabaseManagerService)
private readonly databaseService: DatabaseManagerService
) {}
async create(
up: string,
down: string,
databaseId: string
): Promise<Migration> {
const database = await this.databaseService.findById(databaseId);
if (!database) {
throw new Error("Database not found");
}
const migration = this.migrationRepository.create({
up,
down,
database,
});
return await this.migrationRepository.save(migration);
}
async up(databaseId: string): Promise<Migration[]> {
const database = await this.databaseService.findById(databaseId);
if (!database) {
throw new Error("Database not found");
}
const migrations = await this.migrationRepository.find({
where: { database: { id: database.id }, appliedAt: null },
order: { createdAt: "ASC" },
});
const completedMigrations: Migration[] = [];
for (const migration of migrations) {
try {
await this.databaseService.runQuery(database.id, migration.up);
migration.appliedAt = new Date();
await this.migrationRepository.save(migration);
completedMigrations.push(migration);
} catch (error) {
throw new Error(
`Failed to apply migration ${migration.id}: ${error.message}`
);
}
}
return completedMigrations;
}
async down(databaseId: string): Promise<Migration[]> {
const database = await this.databaseService.findById(databaseId);
if (!database) {
throw new Error("Database not found");
}
const migrations = await this.migrationRepository.find({
where: { database: { id: database.id }, appliedAt: Not(IsNull()) },
order: { appliedAt: "DESC" },
});
const revertedMigrations: Migration[] = [];
for (const migration of migrations) {
try {
await this.databaseService.runQuery(database.id, migration.down);
migration.appliedAt = null;
await this.migrationRepository.save(migration);
revertedMigrations.push(migration);
} catch (error) {
throw new Error(
`Failed to revert migration ${migration.id}: ${error.message}`
);
}
}
return revertedMigrations;
}
}

View File

@ -1,30 +0,0 @@
import { Project } from "../../project/entities/project.entity";
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { MigrationTable } from "../migration.constants";
@Entity("migration")
export class Migration {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column()
name: string;
@Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
appliedAt: Date;
@Column({ type: "tinyint", default: 1 })
isApplied: number;
@Column({ type: "tinyint", default: 1 })
isValid: number;
@ManyToOne(() => Project, (project) => project.migrations)
project: Project;
@Column()
sql: string;
@Column({ type: "json", nullable: true })
data: MigrationTable[];
}

View File

@ -1,26 +0,0 @@
export type MigrationFieldType =
| "int"
| "float"
| "bigint"
| "boolean"
| "text"
| "uuid"
| "datetime";
export type MigrationField = {
type: MigrationFieldType;
isNullable: boolean;
isUnique: boolean;
default?: any;
};
export type MigrationRelationType =
| "one-to-many"
| "many-to-one"
| "many-to-many";
export type MigrationTable = {
name: string;
fields: Record<string, MigrationField>;
relations?: Record<string, { table: string; type: MigrationRelationType }>;
};

View File

@ -1,15 +0,0 @@
import { Body, Controller, Post } from "@nestjs/common";
import { MigrationService } from "./migration.service";
import { MigrationTable } from "./migration.constants";
@Controller("migrations")
export class MigrationController {
constructor(private readonly migrationService: MigrationService) {}
@Post("create")
async createMigration(
@Body() body: { token: string; tables: MigrationTable[] }
) {
return await this.migrationService.create(body.tables, body.token);
}
}

View File

@ -1,16 +0,0 @@
import { forwardRef, Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Migration } from "./entities/migration.entity";
import { MigrationController } from "./migration.controller";
import { MigrationService } from "./migration.service";
import { ProjectModule } from "src/project/project.module";
@Module({
imports: [
forwardRef(() => ProjectModule),
TypeOrmModule.forFeature([Migration]),
],
controllers: [MigrationController],
providers: [MigrationService],
})
export class MigrationModule {}

View File

@ -1,200 +0,0 @@
import { Inject, Injectable } from "@nestjs/common";
import { Repository } from "typeorm";
import { Migration } from "./entities/migration.entity";
import { InjectRepository } from "@nestjs/typeorm";
import { MigrationTable } from "./migration.constants";
import { ProjectService } from "src/project/project.service";
@Injectable()
export class MigrationService {
constructor(
@InjectRepository(Migration)
private readonly migrationRepository: Repository<Migration>,
@Inject(ProjectService)
private readonly projectService: ProjectService
) {}
compareTable(
oldTable: MigrationTable,
newTable: MigrationTable
): MigrationTable {
const changes: MigrationTable = {
name: newTable.name,
fields: {},
relations: {},
};
for (const fieldName in newTable.fields) {
if (!oldTable.fields[fieldName]) {
changes.fields[fieldName] = newTable.fields[fieldName];
} else {
const oldField = oldTable.fields[fieldName];
const newField = newTable.fields[fieldName];
if (
oldField.type !== newField.type ||
oldField.isNullable !== newField.isNullable ||
oldField.isUnique !== newField.isUnique ||
oldField.default !== newField.default
) {
changes.fields[fieldName] = newField;
}
}
}
for (const relationName in newTable.relations) {
if (!oldTable.relations || !oldTable.relations[relationName]) {
changes.relations[relationName] = newTable.relations[relationName];
} else {
const oldRelation = oldTable.relations[relationName];
const newRelation = newTable.relations[relationName];
if (
oldRelation.table !== newRelation.table ||
oldRelation.type !== newRelation.type
) {
changes.relations[relationName] = newRelation;
}
}
}
return changes;
}
generateSQL(changes: MigrationTable[]): string {
let sql = "";
for (const table of changes) {
if (table.fields && Object.keys(table.fields).length > 0) {
if (
Object.keys(table.fields).length === Object.keys(table.fields).length
) {
sql += `CREATE TABLE ${table.name} (\n`;
const fieldDefs = [];
for (const fieldName in table.fields) {
const field = table.fields[fieldName];
let fieldDef = ` ${fieldName} ${field.type.toUpperCase()}`;
if (!field.isNullable) {
fieldDef += " NOT NULL";
}
if (field.isUnique) {
fieldDef += " UNIQUE";
}
if (field.default !== undefined) {
fieldDef += ` DEFAULT ${field.default}`;
}
fieldDefs.push(fieldDef);
}
sql += fieldDefs.join(",\n");
sql += `\n);\n\n`;
} else {
for (const fieldName in table.fields) {
const field = table.fields[fieldName];
sql += `ALTER TABLE ${
table.name
} ADD COLUMN ${fieldName} ${field.type.toUpperCase()}`;
if (!field.isNullable) {
sql += " NOT NULL";
}
if (field.isUnique) {
sql += " UNIQUE";
}
if (field.default !== undefined) {
sql += ` DEFAULT ${field.default}`;
}
sql += `;\n`;
}
sql += `\n`;
}
}
if (table.relations && Object.keys(table.relations).length > 0) {
for (const relationName in table.relations) {
const relation = table.relations[relationName];
if (relation.type === "many-to-one") {
sql += `ALTER TABLE ${table.name} ADD COLUMN ${relationName}_id UUID;\n`;
sql += `ALTER TABLE ${table.name} ADD CONSTRAINT fk_${table.name}_${relationName} FOREIGN KEY (${relationName}_id) REFERENCES ${relation.table}(id);\n\n`;
}
if (relation.type === "one-to-many") {
sql += `ALTER TABLE ${relation.table} ADD COLUMN ${table.name}_id UUID;\n`;
}
if (relation.type === "many-to-many") {
const junctionTable = `${table.name}_${relation.table}`;
sql += `CREATE TABLE ${junctionTable} (\n`;
sql += ` ${table.name}_id UUID NOT NULL,\n`;
sql += ` ${relation.table}_id UUID NOT NULL,\n`;
sql += ` PRIMARY KEY (${table.name}_id, ${relation.table}_id),\n`;
sql += ` FOREIGN KEY (${table.name}_id) REFERENCES ${table.name}(id),\n`;
sql += ` FOREIGN KEY (${relation.table}_id) REFERENCES ${relation.table}(id)\n`;
sql += `);\n\n`;
}
}
}
}
return sql;
}
async create(tables: MigrationTable[], token: string): Promise<Migration> {
const project = await this.projectService.findByToken(token);
if (!project) {
throw new Error("Project not found");
}
const migrations = await this.migrationRepository.find({
where: {
project: { token },
isApplied: 1,
},
});
const virtualTables = {};
for (const migration of migrations) {
for (const table of migration.data) {
if (!virtualTables[table.name]) {
virtualTables[table.name] = table;
} else {
virtualTables[table.name] = this.compareTable(
virtualTables[table.name],
table
);
}
}
}
const changes: MigrationTable[] = [];
for (const table of tables) {
if (!virtualTables[table.name]) {
changes.push(table);
} else {
const tableChanges = this.compareTable(
virtualTables[table.name],
table
);
if (
Object.keys(tableChanges.fields).length > 0 ||
(tableChanges.relations &&
Object.keys(tableChanges.relations).length > 0)
) {
changes.push(tableChanges);
}
}
}
if (changes.length === 0) {
throw new Error("No changes detected");
}
const migration = this.migrationRepository.create({
name: `Migration ${new Date().toISOString()}`,
project,
sql: this.generateSQL(changes),
data: changes,
});
return this.migrationRepository.save(migration);
}
}

View File

@ -1,12 +1,18 @@
import { Migration } from "../../migration/entities/migration.entity";
import { Token } from "../../api/entities/token.entity"; import { Token } from "../../api/entities/token.entity";
import { Query } from "../../query/entities/query.entity"; import { Query } from "../../query/entities/query.entity";
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import {
Column,
Entity,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
} from "typeorm";
import { Database } from "src/databaseManager/entities/database.entity";
@Entity("project") @Entity("project")
export class Project { export class Project {
@PrimaryGeneratedColumn("uuid") @PrimaryGeneratedColumn("uuid")
token: string; id: string;
@Column({ type: "varchar", length: 255 }) @Column({ type: "varchar", length: 255 })
name: string; name: string;
@ -14,8 +20,8 @@ export class Project {
@OneToMany(() => Token, (token) => token.project) @OneToMany(() => Token, (token) => token.project)
apiTokens: Token[]; apiTokens: Token[];
@OneToMany(() => Migration, (migration) => migration.project) @OneToOne(() => Database, (database) => database.project)
migrations: Migration[]; database: Database;
@OneToMany(() => Query, (query) => query.project) @OneToMany(() => Query, (query) => query.project)
queries: Query[]; queries: Query[];

View File

@ -15,7 +15,7 @@ export class ProjectService {
return this.projectRepository.save(project); return this.projectRepository.save(project);
} }
findByToken(token: string) { findById(id: string) {
return this.projectRepository.findOne({ where: { token } }); return this.projectRepository.findOne({ where: { id: id } });
} }
} }

View File

@ -98,9 +98,7 @@ export class QueryHandlerService {
} }
async createQuery(queryData: { projectToken: string; source: string }) { async createQuery(queryData: { projectToken: string; source: string }) {
const project = await this.projectService.findByToken( const project = await this.projectService.findById(queryData.projectToken);
queryData.projectToken
);
if (!project) { if (!project) {
throw new Error("Project not found"); throw new Error("Project not found");

View File

@ -1270,6 +1270,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.6:
shebang-command "^2.0.0" shebang-command "^2.0.0"
which "^2.0.1" which "^2.0.1"
crypto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037"
integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==
dayjs@^1.11.13: dayjs@^1.11.13:
version "1.11.18" version "1.11.18"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.18.tgz#835fa712aac52ab9dec8b1494098774ed7070a11" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.18.tgz#835fa712aac52ab9dec8b1494098774ed7070a11"