feat(backend): add settings service with env var handling

New features:
* Settings initialised on startup rather than first request
* Settings are cached in memory for performance
* Reduction of code duplication/defensive coding practices
This commit is contained in:
tigattack
2025-09-22 01:33:27 +01:00
parent 517b5cd7cb
commit 9cb5cd380b
3 changed files with 217 additions and 99 deletions

View File

@@ -1,9 +1,9 @@
const express = require('express');
const { body, validationResult } = require('express-validator');
const { PrismaClient } = require('@prisma/client');
const { v4: uuidv4 } = require('uuid');
const { authenticateToken } = require('../middleware/auth');
const { requireManageSettings } = require('../middleware/permissions');
const { getSettings, updateSettings } = require('../services/settingsService');
const router = express.Router();
const prisma = new PrismaClient();
@@ -13,6 +13,10 @@ async function triggerCrontabUpdates() {
try {
console.log('Triggering crontab updates on all hosts with auto-update enabled...');
// Get current settings for server URL
const settings = await getSettings();
const serverUrl = settings.server_url;
// Get all hosts that have auto-update enabled
const hosts = await prisma.hosts.findMany({
where: {
@@ -40,10 +44,6 @@ async function triggerCrontabUpdates() {
const http = require('http');
const https = require('https');
const settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
});
const serverUrl = settings?.server_url || process.env.SERVER_URL || 'http://localhost:3001';
const url = new URL(`${serverUrl}/api/v1/hosts/ping`);
const isHttps = url.protocol === 'https:';
const client = isHttps ? https : http;
@@ -94,27 +94,8 @@ async function triggerCrontabUpdates() {
// Get current settings
router.get('/', authenticateToken, requireManageSettings, async (req, res) => {
try {
let settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
});
// If no settings exist, create default settings
if (!settings) {
settings = await prisma.settings.create({
data: {
id: uuidv4(),
server_url: 'http://localhost:3001',
server_protocol: 'http',
server_host: 'localhost',
server_port: 3001,
update_interval: 60,
auto_update: false,
signup_enabled: false,
updated_at: new Date()
}
});
}
const settings = await getSettings();
console.log('Returning settings:', settings);
res.json(settings);
} catch (error) {
console.error('Settings fetch error:', error);
@@ -151,61 +132,34 @@ router.put('/', authenticateToken, requireManageSettings, [
const { serverProtocol, serverHost, serverPort, updateInterval, autoUpdate, signupEnabled, githubRepoUrl, repositoryType, sshKeyPath } = req.body;
// Construct server URL from components
const serverUrl = `${serverProtocol}://${serverHost}:${serverPort}`;
// Get current settings to check for update interval changes
const currentSettings = await getSettings();
const oldUpdateInterval = currentSettings.update_interval;
let settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
// Update settings using the service
const updatedSettings = await updateSettings(currentSettings.id, {
server_protocol: serverProtocol,
server_host: serverHost,
server_port: serverPort,
update_interval: updateInterval || 60,
auto_update: autoUpdate || false,
signup_enabled: signupEnabled || false,
github_repo_url: githubRepoUrl !== undefined ? githubRepoUrl : 'git@github.com:9technologygroup/patchmon.net.git',
repository_type: repositoryType || 'public',
ssh_key_path: sshKeyPath || null,
});
if (settings) {
// Update existing settings
const oldUpdateInterval = settings.update_interval;
console.log('Settings updated successfully:', updatedSettings);
settings = await prisma.settings.update({
where: { id: settings.id },
data: {
server_url: serverUrl,
server_protocol: serverProtocol,
server_host: serverHost,
server_port: serverPort,
update_interval: updateInterval || 60,
auto_update: autoUpdate || false,
signup_enabled: signupEnabled || false,
github_repo_url: githubRepoUrl !== undefined ? githubRepoUrl : 'git@github.com:9technologygroup/patchmon.net.git',
repository_type: repositoryType || 'public',
ssh_key_path: sshKeyPath || null,
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...`);
await triggerCrontabUpdates();
}
} else {
// Create new settings
settings = await prisma.settings.create({
data: {
id: uuidv4(),
server_url: serverUrl,
server_protocol: serverProtocol,
server_host: serverHost,
server_port: serverPort,
update_interval: updateInterval || 60,
auto_update: autoUpdate || false,
signup_enabled: signupEnabled || false,
github_repo_url: githubRepoUrl !== undefined ? githubRepoUrl : 'git@github.com:9technologygroup/patchmon.net.git',
repository_type: repositoryType || 'public',
ssh_key_path: sshKeyPath || null,
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...`);
await triggerCrontabUpdates();
}
res.json({
message: 'Settings updated successfully',
settings
settings: updatedSettings
});
} catch (error) {
console.error('Settings update error:', error);
@@ -216,32 +170,19 @@ router.put('/', authenticateToken, requireManageSettings, [
// Get server URL for public use (used by installation scripts)
router.get('/server-url', async (req, res) => {
try {
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 });
const settings = await getSettings();
const serverUrl = settings.server_url;
res.json({ server_url: serverUrl });
} catch (error) {
console.error('Server URL fetch error:', error);
res.json({ server_url: 'http://localhost:3001' });
res.status(500).json({ error: 'Failed to fetch server URL' });
}
});
// Get update interval policy for agents (public endpoint)
router.get('/update-interval', async (req, res) => {
try {
const settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
});
if (!settings) {
return res.json({ updateInterval: 60 });
}
const settings = await getSettings();
res.json({
updateInterval: settings.update_interval,
cronExpression: `*/${settings.update_interval} * * * *` // Generate cron expression
@@ -255,14 +196,7 @@ router.get('/update-interval', async (req, res) => {
// Get auto-update policy for agents (public endpoint)
router.get('/auto-update', async (req, res) => {
try {
const settings = await prisma.settings.findFirst({
orderBy: { updated_at: 'desc' }
});
if (!settings) {
return res.json({ autoUpdate: false });
}
const settings = await getSettings();
res.json({
autoUpdate: settings.auto_update || false
});