mirror of
https://github.com/sassanix/Warracker.git
synced 2026-05-05 15:59:37 -05:00
60239bd637
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
103 lines
4.0 KiB
JavaScript
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');
|
|
})();
|