mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-11-10 08:55:44 +00:00
Refactor database model references to use consistent naming conventions and update related queries
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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 }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user