Refactor database model references to use consistent naming conventions and update related queries

This commit is contained in:
AdamT20054
2025-09-21 06:13:05 +01:00
parent c5ff4b346a
commit 584e5ed52b
10 changed files with 393 additions and 338 deletions

View File

@@ -17,26 +17,29 @@ const authenticateToken = async (req, res, next) => {
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key'); const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key');
// Get user from database // Get user from database
const user = await prisma.user.findUnique({ const user = await prisma.users.findUnique({
where: { id: decoded.userId }, where: { id: decoded.userId },
select: { select: {
id: true, id: true,
username: true, username: true,
email: true, email: true,
role: true, role: true,
isActive: true, is_active: true,
lastLogin: true last_login: true
} }
}); });
if (!user || !user.isActive) { if (!user || !user.is_active) {
return res.status(401).json({ error: 'Invalid or inactive user' }); return res.status(401).json({ error: 'Invalid or inactive user' });
} }
// Update last login // Update last login
await prisma.user.update({ await prisma.users.update({
where: { id: user.id }, where: { id: user.id },
data: { lastLogin: new Date() } data: {
last_login: new Date(),
updated_at: new Date()
}
}); });
req.user = user; req.user = user;
@@ -69,18 +72,18 @@ const optionalAuth = async (req, res, next) => {
if (token) { if (token) {
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key'); const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key');
const user = await prisma.user.findUnique({ const user = await prisma.users.findUnique({
where: { id: decoded.userId }, where: { id: decoded.userId },
select: { select: {
id: true, id: true,
username: true, username: true,
email: true, email: true,
role: true, role: true,
isActive: true is_active: true
} }
}); });
if (user && user.isActive) { if (user && user.is_active) {
req.user = user; req.user = user;
} }
} }

View File

