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
manage-instances.sh
manage-patchmon.sh
manage-patchmon-dev.sh
setup-installer-site.sh
install-server.*
notify-clients-upgrade.sh

View File

@@ -1,12 +1,12 @@
#!/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
# Configuration
PATCHMON_SERVER="${PATCHMON_SERVER:-http://localhost:3001}"
API_VERSION="v1"
AGENT_VERSION="1.2.5"
AGENT_VERSION="1.2.6"
CONFIG_FILE="/etc/patchmon/agent.conf"
CREDENTIALS_FILE="/etc/patchmon/credentials"
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 {
// Check current agent version in database
const agentVersion = await prisma.agentVersion.findFirst({
where: { version: '1.2.5' }
where: { version: '1.2.6' }
});
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('Is Default:', agentVersion.isDefault);
console.log('Script Content Length:', agentVersion.scriptContent?.length || 0);
@@ -18,10 +18,10 @@ async function checkAgentVersion() {
console.log('Updated At:', agentVersion.updatedAt);
// Check if script content contains the current version
if (agentVersion.scriptContent && agentVersion.scriptContent.includes('AGENT_VERSION="1.2.5"')) {
console.log('✅ Script content contains correct version 1.2.5');
if (agentVersion.scriptContent && agentVersion.scriptContent.includes('AGENT_VERSION="1.2.6"')) {
console.log('✅ Script content contains correct version 1.2.6');
} 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
@@ -44,7 +44,7 @@ async function checkAgentVersion() {
}
} 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

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
CORS_ORIGIN=http://localhost:3000
# Rate Limiting
# Rate Limiting (times in milliseconds)
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
LOG_LEVEL=info

View File

@@ -1,6 +1,6 @@
{
"name": "patchmon-backend",
"version": "1.2.5",
"version": "1.2.6",
"description": "Backend API for Linux Patch Monitoring System",
"main": "src/server.js",
"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 {
provider = "prisma-client-js"
}
@@ -10,239 +7,192 @@ datasource db {
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
username String @unique
email String @unique
passwordHash String @map("password_hash")
role String @default("admin") // admin, user
isActive Boolean @default(true) @map("is_active")
lastLogin DateTime? @map("last_login")
createdAt DateTime @map("created_at") @default(now())
updatedAt DateTime @map("updated_at") @updatedAt
// 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 agent_versions {
id String @id
version String @unique
is_current Boolean @default(false)
release_notes String?
download_url String?
min_server_version String?
created_at DateTime @default(now())
updated_at DateTime
is_default Boolean @default(false)
script_content String?
}
model RolePermissions {
id String @id @default(cuid())
role String @unique // admin, user, custom roles
canViewDashboard Boolean @default(true) @map("can_view_dashboard")
canViewHosts Boolean @default(true) @map("can_view_hosts")
canManageHosts Boolean @default(false) @map("can_manage_hosts")
canViewPackages Boolean @default(true) @map("can_view_packages")
canManagePackages Boolean @default(false) @map("can_manage_packages")
canViewUsers Boolean @default(false) @map("can_view_users")
canManageUsers Boolean @default(false) @map("can_manage_users")
canViewReports Boolean @default(true) @map("can_view_reports")
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 {
id String @id @default(cuid())
name String @unique
description String?
color String? @default("#3B82F6") // Hex color for UI display
createdAt DateTime @map("created_at") @default(now())
updatedAt DateTime @map("updated_at") @updatedAt
// Relationships
hosts Host[]
@@map("host_groups")
}
model Host {
id String @id @default(cuid())
friendlyName String @unique @map("friendly_name")
hostname String? // Actual system hostname from agent
ip String?
osType String @map("os_type")
osVersion String @map("os_version")
architecture String?
lastUpdate DateTime @map("last_update") @default(now())
status String @default("active") // active, inactive, error
apiId String @unique @map("api_id") // New API ID for authentication
apiKey String @unique @map("api_key") // New API Key for authentication
hostGroupId String? @map("host_group_id") // Optional group association
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 {
id String @id @default(cuid())
name String @unique
description String?
category String? // system, security, development, etc.
latestVersion String? @map("latest_version")
createdAt DateTime @map("created_at") @default(now())
updatedAt DateTime @map("updated_at") @updatedAt
// Relationships
hostPackages HostPackage[]
@@map("packages")
}
model HostPackage {
id String @id @default(cuid())
hostId String @map("host_id")
packageId String @map("package_id")
currentVersion String @map("current_version")
availableVersion String? @map("available_version")
needsUpdate Boolean @map("needs_update") @default(false)
isSecurityUpdate Boolean @map("is_security_update") @default(false)
lastChecked DateTime @map("last_checked") @default(now())
// Relationships
host Host @relation(fields: [hostId], references: [id], onDelete: Cascade)
package Package @relation(fields: [packageId], references: [id], onDelete: Cascade)
@@unique([hostId, packageId])
@@map("host_packages")
}
model UpdateHistory {
id String @id @default(cuid())
hostId String @map("host_id")
packagesCount Int @map("packages_count")
securityCount Int @map("security_count")
timestamp DateTime @default(now())
status String @default("success") // success, error
errorMessage String? @map("error_message")
// Relationships
host Host @relation(fields: [hostId], references: [id], onDelete: Cascade)
@@map("update_history")
}
model Repository {
id String @id @default(cuid())
name String // Repository name (e.g., "focal", "focal-updates")
url String // Repository URL
distribution String // Distribution (e.g., "focal", "jammy")
components String // Components (e.g., "main restricted universe multiverse")
repoType String @map("repo_type") // "deb" or "deb-src"
isActive Boolean @map("is_active") @default(true)
isSecure Boolean @map("is_secure") @default(true) // HTTPS vs HTTP
priority Int? // Repository priority
description String? // Optional description
createdAt DateTime @map("created_at") @default(now())
updatedAt DateTime @map("updated_at") @updatedAt
// Relationships
hostRepositories HostRepository[]
@@unique([url, distribution, components])
@@map("repositories")
}
model HostRepository {
id String @id @default(cuid())
hostId String @map("host_id")
repositoryId String @map("repository_id")
isEnabled Boolean @map("is_enabled") @default(true)
lastChecked DateTime @map("last_checked") @default(now())
// Relationships
host Host @relation(fields: [hostId], references: [id], onDelete: Cascade)
repository Repository @relation(fields: [repositoryId], references: [id], onDelete: Cascade)
@@unique([hostId, repositoryId])
@@map("host_repositories")
}
model Settings {
id String @id @default(cuid())
serverUrl String @map("server_url") @default("http://localhost:3001")
serverProtocol String @map("server_protocol") @default("http") // http, https
serverHost String @map("server_host") @default("localhost")
serverPort Int @map("server_port") @default(3001)
frontendUrl String @map("frontend_url") @default("http://localhost:3000")
updateInterval Int @map("update_interval") @default(60) // Update interval in minutes
autoUpdate Boolean @map("auto_update") @default(false) // Enable automatic agent updates
githubRepoUrl String @map("github_repo_url") @default("git@github.com:9technologygroup/patchmon.net.git") // GitHub repository URL for version checking
repositoryType String @map("repository_type") @default("public") // "public" or "private"
sshKeyPath String? @map("ssh_key_path") // Optional SSH key path for deploy key authentication
lastUpdateCheck DateTime? @map("last_update_check") // When the system last checked for updates
updateAvailable Boolean @map("update_available") @default(false) // Whether an update is available
latestVersion String? @map("latest_version") // Latest available version
createdAt DateTime @map("created_at") @default(now())
updatedAt DateTime @map("updated_at") @updatedAt
@@map("settings")
}
model DashboardPreferences {
id String @id @default(cuid())
userId String @map("user_id")
cardId String @map("card_id") // e.g., "totalHosts", "securityUpdates", etc.
model dashboard_preferences {
id String @id
user_id String
card_id String
enabled Boolean @default(true)
order Int @default(0)
createdAt DateTime @map("created_at") @default(now())
updatedAt DateTime @map("updated_at") @updatedAt
created_at DateTime @default(now())
updated_at DateTime
users users @relation(fields: [user_id], references: [id], onDelete: Cascade)
// Relationships
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, cardId])
@@map("dashboard_preferences")
@@unique([user_id, card_id])
}
model AgentVersion {
id String @id @default(cuid())
version String @unique // e.g., "1.0.0", "1.1.0"
isCurrent Boolean @default(false) @map("is_current") // Only one version can be current
releaseNotes String? @map("release_notes")
downloadUrl String? @map("download_url") // URL to download the agent script
minServerVersion String? @map("min_server_version") // Minimum server version required
scriptContent String? @map("script_content") // The actual agent script content
isDefault Boolean @default(false) @map("is_default") // Default version for new installations
createdAt DateTime @map("created_at") @default(now())
updatedAt DateTime @map("updated_at") @updatedAt
@@map("agent_versions")
model host_groups {
id String @id
name String @unique
description String?
color String? @default("#3B82F6")
created_at DateTime @default(now())
updated_at DateTime
hosts hosts[]
}
model host_packages {
id String @id
host_id String
package_id String
current_version String
available_version String?
needs_update Boolean @default(false)
is_security_update Boolean @default(false)
last_checked DateTime @default(now())
hosts hosts @relation(fields: [host_id], references: [id], onDelete: Cascade)
packages packages @relation(fields: [package_id], references: [id], onDelete: Cascade)
@@unique([host_id, package_id])
}
model host_repositories {
id String @id
host_id String
repository_id String
is_enabled Boolean @default(true)
last_checked DateTime @default(now())
hosts hosts @relation(fields: [host_id], references: [id], onDelete: Cascade)
repositories repositories @relation(fields: [repository_id], references: [id], onDelete: Cascade)
@@unique([host_id, repository_id])
}
model hosts {
id String @id
friendly_name String @unique
ip String?
os_type String
os_version String
architecture String?
last_update DateTime @default(now())
status String @default("active")
created_at DateTime @default(now())
updated_at DateTime
api_id String @unique
api_key String @unique
host_group_id String?
agent_version String?
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 packages {
id String @id
name String @unique
description String?
category String?
latest_version String?
created_at DateTime @default(now())
updated_at DateTime
host_packages host_packages[]
}
model repositories {
id String @id
name String
url String
distribution String
components String
repo_type String
is_active Boolean @default(true)
is_secure Boolean @default(true)
priority Int?
description String?
created_at DateTime @default(now())
updated_at DateTime
host_repositories host_repositories[]
@@unique([url, distribution, components])
}
model role_permissions {
id String @id
role String @unique
can_view_dashboard Boolean @default(true)
can_view_hosts Boolean @default(true)
can_manage_hosts Boolean @default(false)
can_view_packages Boolean @default(true)
can_manage_packages Boolean @default(false)
can_view_users Boolean @default(false)
can_manage_users Boolean @default(false)
can_view_reports Boolean @default(true)
can_export_data Boolean @default(false)
can_manage_settings Boolean @default(false)
created_at DateTime @default(now())
updated_at DateTime
}
model settings {
id String @id
server_url String @default("http://localhost:3001")
server_protocol String @default("http")
server_host String @default("localhost")
server_port Int @default(3001)
frontend_url String @default("http://localhost:3000")
created_at DateTime @default(now())
updated_at DateTime
update_interval Int @default(60)
auto_update Boolean @default(false)
github_repo_url String @default("git@github.com:9technologygroup/patchmon.net.git")
ssh_key_path String?
repository_type String @default("public")
last_update_check DateTime?
latest_version String?
update_available Boolean @default(false)
}
model update_history {
id String @id
host_id String
packages_count Int
security_count Int
timestamp DateTime @default(now())
status String @default("success")
error_message String?
hosts hosts @relation(fields: [host_id], references: [id], onDelete: Cascade)
}
model users {
id String @id
username String @unique
email String @unique
password_hash String
role String @default("admin")
is_active Boolean @default(true)
last_login DateTime?
created_at DateTime @default(now())
updated_at DateTime
tfa_backup_codes String?
tfa_enabled Boolean @default(false)
tfa_secret String?
dashboard_preferences dashboard_preferences[]
}

View File

@@ -14,7 +14,7 @@ const router = express.Router();
router.get('/current', authenticateToken, async (req, res) => {
try {
// Read version from package.json dynamically
let currentVersion = '1.2.5'; // fallback
let currentVersion = '1.2.6'; // fallback
try {
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' });
}
const currentVersion = '1.2.5';
const currentVersion = '1.2.6';
const latestVersion = settings.latestVersion || currentVersion;
const isUpdateAvailable = settings.updateAvailable || false;
const lastUpdateCheck = settings.lastUpdateCheck;

View File

@@ -59,11 +59,18 @@ if (process.env.TRUST_PROXY) {
}
app.disable('x-powered-by');
// Rate limiting
// Rate limiting with monitoring
const limiter = rateLimit({
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000,
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
@@ -118,16 +125,31 @@ app.get('/health', (req, res) => {
// API routes
const apiVersion = process.env.API_VERSION || 'v1';
// Per-route rate limits
// Per-route rate limits with monitoring
const authLimiter = rateLimit({
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({
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}/hosts`, agentLimiter, hostRoutes);
app.use(`/api/${apiVersion}/host-groups`, hostGroupRoutes);

View File

@@ -101,7 +101,7 @@ class UpdateScheduler {
}
// Read version from package.json dynamically
let currentVersion = '1.2.5'; // fallback
let currentVersion = '1.2.6'; // fallback
try {
const packageJson = require('../../package.json');
if (packageJson && packageJson.version) {
@@ -203,7 +203,7 @@ class UpdateScheduler {
const httpsRepoUrl = `https://api.github.com/repos/${owner}/${repo}/releases/latest`;
// Get current version for User-Agent
let currentVersion = '1.2.5'; // fallback
let currentVersion = '1.2.6'; // fallback
try {
const packageJson = require('../../package.json');
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",
"private": true,
"version": "1.2.5",
"version": "1.2.6",
"type": "module",
"scripts": {
"dev": "vite",

View File

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

View File

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

View File

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

View File

@@ -471,7 +471,7 @@ const Settings = () => {
<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>
<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 className="text-xs text-secondary-500 dark:text-secondary-400 mt-1">
This URL will be used in installation scripts and agent communications.

8
package-lock.json generated
View File

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

View File

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