fixed rate limits into env

This commit is contained in:
Muhammad Ibrahim
2025-09-21 03:58:22 +01:00
parent 9a3827dced
commit f3351d577d
29 changed files with 331 additions and 265 deletions

1
.gitignore vendored
View File

@@ -140,6 +140,7 @@ test-results.xml
deploy-patchmon.sh deploy-patchmon.sh
manage-instances.sh manage-instances.sh
manage-patchmon.sh manage-patchmon.sh
manage-patchmon-dev.sh
setup-installer-site.sh setup-installer-site.sh
install-server.* install-server.*
notify-clients-upgrade.sh notify-clients-upgrade.sh

View File

@@ -1,12 +1,12 @@
#!/bin/bash #!/bin/bash
# PatchMon Agent Script v1.2.5 # PatchMon Agent Script v1.2.6
# This script sends package update information to the PatchMon server using API credentials # This script sends package update information to the PatchMon server using API credentials
# Configuration # Configuration
PATCHMON_SERVER="${PATCHMON_SERVER:-http://localhost:3001}" PATCHMON_SERVER="${PATCHMON_SERVER:-http://localhost:3001}"
API_VERSION="v1" API_VERSION="v1"
AGENT_VERSION="1.2.5" AGENT_VERSION="1.2.6"
CONFIG_FILE="/etc/patchmon/agent.conf" CONFIG_FILE="/etc/patchmon/agent.conf"
CREDENTIALS_FILE="/etc/patchmon/credentials" CREDENTIALS_FILE="/etc/patchmon/credentials"
LOG_FILE="/var/log/patchmon-agent.log" LOG_FILE="/var/log/patchmon-agent.log"

View File

@@ -1 +1,102 @@
const { PrismaClient } = require('@prisma/client');
const fs = require('fs');
const path = require('path');
const prisma = new PrismaClient();
async function addAgentVersion() {
try {
console.log('🚀 Adding agent version to database...');
// Read the agent script file
const agentScriptPath = path.join(__dirname, '..', 'agents', 'patchmon-agent.sh');
if (!fs.existsSync(agentScriptPath)) {
throw new Error(`Agent script not found at: ${agentScriptPath}`);
}
const scriptContent = fs.readFileSync(agentScriptPath, 'utf8');
console.log(`📄 Read agent script (${scriptContent.length} characters)`);
// Extract version from script content
const versionMatch = scriptContent.match(/AGENT_VERSION="([^"]+)"/);
if (!versionMatch) {
throw new Error('Could not extract AGENT_VERSION from script');
}
const version = versionMatch[1];
console.log(`🔍 Found agent version: ${version}`);
// Check if this version already exists
const existingVersion = await prisma.agentVersion.findUnique({
where: { version: version }
});
if (existingVersion) {
console.log(`⚠️ Agent version ${version} already exists in database`);
// Update the existing version with current script content
const updatedVersion = await prisma.agentVersion.update({
where: { version: version },
data: {
scriptContent: scriptContent,
isDefault: true,
isCurrent: true,
releaseNotes: `Agent script version ${version} - Updated during deployment`
}
});
console.log(`✅ Updated existing agent version ${version}`);
return updatedVersion;
}
// Set all other versions to not be current/default
await prisma.agentVersion.updateMany({
where: {
version: { not: version }
},
data: {
isCurrent: false,
isDefault: false
}
});
// Create new agent version
const newVersion = await prisma.agentVersion.create({
data: {
version: version,
scriptContent: scriptContent,
isDefault: true,
isCurrent: true,
releaseNotes: `Agent script version ${version} - Initial deployment`
}
});
console.log(`✅ Created new agent version ${version}`);
console.log(`📊 Version ID: ${newVersion.id}`);
console.log(`📝 Script content length: ${scriptContent.length} characters`);
return newVersion;
} catch (error) {
console.error('❌ Error adding agent version:', error);
throw error;
} finally {
await prisma.$disconnect();
}
}
// Run the function if this script is executed directly
if (require.main === module) {
addAgentVersion()
.then(() => {
console.log('🎉 Agent version setup completed successfully!');
process.exit(0);
})
.catch((error) => {
console.error('💥 Agent version setup failed:', error);
process.exit(1);
});
}
module.exports = { addAgentVersion };

View File

