diff --git a/backend/prisma/migrations/20250921215150_remove_frontend_url/migration.sql b/backend/prisma/migrations/20250921215150_remove_frontend_url/migration.sql
new file mode 100644
index 0000000..1efb445
--- /dev/null
+++ b/backend/prisma/migrations/20250921215150_remove_frontend_url/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "settings" DROP COLUMN "frontend_url";
diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma
index 87e455a..8881b63 100644
--- a/backend/prisma/schema.prisma
+++ b/backend/prisma/schema.prisma
@@ -157,7 +157,6 @@ model settings {
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)
diff --git a/backend/src/routes/settingsRoutes.js b/backend/src/routes/settingsRoutes.js
index 08a4f3b..89674c5 100644
--- a/backend/src/routes/settingsRoutes.js
+++ b/backend/src/routes/settingsRoutes.js
@@ -12,10 +12,10 @@ const prisma = new PrismaClient();
async function triggerCrontabUpdates() {
try {
console.log('Triggering crontab updates on all hosts with auto-update enabled...');
-
+
// Get all hosts that have auto-update enabled
const hosts = await prisma.hosts.findMany({
- where: {
+ where: {
auto_update: true,
status: 'active' // Only update active hosts
},
@@ -26,15 +26,15 @@ async function triggerCrontabUpdates() {
api_key: true
}
});
-
+
console.log(`Found ${hosts.length} hosts with auto-update enabled`);
-
+
// For each host, we'll send a special update command that triggers crontab update
// This is done by sending a ping with a special flag
for (const host of hosts) {
try {
console.log(`Triggering crontab update for host: ${host.friendly_name}`);
-
+
// We'll use the existing ping endpoint but add a special parameter
// The agent will detect this and run update-crontab command
const http = require('http');
@@ -47,12 +47,12 @@ async function triggerCrontabUpdates() {
const url = new URL(`${serverUrl}/api/v1/hosts/ping`);
const isHttps = url.protocol === 'https:';
const client = isHttps ? https : http;
-
+
const postData = JSON.stringify({
triggerCrontabUpdate: true,
message: 'Update interval changed, please update your crontab'
});
-
+
const options = {
hostname: url.hostname,
port: url.port || (isHttps ? 443 : 80),
@@ -65,7 +65,7 @@ async function triggerCrontabUpdates() {
'X-API-KEY': host.api_key
}
};
-
+
const req = client.request(options, (res) => {
if (res.statusCode === 200) {
console.log(`Successfully triggered crontab update for ${host.friendly_name}`);
@@ -73,18 +73,18 @@ async function triggerCrontabUpdates() {
console.error(`Failed to trigger crontab update for ${host.friendly_name}: ${res.statusCode}`);
}
});
-
+
req.on('error', (error) => {
console.error(`Error triggering crontab update for ${host.friendly_name}:`, error.message);
});
-
+
req.write(postData);
req.end();
} catch (error) {
console.error(`Error triggering crontab update for ${host.friendly_name}:`, error.message);
}
}
-
+
console.log('Crontab update trigger completed');
} catch (error) {
console.error('Error in triggerCrontabUpdates:', error);
@@ -97,7 +97,7 @@ router.get('/', authenticateToken, requireManageSettings, async (req, res) => {
let settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
});
-
+
// If no settings exist, create default settings
if (!settings) {
settings = await prisma.settings.create({
@@ -107,7 +107,6 @@ router.get('/', authenticateToken, requireManageSettings, async (req, res) => {
server_protocol: 'http',
server_host: 'localhost',
server_port: 3001,
- frontend_url: 'http://localhost:3000',
update_interval: 60,
auto_update: false,
signup_enabled: false,
@@ -128,7 +127,6 @@ router.put('/', authenticateToken, requireManageSettings, [
body('serverProtocol').isIn(['http', 'https']).withMessage('Protocol must be http or https'),
body('serverHost').isLength({ min: 1 }).withMessage('Server host is required'),
body('serverPort').isInt({ min: 1, max: 65535 }).withMessage('Port must be between 1 and 65535'),
- 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('signupEnabled').isBoolean().withMessage('Signup enabled must be a boolean'),
@@ -151,19 +149,19 @@ router.put('/', authenticateToken, requireManageSettings, [
return res.status(400).json({ errors: errors.array() });
}
- const { serverProtocol, serverHost, serverPort, frontendUrl, updateInterval, autoUpdate, signupEnabled, githubRepoUrl, repositoryType, sshKeyPath } = req.body;
-
+ const { serverProtocol, serverHost, serverPort, updateInterval, autoUpdate, signupEnabled, githubRepoUrl, repositoryType, sshKeyPath } = req.body;
+
// Construct server URL from components
const serverUrl = `${serverProtocol}://${serverHost}:${serverPort}`;
-
+
let settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
});
-
+
if (settings) {
// Update existing settings
const oldUpdateInterval = settings.update_interval;
-
+
settings = await prisma.settings.update({
where: { id: settings.id },
data: {
@@ -171,7 +169,6 @@ router.put('/', authenticateToken, requireManageSettings, [
server_protocol: serverProtocol,
server_host: serverHost,
server_port: serverPort,
- frontend_url: frontendUrl,
update_interval: updateInterval || 60,
auto_update: autoUpdate || false,
signup_enabled: signupEnabled || false,
@@ -181,7 +178,6 @@ router.put('/', authenticateToken, requireManageSettings, [
updated_at: new Date()
}
});
-
// If update interval changed, trigger crontab updates on all hosts with auto-update enabled
if (oldUpdateInterval !== (updateInterval || 60)) {
console.log(`Update interval changed from ${oldUpdateInterval} to ${updateInterval || 60} minutes. Triggering crontab updates...`);
@@ -196,7 +192,6 @@ router.put('/', authenticateToken, requireManageSettings, [
server_protocol: serverProtocol,
server_host: serverHost,
server_port: serverPort,
- frontend_url: frontendUrl,
update_interval: updateInterval || 60,
auto_update: autoUpdate || false,
signup_enabled: signupEnabled || false,
@@ -207,7 +202,7 @@ router.put('/', authenticateToken, requireManageSettings, [
}
});
}
-
+
res.json({
message: 'Settings updated successfully',
settings
@@ -224,11 +219,11 @@ router.get('/server-url', async (req, res) => {
const settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
});
-
+
if (!settings) {
return res.json({ server_url: 'http://localhost:3001' });
}
-
+
res.json({ server_url: settings.server_url });
} catch (error) {
console.error('Server URL fetch error:', error);
@@ -242,12 +237,12 @@ router.get('/update-interval', async (req, res) => {
const settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
});
-
+
if (!settings) {
return res.json({ updateInterval: 60 });
}
-
- res.json({
+
+ res.json({
updateInterval: settings.update_interval,
cronExpression: `*/${settings.update_interval} * * * *` // Generate cron expression
});
@@ -263,12 +258,12 @@ router.get('/auto-update', async (req, res) => {
const settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
});
-
+
if (!settings) {
return res.json({ autoUpdate: false });
}
-
- res.json({
+
+ res.json({
autoUpdate: settings.auto_update || false
});
} catch (error) {
diff --git a/frontend/src/pages/Settings.jsx b/frontend/src/pages/Settings.jsx
index 561a403..c961ead 100644
--- a/frontend/src/pages/Settings.jsx
+++ b/frontend/src/pages/Settings.jsx
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
-import { Save, Server, Globe, Shield, AlertCircle, CheckCircle, Code, Plus, Trash2, Star, Download, X, Settings as SettingsIcon, Clock } from 'lucide-react';
+import { Save, Server, Shield, AlertCircle, CheckCircle, Code, Plus, Trash2, Star, Download, X, Settings as SettingsIcon, Clock } from 'lucide-react';
import { settingsAPI, agentVersionAPI, versionAPI } from '../utils/api';
import { useUpdateNotification } from '../contexts/UpdateNotificationContext';
import UpgradeNotificationIcon from '../components/UpgradeNotificationIcon';
@@ -10,7 +10,6 @@ const Settings = () => {
serverProtocol: 'http',
serverHost: 'localhost',
serverPort: 3001,
- frontendUrl: 'http://localhost:3000',
updateInterval: 60,
autoUpdate: false,
signupEnabled: false,
@@ -21,21 +20,20 @@ const Settings = () => {
});
const [errors, setErrors] = useState({});
const [isDirty, setIsDirty] = useState(false);
-
+
// Tab management
const [activeTab, setActiveTab] = useState('server');
-
+
// Get update notification state
const { updateAvailable } = useUpdateNotification();
-
+
// Tab configuration
const tabs = [
{ id: 'server', name: 'Server Configuration', icon: Server },
- { id: 'frontend', name: 'Frontend Configuration', icon: Globe },
{ id: 'agent', name: 'Agent Management', icon: SettingsIcon },
{ id: 'version', name: 'Server Version', icon: Code, showUpgradeIcon: updateAvailable }
];
-
+
// Agent version management state
const [showAgentVersionModal, setShowAgentVersionModal] = useState(false);
const [editingAgentVersion, setEditingAgentVersion] = useState(null);
@@ -54,7 +52,7 @@ const Settings = () => {
checking: false,
error: null
});
-
+
const [sshTestResult, setSshTestResult] = useState({
testing: false,
success: null,
@@ -77,7 +75,6 @@ const Settings = () => {
serverProtocol: settings.server_protocol || 'http',
serverHost: settings.server_host || 'localhost',
serverPort: settings.server_port || 3001,
- frontendUrl: settings.frontend_url || 'http://localhost:3000',
updateInterval: settings.update_interval || 60,
autoUpdate: settings.auto_update || false,
signupEnabled: settings.signup_enabled === true ? true : false, // Explicit boolean conversion
@@ -189,11 +186,11 @@ const Settings = () => {
// Version checking functions
const checkForUpdates = async () => {
setVersionInfo(prev => ({ ...prev, checking: true, error: null }));
-
+
try {
const response = await versionAPI.checkUpdates();
const data = response.data;
-
+
setVersionInfo({
currentVersion: data.currentVersion,
latestVersion: data.latestVersion,
@@ -224,13 +221,13 @@ const Settings = () => {
}
setSshTestResult({ testing: true, success: null, message: null, error: null });
-
+
try {
const response = await versionAPI.testSshKey({
sshKeyPath: formData.sshKeyPath,
githubRepoUrl: formData.githubRepoUrl
});
-
+
setSshTestResult({
testing: false,
success: true,
@@ -261,7 +258,7 @@ const Settings = () => {
const handleSubmit = (e) => {
e.preventDefault();
-
+
// Only include sshKeyPath if the toggle is enabled
const dataToSubmit = { ...formData };
if (!dataToSubmit.useCustomSshKey) {
@@ -269,31 +266,25 @@ const Settings = () => {
}
// Remove the frontend-only field
delete dataToSubmit.useCustomSshKey;
-
+
updateSettingsMutation.mutate(dataToSubmit);
};
const validateForm = () => {
const newErrors = {};
-
+
if (!formData.serverHost.trim()) {
newErrors.serverHost = 'Server host is required';
}
-
+
if (!formData.serverPort || formData.serverPort < 1 || formData.serverPort > 65535) {
newErrors.serverPort = 'Port must be between 1 and 65535';
}
-
- try {
- new URL(formData.frontendUrl);
- } catch {
- newErrors.frontendUrl = 'Frontend URL must be a valid URL';
- }
-
+
if (!formData.updateInterval || formData.updateInterval < 5 || formData.updateInterval > 1440) {
newErrors.updateInterval = 'Update interval must be between 5 and 1440 minutes';
}
-
+
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
@@ -307,7 +298,7 @@ const Settings = () => {
}
// Remove the frontend-only field
delete dataToSubmit.useCustomSshKey;
-
+
updateSettingsMutation.mutate(dataToSubmit);
}
};
@@ -391,7 +382,7 @@ const Settings = () => {
@@ -848,13 +771,13 @@ const Settings = () => {
Configure automatic version checking against your GitHub repository to notify users of available updates.
- +