Compare commits
15 Commits
1ff479a3cf
...
897862184f
Author | SHA1 | Date | |
---|---|---|---|
897862184f | |||
90a6e5e16b | |||
cbae1d57fe | |||
d2d2ea976b | |||
2705989ff6 | |||
8589a0833e | |||
467979971a | |||
5ce348d61e | |||
dc4131f04c | |||
5261f7b4f4 | |||
b8818a9bec | |||
3ff0a50553 | |||
c0a7362226 | |||
dd08278e28 | |||
980a6ca3a4 |
23
README.md
23
README.md
@ -1,4 +1,4 @@
|
|||||||
# Transmission RSS Manager v2.0.9
|
# Transmission RSS Manager v2.0.12
|
||||||
|
|
||||||
A comprehensive web-based tool to automate and manage your Transmission torrent downloads with RSS feed integration, intelligent media organization, and enhanced security features. Now with automatic updates and easy installation!
|
A comprehensive web-based tool to automate and manage your Transmission torrent downloads with RSS feed integration, intelligent media organization, and enhanced security features. Now with automatic updates and easy installation!
|
||||||
|
|
||||||
@ -14,6 +14,27 @@ If you installed using the bootstrap installer, these requirements should be met
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v2.0.12 (2025-03-10)
|
||||||
|
- **Fixed**: Removed persistent floating update notification that wouldn't disappear
|
||||||
|
- **Fixed**: Major localStorage cleanup to prevent notification state persistence
|
||||||
|
- **Improved**: Complete rewrite of update notification system to use dashboard-only alerts
|
||||||
|
- **Improved**: Added DOM cleanup to remove any rogue notification elements
|
||||||
|
|
||||||
|
### v2.0.11 (2025-03-10)
|
||||||
|
- **Fixed**: Fixed update button persistence with advanced floating notification
|
||||||
|
- **Fixed**: Resolved version display issues with direct package.json reading
|
||||||
|
- **Fixed**: Improved update process for better version reporting
|
||||||
|
- **Fixed**: Resolved conflict between test mode and actual update status
|
||||||
|
- **Added**: Refresh button on update notification for easier version checking
|
||||||
|
- **Added**: Clear test mode indicators to prevent confusion
|
||||||
|
- **Improved**: Enhanced cache busting for more reliable version checking
|
||||||
|
- **Improved**: Better user feedback during update process
|
||||||
|
|
||||||
|
### v2.0.10 (2025-03-10)
|
||||||
|
- **Fixed**: Fixed "fs.existsSync is not a function" error in update check
|
||||||
|
- **Improved**: Better error handling for git repository checks
|
||||||
|
- **Improved**: More robust file system operations for update detection
|
||||||
|
|
||||||
### v2.0.9 (2025-03-07)
|
### v2.0.9 (2025-03-07)
|
||||||
- **Fixed**: Update button now appears properly on dashboard
|
- **Fixed**: Update button now appears properly on dashboard
|
||||||
- **Fixed**: Remote Transmission connection issues resolved
|
- **Fixed**: Remote Transmission connection issues resolved
|
||||||
|
1
modules/post-processor
Symbolic link
1
modules/post-processor
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
post-processor.js
|
1
modules/postProcessor
Symbolic link
1
modules/postProcessor
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
post-processor.js
|
1
modules/postProcessor.js
Symbolic link
1
modules/postProcessor.js
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
post-processor.js
|
1
modules/rss-feed-manager
Symbolic link
1
modules/rss-feed-manager
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
rss-feed-manager.js
|
1
modules/rssFeedManager
Symbolic link
1
modules/rssFeedManager
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
rss-feed-manager.js
|
1
modules/rssFeedManager.js
Symbolic link
1
modules/rssFeedManager.js
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
rss-feed-manager.js
|
1
modules/transmission-client
Symbolic link
1
modules/transmission-client
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
transmission-client.js
|
1
modules/transmissionClient
Symbolic link
1
modules/transmissionClient
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
transmission-client.js
|
1
modules/transmissionClient.js
Symbolic link
1
modules/transmissionClient.js
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
transmission-client.js
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "transmission-rss-manager",
|
"name": "transmission-rss-manager",
|
||||||
"version": "2.0.9",
|
"version": "2.0.12",
|
||||||
"description": "A comprehensive web-based tool to automate and manage your Transmission torrent downloads with RSS feed integration and intelligent media organization",
|
"description": "A comprehensive web-based tool to automate and manage your Transmission torrent downloads with RSS feed integration and intelligent media organization",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -137,8 +137,8 @@
|
|||||||
<div class="mt-2 testing-controls">
|
<div class="mt-2 testing-controls">
|
||||||
<small><a href="#" id="toggle-test-update-button">Toggle Test Update</a></small>
|
<small><a href="#" id="toggle-test-update-button">Toggle Test Update</a></small>
|
||||||
</div>
|
</div>
|
||||||
<div id="update-available" class="mt-3 d-none">
|
<div id="update-available" class="mt-3">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info update-alert" style="display: none;">
|
||||||
<i class="fas fa-arrow-circle-up"></i>
|
<i class="fas fa-arrow-circle-up"></i>
|
||||||
<span>A new version is available!</span>
|
<span>A new version is available!</span>
|
||||||
<button id="btn-update-now" class="btn btn-sm btn-primary ml-2">
|
<button id="btn-update-now" class="btn btn-sm btn-primary ml-2">
|
||||||
@ -545,7 +545,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<p>Transmission RSS Manager v2.0.6</p>
|
<p>Transmission RSS Manager <span id="footer-version">v2.0.10</span></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 text-right">
|
<div class="col-md-6 text-right">
|
||||||
<p><a href="https://git.powerdata.dk/masterdraco/transmission-rss-manager" target="_blank" rel="noopener noreferrer">GitHub</a> | <a href="#" id="show-about-modal">About</a></p>
|
<p><a href="https://git.powerdata.dk/masterdraco/transmission-rss-manager" target="_blank" rel="noopener noreferrer">GitHub</a> | <a href="#" id="show-about-modal">About</a></p>
|
||||||
@ -592,6 +592,39 @@
|
|||||||
|
|
||||||
<h4>Version History</h4>
|
<h4>Version History</h4>
|
||||||
<div class="version-history">
|
<div class="version-history">
|
||||||
|
<div class="version">
|
||||||
|
<h5>v2.0.11 - March 2025</h5>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Fixed</strong>: Update button persistence with floating notification</li>
|
||||||
|
<li><strong>Fixed</strong>: Version display issues with direct package.json reading</li>
|
||||||
|
<li><strong>Fixed</strong>: Update process for better version reporting</li>
|
||||||
|
<li><strong>Fixed</strong>: Conflict between test mode and actual update status</li>
|
||||||
|
<li><strong>Added</strong>: Refresh button on update notification</li>
|
||||||
|
<li><strong>Added</strong>: Clear test mode indicators to prevent confusion</li>
|
||||||
|
<li><strong>Improved</strong>: Enhanced cache busting for version checking</li>
|
||||||
|
<li><strong>Improved</strong>: Better user feedback during update process</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="version">
|
||||||
|
<h5>v2.0.10 - March 2025</h5>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Fixed</strong>: fs.existsSync error in update check</li>
|
||||||
|
<li><strong>Fixed</strong>: Update button now stays visible when update is available</li>
|
||||||
|
<li><strong>Fixed</strong>: Footer version now shows correct running version</li>
|
||||||
|
<li><strong>Improved</strong>: Better error handling for git repository checks</li>
|
||||||
|
<li><strong>Improved</strong>: More robust file system operations for update detection</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="version">
|
||||||
|
<h5>v2.0.9 - March 2025</h5>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Fixed</strong>: Update button now appears properly on dashboard</li>
|
||||||
|
<li><strong>Fixed</strong>: Remote Transmission connection issues resolved</li>
|
||||||
|
<li><strong>Fixed</strong>: Improved connection test with better error handling</li>
|
||||||
|
<li><strong>Added</strong>: System status and update endpoints for version checking</li>
|
||||||
|
<li><strong>Improved</strong>: Update detection and notification on dashboard</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div class="version">
|
<div class="version">
|
||||||
<h5>v2.0.6 - March 2025</h5>
|
<h5>v2.0.6 - March 2025</h5>
|
||||||
<ul>
|
<ul>
|
||||||
@ -638,7 +671,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-center mt-4">
|
<div class="text-center mt-4">
|
||||||
<p><strong>Transmission RSS Manager v2.0.0</strong></p>
|
<p><strong id="about-version">Transmission RSS Manager v2.0.11</strong></p>
|
||||||
<p>© 2025 PowerData.dk - All Rights Reserved</p>
|
<p>© 2025 PowerData.dk - All Rights Reserved</p>
|
||||||
<p><a href="https://powerdata.dk" target="_blank">Visit PowerData.dk</a></p>
|
<p><a href="https://powerdata.dk" target="_blank">Visit PowerData.dk</a></p>
|
||||||
</div>
|
</div>
|
||||||
@ -649,6 +682,60 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Update Alert Custom Styles -->
|
||||||
|
<style>
|
||||||
|
/* Custom styles for update alert to ensure it's visible */
|
||||||
|
.update-alert {
|
||||||
|
display: none;
|
||||||
|
margin-top: 10px !important;
|
||||||
|
border: 2px solid #007bff !important;
|
||||||
|
background-color: #cce5ff !important;
|
||||||
|
color: #004085 !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.2) !important;
|
||||||
|
position: relative !important;
|
||||||
|
z-index: 100 !important;
|
||||||
|
}
|
||||||
|
.update-alert span {
|
||||||
|
color: #004085 !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Floating update notification that's impossible to miss */
|
||||||
|
#floating-update-notification {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
width: 300px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #ff5555;
|
||||||
|
color: white;
|
||||||
|
border: 3px solid #cc0000;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 0 20px rgba(0,0,0,0.5);
|
||||||
|
z-index: 10000;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#floating-update-notification button {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-color: white;
|
||||||
|
color: #cc0000;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
#floating-update-notification button:hover {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Removed floating notification completely -->
|
||||||
|
<!-- The floating notification was removed to fix persistent display issues -->
|
||||||
|
|
||||||
<!-- JavaScript Files -->
|
<!-- JavaScript Files -->
|
||||||
<script src="/js/system-status.js"></script>
|
<script src="/js/system-status.js"></script>
|
||||||
<script src="/js/app.js"></script>
|
<script src="/js/app.js"></script>
|
||||||
|
@ -16,15 +16,30 @@ function initSystemStatus() {
|
|||||||
|
|
||||||
// Load system status
|
// Load system status
|
||||||
function loadSystemStatus() {
|
function loadSystemStatus() {
|
||||||
fetch('/api/system/status', {
|
// Add cache-busting parameter
|
||||||
|
const cacheBuster = `?_=${new Date().getTime()}`;
|
||||||
|
fetch('/api/system/status' + cacheBuster, {
|
||||||
headers: authHeaders()
|
headers: authHeaders()
|
||||||
})
|
})
|
||||||
.then(handleResponse)
|
.then(handleResponse)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.status === 'success') {
|
if (data.status === 'success') {
|
||||||
|
// Update version display
|
||||||
versionElement.textContent = data.data.version;
|
versionElement.textContent = data.data.version;
|
||||||
uptimeElement.textContent = data.data.uptime;
|
uptimeElement.textContent = data.data.uptime;
|
||||||
|
|
||||||
|
// Also update footer version
|
||||||
|
const footerVersion = document.getElementById('footer-version');
|
||||||
|
if (footerVersion) {
|
||||||
|
footerVersion.textContent = 'v' + data.data.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update version in about modal too if it exists
|
||||||
|
const aboutVersionElement = document.getElementById('about-version');
|
||||||
|
if (aboutVersionElement) {
|
||||||
|
aboutVersionElement.textContent = 'Transmission RSS Manager v' + data.data.version;
|
||||||
|
}
|
||||||
|
|
||||||
// Update transmission status with icon
|
// Update transmission status with icon
|
||||||
if (data.data.transmissionStatus === 'Connected') {
|
if (data.data.transmissionStatus === 'Connected') {
|
||||||
transmissionStatusElement.innerHTML = '<i class="fas fa-check-circle text-success"></i> Connected';
|
transmissionStatusElement.innerHTML = '<i class="fas fa-check-circle text-success"></i> Connected';
|
||||||
@ -41,28 +56,149 @@ function initSystemStatus() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// More robust update check status tracking
|
||||||
|
const UPDATE_KEY = 'trm_update_available';
|
||||||
|
const CURRENT_VERSION_KEY = 'trm_current_version';
|
||||||
|
const REMOTE_VERSION_KEY = 'trm_remote_version';
|
||||||
|
|
||||||
|
// Force clear any existing update notification state
|
||||||
|
localStorage.removeItem(UPDATE_KEY);
|
||||||
|
localStorage.removeItem(CURRENT_VERSION_KEY);
|
||||||
|
localStorage.removeItem(REMOTE_VERSION_KEY);
|
||||||
|
|
||||||
|
let updateCheckInProgress = false;
|
||||||
|
|
||||||
|
// Function to show update alert
|
||||||
|
function showUpdateAlert(currentVersion, remoteVersion) {
|
||||||
|
// Set status text in the system status panel
|
||||||
|
updateStatusElement.innerHTML = '<i class="fas fa-exclamation-circle text-warning"></i> Update available';
|
||||||
|
|
||||||
|
// Show only the original alert box in the dashboard
|
||||||
|
try {
|
||||||
|
const alertBox = updateAvailableDiv.querySelector('.alert');
|
||||||
|
if (alertBox) {
|
||||||
|
alertBox.style.display = 'block';
|
||||||
|
const spanElement = alertBox.querySelector('span');
|
||||||
|
if (spanElement) {
|
||||||
|
spanElement.textContent = `A new version is available: ${currentVersion} → ${remoteVersion}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error showing original alert box:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've removed the floating notification entirely, so this part is skipped
|
||||||
|
console.log('Update alert shown in dashboard:', currentVersion, '->', remoteVersion);
|
||||||
|
|
||||||
|
// Store in localStorage
|
||||||
|
localStorage.setItem(UPDATE_KEY, 'true');
|
||||||
|
localStorage.setItem(CURRENT_VERSION_KEY, currentVersion);
|
||||||
|
localStorage.setItem(REMOTE_VERSION_KEY, remoteVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to hide update alert
|
||||||
|
function hideUpdateAlert() {
|
||||||
|
// Hide original alert
|
||||||
|
try {
|
||||||
|
const alertBox = updateAvailableDiv.querySelector('.alert');
|
||||||
|
if (alertBox) {
|
||||||
|
alertBox.style.display = 'none';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error hiding original alert:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear localStorage
|
||||||
|
localStorage.removeItem(UPDATE_KEY);
|
||||||
|
localStorage.removeItem(CURRENT_VERSION_KEY);
|
||||||
|
localStorage.removeItem(REMOTE_VERSION_KEY);
|
||||||
|
|
||||||
|
console.log('Update alert hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check localStorage on init and set up MutationObserver to prevent hiding
|
||||||
|
(function checkStoredUpdateStatus() {
|
||||||
|
const isUpdateAvailable = localStorage.getItem(UPDATE_KEY) === 'true';
|
||||||
|
if (isUpdateAvailable) {
|
||||||
|
const currentVersion = localStorage.getItem(CURRENT_VERSION_KEY);
|
||||||
|
const remoteVersion = localStorage.getItem(REMOTE_VERSION_KEY);
|
||||||
|
if (currentVersion && remoteVersion) {
|
||||||
|
showUpdateAlert(currentVersion, remoteVersion);
|
||||||
|
|
||||||
|
// Set up mutation observer to detect and revert any attempts to hide the update alert
|
||||||
|
const alertBox = updateAvailableDiv.querySelector('.alert');
|
||||||
|
if (alertBox) {
|
||||||
|
const observer = new MutationObserver(function(mutations) {
|
||||||
|
mutations.forEach(function(mutation) {
|
||||||
|
if (mutation.type === 'attributes' &&
|
||||||
|
(mutation.attributeName === 'style' ||
|
||||||
|
mutation.attributeName === 'class')) {
|
||||||
|
|
||||||
|
// If display is being changed to hide the element, force it back to visible
|
||||||
|
if (alertBox.style.display !== 'block' ||
|
||||||
|
alertBox.classList.contains('d-none') ||
|
||||||
|
alertBox.style.visibility === 'hidden' ||
|
||||||
|
alertBox.style.opacity === '0') {
|
||||||
|
|
||||||
|
console.log('Detected attempt to hide update button, forcing display');
|
||||||
|
showUpdateAlert(currentVersion, remoteVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Observe style and class attribute changes
|
||||||
|
observer.observe(alertBox, {
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['style', 'class']
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store observer in window object to prevent garbage collection
|
||||||
|
window._updateButtonObserver = observer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
// Check for updates
|
// Check for updates
|
||||||
function checkForUpdates() {
|
function checkForUpdates() {
|
||||||
updateStatusElement.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i> Checking...';
|
updateStatusElement.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i> Checking...';
|
||||||
updateAvailableDiv.classList.add('d-none');
|
updateCheckInProgress = true;
|
||||||
|
|
||||||
// Add test=true parameter to force update availability for testing
|
// Add test=true parameter to force update availability for testing
|
||||||
const testMode = localStorage.getItem('showUpdateButton') === 'true';
|
const testMode = localStorage.getItem('showUpdateButton') === 'true';
|
||||||
const url = testMode ? '/api/system/check-updates?test=true' : '/api/system/check-updates';
|
const cacheBuster = `_=${new Date().getTime()}`;
|
||||||
|
const url = testMode
|
||||||
|
? `/api/system/check-updates?test=true&${cacheBuster}`
|
||||||
|
: `/api/system/check-updates?${cacheBuster}`;
|
||||||
|
|
||||||
// Set a timeout to detect network issues
|
// Set a timeout to detect network issues
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(() => {
|
||||||
updateStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> Check timed out';
|
updateStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> Check timed out';
|
||||||
|
updateCheckInProgress = false;
|
||||||
showNotification('Update check timed out. Please try again later.', 'warning');
|
showNotification('Update check timed out. Please try again later.', 'warning');
|
||||||
}, 10000); // 10 second timeout
|
}, 10000); // 10 second timeout
|
||||||
|
|
||||||
|
// Create a timeout controller
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeoutId2 = setTimeout(() => controller.abort(), 15000);
|
||||||
|
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
headers: authHeaders(),
|
headers: authHeaders(),
|
||||||
// Add a fetch timeout as well
|
// Add a fetch timeout using abort controller
|
||||||
signal: AbortSignal.timeout(15000) // 15 second timeout (available in modern browsers)
|
signal: controller.signal // 15 second timeout
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
clearTimeout(timeoutId2);
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
clearTimeout(timeoutId2);
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
// Better error checking
|
// Better error checking
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return response.json().then(data => {
|
return response.json().then(data => {
|
||||||
@ -77,22 +213,32 @@ function initSystemStatus() {
|
|||||||
return response.json();
|
return response.json();
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
updateCheckInProgress = false;
|
||||||
|
|
||||||
if (data.status === 'success') {
|
if (data.status === 'success') {
|
||||||
if (data.data && data.data.updateAvailable) {
|
if (data.data && data.data.updateAvailable) {
|
||||||
updateStatusElement.innerHTML = '<i class="fas fa-exclamation-circle text-warning"></i> Update available';
|
// Show update alert with version info
|
||||||
updateAvailableDiv.classList.remove('d-none');
|
showUpdateAlert(data.data.currentVersion, data.data.remoteVersion);
|
||||||
updateAvailableDiv.querySelector('span').textContent =
|
|
||||||
`A new version is available: ${data.data.currentVersion} → ${data.data.remoteVersion}`;
|
// Log to console for debugging
|
||||||
|
console.log('Update available detected:', data.data.currentVersion, '->', data.data.remoteVersion);
|
||||||
} else {
|
} else {
|
||||||
|
// No update available
|
||||||
updateStatusElement.innerHTML = '<i class="fas fa-check-circle text-success"></i> Up to date';
|
updateStatusElement.innerHTML = '<i class="fas fa-check-circle text-success"></i> Up to date';
|
||||||
|
hideUpdateAlert();
|
||||||
|
|
||||||
|
// Force reload system status to ensure version is current
|
||||||
|
setTimeout(() => loadSystemStatus(), 1000);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Error status but with a response
|
// Error status but with a response
|
||||||
updateStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> Check failed';
|
updateStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> Check failed';
|
||||||
showNotification(data.message || 'Failed to check for updates', 'danger');
|
showNotification(data.message || 'Failed to check for updates', 'danger');
|
||||||
|
// Don't clear update status on error - keep any previous update notification
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
updateCheckInProgress = false;
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
console.error('Error checking for updates:', error);
|
console.error('Error checking for updates:', error);
|
||||||
updateStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> Check failed';
|
updateStatusElement.innerHTML = '<i class="fas fa-times-circle text-danger"></i> Check failed';
|
||||||
@ -105,6 +251,8 @@ function initSystemStatus() {
|
|||||||
} else {
|
} else {
|
||||||
showNotification(error.message || 'Failed to connect to server', 'danger');
|
showNotification(error.message || 'Failed to connect to server', 'danger');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't clear update status on error - keep any previous update notification
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,25 +263,67 @@ function initSystemStatus() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show loading state
|
// Disable test mode whenever we try to apply an update
|
||||||
|
localStorage.setItem('showUpdateButton', 'false');
|
||||||
|
|
||||||
|
// Update toggle button text if it exists
|
||||||
|
const testToggle = document.getElementById('toggle-test-update-button');
|
||||||
|
if (testToggle) {
|
||||||
|
testToggle.innerText = 'Enable Test Update';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show loading state on both update buttons
|
||||||
|
// Original button
|
||||||
|
if (updateButton) {
|
||||||
updateButton.disabled = true;
|
updateButton.disabled = true;
|
||||||
updateButton.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i> Updating...';
|
updateButton.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i> Updating...';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Floating notification button
|
||||||
|
const floatingButton = document.getElementById('floating-update-button');
|
||||||
|
if (floatingButton) {
|
||||||
|
floatingButton.disabled = true;
|
||||||
|
floatingButton.textContent = 'Updating...';
|
||||||
|
}
|
||||||
|
|
||||||
showNotification('Applying update. Please wait...', 'info');
|
showNotification('Applying update. Please wait...', 'info');
|
||||||
|
|
||||||
// Set a timeout for the update process
|
// Set a timeout for the update process
|
||||||
const updateTimeoutId = setTimeout(() => {
|
const updateTimeoutId = setTimeout(() => {
|
||||||
|
// Re-enable original button
|
||||||
|
if (updateButton) {
|
||||||
updateButton.disabled = false;
|
updateButton.disabled = false;
|
||||||
updateButton.innerHTML = '<i class="fas fa-download"></i> Update Now';
|
updateButton.innerHTML = '<i class="fas fa-download"></i> Update Now';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-enable floating button
|
||||||
|
if (floatingButton) {
|
||||||
|
floatingButton.disabled = false;
|
||||||
|
floatingButton.textContent = 'Update Now';
|
||||||
|
}
|
||||||
|
|
||||||
showNotification('Update process timed out. Please try again or check server logs.', 'warning');
|
showNotification('Update process timed out. Please try again or check server logs.', 'warning');
|
||||||
}, 60000); // 60 second timeout for the entire update process
|
}, 60000); // 60 second timeout for the entire update process
|
||||||
|
|
||||||
|
// Create a timeout controller
|
||||||
|
const updateController = new AbortController();
|
||||||
|
const updateTimeoutId2 = setTimeout(() => updateController.abort(), 45000);
|
||||||
|
|
||||||
fetch('/api/system/update', {
|
fetch('/api/system/update', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...authHeaders()
|
...authHeaders()
|
||||||
},
|
},
|
||||||
signal: AbortSignal.timeout(45000) // 45 second timeout
|
signal: updateController.signal // 45 second timeout
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
clearTimeout(updateTimeoutId2);
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
clearTimeout(updateTimeoutId2);
|
||||||
|
throw error;
|
||||||
})
|
})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
// Better error checking
|
// Better error checking
|
||||||
@ -153,38 +343,138 @@ function initSystemStatus() {
|
|||||||
clearTimeout(updateTimeoutId);
|
clearTimeout(updateTimeoutId);
|
||||||
|
|
||||||
if (data.status === 'success') {
|
if (data.status === 'success') {
|
||||||
|
// Check if there's an update message to determine if an update was actually applied
|
||||||
|
const updateApplied = data.message && data.message.includes('Update applied successfully');
|
||||||
|
const noNewUpdate = data.data && data.data.output && data.data.output.includes('already have the latest version');
|
||||||
|
|
||||||
|
// Hide update notification
|
||||||
|
hideUpdateAlert();
|
||||||
|
|
||||||
|
if (noNewUpdate) {
|
||||||
|
// If no update was needed, show a different message
|
||||||
|
showNotification('You already have the latest version. No update was needed.', 'info');
|
||||||
|
|
||||||
|
// Re-enable both buttons
|
||||||
|
if (updateButton) {
|
||||||
|
updateButton.disabled = false;
|
||||||
|
updateButton.innerHTML = '<i class="fas fa-download"></i> Check Again';
|
||||||
|
}
|
||||||
|
|
||||||
|
const floatingButton = document.getElementById('floating-update-button');
|
||||||
|
if (floatingButton) {
|
||||||
|
floatingButton.disabled = false;
|
||||||
|
floatingButton.textContent = 'Check Again';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update page to show current version without reloading
|
||||||
|
loadSystemStatus();
|
||||||
|
|
||||||
|
// Double-check system status again after a delay to ensure version is updated
|
||||||
|
setTimeout(() => {
|
||||||
|
loadSystemStatus();
|
||||||
|
checkForUpdates(); // Run check again to update status text
|
||||||
|
}, 2000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show success notification
|
||||||
showNotification('Update applied successfully. The page will reload in 30 seconds.', 'success');
|
showNotification('Update applied successfully. The page will reload in 30 seconds.', 'success');
|
||||||
|
|
||||||
// Show a countdown to reload
|
// Update both buttons with countdown
|
||||||
let secondsLeft = 30;
|
let secondsLeft = 30;
|
||||||
updateButton.innerHTML = `<i class="fas fa-sync"></i> Reloading in ${secondsLeft}s...`;
|
|
||||||
|
|
||||||
|
// Function to update the countdown text
|
||||||
|
function updateCountdown() {
|
||||||
|
// Update original button if it exists
|
||||||
|
if (updateButton) {
|
||||||
|
updateButton.innerHTML = `<i class="fas fa-sync"></i> Reloading in ${secondsLeft}s...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update floating button if it exists
|
||||||
|
const floatingButton = document.getElementById('floating-update-button');
|
||||||
|
if (floatingButton) {
|
||||||
|
floatingButton.textContent = `Reloading in ${secondsLeft}s...`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial text update
|
||||||
|
updateCountdown();
|
||||||
|
|
||||||
|
// Start countdown
|
||||||
const countdownInterval = setInterval(() => {
|
const countdownInterval = setInterval(() => {
|
||||||
secondsLeft--;
|
secondsLeft--;
|
||||||
updateButton.innerHTML = `<i class="fas fa-sync"></i> Reloading in ${secondsLeft}s...`;
|
updateCountdown();
|
||||||
|
|
||||||
if (secondsLeft <= 0) {
|
if (secondsLeft <= 0) {
|
||||||
clearInterval(countdownInterval);
|
clearInterval(countdownInterval);
|
||||||
window.location.reload();
|
|
||||||
|
// Clear localStorage to ensure a clean reload
|
||||||
|
localStorage.removeItem(UPDATE_KEY);
|
||||||
|
localStorage.removeItem(CURRENT_VERSION_KEY);
|
||||||
|
localStorage.removeItem(REMOTE_VERSION_KEY);
|
||||||
|
|
||||||
|
// Also ensure floating notification is completely removed
|
||||||
|
const floatingNotification = document.getElementById('floating-update-notification');
|
||||||
|
if (floatingNotification) {
|
||||||
|
floatingNotification.style.display = 'none';
|
||||||
|
floatingNotification.removeAttribute('style');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force a clean reload
|
||||||
|
window.location.href = window.location.href.split('#')[0] + '?t=' + new Date().getTime();
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
// Set a timer to reload the page after the service has time to restart
|
// Set a timer to reload the page after the service has time to restart
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
clearInterval(countdownInterval);
|
clearInterval(countdownInterval);
|
||||||
window.location.reload();
|
|
||||||
|
// Clear localStorage to ensure a clean reload
|
||||||
|
localStorage.removeItem(UPDATE_KEY);
|
||||||
|
localStorage.removeItem(CURRENT_VERSION_KEY);
|
||||||
|
localStorage.removeItem(REMOTE_VERSION_KEY);
|
||||||
|
|
||||||
|
// Also ensure floating notification is completely removed
|
||||||
|
const floatingNotification = document.getElementById('floating-update-notification');
|
||||||
|
if (floatingNotification) {
|
||||||
|
floatingNotification.style.display = 'none';
|
||||||
|
floatingNotification.removeAttribute('style');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force a clean reload with cache-busting parameter
|
||||||
|
window.location.href = window.location.href.split('#')[0] + '?t=' + new Date().getTime();
|
||||||
}, 30000);
|
}, 30000);
|
||||||
} else {
|
} else {
|
||||||
|
// Enable both buttons on failure
|
||||||
|
if (updateButton) {
|
||||||
updateButton.disabled = false;
|
updateButton.disabled = false;
|
||||||
updateButton.innerHTML = '<i class="fas fa-download"></i> Update Now';
|
updateButton.innerHTML = '<i class="fas fa-download"></i> Update Now';
|
||||||
|
}
|
||||||
|
|
||||||
|
const floatingButton = document.getElementById('floating-update-button');
|
||||||
|
if (floatingButton) {
|
||||||
|
floatingButton.disabled = false;
|
||||||
|
floatingButton.textContent = 'Update Now';
|
||||||
|
}
|
||||||
|
|
||||||
showNotification(data.message || 'Failed to apply update', 'danger');
|
showNotification(data.message || 'Failed to apply update', 'danger');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
clearTimeout(updateTimeoutId);
|
clearTimeout(updateTimeoutId);
|
||||||
console.error('Error applying update:', error);
|
console.error('Error applying update:', error);
|
||||||
|
|
||||||
|
// Re-enable both buttons on error
|
||||||
|
if (updateButton) {
|
||||||
updateButton.disabled = false;
|
updateButton.disabled = false;
|
||||||
updateButton.innerHTML = '<i class="fas fa-download"></i> Update Now';
|
updateButton.innerHTML = '<i class="fas fa-download"></i> Update Now';
|
||||||
|
}
|
||||||
|
|
||||||
|
const floatingButton = document.getElementById('floating-update-button');
|
||||||
|
if (floatingButton) {
|
||||||
|
floatingButton.disabled = false;
|
||||||
|
floatingButton.textContent = 'Update Now';
|
||||||
|
}
|
||||||
|
|
||||||
// More specific error message based on the error type
|
// More specific error message based on the error type
|
||||||
if (error.name === 'AbortError') {
|
if (error.name === 'AbortError') {
|
||||||
@ -209,12 +499,73 @@ function initSystemStatus() {
|
|||||||
updateButton.addEventListener('click', applyUpdate);
|
updateButton.addEventListener('click', applyUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add handler for floating refresh button
|
||||||
|
const floatingRefreshButton = document.getElementById('floating-refresh-button');
|
||||||
|
if (floatingRefreshButton) {
|
||||||
|
floatingRefreshButton.addEventListener('click', () => {
|
||||||
|
// Force a hard refresh of everything
|
||||||
|
floatingRefreshButton.textContent = 'Refreshing...';
|
||||||
|
floatingRefreshButton.disabled = true;
|
||||||
|
|
||||||
|
// Force reload system status
|
||||||
|
loadSystemStatus();
|
||||||
|
|
||||||
|
// Force a check without the test parameter to get real status
|
||||||
|
const realCheckUrl = `/api/system/check-updates?_=${new Date().getTime()}`;
|
||||||
|
fetch(realCheckUrl, { headers: authHeaders() })
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log('Manual refresh result:', data);
|
||||||
|
|
||||||
|
if (data.status === 'success') {
|
||||||
|
// Check if we're in test mode
|
||||||
|
const isTestMode = localStorage.getItem('showUpdateButton') === 'true';
|
||||||
|
|
||||||
|
// If test mode is enabled but update says no update available, disable test mode
|
||||||
|
if (isTestMode && data.data && !data.data.updateAvailable) {
|
||||||
|
localStorage.setItem('showUpdateButton', 'false');
|
||||||
|
testToggle.innerText = 'Enable Test Update';
|
||||||
|
showNotification('Test mode has been disabled - no real update is available', 'info');
|
||||||
|
hideUpdateAlert();
|
||||||
|
showNotification(`Current version: ${data.data.currentVersion}. You are up to date.`, 'success');
|
||||||
|
}
|
||||||
|
// Regular update handling
|
||||||
|
else if (data.data && data.data.updateAvailable) {
|
||||||
|
showUpdateAlert(data.data.currentVersion, data.data.remoteVersion);
|
||||||
|
showNotification(`Update is available: ${data.data.currentVersion} → ${data.data.remoteVersion}`, 'info');
|
||||||
|
} else {
|
||||||
|
hideUpdateAlert();
|
||||||
|
showNotification(`Current version: ${data.data.currentVersion}. You are up to date.`, 'success');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-enable button
|
||||||
|
floatingRefreshButton.textContent = 'Refresh Status';
|
||||||
|
floatingRefreshButton.disabled = false;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error during manual refresh:', error);
|
||||||
|
floatingRefreshButton.textContent = 'Refresh Status';
|
||||||
|
floatingRefreshButton.disabled = false;
|
||||||
|
showNotification('Error checking update status', 'danger');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Test mode toggle (for developers)
|
// Test mode toggle (for developers)
|
||||||
const testToggle = document.getElementById('toggle-test-update-button');
|
const testToggle = document.getElementById('toggle-test-update-button');
|
||||||
if (testToggle) {
|
if (testToggle) {
|
||||||
// Initialize based on current localStorage setting
|
// Initialize based on current localStorage setting
|
||||||
const isTestMode = localStorage.getItem('showUpdateButton') === 'true';
|
const isTestMode = localStorage.getItem('showUpdateButton') === 'true';
|
||||||
|
|
||||||
|
// If test mode is enabled but we have a version mismatch, update the stored version
|
||||||
|
if (isTestMode && versionElement && versionElement.textContent) {
|
||||||
|
const currentVersion = versionElement.textContent.trim();
|
||||||
|
if (localStorage.getItem(CURRENT_VERSION_KEY) !== currentVersion) {
|
||||||
|
localStorage.setItem(CURRENT_VERSION_KEY, currentVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update toggle text
|
// Update toggle text
|
||||||
testToggle.innerText = isTestMode ? 'Disable Test Update' : 'Enable Test Update';
|
testToggle.innerText = isTestMode ? 'Disable Test Update' : 'Enable Test Update';
|
||||||
|
|
||||||
@ -227,16 +578,206 @@ function initSystemStatus() {
|
|||||||
localStorage.setItem('showUpdateButton', newSetting);
|
localStorage.setItem('showUpdateButton', newSetting);
|
||||||
testToggle.innerText = newSetting ? 'Disable Test Update' : 'Enable Test Update';
|
testToggle.innerText = newSetting ? 'Disable Test Update' : 'Enable Test Update';
|
||||||
|
|
||||||
// Re-check for updates with new setting
|
if (newSetting) {
|
||||||
checkForUpdates();
|
// Get the current version from the version element
|
||||||
|
let currentVersion = '2.0.11'; // Default fallback
|
||||||
|
if (versionElement && versionElement.textContent) {
|
||||||
|
currentVersion = versionElement.textContent.trim();
|
||||||
|
}
|
||||||
|
|
||||||
showNotification(`Test update button ${newSetting ? 'enabled' : 'disabled'}`, 'info');
|
// If enabling test mode, force show update button
|
||||||
|
showUpdateAlert(currentVersion, '2.1.0-test');
|
||||||
|
updateStatusElement.innerHTML = '<i class="fas fa-exclamation-circle text-warning"></i> Update available (TEST MODE)';
|
||||||
|
|
||||||
|
// Add test mode indicator to floating notification
|
||||||
|
const floatingNotification = document.getElementById('floating-update-notification');
|
||||||
|
if (floatingNotification) {
|
||||||
|
const testBadge = document.createElement('div');
|
||||||
|
testBadge.style.backgroundColor = 'orange';
|
||||||
|
testBadge.style.color = 'black';
|
||||||
|
testBadge.style.padding = '3px 8px';
|
||||||
|
testBadge.style.borderRadius = '4px';
|
||||||
|
testBadge.style.fontWeight = 'bold';
|
||||||
|
testBadge.style.marginBottom = '5px';
|
||||||
|
testBadge.style.fontSize = '12px';
|
||||||
|
testBadge.textContent = 'TEST MODE - NOT A REAL UPDATE';
|
||||||
|
|
||||||
|
// Insert at the top of the notification
|
||||||
|
floatingNotification.insertBefore(testBadge, floatingNotification.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
showNotification('TEST MODE ENABLED - This is not a real update', 'warning');
|
||||||
|
} else {
|
||||||
|
// If disabling test mode, check for real updates
|
||||||
|
hideUpdateAlert();
|
||||||
|
|
||||||
|
// Force a check without the test parameter to get real status
|
||||||
|
const realCheckUrl = '/api/system/check-updates';
|
||||||
|
fetch(realCheckUrl, { headers: authHeaders() })
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log('Real update check result:', data);
|
||||||
|
if (data.status === 'success' && data.data && !data.data.updateAvailable) {
|
||||||
|
showNotification('No actual updates are available.', 'info');
|
||||||
|
} else if (data.status === 'success' && data.data && data.data.updateAvailable) {
|
||||||
|
showUpdateAlert(data.data.currentVersion, data.data.remoteVersion);
|
||||||
|
showNotification(`A real update is available: ${data.data.currentVersion} → ${data.data.remoteVersion}`, 'info');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error checking for real updates:', error));
|
||||||
|
|
||||||
|
showNotification('Test update button disabled', 'info');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Persistent update button - force display every second if update is available
|
||||||
|
function forceShowUpdateButton() {
|
||||||
|
const isUpdateAvailable = localStorage.getItem(UPDATE_KEY) === 'true';
|
||||||
|
|
||||||
|
if (isUpdateAvailable) {
|
||||||
|
// Get the most current version
|
||||||
|
let currentVersion = localStorage.getItem(CURRENT_VERSION_KEY);
|
||||||
|
const remoteVersion = localStorage.getItem(REMOTE_VERSION_KEY);
|
||||||
|
|
||||||
|
// If we have the version element on screen, use that as the source of truth
|
||||||
|
if (versionElement && versionElement.textContent) {
|
||||||
|
const displayedVersion = versionElement.textContent.trim();
|
||||||
|
// Update stored version if different
|
||||||
|
if (displayedVersion !== currentVersion) {
|
||||||
|
localStorage.setItem(CURRENT_VERSION_KEY, displayedVersion);
|
||||||
|
currentVersion = displayedVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentVersion && remoteVersion) {
|
||||||
|
// Check floating notification
|
||||||
|
const floatingNotification = document.getElementById('floating-update-notification');
|
||||||
|
if (floatingNotification && floatingNotification.style.display !== 'block') {
|
||||||
|
console.log('Forcing floating update notification display');
|
||||||
|
|
||||||
|
// Set the version text
|
||||||
|
const versionElement = document.getElementById('floating-update-version');
|
||||||
|
if (versionElement) {
|
||||||
|
versionElement.textContent = `Version ${currentVersion} → ${remoteVersion}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply strong styling - make sure to completely override any previous styles
|
||||||
|
floatingNotification.setAttribute('style', ''); // Clear any previous styles first
|
||||||
|
floatingNotification.setAttribute('style', ''); // Clear any previous styles first
|
||||||
|
floatingNotification.setAttribute('style',
|
||||||
|
'display: block !important; ' +
|
||||||
|
'visibility: visible !important; ' +
|
||||||
|
'opacity: 1 !important; ' +
|
||||||
|
'position: fixed !important; ' +
|
||||||
|
'top: 20px !important; ' +
|
||||||
|
'right: 20px !important; ' +
|
||||||
|
'width: 300px !important; ' +
|
||||||
|
'padding: 15px !important; ' +
|
||||||
|
'background-color: #ff5555 !important; ' +
|
||||||
|
'color: white !important; ' +
|
||||||
|
'border: 3px solid #cc0000 !important; ' +
|
||||||
|
'border-radius: 5px !important; ' +
|
||||||
|
'box-shadow: 0 0 20px rgba(0,0,0,0.5) !important; ' +
|
||||||
|
'z-index: 10000 !important; ' +
|
||||||
|
'font-weight: bold !important; ' +
|
||||||
|
'text-align: center !important;'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure button has correct event handler
|
||||||
|
const updateButton = document.getElementById('floating-update-button');
|
||||||
|
if (updateButton) {
|
||||||
|
// Remove any existing listeners
|
||||||
|
updateButton.removeEventListener('click', applyUpdate);
|
||||||
|
// Add new listener
|
||||||
|
updateButton.addEventListener('click', applyUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still try the original alert as a fallback
|
||||||
|
try {
|
||||||
|
const alertBox = updateAvailableDiv.querySelector('.alert');
|
||||||
|
if (alertBox && alertBox.style.display !== 'block') {
|
||||||
|
alertBox.style.display = 'block';
|
||||||
|
updateAvailableDiv.style.display = 'block';
|
||||||
|
|
||||||
|
// Update message
|
||||||
|
const spanElement = alertBox.querySelector('span');
|
||||||
|
if (spanElement) {
|
||||||
|
spanElement.textContent = `A new version is available: ${currentVersion} → ${remoteVersion}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error forcing original update button:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
loadSystemStatus();
|
loadSystemStatus();
|
||||||
checkForUpdates();
|
|
||||||
|
// SUPER EMERGENCY FIX: Force hide and remove all update notifications
|
||||||
|
const emergencyFix = () => {
|
||||||
|
// Hard reset all update states
|
||||||
|
localStorage.clear(); // Clear ALL localStorage to be absolutely safe
|
||||||
|
|
||||||
|
// Find and destroy any floating notification elements
|
||||||
|
document.querySelectorAll('[id*="notification"], [id*="update"], [class*="notification"], [class*="update"]').forEach(el => {
|
||||||
|
try {
|
||||||
|
if (el.id !== 'update-status' && !el.id.includes('refresh')) {
|
||||||
|
el.style.cssText = 'display: none !important; visibility: hidden !important; opacity: 0 !important';
|
||||||
|
el.removeAttribute('style');
|
||||||
|
|
||||||
|
if (el.parentNode) {
|
||||||
|
el.parentNode.removeChild(el);
|
||||||
|
console.log('Element removed from DOM:', el.id || el.className || 'unnamed element');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error removing element:', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a MutationObserver to keep killing any notification elements that might reappear
|
||||||
|
if (!window._notificationKiller) {
|
||||||
|
const observer = new MutationObserver(mutations => {
|
||||||
|
mutations.forEach(mutation => {
|
||||||
|
mutation.addedNodes.forEach(node => {
|
||||||
|
if (node.nodeType === 1) { // Element node
|
||||||
|
if ((node.id && (node.id.includes('notification') || node.id.includes('update'))) ||
|
||||||
|
(node.className && (node.className.includes('notification') || node.className.includes('update')))) {
|
||||||
|
if (node.id !== 'update-status' && !node.id.includes('refresh')) {
|
||||||
|
node.style.cssText = 'display: none !important';
|
||||||
|
if (node.parentNode) {
|
||||||
|
node.parentNode.removeChild(node);
|
||||||
|
console.log('Dynamically added notification killed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
|
||||||
|
window._notificationKiller = observer;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Super emergency notification cleanup complete');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run immediately
|
||||||
|
emergencyFix();
|
||||||
|
|
||||||
|
// Run again after delays to ensure it works
|
||||||
|
setTimeout(emergencyFix, 100);
|
||||||
|
setTimeout(emergencyFix, 500);
|
||||||
|
setTimeout(emergencyFix, 1000);
|
||||||
|
|
||||||
// Set interval to refresh uptime every minute
|
// Set interval to refresh uptime every minute
|
||||||
setInterval(loadSystemStatus, 60000);
|
setInterval(loadSystemStatus, 60000);
|
||||||
|
31
server.js
31
server.js
@ -90,8 +90,20 @@ const JWT_SECRET = process.env.JWT_SECRET || 'transmission-rss-manager-secret';
|
|||||||
const JWT_EXPIRY = '24h';
|
const JWT_EXPIRY = '24h';
|
||||||
|
|
||||||
// Get the version from package.json (single source of truth)
|
// Get the version from package.json (single source of truth)
|
||||||
|
// Re-read the package.json file each time to ensure we get the latest version
|
||||||
|
const APP_VERSION = (() => {
|
||||||
|
try {
|
||||||
|
// Use synchronous file read to ensure we have the version before continuing
|
||||||
|
const packageJsonPath = path.join(__dirname, 'package.json');
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||||
|
return packageJson.version;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error reading package.json version:', err);
|
||||||
|
// Fallback to requiring package.json if file read fails
|
||||||
const PACKAGE_JSON = require('./package.json');
|
const PACKAGE_JSON = require('./package.json');
|
||||||
const APP_VERSION = PACKAGE_JSON.version;
|
return PACKAGE_JSON.version;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
// Create Express app
|
// Create Express app
|
||||||
const app = express();
|
const app = express();
|
||||||
@ -1269,10 +1281,25 @@ app.get('/api/system/status', authenticateJWT, async (req, res) => {
|
|||||||
transmissionStatus = 'Disconnected';
|
transmissionStatus = 'Disconnected';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read version directly from package.json to ensure it's always current
|
||||||
|
let currentVersion = APP_VERSION;
|
||||||
|
try {
|
||||||
|
const packageJsonPath = path.join(__dirname, 'package.json');
|
||||||
|
const packageJsonContent = await fsPromises.readFile(packageJsonPath, 'utf8');
|
||||||
|
const packageData = JSON.parse(packageJsonContent);
|
||||||
|
currentVersion = packageData.version;
|
||||||
|
|
||||||
|
// Log the version for debugging
|
||||||
|
console.log(`System status endpoint returning version: ${currentVersion}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error reading package.json in status endpoint:', err);
|
||||||
|
// Fall back to the cached APP_VERSION
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
status: 'success',
|
status: 'success',
|
||||||
data: {
|
data: {
|
||||||
version: APP_VERSION,
|
version: currentVersion,
|
||||||
uptime,
|
uptime,
|
||||||
transmissionStatus
|
transmissionStatus
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user