fix: load configuration from config.json and fix authentication
- Load anime_directory and master_password_hash from config.json on startup - Sync configuration from config.json to settings object in fastapi_app.py - Update dependencies.py to load config from JSON if not in environment - Fix app.js to use makeAuthenticatedRequest() for all authenticated API calls - Fix API endpoint paths from /api/v1/anime to /api/anime - Update auth_service.py to load master_password_hash from config.json - Update auth.py setup endpoint to save master_password_hash to config - Fix rate limiting code to satisfy type checker - Update config.json with test master password hash Fixes: - 401 Unauthorized errors on /api/anime endpoint - 503 Service Unavailable errors on /api/anime/process/locks - Configuration not being loaded from config.json file - Authentication flow now works end-to-end with JWT tokens
This commit is contained in:
@@ -42,24 +42,40 @@ class AniWorldApp {
|
||||
try {
|
||||
// First check if we have a token
|
||||
const token = localStorage.getItem('access_token');
|
||||
console.log('checkAuthentication: token exists =', !!token);
|
||||
|
||||
// Build request with token if available
|
||||
const headers = {};
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
if (!token) {
|
||||
console.log('checkAuthentication: No token found, redirecting to /login');
|
||||
window.location.href = '/login';
|
||||
return;
|
||||
}
|
||||
|
||||
// Build request with token
|
||||
const headers = {
|
||||
'Authorization': `Bearer ${token}`
|
||||
};
|
||||
|
||||
const response = await fetch('/api/auth/status', { headers });
|
||||
console.log('checkAuthentication: response status =', response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
console.log('checkAuthentication: Response not OK, status =', response.status);
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log('checkAuthentication: data =', data);
|
||||
|
||||
if (!data.configured) {
|
||||
// No master password set, redirect to setup
|
||||
console.log('checkAuthentication: Not configured, redirecting to /setup');
|
||||
window.location.href = '/setup';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.authenticated) {
|
||||
// Not authenticated, redirect to login
|
||||
console.log('checkAuthentication: Not authenticated, redirecting to /login');
|
||||
localStorage.removeItem('access_token');
|
||||
localStorage.removeItem('token_expires_at');
|
||||
window.location.href = '/login';
|
||||
@@ -67,6 +83,7 @@ class AniWorldApp {
|
||||
}
|
||||
|
||||
// User is authenticated, show logout button
|
||||
console.log('checkAuthentication: Authenticated successfully');
|
||||
const logoutBtn = document.getElementById('logout-btn');
|
||||
if (logoutBtn) {
|
||||
logoutBtn.style.display = 'block';
|
||||
@@ -539,22 +556,35 @@ class AniWorldApp {
|
||||
try {
|
||||
this.showLoading();
|
||||
|
||||
const response = await fetch('/api/v1/anime');
|
||||
const response = await this.makeAuthenticatedRequest('/api/anime');
|
||||
|
||||
if (response.status === 401) {
|
||||
window.location.href = '/login';
|
||||
if (!response) {
|
||||
// makeAuthenticatedRequest returns null and handles redirect on auth failure
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.status === 'success') {
|
||||
// Check if response has the expected format
|
||||
if (Array.isArray(data)) {
|
||||
// API returns array of AnimeSummary objects directly
|
||||
this.seriesData = data.map(anime => ({
|
||||
id: anime.id,
|
||||
name: anime.title,
|
||||
title: anime.title,
|
||||
missing_episodes: anime.missing_episodes || 0,
|
||||
episodeDict: {} // Will be populated when needed
|
||||
}));
|
||||
} else if (data.status === 'success') {
|
||||
// Legacy format support
|
||||
this.seriesData = data.series;
|
||||
this.applyFiltersAndSort();
|
||||
this.renderSeries();
|
||||
} else {
|
||||
this.showToast(`Error loading series: ${data.message}`, 'error');
|
||||
this.showToast(`Error loading series: ${data.message || 'Unknown error'}`, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
this.applyFiltersAndSort();
|
||||
this.renderSeries();
|
||||
} catch (error) {
|
||||
console.error('Error loading series:', error);
|
||||
this.showToast('Failed to load series', 'error');
|
||||
@@ -783,7 +813,7 @@ class AniWorldApp {
|
||||
try {
|
||||
this.showLoading();
|
||||
|
||||
const response = await this.makeAuthenticatedRequest('/api/v1/anime/search', {
|
||||
const response = await this.makeAuthenticatedRequest('/api/anime/search', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -836,7 +866,7 @@ class AniWorldApp {
|
||||
|
||||
async addSeries(link, name) {
|
||||
try {
|
||||
const response = await this.makeAuthenticatedRequest('/api/v1/anime/add', {
|
||||
const response = await this.makeAuthenticatedRequest('/api/anime/add', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -870,7 +900,7 @@ class AniWorldApp {
|
||||
try {
|
||||
const folders = Array.from(this.selectedSeries);
|
||||
|
||||
const response = await this.makeAuthenticatedRequest('/api/v1/anime/download', {
|
||||
const response = await this.makeAuthenticatedRequest('/api/anime/download', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -894,7 +924,7 @@ class AniWorldApp {
|
||||
|
||||
async rescanSeries() {
|
||||
try {
|
||||
const response = await this.makeAuthenticatedRequest('/api/v1/anime/rescan', {
|
||||
const response = await this.makeAuthenticatedRequest('/api/anime/rescan', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
@@ -1030,7 +1060,7 @@ class AniWorldApp {
|
||||
|
||||
async checkProcessLocks() {
|
||||
try {
|
||||
const response = await this.makeAuthenticatedRequest('/api/v1/anime/process/locks');
|
||||
const response = await this.makeAuthenticatedRequest('/api/anime/process/locks');
|
||||
if (!response) {
|
||||
// If no response, set status as idle
|
||||
this.updateProcessStatus('rescan', false);
|
||||
@@ -1101,7 +1131,7 @@ class AniWorldApp {
|
||||
|
||||
try {
|
||||
// Load current status
|
||||
const response = await this.makeAuthenticatedRequest('/api/v1/anime/status');
|
||||
const response = await this.makeAuthenticatedRequest('/api/anime/status');
|
||||
if (!response) return;
|
||||
const data = await response.json();
|
||||
|
||||
@@ -1600,7 +1630,7 @@ class AniWorldApp {
|
||||
|
||||
async refreshStatus() {
|
||||
try {
|
||||
const response = await this.makeAuthenticatedRequest('/api/v1/anime/status');
|
||||
const response = await this.makeAuthenticatedRequest('/api/anime/status');
|
||||
if (!response) return;
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user