389 lines
13 KiB
TypeScript
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");
|
|
}
|
|
}
|
|
|
|
} |