- Database: 7 Veeam tables + backup_type_udf column on configuration_items - API Client: VSPC REST API v3 client with pagination, rate limiting, Bearer auth - Sync Service: full/incremental sync for orgs, servers, repos, jobs, agent jobs, workloads - Scheduler: veeam-incremental (30min) and veeam-full (daily 2AM) schedules - Compliance Engine: cross-references Autotask config items vs Veeam workloads - API Endpoints: backup-status, companies, workloads, jobs, repos, compliance, sync - UI: Backup Status page with Overview + Contract Compliance tabs - Navigation: added Backup Status link with HardDrive icon - Docker: added VEEAM_VSPC_URL and VEEAM_VSPC_API_KEY env vars to compose
78 lines
2.8 KiB
TypeScript
78 lines
2.8 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import postgresClient from '@/lib/services/postgres-client';
|
|
|
|
export async function GET() {
|
|
try {
|
|
// Total protected workloads
|
|
const workloadsResult = await postgresClient.query(
|
|
'SELECT COUNT(*) as count FROM veeam_protected_workloads'
|
|
);
|
|
const totalProtectedWorkloads = parseInt(workloadsResult.rows[0].count);
|
|
|
|
// Jobs with status counts (last 24h) — combine backup server jobs and agent jobs
|
|
const jobStats24h = await postgresClient.query(`
|
|
SELECT status, COUNT(*) as count FROM (
|
|
SELECT status FROM veeam_backup_jobs WHERE last_run >= NOW() - INTERVAL '24 hours' AND is_enabled = true
|
|
UNION ALL
|
|
SELECT status FROM veeam_backup_agent_jobs WHERE last_run >= NOW() - INTERVAL '24 hours' AND is_enabled = true
|
|
) combined
|
|
GROUP BY status
|
|
`);
|
|
|
|
let successCount = 0;
|
|
let failedCount = 0;
|
|
let warningCount = 0;
|
|
let totalJobs24h = 0;
|
|
for (const row of jobStats24h.rows) {
|
|
const count = parseInt(row.count);
|
|
totalJobs24h += count;
|
|
if (row.status === 'Success') successCount += count;
|
|
else if (row.status === 'Failed') failedCount += count;
|
|
else if (row.status === 'Warning') warningCount += count;
|
|
}
|
|
|
|
const successRate24h = totalJobs24h > 0 ? Math.round((successCount / totalJobs24h) * 1000) / 10 : 100;
|
|
|
|
// Total job counts
|
|
const totalBackupServerJobs = await postgresClient.query(
|
|
'SELECT COUNT(*) as count FROM veeam_backup_jobs WHERE is_enabled = true'
|
|
);
|
|
const totalBackupAgentJobs = await postgresClient.query(
|
|
'SELECT COUNT(*) as count FROM veeam_backup_agent_jobs WHERE is_enabled = true'
|
|
);
|
|
|
|
// Last sync time
|
|
const lastSync = await postgresClient.query(
|
|
"SELECT MAX(synced_at) as last_sync FROM veeam_organizations"
|
|
);
|
|
|
|
return NextResponse.json({
|
|
totalProtectedWorkloads,
|
|
unprotectedWorkloads: 0, // TODO: compute from config items without matching workloads
|
|
successRate24h,
|
|
failedJobs24h: failedCount,
|
|
warningJobs24h: warningCount,
|
|
totalRepositoryCapacityBytes: 0,
|
|
totalRepositoryUsedBytes: 0,
|
|
repositoryUsagePercent: 0,
|
|
lastSyncAt: lastSync.rows[0]?.last_sync || null,
|
|
totalBackupServerJobs: parseInt(totalBackupServerJobs.rows[0].count),
|
|
totalBackupAgentJobs: parseInt(totalBackupAgentJobs.rows[0].count),
|
|
});
|
|
} catch (error) {
|
|
console.error('[VEEAM-API] backup-status error:', error);
|
|
return NextResponse.json({
|
|
totalProtectedWorkloads: 0,
|
|
unprotectedWorkloads: 0,
|
|
successRate24h: 0,
|
|
failedJobs24h: 0,
|
|
warningJobs24h: 0,
|
|
totalRepositoryCapacityBytes: 0,
|
|
totalRepositoryUsedBytes: 0,
|
|
repositoryUsagePercent: 0,
|
|
lastSyncAt: null,
|
|
totalBackupServerJobs: 0,
|
|
totalBackupAgentJobs: 0,
|
|
});
|
|
}
|
|
}
|