@@ -6,7 +6,7 @@ const requirePermission = (permission) => {
return async (req, res, next) => { return async (req, res, next) => {
try { try {
// Get user's role permissions // Get user's role permissions
const rolePermissions = await prisma.rolePermissions.findUnique({ const rolePermissions = await prisma.role_permissions.findUnique({
where: { role: req.user.role } where: { role: req.user.role }
}); });

View File

@@ -5,6 +5,7 @@ const { PrismaClient } = require('@prisma/client');
const { body, validationResult } = require('express-validator'); const { body, validationResult } = require('express-validator');
const { authenticateToken, requireAdmin } = require('../middleware/auth'); const { authenticateToken, requireAdmin } = require('../middleware/auth');
const { requireViewUsers, requireManageUsers } = require('../middleware/permissions'); const { requireViewUsers, requireManageUsers } = require('../middleware/permissions');
const { v4: uuidv4 } = require('uuid');
const router = express.Router(); const router = express.Router();
const prisma = new PrismaClient(); const prisma = new PrismaClient();
@@ -21,7 +22,7 @@ const generateToken = (userId) => {
// Admin endpoint to list all users // Admin endpoint to list all users
router.get('/admin/users', authenticateToken, requireViewUsers, async (req, res) => { router.get('/admin/users', authenticateToken, requireViewUsers, async (req, res) => {
try { try {
const users = await prisma.user.findMany({ const users = await prisma.users.findMany({
select: { select: {
id: true, id: true,
username: true, username: true,
@@ -51,7 +52,7 @@ router.post('/admin/users', authenticateToken, requireManageUsers, [
body('password').isLength({ min: 6 }).withMessage('Password must be at least 6 characters'), body('password').isLength({ min: 6 }).withMessage('Password must be at least 6 characters'),
body('role').optional().custom(async (value) => { body('role').optional().custom(async (value) => {
if (!value) return true; // Optional field if (!value) return true; // Optional field
const rolePermissions = await prisma.rolePermissions.findUnique({ const rolePermissions = await prisma.role_permissions.findUnique({
where: { role: value } where: { role: value }
}); });
if (!rolePermissions) { if (!rolePermissions) {
@@ -69,7 +70,7 @@ router.post('/admin/users', authenticateToken, requireManageUsers, [
const { username, email, password, role = 'user' } = req.body; const { username, email, password, role = 'user' } = req.body;
// Check if user already exists // Check if user already exists
const existingUser = await prisma.user.findFirst({ const existingUser = await prisma.users.findFirst({
where: { where: {
OR: [ OR: [
{ username }, { username },
@@ -86,7 +87,7 @@ router.post('/admin/users', authenticateToken, requireManageUsers, [
const passwordHash = await bcrypt.hash(password, 12); const passwordHash = await bcrypt.hash(password, 12);
// Create user // Create user
const user = await prisma.user.create({ const user = await prisma.users.create({
data: { data: {
username, username,
email, email,
@@ -119,7 +120,7 @@ router.put('/admin/users/:userId', authenticateToken, requireManageUsers, [
body('email').optional().isEmail().withMessage('Valid email is required'), body('email').optional().isEmail().withMessage('Valid email is required'),
body('role').optional().custom(async (value) => { body('role').optional().custom(async (value) => {
if (!value) return true; // Optional field if (!value) return true; // Optional field
const rolePermissions = await prisma.rolePermissions.findUnique({ const rolePermissions = await prisma.role_permissions.findUnique({
where: { role: value } where: { role: value }
}); });
if (!rolePermissions) { if (!rolePermissions) {
@@ -146,7 +147,7 @@ router.put('/admin/users/:userId', authenticateToken, requireManageUsers, [
if (typeof isActive === 'boolean') updateData.isActive = isActive; if (typeof isActive === 'boolean') updateData.isActive = isActive;
// Check if user exists // Check if user exists
const existingUser = await prisma.user.findUnique({ const existingUser = await prisma.users.findUnique({
where: { id: userId } where: { id: userId }
}); });
@@ -156,7 +157,7 @@ router.put('/admin/users/:userId', authenticateToken, requireManageUsers, [
// Check if username/email already exists (excluding current user) // Check if username/email already exists (excluding current user)
if (username || email) { if (username || email) {
const duplicateUser = await prisma.user.findFirst({ const duplicateUser = await prisma.users.findFirst({
where: { where: {
AND: [ AND: [
{ id: { not: userId } }, { id: { not: userId } },
@@ -177,7 +178,7 @@ router.put('/admin/users/:userId', authenticateToken, requireManageUsers, [
// Prevent deactivating the last admin // Prevent deactivating the last admin
if (isActive === false && existingUser.role === 'admin') { if (isActive === false && existingUser.role === 'admin') {
const adminCount = await prisma.user.count({ const adminCount = await prisma.users.count({
where: { where: {
role: 'admin', role: 'admin',
isActive: true isActive: true
@@ -190,7 +191,7 @@ router.put('/admin/users/:userId', authenticateToken, requireManageUsers, [
} }
// Update user // Update user
const updatedUser = await prisma.user.update({ const updatedUser = await prisma.users.update({
where: { id: userId }, where: { id: userId },
data: updateData, data: updateData,
select: { select: {
@@ -226,7 +227,7 @@ router.delete('/admin/users/:userId', authenticateToken, requireManageUsers, asy
} }
// Check if user exists // Check if user exists
const user = await prisma.user.findUnique({ const user = await prisma.users.findUnique({
where: { id: userId } where: { id: userId }
}); });
@@ -236,7 +237,7 @@ router.delete('/admin/users/:userId', authenticateToken, requireManageUsers, asy
// Prevent deleting the last admin // Prevent deleting the last admin
if (user.role === 'admin') { if (user.role === 'admin') {
const adminCount = await prisma.user.count({ const adminCount = await prisma.users.count({
where: { where: {
role: 'admin', role: 'admin',
isActive: true isActive: true
@@ -249,7 +250,7 @@ router.delete('/admin/users/:userId', authenticateToken, requireManageUsers, asy
} }
// Delete user // Delete user
await prisma.user.delete({ await prisma.users.delete({
where: { id: userId } where: { id: userId }
}); });
@@ -277,14 +278,14 @@ router.post('/admin/users/:userId/reset-password', authenticateToken, requireMan
const { newPassword } = req.body; const { newPassword } = req.body;
// Check if user exists // Check if user exists
const user = await prisma.user.findUnique({ const user = await prisma.users.findUnique({
where: { id: userId }, where: { id: userId },
select: { select: {
id: true, id: true,
username: true, username: true,
email: true, email: true,
role: true, role: true,
isActive: true is_active: true
} }
}); });
@@ -293,7 +294,7 @@ router.post('/admin/users/:userId/reset-password', authenticateToken, requireMan
} }
// Prevent resetting password of inactive users // Prevent resetting password of inactive users
if (!user.isActive) { if (!user.is_active) {
return res.status(400).json({ error: 'Cannot reset password for inactive user' }); return res.status(400).json({ error: 'Cannot reset password for inactive user' });
} }
@@ -301,7 +302,7 @@ router.post('/admin/users/:userId/reset-password', authenticateToken, requireMan
const passwordHash = await bcrypt.hash(newPassword, 12); const passwordHash = await bcrypt.hash(newPassword, 12);
// Update user password // Update user password
await prisma.user.update({ await prisma.users.update({
where: { id: userId }, where: { id: userId },
data: { passwordHash } data: { passwordHash }
}); });
@@ -338,7 +339,7 @@ router.post('/signup', [
const { username, email, password } = req.body; const { username, email, password } = req.body;
// Check if user already exists // Check if user already exists
const existingUser = await prisma.user.findFirst({ const existingUser = await prisma.users.findFirst({
where: { where: {
OR: [ OR: [
{ username }, { username },
@@ -355,20 +356,22 @@ router.post('/signup', [
const passwordHash = await bcrypt.hash(password, 12); const passwordHash = await bcrypt.hash(password, 12);
// Create user with default 'user' role // Create user with default 'user' role
const user = await prisma.user.create({ const user = await prisma.users.create({
data: { data: {
id: uuidv4(),
username, username,
email, email,
passwordHash, password_hash: passwordHash,
role: 'user' role: 'user',
updated_at: new Date()
}, },
select: { select: {
id: true, id: true,
username: true, username: true,
email: true, email: true,
role: true, role: true,
isActive: true, is_active: true,
createdAt: true created_at: true
} }
}); });
@@ -389,6 +392,8 @@ router.post('/signup', [
}); });
} catch (error) { } catch (error) {
console.error('Signup error:', error); console.error('Signup error:', error);
console.error('Signup error message:', error.message);
console.error('Signup error stack:', error.stack);
res.status(500).json({ error: 'Failed to create account' }); res.status(500).json({ error: 'Failed to create account' });
} }
}); });
@@ -407,21 +412,21 @@ router.post('/login', [
const { username, password } = req.body; const { username, password } = req.body;
// Find user by username or email // Find user by username or email
const user = await prisma.user.findFirst({ const user = await prisma.users.findFirst({
where: { where: {
OR: [ OR: [
{ username }, { username },
{ email: username } { email: username }
], ],
isActive: true is_active: true
}, },
select: { select: {
id: true, id: true,
username: true, username: true,
email: true, email: true,
passwordHash: true, password_hash: true,
role: true, role: true,
tfaEnabled: true tfa_enabled: true
} }
}); });
@@ -430,13 +435,13 @@ router.post('/login', [
} }
// Verify password // Verify password
const isValidPassword = await bcrypt.compare(password, user.passwordHash); const isValidPassword = await bcrypt.compare(password, user.password_hash);
if (!isValidPassword) { if (!isValidPassword) {
return res.status(401).json({ error: 'Invalid credentials' }); return res.status(401).json({ error: 'Invalid credentials' });
} }
// Check if TFA is enabled // Check if TFA is enabled
if (user.tfaEnabled) { if (user.tfa_enabled) {
return res.status(200).json({ return res.status(200).json({
message: 'TFA verification required', message: 'TFA verification required',
requiresTfa: true, requiresTfa: true,
@@ -445,9 +450,12 @@ router.post('/login', [
} }
// Update last login // Update last login
await prisma.user.update({ await prisma.users.update({
where: { id: user.id }, where: { id: user.id },
data: { lastLogin: new Date() } data: {
last_login: new Date(),
updated_at: new Date()
}
}); });
// Generate token // Generate token
@@ -484,22 +492,22 @@ router.post('/verify-tfa', [
const { username, token } = req.body; const { username, token } = req.body;
// Find user // Find user
const user = await prisma.user.findFirst({ const user = await prisma.users.findFirst({
where: { where: {
OR: [ OR: [
{ username }, { username },
{ email: username } { email: username }
], ],
isActive: true, is_active: true,
tfaEnabled: true tfa_enabled: true
}, },
select: { select: {
id: true, id: true,
username: true, username: true,
email: true, email: true,
role: true, role: true,
tfaSecret: true, tfa_secret: true,
tfaBackupCodes: true tfa_backup_codes: true
} }
}); });

View File

@@ -9,8 +9,8 @@ const prisma = new PrismaClient();
// Get user's dashboard preferences // Get user's dashboard preferences
router.get('/', authenticateToken, async (req, res) => { router.get('/', authenticateToken, async (req, res) => {
try { try {
const preferences = await prisma.dashboardPreferences.findMany({ const preferences = await prisma.dashboard_preferences.findMany({
where: { userId: req.user.id }, where: { user_id: req.user.id },
orderBy: { order: 'asc' } orderBy: { order: 'asc' }
}); });
@@ -38,19 +38,21 @@ router.put('/', authenticateToken, [
const userId = req.user.id; const userId = req.user.id;
// Delete existing preferences for this user // Delete existing preferences for this user
await prisma.dashboardPreferences.deleteMany({ await prisma.dashboard_preferences.deleteMany({
where: { userId } where: { user_id: userId }
}); });
// Create new preferences // Create new preferences
const newPreferences = preferences.map(pref => ({ const newPreferences = preferences.map(pref => ({
userId, id: require('uuid').v4(),
cardId: pref.cardId, user_id: userId,
card_id: pref.cardId,
enabled: pref.enabled, enabled: pref.enabled,
order: pref.order order: pref.order,
updated_at: new Date()
})); }));
const createdPreferences = await prisma.dashboardPreferences.createMany({ const createdPreferences = await prisma.dashboard_preferences.createMany({
data: newPreferences data: newPreferences
}); });

View File

@@ -18,7 +18,7 @@ router.get('/stats', authenticateToken, requireViewDashboard, async (req, res) =
// Get the agent update interval setting // Get the agent update interval setting
const settings = await prisma.settings.findFirst(); const settings = await prisma.settings.findFirst();
const updateIntervalMinutes = settings?.updateInterval || 60; // Default to 60 minutes if no setting const updateIntervalMinutes = settings?.update_interval || 60; // Default to 60 minutes if no setting
// Calculate the threshold based on the actual update interval // Calculate the threshold based on the actual update interval
// Use 2x the update interval as the threshold for "errored" hosts // Use 2x the update interval as the threshold for "errored" hosts
@@ -37,66 +37,66 @@ router.get('/stats', authenticateToken, requireViewDashboard, async (req, res) =
updateTrends updateTrends
] = await Promise.all([ ] = await Promise.all([
// Total hosts count // Total hosts count
prisma.host.count({ prisma.hosts.count({
where: { status: 'active' } where: { status: 'active' }
}), }),
// Hosts needing updates (distinct hosts with packages needing updates) // Hosts needing updates (distinct hosts with packages needing updates)
prisma.host.count({ prisma.hosts.count({
where: { where: {
status: 'active', status: 'active',
hostPackages: { host_packages: {
some: { some: {
needsUpdate: true needs_update: true
} }
} }
} }
}), }),
// Total outdated packages across all hosts // Total outdated packages across all hosts
prisma.hostPackage.count({ prisma.host_packages.count({
where: { needsUpdate: true } where: { needs_update: true }
}), }),
// Errored hosts (not updated within threshold based on update interval) // Errored hosts (not updated within threshold based on update interval)
prisma.host.count({ prisma.hosts.count({
where: { where: {
status: 'active', status: 'active',
lastUpdate: { last_update: {
lt: thresholdTime lt: thresholdTime
} }
} }
}), }),
// Security updates count // Security updates count
prisma.hostPackage.count({ prisma.host_packages.count({
where: { where: {
needsUpdate: true, needs_update: true,
isSecurityUpdate: true is_security_update: true
} }
}), }),
// Offline/Stale hosts (not updated within 3x the update interval) // Offline/Stale hosts (not updated within 3x the update interval)
prisma.host.count({ prisma.hosts.count({
where: { where: {
status: 'active', status: 'active',
lastUpdate: { last_update: {
lt: moment(now).subtract(updateIntervalMinutes * 3, 'minutes').toDate() lt: moment(now).subtract(updateIntervalMinutes * 3, 'minutes').toDate()
} }
} }
}), }),
// OS distribution for pie chart // OS distribution for pie chart
prisma.host.groupBy({ prisma.hosts.groupBy({
by: ['osType'], by: ['os_type'],
where: { status: 'active' }, where: { status: 'active' },
_count: { _count: {
osType: true os_type: true
} }
}), }),
// Update trends for the last 7 days // Update trends for the last 7 days
prisma.updateHistory.groupBy({ prisma.update_history.groupBy({
by: ['timestamp'], by: ['timestamp'],
where: { where: {
timestamp: { timestamp: {
@@ -107,16 +107,16 @@ router.get('/stats', authenticateToken, requireViewDashboard, async (req, res) =
id: true id: true
}, },
_sum: { _sum: {
packagesCount: true, packages_count: true,
securityCount: true security_count: true
} }
}) })
]); ]);
// Format OS distribution for pie chart // Format OS distribution for pie chart
const osDistributionFormatted = osDistribution.map(item => ({ const osDistributionFormatted = osDistribution.map(item => ({
name: item.osType, name: item.os_type,
count: item._count.osType count: item._count.os_type
})); }));
// Calculate update status distribution // Calculate update status distribution
@@ -158,7 +158,7 @@ router.get('/stats', authenticateToken, requireViewDashboard, async (req, res) =
// Get hosts with their update status // Get hosts with their update status
router.get('/hosts', authenticateToken, requireViewHosts, async (req, res) => { router.get('/hosts', authenticateToken, requireViewHosts, async (req, res) => {
try { try {
const hosts = await prisma.host.findMany({ const hosts = await prisma.hosts.findMany({
// Show all hosts regardless of status // Show all hosts regardless of status
select: { select: {
id: true, id: true,
@@ -194,7 +194,7 @@ router.get('/hosts', authenticateToken, requireViewHosts, async (req, res) => {
// Get update counts for each host separately // Get update counts for each host separately
const hostsWithUpdateInfo = await Promise.all( const hostsWithUpdateInfo = await Promise.all(
hosts.map(async (host) => { hosts.map(async (host) => {
const updatesCount = await prisma.hostPackage.count({ const updatesCount = await prisma.host_packages.count({
where: { where: {
hostId: host.id, hostId: host.id,
needsUpdate: true needsUpdate: true
@@ -202,7 +202,7 @@ router.get('/hosts', authenticateToken, requireViewHosts, async (req, res) => {
}); });
// Get total packages count for this host // Get total packages count for this host
const totalPackagesCount = await prisma.hostPackage.count({ const totalPackagesCount = await prisma.host_packages.count({
where: { where: {
hostId: host.id hostId: host.id
} }
@@ -210,11 +210,11 @@ router.get('/hosts', authenticateToken, requireViewHosts, async (req, res) => {
// Get the agent update interval setting for stale calculation // Get the agent update interval setting for stale calculation
const settings = await prisma.settings.findFirst(); const settings = await prisma.settings.findFirst();
const updateIntervalMinutes = settings?.updateInterval || 60; const updateIntervalMinutes = settings?.update_interval || 60;
const thresholdMinutes = updateIntervalMinutes * 2; const thresholdMinutes = updateIntervalMinutes * 2;
// Calculate effective status based on reporting interval // Calculate effective status based on reporting interval
const isStale = moment(host.lastUpdate).isBefore(moment().subtract(thresholdMinutes, 'minutes')); const isStale = moment(host.last_update).isBefore(moment().subtract(thresholdMinutes, 'minutes'));
let effectiveStatus = host.status; let effectiveStatus = host.status;
// Override status if host hasn't reported within threshold // Override status if host hasn't reported within threshold
@@ -242,7 +242,7 @@ router.get('/hosts', authenticateToken, requireViewHosts, async (req, res) => {
// Get packages that need updates across all hosts // Get packages that need updates across all hosts
router.get('/packages', authenticateToken, requireViewPackages, async (req, res) => { router.get('/packages', authenticateToken, requireViewPackages, async (req, res) => {
try { try {
const packages = await prisma.package.findMany({ const packages = await prisma.packages.findMany({
where: { where: {
hostPackages: { hostPackages: {
some: { some: {
@@ -282,10 +282,10 @@ router.get('/packages', authenticateToken, requireViewPackages, async (req, res)
name: pkg.name, name: pkg.name,
description: pkg.description, description: pkg.description,
category: pkg.category, category: pkg.category,
latestVersion: pkg.latestVersion, latestVersion: pkg.latest_version,
affectedHostsCount: pkg.hostPackages.length, affectedHostsCount: pkg.host_packages.length,
isSecurityUpdate: pkg.hostPackages.some(hp => hp.isSecurityUpdate), isSecurityUpdate: pkg.host_packages.some(hp => hp.isSecurityUpdate),
affectedHosts: pkg.hostPackages.map(hp => ({ affectedHosts: pkg.host_packages.map(hp => ({
hostId: hp.host.id, hostId: hp.host.id,
friendlyName: hp.host.friendlyName, friendlyName: hp.host.friendlyName,
osType: hp.host.osType, osType: hp.host.osType,
@@ -307,7 +307,7 @@ router.get('/hosts/:hostId', authenticateToken, requireViewHosts, async (req, re
try { try {
const { hostId } = req.params; const { hostId } = req.params;
const host = await prisma.host.findUnique({ const host = await prisma.hosts.findUnique({
where: { id: hostId }, where: { id: hostId },
include: { include: {
hostGroup: { hostGroup: {
@@ -341,9 +341,9 @@ router.get('/hosts/:hostId', authenticateToken, requireViewHosts, async (req, re
const hostWithStats = { const hostWithStats = {
...host, ...host,
stats: { stats: {
totalPackages: host.hostPackages.length, totalPackages: host.host_packages.length,
outdatedPackages: host.hostPackages.filter(hp => hp.needsUpdate).length, outdatedPackages: host.host_packages.filter(hp => hp.needsUpdate).length,
securityUpdates: host.hostPackages.filter(hp => hp.needsUpdate && hp.isSecurityUpdate).length securityUpdates: host.host_packages.filter(hp => hp.needsUpdate && hp.isSecurityUpdate).length
} }
}; };

View File

@@ -10,7 +10,7 @@ const prisma = new PrismaClient();
// Get all host groups // Get all host groups
router.get('/', authenticateToken, async (req, res) => { router.get('/', authenticateToken, async (req, res) => {
try { try {
const hostGroups = await prisma.hostGroup.findMany({ const hostGroups = await prisma.host_groups.findMany({
include: { include: {
_count: { _count: {
select: { select: {
@@ -35,7 +35,7 @@ router.get('/:id', authenticateToken, async (req, res) => {
try { try {
const { id } = req.params; const { id } = req.params;
const hostGroup = await prisma.hostGroup.findUnique({ const hostGroup = await prisma.host_groups.findUnique({
where: { id }, where: { id },
include: { include: {
hosts: { hosts: {
@@ -79,7 +79,7 @@ router.post('/', authenticateToken, requireManageHosts, [
const { name, description, color } = req.body; const { name, description, color } = req.body;
// Check if host group with this name already exists // Check if host group with this name already exists
const existingGroup = await prisma.hostGroup.findUnique({ const existingGroup = await prisma.host_groups.findUnique({
where: { name } where: { name }
}); });
@@ -87,7 +87,7 @@ router.post('/', authenticateToken, requireManageHosts, [
return res.status(400).json({ error: 'A host group with this name already exists' }); return res.status(400).json({ error: 'A host group with this name already exists' });
} }
const hostGroup = await prisma.hostGroup.create({ const hostGroup = await prisma.host_groups.create({
data: { data: {
name, name,
description: description || null, description: description || null,
@@ -118,7 +118,7 @@ router.put('/:id', authenticateToken, requireManageHosts, [
const { name, description, color } = req.body; const { name, description, color } = req.body;
// Check if host group exists // Check if host group exists
const existingGroup = await prisma.hostGroup.findUnique({ const existingGroup = await prisma.host_groups.findUnique({
where: { id } where: { id }
}); });
@@ -127,7 +127,7 @@ router.put('/:id', authenticateToken, requireManageHosts, [
} }
// Check if another host group with this name already exists // Check if another host group with this name already exists
const duplicateGroup = await prisma.hostGroup.findFirst({ const duplicateGroup = await prisma.host_groups.findFirst({
where: { where: {
name, name,
id: { not: id } id: { not: id }
@@ -138,7 +138,7 @@ router.put('/:id', authenticateToken, requireManageHosts, [
return res.status(400).json({ error: 'A host group with this name already exists' }); return res.status(400).json({ error: 'A host group with this name already exists' });
} }
const hostGroup = await prisma.hostGroup.update({ const hostGroup = await prisma.host_groups.update({
where: { id }, where: { id },
data: { data: {
name, name,
@@ -160,7 +160,7 @@ router.delete('/:id', authenticateToken, requireManageHosts, async (req, res) =>
const { id } = req.params; const { id } = req.params;
// Check if host group exists // Check if host group exists
const existingGroup = await prisma.hostGroup.findUnique({ const existingGroup = await prisma.host_groups.findUnique({
where: { id }, where: { id },
include: { include: {
_count: { _count: {
@@ -182,7 +182,7 @@ router.delete('/:id', authenticateToken, requireManageHosts, async (req, res) =>
}); });
} }
await prisma.hostGroup.delete({ await prisma.host_groups.delete({
where: { id } where: { id }
}); });
@@ -198,7 +198,7 @@ router.get('/:id/hosts', authenticateToken, async (req, res) => {
try { try {
const { id } = req.params; const { id } = req.params;
const hosts = await prisma.host.findMany({ const hosts = await prisma.hosts.findMany({
where: { hostGroupId: id }, where: { hostGroupId: id },
select: { select: {
id: true, id: true,

View File

@@ -21,7 +21,7 @@ router.get('/agent/download', async (req, res) => {
if (version) { if (version) {
// Download specific version // Download specific version
agentVersion = await prisma.agentVersion.findUnique({ agentVersion = await prisma.agent_versions.findUnique({
where: { version } where: { version }
}); });
@@ -30,16 +30,16 @@ router.get('/agent/download', async (req, res) => {
} }
} else { } else {
// Download current version (latest) // Download current version (latest)
agentVersion = await prisma.agentVersion.findFirst({ agentVersion = await prisma.agent_versions.findFirst({
where: { isCurrent: true }, where: { is_current: true },
orderBy: { createdAt: 'desc' } orderBy: { created_at: 'desc' }
}); });
if (!agentVersion) { if (!agentVersion) {
// Fallback to default version // Fallback to default version
agentVersion = await prisma.agentVersion.findFirst({ agentVersion = await prisma.agent_versions.findFirst({
where: { isDefault: true }, where: { is_default: true },
orderBy: { createdAt: 'desc' } orderBy: { created_at: 'desc' }
}); });
} }
} }
@@ -49,10 +49,10 @@ router.get('/agent/download', async (req, res) => {
} }
// Use script content from database if available, otherwise fallback to file // Use script content from database if available, otherwise fallback to file
if (agentVersion.scriptContent) { if (agentVersion.script_content) {
res.setHeader('Content-Type', 'application/x-shellscript'); res.setHeader('Content-Type', 'application/x-shellscript');
res.setHeader('Content-Disposition', `attachment; filename="patchmon-agent-${agentVersion.version}.sh"`); res.setHeader('Content-Disposition', `attachment; filename="patchmon-agent-${agentVersion.version}.sh"`);
res.send(agentVersion.scriptContent); res.send(agentVersion.script_content);
} else { } else {
// Fallback to file system // Fallback to file system
const agentPath = path.join(__dirname, '../../../agents/patchmon-agent.sh'); const agentPath = path.join(__dirname, '../../../agents/patchmon-agent.sh');
@@ -74,9 +74,9 @@ router.get('/agent/download', async (req, res) => {
// Version check endpoint for agents // Version check endpoint for agents
router.get('/agent/version', async (req, res) => { router.get('/agent/version', async (req, res) => {
try { try {
const currentVersion = await prisma.agentVersion.findFirst({ const currentVersion = await prisma.agent_versions.findFirst({
where: { isCurrent: true }, where: { is_current: true },
orderBy: { createdAt: 'desc' } orderBy: { created_at: 'desc' }
}); });
if (!currentVersion) { if (!currentVersion) {
@@ -85,9 +85,9 @@ router.get('/agent/version', async (req, res) => {
res.json({ res.json({
currentVersion: currentVersion.version, currentVersion: currentVersion.version,
downloadUrl: currentVersion.downloadUrl || `/api/v1/hosts/agent/download`, downloadUrl: currentVersion.download_url || `/api/v1/hosts/agent/download`,
releaseNotes: currentVersion.releaseNotes, releaseNotes: currentVersion.release_notes,
minServerVersion: currentVersion.minServerVersion minServerVersion: currentVersion.min_server_version
}); });
} catch (error) { } catch (error) {
console.error('Version check error:', error); console.error('Version check error:', error);
@@ -112,10 +112,10 @@ const validateApiCredentials = async (req, res, next) => {
return res.status(401).json({ error: 'API ID and Key required' }); return res.status(401).json({ error: 'API ID and Key required' });
} }
const host = await prisma.host.findFirst({ const host = await prisma.hosts.findFirst({
where: { where: {
apiId: apiId, api_id: apiId,
apiKey: apiKey api_key: apiKey
} }
}); });
@@ -148,8 +148,8 @@ router.post('/create', authenticateToken, requireManageHosts, [
const { apiId, apiKey } = generateApiCredentials(); const { apiId, apiKey } = generateApiCredentials();
// Check if host already exists // Check if host already exists
const existingHost = await prisma.host.findUnique({ const existingHost = await prisma.hosts.findUnique({
where: { friendlyName } where: { friendly_name: friendlyName }
}); });
if (existingHost) { if (existingHost) {
@@ -158,7 +158,7 @@ router.post('/create', authenticateToken, requireManageHosts, [
// If hostGroupId is provided, verify the group exists // If hostGroupId is provided, verify the group exists
if (hostGroupId) { if (hostGroupId) {
const hostGroup = await prisma.hostGroup.findUnique({ const hostGroup = await prisma.host_groups.findUnique({
where: { id: hostGroupId } where: { id: hostGroupId }
}); });
@@ -168,20 +168,22 @@ router.post('/create', authenticateToken, requireManageHosts, [
} }
// Create new host with API credentials - system info will be populated when agent connects // Create new host with API credentials - system info will be populated when agent connects
const host = await prisma.host.create({ const host = await prisma.hosts.create({
data: { data: {
friendlyName, id: uuidv4(),
osType: 'unknown', // Will be updated when agent connects friendly_name: friendlyName,
osVersion: 'unknown', // Will be updated when agent connects os_type: 'unknown', // Will be updated when agent connects
os_version: 'unknown', // Will be updated when agent connects
ip: null, // Will be updated when agent connects ip: null, // Will be updated when agent connects
architecture: null, // Will be updated when agent connects architecture: null, // Will be updated when agent connects
apiId, api_id: apiId,
apiKey, api_key: apiKey,
hostGroupId: hostGroupId || null, host_group_id: hostGroupId || null,
status: 'pending' // Will change to 'active' when agent connects status: 'pending', // Will change to 'active' when agent connects
updated_at: new Date()
}, },
include: { include: {
hostGroup: { host_groups: {
select: { select: {
id: true, id: true,
name: true, name: true,
@@ -194,10 +196,10 @@ router.post('/create', authenticateToken, requireManageHosts, [
res.status(201).json({ res.status(201).json({
message: 'Host created successfully', message: 'Host created successfully',
hostId: host.id, hostId: host.id,
friendlyName: host.friendlyName, friendlyName: host.friendly_name,
apiId: host.apiId, apiId: host.api_id,
apiKey: host.apiKey, apiKey: host.api_key,
hostGroup: host.hostGroup, hostGroup: host.host_groups,
instructions: 'Use these credentials in your patchmon agent configuration. System information will be automatically detected when the agent connects.' instructions: 'Use these credentials in your patchmon agent configuration. System information will be automatically detected when the agent connects.'
}); });
} catch (error) { } catch (error) {
@@ -250,40 +252,43 @@ router.post('/update', validateApiCredentials, [
const host = req.hostRecord; const host = req.hostRecord;
// Update host last update timestamp and system info if provided // Update host last update timestamp and system info if provided
const updateData = { lastUpdate: new Date() }; const updateData = {
last_update: new Date(),
updated_at: new Date()
};
// Basic system info // Basic system info
if (req.body.osType) updateData.osType = req.body.osType; if (req.body.osType) updateData.os_type = req.body.osType;
if (req.body.osVersion) updateData.osVersion = req.body.osVersion; if (req.body.osVersion) updateData.os_version = req.body.osVersion;
if (req.body.hostname) updateData.hostname = req.body.hostname; if (req.body.hostname) updateData.hostname = req.body.hostname;
if (req.body.ip) updateData.ip = req.body.ip; if (req.body.ip) updateData.ip = req.body.ip;
if (req.body.architecture) updateData.architecture = req.body.architecture; if (req.body.architecture) updateData.architecture = req.body.architecture;
if (req.body.agentVersion) updateData.agentVersion = req.body.agentVersion; if (req.body.agentVersion) updateData.agent_version = req.body.agentVersion;
// Hardware Information // Hardware Information
if (req.body.cpuModel) updateData.cpuModel = req.body.cpuModel; if (req.body.cpuModel) updateData.cpu_model = req.body.cpuModel;
if (req.body.cpuCores) updateData.cpuCores = req.body.cpuCores; if (req.body.cpuCores) updateData.cpu_cores = req.body.cpuCores;
if (req.body.ramInstalled) updateData.ramInstalled = req.body.ramInstalled; if (req.body.ramInstalled) updateData.ram_installed = req.body.ramInstalled;
if (req.body.swapSize !== undefined) updateData.swapSize = req.body.swapSize; if (req.body.swapSize !== undefined) updateData.swap_size = req.body.swapSize;
if (req.body.diskDetails) updateData.diskDetails = req.body.diskDetails; if (req.body.diskDetails) updateData.disk_details = req.body.diskDetails;
// Network Information // Network Information
if (req.body.gatewayIp) updateData.gatewayIp = req.body.gatewayIp; if (req.body.gatewayIp) updateData.gateway_ip = req.body.gatewayIp;
if (req.body.dnsServers) updateData.dnsServers = req.body.dnsServers; if (req.body.dnsServers) updateData.dns_servers = req.body.dnsServers;
if (req.body.networkInterfaces) updateData.networkInterfaces = req.body.networkInterfaces; if (req.body.networkInterfaces) updateData.network_interfaces = req.body.networkInterfaces;
// System Information // System Information
if (req.body.kernelVersion) updateData.kernelVersion = req.body.kernelVersion; if (req.body.kernelVersion) updateData.kernel_version = req.body.kernelVersion;
if (req.body.selinuxStatus) updateData.selinuxStatus = req.body.selinuxStatus; if (req.body.selinuxStatus) updateData.selinux_status = req.body.selinuxStatus;
if (req.body.systemUptime) updateData.systemUptime = req.body.systemUptime; if (req.body.systemUptime) updateData.system_uptime = req.body.systemUptime;
if (req.body.loadAverage) updateData.loadAverage = req.body.loadAverage; if (req.body.loadAverage) updateData.load_average = req.body.loadAverage;
// If this is the first update (status is 'pending'), change to 'active' // If this is the first update (status is 'pending'), change to 'active'
if (host.status === 'pending') { if (host.status === 'pending') {
updateData.status = 'active'; updateData.status = 'active';
} }
await prisma.host.update({ await prisma.hosts.update({
where: { id: host.id }, where: { id: host.id },
data: updateData data: updateData
}); });
@@ -291,46 +296,52 @@ router.post('/update', validateApiCredentials, [
// Process packages in transaction // Process packages in transaction
await prisma.$transaction(async (tx) => { await prisma.$transaction(async (tx) => {
// Clear existing host packages // Clear existing host packages
await tx.hostPackage.deleteMany({ await tx.host_packages.deleteMany({
where: { hostId: host.id } where: { host_id: host.id }
}); });
// Process each package // Process each package
for (const packageData of packages) { for (const packageData of packages) {
// Find or create package // Find or create package
let pkg = await tx.package.findUnique({ let pkg = await tx.packages.findUnique({
where: { name: packageData.name } where: { name: packageData.name }
}); });
if (!pkg) { if (!pkg) {
pkg = await tx.package.create({ pkg = await tx.packages.create({
data: { data: {
id: uuidv4(),
name: packageData.name, name: packageData.name,
description: packageData.description || null, description: packageData.description || null,
category: packageData.category || null, category: packageData.category || null,
latestVersion: packageData.availableVersion || packageData.currentVersion latest_version: packageData.availableVersion || packageData.currentVersion,
updated_at: new Date()
} }
}); });
} else { } else {
// Update package latest version if newer // Update package latest version if newer
if (packageData.availableVersion && packageData.availableVersion !== pkg.latestVersion) { if (packageData.availableVersion && packageData.availableVersion !== pkg.latest_version) {
await tx.package.update({ await tx.packages.update({
where: { id: pkg.id }, where: { id: pkg.id },
data: { latestVersion: packageData.availableVersion } data: {
latest_version: packageData.availableVersion,
updated_at: new Date()
}
}); });
} }
} }
// Create host package relationship // Create host package relationship
await tx.hostPackage.create({ await tx.host_packages.create({
data: { data: {
hostId: host.id, id: uuidv4(),
packageId: pkg.id, host_id: host.id,
currentVersion: packageData.currentVersion, package_id: pkg.id,
availableVersion: packageData.availableVersion || null, current_version: packageData.currentVersion,
needsUpdate: packageData.needsUpdate, available_version: packageData.availableVersion || null,
isSecurityUpdate: packageData.isSecurityUpdate || false, needs_update: packageData.needsUpdate,
lastChecked: new Date() is_security_update: packageData.isSecurityUpdate || false,
last_checked: new Date()
} }
}); });
} }
@@ -338,8 +349,8 @@ router.post('/update', validateApiCredentials, [
// Process repositories if provided // Process repositories if provided
if (repositories && Array.isArray(repositories)) { if (repositories && Array.isArray(repositories)) {
// Clear existing host repositories // Clear existing host repositories
await tx.hostRepository.deleteMany({ await tx.host_repositories.deleteMany({
where: { hostId: host.id } where: { host_id: host.id }
}); });
// Deduplicate repositories by URL+distribution+components to avoid constraint violations // Deduplicate repositories by URL+distribution+components to avoid constraint violations
@@ -354,7 +365,7 @@ router.post('/update', validateApiCredentials, [
// Process each unique repository // Process each unique repository
for (const repoData of uniqueRepos.values()) { for (const repoData of uniqueRepos.values()) {
// Find or create repository // Find or create repository
let repo = await tx.repository.findFirst({ let repo = await tx.repositories.findFirst({
where: { where: {
url: repoData.url, url: repoData.url,
distribution: repoData.distribution, distribution: repoData.distribution,
@@ -363,27 +374,30 @@ router.post('/update', validateApiCredentials, [
}); });
if (!repo) { if (!repo) {
repo = await tx.repository.create({ repo = await tx.repositories.create({
data: { data: {
id: uuidv4(),
name: repoData.name, name: repoData.name,
url: repoData.url, url: repoData.url,
distribution: repoData.distribution, distribution: repoData.distribution,
components: repoData.components, components: repoData.components,
repoType: repoData.repoType, repo_type: repoData.repoType,
isActive: true, is_active: true,
isSecure: repoData.isSecure || false, is_secure: repoData.isSecure || false,
description: `${repoData.repoType} repository for ${repoData.distribution}` description: `${repoData.repoType} repository for ${repoData.distribution}`,
updated_at: new Date()
} }
}); });
} }
// Create host repository relationship // Create host repository relationship
await tx.hostRepository.create({ await tx.host_repositories.create({
data: { data: {
hostId: host.id, id: uuidv4(),
repositoryId: repo.id, host_id: host.id,
isEnabled: repoData.isEnabled !== false, // Default to enabled repository_id: repo.id,
lastChecked: new Date() is_enabled: repoData.isEnabled !== false, // Default to enabled
last_checked: new Date()
} }
}); });
} }
@@ -394,11 +408,12 @@ router.post('/update', validateApiCredentials, [
const securityCount = packages.filter(pkg => pkg.isSecurityUpdate).length; const securityCount = packages.filter(pkg => pkg.isSecurityUpdate).length;
const updatesCount = packages.filter(pkg => pkg.needsUpdate).length; const updatesCount = packages.filter(pkg => pkg.needsUpdate).length;
await prisma.updateHistory.create({ await prisma.update_history.create({
data: { data: {
hostId: host.id, id: uuidv4(),
packagesCount: updatesCount, host_id: host.id,
securityCount, packages_count: updatesCount,
security_count: securityCount,
status: 'success' status: 'success'
} }
}); });
@@ -408,15 +423,15 @@ router.post('/update', validateApiCredentials, [
try { try {
const settings = await prisma.settings.findFirst(); const settings = await prisma.settings.findFirst();
// Check both global auto-update setting AND host-specific auto-update setting // Check both global auto-update setting AND host-specific auto-update setting
if (settings && settings.autoUpdate && host.autoUpdate) { if (settings && settings.auto_update && host.auto_update) {
// Get current agent version from the request // Get current agent version from the request
const currentAgentVersion = req.body.agentVersion; const currentAgentVersion = req.body.agentVersion;
if (currentAgentVersion) { if (currentAgentVersion) {
// Get the latest agent version // Get the latest agent version
const latestAgentVersion = await prisma.agentVersion.findFirst({ const latestAgentVersion = await prisma.agent_versions.findFirst({
where: { isCurrent: true }, where: { is_current: true },
orderBy: { createdAt: 'desc' } orderBy: { created_at: 'desc' }
}); });
if (latestAgentVersion && latestAgentVersion.version !== currentAgentVersion) { if (latestAgentVersion && latestAgentVersion.version !== currentAgentVersion) {
@@ -450,7 +465,7 @@ router.post('/update', validateApiCredentials, [
// Check if crontab update is needed (when update interval changes) // Check if crontab update is needed (when update interval changes)
// This is a simple check - if the host has auto-update enabled, we'll suggest crontab update // This is a simple check - if the host has auto-update enabled, we'll suggest crontab update
if (host.autoUpdate) { if (host.auto_update) {
// For now, we'll always suggest crontab update to ensure it's current // For now, we'll always suggest crontab update to ensure it's current
// In a more sophisticated implementation, we could track when the interval last changed // In a more sophisticated implementation, we could track when the interval last changed
response.crontabUpdate = { response.crontabUpdate = {
@@ -466,13 +481,14 @@ router.post('/update', validateApiCredentials, [
// Log error in update history // Log error in update history
try { try {
await prisma.updateHistory.create({ await prisma.update_history.create({
data: { data: {
hostId: req.hostRecord.id, id: uuidv4(),
packagesCount: 0, host_id: req.hostRecord.id,
securityCount: 0, packages_count: 0,
security_count: 0,
status: 'error', status: 'error',
errorMessage: error.message error_message: error.message
} }
}); });
} catch (logError) { } catch (logError) {
@@ -486,20 +502,20 @@ router.post('/update', validateApiCredentials, [
// Get host information (now uses API credentials) // Get host information (now uses API credentials)
router.get('/info', validateApiCredentials, async (req, res) => { router.get('/info', validateApiCredentials, async (req, res) => {
try { try {
const host = await prisma.host.findUnique({ const host = await prisma.hosts.findUnique({
where: { id: req.hostRecord.id }, where: { id: req.hostRecord.id },
select: { select: {
id: true, id: true,
friendlyName: true, friendly_name: true,
hostname: true, hostname: true,
ip: true, ip: true,
osType: true, os_type: true,
osVersion: true, os_version: true,
architecture: true, architecture: true,
lastUpdate: true, last_update: true,
status: true, status: true,
createdAt: true, created_at: true,
apiId: true // Include API ID for reference api_id: true // Include API ID for reference
} }
}); });
@@ -514,20 +530,23 @@ router.get('/info', validateApiCredentials, async (req, res) => {
router.post('/ping', validateApiCredentials, async (req, res) => { router.post('/ping', validateApiCredentials, async (req, res) => {
try { try {
// Update last update timestamp // Update last update timestamp
await prisma.host.update({ await prisma.hosts.update({
where: { id: req.hostRecord.id }, where: { id: req.hostRecord.id },
data: { lastUpdate: new Date() } data: {
last_update: new Date(),
updated_at: new Date()
}
}); });
const response = { const response = {
message: 'Ping successful', message: 'Ping successful',
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
friendlyName: req.hostRecord.friendlyName friendlyName: req.hostRecord.friendly_name
}; };
// Check if this is a crontab update trigger // Check if this is a crontab update trigger
if (req.body.triggerCrontabUpdate && req.hostRecord.autoUpdate) { if (req.body.triggerCrontabUpdate && req.hostRecord.auto_update) {
console.log(`Triggering crontab update for host: ${req.hostRecord.friendlyName}`); console.log(`Triggering crontab update for host: ${req.hostRecord.friendly_name}`);
response.crontabUpdate = { response.crontabUpdate = {
shouldUpdate: true, shouldUpdate: true,
message: 'Update interval changed, please run: /usr/local/bin/patchmon-agent.sh update-crontab', message: 'Update interval changed, please run: /usr/local/bin/patchmon-agent.sh update-crontab',
@@ -547,7 +566,7 @@ router.post('/:hostId/regenerate-credentials', authenticateToken, requireManageH
try { try {
const { hostId } = req.params; const { hostId } = req.params;
const host = await prisma.host.findUnique({ const host = await prisma.hosts.findUnique({
where: { id: hostId } where: { id: hostId }
}); });
@@ -559,16 +578,20 @@ router.post('/:hostId/regenerate-credentials', authenticateToken, requireManageH
const { apiId, apiKey } = generateApiCredentials(); const { apiId, apiKey } = generateApiCredentials();
// Update host with new credentials // Update host with new credentials
const updatedHost = await prisma.host.update({ const updatedHost = await prisma.hosts.update({
where: { id: hostId }, where: { id: hostId },
data: { apiId, apiKey } data: {
api_id: apiId,
api_key: apiKey,
updated_at: new Date()
}
}); });
res.json({ res.json({
message: 'API credentials regenerated successfully', message: 'API credentials regenerated successfully',
hostname: updatedHost.hostname, hostname: updatedHost.hostname,
apiId: updatedHost.apiId, apiId: updatedHost.api_id,
apiKey: updatedHost.apiKey, apiKey: updatedHost.api_key,
warning: 'Previous credentials are now invalid. Update your agent configuration.' warning: 'Previous credentials are now invalid. Update your agent configuration.'
}); });
} catch (error) { } catch (error) {
@@ -593,7 +616,7 @@ router.put('/bulk/group', authenticateToken, requireManageHosts, [
// If hostGroupId is provided, verify the group exists // If hostGroupId is provided, verify the group exists
if (hostGroupId) { if (hostGroupId) {
const hostGroup = await prisma.hostGroup.findUnique({ const hostGroup = await prisma.host_groups.findUnique({
where: { id: hostGroupId } where: { id: hostGroupId }
}); });
@@ -603,9 +626,9 @@ router.put('/bulk/group', authenticateToken, requireManageHosts, [
} }
// Check if all hosts exist // Check if all hosts exist
const existingHosts = await prisma.host.findMany({ const existingHosts = await prisma.hosts.findMany({
where: { id: { in: hostIds } }, where: { id: { in: hostIds } },
select: { id: true, friendlyName: true } select: { id: true, friendly_name: true }
}); });
if (existingHosts.length !== hostIds.length) { if (existingHosts.length !== hostIds.length) {
@@ -618,20 +641,21 @@ router.put('/bulk/group', authenticateToken, requireManageHosts, [
} }
// Bulk update host groups // Bulk update host groups
const updateResult = await prisma.host.updateMany({ const updateResult = await prisma.hosts.updateMany({
where: { id: { in: hostIds } }, where: { id: { in: hostIds } },
data: { data: {
hostGroupId: hostGroupId || null host_group_id: hostGroupId || null,
updated_at: new Date()
} }
}); });
// Get updated hosts with group information // Get updated hosts with group information
const updatedHosts = await prisma.host.findMany({ const updatedHosts = await prisma.hosts.findMany({
where: { id: { in: hostIds } }, where: { id: { in: hostIds } },
select: { select: {
id: true, id: true,
friendlyName: true, friendly_name: true,
hostGroup: { host_groups: {
select: { select: {
id: true, id: true,
name: true, name: true,
@@ -666,7 +690,7 @@ router.put('/:hostId/group', authenticateToken, requireManageHosts, [
const { hostGroupId } = req.body; const { hostGroupId } = req.body;
// Check if host exists // Check if host exists
const host = await prisma.host.findUnique({ const host = await prisma.hosts.findUnique({
where: { id: hostId } where: { id: hostId }
}); });
@@ -676,7 +700,7 @@ router.put('/:hostId/group', authenticateToken, requireManageHosts, [
// If hostGroupId is provided, verify the group exists // If hostGroupId is provided, verify the group exists
if (hostGroupId) { if (hostGroupId) {
const hostGroup = await prisma.hostGroup.findUnique({ const hostGroup = await prisma.host_groups.findUnique({
where: { id: hostGroupId } where: { id: hostGroupId }
}); });
@@ -686,13 +710,14 @@ router.put('/:hostId/group', authenticateToken, requireManageHosts, [
} }
// Update host group // Update host group
const updatedHost = await prisma.host.update({ const updatedHost = await prisma.hosts.update({
where: { id: hostId }, where: { id: hostId },
data: { data: {
hostGroupId: hostGroupId || null host_group_id: hostGroupId || null,
updated_at: new Date()
}, },
include: { include: {
hostGroup: { host_groups: {
select: { select: {
id: true, id: true,
name: true, name: true,
@@ -715,23 +740,23 @@ router.put('/:hostId/group', authenticateToken, requireManageHosts, [
// Admin endpoint to list all hosts // Admin endpoint to list all hosts
router.get('/admin/list', authenticateToken, requireManageHosts, async (req, res) => { router.get('/admin/list', authenticateToken, requireManageHosts, async (req, res) => {
try { try {
const hosts = await prisma.host.findMany({ const hosts = await prisma.hosts.findMany({
select: { select: {
id: true, id: true,
friendlyName: true, friendly_name: true,
hostname: true, hostname: true,
ip: true, ip: true,
osType: true, os_type: true,
osVersion: true, os_version: true,
architecture: true, architecture: true,
lastUpdate: true, last_update: true,
status: true, status: true,
apiId: true, api_id: true,
agentVersion: true, agent_version: true,
autoUpdate: true, auto_update: true,
createdAt: true created_at: true
}, },
orderBy: { createdAt: 'desc' } orderBy: { created_at: 'desc' }
}); });
res.json(hosts); res.json(hosts);
@@ -747,7 +772,7 @@ router.delete('/:hostId', authenticateToken, requireManageHosts, async (req, res
const { hostId } = req.params; const { hostId } = req.params;
// Delete host and all related data (cascade) // Delete host and all related data (cascade)
await prisma.host.delete({ await prisma.hosts.delete({
where: { id: hostId } where: { id: hostId }
}); });
@@ -771,17 +796,20 @@ router.patch('/:hostId/auto-update', authenticateToken, requireManageHosts, [
const { hostId } = req.params; const { hostId } = req.params;
const { autoUpdate } = req.body; const { autoUpdate } = req.body;
const host = await prisma.host.update({ const host = await prisma.hosts.update({
where: { id: hostId }, where: { id: hostId },
data: { autoUpdate } data: {
auto_update: autoUpdate,
updated_at: new Date()
}
}); });
res.json({ res.json({
message: `Host auto-update ${autoUpdate ? 'enabled' : 'disabled'} successfully`, message: `Host auto-update ${autoUpdate ? 'enabled' : 'disabled'} successfully`,
host: { host: {
id: host.id, id: host.id,
friendlyName: host.friendlyName, friendlyName: host.friendly_name,
autoUpdate: host.autoUpdate autoUpdate: host.auto_update
} }
}); });
} catch (error) { } catch (error) {
@@ -811,7 +839,7 @@ router.get('/install', async (req, res) => {
// Replace the default server URL in the script with the configured one // Replace the default server URL in the script with the configured one
script = script.replace( script = script.replace(
/PATCHMON_URL="[^"]*"/g, /PATCHMON_URL="[^"]*"/g,
`PATCHMON_URL="${settings.serverUrl}"` `PATCHMON_URL="${settings.server_url}"`
); );
} }
} catch (settingsError) { } catch (settingsError) {
@@ -832,8 +860,8 @@ router.get('/install', async (req, res) => {
// Get all agent versions (admin only) // Get all agent versions (admin only)
router.get('/agent/versions', authenticateToken, requireManageSettings, async (req, res) => { router.get('/agent/versions', authenticateToken, requireManageSettings, async (req, res) => {
try { try {
const versions = await prisma.agentVersion.findMany({ const versions = await prisma.agent_versions.findMany({
orderBy: { createdAt: 'desc' } orderBy: { created_at: 'desc' }
}); });
res.json(versions); res.json(versions);
@@ -861,7 +889,7 @@ router.post('/agent/versions', authenticateToken, requireManageSettings, [
const { version, releaseNotes, downloadUrl, minServerVersion, scriptContent, isDefault } = req.body; const { version, releaseNotes, downloadUrl, minServerVersion, scriptContent, isDefault } = req.body;
// Check if version already exists // Check if version already exists
const existingVersion = await prisma.agentVersion.findUnique({ const existingVersion = await prisma.agent_versions.findUnique({
where: { version } where: { version }
}); });
@@ -871,21 +899,26 @@ router.post('/agent/versions', authenticateToken, requireManageSettings, [
// If this is being set as default, unset other defaults // If this is being set as default, unset other defaults
if (isDefault) { if (isDefault) {
await prisma.agentVersion.updateMany({ await prisma.agent_versions.updateMany({
where: { isDefault: true }, where: { is_default: true },
data: { isDefault: false } data: {
is_default: false,
updated_at: new Date()
}
}); });
} }
const agentVersion = await prisma.agentVersion.create({ const agentVersion = await prisma.agent_versions.create({
data: { data: {
id: uuidv4(),
version, version,
releaseNotes, release_notes: releaseNotes,
downloadUrl, download_url: downloadUrl,
minServerVersion, min_server_version: minServerVersion,
scriptContent, script_content: scriptContent,
isDefault: isDefault || false, is_default: isDefault || false,
isCurrent: false is_current: false,
updated_at: new Date()
} }
}); });
@@ -902,13 +935,13 @@ router.patch('/agent/versions/:versionId/current', authenticateToken, requireMan
const { versionId } = req.params; const { versionId } = req.params;
// First, unset all current versions // First, unset all current versions
await prisma.agentVersion.updateMany({ await prisma.agent_versions.updateMany({
where: { isCurrent: true }, where: { isCurrent: true },
data: { isCurrent: false } data: { isCurrent: false }
}); });
// Set the specified version as current // Set the specified version as current
const agentVersion = await prisma.agentVersion.update({ const agentVersion = await prisma.agent_versions.update({
where: { id: versionId }, where: { id: versionId },
data: { isCurrent: true } data: { isCurrent: true }
}); });
@@ -926,13 +959,13 @@ router.patch('/agent/versions/:versionId/default', authenticateToken, requireMan
const { versionId } = req.params; const { versionId } = req.params;
// First, unset all default versions // First, unset all default versions
await prisma.agentVersion.updateMany({ await prisma.agent_versions.updateMany({
where: { isDefault: true }, where: { isDefault: true },
data: { isDefault: false } data: { isDefault: false }
}); });
// Set the specified version as default // Set the specified version as default
const agentVersion = await prisma.agentVersion.update({ const agentVersion = await prisma.agent_versions.update({
where: { id: versionId }, where: { id: versionId },
data: { isDefault: true } data: { isDefault: true }
}); });
@@ -949,7 +982,7 @@ router.delete('/agent/versions/:versionId', authenticateToken, requireManageSett
try { try {
const { versionId } = req.params; const { versionId } = req.params;
const agentVersion = await prisma.agentVersion.findUnique({ const agentVersion = await prisma.agent_versions.findUnique({
where: { id: versionId } where: { id: versionId }
}); });
@@ -957,11 +990,11 @@ router.delete('/agent/versions/:versionId', authenticateToken, requireManageSett
return res.status(404).json({ error: 'Agent version not found' }); return res.status(404).json({ error: 'Agent version not found' });
} }
if (agentVersion.isCurrent) { if (agentVersion.is_current) {
return res.status(400).json({ error: 'Cannot delete current agent version' }); return res.status(400).json({ error: 'Cannot delete current agent version' });
} }
await prisma.agentVersion.delete({ await prisma.agent_versions.delete({
where: { id: versionId } where: { id: versionId }
}); });
@@ -986,7 +1019,7 @@ router.patch('/:hostId/friendly-name', authenticateToken, requireManageHosts, [
const { friendlyName } = req.body; const { friendlyName } = req.body;
// Check if host exists // Check if host exists
const host = await prisma.host.findUnique({ const host = await prisma.hosts.findUnique({
where: { id: hostId } where: { id: hostId }
}); });
@@ -995,7 +1028,7 @@ router.patch('/:hostId/friendly-name', authenticateToken, requireManageHosts, [
} }
// Check if friendly name is already taken by another host // Check if friendly name is already taken by another host
const existingHost = await prisma.host.findFirst({ const existingHost = await prisma.hosts.findFirst({
where: { where: {
friendlyName: friendlyName, friendlyName: friendlyName,
id: { not: hostId } id: { not: hostId }
@@ -1007,7 +1040,7 @@ router.patch('/:hostId/friendly-name', authenticateToken, requireManageHosts, [
} }
// Update the friendly name // Update the friendly name
const updatedHost = await prisma.host.update({ const updatedHost = await prisma.hosts.update({
where: { id: hostId }, where: { id: hostId },
data: { friendlyName }, data: { friendlyName },
select: { select: {

View File

@@ -34,16 +34,16 @@ router.get('/', async (req, res) => {
category ? { category: { equals: category } } : {}, category ? { category: { equals: category } } : {},
// Update status filters // Update status filters
needsUpdate ? { needsUpdate ? {
hostPackages: { host_packages: {
some: { some: {
needsUpdate: needsUpdate === 'true' needs_update: needsUpdate === 'true'
} }
} }
} : {}, } : {},
isSecurityUpdate ? { isSecurityUpdate ? {
hostPackages: { host_packages: {
some: { some: {
isSecurityUpdate: isSecurityUpdate === 'true' is_security_update: isSecurityUpdate === 'true'
} }
} }
} : {} } : {}
@@ -52,17 +52,17 @@ router.get('/', async (req, res) => {
// Get packages with counts // Get packages with counts
const [packages, totalCount] = await Promise.all([ const [packages, totalCount] = await Promise.all([
prisma.package.findMany({ prisma.packages.findMany({
where, where,
select: { select: {
id: true, id: true,
name: true, name: true,
description: true, description: true,
category: true, category: true,
latestVersion: true, latest_version: true,
createdAt: true, created_at: true,
_count: { _count: {
hostPackages: true host_packages: true
} }
}, },
skip, skip,
@@ -71,38 +71,38 @@ router.get('/', async (req, res) => {
name: 'asc' name: 'asc'
} }
}), }),
prisma.package.count({ where }) prisma.packages.count({ where })
]); ]);
// Get additional stats for each package // Get additional stats for each package
const packagesWithStats = await Promise.all( const packagesWithStats = await Promise.all(
packages.map(async (pkg) => { packages.map(async (pkg) => {
const [updatesCount, securityCount, affectedHosts] = await Promise.all([ const [updatesCount, securityCount, affectedHosts] = await Promise.all([
prisma.hostPackage.count({ prisma.host_packages.count({
where: { where: {
packageId: pkg.id, package_id: pkg.id,
needsUpdate: true needs_update: true
} }
}), }),
prisma.hostPackage.count({ prisma.host_packages.count({
where: { where: {
packageId: pkg.id, package_id: pkg.id,
needsUpdate: true, needs_update: true,
isSecurityUpdate: true is_security_update: true
} }
}), }),
prisma.hostPackage.findMany({ prisma.host_packages.findMany({
where: { where: {
packageId: pkg.id, package_id: pkg.id,
needsUpdate: true needs_update: true
}, },
select: { select: {
host: { hosts: {
select: { select: {
id: true, id: true,
friendlyName: true, friendly_name: true,
hostname: true, hostname: true,
osType: true os_type: true
} }
} }
}, },
@@ -142,7 +142,7 @@ router.get('/:packageId', async (req, res) => {
try { try {
const { packageId } = req.params; const { packageId } = req.params;
const packageData = await prisma.package.findUnique({ const packageData = await prisma.packages.findUnique({
where: { id: packageId }, where: { id: packageId },
include: { include: {
hostPackages: { hostPackages: {

View File

@@ -9,7 +9,7 @@ const prisma = new PrismaClient();
// Get all role permissions // Get all role permissions
router.get('/roles', authenticateToken, requireManageSettings, async (req, res) => { router.get('/roles', authenticateToken, requireManageSettings, async (req, res) => {
try { try {
const permissions = await prisma.rolePermissions.findMany({ const permissions = await prisma.role_permissions.findMany({
orderBy: { orderBy: {
role: 'asc' role: 'asc'
} }
@@ -27,7 +27,7 @@ router.get('/roles/:role', authenticateToken, requireManageSettings, async (req,
try { try {
const { role } = req.params; const { role } = req.params;
const permissions = await prisma.rolePermissions.findUnique({ const permissions = await prisma.role_permissions.findUnique({
where: { role } where: { role }
}); });
@@ -64,32 +64,35 @@ router.put('/roles/:role', authenticateToken, requireManageSettings, async (req,
return res.status(400).json({ error: 'Cannot modify admin role permissions' }); return res.status(400).json({ error: 'Cannot modify admin role permissions' });
} }
const permissions = await prisma.rolePermissions.upsert({ const permissions = await prisma.role_permissions.upsert({
where: { role }, where: { role },
update: { update: {
canViewDashboard, can_view_dashboard: canViewDashboard,
canViewHosts, can_view_hosts: canViewHosts,
canManageHosts, can_manage_hosts: canManageHosts,
canViewPackages, can_view_packages: canViewPackages,
canManagePackages, can_manage_packages: canManagePackages,
canViewUsers, can_view_users: canViewUsers,
canManageUsers, can_manage_users: canManageUsers,
canViewReports, can_view_reports: canViewReports,
canExportData, can_export_data: canExportData,
canManageSettings can_manage_settings: canManageSettings,
updated_at: new Date()
}, },
create: { create: {
id: require('uuid').v4(),
role, role,
canViewDashboard, can_view_dashboard: canViewDashboard,
canViewHosts, can_view_hosts: canViewHosts,
canManageHosts, can_manage_hosts: canManageHosts,
canViewPackages, can_view_packages: canViewPackages,
canManagePackages, can_manage_packages: canManagePackages,
canViewUsers, can_view_users: canViewUsers,
canManageUsers, can_manage_users: canManageUsers,
canViewReports, can_view_reports: canViewReports,
canExportData, can_export_data: canExportData,
canManageSettings can_manage_settings: canManageSettings,
updated_at: new Date()
} }
}); });
@@ -114,7 +117,7 @@ router.delete('/roles/:role', authenticateToken, requireManageSettings, async (r
} }
// Check if any users are using this role // Check if any users are using this role
const usersWithRole = await prisma.user.count({ const usersWithRole = await prisma.users.count({
where: { role } where: { role }
}); });
@@ -124,7 +127,7 @@ router.delete('/roles/:role', authenticateToken, requireManageSettings, async (r
}); });
} }
await prisma.rolePermissions.delete({ await prisma.role_permissions.delete({
where: { role } where: { role }
}); });
@@ -142,7 +145,7 @@ router.get('/user-permissions', authenticateToken, async (req, res) => {
try { try {
const userRole = req.user.role; const userRole = req.user.role;
const permissions = await prisma.rolePermissions.findUnique({ const permissions = await prisma.role_permissions.findUnique({
where: { role: userRole } where: { role: userRole }
}); });

View File

@@ -1,6 +1,7 @@
const express = require('express'); const express = require('express');
const { body, validationResult } = require('express-validator'); const { body, validationResult } = require('express-validator');
const { PrismaClient } = require('@prisma/client'); const { PrismaClient } = require('@prisma/client');
const { v4: uuidv4 } = require('uuid');
const { authenticateToken } = require('../middleware/auth'); const { authenticateToken } = require('../middleware/auth');
const { requireManageSettings } = require('../middleware/permissions'); const { requireManageSettings } = require('../middleware/permissions');
@@ -96,13 +97,15 @@ router.get('/', authenticateToken, requireManageSettings, async (req, res) => {
if (!settings) { if (!settings) {
settings = await prisma.settings.create({ settings = await prisma.settings.create({
data: { data: {
serverUrl: 'http://localhost:3001', id: uuidv4(),
serverProtocol: 'http', server_url: 'http://localhost:3001',
serverHost: 'localhost', server_protocol: 'http',
serverPort: 3001, server_host: 'localhost',
frontendUrl: 'http://localhost:3000', server_port: 3001,
updateInterval: 60, frontend_url: 'http://localhost:3000',
autoUpdate: false update_interval: 60,
auto_update: false,
updated_at: new Date()
} }
}); });
} }
@@ -171,16 +174,17 @@ router.put('/', authenticateToken, requireManageSettings, [
settings = await prisma.settings.update({ settings = await prisma.settings.update({
where: { id: settings.id }, where: { id: settings.id },
data: { data: {
serverUrl, server_url: serverUrl,
serverProtocol, server_protocol: serverProtocol,
serverHost, server_host: serverHost,
serverPort, server_port: serverPort,
frontendUrl, frontend_url: frontendUrl,
updateInterval: updateInterval || 60, update_interval: updateInterval || 60,
autoUpdate: autoUpdate || false, auto_update: autoUpdate || false,
githubRepoUrl: githubRepoUrl !== undefined ? githubRepoUrl : 'git@github.com:9technologygroup/patchmon.net.git', github_repo_url: githubRepoUrl !== undefined ? githubRepoUrl : 'git@github.com:9technologygroup/patchmon.net.git',
repositoryType: repositoryType || 'public', repository_type: repositoryType || 'public',
sshKeyPath: sshKeyPath || null ssh_key_path: sshKeyPath || null,
updated_at: new Date()
} }
}); });
console.log('Settings updated successfully:', settings); console.log('Settings updated successfully:', settings);
@@ -194,16 +198,18 @@ router.put('/', authenticateToken, requireManageSettings, [
// Create new settings // Create new settings
settings = await prisma.settings.create({ settings = await prisma.settings.create({
data: { data: {
serverUrl, id: uuidv4(),
serverProtocol, server_url: serverUrl,
serverHost, server_protocol: serverProtocol,
serverPort, server_host: serverHost,
frontendUrl, server_port: serverPort,
updateInterval: updateInterval || 60, frontend_url: frontendUrl,
autoUpdate: autoUpdate || false, update_interval: updateInterval || 60,
githubRepoUrl: githubRepoUrl !== undefined ? githubRepoUrl : 'git@github.com:9technologygroup/patchmon.net.git', auto_update: autoUpdate || false,
repositoryType: repositoryType || 'public', github_repo_url: githubRepoUrl !== undefined ? githubRepoUrl : 'git@github.com:9technologygroup/patchmon.net.git',
sshKeyPath: sshKeyPath || null repository_type: repositoryType || 'public',
ssh_key_path: sshKeyPath || null,
updated_at: new Date()
} }
}); });
} }