Files
Warracker/frontend/footer-fix.js
T
sassanix 60239bd637 Fix Apprise notification system, scheduler stability, and email configuration
Fixes & Enhancements

* Resolved five critical Apprise notification issues:
  • Ensured configuration reload during scheduled jobs
  • Fixed warranty data fetching for Apprise-only users
  • Refactored notification dispatch logic with dedicated helpers
  • Corrected handler scoping via Flask app context
  • Wrapped scheduler jobs with Flask app context to prevent context errors
  → Verified: Scheduled Apprise notifications now work reliably for "Apprise only" and "Both" channels.

* Added support for SMTP\_FROM\_ADDRESS environment variable, allowing sender address customization independent of SMTP username. (PR #115)

* Fixed duplicate scheduled notifications in multi-worker environments:
  • Strengthened should\_run\_scheduler() logic
  • Now guarantees exactly one scheduler instance across all Gunicorn modes.

* Fixed stale database connection handling in scheduled jobs:
  • Fresh connection acquired each run, properly released via try/finally
  • Eliminates "server closed the connection" errors.

* Definitive scheduler logic fix for all memory modes (ultra-light, optimized, performance):
  • Single-worker runs scheduler if GUNICORN\_WORKER\_ID is unset
  • Multi-worker: only worker 0 runs scheduler.

Impact

* Apprise and Email notifications are now stable, reliable, and production-ready
* No more duplicate or missed notifications across all memory modes
* Improved system efficiency and robustness
2025-08-24 12:34:40 -03:00

103 lines
4.0 KiB
JavaScript

/**
* Footer Width Fix - Universal
* Ensures the Warracker footer spans full width across all pages
* Handles both light and dark themes automatically
*/
(function() {
'use strict';
// Apply footer styles based on theme
function applyFooterStyles() {
const footer = document.getElementById('warrackerFooter');
const link = document.getElementById('warrackerFooterLink');
if (!footer) return; // Exit if footer doesn't exist on this page
// Detect dark mode
const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark' ||
document.documentElement.classList.contains('dark-mode') ||
document.body.classList.contains('dark-mode');
if (isDarkMode) {
// Dark mode styles with full width override
footer.style.cssText = `
width: 100vw !important;
margin-top: 50px !important;
margin-left: calc(-50vw + 50%) !important;
margin-right: calc(-50vw + 50%) !important;
padding: 20px !important;
text-align: center !important;
border-top: 1px solid #444 !important;
background-color: #2d2d2d !important;
color: #e0e0e0 !important;
font-size: 0.9rem !important;
position: relative !important;
left: 0 !important;
right: 0 !important;
max-width: none !important;
box-sizing: border-box !important;
`;
if (link) {
link.style.cssText = 'color: #4dabf7 !important; text-decoration: none !important; font-weight: 500 !important;';
}
} else {
// Light mode styles with full width override
footer.style.cssText = `
width: 100vw !important;
margin-top: 50px !important;
margin-left: calc(-50vw + 50%) !important;
margin-right: calc(-50vw + 50%) !important;
padding: 20px !important;
text-align: center !important;
border-top: 1px solid #e0e0e0 !important;
background-color: #ffffff !important;
color: #333333 !important;
font-size: 0.9rem !important;
position: relative !important;
left: 0 !important;
right: 0 !important;
max-width: none !important;
box-sizing: border-box !important;
`;
if (link) {
link.style.cssText = 'color: #3498db !important; text-decoration: none !important; font-weight: 500 !important;';
}
}
}
// Initialize footer styles when DOM is ready
function initFooterFix() {
applyFooterStyles();
// Watch for theme changes on document.documentElement
const observer = new MutationObserver(applyFooterStyles);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme', 'class']
});
// Also watch body for theme changes (fallback)
observer.observe(document.body, {
attributes: true,
attributeFilter: ['class']
});
// Listen for custom theme change events if they exist
document.addEventListener('themeChanged', applyFooterStyles);
window.addEventListener('themeChanged', applyFooterStyles);
}
// Auto-initialize when DOM is loaded
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initFooterFix);
} else {
// DOM is already loaded
initFooterFix();
}
// Expose function globally in case manual calls are needed
window.applyFooterFix = applyFooterStyles;
console.log('Footer Fix loaded - Footer will span full width on all pages');
})();