fix: gate frontend polling timers and intercept unauthenticated requests to prevent 403 errors on login screen
This commit is contained in:
1 parent
750cfba699
commit
5c9f81f0c9
8 files changed
+42
-42
No files matched your search
@@ -33,6 +33,21 @@ class ApiConstants {
|
||||
InterceptorsWrapper(
|
||||
onRequest: (options, handler) {
|
||||
options.baseUrl = _baseUrl;
|
||||
|
||||
// Prevent unauthenticated or malformed token requests from hitting the server
|
||||
final authHeader = options.headers['Authorization']?.toString() ?? '';
|
||||
if (authHeader == 'Bearer null' ||
|
||||
authHeader == 'Bearer ' ||
|
||||
authHeader == 'Bearer undefined') {
|
||||
return handler.reject(
|
||||
DioException(
|
||||
requestOptions: options,
|
||||
type: DioExceptionType.cancel,
|
||||
error: 'Unauthenticated request cancelled: Invalid Authorization header ($authHeader)',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return handler.next(options);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -77,44 +77,16 @@ class AuthNotifier extends AsyncNotifier<UserModel?> {
|
||||
}
|
||||
|
||||
void _invalidateAllProviders() {
|
||||
// Documents & folders
|
||||
// Only reset persistent filter and navigation states.
|
||||
// autoDispose FutureProviders clean themselves up automatically when screens unmount;
|
||||
// invalidating them during logout triggers unnecessary unauthenticated API requests.
|
||||
ref.invalidate(documentQueryProvider);
|
||||
ref.invalidate(documentPageProvider);
|
||||
ref.invalidate(documentMutationProvider);
|
||||
ref.invalidate(printQueueMutationProvider);
|
||||
ref.invalidate(trashMutationProvider);
|
||||
ref.invalidate(requirementMutationProvider);
|
||||
ref.invalidate(folderMutationProvider);
|
||||
ref.invalidate(openedFolderProvider);
|
||||
|
||||
// Archives
|
||||
ref.invalidate(archiveQueryProvider);
|
||||
ref.invalidate(archiveDocumentQueryProvider);
|
||||
ref.invalidate(archiveMutationProvider);
|
||||
|
||||
// Students
|
||||
ref.invalidate(studentQueryProvider);
|
||||
ref.invalidate(studentMutationProvider);
|
||||
|
||||
// Users & notifications
|
||||
ref.invalidate(usersProvider);
|
||||
ref.invalidate(notificationsProvider);
|
||||
|
||||
// Navigation
|
||||
ref.invalidate(activeTabProvider);
|
||||
|
||||
// Dashboard
|
||||
ref.invalidate(dashboardDataProvider);
|
||||
|
||||
// Setup / Settings
|
||||
ref.invalidate(academicYearsListProvider);
|
||||
ref.invalidate(sectionsListProvider);
|
||||
ref.invalidate(gradeLevelsListProvider);
|
||||
ref.invalidate(setupMutationProvider);
|
||||
|
||||
// Reports
|
||||
ref.invalidate(selectedAcademicYearIdProvider);
|
||||
ref.invalidate(academicYearsProvider);
|
||||
ref.invalidate(selectedGradeLevelProvider);
|
||||
ref.invalidate(selectedSectionIdProvider);
|
||||
ref.invalidate(selectedStatusFilterProvider);
|
||||
@@ -123,9 +95,6 @@ class AuthNotifier extends AsyncNotifier<UserModel?> {
|
||||
ref.invalidate(missingDocsFilterExpandedProvider);
|
||||
ref.invalidate(yearlyComparisonSelectedYearsProvider);
|
||||
ref.invalidate(yearlyComparisonSelectedStatusesProvider);
|
||||
|
||||
// OCR
|
||||
ref.invalidate(ocrProvider);
|
||||
}
|
||||
|
||||
Future<void> refreshUser() async {
|
||||
|
||||
@@ -84,9 +84,12 @@ class _ArchivesScreenState extends ConsumerState<ArchivesScreen>
|
||||
_tabController = TabController(length: 2, vsync: this);
|
||||
|
||||
_pollingTimer = Timer.periodic(const Duration(seconds: 15), (_) {
|
||||
if (!mounted) return;
|
||||
ref.invalidate(archiveStudentFoldersProvider);
|
||||
ref.invalidate(archiveDocumentPageProvider);
|
||||
if (mounted &&
|
||||
ref.read(authProvider).value != null &&
|
||||
ref.read(activeTabProvider) == 'Archives') {
|
||||
ref.invalidate(archiveStudentFoldersProvider);
|
||||
ref.invalidate(archiveDocumentPageProvider);
|
||||
}
|
||||
});
|
||||
|
||||
_tabController.addListener(() {
|
||||
|
||||
@@ -99,7 +99,9 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
|
||||
void _startPolling() {
|
||||
_pollingTimer?.cancel();
|
||||
_pollingTimer = Timer.periodic(const Duration(seconds: 5), (_) {
|
||||
if (mounted && ref.read(activeTabProvider) == 'Dashboard') {
|
||||
if (mounted &&
|
||||
ref.read(authProvider).value != null &&
|
||||
ref.read(activeTabProvider) == 'Dashboard') {
|
||||
ref.invalidate(dashboardDataProvider);
|
||||
ref.invalidate(dashboardKpisProvider);
|
||||
ref.read(notificationsProvider.notifier).refreshNotifications();
|
||||
|
||||
@@ -13,6 +13,7 @@ import '../../shared/inputs/app_search_bar.dart';
|
||||
import '../../shared/buttons/primary_button.dart';
|
||||
import '../../providers/document_provider.dart';
|
||||
import '../../providers/student_provider.dart';
|
||||
import '../../providers/auth_provider.dart';
|
||||
import '../../shared/widgets/app_pagination.dart';
|
||||
import '../../providers/navigation_provider.dart';
|
||||
|
||||
@@ -95,7 +96,9 @@ class _DocumentsScreenState extends ConsumerState<DocumentsScreen>
|
||||
_searchFocusNode.addListener(_onSearchFocusChanged);
|
||||
|
||||
_pollingTimer = Timer.periodic(const Duration(seconds: 15), (_) {
|
||||
if (mounted) {
|
||||
if (mounted &&
|
||||
ref.read(authProvider).value != null &&
|
||||
ref.read(activeTabProvider) == 'Documents') {
|
||||
if (_tabController.index == 0) {
|
||||
ref.invalidate(foldersProvider);
|
||||
ref.invalidate(studentFoldersProvider);
|
||||
|
||||
@@ -13,6 +13,7 @@ import '../../shared/buttons/primary_button.dart';
|
||||
import '../../shared/cards/stat_card.dart';
|
||||
import '../../providers/reports_provider.dart';
|
||||
import '../../providers/navigation_provider.dart';
|
||||
import '../../providers/auth_provider.dart';
|
||||
import '../../shared/dialogs/file_save_preview_dialog.dart';
|
||||
import '../documents/widgets/student_profile_modal.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
@@ -59,7 +60,9 @@ class _ReportsScreenState extends ConsumerState<ReportsScreen> {
|
||||
|
||||
// Polling every 5 seconds
|
||||
_pollingTimer = Timer.periodic(const Duration(seconds: 5), (_) {
|
||||
if (mounted && ref.read(activeTabProvider) == 'Reports') {
|
||||
if (mounted &&
|
||||
ref.read(authProvider).value != null &&
|
||||
ref.read(activeTabProvider) == 'Reports') {
|
||||
_refreshData();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import '../../shared/inputs/custom_text_field.dart';
|
||||
import '../../shared/buttons/primary_button.dart';
|
||||
import '../../providers/users_provider.dart';
|
||||
import '../../providers/setup_provider.dart';
|
||||
import '../../providers/auth_provider.dart';
|
||||
import '../../../domain/entities/setup_models.dart';
|
||||
import '../../../domain/entities/system_user.dart';
|
||||
import '../../shared/dialogs/error_dialog.dart';
|
||||
@@ -63,7 +64,9 @@ class _TeacherManagementModalState extends ConsumerState<TeacherManagementModal>
|
||||
|
||||
// Polling every 5 seconds
|
||||
_pollingTimer = Timer.periodic(const Duration(seconds: 5), (_) {
|
||||
_refreshData();
|
||||
if (mounted && ref.read(authProvider).value != null) {
|
||||
_refreshData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user