uaestats_fe/lib/infrastructure/data/p_auth_repo.dart
2026-04-09 12:55:10 +05:30

166 lines
4.7 KiB
Dart
Executable File

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:injectable/injectable.dart';
import 'package:jwt_decoder/jwt_decoder.dart';
import 'package:pocketbase/pocketbase.dart';
import 'package:uae_stat/domain/entities/preferences_entity.dart';
import 'package:uae_stat/domain/entities/session_entity.dart';
import 'package:uae_stat/domain/repos/t_auth_repo.dart';
import 'package:uae_stat/domain/use_cases/auth_use_case.dart';
import 'package:uae_stat/infrastructure/services/pocketbase_service.dart';
@dev
@prod
@Injectable(as: TAuthRepo)
class PAuthRepo implements TAuthRepo {
@override
Future<SessionEntity?> fetchLocallyStored() async {
final se = await _LocalAuthRepo.fetchLocallyStored();
if (se == null) return null;
PocketBaseService.authStore.save(
se.token,
RecordModel.fromJson(se.modelJson),
);
return se;
}
SessionEntity _sessionEntityFromPocketbaseRecord(RecordAuth ra) {
final decodedToken = JwtDecoder.decode(ra.token);
final expInSeconds = decodedToken['exp'];
final expInMilliseconds = expInSeconds * 1000;
final expiresOn = DateTime.fromMillisecondsSinceEpoch(expInMilliseconds);
final se = SessionEntity(
id: ra.record!.id,
expiresOn: expiresOn,
token: ra.token,
modelJson: ra.record!.toJson(),
);
return se;
}
@override
Future<SessionEntity> fetchRefreshedSession() async {
final user = await PocketBaseService.users.authRefresh().timeout(Duration(seconds: 5));
final se = _sessionEntityFromPocketbaseRecord(user);
await _LocalAuthRepo.storeLocally(se);
return se;
}
@override
Future<SessionEntity> login(String email, String pw) async {
late final RecordAuth user;
try {
user = await PocketBaseService.users.authWithPassword(
email,
pw,
);
} on ClientException catch (err) {
final message = err.response['message'];
final isWrongEmailPw = message == 'Failed to authenticate.';
if (isWrongEmailPw) throw LoginError.invalidEmailPw;
rethrow;
}
final isVerified = user.record!.getBoolValue('verified');
if (!isVerified) throw LoginError.emailAddressNotVerified;
final se = _sessionEntityFromPocketbaseRecord(user);
await _LocalAuthRepo.storeLocally(se);
return se;
}
@override
Future<SessionEntity> googleAndAppleAuthencation(token) async {
late final RecordAuth user;
try {
user = token;
} on ClientException catch (err) {
final message = err.response['message'];
final isWrongEmailPw = message == 'Failed to authenticate.';
if (isWrongEmailPw) throw LoginError.invalidEmailPw;
rethrow;
}
final isVerified = user.record!.getBoolValue('verified');
if (!isVerified) throw LoginError.emailAddressNotVerified;
final se = _sessionEntityFromPocketbaseRecord(user);
await _LocalAuthRepo.storeLocally(se);
return se;
}
@override
Future<void> logout() {
PocketBaseService.authStore.clear();
return _LocalAuthRepo.clear();
}
/// throws [RegisterError]
@override
Future<void> register({
required String name,
required String email,
required String pw,
required PreferencesEntity preferences,
}) async {
try {
await PocketBaseService.users.create(
body: {
'email': email,
'password': pw,
'passwordConfirm': pw,
'name': name,
...preferences.toJson(),
},
);
} catch (err) {
if (err is ClientException) {
if (err.response['data']['email']['message'] ==
'The email is invalid or already in use.') {
throw RegisterError.emailAlreadyInUse;
}
}
}
PocketBaseService.users.requestVerification(email);
}
@override
Future<void> requestEmailVerification(
String email,
) =>
PocketBaseService.users.requestVerification(email);
@override
Future<void> requestPwReset(
String email,
) =>
PocketBaseService.users.requestPasswordReset(
email,
);
@override
bool didSessionExpire() => PocketBaseService.authStore.isValid;
}
abstract class _LocalAuthRepo {
static const _key = 'session';
static const _storage = FlutterSecureStorage();
static Future<SessionEntity?> fetchLocallyStored() async {
final e = await _storage.read(key: _key);
if (e == null) return null;
final se = SessionEntity.fromJson(e);
if (se.freshness == SessionFreshness.expired) {
await clear();
return null;
}
return se;
}
static Future<void> storeLocally(
SessionEntity se,
) =>
_storage.write(
key: _key,
value: se.toJson(),
);
static Future<void> clear() => _storage.delete(key: _key);
}