// src/repositories/RouterRepository.ts import { RouterData, Study, VM, VMUpdate, Container } from '../types'; import pool from '../config/db'; import { RowDataPacket, ResultSetHeader } from 'mysql2'; import logger from '../utils/logger'; import { Pool } from 'mysql2/promise'; export class RouterRepository { constructor(private pool: Pool) {} // Modified constructor // src/repositories/RouterRepository.ts // src/repositories/RouterRepository.ts async updateVMs(routerId: string, vms: VMUpdate[]): Promise { console.log('Repository: Starting VM update for router:', routerId); const connection = await this.pool.getConnection(); try { await connection.beginTransaction(); console.log('Started transaction'); // First verify router exists const [routers] = await connection.query( 'SELECT id FROM routers WHERE router_id = ?', [routerId] ); console.log('Router query result:', routers); if (!Array.isArray(routers) || routers.length === 0) { throw new Error(`Router ${routerId} not found`); } // Log each VM update for (const vm of vms) { console.log(`Processing VM ${vm.vm_number}`); const [result]: any = await connection.query( `UPDATE vm_details SET status_code = ?, updated_at = CURRENT_TIMESTAMP WHERE router_id = ? AND vm_number = ?`, [vm.status_code, routerId, vm.vm_number] ); console.log('Update result:', result); if (!result.affectedRows) { console.log('No rows affected, attempting insert'); await connection.query( `INSERT INTO vm_details (router_id, vm_number, status_code) VALUES (?, ?, ?)`, [routerId, vm.vm_number, vm.status_code] ); } } await connection.commit(); console.log('Transaction committed'); const [updatedVMs] = await connection.query( `SELECT * FROM vm_details WHERE router_id = ? ORDER BY vm_number`, [routerId] ); return updatedVMs; } catch (err) { console.error('Repository error:', err); await connection.rollback(); throw err; } finally { connection.release(); console.log('Connection released'); } } private async getRouterStudies(routerId: number): Promise { try { const [rows] = await pool.query( `SELECT study_instance_uid as siuid, patient_id as patientId, accession_number as accessionNumber, patient_name as patientName, DATE_FORMAT(study_date, '%Y-%m-%d') as studyDate, modality, study_description as studyDescription, CONCAT(UPPER(SUBSTRING(study_status_code, 1, 1)), LOWER(SUBSTRING(study_status_code, 2))) as studyStatusCode FROM dicom_study_overview WHERE router_id = ? ORDER BY updated_at DESC LIMIT 1`, [routerId] ); return rows as Study[]; } catch (error) { logger.error(`Error fetching studies for router ${routerId}:`, error); return []; } } private async getRouterVMs(routerId: number): Promise { try { // First get the router_id string from routers table const [routerRow] = await pool.query( 'SELECT router_id FROM routers WHERE id = ?', [routerId] ); if (!routerRow || !routerRow[0]) { return []; } const routerIdString = routerRow[0].router_id; // Then use this to query vm_details const [rows] = await pool.query( `SELECT id, status_code as status FROM vm_details WHERE router_id = ?`, [routerIdString] ); console.log(`VMs for router ${routerId} (${routerIdString}):`, rows); return rows as VM[]; } catch (error) { logger.error(`Error fetching VMs for router ${routerId}:`, error); return []; } } private async getRouterContainers(routerId: number): Promise { try { // Then use this to query vm_details const [rows] = await pool.query( `SELECT container_name, status_code FROM container_status WHERE router_id = ?`, [routerId] ); return rows as Container[]; } catch (error) { logger.error(`Error fetching Containers for router ${routerId}:`, error); return []; } } private async calculateVMStatus (lastSeen: string | number | Date): Promise { const currentTime = new Date(); const lastSeenTime = new Date(lastSeen); // Use getTime() to get timestamps in milliseconds const diffInMinutes = (currentTime.getTime() - lastSeenTime.getTime()) / (1000 * 60); return diffInMinutes > 1 ? 'NET_OFFLINE' : 'NET_ONLINE'; }; private async calculateSystemStatus (vpnStatus: string, appStatus: string, vmStatus: string): Promise { return vpnStatus === 'VPN_CONNECTED' && appStatus === 'CONTAINER_RUNNING' && vmStatus === 'NET_ONLINE' ? 'CONNECTED' : 'DISCONNECTED'; }; async updateRouterAndContainerStatus(id: number, vpnStatus: string, appStatus: string): Promise { // Update router status await pool.query( `UPDATE routers SET vpn_status_code = ?, app_status_code = ?, updated_at = NOW() WHERE id = ?`, [vpnStatus, appStatus, id] ); // Update container status await pool.query( `UPDATE container_status SET status_code = ?, updated_at = NOW() WHERE router_id = ?`, ["CONTAINER_STOPPED", id] ); } private async transformDatabaseRouter(dbRouter: any, index: number): Promise { try { const lastSeen = new Date(dbRouter.last_seen).toISOString(); const vpnStatus = dbRouter.vpn_status_code.toString(); const appStatus = dbRouter.app_status_code.toString(); const vmStatus = await this.calculateVMStatus(lastSeen); const routerStatus = await this.calculateSystemStatus(vpnStatus, appStatus, vmStatus); // Update vpnStatus and appStatus based on vmStatus let updatedVpnStatus = vpnStatus; let updatedAppStatus = appStatus; if (vmStatus === 'NET_OFFLINE') { updatedVpnStatus = 'VPN_DISCONNECTED'; updatedAppStatus = 'CONTAINER_STOPPED'; await this.updateRouterAndContainerStatus(dbRouter.id, updatedVpnStatus, updatedAppStatus); } const studies = await this.getRouterStudies(dbRouter.id); //const vms = await this.getRouterVMs(dbRouter.id); const vms:VM[] = []; const containers = await this.getRouterContainers(dbRouter.id); return { id: dbRouter.id, slNo: index + 1, routerId: dbRouter.router_id, facility: dbRouter.facility, routerAlias: dbRouter.router_alias, facilityAET: dbRouter.facility_aet, openvpnIp: dbRouter.openvpn_ip, routerVmPrimaryIp: dbRouter.router_vm_primary_ip, lastSeen: lastSeen, diskStatus: dbRouter.disk_status_code, diskUsage: parseFloat(dbRouter.disk_usage), freeDisk: parseInt(dbRouter.free_disk), totalDisk: parseInt(dbRouter.total_disk), routerActivity: { studies }, systemStatus: { vpnStatus: updatedVpnStatus, appStatus: updatedAppStatus, vmStatus: vmStatus, routerStatus: routerStatus, vms, containers } }; } catch (error) { logger.error(`Error transforming router ${dbRouter.id}:`, error); throw error; } } async findAll(): Promise { try { const [rows] = await pool.query( 'SELECT * FROM routers ORDER BY created_at DESC' ); const routers = await Promise.all( rows.map((row, index) => this.transformDatabaseRouter(row, index)) ); return routers; } catch (error) { logger.error('Error in RouterRepository.findAll:', error); throw error; } } async findById(id: number): Promise { const [rows] = await pool.query( 'SELECT * FROM routers WHERE id = ?', [id] ); if (!rows.length) return null; return this.transformDatabaseRouter(rows[0], 0); } async findByRouterId(routerId: string): Promise { const [rows] = await pool.query( 'SELECT * FROM routers WHERE router_id = ?', [routerId] ); if (!rows.length) return null; return this.transformDatabaseRouter(rows[0], 0); } async findByFacility(facility: string): Promise { const [rows] = await pool.query( 'SELECT * FROM routers WHERE facility = ? ORDER BY created_at DESC', [facility] ); const routers = await Promise.all( rows.map((row, index) => this.transformDatabaseRouter(row, index)) ); return routers; } async create(router: Partial): Promise { const [result] = await pool.query( `INSERT INTO routers ( router_id, facility, router_alias, facility_aet, openvpn_ip, router_vm_primary_ip, last_seen, vpn_status_code, disk_status_code, app_status_code, license_status, free_disk, total_disk, disk_usage ) VALUES (?, ?, ?, ?, ?, ?, NOW(), ?, ?, ?, 'inactive', ?, ?, ?)`, [ router.routerId, router.facility, router.routerAlias, router.facilityAET, router.openvpnIp, router.routerVmPrimaryIp, router.systemStatus?.vpnStatus || 'unknown', router.diskStatus || 'unknown', router.systemStatus?.appStatus || 'unknown', router.freeDisk, router.totalDisk, router.diskUsage || 0 ] ); return this.findById(result.insertId) as Promise; } async update(id: number, router: Partial): Promise { const updates: Record = {}; if (router.routerId) updates.router_id = router.routerId; if (router.facility) updates.facility = router.facility; if (router.routerAlias) updates.router_alias = router.routerAlias; if (router.facilityAET) updates.facility_aet = router.facilityAET; if (router.openvpnIp) updates.openvpn_ip = router.openvpnIp; if (router.routerVmPrimaryIp) updates.router_vm_primary_ip = router.routerVmPrimaryIp; if (router.diskStatus) updates.disk_status_code = router.diskStatus; if (router.systemStatus?.vpnStatus) updates.vpn_status_code = router.systemStatus?.vpnStatus; if (router.systemStatus?.appStatus) updates.app_status_code = router.systemStatus?.appStatus; if (router.freeDisk !== undefined || router.totalDisk !== undefined) { const existingRouter = await this.findById(id); if (!existingRouter) return null; updates.free_disk = router.freeDisk ?? existingRouter.freeDisk; updates.total_disk = router.totalDisk ?? existingRouter.totalDisk; updates.disk_usage = ((updates.total_disk - updates.free_disk) / updates.total_disk) * 100; } if (Object.keys(updates).length > 0) { const setClauses = Object.keys(updates) .map(key => `${key} = ?`) .join(', '); await pool.query( `UPDATE routers SET ${setClauses}, last_seen = NOW(), updated_at = NOW() WHERE id = ?`, [...Object.values(updates), id] ); } return this.findById(id); } async delete(id: number): Promise { const [result] = await pool.query( 'DELETE FROM routers WHERE id = ?', [id] ); return result.affectedRows > 0; } async upsertContainerStatus(routerId: string, containers: Container[]): Promise { const values = containers.map((container) => [ routerId, container.container_name, container.status_code, new Date(), // created_at new Date(), // updated_at ]); const query = ` INSERT INTO container_status (router_id, container_name, status_code, created_at, updated_at) VALUES ${values.map(() => "(?, ?, ?, ?, ?)").join(", ")} ON DUPLICATE KEY UPDATE status_code = VALUES(status_code), updated_at = VALUES(updated_at); `; // Flatten the values array manually (compatible with older TypeScript versions) const flattenedValues = values.reduce((acc, val) => acc.concat(val), []); try { const [result] = await pool.query(query, flattenedValues); return result; } catch (error) { logger.error("Error inserting or updating container status:"); logger.error(`Query: ${query}`); logger.error(`Flattened Values: ${JSON.stringify(flattenedValues)}`); logger.error("Error Details:", error); throw new Error("Error inserting or updating container status"); } } }