Настройки, авторизация
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
abstract class AuthLocalDataSource {
|
||||
Future<String?> getToken();
|
||||
Future<void> saveToken(String token);
|
||||
Future<String?> getRefreshToken();
|
||||
Future<void> saveRefreshToken(String token);
|
||||
Future<void> removeToken();
|
||||
Future<String?> getUserId();
|
||||
Future<void> saveUserId(String userId);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'auth_local_datasource.dart';
|
||||
|
||||
class AuthLocalDataSourceImpl implements AuthLocalDataSource {
|
||||
final SharedPreferences sharedPreferences;
|
||||
|
||||
static const String _accessTokenKey = 'access_token';
|
||||
static const String _refreshTokenKey = 'refresh_token';
|
||||
static const String _userIdKey = 'user_id';
|
||||
|
||||
AuthLocalDataSourceImpl(this.sharedPreferences);
|
||||
|
||||
@override
|
||||
Future<String?> getToken() async {
|
||||
return sharedPreferences.getString(_accessTokenKey);
|
||||
}
|
||||
|
||||
Future<String?> getRefreshToken() async {
|
||||
return sharedPreferences.getString(_refreshTokenKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveToken(String token) async {
|
||||
await sharedPreferences.setString(_accessTokenKey, token);
|
||||
}
|
||||
|
||||
Future<void> saveRefreshToken(String token) async {
|
||||
await sharedPreferences.setString(_refreshTokenKey, token);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeToken() async {
|
||||
await sharedPreferences.remove(_accessTokenKey);
|
||||
await sharedPreferences.remove(_refreshTokenKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String?> getUserId() async {
|
||||
return sharedPreferences.getString(_userIdKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> saveUserId(String userId) async {
|
||||
await sharedPreferences.setString(_userIdKey, userId);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> removeUserId() async {
|
||||
await sharedPreferences.remove(_userIdKey);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import '../../../../core/errors/result.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../models/auth_response.dart';
|
||||
|
||||
abstract class AuthRemoteDataSource {
|
||||
Future<Result<User>> login(String email, String password);
|
||||
Future<Result<User>> register(String email, String password, String name);
|
||||
Future<Result<AuthResponse>> login(String username, String password);
|
||||
Future<Result<AuthResponse>> register(String username, String password, String name);
|
||||
Future<Result<void>> logout();
|
||||
Future<Result<User>> getCurrentUser();
|
||||
Future<Result<void>> refreshToken();
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import '../../../../core/errors/errors.dart';
|
||||
import '../../../../core/network/api_client.dart';
|
||||
import '../../../../core/errors/result.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../models/auth_response.dart';
|
||||
import 'auth_remote_datasource.dart';
|
||||
|
||||
class AuthRemoteDataSourceImpl implements AuthRemoteDataSource {
|
||||
final ApiClient apiClient;
|
||||
|
||||
AuthRemoteDataSourceImpl(this.apiClient);
|
||||
|
||||
@override
|
||||
Future<Result<AuthResponse>> login(String username, String password) async {
|
||||
final result = await apiClient.post<Map<String, dynamic>>(
|
||||
'/api/auth/login',
|
||||
data: {
|
||||
'username': username,
|
||||
'password': password,
|
||||
},
|
||||
);
|
||||
|
||||
return result.fold(
|
||||
(error) => Result.failure(error),
|
||||
(data) {
|
||||
try {
|
||||
return Result.success(AuthResponse.fromJson(data));
|
||||
} catch (e) {
|
||||
return Result.failure(AppError.parsing(message: 'Invalid server response format: $e'));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<AuthResponse>> register(String username, String password, String name) async {
|
||||
final result = await apiClient.post<Map<String, dynamic>>(
|
||||
'/api/auth/register',
|
||||
data: {
|
||||
'username': username,
|
||||
'password': password,
|
||||
'displayName': name,
|
||||
},
|
||||
);
|
||||
|
||||
return result.fold(
|
||||
(error) => Result.failure(error),
|
||||
(data) {
|
||||
try {
|
||||
return Result.success(AuthResponse.fromJson(data));
|
||||
} catch (e) {
|
||||
return Result.failure(AppError.parsing(message: 'Invalid server response format: $e'));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<void>> logout() async {
|
||||
return await apiClient.post('/api/auth/logout');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<User>> getCurrentUser() async {
|
||||
final result = await apiClient.get<Map<String, dynamic>>('/api/auth/me');
|
||||
return result.fold(
|
||||
(error) => Result.failure(error),
|
||||
(data) {
|
||||
try {
|
||||
return Result.success(User(
|
||||
id: data['userId'] as String,
|
||||
email: data['username'] as String,
|
||||
name: data['displayName'] as String,
|
||||
avatarUrl: data['avatar'] as String?,
|
||||
));
|
||||
} catch (e) {
|
||||
return Result.failure(AppError.parsing(message: 'Invalid user data format: $e'));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<void>> refreshToken() async {
|
||||
return await apiClient.post('/api/auth/refresh');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
|
||||
part 'auth_response.freezed.dart';
|
||||
part 'auth_response.g.dart';
|
||||
|
||||
@freezed
|
||||
class AuthResponse with _$AuthResponse {
|
||||
const factory AuthResponse({
|
||||
required String accessToken,
|
||||
required String refreshToken,
|
||||
required String userId,
|
||||
required String username,
|
||||
required String displayName,
|
||||
}) = _AuthResponse;
|
||||
|
||||
const AuthResponse._();
|
||||
|
||||
factory AuthResponse.fromJson(Map<String, dynamic> json) =>
|
||||
_$AuthResponseFromJson(json);
|
||||
|
||||
// Helper to convert to domain User
|
||||
User toDomainUser() {
|
||||
return User(
|
||||
id: userId,
|
||||
email: username, // Использование логина как email в сущности User
|
||||
name: displayName,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
// coverage:ignore-file
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||
|
||||
part of 'auth_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// FreezedGenerator
|
||||
// **************************************************************************
|
||||
|
||||
T _$identity<T>(T value) => value;
|
||||
|
||||
final _privateConstructorUsedError = UnsupportedError(
|
||||
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||
|
||||
AuthResponse _$AuthResponseFromJson(Map<String, dynamic> json) {
|
||||
return _AuthResponse.fromJson(json);
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$AuthResponse {
|
||||
String get accessToken => throw _privateConstructorUsedError;
|
||||
String get refreshToken => throw _privateConstructorUsedError;
|
||||
String get userId => throw _privateConstructorUsedError;
|
||||
String get username => throw _privateConstructorUsedError;
|
||||
String get displayName => throw _privateConstructorUsedError;
|
||||
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@JsonKey(ignore: true)
|
||||
$AuthResponseCopyWith<AuthResponse> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class $AuthResponseCopyWith<$Res> {
|
||||
factory $AuthResponseCopyWith(
|
||||
AuthResponse value, $Res Function(AuthResponse) then) =
|
||||
_$AuthResponseCopyWithImpl<$Res, AuthResponse>;
|
||||
@useResult
|
||||
$Res call(
|
||||
{String accessToken,
|
||||
String refreshToken,
|
||||
String userId,
|
||||
String username,
|
||||
String displayName});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class _$AuthResponseCopyWithImpl<$Res, $Val extends AuthResponse>
|
||||
implements $AuthResponseCopyWith<$Res> {
|
||||
_$AuthResponseCopyWithImpl(this._value, this._then);
|
||||
|
||||
// ignore: unused_field
|
||||
final $Val _value;
|
||||
// ignore: unused_field
|
||||
final $Res Function($Val) _then;
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? accessToken = null,
|
||||
Object? refreshToken = null,
|
||||
Object? userId = null,
|
||||
Object? username = null,
|
||||
Object? displayName = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
accessToken: null == accessToken
|
||||
? _value.accessToken
|
||||
: accessToken // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
refreshToken: null == refreshToken
|
||||
? _value.refreshToken
|
||||
: refreshToken // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
userId: null == userId
|
||||
? _value.userId
|
||||
: userId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
username: null == username
|
||||
? _value.username
|
||||
: username // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
displayName: null == displayName
|
||||
? _value.displayName
|
||||
: displayName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract class _$$AuthResponseImplCopyWith<$Res>
|
||||
implements $AuthResponseCopyWith<$Res> {
|
||||
factory _$$AuthResponseImplCopyWith(
|
||||
_$AuthResponseImpl value, $Res Function(_$AuthResponseImpl) then) =
|
||||
__$$AuthResponseImplCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call(
|
||||
{String accessToken,
|
||||
String refreshToken,
|
||||
String userId,
|
||||
String username,
|
||||
String displayName});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
class __$$AuthResponseImplCopyWithImpl<$Res>
|
||||
extends _$AuthResponseCopyWithImpl<$Res, _$AuthResponseImpl>
|
||||
implements _$$AuthResponseImplCopyWith<$Res> {
|
||||
__$$AuthResponseImplCopyWithImpl(
|
||||
_$AuthResponseImpl _value, $Res Function(_$AuthResponseImpl) _then)
|
||||
: super(_value, _then);
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
@override
|
||||
$Res call({
|
||||
Object? accessToken = null,
|
||||
Object? refreshToken = null,
|
||||
Object? userId = null,
|
||||
Object? username = null,
|
||||
Object? displayName = null,
|
||||
}) {
|
||||
return _then(_$AuthResponseImpl(
|
||||
accessToken: null == accessToken
|
||||
? _value.accessToken
|
||||
: accessToken // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
refreshToken: null == refreshToken
|
||||
? _value.refreshToken
|
||||
: refreshToken // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
userId: null == userId
|
||||
? _value.userId
|
||||
: userId // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
username: null == username
|
||||
? _value.username
|
||||
: username // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
displayName: null == displayName
|
||||
? _value.displayName
|
||||
: displayName // ignore: cast_nullable_to_non_nullable
|
||||
as String,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
class _$AuthResponseImpl extends _AuthResponse {
|
||||
const _$AuthResponseImpl(
|
||||
{required this.accessToken,
|
||||
required this.refreshToken,
|
||||
required this.userId,
|
||||
required this.username,
|
||||
required this.displayName})
|
||||
: super._();
|
||||
|
||||
factory _$AuthResponseImpl.fromJson(Map<String, dynamic> json) =>
|
||||
_$$AuthResponseImplFromJson(json);
|
||||
|
||||
@override
|
||||
final String accessToken;
|
||||
@override
|
||||
final String refreshToken;
|
||||
@override
|
||||
final String userId;
|
||||
@override
|
||||
final String username;
|
||||
@override
|
||||
final String displayName;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AuthResponse(accessToken: $accessToken, refreshToken: $refreshToken, userId: $userId, username: $username, displayName: $displayName)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) ||
|
||||
(other.runtimeType == runtimeType &&
|
||||
other is _$AuthResponseImpl &&
|
||||
(identical(other.accessToken, accessToken) ||
|
||||
other.accessToken == accessToken) &&
|
||||
(identical(other.refreshToken, refreshToken) ||
|
||||
other.refreshToken == refreshToken) &&
|
||||
(identical(other.userId, userId) || other.userId == userId) &&
|
||||
(identical(other.username, username) ||
|
||||
other.username == username) &&
|
||||
(identical(other.displayName, displayName) ||
|
||||
other.displayName == displayName));
|
||||
}
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
runtimeType, accessToken, refreshToken, userId, username, displayName);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
_$$AuthResponseImplCopyWith<_$AuthResponseImpl> get copyWith =>
|
||||
__$$AuthResponseImplCopyWithImpl<_$AuthResponseImpl>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$$AuthResponseImplToJson(
|
||||
this,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _AuthResponse extends AuthResponse {
|
||||
const factory _AuthResponse(
|
||||
{required final String accessToken,
|
||||
required final String refreshToken,
|
||||
required final String userId,
|
||||
required final String username,
|
||||
required final String displayName}) = _$AuthResponseImpl;
|
||||
const _AuthResponse._() : super._();
|
||||
|
||||
factory _AuthResponse.fromJson(Map<String, dynamic> json) =
|
||||
_$AuthResponseImpl.fromJson;
|
||||
|
||||
@override
|
||||
String get accessToken;
|
||||
@override
|
||||
String get refreshToken;
|
||||
@override
|
||||
String get userId;
|
||||
@override
|
||||
String get username;
|
||||
@override
|
||||
String get displayName;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$AuthResponseImplCopyWith<_$AuthResponseImpl> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'auth_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$AuthResponseImpl _$$AuthResponseImplFromJson(Map<String, dynamic> json) =>
|
||||
_$AuthResponseImpl(
|
||||
accessToken: json['accessToken'] as String,
|
||||
refreshToken: json['refreshToken'] as String,
|
||||
userId: json['userId'] as String,
|
||||
username: json['username'] as String,
|
||||
displayName: json['displayName'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$AuthResponseImplToJson(_$AuthResponseImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'accessToken': instance.accessToken,
|
||||
'refreshToken': instance.refreshToken,
|
||||
'userId': instance.userId,
|
||||
'username': instance.username,
|
||||
'displayName': instance.displayName,
|
||||
};
|
||||
@@ -1,47 +1,64 @@
|
||||
import '../../../../core/errors/errors.dart';
|
||||
import '../../../../core/errors/result.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../../domain/repositories/auth_repository.dart';
|
||||
import '../datasources/auth_local_datasource.dart';
|
||||
import '../datasources/auth_remote_datasource.dart';
|
||||
|
||||
class AuthRepositoryImpl implements AuthRepository {
|
||||
@override
|
||||
bool get isLoggedIn => false;
|
||||
final AuthRemoteDataSource remoteDataSource;
|
||||
final AuthLocalDataSource localDataSource;
|
||||
|
||||
AuthRepositoryImpl({
|
||||
required this.remoteDataSource,
|
||||
required this.localDataSource,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Result<User>> getCurrentUser() async {
|
||||
return const Result.failure(AppError.unauthorized(message: 'Not logged in'));
|
||||
Future<Result<User>> login(String username, String password) async {
|
||||
final result = await remoteDataSource.login(username, password);
|
||||
return result.fold(
|
||||
(error) => Result.failure(error),
|
||||
(response) async {
|
||||
await localDataSource.saveToken(response.accessToken);
|
||||
await localDataSource.saveRefreshToken(response.refreshToken);
|
||||
await localDataSource.saveUserId(response.userId);
|
||||
return Result.success(response.toDomainUser());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<User>> login(String email, String password) async {
|
||||
// TODO: Implement real login
|
||||
return Result.success(
|
||||
User(
|
||||
id: '1',
|
||||
email: email,
|
||||
name: 'Test User',
|
||||
),
|
||||
Future<Result<User>> register(String username, String password, String name) async {
|
||||
final result = await remoteDataSource.register(username, password, name);
|
||||
return result.fold(
|
||||
(error) => Result.failure(error),
|
||||
(response) async {
|
||||
await localDataSource.saveToken(response.accessToken);
|
||||
await localDataSource.saveRefreshToken(response.refreshToken);
|
||||
await localDataSource.saveUserId(response.userId);
|
||||
return Result.success(response.toDomainUser());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<void>> logout() async {
|
||||
await remoteDataSource.logout();
|
||||
await localDataSource.removeToken();
|
||||
await localDataSource.removeUserId();
|
||||
return const Result.success(null);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<User>> register(String email, String password, String name) async {
|
||||
return Result.success(
|
||||
User(
|
||||
id: '1',
|
||||
email: email,
|
||||
name: name,
|
||||
),
|
||||
);
|
||||
Future<Result<User>> getCurrentUser() async {
|
||||
return await remoteDataSource.getCurrentUser();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Result<void>> refreshToken() async {
|
||||
return const Result.success(null);
|
||||
return await remoteDataSource.refreshToken();
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isLoggedIn => false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user