feat: 切换邮箱认证并重构前后端启动与门禁
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../../../../core/logging/logger.dart';
|
||||
import '../../data/repositories/auth_repository.dart';
|
||||
import 'auth_state.dart';
|
||||
|
||||
class AuthBloc extends ChangeNotifier {
|
||||
AuthBloc({required AuthRepository repository}) : _repository = repository;
|
||||
|
||||
final AuthRepository _repository;
|
||||
final Logger _logger = getLogger('features.auth.bloc');
|
||||
AuthState _state = AuthState.initial;
|
||||
bool _handlingUnauthorized = false;
|
||||
|
||||
AuthState get state => _state;
|
||||
|
||||
Future<void> start() async {
|
||||
_state = _state.copyWith(status: AuthStatus.loading, errorMessage: null);
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final user = await _repository.recoverSession();
|
||||
if (user == null) {
|
||||
_state = const AuthState(status: AuthStatus.unauthenticated);
|
||||
} else {
|
||||
_state = AuthState(status: AuthStatus.authenticated, user: user);
|
||||
}
|
||||
notifyListeners();
|
||||
} catch (error, stackTrace) {
|
||||
_logger.error(
|
||||
message: 'Session recovery failed',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
await _repository.clearLocalSession();
|
||||
_state = AuthState(
|
||||
status: AuthStatus.unauthenticated,
|
||||
errorMessage: _toSafeMessage(error),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> sendOtp(String email) async {
|
||||
await _repository.sendOtp(email);
|
||||
}
|
||||
|
||||
Future<void> loginWithOtp({
|
||||
required String email,
|
||||
required String otp,
|
||||
}) async {
|
||||
final user = await _repository.loginWithEmailOtp(email: email, otp: otp);
|
||||
_logger.info(message: 'User logged in', extra: {'user_id': user.id});
|
||||
_state = AuthState(status: AuthStatus.authenticated, user: user);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
Object? caughtError;
|
||||
StackTrace? caughtStackTrace;
|
||||
try {
|
||||
await _repository.logout();
|
||||
} catch (error, stackTrace) {
|
||||
caughtError = error;
|
||||
caughtStackTrace = stackTrace;
|
||||
_logger.error(
|
||||
message: 'User logout failed',
|
||||
error: error,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
}
|
||||
_logger.info(message: 'User logged out');
|
||||
_state = const AuthState(status: AuthStatus.unauthenticated);
|
||||
notifyListeners();
|
||||
if (caughtError != null) {
|
||||
Error.throwWithStackTrace(caughtError, caughtStackTrace!);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> handleUnauthorized401() async {
|
||||
if (_handlingUnauthorized) {
|
||||
return;
|
||||
}
|
||||
_handlingUnauthorized = true;
|
||||
try {
|
||||
await _repository.clearLocalSession();
|
||||
_logger.warning(message: 'Session invalidated by 401 callback');
|
||||
_state = const AuthState(status: AuthStatus.unauthenticated);
|
||||
notifyListeners();
|
||||
} finally {
|
||||
_handlingUnauthorized = false;
|
||||
}
|
||||
}
|
||||
|
||||
String _toSafeMessage(Object error) {
|
||||
return 'Request failed, please try again';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import '../../data/models/auth_user.dart';
|
||||
|
||||
enum AuthStatus { initial, loading, authenticated, unauthenticated }
|
||||
|
||||
class AuthState {
|
||||
const AuthState({required this.status, this.user, this.errorMessage});
|
||||
|
||||
final AuthStatus status;
|
||||
final AuthUser? user;
|
||||
final String? errorMessage;
|
||||
|
||||
AuthState copyWith({
|
||||
AuthStatus? status,
|
||||
AuthUser? user,
|
||||
String? errorMessage,
|
||||
}) {
|
||||
return AuthState(
|
||||
status: status ?? this.status,
|
||||
user: user ?? this.user,
|
||||
errorMessage: errorMessage,
|
||||
);
|
||||
}
|
||||
|
||||
static const AuthState initial = AuthState(status: AuthStatus.initial);
|
||||
}
|
||||
Reference in New Issue
Block a user