Various Fixes and QoL enhancements.

This commit is contained in:
Stefan Hardegger
2025-07-26 12:05:54 +02:00
parent 5e8164c6a4
commit f95d7aa8bb
32 changed files with 758 additions and 136 deletions

View File

@@ -21,15 +21,36 @@ api.interceptors.request.use((config) => {
return config;
});
// Global auth failure handler - can be set by AuthContext
let globalAuthFailureHandler: (() => void) | null = null;
export const setGlobalAuthFailureHandler = (handler: () => void) => {
globalAuthFailureHandler = handler;
};
// Response interceptor to handle auth errors
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Clear invalid token and redirect to login
// Handle authentication failures
if (error.response?.status === 401 || error.response?.status === 403) {
console.warn('Authentication failed, token may be expired or invalid');
// Clear invalid token
localStorage.removeItem('auth-token');
window.location.href = '/login';
// Use global handler if available (from AuthContext), otherwise fallback to direct redirect
if (globalAuthFailureHandler) {
globalAuthFailureHandler();
} else {
// Fallback for cases where AuthContext isn't available
window.location.href = '/login';
}
// Return a more specific error for components to handle gracefully
return Promise.reject(new Error('Authentication required'));
}
return Promise.reject(error);
}
);
@@ -150,6 +171,22 @@ export const storyApi = {
const response = await api.post('/stories/recreate-typesense-collection');
return response.data;
},
checkDuplicate: async (title: string, authorName: string): Promise<{
hasDuplicates: boolean;
count: number;
duplicates: Array<{
id: string;
title: string;
authorName: string;
createdAt: string;
}>;
}> => {
const response = await api.get('/stories/check-duplicate', {
params: { title, authorName }
});
return response.data;
},
};
// Author endpoints
@@ -240,6 +277,11 @@ export const tagApi = {
// Backend returns TagDto[], extract just the names
return response.data.map((tag: Tag) => tag.name);
},
getCollectionTags: async (): Promise<Tag[]> => {
const response = await api.get('/tags/collections');
return response.data;
},
};
// Series endpoints