From b0e93794231e58fa11808ec11701c791142dfbcb Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Wed, 5 Feb 2025 07:35:43 +0900 Subject: [PATCH 1/7] refactor: remove password field from UserEntity and User table for enhanced security --- lib/data/tables/user_table.dart | 1 - lib/domain/entities/user_entity.dart | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/data/tables/user_table.dart b/lib/data/tables/user_table.dart index 99458f95..c44594f7 100644 --- a/lib/data/tables/user_table.dart +++ b/lib/data/tables/user_table.dart @@ -4,7 +4,6 @@ import 'package:uuid/uuid.dart'; class Users extends Table { TextColumn get id => text().clientDefault(() => Uuid().v7())(); TextColumn get email => text().withLength(min: 1, max: 320)(); - TextColumn get password => text().withLength(min: 1, max: 30)(); TextColumn get name => text().withLength(min: 1, max: 30)(); IntColumn get spareTime => integer()(); TextColumn get note => text()(); diff --git a/lib/domain/entities/user_entity.dart b/lib/domain/entities/user_entity.dart index ac6972dc..67ec8e8b 100644 --- a/lib/domain/entities/user_entity.dart +++ b/lib/domain/entities/user_entity.dart @@ -3,26 +3,25 @@ import '/core/database/database.dart'; class UserEntity { final String id; final String email; - final String password; final String name; final int spareTime; final String note; final double score; + final bool isOnboardingCompleted; UserEntity( {required this.id, required this.email, - required this.password, required this.name, required this.spareTime, required this.note, - required this.score}); + required this.score, + this.isOnboardingCompleted = false}); static UserEntity fromModel(User user) { return UserEntity( id: user.id, email: user.email, - password: user.password, name: user.name, spareTime: user.spareTime, note: user.note, @@ -34,7 +33,6 @@ class UserEntity { return User( id: id, email: email, - password: password, name: name, spareTime: spareTime, note: note, @@ -44,6 +42,6 @@ class UserEntity { @override String toString() { - return 'UserEntity(id: $id, email: $email, password: $password, name: $name, spareTime: $spareTime, note: $note, score: $score)'; + return 'UserEntity(id: $id, email: $email, name: $name, spareTime: $spareTime, note: $note, score: $score)'; } } From 1d7f07df3179c5abb45f54e4caf4633665499ea5 Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Wed, 5 Feb 2025 17:55:02 +0900 Subject: [PATCH 2/7] feat: add signIn and signUp endpoints to the Endpoint class --- lib/core/constants/endpoint.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/core/constants/endpoint.dart b/lib/core/constants/endpoint.dart index f8b0f412..f5148de4 100644 --- a/lib/core/constants/endpoint.dart +++ b/lib/core/constants/endpoint.dart @@ -1,6 +1,13 @@ import 'dart:core'; class Endpoint { + //user + static const _signIn = '/login'; + static const _signUp = '/sign-up'; + + static get signIn => _signIn; + static get signUp => _signUp; + // schedule static const _getScheduleById = '/schedule/show/id?scheduleId='; static const _getSchedulesByDate = '/schedule/show'; @@ -34,8 +41,7 @@ class Endpoint { static const _updateDefaultPreparation = '/preparationuser/modify'; // 사용자 준비과정 수정 - static const _updatePreparationByScheduleId = - '/preparationschedule/modify'; // 스케줄별 준비과정 수정 + static const _updatePreparationByScheduleId = '/preparationschedule/modify'; // delelte는 api가 없음. // delete는 api로 요청을 보내는 게 아니라, 전부 없애고 다시 순서를 조정하는 형태로 From d9aed01c7f21b17520c2aa217d26424b4b60ce04 Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Wed, 5 Feb 2025 17:55:08 +0900 Subject: [PATCH 3/7] refactor: change spareTime type from int to Duration for improved time handling --- lib/domain/entities/user_entity.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/domain/entities/user_entity.dart b/lib/domain/entities/user_entity.dart index 67ec8e8b..7314cec2 100644 --- a/lib/domain/entities/user_entity.dart +++ b/lib/domain/entities/user_entity.dart @@ -4,7 +4,7 @@ class UserEntity { final String id; final String email; final String name; - final int spareTime; + final Duration spareTime; final String note; final double score; final bool isOnboardingCompleted; @@ -23,7 +23,7 @@ class UserEntity { id: user.id, email: user.email, name: user.name, - spareTime: user.spareTime, + spareTime: Duration(minutes: user.spareTime), note: user.note, score: user.score, ); @@ -34,7 +34,7 @@ class UserEntity { id: id, email: email, name: name, - spareTime: spareTime, + spareTime: spareTime.inMinutes, note: note, score: score, ); From da2f7e0f3d9421956e948e0f20aab8dbfb55a2fb Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Wed, 5 Feb 2025 17:55:13 +0900 Subject: [PATCH 4/7] feat: add SignInUserResponseModel for handling user sign-in responses --- .../models/sign_in_user_response_model.dart | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 lib/data/models/sign_in_user_response_model.dart diff --git a/lib/data/models/sign_in_user_response_model.dart b/lib/data/models/sign_in_user_response_model.dart new file mode 100644 index 00000000..6f771350 --- /dev/null +++ b/lib/data/models/sign_in_user_response_model.dart @@ -0,0 +1,42 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:on_time_front/domain/entities/user_entity.dart'; + +part 'sign_in_user_response_model.g.dart'; + +@JsonSerializable() +class SignInUserResponseModel { + final String id; + final String email; + final String name; + final int spareTime; + final String? note; + final double score; + final String? role; + + const SignInUserResponseModel({ + required this.id, + required this.email, + required this.name, + required this.spareTime, + required this.score, + this.role, + this.note, + }); + + UserEntity toEntity() { + return UserEntity( + id: id, + email: email, + name: name, + spareTime: Duration(minutes: spareTime), + score: score, + isOnboardingCompleted: role == 'GUEST' ? false : true, + note: note ?? '', + ); + } + + factory SignInUserResponseModel.fromJson(Map json) => + _$SignInUserResponseModelFromJson(json); + + Map toJson() => _$SignInUserResponseModelToJson(this); +} From aa5e307b1d9e41fba1e2c17129a15e1d5dc9634c Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Wed, 5 Feb 2025 17:55:18 +0900 Subject: [PATCH 5/7] feat: add TokenEntity for managing access and refresh tokens --- lib/domain/entities/token_entity.dart | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 lib/domain/entities/token_entity.dart diff --git a/lib/domain/entities/token_entity.dart b/lib/domain/entities/token_entity.dart new file mode 100644 index 00000000..496b3ecb --- /dev/null +++ b/lib/domain/entities/token_entity.dart @@ -0,0 +1,22 @@ +import 'package:dio/dio.dart'; +import 'package:equatable/equatable.dart'; + +class TokenEntity extends Equatable { + final String accessToken; + final String refreshToken; + + const TokenEntity({ + required this.accessToken, + required this.refreshToken, + }); + + static TokenEntity fromHeaders(Headers headers) { + return TokenEntity( + accessToken: headers.value('authorization')!, + refreshToken: headers.value('refresh-token')!, + ); + } + + @override + List get props => [accessToken, refreshToken]; +} From 68970958fbb18c13063261303164e2e49e87acd1 Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Wed, 5 Feb 2025 17:55:24 +0900 Subject: [PATCH 6/7] feat: implement AuthenticationRemoteDataSource for user sign-in and sign-up --- .../authentication_remote_data_source.dart | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 lib/data/data_sources/authentication_remote_data_source.dart diff --git a/lib/data/data_sources/authentication_remote_data_source.dart b/lib/data/data_sources/authentication_remote_data_source.dart new file mode 100644 index 00000000..541755c3 --- /dev/null +++ b/lib/data/data_sources/authentication_remote_data_source.dart @@ -0,0 +1,90 @@ +import 'package:dio/dio.dart'; +import 'package:injectable/injectable.dart'; +import 'package:on_time_front/core/constants/endpoint.dart'; +import 'package:on_time_front/data/models/sign_in_user_response_model.dart'; +import 'package:on_time_front/domain/entities/token_entity.dart'; +import 'package:on_time_front/domain/entities/user_entity.dart'; + +abstract interface class AuthenticationRemoteDataSource { + Future<(UserEntity, TokenEntity)> signIn(String email, String password); + + Future<(UserEntity, TokenEntity)> signUp( + String email, String password, String name); + + // Future<(UserEntity, String)> signInWithGoogle(String idToken); +} + +@Injectable(as: AuthenticationRemoteDataSource) +class AuthenticationRemoteDataSourceImpl + implements AuthenticationRemoteDataSource { + final Dio dio; + AuthenticationRemoteDataSourceImpl(this.dio); + + @override + Future<(UserEntity, TokenEntity)> signIn( + String email, String password) async { + try { + final result = await dio.post( + Endpoint.signIn, + data: { + 'email': email, + 'password': password, + }, + ); + if (result.statusCode == 200) { + final user = SignInUserResponseModel.fromJson(result.data['data']); + final token = TokenEntity.fromHeaders(result.headers); + return (user.toEntity(), token); + } else { + throw Exception('Error signing in'); + } + } catch (e) { + rethrow; + } + } + + @override + Future<(UserEntity, TokenEntity)> signUp( + String email, String password, String name) async { + try { + final result = await dio.post( + Endpoint.signUp, + data: { + 'email': email, + 'password': password, + 'name': name, + }, + ); + if (result.statusCode == 200) { + final user = SignInUserResponseModel.fromJson(result.data['data']); + final token = TokenEntity.fromHeaders(result.headers); + return (user.toEntity(), token); + } else { + throw Exception('Error signing up'); + } + } catch (e) { + rethrow; + } + } + + // @override + // Future<(UserEntity, String)> signInWithGoogle(String idToken) async { + // try { + // final result = await dio.post( + // Endpoint.signInWithGoogle, + // data: { + // 'idToken': idToken, + // }, + // ); + // if (result.statusCode == 200) { + // final user = UserEntity.fromModel(result.data); + // final token = result.headers['authorization']! as String; + // return (user, token); + // } else { + // throw Exception('Error signing in with Google'); + // } + // } catch (e) { + // rethrow; + // } + // } +} From 9a1ac49de505b17f5c9d89a9370f429e2b261cbc Mon Sep 17 00:00:00 2001 From: jjoonleo Date: Wed, 5 Feb 2025 18:02:24 +0900 Subject: [PATCH 7/7] refactor: remove hardcoded password from user test data --- test/data/daos/preparation_schedule_dao_test.dart | 1 - test/data/daos/preparation_user_dao_test.dart | 1 - 2 files changed, 2 deletions(-) diff --git a/test/data/daos/preparation_schedule_dao_test.dart b/test/data/daos/preparation_schedule_dao_test.dart index 304a6dd9..644da12c 100644 --- a/test/data/daos/preparation_schedule_dao_test.dart +++ b/test/data/daos/preparation_schedule_dao_test.dart @@ -42,7 +42,6 @@ void main() { UsersCompanion( id: drift.Value(userId), email: drift.Value('testuser@example.com'), - password: drift.Value('password123'), name: drift.Value('Test User'), spareTime: drift.Value(Duration(minutes: 30).inSeconds), note: drift.Value('Test Note'), diff --git a/test/data/daos/preparation_user_dao_test.dart b/test/data/daos/preparation_user_dao_test.dart index 2e3802d4..cefc03cb 100644 --- a/test/data/daos/preparation_user_dao_test.dart +++ b/test/data/daos/preparation_user_dao_test.dart @@ -43,7 +43,6 @@ void main() { UsersCompanion( id: drift.Value(userId), email: drift.Value('testuser@example.com'), - password: drift.Value('password123'), name: drift.Value('Test User'), spareTime: drift.Value(Duration(minutes: 30).inSeconds), note: drift.Value('Test Note'),