mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-10-22 23:32:03 +00:00
added ability to specify deploy key path
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "settings" ADD COLUMN "ssh_key_path" TEXT;
|
@@ -180,6 +180,7 @@ model Settings {
|
||||
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
|
||||
sshKeyPath String? @map("ssh_key_path") // Optional SSH key path for deploy key authentication
|
||||
createdAt DateTime @map("created_at") @default(now())
|
||||
updatedAt DateTime @map("updated_at") @updatedAt
|
||||
|
||||
|
@@ -123,7 +123,8 @@ router.put('/', authenticateToken, requireManageSettings, [
|
||||
body('frontendUrl').isLength({ min: 1 }).withMessage('Frontend URL is required'),
|
||||
body('updateInterval').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('githubRepoUrl').optional().isLength({ min: 1 }).withMessage('GitHub repo URL must be a non-empty string')
|
||||
body('githubRepoUrl').optional().isLength({ min: 1 }).withMessage('GitHub repo URL must be a non-empty string'),
|
||||
body('sshKeyPath').optional().isLength({ min: 1 }).withMessage('SSH key path must be a non-empty string')
|
||||
], async (req, res) => {
|
||||
try {
|
||||
console.log('Settings update request body:', req.body);
|
||||
@@ -133,8 +134,8 @@ router.put('/', authenticateToken, requireManageSettings, [
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
|
||||
const { serverProtocol, serverHost, serverPort, frontendUrl, updateInterval, autoUpdate, githubRepoUrl } = req.body;
|
||||
console.log('Extracted values:', { serverProtocol, serverHost, serverPort, frontendUrl, updateInterval, autoUpdate, githubRepoUrl });
|
||||
const { serverProtocol, serverHost, serverPort, frontendUrl, updateInterval, autoUpdate, githubRepoUrl, sshKeyPath } = req.body;
|
||||
console.log('Extracted values:', { serverProtocol, serverHost, serverPort, frontendUrl, updateInterval, autoUpdate, githubRepoUrl, sshKeyPath });
|
||||
|
||||
// Construct server URL from components
|
||||
const serverUrl = `${serverProtocol}://${serverHost}:${serverPort}`;
|
||||
@@ -165,7 +166,8 @@ router.put('/', authenticateToken, requireManageSettings, [
|
||||
frontendUrl,
|
||||
updateInterval: updateInterval || 60,
|
||||
autoUpdate: autoUpdate || false,
|
||||
githubRepoUrl: githubRepoUrl || 'git@github.com:9technologygroup/patchmon.net.git'
|
||||
githubRepoUrl: githubRepoUrl || 'git@github.com:9technologygroup/patchmon.net.git',
|
||||
sshKeyPath: sshKeyPath || null
|
||||
}
|
||||
});
|
||||
console.log('Settings updated successfully:', settings);
|
||||
@@ -186,7 +188,8 @@ router.put('/', authenticateToken, requireManageSettings, [
|
||||
frontendUrl,
|
||||
updateInterval: updateInterval || 60,
|
||||
autoUpdate: autoUpdate || false,
|
||||
githubRepoUrl: githubRepoUrl || 'git@github.com:9technologygroup/patchmon.net.git'
|
||||
githubRepoUrl: githubRepoUrl || 'git@github.com:9technologygroup/patchmon.net.git',
|
||||
sshKeyPath: sshKeyPath || null
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -59,22 +59,56 @@ router.get('/check-updates', authenticateToken, requireManageSettings, async (re
|
||||
return res.status(400).json({ error: 'Invalid GitHub repository URL format' });
|
||||
}
|
||||
|
||||
// Use SSH to fetch latest tag from private repository
|
||||
// Use SSH with deploy keys (secure approach)
|
||||
const sshRepoUrl = `git@github.com:${owner}/${repo}.git`;
|
||||
|
||||
try {
|
||||
// Set up environment for SSH
|
||||
const sshKeyPath = process.env.HOME + '/.ssh/id_ed25519';
|
||||
let sshKeyPath = null;
|
||||
|
||||
// First, try to use the configured SSH key path from settings
|
||||
if (settings.sshKeyPath) {
|
||||
try {
|
||||
require('fs').accessSync(settings.sshKeyPath);
|
||||
sshKeyPath = settings.sshKeyPath;
|
||||
console.log(`Using configured SSH key at: ${sshKeyPath}`);
|
||||
} catch (e) {
|
||||
console.warn(`Configured SSH key path not accessible: ${settings.sshKeyPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
// If no configured path or it's not accessible, try common locations
|
||||
if (!sshKeyPath) {
|
||||
const possibleKeyPaths = [
|
||||
'/root/.ssh/id_ed25519', // Root user (if service runs as root)
|
||||
'/root/.ssh/id_rsa', // Root user RSA key
|
||||
'/home/patchmon/.ssh/id_ed25519', // PatchMon user
|
||||
'/home/patchmon/.ssh/id_rsa', // PatchMon user RSA key
|
||||
'/var/www/.ssh/id_ed25519', // Web user
|
||||
'/var/www/.ssh/id_rsa' // Web user RSA key
|
||||
];
|
||||
|
||||
for (const path of possibleKeyPaths) {
|
||||
try {
|
||||
require('fs').accessSync(path);
|
||||
sshKeyPath = path;
|
||||
console.log(`Found SSH key at: ${path}`);
|
||||
break;
|
||||
} catch (e) {
|
||||
// Key not found at this path, try next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sshKeyPath) {
|
||||
throw new Error('No SSH deploy key found. Please configure the SSH key path in settings or ensure a deploy key is installed in one of the expected locations.');
|
||||
}
|
||||
|
||||
const env = {
|
||||
...process.env,
|
||||
GIT_SSH_COMMAND: `ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no`
|
||||
GIT_SSH_COMMAND: `ssh -i ${sshKeyPath} -o StrictHostKeyChecking=no -o IdentitiesOnly=yes`
|
||||
};
|
||||
|
||||
console.log('SSH Key Path:', sshKeyPath);
|
||||
console.log('SSH Command:', env.GIT_SSH_COMMAND);
|
||||
console.log('Repository URL:', sshRepoUrl);
|
||||
|
||||
// Fetch the latest tag using SSH with explicit key
|
||||
// Fetch the latest tag using SSH with deploy key
|
||||
const { stdout: latestTag } = await execAsync(
|
||||
`git ls-remote --tags --sort=-version:refname ${sshRepoUrl} | head -n 1 | sed 's/.*refs\\/tags\\///' | sed 's/\\^{}//'`,
|
||||
{
|
||||
@@ -83,34 +117,12 @@ router.get('/check-updates', authenticateToken, requireManageSettings, async (re
|
||||
}
|
||||
);
|
||||
|
||||
console.log('Latest tag output:', latestTag);
|
||||
|
||||
const latestVersion = latestTag.trim().replace('v', ''); // Remove 'v' prefix
|
||||
const currentVersion = '1.2.3';
|
||||
|
||||
// Simple version comparison (assumes semantic versioning)
|
||||
const isUpdateAvailable = compareVersions(latestVersion, currentVersion) > 0;
|
||||
|
||||
// Get additional tag information
|
||||
let tagInfo = {};
|
||||
try {
|
||||
const { stdout: tagDetails } = await execAsync(
|
||||
`git ls-remote --tags ${sshRepoUrl} | grep "${latestTag.trim()}" | head -n 1`,
|
||||
{
|
||||
timeout: 5000,
|
||||
env: env
|
||||
}
|
||||
);
|
||||
|
||||
// Extract commit hash and other details if needed
|
||||
const parts = tagDetails.trim().split('\t');
|
||||
if (parts.length >= 2) {
|
||||
tagInfo.commitHash = parts[0];
|
||||
}
|
||||
} catch (tagDetailError) {
|
||||
console.warn('Could not fetch tag details:', tagDetailError.message);
|
||||
}
|
||||
|
||||
res.json({
|
||||
currentVersion,
|
||||
latestVersion,
|
||||
@@ -118,34 +130,40 @@ router.get('/check-updates', authenticateToken, requireManageSettings, async (re
|
||||
latestRelease: {
|
||||
tagName: latestTag.trim(),
|
||||
version: latestVersion,
|
||||
commitHash: tagInfo.commitHash,
|
||||
repository: `${owner}/${repo}`,
|
||||
sshUrl: sshRepoUrl
|
||||
sshUrl: sshRepoUrl,
|
||||
sshKeyUsed: sshKeyPath
|
||||
}
|
||||
});
|
||||
|
||||
} catch (sshError) {
|
||||
console.error('SSH Git error:', sshError.message);
|
||||
|
||||
// Check if it's a permission/access issue
|
||||
|
||||
if (sshError.message.includes('Permission denied') || sshError.message.includes('Host key verification failed')) {
|
||||
return res.status(403).json({
|
||||
return res.status(403).json({
|
||||
error: 'SSH access denied to repository',
|
||||
suggestion: 'Ensure your SSH key is properly configured and has access to the repository. Check your ~/.ssh/config and known_hosts.'
|
||||
});
|
||||
}
|
||||
|
||||
if (sshError.message.includes('not found') || sshError.message.includes('does not exist')) {
|
||||
return res.status(404).json({
|
||||
error: 'Repository not found',
|
||||
suggestion: 'Check that the repository URL is correct and accessible.'
|
||||
suggestion: 'Ensure your deploy key is properly configured and has access to the repository. Check that the key has read access to the repository.'
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
if (sshError.message.includes('not found') || sshError.message.includes('does not exist')) {
|
||||
return res.status(404).json({
|
||||
error: 'Repository not found',
|
||||
suggestion: 'Check that the repository URL is correct and accessible with the deploy key.'
|
||||
});
|
||||
}
|
||||
|
||||
if (sshError.message.includes('No SSH deploy key found')) {
|
||||
return res.status(400).json({
|
||||
error: 'No SSH deploy key found',
|
||||
suggestion: 'Please install a deploy key in one of the expected locations: /root/.ssh/, /home/patchmon/.ssh/, or /var/www/.ssh/'
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
error: 'Failed to fetch repository information',
|
||||
details: sshError.message,
|
||||
suggestion: 'Check SSH key configuration and repository access permissions.'
|
||||
suggestion: 'Check deploy key configuration and repository access permissions.'
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,8 @@ const Settings = () => {
|
||||
frontendUrl: 'http://localhost:3000',
|
||||
updateInterval: 60,
|
||||
autoUpdate: false,
|
||||
githubRepoUrl: 'git@github.com:9technologygroup/patchmon.net.git'
|
||||
githubRepoUrl: 'git@github.com:9technologygroup/patchmon.net.git',
|
||||
sshKeyPath: ''
|
||||
});
|
||||
const [errors, setErrors] = useState({});
|
||||
const [isDirty, setIsDirty] = useState(false);
|
||||
@@ -66,7 +67,8 @@ const Settings = () => {
|
||||
frontendUrl: settings.frontendUrl || 'http://localhost:3000',
|
||||
updateInterval: settings.updateInterval || 60,
|
||||
autoUpdate: settings.autoUpdate || false,
|
||||
githubRepoUrl: settings.githubRepoUrl || 'git@github.com:9technologygroup/patchmon.net.git'
|
||||
githubRepoUrl: settings.githubRepoUrl || 'git@github.com:9technologygroup/patchmon.net.git',
|
||||
sshKeyPath: settings.sshKeyPath || ''
|
||||
};
|
||||
console.log('Setting form data to:', newFormData);
|
||||
setFormData(newFormData);
|
||||
@@ -722,6 +724,22 @@ const Settings = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-secondary-700 dark:text-secondary-200 mb-2">
|
||||
SSH Key Path (Optional)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={formData.sshKeyPath || ''}
|
||||
onChange={(e) => handleInputChange('sshKeyPath', e.target.value)}
|
||||
className="w-full border border-secondary-300 dark:border-secondary-600 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500 bg-white dark:bg-secondary-700 text-secondary-900 dark:text-white font-mono text-sm"
|
||||
placeholder="/root/.ssh/id_ed25519"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-secondary-500 dark:text-secondary-400">
|
||||
Path to your SSH deploy key. Leave empty to auto-detect from common locations.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="bg-white dark:bg-secondary-800 rounded-lg p-4 border border-secondary-200 dark:border-secondary-600">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
|
Reference in New Issue
Block a user