- 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
76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import postgresClient from '@/lib/services/postgres-client';
|
|
|
|
export async function GET() {
|
|
try {
|
|
// Get compliance summary
|
|
const summaryResult = await postgresClient.query(`
|
|
SELECT
|
|
mismatch_type,
|
|
COUNT(*) as count
|
|
FROM veeam_compliance_results
|
|
GROUP BY mismatch_type
|
|
`);
|
|
|
|
let contractedNotBackedUp = 0;
|
|
let backedUpNotContracted = 0;
|
|
for (const row of summaryResult.rows) {
|
|
if (row.mismatch_type === 'contracted_not_backed_up') {
|
|
contractedNotBackedUp = parseInt(row.count);
|
|
} else if (row.mismatch_type === 'backed_up_not_contracted') {
|
|
backedUpNotContracted = parseInt(row.count);
|
|
}
|
|
}
|
|
|
|
// Get total contracted devices (config items with backup UDF + company has active contract)
|
|
const contractedResult = await postgresClient.query(`
|
|
SELECT COUNT(DISTINCT ci.id) as count
|
|
FROM configuration_items ci
|
|
JOIN contracts ct ON ct.company_id = ci.company_id AND ct.status = 1
|
|
WHERE ci.backup_type_udf IS NOT NULL
|
|
AND ci.backup_type_udf != ''
|
|
AND ci.is_active = true
|
|
AND ci.is_deleted = false
|
|
`);
|
|
const totalContracted = parseInt(contractedResult.rows[0]?.count || '0');
|
|
const matched = totalContracted - contractedNotBackedUp;
|
|
|
|
// Get last computed time
|
|
const lastComputed = await postgresClient.query(
|
|
'SELECT MAX(computed_at) as computed_at FROM veeam_compliance_results'
|
|
);
|
|
|
|
// Get mismatch details
|
|
const mismatches = await postgresClient.query(`
|
|
SELECT
|
|
cr.*,
|
|
c.company_name
|
|
FROM veeam_compliance_results cr
|
|
LEFT JOIN companies c ON c.id = cr.company_id
|
|
ORDER BY cr.mismatch_type, c.company_name, cr.device_name
|
|
`);
|
|
|
|
return NextResponse.json({
|
|
summary: {
|
|
totalContractedDevices: totalContracted,
|
|
matchedDevices: Math.max(0, matched),
|
|
contractedNotBackedUp,
|
|
backedUpNotContracted,
|
|
computedAt: lastComputed.rows[0]?.computed_at || null,
|
|
},
|
|
mismatches: mismatches.rows,
|
|
});
|
|
} catch (error) {
|
|
console.error('[VEEAM-API] compliance error:', error);
|
|
return NextResponse.json({
|
|
summary: {
|
|
totalContractedDevices: 0,
|
|
matchedDevices: 0,
|
|
contractedNotBackedUp: 0,
|
|
backedUpNotContracted: 0,
|
|
computedAt: null,
|
|
},
|
|
mismatches: [],
|
|
});
|
|
}
|
|
}
|