@@ -6,11 +6,11 @@ async function checkAgentVersion() {
try { try {
// Check current agent version in database // Check current agent version in database
const agentVersion = await prisma.agentVersion.findFirst({ const agentVersion = await prisma.agentVersion.findFirst({
where: { version: '1.2.5' } where: { version: '1.2.6' }
}); });
if (agentVersion) { if (agentVersion) {
console.log('✅ Agent version 1.2.5 found in database'); console.log('✅ Agent version 1.2.6 found in database');
console.log('Version:', agentVersion.version); console.log('Version:', agentVersion.version);
console.log('Is Default:', agentVersion.isDefault); console.log('Is Default:', agentVersion.isDefault);
console.log('Script Content Length:', agentVersion.scriptContent?.length || 0); console.log('Script Content Length:', agentVersion.scriptContent?.length || 0);
@@ -18,10 +18,10 @@ async function checkAgentVersion() {
console.log('Updated At:', agentVersion.updatedAt); console.log('Updated At:', agentVersion.updatedAt);
// Check if script content contains the current version // Check if script content contains the current version
if (agentVersion.scriptContent && agentVersion.scriptContent.includes('AGENT_VERSION="1.2.5"')) { if (agentVersion.scriptContent && agentVersion.scriptContent.includes('AGENT_VERSION="1.2.6"')) {
console.log('✅ Script content contains correct version 1.2.5'); console.log('✅ Script content contains correct version 1.2.6');
} else { } else {
console.log('❌ Script content does not contain version 1.2.5'); console.log('❌ Script content does not contain version 1.2.6');
} }
// Check if script content contains system info functions // Check if script content contains system info functions
@@ -44,7 +44,7 @@ async function checkAgentVersion() {
} }
} else { } else {
console.log('❌ Agent version 1.2.5 not found in database'); console.log('❌ Agent version 1.2.6 not found in database');
} }
// List all agent versions // List all agent versions

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -9,9 +9,13 @@ NODE_ENV=development
API_VERSION=v1 API_VERSION=v1
CORS_ORIGIN=http://localhost:3000 CORS_ORIGIN=http://localhost:3000
# Rate Limiting # Rate Limiting (times in milliseconds)
RATE_LIMIT_WINDOW_MS=900000 RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=100 RATE_LIMIT_MAX=5000
AUTH_RATE_LIMIT_WINDOW_MS=600000
AUTH_RATE_LIMIT_MAX=500
AGENT_RATE_LIMIT_WINDOW_MS=60000
AGENT_RATE_LIMIT_MAX=1000
# Logging # Logging
LOG_LEVEL=info LOG_LEVEL=info

View File

@@ -1,6 +1,6 @@
{ {
"name": "patchmon-backend", "name": "patchmon-backend",
"version": "1.2.5", "version": "1.2.6",
"description": "Backend API for Linux Patch Monitoring System", "description": "Backend API for Linux Patch Monitoring System",
"main": "src/server.js", "main": "src/server.js",
"scripts": { "scripts": {

View File

@@ -1,6 +1,3 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
} }
@@ -10,239 +7,192 @@ datasource db {
url = env("DATABASE_URL") url = env("DATABASE_URL")
} }
model User { model agent_versions {
id String @id @default(cuid()) id String @id
username String @unique version String @unique
email String @unique is_current Boolean @default(false)
passwordHash String @map("password_hash") release_notes String?
role String @default("admin") // admin, user download_url String?
isActive Boolean @default(true) @map("is_active") min_server_version String?
lastLogin DateTime? @map("last_login") created_at DateTime @default(now())
createdAt DateTime @map("created_at") @default(now()) updated_at DateTime
updatedAt DateTime @map("updated_at") @updatedAt is_default Boolean @default(false)
script_content String?
// Two-Factor Authentication
tfaEnabled Boolean @default(false) @map("tfa_enabled")
tfaSecret String? @map("tfa_secret")
tfaBackupCodes String? @map("tfa_backup_codes") // JSON array of backup codes
// Relationships
dashboardPreferences DashboardPreferences[]
@@map("users")
} }
model RolePermissions { model dashboard_preferences {
id String @id @default(cuid()) id String @id
role String @unique // admin, user, custom roles user_id String
canViewDashboard Boolean @default(true) @map("can_view_dashboard") card_id String
canViewHosts Boolean @default(true) @map("can_view_hosts") enabled Boolean @default(true)
canManageHosts Boolean @default(false) @map("can_manage_hosts") order Int @default(0)
canViewPackages Boolean @default(true) @map("can_view_packages") created_at DateTime @default(now())
canManagePackages Boolean @default(false) @map("can_manage_packages") updated_at DateTime
canViewUsers Boolean @default(false) @map("can_view_users") users users @relation(fields: [user_id], references: [id], onDelete: Cascade)
canManageUsers Boolean @default(false) @map("can_manage_users")
canViewReports Boolean @default(true) @map("can_view_reports") @@unique([user_id, card_id])
canExportData Boolean @default(false) @map("can_export_data")
canManageSettings Boolean @default(false) @map("can_manage_settings")
createdAt DateTime @map("created_at") @default(now())
updatedAt DateTime @map("updated_at") @updatedAt
@@map("role_permissions")
} }
model HostGroup { model host_groups {
id String @id @default(cuid()) id String @id
name String @unique name String @unique
description String? description String?
color String? @default("#3B82F6") // Hex color for UI display color String? @default("#3B82F6")
createdAt DateTime @map("created_at") @default(now()) created_at DateTime @default(now())
updatedAt DateTime @map("updated_at") @updatedAt updated_at DateTime
hosts hosts[]
// Relationships
hosts Host[]
@@map("host_groups")
} }
model Host { model host_packages {
id String @id @default(cuid()) id String @id
friendlyName String @unique @map("friendly_name") host_id String
hostname String? // Actual system hostname from agent package_id String
ip String? current_version String
osType String @map("os_type") available_version String?
osVersion String @map("os_version") needs_update Boolean @default(false)
architecture String? is_security_update Boolean @default(false)
lastUpdate DateTime @map("last_update") @default(now()) last_checked DateTime @default(now())
status String @default("active") // active, inactive, error hosts hosts @relation(fields: [host_id], references: [id], onDelete: Cascade)
apiId String @unique @map("api_id") // New API ID for authentication packages packages @relation(fields: [package_id], references: [id], onDelete: Cascade)
apiKey String @unique @map("api_key") // New API Key for authentication
hostGroupId String? @map("host_group_id") // Optional group association @@unique([host_id, package_id])
agentVersion String? @map("agent_version") // Agent script version
autoUpdate Boolean @map("auto_update") @default(true) // Enable auto-update for this host
// Hardware Information
cpuModel String? @map("cpu_model") // CPU model name
cpuCores Int? @map("cpu_cores") // Number of CPU cores
ramInstalled Int? @map("ram_installed") // RAM in GB
swapSize Int? @map("swap_size") // Swap size in GB
diskDetails Json? @map("disk_details") // Array of disk objects
// Network Information
gatewayIp String? @map("gateway_ip") // Gateway IP address
dnsServers Json? @map("dns_servers") // Array of DNS servers
networkInterfaces Json? @map("network_interfaces") // Array of network interface objects
// System Information
kernelVersion String? @map("kernel_version") // Kernel version
selinuxStatus String? @map("selinux_status") // SELinux status (enabled/disabled/permissive)
systemUptime String? @map("system_uptime") // System uptime
loadAverage Json? @map("load_average") // Load average (1min, 5min, 15min)
createdAt DateTime @map("created_at") @default(now())
updatedAt DateTime @map("updated_at") @updatedAt
// Relationships
hostPackages HostPackage[]
updateHistory UpdateHistory[]
hostRepositories HostRepository[]
hostGroup HostGroup? @relation(fields: [hostGroupId], references: [id], onDelete: SetNull)
@@map("hosts")
} }
model Package { model host_repositories {
id String @id @default(cuid()) id String @id
name String @unique host_id String
description String? repository_id String
category String? // system, security, development, etc. is_enabled Boolean @default(true)
latestVersion String? @map("latest_version") last_checked DateTime @default(now())
createdAt DateTime @map("created_at") @default(now()) hosts hosts @relation(fields: [host_id], references: [id], onDelete: Cascade)
updatedAt DateTime @map("updated_at") @updatedAt repositories repositories @relation(fields: [repository_id], references: [id], onDelete: Cascade)
// Relationships @@unique([host_id, repository_id])
hostPackages HostPackage[]
@@map("packages")
} }
model HostPackage { model hosts {
id String @id @default(cuid()) id String @id
hostId String @map("host_id") friendly_name String @unique
packageId String @map("package_id") ip String?
currentVersion String @map("current_version") os_type String
availableVersion String? @map("available_version") os_version String
needsUpdate Boolean @map("needs_update") @default(false) architecture String?
isSecurityUpdate Boolean @map("is_security_update") @default(false) last_update DateTime @default(now())
lastChecked DateTime @map("last_checked") @default(now()) status String @default("active")
created_at DateTime @default(now())
// Relationships updated_at DateTime
host Host @relation(fields: [hostId], references: [id], onDelete: Cascade) api_id String @unique
package Package @relation(fields: [packageId], references: [id], onDelete: Cascade) api_key String @unique
host_group_id String?
@@unique([hostId, packageId]) agent_version String?
@@map("host_packages") auto_update Boolean @default(true)
cpu_cores Int?
cpu_model String?
disk_details Json?
dns_servers Json?
gateway_ip String?
hostname String?
kernel_version String?
load_average Json?
network_interfaces Json?
ram_installed Int?
selinux_status String?
swap_size Int?
system_uptime String?
host_packages host_packages[]
host_repositories host_repositories[]
host_groups host_groups? @relation(fields: [host_group_id], references: [id])
update_history update_history[]
} }
model UpdateHistory { model packages {
id String @id @default(cuid()) id String @id
hostId String @map("host_id") name String @unique
packagesCount Int @map("packages_count") description String?
securityCount Int @map("security_count") category String?
timestamp DateTime @default(now()) latest_version String?
status String @default("success") // success, error created_at DateTime @default(now())
errorMessage String? @map("error_message") updated_at DateTime
host_packages host_packages[]
// Relationships
host Host @relation(fields: [hostId], references: [id], onDelete: Cascade)
@@map("update_history")
} }
model Repository { model repositories {
id String @id @default(cuid()) id String @id
name String // Repository name (e.g., "focal", "focal-updates") name String
url String // Repository URL url String
distribution String // Distribution (e.g., "focal", "jammy") distribution String
components String // Components (e.g., "main restricted universe multiverse") components String
repoType String @map("repo_type") // "deb" or "deb-src" repo_type String
isActive Boolean @map("is_active") @default(true) is_active Boolean @default(true)
isSecure Boolean @map("is_secure") @default(true) // HTTPS vs HTTP is_secure Boolean @default(true)
priority Int? // Repository priority priority Int?
description String? // Optional description description String?
createdAt DateTime @map("created_at") @default(now()) created_at DateTime @default(now())
updatedAt DateTime @map("updated_at") @updatedAt updated_at DateTime
host_repositories host_repositories[]
// Relationships
hostRepositories HostRepository[]
@@unique([url, distribution, components]) @@unique([url, distribution, components])
@@map("repositories")
} }
model HostRepository { model role_permissions {
id String @id @default(cuid()) id String @id
hostId String @map("host_id") role String @unique
repositoryId String @map("repository_id") can_view_dashboard Boolean @default(true)
isEnabled Boolean @map("is_enabled") @default(true) can_view_hosts Boolean @default(true)
lastChecked DateTime @map("last_checked") @default(now()) can_manage_hosts Boolean @default(false)
can_view_packages Boolean @default(true)
// Relationships can_manage_packages Boolean @default(false)
host Host @relation(fields: [hostId], references: [id], onDelete: Cascade) can_view_users Boolean @default(false)
repository Repository @relation(fields: [repositoryId], references: [id], onDelete: Cascade) can_manage_users Boolean @default(false)
can_view_reports Boolean @default(true)
@@unique([hostId, repositoryId]) can_export_data Boolean @default(false)
@@map("host_repositories") can_manage_settings Boolean @default(false)
created_at DateTime @default(now())
updated_at DateTime
} }
model Settings { model settings {
id String @id @default(cuid()) id String @id
serverUrl String @map("server_url") @default("http://localhost:3001") server_url String @default("http://localhost:3001")
serverProtocol String @map("server_protocol") @default("http") // http, https server_protocol String @default("http")
serverHost String @map("server_host") @default("localhost") server_host String @default("localhost")
serverPort Int @map("server_port") @default(3001) server_port Int @default(3001)
frontendUrl String @map("frontend_url") @default("http://localhost:3000") frontend_url String @default("http://localhost:3000")
updateInterval Int @map("update_interval") @default(60) // Update interval in minutes created_at DateTime @default(now())
autoUpdate Boolean @map("auto_update") @default(false) // Enable automatic agent updates updated_at DateTime
githubRepoUrl String @map("github_repo_url") @default("git@github.com:9technologygroup/patchmon.net.git") // GitHub repository URL for version checking update_interval Int @default(60)
repositoryType String @map("repository_type") @default("public") // "public" or "private" auto_update Boolean @default(false)
sshKeyPath String? @map("ssh_key_path") // Optional SSH key path for deploy key authentication github_repo_url String @default("git@github.com:9technologygroup/patchmon.net.git")
lastUpdateCheck DateTime? @map("last_update_check") // When the system last checked for updates ssh_key_path String?
updateAvailable Boolean @map("update_available") @default(false) // Whether an update is available repository_type String @default("public")
latestVersion String? @map("latest_version") // Latest available version last_update_check DateTime?
createdAt DateTime @map("created_at") @default(now()) latest_version String?
updatedAt DateTime @map("updated_at") @updatedAt update_available Boolean @default(false)
@@map("settings")
} }
model DashboardPreferences { model update_history {
id String @id @default(cuid()) id String @id
userId String @map("user_id") host_id String
cardId String @map("card_id") // e.g., "totalHosts", "securityUpdates", etc. packages_count Int
enabled Boolean @default(true) security_count Int
order Int @default(0) timestamp DateTime @default(now())
createdAt DateTime @map("created_at") @default(now()) status String @default("success")
updatedAt DateTime @map("updated_at") @updatedAt error_message String?
hosts hosts @relation(fields: [host_id], references: [id], onDelete: Cascade)
// Relationships
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, cardId])
@@map("dashboard_preferences")
} }
model AgentVersion { model users {
id String @id @default(cuid()) id String @id
version String @unique // e.g., "1.0.0", "1.1.0" username String @unique
isCurrent Boolean @default(false) @map("is_current") // Only one version can be current email String @unique
releaseNotes String? @map("release_notes") password_hash String
downloadUrl String? @map("download_url") // URL to download the agent script role String @default("admin")
minServerVersion String? @map("min_server_version") // Minimum server version required is_active Boolean @default(true)
scriptContent String? @map("script_content") // The actual agent script content last_login DateTime?
isDefault Boolean @default(false) @map("is_default") // Default version for new installations created_at DateTime @default(now())
createdAt DateTime @map("created_at") @default(now()) updated_at DateTime
updatedAt DateTime @map("updated_at") @updatedAt tfa_backup_codes String?
tfa_enabled Boolean @default(false)
@@map("agent_versions") tfa_secret String?
} dashboard_preferences dashboard_preferences[]
}

View File

@@ -14,7 +14,7 @@ const router = express.Router();
router.get('/current', authenticateToken, async (req, res) => { router.get('/current', authenticateToken, async (req, res) => {
try { try {
// Read version from package.json dynamically // Read version from package.json dynamically
let currentVersion = '1.2.5'; // fallback let currentVersion = '1.2.6'; // fallback
try { try {
const packageJson = require('../../package.json'); const packageJson = require('../../package.json');
@@ -158,7 +158,7 @@ router.get('/check-updates', authenticateToken, requireManageSettings, async (re
return res.status(400).json({ error: 'Settings not found' }); return res.status(400).json({ error: 'Settings not found' });
} }
const currentVersion = '1.2.5'; const currentVersion = '1.2.6';
const latestVersion = settings.latestVersion || currentVersion; const latestVersion = settings.latestVersion || currentVersion;
const isUpdateAvailable = settings.updateAvailable || false; const isUpdateAvailable = settings.updateAvailable || false;
const lastUpdateCheck = settings.lastUpdateCheck; const lastUpdateCheck = settings.lastUpdateCheck;

View File

@@ -59,11 +59,18 @@ if (process.env.TRUST_PROXY) {
} }
app.disable('x-powered-by'); app.disable('x-powered-by');
// Rate limiting // Rate limiting with monitoring
const limiter = rateLimit({ const limiter = rateLimit({
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000, windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000,
max: parseInt(process.env.RATE_LIMIT_MAX) || 100, max: parseInt(process.env.RATE_LIMIT_MAX) || 100,
message: 'Too many requests from this IP, please try again later.', message: {
error: 'Too many requests from this IP, please try again later.',
retryAfter: Math.ceil((parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000) / 1000)
},
standardHeaders: true,
legacyHeaders: false,
skipSuccessfulRequests: true, // Don't count successful requests
skipFailedRequests: false, // Count failed requests
}); });
// Middleware // Middleware
@@ -118,16 +125,31 @@ app.get('/health', (req, res) => {
// API routes // API routes
const apiVersion = process.env.API_VERSION || 'v1'; const apiVersion = process.env.API_VERSION || 'v1';
// Per-route rate limits // Per-route rate limits with monitoring
const authLimiter = rateLimit({ const authLimiter = rateLimit({
windowMs: parseInt(process.env.AUTH_RATE_LIMIT_WINDOW_MS) || 10 * 60 * 1000, windowMs: parseInt(process.env.AUTH_RATE_LIMIT_WINDOW_MS) || 10 * 60 * 1000,
max: parseInt(process.env.AUTH_RATE_LIMIT_MAX) || 20 max: parseInt(process.env.AUTH_RATE_LIMIT_MAX) || 20,
message: {
error: 'Too many authentication requests, please try again later.',
retryAfter: Math.ceil((parseInt(process.env.AUTH_RATE_LIMIT_WINDOW_MS) || 10 * 60 * 1000) / 1000)
},
standardHeaders: true,
legacyHeaders: false,
skipSuccessfulRequests: true,
}); });
const agentLimiter = rateLimit({ const agentLimiter = rateLimit({
windowMs: parseInt(process.env.AGENT_RATE_LIMIT_WINDOW_MS) || 60 * 1000, windowMs: parseInt(process.env.AGENT_RATE_LIMIT_WINDOW_MS) || 60 * 1000,
max: parseInt(process.env.AGENT_RATE_LIMIT_MAX) || 120 max: parseInt(process.env.AGENT_RATE_LIMIT_MAX) || 120,
message: {
error: 'Too many agent requests, please try again later.',
retryAfter: Math.ceil((parseInt(process.env.AGENT_RATE_LIMIT_WINDOW_MS) || 60 * 1000) / 1000)
},
standardHeaders: true,
legacyHeaders: false,
skipSuccessfulRequests: true,
}); });
app.use(`/api/${apiVersion}/auth`, authLimiter, authRoutes); app.use(`/api/${apiVersion}/auth`, authLimiter, authRoutes);
app.use(`/api/${apiVersion}/hosts`, agentLimiter, hostRoutes); app.use(`/api/${apiVersion}/hosts`, agentLimiter, hostRoutes);
app.use(`/api/${apiVersion}/host-groups`, hostGroupRoutes); app.use(`/api/${apiVersion}/host-groups`, hostGroupRoutes);

View File

@@ -101,7 +101,7 @@ class UpdateScheduler {
} }
// Read version from package.json dynamically // Read version from package.json dynamically
let currentVersion = '1.2.5'; // fallback let currentVersion = '1.2.6'; // fallback
try { try {
const packageJson = require('../../package.json'); const packageJson = require('../../package.json');
if (packageJson && packageJson.version) { if (packageJson && packageJson.version) {
@@ -203,7 +203,7 @@ class UpdateScheduler {
const httpsRepoUrl = `https://api.github.com/repos/${owner}/${repo}/releases/latest`; const httpsRepoUrl = `https://api.github.com/repos/${owner}/${repo}/releases/latest`;
// Get current version for User-Agent // Get current version for User-Agent
let currentVersion = '1.2.5'; // fallback let currentVersion = '1.2.6'; // fallback
try { try {
const packageJson = require('../../package.json'); const packageJson = require('../../package.json');
if (packageJson && packageJson.version) { if (packageJson && packageJson.version) {

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1,7 +1,7 @@
{ {
"name": "patchmon-frontend", "name": "patchmon-frontend",
"private": true, "private": true,
"version": "1.2.5", "version": "1.2.6",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -92,8 +92,8 @@ const Dashboard = () => {
const { data: stats, isLoading, error, refetch } = useQuery({ const { data: stats, isLoading, error, refetch } = useQuery({
queryKey: ['dashboardStats'], queryKey: ['dashboardStats'],
queryFn: () => dashboardAPI.getStats().then(res => res.data), queryFn: () => dashboardAPI.getStats().then(res => res.data),
refetchInterval: 60000, // Refresh every minute refetchInterval: 300000, // Refresh every 5 minutes instead of 1 minute
staleTime: 30000, // Consider data stale after 30 seconds staleTime: 120000, // Consider data stale after 2 minutes
}) })
// Fetch settings to get the agent update interval // Fetch settings to get the agent update interval

View File

@@ -735,8 +735,8 @@ const Hosts = () => {
const { data: hosts, isLoading, error, refetch } = useQuery({ const { data: hosts, isLoading, error, refetch } = useQuery({
queryKey: ['hosts'], queryKey: ['hosts'],
queryFn: () => dashboardAPI.getHosts().then(res => res.data), queryFn: () => dashboardAPI.getHosts().then(res => res.data),
refetchInterval: 60000, refetchInterval: 300000, // Refresh every 5 minutes instead of 1 minute
staleTime: 30000, staleTime: 120000, // Consider data stale after 2 minutes
}) })
const { data: hostGroups } = useQuery({ const { data: hostGroups } = useQuery({

View File

@@ -101,16 +101,16 @@ const Packages = () => {
const { data: packages, isLoading, error, refetch } = useQuery({ const { data: packages, isLoading, error, refetch } = useQuery({
queryKey: ['packages'], queryKey: ['packages'],
queryFn: () => dashboardAPI.getPackages().then(res => res.data), queryFn: () => dashboardAPI.getPackages().then(res => res.data),
refetchInterval: 60000, refetchInterval: 300000, // Refresh every 5 minutes instead of 1 minute
staleTime: 30000, staleTime: 120000, // Consider data stale after 2 minutes
}) })
// Fetch hosts data to get total packages count // Fetch hosts data to get total packages count
const { data: hosts } = useQuery({ const { data: hosts } = useQuery({
queryKey: ['hosts'], queryKey: ['hosts'],
queryFn: () => dashboardAPI.getHosts().then(res => res.data), queryFn: () => dashboardAPI.getHosts().then(res => res.data),
refetchInterval: 60000, refetchInterval: 300000, // Refresh every 5 minutes instead of 1 minute
staleTime: 30000, staleTime: 120000, // Consider data stale after 2 minutes
}) })
// Filter and sort packages // Filter and sort packages

View File

@@ -471,12 +471,12 @@ const Settings = () => {
<div className="mt-4 p-4 bg-secondary-50 dark:bg-secondary-700 rounded-md"> <div className="mt-4 p-4 bg-secondary-50 dark:bg-secondary-700 rounded-md">
<h4 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">Server URL</h4> <h4 className="text-sm font-medium text-secondary-900 dark:text-white mb-2">Server URL</h4>
<p className="text-sm text-secondary-600 dark:text-secondary-300 font-mono"> <p className="text-sm text-secondary-600 dark:text-secondary-300 font-mono">
{formData.serverProtocol}://{formData.serverHost}:{formData.serverPort} {formData.serverProtocol}://{formData.serverHost}{formData.serverProtocol === 'https' ? ':443' : `:${formData.serverPort}`}
</p> </p>
<p className="text-xs text-secondary-500 dark:text-secondary-400 mt-1"> <p className="text-xs text-secondary-500 dark:text-secondary-400 mt-1">
This URL will be used in installation scripts and agent communications. This URL will be used in installation scripts and agent communications.
</p> </p>
</div> </div>
{/* Update Interval */} {/* Update Interval */}
<div> <div>

8
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "patchmon", "name": "patchmon",
"version": "1.2.4", "version": "1.2.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "patchmon", "name": "patchmon",
"version": "1.2.4", "version": "1.2.6",
"workspaces": [ "workspaces": [
"backend", "backend",
"frontend" "frontend"
@@ -20,7 +20,7 @@
}, },
"backend": { "backend": {
"name": "patchmon-backend", "name": "patchmon-backend",
"version": "1.2.4", "version": "1.2.6",
"dependencies": { "dependencies": {
"@prisma/client": "^5.7.0", "@prisma/client": "^5.7.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
@@ -47,7 +47,7 @@
}, },
"frontend": { "frontend": {
"name": "patchmon-frontend", "name": "patchmon-frontend",
"version": "1.2.4", "version": "1.2.6",
"dependencies": { "dependencies": {
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0", "@dnd-kit/sortable": "^10.0.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "patchmon", "name": "patchmon",
"version": "1.2.5", "version": "1.2.6",
"description": "Linux Patch Monitoring System", "description": "Linux Patch Monitoring System",
"private": true, "private": true,
"workspaces": [ "workspaces": [