Compare commits

..

17 Commits

Author SHA1 Message Date
renovate[bot]
ab9cc9bbec Update dependency @vitejs/plugin-react to v5 2025-10-24 14:15:22 +00:00
9 Technology Group LTD
8d372411be Merge pull request #208 from PatchMon/post1-3-0
Fixed some ratelimits that were hardcoded and ammended docker compose…
2025-10-22 15:37:50 +01:00
Muhammad Ibrahim
de449c547f Fixed some ratelimits that were hardcoded and ammended docker compose to take into consideration rate limits 2025-10-22 15:22:14 +01:00
9 Technology Group LTD
cd03f0e66a Merge pull request #206 from PatchMon/post1-3-0
Made the setup.sh regenerate the .env variables
2025-10-22 14:33:18 +01:00
Muhammad Ibrahim
a8bd09be89 Made the setup.sh regenerate the .env variables 2025-10-22 14:15:49 +01:00
9 Technology Group LTD
deb6bed1a6 Merge pull request #204 from PatchMon/post1-3-0
Improving the setup.sh script to handle the nginx configuration changes on bare-metal type instances.

Also amended the env.example files to suit.
2025-10-22 13:47:03 +01:00
Muhammad Ibrahim
3ae8422487 modified nginx config for updates 2025-10-22 12:12:06 +01:00
9 Technology Group LTD
0189a307ef Merge pull request #194 from PatchMon/feature/go-agent
I think we are ready now for 1.3.0 :)
2025-10-21 21:40:15 +01:00
9 Technology Group LTD
00abbc8c62 Merge pull request #191 from PatchMon/feature/go-agent
Feature/go agent
2025-10-20 23:06:35 +01:00
9 Technology Group LTD
c9aef78912 Merge pull request #190 from PatchMon/feature/go-agent
Remove /bullboard from caching
2025-10-20 20:26:58 +01:00
9 Technology Group LTD
fd2df0729e Merge pull request #189 from PatchMon/feature/go-agent
added bullboard url for docker nginx template
2025-10-20 19:46:50 +01:00
9 Technology Group LTD
d7f7b24f8f Merge pull request #188 from PatchMon/feature/go-agent
Added axios in package.json
2025-10-20 19:21:07 +01:00
9 Technology Group LTD
1ef2308d56 Agent version detection and added nginx template 2025-10-20 18:55:43 +01:00
9 Technology Group LTD
fcd1b52e0e Merge pull request #186 from PatchMon/feature/go-agent
Bull Board
2025-10-19 20:58:03 +01:00
9 Technology Group LTD
5be8e01aa3 Merge pull request #185 from PatchMon/feature/go-agent
Modified the proxmox_auto-enroll.sh script to suit the new method
2025-10-19 19:03:17 +01:00
9 Technology Group LTD
293733dc0b Merge pull request #183 from PatchMon/feature/go-agent
Improved detection logic and upgrade mechanism using intermeditary sc…
2025-10-19 18:01:34 +01:00
9 Technology Group LTD
c7ab40e4a2 Merge pull request #182 from PatchMon/feature/go-agent
Fixed upgrade detection logic
2025-10-18 21:59:48 +01:00
6 changed files with 337 additions and 28 deletions

View File

