mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-11-04 14:03:17 +00:00
fixed rate limits into env
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 };
|
||||
@@ -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
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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[]
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "patchmon-frontend",
|
||||
"private": true,
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.6",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
8
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "patchmon",
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.6",
|
||||
"description": "Linux Patch Monitoring System",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
|
||||
Reference in New Issue
Block a user