TF-4136 Add config Sentry

This commit is contained in:
dab246
2025-12-15 12:01:21 +07:00
committed by Dat H. Pham
parent ad768f1552
commit ec75bb3cab
46 changed files with 804 additions and 216 deletions
+1 -1
View File
@@ -81,7 +81,6 @@ abstract class BaseController extends GetxController
final ImagePaths imagePaths = Get.find<ImagePaths>();
final ResponsiveUtils responsiveUtils = Get.find<ResponsiveUtils>();
final Uuid uuid = Get.find<Uuid>();
final ApplicationManager applicationManager = Get.find<ApplicationManager>();
final ToastManager toastManager = Get.find<ToastManager>();
final TwakeAppManager twakeAppManager = Get.find<TwakeAppManager>();
@@ -554,6 +553,7 @@ abstract class BaseController extends GetxController
Future<void> clearDataAndGoToLoginPage() async {
log('$runtimeType::clearDataAndGoToLoginPage:');
SentryManager.instance.clearUser();
await clearAllData();
removeAllPageAndGoToLogin();
}
@@ -82,7 +82,7 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
}
@override
void onError(DioError err, ErrorInterceptorHandler handler) async {
void onError(DioException err, ErrorInterceptorHandler handler) async {
logWarning('AuthorizationInterceptors::onError(): DIO_ERROR = $err');
try {
final requestOptions = err.requestOptions;
@@ -159,7 +159,7 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
);
if (e is ServerError || e is TemporarilyUnavailable) {
return super.onError(
DioError(requestOptions: err.requestOptions, error: e),
DioException(requestOptions: err.requestOptions, error: e),
handler,
);
} else {
@@ -5,6 +5,7 @@ import 'dart:io';
import 'package:core/data/model/query/query_parameter.dart';
import 'package:core/data/network/dio_client.dart';
import 'package:core/utils/app_logger.dart';
import 'package:dio/dio.dart' show DioException;
import 'package:get/get_connect/http/src/exceptions/exceptions.dart';
import 'package:model/oidc/oidc_configuration.dart';
import 'package:model/oidc/request/oidc_request.dart';
@@ -14,10 +15,9 @@ import 'package:model/oidc/response/oidc_user_info.dart';
import 'package:tmail_ui_user/features/login/data/extensions/service_path_extension.dart';
import 'package:tmail_ui_user/features/login/data/network/config/oidc_constant.dart';
import 'package:tmail_ui_user/features/login/data/network/endpoint.dart';
import 'package:tmail_ui_user/features/login/domain/exceptions/login_exception.dart';
import 'package:tmail_ui_user/features/login/data/network/oidc_error.dart';
import 'package:tmail_ui_user/features/login/domain/exceptions/login_exception.dart';
import 'package:tmail_ui_user/main/utils/app_config.dart';
import 'package:dio/dio.dart' show DioError;
class OIDCHttpClient {
@@ -45,7 +45,7 @@ class OIDCHttpClient {
} on FormatException catch (exception) {
log('checkOIDCIsAvailable(): error while parsing server response (JSON expected): ${exception.message}');
throw InvalidOIDCResponseException();
} on DioError catch (exception) {
} on DioException catch (exception) {
if (exception.error is HandshakeException) {
throw exception.error!;
} else if (exception.error is SocketException) {
@@ -29,6 +29,7 @@ import 'package:jmap_dart_client/jmap/quotas/quota.dart';
import 'package:model/model.dart';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
import 'package:rxdart/transformers.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:server_settings/server_settings/tmail_server_settings_extension.dart';
import 'package:tmail_ui_user/features/base/action/ui_action.dart';
import 'package:tmail_ui_user/features/base/mixin/ai_scribe_mixin.dart';
@@ -389,7 +390,7 @@ class MailboxDashBoardController extends ReloadableController
_registerStreamListener();
BackButtonInterceptor.add(onBackButtonInterceptor, name: AppRoutes.dashboard);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await applicationManager.initUserAgent();
await ApplicationManager().initUserAgent();
});
super.onInit();
}
@@ -868,6 +869,15 @@ class MailboxDashBoardController extends ReloadableController
accountId.value = currentAccountId;
synchronizeOwnEmailAddress(session.getOwnEmailAddressOrEmpty());
SentryManager.instance.setUser(
SentryUser(
id: currentAccountId.asString,
name: session.getUserDisplayName(),
username: session.username.value,
email: session.getOwnEmailAddressOrEmpty(),
)
);
_setUpMinInputLengthAutocomplete();
injectAutoCompleteBindings(session, currentAccountId);
injectRuleFilterBindings(session, currentAccountId);
@@ -3403,7 +3413,7 @@ class MailboxDashBoardController extends ReloadableController
_refreshActionEventController.close();
_notificationManager.closeStream();
_fcmService.closeStream();
applicationManager.releaseUserAgent();
ApplicationManager().releaseUserAgent();
BackButtonInterceptor.removeByName(AppRoutes.dashboard);
_identities = null;
outboxMailbox = null;
@@ -13,6 +13,7 @@ import 'package:jmap_dart_client/jmap/quotas/quota.dart';
import 'package:model/model.dart';
import 'package:rule_filter/rule_filter/capability_rule_filter.dart';
import 'package:scribe/scribe/ai/presentation/utils/ai_scribe_constants.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:server_settings/server_settings/capability_server_settings.dart';
import 'package:tmail_ui_user/features/base/action/ui_action.dart';
import 'package:tmail_ui_user/features/base/mixin/ai_scribe_mixin.dart';
@@ -151,6 +152,18 @@ class ManageAccountDashBoardController extends ReloadableController
sessionCurrent = session;
accountId.value = session?.accountId;
synchronizeOwnEmailAddress(session?.getOwnEmailAddressOrEmpty() ?? '');
if (session != null) {
SentryManager.instance.setUser(
SentryUser(
id: session.accountId.asString,
name: session.getUserDisplayName(),
username: session.username.value,
email: session.getOwnEmailAddressOrEmpty(),
),
);
}
_setUpMinInputLengthAutocomplete();
_bindingInteractorForMenuItemView(sessionCurrent, accountId.value);
_getVacationResponse();
@@ -147,8 +147,8 @@ class FileUploader {
resultJson: resultJson,
fileName: argsUpload.fileInfo.fileName);
}
} on DioError catch (exception) {
logWarning('FileUploader::_handleUploadAttachmentAction():DioError: $exception');
} on DioException catch (exception) {
logWarning('FileUploader::_handleUploadAttachmentAction():DioException: $exception');
throw exception.copyWith(
requestOptions: exception.requestOptions.copyWith(data: ''));
@@ -62,7 +62,7 @@ class UploadAttachment with EquatableMixin {
_updateEvent(Right(SuccessAttachmentUploadState(uploadTaskId, attachment, fileInfo)));
} catch (error, stackTrace) {
logWarning('UploadAttachment::upload():ERROR: $error');
if (error is DioError && error.type == DioErrorType.cancel) {
if (error is DioException && error.type == DioExceptionType.cancel) {
_updateEvent(Left(CancelAttachmentUploadState(uploadTaskId)));
} else {
try {
+3 -32
View File
@@ -1,51 +1,22 @@
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/build_utils.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
import 'package:tmail_ui_user/main/bindings/main_bindings.dart';
import 'package:tmail_ui_user/main/deep_links/deep_links_manager.dart';
import 'package:scribe/scribe/ai/localizations/scribe_localizations.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations_delegate.dart';
import 'package:tmail_ui_user/main/localizations/localization_service.dart';
import 'package:tmail_ui_user/main/main_entry.dart';
import 'package:tmail_ui_user/main/app_runner.dart';
import 'package:tmail_ui_user/main/pages/app_pages.dart';
import 'package:tmail_ui_user/main/routes/app_routes.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
import 'package:tmail_ui_user/main/utils/app_utils.dart';
import 'package:tmail_ui_user/main/utils/asset_preloader.dart';
import 'package:tmail_ui_user/main/utils/cozy_integration.dart';
import 'package:url_strategy/url_strategy.dart';
import 'package:worker_manager/worker_manager.dart';
Future<void> main() async {
initLogger(() async {
WidgetsFlutterBinding.ensureInitialized();
await runTmail();
});
}
Future<void> runTmail() async {
ThemeUtils.setSystemLightUIStyle();
await Future.wait([
MainBindings().dependencies(),
HiveCacheConfig.instance.setUp(),
Executor().warmUp(log: BuildUtils.isDebugMode),
AppUtils.loadEnvFile(),
if (PlatformInfo.isWeb)
AssetPreloader.preloadHtmlEditorAssets(),
]);
await CozyIntegration.integrateCozy();
await HiveCacheConfig.instance.initializeEncryptionKey();
setPathUrlStrategy();
runApp(const TMailApp());
await runAppWithMonitoring(runTmail);
}
class TMailApp extends StatefulWidget {
+49
View File
@@ -0,0 +1,49 @@
import 'dart:async';
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/sentry/sentry_manager.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:tmail_ui_user/main.dart';
import 'package:tmail_ui_user/main/main_entry.dart';
Future<void> runAppWithMonitoring(Future<void> Function() runTmail) async {
await runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized();
// Handling Flutter UI and Build Errors
FlutterError.onError = (details) async {
logError(
'FlutterError: ${details.exception}',
exception: details.exception,
stackTrace: details.stack,
);
FlutterError.presentError(details);
};
// Handling Uncaught and Platform-Specific Errors
PlatformDispatcher.instance.onError = (error, stack) {
logError(
'PlatformDispatcher: Error: $error',
exception: error,
stackTrace: stack,
);
return true;
};
await SentryManager.instance.initialize(
appRunner: () async {
await runTmailPreload();
runApp(SentryWidget(child: const TMailApp()));
},
fallBackRunner: runTmail,
);
}, (error, stack) async {
logError(
'Uncaught zone error: $error',
exception: error,
stackTrace: stack,
);
});
}
@@ -3,7 +3,6 @@ import 'package:core/data/utils/device_manager.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/app_toast.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/utils/application_manager.dart';
import 'package:core/utils/config/app_config_loader.dart';
import 'package:core/utils/file_utils.dart';
import 'package:core/utils/platform_info.dart';
@@ -73,7 +72,6 @@ class CoreBindings extends Bindings {
Get.put(AppConfigLoader());
Get.put(FileUtils());
Get.put(PrintUtils());
Get.put(ApplicationManager(Get.find<DeviceInfoPlugin>()));
Get.put(BeforeReconnectManager());
if (PlatformInfo.isIOS) {
Get.put(IOSNotificationManager());
@@ -5,10 +5,10 @@ import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:core/core.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_appauth/flutter_appauth.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/http/http_client.dart';
import 'package:sentry_dio/sentry_dio.dart';
import 'package:tmail_ui_user/features/contact/data/network/contact_api.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/email/data/network/mdn_api.dart';
@@ -88,18 +88,22 @@ class NetworkBindings extends Bindings {
}
void _bindingInterceptors() {
final dio = Get.find<Dio>();
Get.put(DynamicUrlInterceptors());
Get.put(AuthorizationInterceptors(
Get.find<Dio>(),
dio,
Get.find<AuthenticationClientBase>(),
Get.find<TokenOidcCacheManager>(),
Get.find<AccountCacheManager>(),
Get.find<IOSSharingManager>(),
));
Get.find<Dio>().interceptors.add(Get.find<DynamicUrlInterceptors>());
Get.find<Dio>().interceptors.add(Get.find<AuthorizationInterceptors>());
if (kDebugMode) {
Get.find<Dio>().interceptors.add(LogInterceptor(requestBody: true));
dio.interceptors.add(Get.find<DynamicUrlInterceptors>());
dio.interceptors.add(Get.find<AuthorizationInterceptors>());
if (BuildUtils.isDebugMode) {
dio.interceptors.add(LogInterceptor(requestBody: true));
}
if (SentryManager.instance.isSentryAvailable) {
dio.addSentry();
}
}
@@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:flutter_appauth/flutter_appauth.dart';
import 'package:get/get.dart';
import 'package:jmap_dart_client/http/http_client.dart';
import 'package:sentry_dio/sentry_dio.dart';
import 'package:tmail_ui_user/features/email/data/local/html_analyzer.dart';
import 'package:tmail_ui_user/features/email/data/network/email_api.dart';
import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart';
@@ -51,20 +52,23 @@ class NetworkIsolateBindings extends Bindings {
}
void _bindingInterceptors() {
Get.put(AuthorizationInterceptors(
Get.find<Dio>(tag: BindingTag.isolateTag),
final dio = Get.find<Dio>(tag: BindingTag.isolateTag);
final authorizationInterceptors = AuthorizationInterceptors(
dio,
Get.find<AuthenticationClientBase>(tag: BindingTag.isolateTag),
Get.find<TokenOidcCacheManager>(tag: BindingTag.isolateTag),
Get.find<AccountCacheManager>(tag: BindingTag.isolateTag),
Get.find<IOSSharingManager>(tag: BindingTag.isolateTag),
), tag: BindingTag.isolateTag);
Get.find<Dio>(tag: BindingTag.isolateTag).interceptors
.add(Get.find<DynamicUrlInterceptors>());
Get.find<Dio>(tag: BindingTag.isolateTag).interceptors
.add(Get.find<AuthorizationInterceptors>(tag: BindingTag.isolateTag));
);
Get.put(authorizationInterceptors, tag: BindingTag.isolateTag);
dio.interceptors.add(Get.find<DynamicUrlInterceptors>());
dio.interceptors.add(authorizationInterceptors);
if (BuildUtils.isDebugMode) {
Get.find<Dio>(tag: BindingTag.isolateTag).interceptors
.add(LogInterceptor(requestBody: true));
dio.interceptors.add(LogInterceptor(requestBody: true));
}
if (SentryManager.instance.isSentryAvailable) {
dio.addSentry();
}
}
@@ -32,7 +32,7 @@ class RemoteExceptionThrower extends ExceptionThrower {
}
void handleDioError(dynamic error) {
if (error is DioError) {
if (error is DioException) {
logWarning(
'RemoteExceptionThrower::throwException():type: ${error.type} | response: ${error.response} | error: ${error.error}',
);
@@ -74,13 +74,13 @@ class RemoteExceptionThrower extends ExceptionThrower {
throw error;
}
void _handleDioErrorWithoutResponse(DioError error) {
void _handleDioErrorWithoutResponse(DioException error) {
switch (error.type) {
case DioErrorType.connectionTimeout:
case DioExceptionType.connectionTimeout:
throw ConnectionTimeout(message: error.message);
case DioErrorType.connectionError:
case DioExceptionType.connectionError:
throw ConnectionError(message: error.message);
case DioErrorType.badResponse:
case DioExceptionType.badResponse:
throw const BadCredentialsException();
default:
final underlyingError = error.error;
+34
View File
@@ -0,0 +1,34 @@
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:core/utils/build_utils.dart';
import 'package:core/utils/config/env_loader.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/widgets.dart';
import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart';
import 'package:tmail_ui_user/main.dart';
import 'package:tmail_ui_user/main/bindings/main_bindings.dart';
import 'package:tmail_ui_user/main/utils/asset_preloader.dart';
import 'package:tmail_ui_user/main/utils/cozy_integration.dart';
import 'package:url_strategy/url_strategy.dart';
import 'package:worker_manager/worker_manager.dart';
Future<void> runTmail() async {
await runTmailPreload();
runApp(const TMailApp());
}
Future<void> runTmailPreload() async {
ThemeUtils.setSystemLightUIStyle();
await Future.wait([
MainBindings().dependencies(),
HiveCacheConfig.instance.setUp(),
Executor().warmUp(log: BuildUtils.isDebugMode),
EnvLoader.loadEnvFile(),
if (PlatformInfo.isWeb) AssetPreloader.preloadHtmlEditorAssets(),
]);
await CozyIntegration.integrateCozy();
await HiveCacheConfig.instance.initializeEncryptionKey();
setPathUrlStrategy();
}
-2
View File
@@ -11,9 +11,7 @@ class AppConfig {
static const int warningAttachmentFileSizeInMegabytes = 10;
static const int defaultLimitAutocomplete = 8;
static const String envFileName = 'env.file';
static const String appDashboardConfigurationPath = "configurations/app_dashboard.json";
static const String appFCMConfigurationPath = "configurations/env.fcm";
static const String iOSKeychainSharingGroupId = 'KUT463DS29.com.linagora.ios.teammail.shared';
static const String iOSKeychainSharingService = 'com.linagora.ios.teammail.sessions';
static const String saasPlatform = 'saas';
+1 -21
View File
@@ -2,36 +2,16 @@ import 'package:core/core.dart';
import 'package:date_format/date_format.dart' as date_format;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart' as intl;
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
import 'package:tmail_ui_user/main/localizations/language_code_constants.dart';
import 'package:tmail_ui_user/main/utils/app_config.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:tmail_ui_user/main/universal_import/html_stub.dart' as html;
import 'package:url_launcher/url_launcher.dart';
class AppUtils {
const AppUtils._();
static Future<void> loadEnvFile() async {
await dotenv.load(fileName: AppConfig.envFileName);
final mapEnvData = Map<String, String>.from(dotenv.env);
try {
await loadFcmConfigFileToEnv(currentMapEnvData: mapEnvData);
} catch (e) {
logWarning('AppUtils::loadEnvFile:loadFcmConfigFileToEnv: Exception = $e');
await dotenv.load(fileName: AppConfig.envFileName);
}
}
static Future<void> loadFcmConfigFileToEnv({Map<String, String>? currentMapEnvData}) {
return dotenv.load(
fileName: AppConfig.appFCMConfigurationPath,
mergeWith: currentMapEnvData ?? {}
);
}
static void launchLink(String url, {bool isNewTab = true}) {
log('AppUtils::launchLink: url = $url');
if (PlatformInfo.isWeb && HtmlUtils.isSafariBelow17()) {
+1 -1
View File
@@ -158,7 +158,7 @@ class ToastManager {
exception,
useDefaultMessage: true,
);
} else if (_isEmptySpamFolderFailure(exception)) {
} else if (_isEmptySpamFolderFailure(failure)) {
message = message ?? appLocalizations.emptySpamFolderFailed;
} else if (_isEmptyTrashFolderFailure(failure)) {
message = message ?? appLocalizations.emptyTrashFolderFailed;