@@ -295,7 +295,7 @@ app.disable("x-powered-by");
// Rate limiting with monitoring // Rate limiting with monitoring
const limiter = rateLimit({ const limiter = rateLimit({
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10) || 15 * 60 * 1000, windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS, 10) || 15 * 60 * 1000,
max: parseInt(process.env.RATE_LIMIT_MAX, 10) || 100, max: parseInt(process.env.RATE_LIMIT_MAX, 10) || 5000,
message: { message: {
error: "Too many requests from this IP, please try again later.", error: "Too many requests from this IP, please try again later.",
retryAfter: Math.ceil( retryAfter: Math.ceil(
@@ -424,7 +424,7 @@ const apiVersion = process.env.API_VERSION || "v1";
const authLimiter = rateLimit({ const authLimiter = rateLimit({
windowMs: windowMs:
parseInt(process.env.AUTH_RATE_LIMIT_WINDOW_MS, 10) || 10 * 60 * 1000, parseInt(process.env.AUTH_RATE_LIMIT_WINDOW_MS, 10) || 10 * 60 * 1000,
max: parseInt(process.env.AUTH_RATE_LIMIT_MAX, 10) || 20, max: parseInt(process.env.AUTH_RATE_LIMIT_MAX, 10) || 500,
message: { message: {
error: "Too many authentication requests, please try again later.", error: "Too many authentication requests, please try again later.",
retryAfter: Math.ceil( retryAfter: Math.ceil(
@@ -438,7 +438,7 @@ const authLimiter = rateLimit({
}); });
const agentLimiter = rateLimit({ const agentLimiter = rateLimit({
windowMs: parseInt(process.env.AGENT_RATE_LIMIT_WINDOW_MS, 10) || 60 * 1000, windowMs: parseInt(process.env.AGENT_RATE_LIMIT_WINDOW_MS, 10) || 60 * 1000,
max: parseInt(process.env.AGENT_RATE_LIMIT_MAX, 10) || 120, max: parseInt(process.env.AGENT_RATE_LIMIT_MAX, 10) || 1000,
message: { message: {
error: "Too many agent requests, please try again later.", error: "Too many agent requests, please try again later.",
retryAfter: Math.ceil( retryAfter: Math.ceil(

View File

@@ -50,6 +50,13 @@ services:
SERVER_HOST: localhost SERVER_HOST: localhost
SERVER_PORT: 3000 SERVER_PORT: 3000
CORS_ORIGIN: http://localhost:3000 CORS_ORIGIN: http://localhost:3000
# Rate Limiting (times in milliseconds)
RATE_LIMIT_WINDOW_MS: 900000
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
# Redis Configuration # Redis Configuration
REDIS_HOST: redis REDIS_HOST: redis
REDIS_PORT: 6379 REDIS_PORT: 6379

View File

@@ -56,6 +56,13 @@ services:
SERVER_HOST: localhost SERVER_HOST: localhost
SERVER_PORT: 3000 SERVER_PORT: 3000
CORS_ORIGIN: http://localhost:3000 CORS_ORIGIN: http://localhost:3000
# Rate Limiting (times in milliseconds)
RATE_LIMIT_WINDOW_MS: 900000
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
# Redis Configuration # Redis Configuration
REDIS_HOST: redis REDIS_HOST: redis
REDIS_PORT: 6379 REDIS_PORT: 6379

View File

@@ -32,7 +32,7 @@
"devDependencies": { "devDependencies": {
"@types/react": "^18.3.14", "@types/react": "^18.3.14",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^5.0.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",

42
package-lock.json generated
View File

@@ -83,7 +83,7 @@
"devDependencies": { "devDependencies": {
"@types/react": "^18.3.14", "@types/react": "^18.3.14",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^5.0.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"postcss": "^8.5.6", "postcss": "^8.5.6",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
@@ -142,6 +142,7 @@
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3", "@babel/generator": "^7.28.3",
@@ -595,6 +596,7 @@
"resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-6.13.1.tgz", "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-6.13.1.tgz",
"integrity": "sha512-DzPjCFzjEbDukhfSd7nLdTLVKIv5waARQuAXETSRqiKTN4vSA1KNdaJ8p72YwHujKO19yFW1zWjNKrzsa8DCIg==", "integrity": "sha512-DzPjCFzjEbDukhfSd7nLdTLVKIv5waARQuAXETSRqiKTN4vSA1KNdaJ8p72YwHujKO19yFW1zWjNKrzsa8DCIg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@bull-board/api": "6.13.1" "@bull-board/api": "6.13.1"
} }
@@ -636,6 +638,7 @@
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/accessibility": "^3.1.1",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
@@ -1501,9 +1504,9 @@
} }
}, },
"node_modules/@rolldown/pluginutils": { "node_modules/@rolldown/pluginutils": {
"version": "1.0.0-beta.27", "version": "1.0.0-beta.43",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.43.tgz",
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", "integrity": "sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -1938,6 +1941,7 @@
"integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/prop-types": "*", "@types/prop-types": "*",
"csstype": "^3.0.2" "csstype": "^3.0.2"
@@ -1960,21 +1964,21 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vitejs/plugin-react": { "node_modules/@vitejs/plugin-react": {
"version": "4.7.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.0.tgz",
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", "integrity": "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/core": "^7.28.0", "@babel/core": "^7.28.4",
"@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-self": "^7.27.1",
"@babel/plugin-transform-react-jsx-source": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1",
"@rolldown/pluginutils": "1.0.0-beta.27", "@rolldown/pluginutils": "1.0.0-beta.43",
"@types/babel__core": "^7.20.5", "@types/babel__core": "^7.20.5",
"react-refresh": "^0.17.0" "react-refresh": "^0.18.0"
}, },
"engines": { "engines": {
"node": "^14.18.0 || >=16.0.0" "node": "^20.19.0 || >=22.12.0"
}, },
"peerDependencies": { "peerDependencies": {
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
@@ -2235,6 +2239,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.8.3", "baseline-browser-mapping": "^2.8.3",
"caniuse-lite": "^1.0.30001741", "caniuse-lite": "^1.0.30001741",
@@ -2454,6 +2459,7 @@
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz",
"integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@kurkle/color": "^0.3.0" "@kurkle/color": "^0.3.0"
}, },
@@ -3160,6 +3166,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
@@ -4978,6 +4985,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.11",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
@@ -5121,6 +5129,7 @@
"devOptional": true, "devOptional": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"@prisma/config": "6.16.2", "@prisma/config": "6.16.2",
"@prisma/engines": "6.16.2" "@prisma/engines": "6.16.2"
@@ -5342,6 +5351,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
}, },
@@ -5364,6 +5374,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0", "loose-envify": "^1.1.0",
"scheduler": "^0.23.2" "scheduler": "^0.23.2"
@@ -5382,9 +5393,9 @@
} }
}, },
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.17.0", "version": "0.18.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
"integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -6211,6 +6222,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -6399,6 +6411,7 @@
"integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",
@@ -6492,6 +6505,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },

301
setup.sh
View File

@@ -51,12 +51,12 @@ function log_message() {
} }
DEPLOYMENT_BRANCH="main" DEPLOYMENT_BRANCH="main"
GITHUB_REPO="" GITHUB_REPO=""
DB_SAFE_DB_DB_USER="" DB_SAFE_NAME=""
DB_PASS="" DB_PASS=""
JWT_SECRET="" JWT_SECRET=""
BACKEND_PORT="" BACKEND_PORT=""
APP_DIR="" APP_DIR=""
SERVICE_USE_LETSENCRYPT="true" # Will be set based on user input USE_LETSENCRYPT="false" # Will be set based on user input
SERVER_PROTOCOL_SEL="https" SERVER_PROTOCOL_SEL="https"
SERVER_PORT_SEL="" # Will be set to BACKEND_PORT in init_instance_vars SERVER_PORT_SEL="" # Will be set to BACKEND_PORT in init_instance_vars
SETUP_NGINX="true" SETUP_NGINX="true"
@@ -449,15 +449,47 @@ find_next_redis_db() {
local db_num=0 local db_num=0
local max_attempts=16 # Redis default is 16 databases local max_attempts=16 # Redis default is 16 databases
# Check if Redis requires authentication
local test_output
test_output=$(redis-cli -h localhost -p 6379 ping 2>&1)
# Determine auth requirements
local auth_required=false
local redis_auth_args=""
if echo "$test_output" | grep -q "NOAUTH\|WRONGPASS"; then
auth_required=true
# Try to load admin credentials if ACL file exists
if [ -f /etc/redis/users.acl ] && grep -q "^user admin" /etc/redis/users.acl; then
# Redis is configured with ACL - try to extract admin password
print_info "Redis requires authentication, attempting with admin credentials..."
# For multi-instance setups, we can't know the admin password yet
# So we'll just use database 0 as default
print_info "Using database 0 (Redis ACL already configured)"
echo "0"
return 0
fi
fi
while [ $db_num -lt $max_attempts ]; do while [ $db_num -lt $max_attempts ]; do
# Test if database is empty # Test if database is empty
local key_count local key_count
local redis_output local redis_output
# Try to get database size # Try to get database size (with or without auth)
redis_output=$(redis-cli -h localhost -p 6379 -n "$db_num" DBSIZE 2>&1) redis_output=$(redis-cli -h localhost -p 6379 -n "$db_num" DBSIZE 2>&1)
# Check for errors # Check for authentication errors
if echo "$redis_output" | grep -q "NOAUTH\|WRONGPASS"; then
# If we hit auth errors and haven't configured yet, use database 0
print_info "Redis requires authentication, defaulting to database 0"
echo "0"
return 0
fi
# Check for other errors
if echo "$redis_output" | grep -q "ERR"; then if echo "$redis_output" | grep -q "ERR"; then
if echo "$redis_output" | grep -q "invalid DB index"; then if echo "$redis_output" | grep -q "invalid DB index"; then
print_warning "Reached maximum database limit at database $db_num" print_warning "Reached maximum database limit at database $db_num"
@@ -471,7 +503,7 @@ find_next_redis_db() {
key_count="$redis_output" key_count="$redis_output"
# If database is empty, use it # If database is empty, use it
if [ "$key_count" = "0" ]; then if [ "$key_count" = "0" ] || [ "$key_count" = "(integer) 0" ]; then
print_status "Found available Redis database: $db_num (empty)" print_status "Found available Redis database: $db_num (empty)"
echo "$db_num" echo "$db_num"
return 0 return 0
@@ -1238,6 +1270,7 @@ server {
# Static assets caching (exclude Bull Board assets) # Static assets caching (exclude Bull Board assets)
location ~* ^/(?!bullboard).*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { location ~* ^/(?!bullboard).*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
root $app_dir/frontend/dist;
expires 1y; expires 1y;
add_header Cache-Control "public, immutable"; add_header Cache-Control "public, immutable";
} }
@@ -1320,6 +1353,7 @@ server {
# Static assets caching (exclude Bull Board assets) # Static assets caching (exclude Bull Board assets)
location ~* ^/(?!bullboard).*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { location ~* ^/(?!bullboard).*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
root $app_dir/frontend/dist;
expires 1y; expires 1y;
add_header Cache-Control "public, immutable"; add_header Cache-Control "public, immutable";
} }
@@ -1588,7 +1622,13 @@ EOF
chmod 644 "$APP_DIR/patchmon-install.log" || true chmod 644 "$APP_DIR/patchmon-install.log" || true
fi fi
print_status "Unified deployment info saved to: $SUMMARY_FILE" # Verify file was created
if [ -f "$SUMMARY_FILE" ]; then
print_status "Deployment summary appended to: $SUMMARY_FILE"
else
print_error "⚠️ Failed to append to deployment-info.txt file"
return 1
fi
} }
# Email notification function removed for self-hosting deployment # Email notification function removed for self-hosting deployment
@@ -1630,7 +1670,7 @@ Redis Information:
- Host: localhost - Host: localhost
- Port: 6379 - Port: 6379
- User: $REDIS_USER - User: $REDIS_USER
- Password: $REDIS_PASSWORD - Password: $REDIS_USER_PASSWORD
- Database: $REDIS_DB - Database: $REDIS_DB
Networking: Networking:
@@ -1663,7 +1703,14 @@ EOF
chmod 644 "$INFO_FILE" chmod 644 "$INFO_FILE"
chown "$INSTANCE_USER:$INSTANCE_USER" "$INFO_FILE" chown "$INSTANCE_USER:$INSTANCE_USER" "$INFO_FILE"
print_status "Deployment information saved to: $INFO_FILE" # Verify file was created
if [ -f "$INFO_FILE" ]; then
print_status "Deployment information saved to: $INFO_FILE"
print_info "File details: $(ls -lh "$INFO_FILE" | awk '{print $5, $9}')"
else
print_error "⚠️ Failed to create deployment-info.txt file"
return 1
fi
} }
# Restart PatchMon service # Restart PatchMon service
@@ -1785,7 +1832,7 @@ deploy_instance() {
echo -e "${YELLOW}Database User: $DB_USER${NC}" echo -e "${YELLOW}Database User: $DB_USER${NC}"
echo -e "${YELLOW}Database Password: $DB_PASS${NC}" echo -e "${YELLOW}Database Password: $DB_PASS${NC}"
echo -e "${YELLOW}Redis User: $REDIS_USER${NC}" echo -e "${YELLOW}Redis User: $REDIS_USER${NC}"
echo -e "${YELLOW}Redis Password: $REDIS_PASSWORD${NC}" echo -e "${YELLOW}Redis User Password: $REDIS_USER_PASSWORD${NC}"
echo -e "${YELLOW}Redis Database: $REDIS_DB${NC}" echo -e "${YELLOW}Redis Database: $REDIS_DB${NC}"
echo -e "${YELLOW}JWT Secret: $JWT_SECRET${NC}" echo -e "${YELLOW}JWT Secret: $JWT_SECRET${NC}"
echo -e "${YELLOW}Backend Port: $BACKEND_PORT${NC}" echo -e "${YELLOW}Backend Port: $BACKEND_PORT${NC}"
@@ -1858,7 +1905,8 @@ deploy_instance() {
echo "" echo ""
print_info "Next steps:" print_info "Next steps:"
echo " • Visit your URL: $SERVER_PROTOCOL_SEL://$FQDN (ensure DNS is configured)" echo " • Visit your URL: $SERVER_PROTOCOL_SEL://$FQDN (ensure DNS is configured)"
echo "Useful deployment information is stored in: $APP_DIR/deployment-info.txt" echo "Deployment information file: $APP_DIR/deployment-info.txt"
echo " • View deployment info: cat $APP_DIR/deployment-info.txt"
echo "" echo ""
# Suppress JSON echo to terminal; details already logged and saved to summary/credentials files # Suppress JSON echo to terminal; details already logged and saved to summary/credentials files
@@ -2097,6 +2145,236 @@ EOF
return 0 return 0
} }
# Update .env file with missing variables while preserving existing values
update_env_file() {
print_info "Checking .env file for missing variables..."
local env_file="$instance_dir/backend/.env"
if [ ! -f "$env_file" ]; then
print_error ".env file not found at $env_file"
return 1
fi
# Backup existing .env
cp "$env_file" "$env_file.backup.$(date +%Y%m%d_%H%M%S)"
print_info "Backed up existing .env file"
# Source existing .env to get current values
set -a
source "$env_file"
set +a
# Define all expected variables with their defaults
# Only set if not already defined (preserves user values)
# Database (already loaded from .env)
: ${PM_DB_CONN_MAX_ATTEMPTS:=30}
: ${PM_DB_CONN_WAIT_INTERVAL:=2}
# JWT (JWT_SECRET should already exist)
: ${JWT_EXPIRES_IN:=1h}
: ${JWT_REFRESH_EXPIRES_IN:=7d}
# Server
: ${NODE_ENV:=production}
# API
: ${API_VERSION:=v1}
# CORS (preserve existing or use current FQDN)
if [ -z "$CORS_ORIGIN" ]; then
# Determine protocol from existing URL or default to https
if echo "$DATABASE_URL" | grep -q "localhost"; then
CORS_ORIGIN="http://$SELECTED_INSTANCE"
else
CORS_ORIGIN="https://$SELECTED_INSTANCE"
fi
fi
# Session
: ${SESSION_INACTIVITY_TIMEOUT_MINUTES:=30}
# User
: ${DEFAULT_USER_ROLE:=user}
# Rate Limiting
: ${RATE_LIMIT_WINDOW_MS:=900000}
: ${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}
# Redis (already handled by update_redis_configuration if missing)
: ${REDIS_HOST:=localhost}
: ${REDIS_PORT:=6379}
: ${REDIS_DB:=0}
# Logging
: ${LOG_LEVEL:=info}
: ${ENABLE_LOGGING:=true}
# TFA
: ${TFA_REMEMBER_ME_EXPIRES_IN:=30d}
: ${TFA_MAX_REMEMBER_SESSIONS:=5}
: ${TFA_SUSPICIOUS_ACTIVITY_THRESHOLD:=3}
# Track which variables were added
local added_vars=()
# Check and add missing variables
if ! grep -q "^PM_DB_CONN_MAX_ATTEMPTS=" "$env_file"; then
added_vars+=("PM_DB_CONN_MAX_ATTEMPTS")
fi
if ! grep -q "^PM_DB_CONN_WAIT_INTERVAL=" "$env_file"; then
added_vars+=("PM_DB_CONN_WAIT_INTERVAL")
fi
if ! grep -q "^JWT_EXPIRES_IN=" "$env_file"; then
added_vars+=("JWT_EXPIRES_IN")
fi
if ! grep -q "^JWT_REFRESH_EXPIRES_IN=" "$env_file"; then
added_vars+=("JWT_REFRESH_EXPIRES_IN")
fi
if ! grep -q "^API_VERSION=" "$env_file"; then
added_vars+=("API_VERSION")
fi
if ! grep -q "^CORS_ORIGIN=" "$env_file"; then
added_vars+=("CORS_ORIGIN")
fi
if ! grep -q "^SESSION_INACTIVITY_TIMEOUT_MINUTES=" "$env_file"; then
added_vars+=("SESSION_INACTIVITY_TIMEOUT_MINUTES")
fi
if ! grep -q "^DEFAULT_USER_ROLE=" "$env_file"; then
added_vars+=("DEFAULT_USER_ROLE")
fi
if ! grep -q "^RATE_LIMIT_WINDOW_MS=" "$env_file"; then
added_vars+=("RATE_LIMIT_WINDOW_MS")
fi
if ! grep -q "^RATE_LIMIT_MAX=" "$env_file"; then
added_vars+=("RATE_LIMIT_MAX")
fi
if ! grep -q "^AUTH_RATE_LIMIT_WINDOW_MS=" "$env_file"; then
added_vars+=("AUTH_RATE_LIMIT_WINDOW_MS")
fi
if ! grep -q "^AUTH_RATE_LIMIT_MAX=" "$env_file"; then
added_vars+=("AUTH_RATE_LIMIT_MAX")
fi
if ! grep -q "^AGENT_RATE_LIMIT_WINDOW_MS=" "$env_file"; then
added_vars+=("AGENT_RATE_LIMIT_WINDOW_MS")
fi
if ! grep -q "^AGENT_RATE_LIMIT_MAX=" "$env_file"; then
added_vars+=("AGENT_RATE_LIMIT_MAX")
fi
if ! grep -q "^LOG_LEVEL=" "$env_file"; then
added_vars+=("LOG_LEVEL")
fi
if ! grep -q "^ENABLE_LOGGING=" "$env_file"; then
added_vars+=("ENABLE_LOGGING")
fi
if ! grep -q "^TFA_REMEMBER_ME_EXPIRES_IN=" "$env_file"; then
added_vars+=("TFA_REMEMBER_ME_EXPIRES_IN")
fi
if ! grep -q "^TFA_MAX_REMEMBER_SESSIONS=" "$env_file"; then
added_vars+=("TFA_MAX_REMEMBER_SESSIONS")
fi
if ! grep -q "^TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=" "$env_file"; then
added_vars+=("TFA_SUSPICIOUS_ACTIVITY_THRESHOLD")
fi
# If there are missing variables, add them
if [ ${#added_vars[@]} -gt 0 ]; then
print_info "Adding ${#added_vars[@]} missing environment variable(s)..."
cat >> "$env_file" << EOF
# Environment variables added during update on $(date)
EOF
# Add database config if missing
if printf '%s\n' "${added_vars[@]}" | grep -q "PM_DB_CONN_MAX_ATTEMPTS"; then
echo "PM_DB_CONN_MAX_ATTEMPTS=$PM_DB_CONN_MAX_ATTEMPTS" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "PM_DB_CONN_WAIT_INTERVAL"; then
echo "PM_DB_CONN_WAIT_INTERVAL=$PM_DB_CONN_WAIT_INTERVAL" >> "$env_file"
fi
# Add JWT config if missing
if printf '%s\n' "${added_vars[@]}" | grep -q "JWT_EXPIRES_IN"; then
echo "JWT_EXPIRES_IN=$JWT_EXPIRES_IN" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "JWT_REFRESH_EXPIRES_IN"; then
echo "JWT_REFRESH_EXPIRES_IN=$JWT_REFRESH_EXPIRES_IN" >> "$env_file"
fi
# Add API config if missing
if printf '%s\n' "${added_vars[@]}" | grep -q "API_VERSION"; then
echo "API_VERSION=$API_VERSION" >> "$env_file"
fi
# Add CORS config if missing
if printf '%s\n' "${added_vars[@]}" | grep -q "CORS_ORIGIN"; then
echo "CORS_ORIGIN=$CORS_ORIGIN" >> "$env_file"
fi
# Add session config if missing
if printf '%s\n' "${added_vars[@]}" | grep -q "SESSION_INACTIVITY_TIMEOUT_MINUTES"; then
echo "SESSION_INACTIVITY_TIMEOUT_MINUTES=$SESSION_INACTIVITY_TIMEOUT_MINUTES" >> "$env_file"
fi
# Add user config if missing
if printf '%s\n' "${added_vars[@]}" | grep -q "DEFAULT_USER_ROLE"; then
echo "DEFAULT_USER_ROLE=$DEFAULT_USER_ROLE" >> "$env_file"
fi
# Add rate limiting if missing
if printf '%s\n' "${added_vars[@]}" | grep -q "RATE_LIMIT_WINDOW_MS"; then
echo "RATE_LIMIT_WINDOW_MS=$RATE_LIMIT_WINDOW_MS" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "RATE_LIMIT_MAX"; then
echo "RATE_LIMIT_MAX=$RATE_LIMIT_MAX" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "AUTH_RATE_LIMIT_WINDOW_MS"; then
echo "AUTH_RATE_LIMIT_WINDOW_MS=$AUTH_RATE_LIMIT_WINDOW_MS" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "AUTH_RATE_LIMIT_MAX"; then
echo "AUTH_RATE_LIMIT_MAX=$AUTH_RATE_LIMIT_MAX" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "AGENT_RATE_LIMIT_WINDOW_MS"; then
echo "AGENT_RATE_LIMIT_WINDOW_MS=$AGENT_RATE_LIMIT_WINDOW_MS" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "AGENT_RATE_LIMIT_MAX"; then
echo "AGENT_RATE_LIMIT_MAX=$AGENT_RATE_LIMIT_MAX" >> "$env_file"
fi
# Add logging config if missing
if printf '%s\n' "${added_vars[@]}" | grep -q "LOG_LEVEL"; then
echo "LOG_LEVEL=$LOG_LEVEL" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "ENABLE_LOGGING"; then
echo "ENABLE_LOGGING=$ENABLE_LOGGING" >> "$env_file"
fi
# Add TFA config if missing
if printf '%s\n' "${added_vars[@]}" | grep -q "TFA_REMEMBER_ME_EXPIRES_IN"; then
echo "TFA_REMEMBER_ME_EXPIRES_IN=$TFA_REMEMBER_ME_EXPIRES_IN" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "TFA_MAX_REMEMBER_SESSIONS"; then
echo "TFA_MAX_REMEMBER_SESSIONS=$TFA_MAX_REMEMBER_SESSIONS" >> "$env_file"
fi
if printf '%s\n' "${added_vars[@]}" | grep -q "TFA_SUSPICIOUS_ACTIVITY_THRESHOLD"; then
echo "TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=$TFA_SUSPICIOUS_ACTIVITY_THRESHOLD" >> "$env_file"
fi
print_status ".env file updated with ${#added_vars[@]} new variable(s)"
print_info "Added variables: ${added_vars[*]}"
else
print_status ".env file is up to date - no missing variables"
fi
return 0
}
# Update nginx configuration for existing installation # Update nginx configuration for existing installation
update_nginx_configuration() { update_nginx_configuration() {
print_info "Updating nginx configuration..." print_info "Updating nginx configuration..."
@@ -2274,6 +2552,9 @@ update_installation() {
# Check and update Redis configuration if needed (for legacy installations) # Check and update Redis configuration if needed (for legacy installations)
update_redis_configuration update_redis_configuration
# Update .env file with any missing variables (preserve existing values)
update_env_file
# Update nginx configuration with latest improvements # Update nginx configuration with latest improvements
update_nginx_configuration update_nginx_configuration