feat: Add optional web interface authentication with session management
Local Docker Build (Dev) / build-dev (pull_request) Successful in 3s

Authentication Features:
  - Optional HTTP Basic Auth with session cookies for web interface protection
  - Configurable username/password with session timeout (default: 1 hour)
  - Selective route protection (web interface protected, webhooks/APIs remain public)
  - Authentication status display with logout functionality in web UI
  - Session cleanup and graceful logout with browser re-authentication prompt

  Configuration:
  - WEB_AUTH_ENABLED in .env (default: false - no breaking changes)
  - WEB_AUTH_USERNAME/WEB_AUTH_PASSWORD in .env.secrets
  - WEB_AUTH_SESSION_TIMEOUT configurable (5min-24h range)

  Development Tools:
  - Added debug_tv.py for TV series/episode debugging (series, season, episode levels)
  - Comprehensive episode data inspection with validation and sources breakdown
  - Complements existing debug_movie.py for complete media debugging coverage

  Technical Implementation:
  - SimpleAuthMiddleware with FastAPI integration
  - Session management with automatic cleanup
  - Authentication status API endpoints
  - Responsive CSS styling for auth UI elements
  - JavaScript functions for auth checking and logout
This commit is contained in:
2025-10-20 16:21:50 -04:00
parent 1ad2a3d945
commit c66c19dc59
11 changed files with 570 additions and 17 deletions
+59
View File
@@ -10,6 +10,7 @@ let dashboardData = null;
document.addEventListener('DOMContentLoaded', function() {
initializeTabs();
initializeEventListeners();
checkAuthStatus(); // Check authentication status on page load
loadDashboard();
loadSeriesSources();
});
@@ -1375,4 +1376,62 @@ function updateEpisodeModalCounts() {
${videoCountText}
`;
}
}
// ===========================
// Authentication Functions
// ===========================
async function checkAuthStatus() {
try {
const response = await fetch('/api/auth/status');
const authStatus = await response.json();
const authStatusDiv = document.getElementById('auth-status');
const authUsernameSpan = document.getElementById('auth-username');
if (authStatus.auth_enabled && authStatus.authenticated) {
// Show authentication status with username
authUsernameSpan.textContent = authStatus.username;
authStatusDiv.style.display = 'flex';
} else if (authStatus.auth_enabled && !authStatus.authenticated) {
// This shouldn't happen if middleware is working, but handle it
console.warn('Auth enabled but not authenticated - middleware may be misconfigured');
} else {
// Authentication disabled - hide auth status
authStatusDiv.style.display = 'none';
}
} catch (error) {
console.error('Failed to check authentication status:', error);
// Hide auth status on error
document.getElementById('auth-status').style.display = 'none';
}
}
async function logout() {
if (!confirm('Are you sure you want to logout?')) {
return;
}
try {
const response = await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'same-origin'
});
if (response.ok) {
showToast('✅ Logged out successfully', 'success');
// Reload page to trigger authentication prompt
setTimeout(() => {
window.location.reload();
}, 1500);
} else {
showToast('❌ Logout failed', 'error');
}
} catch (error) {
console.error('Logout failed:', error);
showToast('❌ Logout error', 'error');
}
}