초기 커밋
This commit is contained in:
28
lib/features/auth/data/datasources/auth_remote_source.dart
Normal file
28
lib/features/auth/data/datasources/auth_remote_source.dart
Normal file
@@ -0,0 +1,28 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:retrofit/retrofit.dart';
|
||||
|
||||
import '../../../../core/constants/api_constants.dart';
|
||||
import '../models/login_request.dart';
|
||||
import '../models/register_request.dart';
|
||||
import '../models/token_response.dart';
|
||||
|
||||
part 'auth_remote_source.g.dart';
|
||||
|
||||
@RestApi()
|
||||
abstract class AuthRemoteSource {
|
||||
factory AuthRemoteSource(Dio dio) = _AuthRemoteSource;
|
||||
|
||||
@POST(ApiConstants.login)
|
||||
Future<TokenResponse> login(@Body() LoginRequest request);
|
||||
|
||||
@POST(ApiConstants.register)
|
||||
Future<TokenResponse> register(@Body() RegisterRequest request);
|
||||
|
||||
@POST(ApiConstants.logout)
|
||||
Future<void> logout();
|
||||
|
||||
@POST(ApiConstants.refreshToken)
|
||||
Future<TokenResponse> refreshToken(
|
||||
@Body() Map<String, String> body,
|
||||
);
|
||||
}
|
||||
15
lib/features/auth/data/models/login_request.dart
Normal file
15
lib/features/auth/data/models/login_request.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'login_request.freezed.dart';
|
||||
part 'login_request.g.dart';
|
||||
|
||||
@freezed
|
||||
class LoginRequest with _$LoginRequest {
|
||||
const factory LoginRequest({
|
||||
required String email,
|
||||
required String password,
|
||||
}) = _LoginRequest;
|
||||
|
||||
factory LoginRequest.fromJson(Map<String, dynamic> json) =>
|
||||
_$LoginRequestFromJson(json);
|
||||
}
|
||||
16
lib/features/auth/data/models/register_request.dart
Normal file
16
lib/features/auth/data/models/register_request.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'register_request.freezed.dart';
|
||||
part 'register_request.g.dart';
|
||||
|
||||
@freezed
|
||||
class RegisterRequest with _$RegisterRequest {
|
||||
const factory RegisterRequest({
|
||||
required String email,
|
||||
required String password,
|
||||
required String name,
|
||||
}) = _RegisterRequest;
|
||||
|
||||
factory RegisterRequest.fromJson(Map<String, dynamic> json) =>
|
||||
_$RegisterRequestFromJson(json);
|
||||
}
|
||||
16
lib/features/auth/data/models/token_response.dart
Normal file
16
lib/features/auth/data/models/token_response.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'token_response.freezed.dart';
|
||||
part 'token_response.g.dart';
|
||||
|
||||
@freezed
|
||||
class TokenResponse with _$TokenResponse {
|
||||
const factory TokenResponse({
|
||||
@JsonKey(name: 'access_token') required String accessToken,
|
||||
@JsonKey(name: 'refresh_token') required String refreshToken,
|
||||
@JsonKey(name: 'token_type') @Default('bearer') String tokenType,
|
||||
}) = _TokenResponse;
|
||||
|
||||
factory TokenResponse.fromJson(Map<String, dynamic> json) =>
|
||||
_$TokenResponseFromJson(json);
|
||||
}
|
||||
123
lib/features/auth/data/repositories/auth_repository_impl.dart
Normal file
123
lib/features/auth/data/repositories/auth_repository_impl.dart
Normal file
@@ -0,0 +1,123 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:talker/talker.dart';
|
||||
|
||||
import '../../../../core/constants/app_constants.dart';
|
||||
import '../../../../core/error/exceptions.dart';
|
||||
import '../../../../core/storage/secure_storage.dart';
|
||||
import '../../../../shared/models/user_role.dart';
|
||||
import '../../domain/entities/user.dart';
|
||||
import '../../domain/repositories/auth_repository.dart';
|
||||
import '../datasources/auth_remote_source.dart';
|
||||
import '../models/login_request.dart';
|
||||
import '../models/register_request.dart';
|
||||
|
||||
class AuthRepositoryImpl implements AuthRepository {
|
||||
AuthRepositoryImpl({
|
||||
required this.remoteSource,
|
||||
required this.secureStorage,
|
||||
required this.talker,
|
||||
});
|
||||
|
||||
final AuthRemoteSource remoteSource;
|
||||
final SecureStorage secureStorage;
|
||||
final Talker talker;
|
||||
|
||||
@override
|
||||
Future<User> login({
|
||||
required String email,
|
||||
required String password,
|
||||
}) async {
|
||||
try {
|
||||
final response = await remoteSource.login(
|
||||
LoginRequest(email: email, password: password),
|
||||
);
|
||||
|
||||
await _saveTokens(response.accessToken, response.refreshToken);
|
||||
|
||||
// 로그인 후 사용자 정보 조회
|
||||
final user = await getCurrentUser();
|
||||
if (user == null) {
|
||||
throw const ServerException(
|
||||
message: '사용자 정보를 가져올 수 없습니다',
|
||||
statusCode: 500,
|
||||
);
|
||||
}
|
||||
return user;
|
||||
} catch (e) {
|
||||
talker.error('Login failed', e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User> register({
|
||||
required String email,
|
||||
required String password,
|
||||
required String name,
|
||||
}) async {
|
||||
try {
|
||||
final response = await remoteSource.register(
|
||||
RegisterRequest(email: email, password: password, name: name),
|
||||
);
|
||||
|
||||
await _saveTokens(response.accessToken, response.refreshToken);
|
||||
|
||||
return User(
|
||||
id: '',
|
||||
email: email,
|
||||
name: name,
|
||||
role: UserRole.user,
|
||||
);
|
||||
} catch (e) {
|
||||
talker.error('Register failed', e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> logout() async {
|
||||
try {
|
||||
await remoteSource.logout();
|
||||
} catch (_) {
|
||||
// 서버 로그아웃 실패해도 로컬 토큰은 삭제
|
||||
} finally {
|
||||
await secureStorage.delete(key: AppConstants.accessTokenKey);
|
||||
await secureStorage.delete(key: AppConstants.refreshTokenKey);
|
||||
await secureStorage.delete(key: AppConstants.userKey);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<User?> getCurrentUser() async {
|
||||
try {
|
||||
final userData = await secureStorage.read(key: AppConstants.userKey);
|
||||
if (userData != null) {
|
||||
return User.fromJson(
|
||||
jsonDecode(userData) as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
talker.error('Get current user failed', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isLoggedIn() async {
|
||||
final token = await secureStorage.read(key: AppConstants.accessTokenKey);
|
||||
return token != null;
|
||||
}
|
||||
|
||||
Future<void> _saveTokens(String accessToken, String refreshToken) async {
|
||||
await secureStorage.write(
|
||||
key: AppConstants.accessTokenKey,
|
||||
value: accessToken,
|
||||
);
|
||||
await secureStorage.write(
|
||||
key: AppConstants.refreshTokenKey,
|
||||
value: refreshToken,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user