feat: Complete frontend-backend integration with JWT authentication

Implemented full JWT-based authentication integration between frontend and backend:

Frontend Changes:
- Updated login.html to store JWT tokens in localStorage after successful login
- Updated setup.html to use correct API payload format (master_password)
- Modified app.js and queue.js to include Bearer tokens in all authenticated requests
- Updated makeAuthenticatedRequest() to add Authorization header with JWT token
- Enhanced checkAuthentication() to verify token and redirect on 401 responses
- Updated logout() to clear tokens from localStorage

API Endpoint Updates:
- Mapped queue API endpoints to new backend structure
- /api/queue/clear → /api/queue/completed (DELETE) for clearing completed
- /api/queue/remove → /api/queue/{item_id} (DELETE) for single removal
- /api/queue/retry payload changed to {item_ids: []} array format
- /api/download/pause|resume|cancel → /api/queue/pause|resume|stop

Testing:
- Created test_frontend_integration_smoke.py with JWT token validation tests
- Verified login returns access_token, token_type, and expires_at
- Tested Bearer token authentication on protected endpoints
- Smoke tests passing for authentication flow

Documentation:
- Updated infrastructure.md with JWT authentication implementation details
- Documented token storage, API endpoint changes, and response formats
- Marked Frontend Integration task as completed in instructions.md
- Added frontend integration testing section

WebSocket:
- Verified WebSocket integration with new backend (already functional)
- Dual event handlers support both old and new message types
- Room-based subscriptions working correctly

This completes Task 7: Frontend Integration from the development instructions.
This commit is contained in:
2025-10-17 19:27:52 +02:00
parent 2bc616a062
commit 0957a6e183
8 changed files with 550 additions and 89 deletions

View File

@@ -323,13 +323,19 @@
const data = await response.json();
if (data.status === 'success') {
showMessage(data.message, 'success');
if (response.ok && data.access_token) {
// Store JWT token in localStorage
localStorage.setItem('access_token', data.access_token);
if (data.expires_at) {
localStorage.setItem('token_expires_at', data.expires_at);
}
showMessage('Login successful', 'success');
setTimeout(() => {
window.location.href = '/';
}, 1000);
} else {
showMessage(data.message, 'error');
const errorMessage = data.detail || data.message || 'Invalid credentials';
showMessage(errorMessage, 'error');
passwordInput.value = '';
passwordInput.focus();
}

View File

@@ -503,22 +503,20 @@
'Content-Type': 'application/json',
},
body: JSON.stringify({
password,
directory
master_password: password
})
});
const data = await response.json();
if (data.status === 'success') {
showMessage('Setup completed successfully! Redirecting...', 'success');
if (response.ok && data.status === 'ok') {
showMessage('Setup completed successfully! Redirecting to login...', 'success');
setTimeout(() => {
// Use redirect_url from API response, fallback to /login
const redirectUrl = data.redirect_url || '/login';
window.location.href = redirectUrl;
window.location.href = '/login';
}, 2000);
} else {
showMessage(data.message, 'error');
const errorMessage = data.detail || data.message || 'Setup failed';
showMessage(errorMessage, 'error');
}
} catch (error) {
showMessage('Setup failed. Please try again.', 'error');