feat: Phase 3 – Modul-System (Manifest, ZIP-Installation, Lifecycle, Prozess-Manager)
This commit is contained in:
137
apps/platform-backend/src/modules/module.repository.ts
Normal file
137
apps/platform-backend/src/modules/module.repository.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DatabaseService } from '../database/database.service';
|
||||
import type { ModuleManifest, ModuleRecord, ModuleStatus } from './manifest.types';
|
||||
|
||||
interface ModuleRow {
|
||||
id: string;
|
||||
module_id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
version: string;
|
||||
description: string;
|
||||
author: string;
|
||||
path: string;
|
||||
status: ModuleStatus;
|
||||
internal_port: number;
|
||||
healthcheck_url: string;
|
||||
enabled: boolean;
|
||||
created_at: Date;
|
||||
updated_at: Date;
|
||||
}
|
||||
|
||||
const MODULE_COLUMNS = `id, module_id, name, slug, version, description, author, path,
|
||||
status, internal_port, healthcheck_url, enabled, created_at, updated_at`;
|
||||
|
||||
/**
|
||||
* Modul-Repository (Infrastructure): Datenbankzugriffe für die Modul-Registry.
|
||||
* Keine Business-Logik – nur parametrisierten Datenzugriff.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ModuleRepository {
|
||||
constructor(private readonly database: DatabaseService) {}
|
||||
|
||||
async list(): Promise<ModuleRecord[]> {
|
||||
const result = await this.database.query<ModuleRow>(
|
||||
`SELECT ${MODULE_COLUMNS} FROM modules ORDER BY created_at ASC`,
|
||||
);
|
||||
return result.rows.map((row) => this.mapRow(row));
|
||||
}
|
||||
|
||||
async findByModuleId(moduleId: string): Promise<ModuleRecord | null> {
|
||||
const result = await this.database.query<ModuleRow>(
|
||||
`SELECT ${MODULE_COLUMNS} FROM modules WHERE module_id = $1`,
|
||||
[moduleId],
|
||||
);
|
||||
return result.rows[0] ? this.mapRow(result.rows[0]) : null;
|
||||
}
|
||||
|
||||
async findBySlug(slug: string): Promise<ModuleRecord | null> {
|
||||
const result = await this.database.query<ModuleRow>(
|
||||
`SELECT ${MODULE_COLUMNS} FROM modules WHERE slug = $1`,
|
||||
[slug],
|
||||
);
|
||||
return result.rows[0] ? this.mapRow(result.rows[0]) : null;
|
||||
}
|
||||
|
||||
async findByPort(port: number): Promise<ModuleRecord | null> {
|
||||
const result = await this.database.query<ModuleRow>(
|
||||
`SELECT ${MODULE_COLUMNS} FROM modules WHERE internal_port = $1`,
|
||||
[port],
|
||||
);
|
||||
return result.rows[0] ? this.mapRow(result.rows[0]) : null;
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<ModuleRecord | null> {
|
||||
const result = await this.database.query<ModuleRow>(
|
||||
`SELECT ${MODULE_COLUMNS} FROM modules WHERE id = $1`,
|
||||
[id],
|
||||
);
|
||||
return result.rows[0] ? this.mapRow(result.rows[0]) : null;
|
||||
}
|
||||
|
||||
async findByStatus(statuses: readonly ModuleStatus[]): Promise<ModuleRecord[]> {
|
||||
const result = await this.database.query<ModuleRow>(
|
||||
`SELECT ${MODULE_COLUMNS} FROM modules WHERE status = ANY($1::text[])`,
|
||||
[statuses],
|
||||
);
|
||||
return result.rows.map((row) => this.mapRow(row));
|
||||
}
|
||||
|
||||
async create(manifest: ModuleManifest, directory: string): Promise<ModuleRecord> {
|
||||
const result = await this.database.query<ModuleRow>(
|
||||
`INSERT INTO modules
|
||||
(module_id, name, slug, version, description, author, path, status, internal_port, healthcheck_url)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, 'INSTALLED', $8, $9)
|
||||
RETURNING ${MODULE_COLUMNS}`,
|
||||
[
|
||||
manifest.id,
|
||||
manifest.name,
|
||||
manifest.slug,
|
||||
manifest.version,
|
||||
manifest.description,
|
||||
manifest.author,
|
||||
directory,
|
||||
manifest.port,
|
||||
manifest.healthcheck,
|
||||
],
|
||||
);
|
||||
return this.mapRow(result.rows[0]);
|
||||
}
|
||||
|
||||
async updateStatus(id: string, status: ModuleStatus): Promise<void> {
|
||||
await this.database.query(
|
||||
'UPDATE modules SET status = $2, updated_at = now() WHERE id = $1',
|
||||
[id, status],
|
||||
);
|
||||
}
|
||||
|
||||
async updateEnabled(id: string, enabled: boolean): Promise<void> {
|
||||
await this.database.query(
|
||||
'UPDATE modules SET enabled = $2, updated_at = now() WHERE id = $1',
|
||||
[id, enabled],
|
||||
);
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
await this.database.query('DELETE FROM modules WHERE id = $1', [id]);
|
||||
}
|
||||
|
||||
private mapRow(row: ModuleRow): ModuleRecord {
|
||||
return {
|
||||
id: row.id,
|
||||
moduleId: row.module_id,
|
||||
name: row.name,
|
||||
slug: row.slug,
|
||||
version: row.version,
|
||||
description: row.description,
|
||||
author: row.author,
|
||||
path: row.path,
|
||||
status: row.status,
|
||||
internalPort: row.internal_port,
|
||||
healthcheckUrl: row.healthcheck_url,
|
||||
enabled: row.enabled,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user