mirror of
https://github.com/immich-app/immich.git
synced 2025-01-01 08:31:59 +00:00
196 lines
6 KiB
Dart
196 lines
6 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
import 'package:immich_mobile/entities/store.entity.dart';
|
|
import 'package:immich_mobile/interfaces/auth.interface.dart';
|
|
import 'package:immich_mobile/interfaces/auth_api.interface.dart';
|
|
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
|
import 'package:immich_mobile/models/auth/login_response.model.dart';
|
|
import 'package:immich_mobile/providers/api.provider.dart';
|
|
import 'package:immich_mobile/repositories/auth.repository.dart';
|
|
import 'package:immich_mobile/repositories/auth_api.repository.dart';
|
|
import 'package:immich_mobile/services/api.service.dart';
|
|
import 'package:immich_mobile/services/network.service.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:openapi/api.dart';
|
|
|
|
final authServiceProvider = Provider(
|
|
(ref) => AuthService(
|
|
ref.watch(authApiRepositoryProvider),
|
|
ref.watch(authRepositoryProvider),
|
|
ref.watch(apiServiceProvider),
|
|
ref.watch(networkServiceProvider),
|
|
),
|
|
);
|
|
|
|
class AuthService {
|
|
final IAuthApiRepository _authApiRepository;
|
|
final IAuthRepository _authRepository;
|
|
final ApiService _apiService;
|
|
final NetworkService _networkService;
|
|
|
|
final _log = Logger("AuthService");
|
|
|
|
AuthService(
|
|
this._authApiRepository,
|
|
this._authRepository,
|
|
this._apiService,
|
|
this._networkService,
|
|
);
|
|
|
|
/// Validates the provided server URL by resolving and setting the endpoint.
|
|
/// Also sets the device info header and stores the valid URL.
|
|
///
|
|
/// [url] - The server URL to be validated.
|
|
///
|
|
/// Returns the validated and resolved server URL as a [String].
|
|
///
|
|
/// Throws an exception if the URL cannot be resolved or set.
|
|
Future<String> validateServerUrl(String url) async {
|
|
final validUrl = await _apiService.resolveAndSetEndpoint(url);
|
|
await _apiService.setDeviceInfoHeader();
|
|
Store.put(StoreKey.serverUrl, validUrl);
|
|
|
|
return validUrl;
|
|
}
|
|
|
|
Future<bool> validateAuxilaryServerUrl(String url) async {
|
|
final httpclient = HttpClient();
|
|
bool isValid = false;
|
|
|
|
try {
|
|
final uri = Uri.parse('$url/users/me');
|
|
final request = await httpclient.getUrl(uri);
|
|
|
|
// add auth token + any configured custom headers
|
|
final customHeaders = ApiService.getRequestHeaders();
|
|
customHeaders.forEach((key, value) {
|
|
request.headers.add(key, value);
|
|
});
|
|
|
|
final response = await request.close();
|
|
if (response.statusCode == 200) {
|
|
isValid = true;
|
|
}
|
|
} catch (error) {
|
|
_log.severe("Error validating auxilary endpoint", error);
|
|
} finally {
|
|
httpclient.close();
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
Future<LoginResponse> login(String email, String password) {
|
|
return _authApiRepository.login(email, password);
|
|
}
|
|
|
|
/// Performs user logout operation by making a server request and clearing local data.
|
|
///
|
|
/// This method attempts to log out the user through the authentication API repository.
|
|
/// If the server request fails, the error is logged but local data is still cleared.
|
|
/// The local data cleanup is guaranteed to execute regardless of the server request outcome.
|
|
///
|
|
/// Throws any unhandled exceptions from the API request or local data clearing operations.
|
|
Future<void> logout() async {
|
|
try {
|
|
await _authApiRepository.logout();
|
|
} catch (error, stackTrace) {
|
|
_log.severe("Error logging out", error, stackTrace);
|
|
} finally {
|
|
await clearLocalData().catchError((error, stackTrace) {
|
|
_log.severe("Error clearing local data", error, stackTrace);
|
|
});
|
|
}
|
|
}
|
|
|
|
/// Clears all local authentication-related data.
|
|
///
|
|
/// This method performs a concurrent deletion of:
|
|
/// - Authentication repository data
|
|
/// - Current user information
|
|
/// - Access token
|
|
/// - Asset ETag
|
|
///
|
|
/// All deletions are executed in parallel using [Future.wait].
|
|
Future<void> clearLocalData() {
|
|
return Future.wait([
|
|
_authRepository.clearLocalData(),
|
|
Store.delete(StoreKey.currentUser),
|
|
Store.delete(StoreKey.accessToken),
|
|
Store.delete(StoreKey.assetETag),
|
|
Store.delete(StoreKey.autoEndpointSwitching),
|
|
Store.delete(StoreKey.preferredWifiName),
|
|
Store.delete(StoreKey.localEndpoint),
|
|
Store.delete(StoreKey.externalEndpointList),
|
|
]);
|
|
}
|
|
|
|
Future<void> changePassword(String newPassword) {
|
|
try {
|
|
return _authApiRepository.changePassword(newPassword);
|
|
} catch (error, stackTrace) {
|
|
_log.severe("Error changing password", error, stackTrace);
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
Future<String?> setOpenApiServiceEndpoint() async {
|
|
final enable = _authRepository.getEndpointSwitchingFeature();
|
|
if (!enable) {
|
|
return null;
|
|
}
|
|
|
|
final wifiName = await _networkService.getWifiName();
|
|
final savedWifiName = _authRepository.getPreferredWifiName();
|
|
String? endpoint;
|
|
|
|
if (wifiName == savedWifiName) {
|
|
endpoint = await _setLocalConnection();
|
|
}
|
|
|
|
endpoint ??= await _setRemoteConnection();
|
|
|
|
return endpoint;
|
|
}
|
|
|
|
Future<String?> _setLocalConnection() async {
|
|
try {
|
|
final localEndpoint = _authRepository.getLocalEndpoint();
|
|
if (localEndpoint != null) {
|
|
await _apiService.resolveAndSetEndpoint(localEndpoint);
|
|
return localEndpoint;
|
|
}
|
|
} catch (error, stackTrace) {
|
|
_log.severe("Cannot set local endpoint", error, stackTrace);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
Future<String?> _setRemoteConnection() async {
|
|
List<AuxilaryEndpoint> endpointList;
|
|
|
|
try {
|
|
endpointList = _authRepository.getExternalEndpointList();
|
|
} catch (error, stackTrace) {
|
|
_log.severe("Cannot get external endpoint", error, stackTrace);
|
|
return null;
|
|
}
|
|
|
|
for (final endpoint in endpointList) {
|
|
try {
|
|
return await _apiService.resolveAndSetEndpoint(endpoint.url);
|
|
} on ApiException catch (error) {
|
|
_log.severe("Cannot resolve endpoint", error);
|
|
continue;
|
|
} catch (_) {
|
|
_log.severe("Auxilary server is not valid");
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|