// Version and update endpoints for server.js // Add these imports at the top of server.js const { exec } = require('child_process'); const { promisify } = require('util'); const execAsync = promisify(exec); const fs = require('fs'); const path = require('path'); const semver = require('semver'); // Add this - for semantic version comparison // Add these endpoints // Get system status including version and uptime app.get('/api/system/status', async (req, res) => { try { // Get package.json for version info const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8')); const version = packageJson.version; // Get system uptime const uptimeSeconds = Math.floor(process.uptime()); const hours = Math.floor(uptimeSeconds / 3600); const minutes = Math.floor((uptimeSeconds % 3600) / 60); const seconds = uptimeSeconds % 60; const uptime = `${hours}h ${minutes}m ${seconds}s`; // Check transmission connection let transmissionStatus = 'Connected'; try { await transmissionClient.sessionGet(); } catch (err) { transmissionStatus = 'Disconnected'; } res.json({ status: 'success', data: { version, uptime, transmissionStatus } }); } catch (error) { console.error('Error getting system status:', error); res.status(500).json({ status: 'error', message: 'Failed to get system status' }); } }); // Check for updates app.get('/api/system/check-updates', async (req, res) => { try { // Check if git is available and if this is a git repository const isGitRepo = fs.existsSync(path.join(__dirname, '.git')); if (!isGitRepo) { return res.json({ status: 'error', message: 'This installation is not set up for updates. Please use the bootstrap installer.' }); } // Get current version from the global APP_VERSION constant const currentVersion = APP_VERSION; // Check for test mode flag which forces update availability for testing const testMode = req.query.test === 'true'; if (testMode) { // In test mode, always return that an update is available return res.json({ status: 'success', data: { updateAvailable: true, currentVersion, remoteVersion: '2.1.0-test', commitsBehind: 1, testMode: true } }); } // Normal mode - fetch latest updates without applying them await execAsync('git fetch'); // Check if we're behind the remote repository const { stdout } = await execAsync('git rev-list HEAD..origin/main --count'); const behindCount = parseInt(stdout.trim()); if (behindCount > 0) { // Get the new version from the remote package.json const { stdout: remotePackageJson } = await execAsync('git show origin/main:package.json'); const remotePackage = JSON.parse(remotePackageJson); const remoteVersion = remotePackage.version; // Compare versions semantically - only consider it an update if remote version is higher const isNewerVersion = semver.gt(remoteVersion, currentVersion); return res.json({ status: 'success', data: { updateAvailable: isNewerVersion, currentVersion, remoteVersion, commitsBehind: behindCount, newerVersion: isNewerVersion } }); } else { return res.json({ status: 'success', data: { updateAvailable: false, currentVersion } }); } } catch (error) { console.error('Error checking for updates:', error); res.status(500).json({ status: 'error', message: 'Failed to check for updates' }); } }); // Apply updates app.post('/api/system/update', async (req, res) => { try { // Check if git is available and if this is a git repository const isGitRepo = fs.existsSync(path.join(__dirname, '.git')); if (!isGitRepo) { return res.status(400).json({ status: 'error', message: 'This installation is not set up for updates. Please use the bootstrap installer.' }); } // Run the update script const updateScriptPath = path.join(__dirname, 'scripts', 'update.sh'); // Make sure the update script is executable await execAsync(`chmod +x ${updateScriptPath}`); // Execute the update script const { stdout, stderr } = await execAsync(updateScriptPath); // If we get here, the update was successful // The service will be restarted by the update script res.json({ status: 'success', message: 'Update applied successfully. The service will restart.', data: { output: stdout, errors: stderr } }); } catch (error) { console.error('Error applying update:', error); res.status(500).json({ status: 'error', message: 'Failed to apply update', error: error.message }); } });