feat: enhance database management with new migration and database node functionalities, including CRUD operations and test cases

This commit is contained in:
lborv
2025-09-27 23:41:32 +03:00
parent 0d5b2830ed
commit 785a7bfe8e
21 changed files with 333 additions and 189 deletions

View File

@ -1,4 +1,4 @@
import { Controller, Get, Inject, Post } from "@nestjs/common";
import { Controller, Get, Post, Body, Param } from "@nestjs/common";
import { DatabaseManagerService } from "./database.manager.service";
import { DatabaseNodeService } from "../databaseNode/database.node.service";
import { MigrationService } from "../migration/migration.service";
@ -6,27 +6,22 @@ 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 }
) {
createDatabase(@Body() body: { projectId: string; databaseNodeId: string }) {
return this.databaseManagerService.createDatabase(
body.projectId,
body.databaseNodeId
body.databaseNodeId,
body.projectId
);
}
@Post("node/create")
addDatabaseNode(
@Inject("body")
@Body()
body: {
host: string;
port: number;
@ -43,18 +38,18 @@ export class DatabaseManagerController {
}
@Get("migration/up/:databaseId")
migrateUp(@Inject("params") params: { databaseId: string }) {
return this.migrationService.up(params.databaseId);
migrateUp(@Param("databaseId") databaseId: string) {
return this.migrationService.up(databaseId);
}
@Get("migration/down/:databaseId")
migrateDown(@Inject("params") params: { databaseId: string }) {
return this.migrationService.down(params.databaseId);
migrateDown(@Param("databaseId") databaseId: string) {
return this.migrationService.down(databaseId);
}
@Post("migration/create")
createMigration(
@Inject("body")
@Body()
body: {
up: string;
down: string;
@ -66,9 +61,9 @@ export class DatabaseManagerController {
@Post("query/:databaseId")
runQuery(
@Inject("params") params: { databaseId: string },
@Inject("body") body: { query: string }
@Param("databaseId") databaseId: string,
@Body() body: { query: string }
) {
return this.databaseManagerService.runQuery(params.databaseId, body.query);
return this.databaseManagerService.runQuery(databaseId, body.query);
}
}

View File

@ -12,7 +12,7 @@ import { DatabaseNodeService } from "../databaseNode/database.node.service";
@Module({
imports: [
forwardRef(() => ProjectModule),
MigrationModule,
forwardRef(() => MigrationModule),
TypeOrmModule.forFeature([Database, DatabaseNode, Project]),
],
controllers: [DatabaseManagerController],

View File

@ -1,4 +1,4 @@
import { Inject, Injectable } from "@nestjs/common";
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Database } from "../entities/database.entity";
import { Repository } from "typeorm";
@ -12,9 +12,7 @@ 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();
@ -24,8 +22,19 @@ export class DatabaseManagerService extends DatabaseEncryptionService {
return this.databaseRepository.findOne({ where: { id } });
}
async runQuery(databaseId: string, query: string) {
const database = await this.findById(databaseId);
async findByIdWithRelations(id: string): Promise<Database | null> {
return this.databaseRepository.findOne({
where: { id },
relations: ["node", "project"],
});
}
async runQuery(
databaseId: string,
query: string,
queryUser: boolean = false
): Promise<any> {
const database = await this.findByIdWithRelations(databaseId);
if (!database) {
throw new Error("Database not found");
@ -34,7 +43,7 @@ export class DatabaseManagerService extends DatabaseEncryptionService {
const dbConnection = await mysql.createConnection({
host: database.node.host,
port: database.node.port,
user: database.c_username,
user: queryUser ? database.q_username : database.c_username,
password: this.decryptPassword(database.password),
database: database.database,
enableKeepAlive: true,

View File

@ -61,6 +61,16 @@ export class DatabaseNodeService extends DatabaseEncryptionService {
username: string,
password: string
): Promise<DatabaseNode> {
const existingNode = await this.databaseNodeRepository.findOne({
where: { host, port },
});
if (existingNode) {
existingNode.password = this.encryptPassword(password);
existingNode.username = username;
return this.databaseNodeRepository.save(existingNode);
}
const encryptedPassword = this.encryptPassword(password);
const databaseNode = this.databaseNodeRepository.create({
host,
@ -68,6 +78,7 @@ export class DatabaseNodeService extends DatabaseEncryptionService {
username,
password: encryptedPassword,
});
return this.databaseNodeRepository.save(databaseNode);
}
@ -81,7 +92,7 @@ export class DatabaseNodeService extends DatabaseEncryptionService {
return {
host: node.host,
port: node.port,
username: node.username,
user: node.username,
password: this.decryptPassword(node.password),
};
}

View File

@ -1,13 +1,14 @@
import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToMany,
OneToOne,
PrimaryGeneratedColumn,
} from "typeorm";
import { Migration } from "./migration.entity";
import { Project } from "src/project/entities/project.entity";
import { Project } from "../../project/entities/project.entity";
import { DatabaseNode } from "./database.node.entity";
@Entity("database")
@ -31,6 +32,7 @@ export class Database {
migrations: Migration[];
@OneToOne(() => Project, (project) => project.database)
@JoinColumn()
project: Project;
@ManyToOne(() => DatabaseNode, (node) => node.databases)

View File

@ -1,7 +1,14 @@
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import {
Column,
Entity,
Index,
OneToMany,
PrimaryGeneratedColumn,
} from "typeorm";
import { Database } from "./database.entity";
@Entity("databaseNode")
@Index("IDX_DATABASE_NODE_HOST_PORT", ["host", "port"], { unique: true })
export class DatabaseNode {
@PrimaryGeneratedColumn("uuid")
id: string;

View File

@ -1,9 +1,14 @@
import { DatabaseManagerModule } from "src/databaseManager/database/database.manager.module";
import { forwardRef, Module } from "@nestjs/common";
import { MigrationService } from "./migration.service";
import { DatabaseModule } from "src/database/database.module";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Migration } from "../entities/migration.entity";
@Module({
imports: [forwardRef(() => DatabaseModule)],
imports: [
forwardRef(() => DatabaseManagerModule),
TypeOrmModule.forFeature([Migration]),
],
controllers: [],
providers: [MigrationService],
exports: [MigrationService],

View File

@ -1,4 +1,4 @@
import { Inject, Injectable } from "@nestjs/common";
import { Injectable, forwardRef, Inject } from "@nestjs/common";
import { IsNull, Not, Repository } from "typeorm";
import { Migration } from "../entities/migration.entity";
import { InjectRepository } from "@nestjs/typeorm";
@ -9,7 +9,7 @@ export class MigrationService {
constructor(
@InjectRepository(Migration)
private readonly migrationRepository: Repository<Migration>,
@Inject(DatabaseManagerService)
@Inject(forwardRef(() => DatabaseManagerService))
private readonly databaseService: DatabaseManagerService
) {}