From 6b059c6bfc99f095d88b35a5e5e74723a63887a1 Mon Sep 17 00:00:00 2001 From: Alex Tarana <81036793+alextarana@users.noreply.github.com> Date: Wed, 11 Jun 2025 14:30:11 +0200 Subject: [PATCH 1/2] Add ZTE router support with manufacturer selection --- lib/application/auth/auth_bloc.dart | 20 ++++- .../auth/sign_in_form/sign_in_form_bloc.dart | 17 +++- lib/domain/auth/manufacturer.dart | 1 + .../auth/router_zte_auth_facade.dart | 81 +++++++++++++++++++ lib/injection.config.dart | 18 ++++- .../pages/sign_in/widgets/sign_in_form.dart | 49 ++++++++--- 6 files changed, 167 insertions(+), 19 deletions(-) create mode 100644 lib/domain/auth/manufacturer.dart create mode 100644 lib/infrastructure/auth/router_zte_auth_facade.dart diff --git a/lib/application/auth/auth_bloc.dart b/lib/application/auth/auth_bloc.dart index 1b3ada3..a2803e2 100644 --- a/lib/application/auth/auth_bloc.dart +++ b/lib/application/auth/auth_bloc.dart @@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:injectable/injectable.dart'; import 'package:ltemanager2/domain/auth/i_auth_facade.dart'; +import 'package:shared_preferences/shared_preferences.dart'; part 'auth_bloc.freezed.dart'; part 'auth_event.dart'; @@ -10,12 +11,18 @@ part 'auth_state.dart'; @injectable class AuthBloc extends Bloc { - final IAuthFacade _authFacade; + final IAuthFacade _huaweiAuthFacade; + final IAuthFacade _zteAuthFacade; + final SharedPreferences _preferences; - AuthBloc(this._authFacade) : super(const AuthState.initial()) { + AuthBloc( + this._huaweiAuthFacade, + @Named('RouterZteAuthFacade') this._zteAuthFacade, + this._preferences, + ) : super(const AuthState.initial()) { on( (event, emit) async { - final result = await _authFacade.signOut(); + final result = await _getFacade().signOut(); emit( result.fold( (_) => const AuthState.authenticated(), @@ -25,7 +32,7 @@ class AuthBloc extends Bloc { }, ); on((event, emit) async { - final userOption = await _authFacade.isSignedIn(); + final userOption = await _getFacade().isSignedIn(); debugPrint(userOption.toString()); @@ -37,4 +44,9 @@ class AuthBloc extends Bloc { ); }); } + + IAuthFacade _getFacade() { + final manufacturer = _preferences.getString('_manufacturer') ?? 'huawei'; + return manufacturer == 'zte' ? _zteAuthFacade : _huaweiAuthFacade; + } } diff --git a/lib/application/auth/sign_in_form/sign_in_form_bloc.dart b/lib/application/auth/sign_in_form/sign_in_form_bloc.dart index 2841877..25be947 100644 --- a/lib/application/auth/sign_in_form/sign_in_form_bloc.dart +++ b/lib/application/auth/sign_in_form/sign_in_form_bloc.dart @@ -6,6 +6,7 @@ import 'package:injectable/injectable.dart'; import 'package:ltemanager2/domain/auth/auth_failure.dart'; import 'package:ltemanager2/domain/auth/i_auth_facade.dart'; import 'package:ltemanager2/domain/auth/value_objects.dart'; +import 'package:shared_preferences/shared_preferences.dart'; part 'sign_in_form_bloc.freezed.dart'; part 'sign_in_form_event.dart'; @@ -13,9 +14,15 @@ part 'sign_in_form_state.dart'; @injectable class SignInFormBloc extends Bloc { - final IAuthFacade _authFacade; + final IAuthFacade _huaweiAuthFacade; + final IAuthFacade _zteAuthFacade; + final SharedPreferences _preferences; - SignInFormBloc(this._authFacade) : super(SignInFormState.initial()) { + SignInFormBloc( + this._huaweiAuthFacade, + @Named('RouterZteAuthFacade') this._zteAuthFacade, + this._preferences, + ) : super(SignInFormState.initial()) { on( (event, emit) { emit( @@ -62,7 +69,11 @@ class SignInFormBloc extends Bloc { ), ); - failureOrSuccess = await _authFacade.signInWithEmailAndPassword( + final manufacturer = + _preferences.getString('_manufacturer') ?? 'huawei'; + final facade = + manufacturer == 'zte' ? _zteAuthFacade : _huaweiAuthFacade; + failureOrSuccess = await facade.signInWithEmailAndPassword( url: state.ipAddress, username: state.username, password: state.password, diff --git a/lib/domain/auth/manufacturer.dart b/lib/domain/auth/manufacturer.dart new file mode 100644 index 0000000..2df007c --- /dev/null +++ b/lib/domain/auth/manufacturer.dart @@ -0,0 +1 @@ +enum Manufacturer { huawei, zte } diff --git a/lib/infrastructure/auth/router_zte_auth_facade.dart b/lib/infrastructure/auth/router_zte_auth_facade.dart new file mode 100644 index 0000000..098c831 --- /dev/null +++ b/lib/infrastructure/auth/router_zte_auth_facade.dart @@ -0,0 +1,81 @@ +import 'dart:convert'; + +import 'package:crypto/crypto.dart'; +import 'package:dartz/dartz.dart'; +import 'package:http/http.dart' as http; +import 'package:injectable/injectable.dart'; +import 'package:ltemanager2/domain/auth/auth_failure.dart'; +import 'package:ltemanager2/domain/auth/i_auth_facade.dart'; +import 'package:ltemanager2/domain/auth/value_objects.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +@LazySingleton(as: IAuthFacade, instanceName: 'RouterZteAuthFacade') +class RouterZteAuthFacade implements IAuthFacade { + final SharedPreferences _sharedPreferences; + + RouterZteAuthFacade(this._sharedPreferences); + + Future _setRouterURL(String url) async { + await _sharedPreferences.setString('_baseUrl', url); + } + + @override + Future> signInWithEmailAndPassword({ + required IPAddress url, + required Username username, + required Password password, + }) async { + final urlStr = url.value.getOrElse(() => ''); + await _setRouterURL(urlStr); + final passwordStr = password.value.getOrElse(() => ''); + + try { + final ldResp = await http.get( + Uri.parse('$urlStr/goform/goform_get_cmd_process?isTest=false&cmd=LD'), + headers: {'referer': urlStr}, + ); + if (ldResp.statusCode != 200) { + return left(const AuthFailure.serverError()); + } + final ld = (jsonDecode(ldResp.body) as Map)['LD'] as String; + + final first = sha256.convert(utf8.encode(passwordStr)).toString().toUpperCase(); + final second = sha256.convert(utf8.encode('$first$ld')).toString().toUpperCase(); + + final loginResp = await http.get( + Uri.parse('$urlStr/goform/goform_set_cmd_process?isTest=false&goformId=LOGIN&password=$second'), + headers: {'referer': urlStr}, + ); + + if (loginResp.statusCode == 200) { + final body = jsonDecode(loginResp.body) as Map; + if (body['result'] == '0') { + await _sharedPreferences.setString( + '_sessionCookie', + loginResp.headers['set-cookie'] ?? '', + ); + return right(unit); + } + return left(const AuthFailure.invalidEmailAndPasswordCombination()); + } + return left(const AuthFailure.serverError()); + } catch (_) { + return left(const AuthFailure.serverError()); + } + } + + @override + Future> isSignedIn() async { + final cookie = _sharedPreferences.getString('_sessionCookie') ?? ''; + if (cookie.isEmpty) { + return left(const AuthFailure.notAuthenticated()); + } + return right(unit); + } + + @override + Future> signOut() async { + await _sharedPreferences.setString('_sessionCookie', ''); + return right(unit); + } +} diff --git a/lib/injection.config.dart b/lib/injection.config.dart index 27dc305..c18a469 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -12,6 +12,7 @@ import 'package:shared_preferences/shared_preferences.dart' as _i4; import 'application/auth/auth_bloc.dart' as _i10; import 'application/auth/sign_in_form/sign_in_form_bloc.dart' as _i9; import 'domain/auth/i_auth_facade.dart' as _i5; +import 'infrastructure/auth/router_zte_auth_facade.dart' as _i12; import 'infrastructure/auth/router_auth_facade.dart' as _i7; import 'infrastructure/auth/router_huawei_auth_facade.dart' as _i6; import 'infrastructure/core/module_injectable.dart' as _i11; @@ -41,6 +42,10 @@ Future<_i1.GetIt> $initGetIt( get<_i3.HuaweiRouterApi>(), get<_i4.SharedPreferences>(), )); + gh.lazySingleton<_i5.IAuthFacade>( + () => _i12.RouterZteAuthFacade(get<_i4.SharedPreferences>()), + instanceName: 'RouterZteAuthFacade', + ); gh.lazySingleton<_i5.IAuthFacade>( () => _i7.RouterAuthFacade( get<_i8.RouterApi>(), @@ -48,9 +53,16 @@ Future<_i1.GetIt> $initGetIt( ), instanceName: 'RouterGenericAuthFacade', ); - gh.factory<_i9.SignInFormBloc>( - () => _i9.SignInFormBloc(get<_i5.IAuthFacade>())); - gh.factory<_i10.AuthBloc>(() => _i10.AuthBloc(get<_i5.IAuthFacade>())); + gh.factory<_i9.SignInFormBloc>(() => _i9.SignInFormBloc( + get<_i5.IAuthFacade>(), + get<_i5.IAuthFacade>(instanceName: 'RouterZteAuthFacade'), + get<_i4.SharedPreferences>(), + )); + gh.factory<_i10.AuthBloc>(() => _i10.AuthBloc( + get<_i5.IAuthFacade>(), + get<_i5.IAuthFacade>(instanceName: 'RouterZteAuthFacade'), + get<_i4.SharedPreferences>(), + )); return get; } diff --git a/lib/presentation/pages/sign_in/widgets/sign_in_form.dart b/lib/presentation/pages/sign_in/widgets/sign_in_form.dart index 55d437b..b8ee47d 100644 --- a/lib/presentation/pages/sign_in/widgets/sign_in_form.dart +++ b/lib/presentation/pages/sign_in/widgets/sign_in_form.dart @@ -8,10 +8,19 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:ltemanager2/application/auth/auth_bloc.dart'; import 'package:ltemanager2/application/auth/sign_in_form/sign_in_form_bloc.dart'; import 'package:ltemanager2/presentation/routes/router.gr.dart'; +import 'package:ltemanager2/domain/auth/manufacturer.dart'; +import 'package:ltemanager2/utilities/SharedPreferencesFunctions.dart'; -class SignInForm extends StatelessWidget { +class SignInForm extends StatefulWidget { const SignInForm({Key? key}) : super(key: key); + @override + State createState() => _SignInFormState(); +} + +class _SignInFormState extends State { + Manufacturer _manufacturer = Manufacturer.huawei; + @override Widget build(BuildContext context) { return BlocConsumer( @@ -256,16 +265,37 @@ class SignInForm extends StatelessWidget { ), (_) => null, ), - onEditingComplete: () => - TextInput.finishAutofillContext(), - ), - ), - ), - ], + onEditingComplete: () => + TextInput.finishAutofillContext(), ), ), - const Spacer(), - const SizedBox(height: 8), + ), + const SizedBox(height: 8), + DropdownButton( + value: _manufacturer, + onChanged: (val) { + if (val != null) { + setState(() { + _manufacturer = val; + }); + } + }, + items: const [ + DropdownMenuItem( + value: Manufacturer.huawei, + child: Text('Huawei'), + ), + DropdownMenuItem( + value: Manufacturer.zte, + child: Text('ZTE'), + ), + ], + ), + ], + ), + ), + const Spacer(), + const SizedBox(height: 8), OutlinedButton( style: ButtonStyle( minimumSize: MaterialStateProperty.all(const Size(250, 50)), @@ -279,6 +309,7 @@ class SignInForm extends StatelessWidget { ), ), onPressed: () { + saveSharedPref('_manufacturer', _manufacturer.name); context.read().add( const SignInFormEvent .signInWithEmailAndPasswordPressed(), From f65449239a1dfa3143fc8f97f1768c72039c8fd8 Mon Sep 17 00:00:00 2001 From: Alex Tarana Date: Wed, 11 Jun 2025 14:46:44 +0200 Subject: [PATCH 2/2] Refactor Huawei and ZTE router authentication facades for consistency and update dependency injection configuration --- .../auth/router_huawei_auth_facade.dart | 1 + .../auth/router_zte_auth_facade.dart | 15 +++-- lib/injection.config.dart | 57 ++++++++++--------- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/lib/infrastructure/auth/router_huawei_auth_facade.dart b/lib/infrastructure/auth/router_huawei_auth_facade.dart index 76690ef..41dcca7 100644 --- a/lib/infrastructure/auth/router_huawei_auth_facade.dart +++ b/lib/infrastructure/auth/router_huawei_auth_facade.dart @@ -15,6 +15,7 @@ import 'package:ltemanager2/infrastructure/core/router_huawei_api.dart'; import 'package:chopper/chopper.dart'; import 'package:shared_preferences/shared_preferences.dart'; +@Named("HuaweiRouterAuthFacade") @LazySingleton(as: IAuthFacade) class RouterHuaweiAuthFacade implements IAuthFacade { final HuaweiRouterApi _api; diff --git a/lib/infrastructure/auth/router_zte_auth_facade.dart b/lib/infrastructure/auth/router_zte_auth_facade.dart index 098c831..0c6b362 100644 --- a/lib/infrastructure/auth/router_zte_auth_facade.dart +++ b/lib/infrastructure/auth/router_zte_auth_facade.dart @@ -9,7 +9,8 @@ import 'package:ltemanager2/domain/auth/i_auth_facade.dart'; import 'package:ltemanager2/domain/auth/value_objects.dart'; import 'package:shared_preferences/shared_preferences.dart'; -@LazySingleton(as: IAuthFacade, instanceName: 'RouterZteAuthFacade') +@Named("ZTERouterAuthFacade") +@LazySingleton(as: IAuthFacade) class RouterZteAuthFacade implements IAuthFacade { final SharedPreferences _sharedPreferences; @@ -37,13 +38,17 @@ class RouterZteAuthFacade implements IAuthFacade { if (ldResp.statusCode != 200) { return left(const AuthFailure.serverError()); } - final ld = (jsonDecode(ldResp.body) as Map)['LD'] as String; + final ld = + (jsonDecode(ldResp.body) as Map)['LD'] as String; - final first = sha256.convert(utf8.encode(passwordStr)).toString().toUpperCase(); - final second = sha256.convert(utf8.encode('$first$ld')).toString().toUpperCase(); + final first = + sha256.convert(utf8.encode(passwordStr)).toString().toUpperCase(); + final second = + sha256.convert(utf8.encode('$first$ld')).toString().toUpperCase(); final loginResp = await http.get( - Uri.parse('$urlStr/goform/goform_set_cmd_process?isTest=false&goformId=LOGIN&password=$second'), + Uri.parse( + '$urlStr/goform/goform_set_cmd_process?isTest=false&goformId=LOGIN&password=$second'), headers: {'referer': urlStr}, ); diff --git a/lib/injection.config.dart b/lib/injection.config.dart index c18a469..091636c 100644 --- a/lib/injection.config.dart +++ b/lib/injection.config.dart @@ -9,14 +9,14 @@ import 'package:get_it/get_it.dart' as _i1; import 'package:injectable/injectable.dart' as _i2; import 'package:shared_preferences/shared_preferences.dart' as _i4; -import 'application/auth/auth_bloc.dart' as _i10; -import 'application/auth/sign_in_form/sign_in_form_bloc.dart' as _i9; -import 'domain/auth/i_auth_facade.dart' as _i5; -import 'infrastructure/auth/router_zte_auth_facade.dart' as _i12; -import 'infrastructure/auth/router_auth_facade.dart' as _i7; -import 'infrastructure/auth/router_huawei_auth_facade.dart' as _i6; -import 'infrastructure/core/module_injectable.dart' as _i11; -import 'infrastructure/core/router_api.dart' as _i8; +import 'application/auth/auth_bloc.dart' as _i7; +import 'application/auth/sign_in_form/sign_in_form_bloc.dart' as _i5; +import 'domain/auth/i_auth_facade.dart' as _i6; +import 'infrastructure/auth/router_auth_facade.dart' as _i9; +import 'infrastructure/auth/router_huawei_auth_facade.dart' as _i8; +import 'infrastructure/auth/router_zte_auth_facade.dart' as _i11; +import 'infrastructure/core/module_injectable.dart' as _i12; +import 'infrastructure/core/router_api.dart' as _i10; import 'infrastructure/core/router_huawei_api.dart' as _i3; // ignore_for_file: unnecessary_lambdas @@ -38,32 +38,35 @@ Future<_i1.GetIt> $initGetIt( () => flutterModule.prefs, preResolve: true, ); - gh.lazySingleton<_i5.IAuthFacade>(() => _i6.RouterHuaweiAuthFacade( - get<_i3.HuaweiRouterApi>(), + gh.factory<_i5.SignInFormBloc>(() => _i5.SignInFormBloc( + get<_i6.IAuthFacade>(), + get<_i6.IAuthFacade>(instanceName: 'RouterZteAuthFacade'), get<_i4.SharedPreferences>(), )); - gh.lazySingleton<_i5.IAuthFacade>( - () => _i12.RouterZteAuthFacade(get<_i4.SharedPreferences>()), - instanceName: 'RouterZteAuthFacade', + gh.factory<_i7.AuthBloc>(() => _i7.AuthBloc( + get<_i6.IAuthFacade>(), + get<_i6.IAuthFacade>(instanceName: 'RouterZteAuthFacade'), + get<_i4.SharedPreferences>(), + )); + gh.lazySingleton<_i6.IAuthFacade>( + () => _i8.RouterHuaweiAuthFacade( + get<_i3.HuaweiRouterApi>(), + get<_i4.SharedPreferences>(), + ), + instanceName: 'HuaweiRouterAuthFacade', ); - gh.lazySingleton<_i5.IAuthFacade>( - () => _i7.RouterAuthFacade( - get<_i8.RouterApi>(), + gh.lazySingleton<_i6.IAuthFacade>( + () => _i9.RouterAuthFacade( + get<_i10.RouterApi>(), get<_i4.SharedPreferences>(), ), instanceName: 'RouterGenericAuthFacade', ); - gh.factory<_i9.SignInFormBloc>(() => _i9.SignInFormBloc( - get<_i5.IAuthFacade>(), - get<_i5.IAuthFacade>(instanceName: 'RouterZteAuthFacade'), - get<_i4.SharedPreferences>(), - )); - gh.factory<_i10.AuthBloc>(() => _i10.AuthBloc( - get<_i5.IAuthFacade>(), - get<_i5.IAuthFacade>(instanceName: 'RouterZteAuthFacade'), - get<_i4.SharedPreferences>(), - )); + gh.lazySingleton<_i6.IAuthFacade>( + () => _i11.RouterZteAuthFacade(get<_i4.SharedPreferences>()), + instanceName: 'ZTERouterAuthFacade', + ); return get; } -class _$FlutterModule extends _i11.FlutterModule {} +class _$FlutterModule extends _i12.FlutterModule {}