389 lines
13 KiB
TypeScript

// 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<any> {
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<Study[]> {
try {
const [rows] = await pool.query<RowDataPacket[]>(
`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<VM[]> {
try {
// First get the router_id string from routers table
const [routerRow] = await pool.query<RowDataPacket[]>(
'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<RowDataPacket[]>(
`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<Container[]> {
try {
// Then use this to query vm_details
const [rows] = await pool.query<RowDataPacket[]>(
`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<string> {
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<string> {
return vpnStatus === 'VPN_CONNECTED' && appStatus === 'CONTAINER_RUNNING' && vmStatus === 'NET_ONLINE' ? 'CONNECTED' : 'DISCONNECTED';
};
async updateRouterAndContainerStatus(id: number, vpnStatus: string, appStatus: string): Promise<void> {
// 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<RouterData> {
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<RouterData[]> {
try {
const [rows] = await pool.query<RowDataPacket[]>(
'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<RouterData | null> {
const [rows] = await pool.query<RowDataPacket[]>(
'SELECT * FROM routers WHERE id = ?',
[id]
);
if (!rows.length) return null;
return this.transformDatabaseRouter(rows[0], 0);
}
async findByRouterId(routerId: string): Promise<RouterData | null> {
const [rows] = await pool.query<RowDataPacket[]>(
'SELECT * FROM routers WHERE router_id = ?',
[routerId]
);
if (!rows.length) return null;
return this.transformDatabaseRouter(rows[0], 0);
}
async findByFacility(facility: string): Promise<RouterData[]> {
const [rows] = await pool.query<RowDataPacket[]>(
'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<RouterData>): Promise<RouterData> {
const [result] = await pool.query<ResultSetHeader>(
`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<RouterData>;
}
async update(id: number, router: Partial<RouterData>): Promise<RouterData | null> {
const updates: Record<string, any> = {};
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<boolean> {
const [result] = await pool.query<ResultSetHeader>(
'DELETE FROM routers WHERE id = ?',
[id]
);
return result.affectedRows > 0;
}
async upsertContainerStatus(routerId: string, containers: Container[]): Promise<ResultSetHeader> {
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<ResultSetHeader>(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");
}
}
}