diff --git a/src/server/database-prisma.ts b/src/server/database-prisma.ts index dcbee5a..b7de9ac 100644 --- a/src/server/database-prisma.ts +++ b/src/server/database-prisma.ts @@ -1,16 +1,117 @@ - import { prisma } from './db'; import { join } from 'path'; import { writeFileSync, unlinkSync, chmodSync, mkdirSync } from 'fs'; import { existsSync } from 'fs'; import type { CreateServerData } from '../types/server'; +import type { Prisma } from '../../prisma/generated/prisma/client'; + +// Type definitions based on Prisma schema +type Server = { + id: number; + name: string; + ip: string; + user: string; + password: string | null; + auth_type: string | null; + ssh_key: string | null; + ssh_key_passphrase: string | null; + ssh_port: number | null; + color: string | null; + created_at: Date | null; + updated_at: Date | null; + ssh_key_path: string | null; + key_generated: boolean | null; +}; + +type InstalledScript = { + id: number; + script_name: string; + script_path: string; + container_id: string | null; + server_id: number | null; + execution_mode: string; + installation_date: Date | null; + status: string; + output_log: string | null; + web_ui_ip: string | null; + web_ui_port: number | null; +}; + +type InstalledScriptWithServer = InstalledScript & { + server: Server | null; +}; + +type LXCConfig = { + id: number; + installed_script_id: number; + arch: string | null; + cores: number | null; + memory: number | null; + hostname: string | null; + swap: number | null; + onboot: number | null; + ostype: string | null; + unprivileged: number | null; + net_name: string | null; + net_bridge: string | null; + net_hwaddr: string | null; + net_ip_type: string | null; + net_ip: string | null; + net_gateway: string | null; + net_type: string | null; + net_vlan: number | null; + rootfs_storage: string | null; + rootfs_size: string | null; + feature_keyctl: number | null; + feature_nesting: number | null; + feature_fuse: number | null; + feature_mount: string | null; + tags: string | null; + advanced_config: string | null; + synced_at: Date | null; + config_hash: string | null; + created_at: Date; + updated_at: Date; +}; + +type Backup = { + id: number; + container_id: string; + server_id: number; + hostname: string; + backup_name: string; + backup_path: string; + size: bigint | null; + created_at: Date | null; + storage_name: string; + storage_type: string; + discovered_at: Date; +}; + +type BackupWithServer = Backup & { + server: Server | null; +}; + +type PBSStorageCredential = { + id: number; + server_id: number; + storage_name: string; + pbs_ip: string; + pbs_datastore: string; + pbs_password: string; + pbs_fingerprint: string; + created_at: Date; + updated_at: Date; +}; + +type LXCConfigInput = Partial>; class DatabaseServicePrisma { constructor() { this.init(); } - init() { + init(): void { // Ensure data/ssh-keys directory exists (recursive to create parent dirs) const sshKeysDir = join(process.cwd(), 'data', 'ssh-keys'); if (!existsSync(sshKeysDir)) { @@ -19,11 +120,11 @@ class DatabaseServicePrisma { } // Server CRUD operations - async createServer(serverData: CreateServerData) { + async createServer(serverData: CreateServerData): Promise { const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port, color, key_generated } = serverData; const normalizedPort = ssh_port !== undefined ? parseInt(String(ssh_port), 10) : 22; - let ssh_key_path = null; + let ssh_key_path: string | null = null; // If using SSH key authentication, create persistent key file if (auth_type === 'key' && ssh_key) { @@ -31,7 +132,7 @@ class DatabaseServicePrisma { ssh_key_path = this.createSSHKeyFile(serverId, ssh_key); } - return await prisma.server.create({ + const result = await prisma.server.create({ data: { name, ip, @@ -46,27 +147,30 @@ class DatabaseServicePrisma { color, } }); + return result as Server; } - async getAllServers() { - return await prisma.server.findMany({ + async getAllServers(): Promise { + const result = await prisma.server.findMany({ orderBy: { created_at: 'desc' } }); + return result as Server[]; } - async getServerById(id: number) { - return await prisma.server.findUnique({ + async getServerById(id: number): Promise { + const result = await prisma.server.findUnique({ where: { id } }); + return result as Server | null; } - async updateServer(id: number, serverData: CreateServerData) { + async updateServer(id: number, serverData: CreateServerData): Promise { const { name, ip, user, password, auth_type, ssh_key, ssh_key_passphrase, ssh_port, color, key_generated } = serverData; const normalizedPort = ssh_port !== undefined ? parseInt(String(ssh_port), 10) : undefined; // Get existing server to check for key changes const existingServer = await this.getServerById(id); - let ssh_key_path = existingServer?.ssh_key_path; + let ssh_key_path = existingServer?.ssh_key_path ?? null; // Handle SSH key changes if (auth_type === 'key' && ssh_key) { @@ -102,7 +206,7 @@ class DatabaseServicePrisma { ssh_key_path = null; } - return await prisma.server.update({ + const result = await prisma.server.update({ where: { id }, data: { name, @@ -118,9 +222,10 @@ class DatabaseServicePrisma { color, } }); + return result as Server; } - async deleteServer(id: number) { + async deleteServer(id: number): Promise { // Get server info before deletion to clean up key files const server = await this.getServerById(id); @@ -137,9 +242,10 @@ class DatabaseServicePrisma { } } - return await prisma.server.delete({ + const result = await prisma.server.delete({ where: { id } }); + return result as Server; } // Installed Scripts CRUD operations @@ -153,10 +259,10 @@ class DatabaseServicePrisma { output_log?: string; web_ui_ip?: string; web_ui_port?: number; - }) { + }): Promise { const { script_name, script_path, container_id, server_id, execution_mode, status, output_log, web_ui_ip, web_ui_port } = scriptData; - return await prisma.installedScript.create({ + const result = await prisma.installedScript.create({ data: { script_name, script_path, @@ -169,34 +275,38 @@ class DatabaseServicePrisma { web_ui_port: web_ui_port ?? null, } }); + return result as InstalledScript; } - async getAllInstalledScripts() { - return await prisma.installedScript.findMany({ + async getAllInstalledScripts(): Promise { + const result = await prisma.installedScript.findMany({ include: { server: true }, orderBy: { installation_date: 'desc' } }); + return result as InstalledScriptWithServer[]; } - async getInstalledScriptById(id: number) { - return await prisma.installedScript.findUnique({ + async getInstalledScriptById(id: number): Promise { + const result = await prisma.installedScript.findUnique({ where: { id }, include: { server: true } }); + return result as InstalledScriptWithServer | null; } - async getInstalledScriptsByServer(server_id: number) { - return await prisma.installedScript.findMany({ + async getInstalledScriptsByServer(server_id: number): Promise { + const result = await prisma.installedScript.findMany({ where: { server_id }, include: { server: true }, orderBy: { installation_date: 'desc' } }); + return result as InstalledScriptWithServer[]; } async updateInstalledScript(id: number, updateData: { @@ -206,17 +316,10 @@ class DatabaseServicePrisma { output_log?: string; web_ui_ip?: string; web_ui_port?: number; - }) { + }): Promise { const { script_name, container_id, status, output_log, web_ui_ip, web_ui_port } = updateData; - const updateFields: { - script_name?: string; - container_id?: string; - status?: 'in_progress' | 'success' | 'failed'; - output_log?: string; - web_ui_ip?: string; - web_ui_port?: number; - } = {}; + const updateFields: Prisma.InstalledScriptUpdateInput = {}; if (script_name !== undefined) updateFields.script_name = script_name; if (container_id !== undefined) updateFields.container_id = container_id; if (status !== undefined) updateFields.status = status; @@ -228,33 +331,36 @@ class DatabaseServicePrisma { return { changes: 0 }; } - return await prisma.installedScript.update({ + const result = await prisma.installedScript.update({ where: { id }, data: updateFields }); + return result as InstalledScript; } - async deleteInstalledScript(id: number) { - return await prisma.installedScript.delete({ + async deleteInstalledScript(id: number): Promise { + const result = await prisma.installedScript.delete({ where: { id } }); + return result as InstalledScript; } - async deleteInstalledScriptsByServer(server_id: number) { - return await prisma.installedScript.deleteMany({ + async deleteInstalledScriptsByServer(server_id: number): Promise<{ count: number }> { + const result = await prisma.installedScript.deleteMany({ where: { server_id } }); + return result as { count: number }; } - async getNextServerId() { + async getNextServerId(): Promise { const result = await prisma.server.findFirst({ orderBy: { id: 'desc' }, select: { id: true } }); - return (result?.id ?? 0) + 1; + return ((result as { id: number } | null)?.id ?? 0) + 1; } - createSSHKeyFile(serverId: number, sshKey: string) { + createSSHKeyFile(serverId: number, sshKey: string): string { const sshKeysDir = join(process.cwd(), 'data', 'ssh-keys'); const keyPath = join(sshKeysDir, `server_${serverId}_key`); @@ -267,17 +373,18 @@ class DatabaseServicePrisma { } // LXC Config CRUD operations - async createLXCConfig(scriptId: number, configData: any) { - return await prisma.lXCConfig.create({ + async createLXCConfig(scriptId: number, configData: LXCConfigInput): Promise { + const result = await prisma.lXCConfig.create({ data: { installed_script_id: scriptId, ...configData } }); + return result as LXCConfig; } - async updateLXCConfig(scriptId: number, configData: any) { - return await prisma.lXCConfig.upsert({ + async updateLXCConfig(scriptId: number, configData: LXCConfigInput): Promise { + const result = await prisma.lXCConfig.upsert({ where: { installed_script_id: scriptId }, update: configData, create: { @@ -285,16 +392,18 @@ class DatabaseServicePrisma { ...configData } }); + return result as LXCConfig; } - async getLXCConfigByScriptId(scriptId: number) { - return await prisma.lXCConfig.findUnique({ + async getLXCConfigByScriptId(scriptId: number): Promise { + const result = await prisma.lXCConfig.findUnique({ where: { installed_script_id: scriptId } }); + return result as LXCConfig | null; } - async deleteLXCConfig(scriptId: number) { - return await prisma.lXCConfig.delete({ + async deleteLXCConfig(scriptId: number): Promise { + await prisma.lXCConfig.delete({ where: { installed_script_id: scriptId } }); } @@ -310,7 +419,7 @@ class DatabaseServicePrisma { created_at?: Date; storage_name: string; storage_type: 'local' | 'storage' | 'pbs'; - }) { + }): Promise { // Find existing backup by container_id, server_id, and backup_path const existing = await prisma.backup.findFirst({ where: { @@ -318,11 +427,11 @@ class DatabaseServicePrisma { server_id: backupData.server_id, backup_path: backupData.backup_path, }, - }); + }) as Backup | null; if (existing) { // Update existing backup - return await prisma.backup.update({ + const result = await prisma.backup.update({ where: { id: existing.id }, data: { hostname: backupData.hostname, @@ -334,9 +443,10 @@ class DatabaseServicePrisma { discovered_at: new Date(), }, }); + return result as Backup; } else { // Create new backup - return await prisma.backup.create({ + const result = await prisma.backup.create({ data: { container_id: backupData.container_id, server_id: backupData.server_id, @@ -350,11 +460,12 @@ class DatabaseServicePrisma { discovered_at: new Date(), }, }); + return result as Backup; } } - async getAllBackups() { - return await prisma.backup.findMany({ + async getAllBackups(): Promise { + const result = await prisma.backup.findMany({ include: { server: true, }, @@ -363,58 +474,43 @@ class DatabaseServicePrisma { { created_at: 'desc' }, ], }); + return result as BackupWithServer[]; } - async getBackupById(id: number) { - return await prisma.backup.findUnique({ + async getBackupById(id: number): Promise { + const result = await prisma.backup.findUnique({ where: { id }, include: { server: true, }, }); + return result as BackupWithServer | null; } - async getBackupsByContainerId(containerId: string) { - return await prisma.backup.findMany({ + async getBackupsByContainerId(containerId: string): Promise { + const result = await prisma.backup.findMany({ where: { container_id: containerId }, include: { server: true, }, orderBy: { created_at: 'desc' }, }); + return result as BackupWithServer[]; } - async deleteBackupsForContainer(containerId: string, serverId: number) { - return await prisma.backup.deleteMany({ + async deleteBackupsForContainer(containerId: string, serverId: number): Promise<{ count: number }> { + const result = await prisma.backup.deleteMany({ where: { container_id: containerId, server_id: serverId, }, }); + return result as { count: number }; } - async getBackupsGroupedByContainer(): Promise>> { + async getBackupsGroupedByContainer(): Promise> { const backups = await this.getAllBackups(); - const grouped = new Map(); + const grouped = new Map(); for (const backup of backups) { const key = backup.container_id; @@ -435,8 +531,8 @@ class DatabaseServicePrisma { pbs_datastore: string; pbs_password: string; pbs_fingerprint: string; - }) { - return await prisma.pBSStorageCredential.upsert({ + }): Promise { + const result = await prisma.pBSStorageCredential.upsert({ where: { server_id_storage_name: { server_id: credentialData.server_id, @@ -459,10 +555,11 @@ class DatabaseServicePrisma { pbs_fingerprint: credentialData.pbs_fingerprint, }, }); + return result as PBSStorageCredential; } - async getPBSCredential(serverId: number, storageName: string) { - return await prisma.pBSStorageCredential.findUnique({ + async getPBSCredential(serverId: number, storageName: string): Promise { + const result = await prisma.pBSStorageCredential.findUnique({ where: { server_id_storage_name: { server_id: serverId, @@ -470,17 +567,19 @@ class DatabaseServicePrisma { }, }, }); + return result as PBSStorageCredential | null; } - async getPBSCredentialsByServer(serverId: number) { - return await prisma.pBSStorageCredential.findMany({ + async getPBSCredentialsByServer(serverId: number): Promise { + const result = await prisma.pBSStorageCredential.findMany({ where: { server_id: serverId }, orderBy: { storage_name: 'asc' }, }); + return result as PBSStorageCredential[]; } - async deletePBSCredential(serverId: number, storageName: string) { - return await prisma.pBSStorageCredential.delete({ + async deletePBSCredential(serverId: number, storageName: string): Promise { + const result = await prisma.pBSStorageCredential.delete({ where: { server_id_storage_name: { server_id: serverId, @@ -488,9 +587,10 @@ class DatabaseServicePrisma { }, }, }); + return result as PBSStorageCredential; } - async close() { + async close(): Promise { await prisma.$disconnect(); } } @@ -498,7 +598,7 @@ class DatabaseServicePrisma { // Singleton instance let dbInstance: DatabaseServicePrisma | null = null; -export function getDatabase() { +export function getDatabase(): DatabaseServicePrisma { dbInstance ??= new DatabaseServicePrisma(); return dbInstance; }