style(backend): fmt

This commit is contained in:
tigattack
2025-09-24 22:05:56 +01:00
parent 6d70a67a49
commit 591389a91f
18 changed files with 5891 additions and 4953 deletions

View File

@@ -1,426 +1,457 @@
const express = require('express');
const { PrismaClient } = require('@prisma/client');
const moment = require('moment');
const { authenticateToken } = require('../middleware/auth');
const {
requireViewDashboard,
requireViewHosts,
requireViewPackages,
requireViewUsers
} = require('../middleware/permissions');
const express = require("express");
const { PrismaClient } = require("@prisma/client");
const moment = require("moment");
const { authenticateToken } = require("../middleware/auth");
const {
requireViewDashboard,
requireViewHosts,
requireViewPackages,
requireViewUsers,
} = require("../middleware/permissions");
const router = express.Router();
const prisma = new PrismaClient();
// Get dashboard statistics
router.get('/stats', authenticateToken, requireViewDashboard, async (req, res) => {
try {
const now = new Date();
// Get the agent update interval setting
const settings = await prisma.settings.findFirst();
const updateIntervalMinutes = settings?.update_interval || 60; // Default to 60 minutes if no setting
// Calculate the threshold based on the actual update interval
// Use 2x the update interval as the threshold for "errored" hosts
const thresholdMinutes = updateIntervalMinutes * 2;
const thresholdTime = moment(now).subtract(thresholdMinutes, 'minutes').toDate();
router.get(
"/stats",
authenticateToken,
requireViewDashboard,
async (req, res) => {
try {
const now = new Date();
// Get all statistics in parallel for better performance
const [
totalHosts,
hostsNeedingUpdates,
totalOutdatedPackages,
erroredHosts,
securityUpdates,
offlineHosts,
totalHostGroups,
totalUsers,
totalRepos,
osDistribution,
updateTrends
] = await Promise.all([
// Total hosts count (all hosts regardless of status)
prisma.hosts.count(),
// Get the agent update interval setting
const settings = await prisma.settings.findFirst();
const updateIntervalMinutes = settings?.update_interval || 60; // Default to 60 minutes if no setting
// Hosts needing updates (distinct hosts with packages needing updates)
prisma.hosts.count({
where: {
host_packages: {
some: {
needs_update: true
}
}
}
}),
// Calculate the threshold based on the actual update interval
// Use 2x the update interval as the threshold for "errored" hosts
const thresholdMinutes = updateIntervalMinutes * 2;
const thresholdTime = moment(now)
.subtract(thresholdMinutes, "minutes")
.toDate();
// Total outdated packages across all hosts
prisma.host_packages.count({
where: { needs_update: true }
}),
// Get all statistics in parallel for better performance
const [
totalHosts,
hostsNeedingUpdates,
totalOutdatedPackages,
erroredHosts,
securityUpdates,
offlineHosts,
totalHostGroups,
totalUsers,
totalRepos,
osDistribution,
updateTrends,
] = await Promise.all([
// Total hosts count (all hosts regardless of status)
prisma.hosts.count(),
// Errored hosts (not updated within threshold based on update interval)
prisma.hosts.count({
where: {
status: 'active',
last_update: {
lt: thresholdTime
}
}
}),
// Hosts needing updates (distinct hosts with packages needing updates)
prisma.hosts.count({
where: {
host_packages: {
some: {
needs_update: true,
},
},
},
}),
// Security updates count
prisma.host_packages.count({
where: {
needs_update: true,
is_security_update: true
}
}),
// Total outdated packages across all hosts
prisma.host_packages.count({
where: { needs_update: true },
}),
// Offline/Stale hosts (not updated within 3x the update interval)
prisma.hosts.count({
where: {
status: 'active',
last_update: {
lt: moment(now).subtract(updateIntervalMinutes * 3, 'minutes').toDate()
}
}
}),
// Errored hosts (not updated within threshold based on update interval)
prisma.hosts.count({
where: {
status: "active",
last_update: {
lt: thresholdTime,
},
},
}),
// Total host groups count
prisma.host_groups.count(),
// Security updates count
prisma.host_packages.count({
where: {
needs_update: true,
is_security_update: true,
},
}),
// Total users count
prisma.users.count(),
// Offline/Stale hosts (not updated within 3x the update interval)
prisma.hosts.count({
where: {
status: "active",
last_update: {
lt: moment(now)
.subtract(updateIntervalMinutes * 3, "minutes")
.toDate(),
},
},
}),
// Total repositories count
prisma.repositories.count(),
// Total host groups count
prisma.host_groups.count(),
// OS distribution for pie chart
prisma.hosts.groupBy({
by: ['os_type'],
where: { status: 'active' },
_count: {
os_type: true
}
}),
// Total users count
prisma.users.count(),
// Update trends for the last 7 days
prisma.update_history.groupBy({
by: ['timestamp'],
where: {
timestamp: {
gte: moment(now).subtract(7, 'days').toDate()
}
},
_count: {
id: true
},
_sum: {
packages_count: true,
security_count: true
}
})
]);
// Total repositories count
prisma.repositories.count(),
// Format OS distribution for pie chart
const osDistributionFormatted = osDistribution.map(item => ({
name: item.os_type,
count: item._count.os_type
}));
// OS distribution for pie chart
prisma.hosts.groupBy({
by: ["os_type"],
where: { status: "active" },
_count: {
os_type: true,
},
}),
// Calculate update status distribution
const updateStatusDistribution = [
{ name: 'Up to date', count: totalHosts - hostsNeedingUpdates },
{ name: 'Needs updates', count: hostsNeedingUpdates },
{ name: 'Errored', count: erroredHosts }
];
// Update trends for the last 7 days
prisma.update_history.groupBy({
by: ["timestamp"],
where: {
timestamp: {
gte: moment(now).subtract(7, "days").toDate(),
},
},
_count: {
id: true,
},
_sum: {
packages_count: true,
security_count: true,
},
}),
]);
// Package update priority distribution
const packageUpdateDistribution = [
{ name: 'Security', count: securityUpdates },
{ name: 'Regular', count: totalOutdatedPackages - securityUpdates }
];
// Format OS distribution for pie chart
const osDistributionFormatted = osDistribution.map((item) => ({
name: item.os_type,
count: item._count.os_type,
}));
res.json({
cards: {
totalHosts,
hostsNeedingUpdates,
upToDateHosts: Math.max(totalHosts - hostsNeedingUpdates, 0),
totalOutdatedPackages,
erroredHosts,
securityUpdates,
offlineHosts,
totalHostGroups,
totalUsers,
totalRepos
},
charts: {
osDistribution: osDistributionFormatted,
updateStatusDistribution,
packageUpdateDistribution
},
trends: updateTrends,
lastUpdated: now.toISOString()
});
} catch (error) {
console.error('Error fetching dashboard stats:', error);
res.status(500).json({ error: 'Failed to fetch dashboard statistics' });
}
});
// Calculate update status distribution
const updateStatusDistribution = [
{ name: "Up to date", count: totalHosts - hostsNeedingUpdates },
{ name: "Needs updates", count: hostsNeedingUpdates },
{ name: "Errored", count: erroredHosts },
];
// Package update priority distribution
const packageUpdateDistribution = [
{ name: "Security", count: securityUpdates },
{ name: "Regular", count: totalOutdatedPackages - securityUpdates },
];
res.json({
cards: {
totalHosts,
hostsNeedingUpdates,
upToDateHosts: Math.max(totalHosts - hostsNeedingUpdates, 0),
totalOutdatedPackages,
erroredHosts,
securityUpdates,
offlineHosts,
totalHostGroups,
totalUsers,
totalRepos,
},
charts: {
osDistribution: osDistributionFormatted,
updateStatusDistribution,
packageUpdateDistribution,
},
trends: updateTrends,
lastUpdated: now.toISOString(),
});
} catch (error) {
console.error("Error fetching dashboard stats:", error);
res.status(500).json({ error: "Failed to fetch dashboard statistics" });
}
},
);
// Get hosts with their update status
router.get('/hosts', authenticateToken, requireViewHosts, async (req, res) => {
try {
const hosts = await prisma.hosts.findMany({
// Show all hosts regardless of status
select: {
id: true,
friendly_name: true,
hostname: true,
ip: true,
os_type: true,
os_version: true,
last_update: true,
status: true,
agent_version: true,
auto_update: true,
host_groups: {
select: {
id: true,
name: true,
color: true
}
},
_count: {
select: {
host_packages: {
where: {
needs_update: true
}
}
}
}
},
orderBy: { last_update: 'desc' }
});
router.get("/hosts", authenticateToken, requireViewHosts, async (req, res) => {
try {
const hosts = await prisma.hosts.findMany({
// Show all hosts regardless of status
select: {
id: true,
friendly_name: true,
hostname: true,
ip: true,
os_type: true,
os_version: true,
last_update: true,
status: true,
agent_version: true,
auto_update: true,
host_groups: {
select: {
id: true,
name: true,
color: true,
},
},
_count: {
select: {
host_packages: {
where: {
needs_update: true,
},
},
},
},
},
orderBy: { last_update: "desc" },
});
// Get update counts for each host separately
const hostsWithUpdateInfo = await Promise.all(
hosts.map(async (host) => {
const updatesCount = await prisma.host_packages.count({
where: {
host_id: host.id,
needs_update: true
}
});
// Get update counts for each host separately
const hostsWithUpdateInfo = await Promise.all(
hosts.map(async (host) => {
const updatesCount = await prisma.host_packages.count({
where: {
host_id: host.id,
needs_update: true,
},
});
// Get total packages count for this host
const totalPackagesCount = await prisma.host_packages.count({
where: {
host_id: host.id
}
});
// Get total packages count for this host
const totalPackagesCount = await prisma.host_packages.count({
where: {
host_id: host.id,
},
});
// Get the agent update interval setting for stale calculation
const settings = await prisma.settings.findFirst();
const updateIntervalMinutes = settings?.update_interval || 60;
const thresholdMinutes = updateIntervalMinutes * 2;
// Get the agent update interval setting for stale calculation
const settings = await prisma.settings.findFirst();
const updateIntervalMinutes = settings?.update_interval || 60;
const thresholdMinutes = updateIntervalMinutes * 2;
// Calculate effective status based on reporting interval
const isStale = moment(host.last_update).isBefore(moment().subtract(thresholdMinutes, 'minutes'));
let effectiveStatus = host.status;
// Override status if host hasn't reported within threshold
if (isStale && host.status === 'active') {
effectiveStatus = 'inactive';
}
// Calculate effective status based on reporting interval
const isStale = moment(host.last_update).isBefore(
moment().subtract(thresholdMinutes, "minutes"),
);
let effectiveStatus = host.status;
return {
...host,
updatesCount,
totalPackagesCount,
isStale,
effectiveStatus
};
})
);
// Override status if host hasn't reported within threshold
if (isStale && host.status === "active") {
effectiveStatus = "inactive";
}
res.json(hostsWithUpdateInfo);
} catch (error) {
console.error('Error fetching hosts:', error);
res.status(500).json({ error: 'Failed to fetch hosts' });
}
return {
...host,
updatesCount,
totalPackagesCount,
isStale,
effectiveStatus,
};
}),
);
res.json(hostsWithUpdateInfo);
} catch (error) {
console.error("Error fetching hosts:", error);
res.status(500).json({ error: "Failed to fetch hosts" });
}
});
// Get packages that need updates across all hosts
router.get('/packages', authenticateToken, requireViewPackages, async (req, res) => {
try {
const packages = await prisma.packages.findMany({
where: {
host_packages: {
some: {
needs_update: true
}
}
},
select: {
id: true,
name: true,
description: true,
category: true,
latest_version: true,
host_packages: {
where: { needs_update: true },
select: {
current_version: true,
available_version: true,
is_security_update: true,
hosts: {
select: {
id: true,
friendly_name: true,
os_type: true
}
}
}
}
},
orderBy: {
name: 'asc'
}
});
router.get(
"/packages",
authenticateToken,
requireViewPackages,
async (req, res) => {
try {
const packages = await prisma.packages.findMany({
where: {
host_packages: {
some: {
needs_update: true,
},
},
},
select: {
id: true,
name: true,
description: true,
category: true,
latest_version: true,
host_packages: {
where: { needs_update: true },
select: {
current_version: true,
available_version: true,
is_security_update: true,
hosts: {
select: {
id: true,
friendly_name: true,
os_type: true,
},
},
},
},
},
orderBy: {
name: "asc",
},
});
const packagesWithHostInfo = packages.map(pkg => ({
id: pkg.id,
name: pkg.name,
description: pkg.description,
category: pkg.category,
latestVersion: pkg.latest_version,
affectedHostsCount: pkg.host_packages.length,
isSecurityUpdate: pkg.host_packages.some(hp => hp.is_security_update),
affectedHosts: pkg.host_packages.map(hp => ({
hostId: hp.hosts.id,
friendlyName: hp.hosts.friendly_name,
osType: hp.hosts.os_type,
currentVersion: hp.current_version,
availableVersion: hp.available_version,
isSecurityUpdate: hp.is_security_update
}))
}));
const packagesWithHostInfo = packages.map((pkg) => ({
id: pkg.id,
name: pkg.name,
description: pkg.description,
category: pkg.category,
latestVersion: pkg.latest_version,
affectedHostsCount: pkg.host_packages.length,
isSecurityUpdate: pkg.host_packages.some((hp) => hp.is_security_update),
affectedHosts: pkg.host_packages.map((hp) => ({
hostId: hp.hosts.id,
friendlyName: hp.hosts.friendly_name,
osType: hp.hosts.os_type,
currentVersion: hp.current_version,
availableVersion: hp.available_version,
isSecurityUpdate: hp.is_security_update,
})),
}));
res.json(packagesWithHostInfo);
} catch (error) {
console.error('Error fetching packages:', error);
res.status(500).json({ error: 'Failed to fetch packages' });
}
});
res.json(packagesWithHostInfo);
} catch (error) {
console.error("Error fetching packages:", error);
res.status(500).json({ error: "Failed to fetch packages" });
}
},
);
// Get detailed host information
router.get('/hosts/:hostId', authenticateToken, requireViewHosts, async (req, res) => {
try {
const { hostId } = req.params;
const host = await prisma.hosts.findUnique({
where: { id: hostId },
include: {
host_groups: {
select: {
id: true,
name: true,
color: true
}
},
host_packages: {
include: {
packages: true
},
orderBy: {
needs_update: 'desc'
}
},
update_history: {
orderBy: {
timestamp: 'desc'
},
take: 10
}
}
});
router.get(
"/hosts/:hostId",
authenticateToken,
requireViewHosts,
async (req, res) => {
try {
const { hostId } = req.params;
if (!host) {
return res.status(404).json({ error: 'Host not found' });
}
const host = await prisma.hosts.findUnique({
where: { id: hostId },
include: {
host_groups: {
select: {
id: true,
name: true,
color: true,
},
},
host_packages: {
include: {
packages: true,
},
orderBy: {
needs_update: "desc",
},
},
update_history: {
orderBy: {
timestamp: "desc",
},
take: 10,
},
},
});
const hostWithStats = {
...host,
stats: {
total_packages: host.host_packages.length,
outdated_packages: host.host_packages.filter(hp => hp.needs_update).length,
security_updates: host.host_packages.filter(hp => hp.needs_update && hp.is_security_update).length
}
};
if (!host) {
return res.status(404).json({ error: "Host not found" });
}
res.json(hostWithStats);
} catch (error) {
console.error('Error fetching host details:', error);
res.status(500).json({ error: 'Failed to fetch host details' });
}
});
const hostWithStats = {
...host,
stats: {
total_packages: host.host_packages.length,
outdated_packages: host.host_packages.filter((hp) => hp.needs_update)
.length,
security_updates: host.host_packages.filter(
(hp) => hp.needs_update && hp.is_security_update,
).length,
},
};
res.json(hostWithStats);
} catch (error) {
console.error("Error fetching host details:", error);
res.status(500).json({ error: "Failed to fetch host details" });
}
},
);
// Get recent users ordered by last_login desc
router.get('/recent-users', authenticateToken, requireViewUsers, async (req, res) => {
try {
const users = await prisma.users.findMany({
where: {
last_login: {
not: null
}
},
select: {
id: true,
username: true,
email: true,
role: true,
last_login: true,
created_at: true
},
orderBy: [
{ last_login: 'desc' },
{ created_at: 'desc' }
],
take: 5
});
router.get(
"/recent-users",
authenticateToken,
requireViewUsers,
async (req, res) => {
try {
const users = await prisma.users.findMany({
where: {
last_login: {
not: null,
},
},
select: {
id: true,
username: true,
email: true,
role: true,
last_login: true,
created_at: true,
},
orderBy: [{ last_login: "desc" }, { created_at: "desc" }],
take: 5,
});
res.json(users);
} catch (error) {
console.error('Error fetching recent users:', error);
res.status(500).json({ error: 'Failed to fetch recent users' });
}
});
res.json(users);
} catch (error) {
console.error("Error fetching recent users:", error);
res.status(500).json({ error: "Failed to fetch recent users" });
}
},
);
// Get recent hosts that have sent data (ordered by last_update desc)
router.get('/recent-collection', authenticateToken, requireViewHosts, async (req, res) => {
try {
const hosts = await prisma.hosts.findMany({
select: {
id: true,
friendly_name: true,
hostname: true,
last_update: true,
status: true
},
orderBy: {
last_update: 'desc'
},
take: 5
});
router.get(
"/recent-collection",
authenticateToken,
requireViewHosts,
async (req, res) => {
try {
const hosts = await prisma.hosts.findMany({
select: {
id: true,
friendly_name: true,
hostname: true,
last_update: true,
status: true,
},
orderBy: {
last_update: "desc",
},
take: 5,
});
res.json(hosts);
} catch (error) {
console.error('Error fetching recent collection:', error);
res.status(500).json({ error: 'Failed to fetch recent collection' });
}
});
res.json(hosts);
} catch (error) {
console.error("Error fetching recent collection:", error);
res.status(500).json({ error: "Failed to fetch recent collection" });
}
},
);
module.exports = router;
module.exports = router;