mirror of
				https://github.com/9technologygroup/patchmon.net.git
				synced 2025-10-31 12:03:47 +00:00 
			
		
		
		
	Relaid out settings page and configured agent and other communication to use curl flags which can be configured to ignore ssl cert if self-hosted.
This commit is contained in:
		| @@ -0,0 +1,10 @@ | ||||
| -- Fix dashboard preferences unique constraint | ||||
| -- This migration fixes the unique constraint on dashboard_preferences table | ||||
|  | ||||
| -- Drop existing indexes if they exist | ||||
| DROP INDEX IF EXISTS "dashboard_preferences_card_id_key"; | ||||
| DROP INDEX IF EXISTS "dashboard_preferences_user_id_card_id_key"; | ||||
| DROP INDEX IF EXISTS "dashboard_preferences_user_id_key"; | ||||
|  | ||||
| -- Add the correct unique constraint | ||||
| ALTER TABLE "dashboard_preferences" ADD CONSTRAINT "dashboard_preferences_user_id_card_id_key" UNIQUE ("user_id", "card_id"); | ||||
| @@ -0,0 +1,4 @@ | ||||
| -- Add ignore_ssl_self_signed column to settings table | ||||
| -- This allows users to configure whether curl commands should ignore SSL certificate validation | ||||
|  | ||||
| ALTER TABLE "settings" ADD COLUMN "ignore_ssl_self_signed" BOOLEAN NOT NULL DEFAULT false; | ||||
| @@ -7,7 +7,6 @@ datasource db { | ||||
|   url      = env("DATABASE_URL") | ||||
| } | ||||
|  | ||||
|  | ||||
| model dashboard_preferences { | ||||
|   id         String   @id | ||||
|   user_id    String | ||||
| @@ -157,6 +156,7 @@ model settings { | ||||
|   update_available  Boolean   @default(false) | ||||
|   signup_enabled    Boolean   @default(false) | ||||
|   default_user_role String    @default("user") | ||||
|   ignore_ssl_self_signed Boolean @default(false) | ||||
| } | ||||
|  | ||||
| model update_history { | ||||
| @@ -175,8 +175,6 @@ model users { | ||||
|   username              String                  @unique | ||||
|   email                 String                  @unique | ||||
|   password_hash         String | ||||
|   first_name            String? | ||||
|   last_name             String? | ||||
|   role                  String                  @default("admin") | ||||
|   is_active             Boolean                 @default(true) | ||||
|   last_login            DateTime? | ||||
| @@ -185,5 +183,7 @@ model users { | ||||
|   tfa_backup_codes      String? | ||||
|   tfa_enabled           Boolean                 @default(false) | ||||
|   tfa_secret            String? | ||||
|   first_name            String? | ||||
|   last_name             String? | ||||
|   dashboard_preferences dashboard_preferences[] | ||||
| } | ||||
|   | ||||
| @@ -45,11 +45,26 @@ router.get("/agent/download", async (req, res) => { | ||||
| 		} | ||||
|  | ||||
| 		// Read file and convert line endings | ||||
| 		const scriptContent = fs | ||||
| 		let scriptContent = fs | ||||
| 			.readFileSync(agentPath, "utf8") | ||||
| 			.replace(/\r\n/g, "\n") | ||||
| 			.replace(/\r/g, "\n"); | ||||
|  | ||||
| 		// Determine curl flags dynamically from settings for consistency | ||||
| 		let curlFlags = "-s"; | ||||
| 		try { | ||||
| 			const settings = await prisma.settings.findFirst(); | ||||
| 			if (settings && settings.ignore_ssl_self_signed === true) { | ||||
| 				curlFlags = "-sk"; | ||||
| 			} | ||||
| 		} catch (_) {} | ||||
|  | ||||
| 		// Inject the curl flags into the script | ||||
| 		scriptContent = scriptContent.replace( | ||||
| 			'CURL_FLAGS=""', | ||||
| 			`CURL_FLAGS="${curlFlags}"`, | ||||
| 		); | ||||
|  | ||||
| 		res.setHeader("Content-Type", "application/x-shellscript"); | ||||
| 		res.setHeader( | ||||
| 			"Content-Disposition", | ||||
| @@ -1101,11 +1116,21 @@ router.get("/install", async (req, res) => { | ||||
| 			); | ||||
| 		} | ||||
|  | ||||
| 		// Inject the API credentials and server URL into the script as environment variables | ||||
| 		// Determine curl flags dynamically from settings (ignore self-signed) | ||||
| 		let curlFlags = "-s"; | ||||
| 		try { | ||||
| 			const settings = await prisma.settings.findFirst(); | ||||
| 			if (settings && settings.ignore_ssl_self_signed === true) { | ||||
| 				curlFlags = "-sk"; | ||||
| 			} | ||||
| 		} catch (_) {} | ||||
|  | ||||
| 		// Inject the API credentials, server URL, and curl flags into the script | ||||
| 		const envVars = `#!/bin/bash | ||||
| export PATCHMON_URL="${serverUrl}" | ||||
| export API_ID="${host.api_id}" | ||||
| export API_KEY="${host.api_key}" | ||||
| export CURL_FLAGS="${curlFlags}" | ||||
|  | ||||
| `; | ||||
|  | ||||
| @@ -1141,7 +1166,24 @@ router.get("/remove", async (_req, res) => { | ||||
| 		} | ||||
|  | ||||
| 		// Read the script content | ||||
| 		const script = fs.readFileSync(scriptPath, "utf8"); | ||||
| 		let script = fs.readFileSync(scriptPath, "utf8"); | ||||
|  | ||||
| 		// Convert line endings | ||||
| 		script = script.replace(/\r\n/g, "\n").replace(/\r/g, "\n"); | ||||
|  | ||||
| 		// Determine curl flags dynamically from settings for consistency | ||||
| 		let curlFlags = "-s"; | ||||
| 		try { | ||||
| 			const settings = await prisma.settings.findFirst(); | ||||
| 			if (settings && settings.ignore_ssl_self_signed === true) { | ||||
| 				curlFlags = "-sk"; | ||||
| 			} | ||||
| 		} catch (_) {} | ||||
|  | ||||
| 		// Prepend environment for CURL_FLAGS so script can use it if needed | ||||
| 		const envPrefix = `#!/bin/bash\nexport CURL_FLAGS="${curlFlags}"\n\n`; | ||||
| 		script = script.replace(/^#!/, "#"); | ||||
| 		script = envPrefix + script; | ||||
|  | ||||
| 		// Set appropriate headers for script download | ||||
| 		res.setHeader("Content-Type", "text/plain"); | ||||
|   | ||||
| @@ -165,19 +165,31 @@ router.put( | ||||
| 	requireManageSettings, | ||||
| 	[ | ||||
| 		body("serverProtocol") | ||||
| 			.optional() | ||||
| 			.isIn(["http", "https"]) | ||||
| 			.withMessage("Protocol must be http or https"), | ||||
| 		body("serverHost") | ||||
| 			.optional() | ||||
| 			.isLength({ min: 1 }) | ||||
| 			.withMessage("Server host is required"), | ||||
| 		body("serverPort") | ||||
| 			.optional() | ||||
| 			.isInt({ min: 1, max: 65535 }) | ||||
| 			.withMessage("Port must be between 1 and 65535"), | ||||
| 		body("updateInterval") | ||||
| 			.optional() | ||||
| 			.isInt({ min: 5, max: 1440 }) | ||||
| 			.withMessage("Update interval must be between 5 and 1440 minutes"), | ||||
| 		body("autoUpdate").isBoolean().withMessage("Auto update must be a boolean"), | ||||
| 		body("autoUpdate") | ||||
| 			.optional() | ||||
| 			.isBoolean() | ||||
| 			.withMessage("Auto update must be a boolean"), | ||||
| 		body("ignoreSslSelfSigned") | ||||
| 			.optional() | ||||
| 			.isBoolean() | ||||
| 			.withMessage("Ignore SSL self-signed must be a boolean"), | ||||
| 		body("signupEnabled") | ||||
| 			.optional() | ||||
| 			.isBoolean() | ||||
| 			.withMessage("Signup enabled must be a boolean"), | ||||
| 		body("defaultUserRole") | ||||
| @@ -218,6 +230,7 @@ router.put( | ||||
| 				serverPort, | ||||
| 				updateInterval, | ||||
| 				autoUpdate, | ||||
| 				ignoreSslSelfSigned, | ||||
| 				signupEnabled, | ||||
| 				defaultUserRole, | ||||
| 				githubRepoUrl, | ||||
| @@ -229,32 +242,43 @@ router.put( | ||||
| 			const currentSettings = await getSettings(); | ||||
| 			const oldUpdateInterval = currentSettings.update_interval; | ||||
|  | ||||
| 			// Update settings using the service | ||||
| 			const normalizedInterval = normalizeUpdateInterval(updateInterval || 60); | ||||
| 			// Build update object with only provided fields | ||||
| 			const updateData = {}; | ||||
|  | ||||
| 			const updatedSettings = await updateSettings(currentSettings.id, { | ||||
| 				server_protocol: serverProtocol, | ||||
| 				server_host: serverHost, | ||||
| 				server_port: serverPort, | ||||
| 				update_interval: normalizedInterval, | ||||
| 				auto_update: autoUpdate || false, | ||||
| 				signup_enabled: signupEnabled || false, | ||||
| 				default_user_role: | ||||
| 					defaultUserRole || process.env.DEFAULT_USER_ROLE || "user", | ||||
| 				github_repo_url: | ||||
| 					githubRepoUrl !== undefined | ||||
| 						? githubRepoUrl | ||||
| 						: "git@github.com:9technologygroup/patchmon.net.git", | ||||
| 				repository_type: repositoryType || "public", | ||||
| 				ssh_key_path: sshKeyPath || null, | ||||
| 			}); | ||||
| 			if (serverProtocol !== undefined) | ||||
| 				updateData.server_protocol = serverProtocol; | ||||
| 			if (serverHost !== undefined) updateData.server_host = serverHost; | ||||
| 			if (serverPort !== undefined) updateData.server_port = serverPort; | ||||
| 			if (updateInterval !== undefined) { | ||||
| 				updateData.update_interval = normalizeUpdateInterval(updateInterval); | ||||
| 			} | ||||
| 			if (autoUpdate !== undefined) updateData.auto_update = autoUpdate; | ||||
| 			if (ignoreSslSelfSigned !== undefined) | ||||
| 				updateData.ignore_ssl_self_signed = ignoreSslSelfSigned; | ||||
| 			if (signupEnabled !== undefined) | ||||
| 				updateData.signup_enabled = signupEnabled; | ||||
| 			if (defaultUserRole !== undefined) | ||||
| 				updateData.default_user_role = defaultUserRole; | ||||
| 			if (githubRepoUrl !== undefined) | ||||
| 				updateData.github_repo_url = githubRepoUrl; | ||||
| 			if (repositoryType !== undefined) | ||||
| 				updateData.repository_type = repositoryType; | ||||
| 			if (sshKeyPath !== undefined) updateData.ssh_key_path = sshKeyPath; | ||||
|  | ||||
| 			const updatedSettings = await updateSettings( | ||||
| 				currentSettings.id, | ||||
| 				updateData, | ||||
| 			); | ||||
|  | ||||
| 			console.log("Settings updated successfully:", updatedSettings); | ||||
|  | ||||
| 			// If update interval changed, trigger crontab updates on all hosts with auto-update enabled | ||||
| 			if (oldUpdateInterval !== normalizedInterval) { | ||||
| 			if ( | ||||
| 				updateInterval !== undefined && | ||||
| 				oldUpdateInterval !== updateData.update_interval | ||||
| 			) { | ||||
| 				console.log( | ||||
| 					`Update interval changed from ${oldUpdateInterval} to ${normalizedInterval} minutes. Triggering crontab updates...`, | ||||
| 					`Update interval changed from ${oldUpdateInterval} to ${updateData.update_interval} minutes. Triggering crontab updates...`, | ||||
| 				); | ||||
| 				await triggerCrontabUpdates(); | ||||
| 			} | ||||
|   | ||||
| @@ -43,6 +43,7 @@ async function createSettingsFromEnvironment() { | ||||
| 			update_interval: 60, | ||||
| 			auto_update: false, | ||||
| 			signup_enabled: false, | ||||
| 			ignore_ssl_self_signed: false, | ||||
| 			updated_at: new Date(), | ||||
| 		}, | ||||
| 	}); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user