mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			244 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'dart:io';
 | 
						|
 | 
						|
import 'package:device_info_plus/device_info_plus.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:flutter_udid/flutter_udid.dart';
 | 
						|
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
						|
import 'package:immich_mobile/providers/album/album.provider.dart';
 | 
						|
import 'package:immich_mobile/providers/album/shared_album.provider.dart';
 | 
						|
import 'package:immich_mobile/entities/store.entity.dart';
 | 
						|
import 'package:immich_mobile/models/authentication/authentication_state.model.dart';
 | 
						|
import 'package:immich_mobile/entities/user.entity.dart';
 | 
						|
import 'package:immich_mobile/providers/api.provider.dart';
 | 
						|
import 'package:immich_mobile/providers/db.provider.dart';
 | 
						|
import 'package:immich_mobile/services/api.service.dart';
 | 
						|
import 'package:immich_mobile/utils/db.dart';
 | 
						|
import 'package:immich_mobile/utils/hash.dart';
 | 
						|
import 'package:isar/isar.dart';
 | 
						|
import 'package:logging/logging.dart';
 | 
						|
import 'package:openapi/api.dart';
 | 
						|
 | 
						|
class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
 | 
						|
  AuthenticationNotifier(
 | 
						|
    this._apiService,
 | 
						|
    this._db,
 | 
						|
    this._ref,
 | 
						|
  ) : super(
 | 
						|
          AuthenticationState(
 | 
						|
            deviceId: "",
 | 
						|
            userId: "",
 | 
						|
            userEmail: "",
 | 
						|
            name: '',
 | 
						|
            profileImagePath: '',
 | 
						|
            isAdmin: false,
 | 
						|
            shouldChangePassword: false,
 | 
						|
            isAuthenticated: false,
 | 
						|
          ),
 | 
						|
        );
 | 
						|
 | 
						|
  final ApiService _apiService;
 | 
						|
  final Isar _db;
 | 
						|
  final StateNotifierProviderRef<AuthenticationNotifier, AuthenticationState>
 | 
						|
      _ref;
 | 
						|
  final _log = Logger("AuthenticationNotifier");
 | 
						|
 | 
						|
  Future<bool> login(
 | 
						|
    String email,
 | 
						|
    String password,
 | 
						|
    String serverUrl,
 | 
						|
  ) async {
 | 
						|
    try {
 | 
						|
      // Resolve API server endpoint from user provided serverUrl
 | 
						|
      await _apiService.resolveAndSetEndpoint(serverUrl);
 | 
						|
      await _apiService.serverInfoApi.pingServer();
 | 
						|
    } catch (e) {
 | 
						|
      debugPrint('Invalid Server Endpoint Url $e');
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // Make sign-in request
 | 
						|
    DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
 | 
						|
 | 
						|
    if (Platform.isIOS) {
 | 
						|
      var iosInfo = await deviceInfoPlugin.iosInfo;
 | 
						|
      _apiService.authenticationApi.apiClient
 | 
						|
          .addDefaultHeader('deviceModel', iosInfo.utsname.machine);
 | 
						|
      _apiService.authenticationApi.apiClient
 | 
						|
          .addDefaultHeader('deviceType', 'iOS');
 | 
						|
    } else {
 | 
						|
      var androidInfo = await deviceInfoPlugin.androidInfo;
 | 
						|
      _apiService.authenticationApi.apiClient
 | 
						|
          .addDefaultHeader('deviceModel', androidInfo.model);
 | 
						|
      _apiService.authenticationApi.apiClient
 | 
						|
          .addDefaultHeader('deviceType', 'Android');
 | 
						|
    }
 | 
						|
 | 
						|
    try {
 | 
						|
      var loginResponse = await _apiService.authenticationApi.login(
 | 
						|
        LoginCredentialDto(
 | 
						|
          email: email,
 | 
						|
          password: password,
 | 
						|
        ),
 | 
						|
      );
 | 
						|
 | 
						|
      if (loginResponse == null) {
 | 
						|
        debugPrint('Login Response is null');
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
 | 
						|
      return setSuccessLoginInfo(
 | 
						|
        accessToken: loginResponse.accessToken,
 | 
						|
        serverUrl: serverUrl,
 | 
						|
      );
 | 
						|
    } catch (e) {
 | 
						|
      debugPrint("Error logging in $e");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Future<void> logout() async {
 | 
						|
    var log = Logger('AuthenticationNotifier');
 | 
						|
    try {
 | 
						|
      String? userEmail = Store.tryGet(StoreKey.currentUser)?.email;
 | 
						|
 | 
						|
      _apiService.authenticationApi
 | 
						|
          .logout()
 | 
						|
          .then((_) => log.info("Logout was successful for $userEmail"))
 | 
						|
          .onError(
 | 
						|
            (error, stackTrace) =>
 | 
						|
                log.severe("Logout failed for $userEmail", error, stackTrace),
 | 
						|
          );
 | 
						|
 | 
						|
      await Future.wait([
 | 
						|
        clearAssetsAndAlbums(_db),
 | 
						|
        Store.delete(StoreKey.currentUser),
 | 
						|
        Store.delete(StoreKey.accessToken),
 | 
						|
      ]);
 | 
						|
      _ref.invalidate(albumProvider);
 | 
						|
      _ref.invalidate(sharedAlbumProvider);
 | 
						|
 | 
						|
      state = state.copyWith(
 | 
						|
        deviceId: "",
 | 
						|
        userId: "",
 | 
						|
        userEmail: "",
 | 
						|
        name: '',
 | 
						|
        profileImagePath: '',
 | 
						|
        isAdmin: false,
 | 
						|
        shouldChangePassword: false,
 | 
						|
        isAuthenticated: false,
 | 
						|
      );
 | 
						|
    } catch (e, stack) {
 | 
						|
      log.severe('Logout failed', e, stack);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  updateUserProfileImagePath(String path) {
 | 
						|
    state = state.copyWith(profileImagePath: path);
 | 
						|
  }
 | 
						|
 | 
						|
  Future<bool> changePassword(String newPassword) async {
 | 
						|
    try {
 | 
						|
      await _apiService.userApi.updateUser(
 | 
						|
        UpdateUserDto(
 | 
						|
          id: state.userId,
 | 
						|
          password: newPassword,
 | 
						|
          shouldChangePassword: false,
 | 
						|
        ),
 | 
						|
      );
 | 
						|
 | 
						|
      state = state.copyWith(shouldChangePassword: false);
 | 
						|
 | 
						|
      return true;
 | 
						|
    } catch (e) {
 | 
						|
      debugPrint("Error changing password $e");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Future<bool> setSuccessLoginInfo({
 | 
						|
    required String accessToken,
 | 
						|
    required String serverUrl,
 | 
						|
    bool offlineLogin = false,
 | 
						|
  }) async {
 | 
						|
    _apiService.setAccessToken(accessToken);
 | 
						|
 | 
						|
    // Get the deviceid from the store if it exists, otherwise generate a new one
 | 
						|
    String deviceId =
 | 
						|
        Store.tryGet(StoreKey.deviceId) ?? await FlutterUdid.consistentUdid;
 | 
						|
 | 
						|
    bool shouldChangePassword = false;
 | 
						|
    User? user;
 | 
						|
 | 
						|
    bool retResult = false;
 | 
						|
    User? offlineUser = Store.tryGet(StoreKey.currentUser);
 | 
						|
 | 
						|
    // If the user is offline and there is a user saved on the device,
 | 
						|
    // if not try an online login
 | 
						|
    if (offlineLogin && offlineUser != null) {
 | 
						|
      user = offlineUser;
 | 
						|
      retResult = false;
 | 
						|
    } else {
 | 
						|
      UserResponseDto? userResponseDto;
 | 
						|
      try {
 | 
						|
        userResponseDto = await _apiService.userApi.getMyUserInfo();
 | 
						|
      } on ApiException catch (error, stackTrace) {
 | 
						|
        _log.severe(
 | 
						|
          "Error getting user information from the server [API EXCEPTION]",
 | 
						|
          error,
 | 
						|
          stackTrace,
 | 
						|
        );
 | 
						|
        if (error.innerException is SocketException) {
 | 
						|
          state = state.copyWith(isAuthenticated: true);
 | 
						|
        }
 | 
						|
      } catch (error, stackTrace) {
 | 
						|
        _log.severe(
 | 
						|
          "Error getting user information from the server [CATCH ALL]",
 | 
						|
          error,
 | 
						|
          stackTrace,
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      if (userResponseDto != null) {
 | 
						|
        Store.put(StoreKey.deviceId, deviceId);
 | 
						|
        Store.put(StoreKey.deviceIdHash, fastHash(deviceId));
 | 
						|
        Store.put(
 | 
						|
          StoreKey.currentUser,
 | 
						|
          User.fromUserDto(userResponseDto),
 | 
						|
        );
 | 
						|
        Store.put(StoreKey.serverUrl, serverUrl);
 | 
						|
        Store.put(StoreKey.accessToken, accessToken);
 | 
						|
 | 
						|
        shouldChangePassword = userResponseDto.shouldChangePassword;
 | 
						|
        user = User.fromUserDto(userResponseDto);
 | 
						|
 | 
						|
        retResult = true;
 | 
						|
      } else {
 | 
						|
        _log.severe("Unable to get user information from the server.");
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    state = state.copyWith(
 | 
						|
      isAuthenticated: true,
 | 
						|
      userId: user.id,
 | 
						|
      userEmail: user.email,
 | 
						|
      name: user.name,
 | 
						|
      profileImagePath: user.profileImagePath,
 | 
						|
      isAdmin: user.isAdmin,
 | 
						|
      shouldChangePassword: shouldChangePassword,
 | 
						|
      deviceId: deviceId,
 | 
						|
    );
 | 
						|
 | 
						|
    return retResult;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
final authenticationProvider =
 | 
						|
    StateNotifierProvider<AuthenticationNotifier, AuthenticationState>((ref) {
 | 
						|
  return AuthenticationNotifier(
 | 
						|
    ref.watch(apiServiceProvider),
 | 
						|
    ref.watch(dbProvider),
 | 
						|
    ref,
 | 
						|
  );
 | 
						|
});
 |