feat: applied auth wirth session token on dart backend
This commit is contained in:
parent
55edac6abe
commit
e9221f36ef
@ -1,11 +1,14 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:backend_dart/application/service/dto/auth_dto.dart';
|
import 'package:backend_dart/application/service/dto/auth_dto.dart';
|
||||||
|
import 'package:backend_dart/application/service/helper.dart';
|
||||||
import 'package:backend_dart/common/request_helper.dart';
|
import 'package:backend_dart/common/request_helper.dart';
|
||||||
import 'package:backend_dart/common/response_helpers.dart';
|
import 'package:backend_dart/common/response_helpers.dart';
|
||||||
import 'package:backend_dart/common/secure_hash.dart';
|
import 'package:backend_dart/common/secure_hash.dart';
|
||||||
import 'package:backend_dart/common/validation.dart';
|
import 'package:backend_dart/common/validation.dart';
|
||||||
import 'package:backend_dart/domain/entities/user.dart';
|
import 'package:backend_dart/domain/entities/user.dart';
|
||||||
import 'package:backend_dart/domain/errors/app_error.dart';
|
import 'package:backend_dart/domain/errors/app_error.dart';
|
||||||
|
import 'package:backend_dart/domain/errors/error.dart';
|
||||||
import 'package:backend_dart/domain/repository/auth_repository.dart';
|
import 'package:backend_dart/domain/repository/auth_repository.dart';
|
||||||
import 'package:backend_dart/domain/repository/user_repository.dart';
|
import 'package:backend_dart/domain/repository/user_repository.dart';
|
||||||
import 'package:fpdart/fpdart.dart';
|
import 'package:fpdart/fpdart.dart';
|
||||||
@ -37,20 +40,28 @@ class AuthService {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.flatMap((user) => authRepository.generateToken(user.id).map((token) =>
|
.flatMap((user) => authRepository.generateToken(user.id).map(
|
||||||
TokenResponseDTO(token: token, userId: user.id).toJson()))
|
(token) => Response.ok(
|
||||||
.map(jsonEncode)
|
jsonEncode(
|
||||||
.toResponse()
|
TokenResponseDTO(token: token, userId: user.id).toJson(),
|
||||||
|
),
|
||||||
|
headers: {
|
||||||
|
'Set-Cookie':
|
||||||
|
'session_token=$token; HttpOnly; Path=/, user_id=${user.id}; HttpOnly; Path=/',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.getOrElse(ResponseHelpers.fromError)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Route to validate a token
|
/// Route to validate a token
|
||||||
@Route.post('/validate')
|
@Route.post('/validate')
|
||||||
Future<Response> validateToken(Request request) async {
|
Future<Response> validateToken(Request request) async {
|
||||||
return requestToJson(request)
|
return readCookie(request, 'session_token')
|
||||||
.flatMap(validateJsonKeys(['token']))
|
.toTaskEither<IError>(() => AppError.authenticationError(
|
||||||
.flatMap((json) => decodeJson(json, TokenRequestDTO.fromJson))
|
message: 'No token found in the request'))
|
||||||
.map((tokenRequest) => tokenRequest.token)
|
|
||||||
.flatMap((token) => authRepository.validateToken(token).map((userId) =>
|
.flatMap((token) => authRepository.validateToken(token).map((userId) =>
|
||||||
TokenResponseDTO(token: token, userId: userId).toJson()))
|
TokenResponseDTO(token: token, userId: userId).toJson()))
|
||||||
.map(jsonEncode)
|
.map(jsonEncode)
|
||||||
@ -61,14 +72,20 @@ class AuthService {
|
|||||||
/// Route to revoke a token
|
/// Route to revoke a token
|
||||||
@Route.post('/logout')
|
@Route.post('/logout')
|
||||||
Future<Response> logout(Request request) async {
|
Future<Response> logout(Request request) async {
|
||||||
return requestToJson(request)
|
return readCookie(request, 'session_token')
|
||||||
.flatMap(validateJsonKeys(['token']))
|
.toTaskEither<IError>(() => AppError.authenticationError(
|
||||||
.flatMap((json) => decodeJson(json, TokenRequestDTO.fromJson))
|
message: 'No token found in the request'))
|
||||||
.map((tokenRequest) => tokenRequest.token)
|
|
||||||
.flatMap(authRepository.revokeToken)
|
.flatMap(authRepository.revokeToken)
|
||||||
.map((_) => {'message': 'Token revoked successfully'})
|
.map((_) => Response.ok(
|
||||||
.map(jsonEncode)
|
jsonEncode({'message': 'Token revoked successfully'}),
|
||||||
.toResponse()
|
headers: {
|
||||||
|
// Clear cookies by setting Max-Age to 0
|
||||||
|
'Set-Cookie':
|
||||||
|
'session_token=; Max-Age=0; HttpOnly; Path=/, user_id=; Max-Age=0; HttpOnly; Path=/',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.getOrElse(ResponseHelpers.fromError)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,18 +16,6 @@ class TokenResponseDTO with _$TokenResponseDTO {
|
|||||||
_$TokenResponseDTOFromJson(json);
|
_$TokenResponseDTOFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TokenRequestDTO represents a request for operations involving tokens.
|
|
||||||
@freezed
|
|
||||||
class TokenRequestDTO with _$TokenRequestDTO {
|
|
||||||
const factory TokenRequestDTO({
|
|
||||||
required String token,
|
|
||||||
}) = _TokenRequestDTO;
|
|
||||||
|
|
||||||
/// JSON serialization
|
|
||||||
factory TokenRequestDTO.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$TokenRequestDTOFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// LoginRequestDTO represents the login request.
|
/// LoginRequestDTO represents the login request.
|
||||||
@freezed
|
@freezed
|
||||||
class LoginRequestDTO with _$LoginRequestDTO {
|
class LoginRequestDTO with _$LoginRequestDTO {
|
||||||
|
@ -182,157 +182,6 @@ abstract class _TokenResponseDTO implements TokenResponseDTO {
|
|||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenRequestDTO _$TokenRequestDTOFromJson(Map<String, dynamic> json) {
|
|
||||||
return _TokenRequestDTO.fromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
mixin _$TokenRequestDTO {
|
|
||||||
String get token => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
/// Serializes this TokenRequestDTO to a JSON map.
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
|
||||||
|
|
||||||
/// Create a copy of TokenRequestDTO
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
$TokenRequestDTOCopyWith<TokenRequestDTO> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $TokenRequestDTOCopyWith<$Res> {
|
|
||||||
factory $TokenRequestDTOCopyWith(
|
|
||||||
TokenRequestDTO value, $Res Function(TokenRequestDTO) then) =
|
|
||||||
_$TokenRequestDTOCopyWithImpl<$Res, TokenRequestDTO>;
|
|
||||||
@useResult
|
|
||||||
$Res call({String token});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$TokenRequestDTOCopyWithImpl<$Res, $Val extends TokenRequestDTO>
|
|
||||||
implements $TokenRequestDTOCopyWith<$Res> {
|
|
||||||
_$TokenRequestDTOCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
/// Create a copy of TokenRequestDTO
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? token = null,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
token: null == token
|
|
||||||
? _value.token
|
|
||||||
: token // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
) as $Val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$TokenRequestDTOImplCopyWith<$Res>
|
|
||||||
implements $TokenRequestDTOCopyWith<$Res> {
|
|
||||||
factory _$$TokenRequestDTOImplCopyWith(_$TokenRequestDTOImpl value,
|
|
||||||
$Res Function(_$TokenRequestDTOImpl) then) =
|
|
||||||
__$$TokenRequestDTOImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({String token});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$TokenRequestDTOImplCopyWithImpl<$Res>
|
|
||||||
extends _$TokenRequestDTOCopyWithImpl<$Res, _$TokenRequestDTOImpl>
|
|
||||||
implements _$$TokenRequestDTOImplCopyWith<$Res> {
|
|
||||||
__$$TokenRequestDTOImplCopyWithImpl(
|
|
||||||
_$TokenRequestDTOImpl _value, $Res Function(_$TokenRequestDTOImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of TokenRequestDTO
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? token = null,
|
|
||||||
}) {
|
|
||||||
return _then(_$TokenRequestDTOImpl(
|
|
||||||
token: null == token
|
|
||||||
? _value.token
|
|
||||||
: token // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
@JsonSerializable()
|
|
||||||
class _$TokenRequestDTOImpl implements _TokenRequestDTO {
|
|
||||||
const _$TokenRequestDTOImpl({required this.token});
|
|
||||||
|
|
||||||
factory _$TokenRequestDTOImpl.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$$TokenRequestDTOImplFromJson(json);
|
|
||||||
|
|
||||||
@override
|
|
||||||
final String token;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'TokenRequestDTO(token: $token)';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$TokenRequestDTOImpl &&
|
|
||||||
(identical(other.token, token) || other.token == token));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, token);
|
|
||||||
|
|
||||||
/// Create a copy of TokenRequestDTO
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$TokenRequestDTOImplCopyWith<_$TokenRequestDTOImpl> get copyWith =>
|
|
||||||
__$$TokenRequestDTOImplCopyWithImpl<_$TokenRequestDTOImpl>(
|
|
||||||
this, _$identity);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return _$$TokenRequestDTOImplToJson(
|
|
||||||
this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class _TokenRequestDTO implements TokenRequestDTO {
|
|
||||||
const factory _TokenRequestDTO({required final String token}) =
|
|
||||||
_$TokenRequestDTOImpl;
|
|
||||||
|
|
||||||
factory _TokenRequestDTO.fromJson(Map<String, dynamic> json) =
|
|
||||||
_$TokenRequestDTOImpl.fromJson;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get token;
|
|
||||||
|
|
||||||
/// Create a copy of TokenRequestDTO
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
_$$TokenRequestDTOImplCopyWith<_$TokenRequestDTOImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoginRequestDTO _$LoginRequestDTOFromJson(Map<String, dynamic> json) {
|
LoginRequestDTO _$LoginRequestDTOFromJson(Map<String, dynamic> json) {
|
||||||
return _LoginRequestDTO.fromJson(json);
|
return _LoginRequestDTO.fromJson(json);
|
||||||
}
|
}
|
||||||
|
@ -20,18 +20,6 @@ Map<String, dynamic> _$$TokenResponseDTOImplToJson(
|
|||||||
'userId': instance.userId,
|
'userId': instance.userId,
|
||||||
};
|
};
|
||||||
|
|
||||||
_$TokenRequestDTOImpl _$$TokenRequestDTOImplFromJson(
|
|
||||||
Map<String, dynamic> json) =>
|
|
||||||
_$TokenRequestDTOImpl(
|
|
||||||
token: json['token'] as String,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> _$$TokenRequestDTOImplToJson(
|
|
||||||
_$TokenRequestDTOImpl instance) =>
|
|
||||||
<String, dynamic>{
|
|
||||||
'token': instance.token,
|
|
||||||
};
|
|
||||||
|
|
||||||
_$LoginRequestDTOImpl _$$LoginRequestDTOImplFromJson(
|
_$LoginRequestDTOImpl _$$LoginRequestDTOImplFromJson(
|
||||||
Map<String, dynamic> json) =>
|
Map<String, dynamic> json) =>
|
||||||
_$LoginRequestDTOImpl(
|
_$LoginRequestDTOImpl(
|
||||||
|
33
backend-dart/lib/application/service/helper.dart
Normal file
33
backend-dart/lib/application/service/helper.dart
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import 'package:backend_dart/domain/errors/app_error.dart';
|
||||||
|
import 'package:backend_dart/domain/errors/error.dart';
|
||||||
|
import 'package:backend_dart/domain/repository/auth_repository.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
|
||||||
|
TaskOption<String> readCookie(Request request, String cookieName) {
|
||||||
|
// Retrieve the "Cookie" header
|
||||||
|
final cookieHeader = request.headers['cookie'];
|
||||||
|
if (cookieHeader == null) return TaskOption.none();
|
||||||
|
// No cookies found
|
||||||
|
|
||||||
|
// Split the cookies into individual key-value pairs
|
||||||
|
final cookies = cookieHeader.split(';');
|
||||||
|
|
||||||
|
// Find the cookie with the matching name
|
||||||
|
for (var cookie in cookies) {
|
||||||
|
final parts = cookie.trim().split('=');
|
||||||
|
if (parts.length == 2 && parts[0] == cookieName && parts[1].isNotEmpty) {
|
||||||
|
return TaskOption.some(parts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaskOption.none(); // Cookie not found
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskEither<IError, String> checkAuth(
|
||||||
|
Request request, AuthRepository authRepository) {
|
||||||
|
return readCookie(request, 'session_token')
|
||||||
|
.toTaskEither<IError>(() => AppError.authenticationError(
|
||||||
|
message: 'No token found in the request'))
|
||||||
|
.flatMap(authRepository.validateToken);
|
||||||
|
}
|
@ -1,10 +1,12 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:backend_dart/application/service/dto/project_dto.dart';
|
import 'package:backend_dart/application/service/dto/project_dto.dart';
|
||||||
|
import 'package:backend_dart/application/service/helper.dart';
|
||||||
import 'package:backend_dart/application/service/mapper/project_dto_mapper.dart';
|
import 'package:backend_dart/application/service/mapper/project_dto_mapper.dart';
|
||||||
import 'package:backend_dart/common/request_helper.dart';
|
import 'package:backend_dart/common/request_helper.dart';
|
||||||
import 'package:backend_dart/common/response_helpers.dart';
|
import 'package:backend_dart/common/response_helpers.dart';
|
||||||
import 'package:backend_dart/common/validation.dart';
|
import 'package:backend_dart/common/validation.dart';
|
||||||
|
import 'package:backend_dart/domain/repository/auth_repository.dart';
|
||||||
import 'package:backend_dart/domain/repository/project_repository.dart';
|
import 'package:backend_dart/domain/repository/project_repository.dart';
|
||||||
import 'package:shelf/shelf.dart';
|
import 'package:shelf/shelf.dart';
|
||||||
import 'package:shelf_router/shelf_router.dart';
|
import 'package:shelf_router/shelf_router.dart';
|
||||||
@ -13,13 +15,14 @@ part 'project_service.g.dart'; // generated with 'pub run build_runner build'
|
|||||||
|
|
||||||
class ProjectService {
|
class ProjectService {
|
||||||
final ProjectRepository projects;
|
final ProjectRepository projects;
|
||||||
|
final AuthRepository authRepository;
|
||||||
final ProjectDtoMapper mapper = ProjectDtoMapper();
|
final ProjectDtoMapper mapper = ProjectDtoMapper();
|
||||||
ProjectService(this.projects);
|
ProjectService(this.projects, this.authRepository);
|
||||||
|
|
||||||
@Route.get('/')
|
@Route.get('/')
|
||||||
Future<Response> listProjects(Request request) async {
|
Future<Response> listProjects(Request request) async {
|
||||||
return projects
|
return checkAuth(request, authRepository)
|
||||||
.findAll()
|
.flatMap((_) => projects.findAll())
|
||||||
.flatMap(mapper.listTo)
|
.flatMap(mapper.listTo)
|
||||||
.map((projects) => projects.map((project) => project.toJson()).toList())
|
.map((projects) => projects.map((project) => project.toJson()).toList())
|
||||||
.map(jsonEncode)
|
.map(jsonEncode)
|
||||||
@ -29,8 +32,8 @@ class ProjectService {
|
|||||||
|
|
||||||
@Route.get('/<projectId>')
|
@Route.get('/<projectId>')
|
||||||
Future<Response> fetchProject(Request request, String projectId) async {
|
Future<Response> fetchProject(Request request, String projectId) async {
|
||||||
return projects
|
return checkAuth(request, authRepository)
|
||||||
.findById(projectId)
|
.flatMap((_) => projects.findById(projectId))
|
||||||
.flatMap(mapper.to)
|
.flatMap(mapper.to)
|
||||||
.map((dto) => dto.toJson())
|
.map((dto) => dto.toJson())
|
||||||
.toResponse()
|
.toResponse()
|
||||||
@ -39,7 +42,8 @@ class ProjectService {
|
|||||||
|
|
||||||
@Route.post('/')
|
@Route.post('/')
|
||||||
Future<Response> createProject(Request request) async {
|
Future<Response> createProject(Request request) async {
|
||||||
return requestToJson(request)
|
return checkAuth(request, authRepository)
|
||||||
|
.flatMap((_) => requestToJson(request))
|
||||||
.flatMap(validateJsonKeys(['name', 'userId'])) // Add required fields
|
.flatMap(validateJsonKeys(['name', 'userId'])) // Add required fields
|
||||||
.flatMap((json) => decodeJson(json, ProjectCreateDto.fromJson))
|
.flatMap((json) => decodeJson(json, ProjectCreateDto.fromJson))
|
||||||
.flatMap(mapper.fromCreateTo)
|
.flatMap(mapper.fromCreateTo)
|
||||||
@ -52,7 +56,8 @@ class ProjectService {
|
|||||||
|
|
||||||
@Route.put('/<projectId>')
|
@Route.put('/<projectId>')
|
||||||
Future<Response> updateProject(Request request, String projectId) async {
|
Future<Response> updateProject(Request request, String projectId) async {
|
||||||
return requestToJson(request)
|
return checkAuth(request, authRepository)
|
||||||
|
.flatMap((_) => requestToJson(request))
|
||||||
.flatMap(validateJsonKeys([]))
|
.flatMap(validateJsonKeys([]))
|
||||||
.flatMap((json) => decodeJson(json, ProjectUpdateDto.fromJson))
|
.flatMap((json) => decodeJson(json, ProjectUpdateDto.fromJson))
|
||||||
.flatMap((dto) => mapper.fromUpdateTo(dto, projectId))
|
.flatMap((dto) => mapper.fromUpdateTo(dto, projectId))
|
||||||
@ -65,8 +70,8 @@ class ProjectService {
|
|||||||
|
|
||||||
@Route.delete('/<projectId>')
|
@Route.delete('/<projectId>')
|
||||||
Future<Response> deleteProject(Request request, String projectId) async {
|
Future<Response> deleteProject(Request request, String projectId) async {
|
||||||
return projects
|
return checkAuth(request, authRepository)
|
||||||
.delete(projectId)
|
.flatMap((_) => projects.delete(projectId))
|
||||||
.flatMap(mapper.to)
|
.flatMap(mapper.to)
|
||||||
.map((dto) => dto.toJson())
|
.map((dto) => dto.toJson())
|
||||||
.toResponse()
|
.toResponse()
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:backend_dart/application/service/dto/project_task_dto.dart';
|
import 'package:backend_dart/application/service/dto/project_task_dto.dart';
|
||||||
|
import 'package:backend_dart/application/service/helper.dart';
|
||||||
import 'package:backend_dart/application/service/mapper/project_task_dto_mapper.dart';
|
import 'package:backend_dart/application/service/mapper/project_task_dto_mapper.dart';
|
||||||
import 'package:backend_dart/common/request_helper.dart';
|
import 'package:backend_dart/common/request_helper.dart';
|
||||||
import 'package:backend_dart/common/response_helpers.dart';
|
import 'package:backend_dart/common/response_helpers.dart';
|
||||||
import 'package:backend_dart/common/validation.dart';
|
import 'package:backend_dart/common/validation.dart';
|
||||||
|
import 'package:backend_dart/domain/repository/auth_repository.dart';
|
||||||
import 'package:backend_dart/domain/repository/project_task_repository.dart';
|
import 'package:backend_dart/domain/repository/project_task_repository.dart';
|
||||||
import 'package:shelf/shelf.dart';
|
import 'package:shelf/shelf.dart';
|
||||||
import 'package:shelf_router/shelf_router.dart';
|
import 'package:shelf_router/shelf_router.dart';
|
||||||
@ -13,13 +15,15 @@ part 'project_task_service.g.dart'; // Generated with 'pub run build_runner buil
|
|||||||
|
|
||||||
class ProjectTaskService {
|
class ProjectTaskService {
|
||||||
final ProjectTaskRepository tasks;
|
final ProjectTaskRepository tasks;
|
||||||
|
final AuthRepository authRepository;
|
||||||
|
|
||||||
final ProjectTaskDtoMapper mapper = ProjectTaskDtoMapper();
|
final ProjectTaskDtoMapper mapper = ProjectTaskDtoMapper();
|
||||||
ProjectTaskService(this.tasks);
|
ProjectTaskService(this.tasks, this.authRepository);
|
||||||
|
|
||||||
@Route.get('/')
|
@Route.get('/')
|
||||||
Future<Response> listTasks(Request request) async {
|
Future<Response> listTasks(Request request) async {
|
||||||
return tasks
|
return checkAuth(request, authRepository)
|
||||||
.findAll()
|
.flatMap((_) => tasks.findAll())
|
||||||
.flatMap(mapper.listTo)
|
.flatMap(mapper.listTo)
|
||||||
.map((tasks) => tasks.map((task) => task.toJson()).toList())
|
.map((tasks) => tasks.map((task) => task.toJson()).toList())
|
||||||
.map(jsonEncode)
|
.map(jsonEncode)
|
||||||
@ -29,8 +33,8 @@ class ProjectTaskService {
|
|||||||
|
|
||||||
@Route.get('/<taskId>')
|
@Route.get('/<taskId>')
|
||||||
Future<Response> fetchTask(Request request, String taskId) async {
|
Future<Response> fetchTask(Request request, String taskId) async {
|
||||||
return tasks
|
return checkAuth(request, authRepository)
|
||||||
.findById(taskId)
|
.flatMap((_) => tasks.findById(taskId))
|
||||||
.flatMap(mapper.to)
|
.flatMap(mapper.to)
|
||||||
.map((dto) => dto.toJson())
|
.map((dto) => dto.toJson())
|
||||||
.toResponse()
|
.toResponse()
|
||||||
@ -39,7 +43,8 @@ class ProjectTaskService {
|
|||||||
|
|
||||||
@Route.post('/')
|
@Route.post('/')
|
||||||
Future<Response> createTask(Request request) async {
|
Future<Response> createTask(Request request) async {
|
||||||
return requestToJson(request)
|
return checkAuth(request, authRepository)
|
||||||
|
.flatMap((_) => requestToJson(request))
|
||||||
.flatMap(validateJsonKeys(['name', 'projectId'])) // Add required fields
|
.flatMap(validateJsonKeys(['name', 'projectId'])) // Add required fields
|
||||||
.flatMap((json) => decodeJson(json, ProjectTaskCreateDto.fromJson))
|
.flatMap((json) => decodeJson(json, ProjectTaskCreateDto.fromJson))
|
||||||
.flatMap(mapper.fromCreateTo)
|
.flatMap(mapper.fromCreateTo)
|
||||||
@ -52,7 +57,8 @@ class ProjectTaskService {
|
|||||||
|
|
||||||
@Route.put('/<taskId>')
|
@Route.put('/<taskId>')
|
||||||
Future<Response> updateTask(Request request, String taskId) async {
|
Future<Response> updateTask(Request request, String taskId) async {
|
||||||
return requestToJson(request)
|
return checkAuth(request, authRepository)
|
||||||
|
.flatMap((_) => requestToJson(request))
|
||||||
.flatMap(validateJsonKeys([]))
|
.flatMap(validateJsonKeys([]))
|
||||||
.flatMap((json) => decodeJson(json, ProjectTaskUpdateDto.fromJson))
|
.flatMap((json) => decodeJson(json, ProjectTaskUpdateDto.fromJson))
|
||||||
.flatMap((dto) => mapper.fromUpdateTo(dto, taskId))
|
.flatMap((dto) => mapper.fromUpdateTo(dto, taskId))
|
||||||
@ -65,8 +71,8 @@ class ProjectTaskService {
|
|||||||
|
|
||||||
@Route.delete('/<taskId>')
|
@Route.delete('/<taskId>')
|
||||||
Future<Response> deleteTask(Request request, String taskId) async {
|
Future<Response> deleteTask(Request request, String taskId) async {
|
||||||
return tasks
|
return checkAuth(request, authRepository)
|
||||||
.delete(taskId)
|
.flatMap((_) => tasks.delete(taskId))
|
||||||
.flatMap(mapper.to)
|
.flatMap(mapper.to)
|
||||||
.map((dto) => dto.toJson())
|
.map((dto) => dto.toJson())
|
||||||
.toResponse()
|
.toResponse()
|
||||||
|
@ -8,22 +8,26 @@ import 'package:riverpod/riverpod.dart';
|
|||||||
|
|
||||||
final userServiceProvider = Provider<UserService>((ref) {
|
final userServiceProvider = Provider<UserService>((ref) {
|
||||||
final database = ref.read(userRepoProvider);
|
final database = ref.read(userRepoProvider);
|
||||||
return UserService(database);
|
final authRepository = ref.read(authProvider);
|
||||||
|
return UserService(database, authRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
final projectServiceProvider = Provider<ProjectService>((ref) {
|
final projectServiceProvider = Provider<ProjectService>((ref) {
|
||||||
final database = ref.read(projectProvider);
|
final database = ref.read(projectProvider);
|
||||||
return ProjectService(database);
|
final authRepository = ref.read(authProvider);
|
||||||
|
return ProjectService(database, authRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
final projectTaskServiceProvider = Provider<ProjectTaskService>((ref) {
|
final projectTaskServiceProvider = Provider<ProjectTaskService>((ref) {
|
||||||
final database = ref.read(projectTaskProvider);
|
final database = ref.read(projectTaskProvider);
|
||||||
return ProjectTaskService(database);
|
final authRepository = ref.read(authProvider);
|
||||||
|
return ProjectTaskService(database, authRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
final timeEntryServiceProvider = Provider<TimeEntryService>((ref) {
|
final timeEntryServiceProvider = Provider<TimeEntryService>((ref) {
|
||||||
final database = ref.read(timeEntryProvider);
|
final database = ref.read(timeEntryProvider);
|
||||||
return TimeEntryService(database);
|
final authRepository = ref.read(authProvider);
|
||||||
|
return TimeEntryService(database, authRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
final authServiceProvider = Provider<AuthService>((ref) {
|
final authServiceProvider = Provider<AuthService>((ref) {
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:backend_dart/application/service/dto/time_entry_dto.dart';
|
import 'package:backend_dart/application/service/dto/time_entry_dto.dart';
|
||||||
|
import 'package:backend_dart/application/service/helper.dart';
|
||||||
import 'package:backend_dart/application/service/mapper/time_entry_dto_mapper.dart';
|
import 'package:backend_dart/application/service/mapper/time_entry_dto_mapper.dart';
|
||||||
import 'package:backend_dart/common/request_helper.dart';
|
import 'package:backend_dart/common/request_helper.dart';
|
||||||
import 'package:backend_dart/common/response_helpers.dart';
|
import 'package:backend_dart/common/response_helpers.dart';
|
||||||
import 'package:backend_dart/common/validation.dart';
|
import 'package:backend_dart/common/validation.dart';
|
||||||
|
import 'package:backend_dart/domain/repository/auth_repository.dart';
|
||||||
import 'package:backend_dart/domain/repository/time_entry_repository.dart';
|
import 'package:backend_dart/domain/repository/time_entry_repository.dart';
|
||||||
import 'package:shelf/shelf.dart';
|
import 'package:shelf/shelf.dart';
|
||||||
import 'package:shelf_router/shelf_router.dart';
|
import 'package:shelf_router/shelf_router.dart';
|
||||||
@ -13,13 +15,14 @@ part 'time_entry_service.g.dart'; // Generated with 'pub run build_runner build'
|
|||||||
|
|
||||||
class TimeEntryService {
|
class TimeEntryService {
|
||||||
final TimeEntryRepository entries;
|
final TimeEntryRepository entries;
|
||||||
|
final AuthRepository authRepository;
|
||||||
final TimeEntryDtoMapper mapper = TimeEntryDtoMapper();
|
final TimeEntryDtoMapper mapper = TimeEntryDtoMapper();
|
||||||
TimeEntryService(this.entries);
|
TimeEntryService(this.entries, this.authRepository);
|
||||||
|
|
||||||
@Route.get('/')
|
@Route.get('/')
|
||||||
Future<Response> listEntries(Request request) async {
|
Future<Response> listEntries(Request request) async {
|
||||||
return entries
|
return checkAuth(request, authRepository)
|
||||||
.findAll()
|
.flatMap((_) => entries.findAll())
|
||||||
.flatMap(mapper.listTo)
|
.flatMap(mapper.listTo)
|
||||||
.map((entries) => entries.map((entry) => entry.toJson()).toList())
|
.map((entries) => entries.map((entry) => entry.toJson()).toList())
|
||||||
.map(jsonEncode)
|
.map(jsonEncode)
|
||||||
@ -29,8 +32,8 @@ class TimeEntryService {
|
|||||||
|
|
||||||
@Route.get('/<entryId>')
|
@Route.get('/<entryId>')
|
||||||
Future<Response> fetchEntry(Request request, String entryId) async {
|
Future<Response> fetchEntry(Request request, String entryId) async {
|
||||||
return entries
|
return checkAuth(request, authRepository)
|
||||||
.findById(entryId)
|
.flatMap((_) => entries.findById(entryId))
|
||||||
.flatMap(mapper.to)
|
.flatMap(mapper.to)
|
||||||
.map((dto) => dto.toJson())
|
.map((dto) => dto.toJson())
|
||||||
.toResponse()
|
.toResponse()
|
||||||
@ -39,7 +42,8 @@ class TimeEntryService {
|
|||||||
|
|
||||||
@Route.post('/')
|
@Route.post('/')
|
||||||
Future<Response> createEntry(Request request) async {
|
Future<Response> createEntry(Request request) async {
|
||||||
return requestToJson(request)
|
return checkAuth(request, authRepository)
|
||||||
|
.flatMap((_) => requestToJson(request))
|
||||||
.flatMap(
|
.flatMap(
|
||||||
validateJsonKeys(['startTime', 'endTime', 'userId', 'projectId']))
|
validateJsonKeys(['startTime', 'endTime', 'userId', 'projectId']))
|
||||||
.flatMap((json) => decodeJson(json, TimeEntryCreateDto.fromJson))
|
.flatMap((json) => decodeJson(json, TimeEntryCreateDto.fromJson))
|
||||||
@ -53,7 +57,8 @@ class TimeEntryService {
|
|||||||
|
|
||||||
@Route.put('/<entryId>')
|
@Route.put('/<entryId>')
|
||||||
Future<Response> updateEntry(Request request, String entryId) async {
|
Future<Response> updateEntry(Request request, String entryId) async {
|
||||||
return requestToJson(request)
|
return checkAuth(request, authRepository)
|
||||||
|
.flatMap((_) => requestToJson(request))
|
||||||
.flatMap(validateJsonKeys([]))
|
.flatMap(validateJsonKeys([]))
|
||||||
.flatMap((json) => decodeJson(json, TimeEntryUpdateDto.fromJson))
|
.flatMap((json) => decodeJson(json, TimeEntryUpdateDto.fromJson))
|
||||||
.flatMap((dto) => mapper.fromUpdateTo(dto, entryId))
|
.flatMap((dto) => mapper.fromUpdateTo(dto, entryId))
|
||||||
@ -66,8 +71,8 @@ class TimeEntryService {
|
|||||||
|
|
||||||
@Route.delete('/<entryId>')
|
@Route.delete('/<entryId>')
|
||||||
Future<Response> deleteEntry(Request request, String entryId) async {
|
Future<Response> deleteEntry(Request request, String entryId) async {
|
||||||
return entries
|
return checkAuth(request, authRepository)
|
||||||
.delete(entryId)
|
.flatMap((_) => entries.delete(entryId))
|
||||||
.map((dto) => dto.toJson())
|
.map((dto) => dto.toJson())
|
||||||
.toResponse()
|
.toResponse()
|
||||||
.run();
|
.run();
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:backend_dart/application/service/dto/user_dto.dart';
|
import 'package:backend_dart/application/service/dto/user_dto.dart';
|
||||||
|
import 'package:backend_dart/application/service/helper.dart';
|
||||||
import 'package:backend_dart/application/service/mapper/user_dto_mapper.dart';
|
import 'package:backend_dart/application/service/mapper/user_dto_mapper.dart';
|
||||||
import 'package:backend_dart/common/request_helper.dart';
|
import 'package:backend_dart/common/request_helper.dart';
|
||||||
import 'package:backend_dart/common/response_helpers.dart';
|
import 'package:backend_dart/common/response_helpers.dart';
|
||||||
import 'package:backend_dart/common/validation.dart';
|
import 'package:backend_dart/common/validation.dart';
|
||||||
|
import 'package:backend_dart/domain/repository/auth_repository.dart';
|
||||||
import 'package:backend_dart/domain/repository/user_repository.dart';
|
import 'package:backend_dart/domain/repository/user_repository.dart';
|
||||||
import 'package:shelf/shelf.dart';
|
import 'package:shelf/shelf.dart';
|
||||||
import 'package:shelf_router/shelf_router.dart';
|
import 'package:shelf_router/shelf_router.dart';
|
||||||
@ -13,13 +15,15 @@ part 'user_service.g.dart'; // generated with 'pub run build_runner build'
|
|||||||
|
|
||||||
class UserService {
|
class UserService {
|
||||||
final UserRepository users;
|
final UserRepository users;
|
||||||
|
final AuthRepository authRepository;
|
||||||
|
|
||||||
final UserDtoMapper mapper = UserDtoMapper();
|
final UserDtoMapper mapper = UserDtoMapper();
|
||||||
UserService(this.users);
|
UserService(this.users, this.authRepository);
|
||||||
|
|
||||||
@Route.get('/')
|
@Route.get('/')
|
||||||
Future<Response> listUsers(Request request) async {
|
Future<Response> listUsers(Request request) async {
|
||||||
return users
|
return checkAuth(request, authRepository)
|
||||||
.findAll()
|
.flatMap((_) => users.findAll())
|
||||||
.flatMap(mapper.listTo)
|
.flatMap(mapper.listTo)
|
||||||
.map((users) => users.map((user) => user.toJson()).toList())
|
.map((users) => users.map((user) => user.toJson()).toList())
|
||||||
.map(jsonEncode)
|
.map(jsonEncode)
|
||||||
@ -29,8 +33,8 @@ class UserService {
|
|||||||
|
|
||||||
@Route.get('/<userId>')
|
@Route.get('/<userId>')
|
||||||
Future<Response> fetchUser(Request request, String userId) async {
|
Future<Response> fetchUser(Request request, String userId) async {
|
||||||
return users
|
return checkAuth(request, authRepository)
|
||||||
.findById(userId)
|
.flatMap((_) => users.findById(userId))
|
||||||
.flatMap(mapper.to)
|
.flatMap(mapper.to)
|
||||||
.map((dto) => dto.toJson())
|
.map((dto) => dto.toJson())
|
||||||
.toResponse()
|
.toResponse()
|
||||||
@ -48,11 +52,22 @@ class UserService {
|
|||||||
.map((dto) => dto.toJson())
|
.map((dto) => dto.toJson())
|
||||||
.toResponse()
|
.toResponse()
|
||||||
.run();
|
.run();
|
||||||
|
//return checkAuth(request, authRepository) // ignore auth for initial user creation
|
||||||
|
// .flatMap((_) => requestToJson(request))
|
||||||
|
// .flatMap(validateJsonKeys(['name', 'email', 'password']))
|
||||||
|
// .flatMap((json) => decodeJson(json, UserCreateDto.fromJson))
|
||||||
|
// .flatMap(mapper.fromCreateTo)
|
||||||
|
// .flatMap(users.create)
|
||||||
|
// .flatMap(mapper.to)
|
||||||
|
// .map((dto) => dto.toJson())
|
||||||
|
// .toResponse()
|
||||||
|
// .run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Route.put('/<userId>')
|
@Route.put('/<userId>')
|
||||||
Future<Response> updateUser(Request request, String userId) async {
|
Future<Response> updateUser(Request request, String userId) async {
|
||||||
return requestToJson(request)
|
return checkAuth(request, authRepository)
|
||||||
|
.flatMap((_) => requestToJson(request))
|
||||||
.flatMap(validateJsonKeys([]))
|
.flatMap(validateJsonKeys([]))
|
||||||
.flatMap((json) => decodeJson(json, UserUpdateDto.fromJson))
|
.flatMap((json) => decodeJson(json, UserUpdateDto.fromJson))
|
||||||
.flatMap((dto) => mapper.fromUpdateTo(dto, userId))
|
.flatMap((dto) => mapper.fromUpdateTo(dto, userId))
|
||||||
@ -65,8 +80,8 @@ class UserService {
|
|||||||
|
|
||||||
@Route.delete('/<userId>')
|
@Route.delete('/<userId>')
|
||||||
Future<Response> deleteUser(Request request, String userId) async {
|
Future<Response> deleteUser(Request request, String userId) async {
|
||||||
return users
|
return checkAuth(request, authRepository)
|
||||||
.delete(userId)
|
.flatMap((_) => users.delete(userId))
|
||||||
.flatMap(mapper.to)
|
.flatMap(mapper.to)
|
||||||
.map((dto) => dto.toJson())
|
.map((dto) => dto.toJson())
|
||||||
.toResponse()
|
.toResponse()
|
||||||
|
@ -14,8 +14,8 @@ extension TaskEitherResponseExtensions
|
|||||||
|
|
||||||
extension TaskEitherResponseExtensionsFromString on TaskEither<IError, String> {
|
extension TaskEitherResponseExtensionsFromString on TaskEither<IError, String> {
|
||||||
Task<Response> toResponse() => match(
|
Task<Response> toResponse() => match(
|
||||||
(left) => ResponseHelpers.fromError(left),
|
ResponseHelpers.fromError,
|
||||||
(right) => Response.ok(right),
|
Response.ok,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,15 +106,15 @@ func (s *AuthService) Logout(c *gin.Context) {
|
|||||||
|
|
||||||
func setSessionTokenCookie(c *gin.Context, user entities.User) func(token string) string {
|
func setSessionTokenCookie(c *gin.Context, user entities.User) func(token string) string {
|
||||||
return func(token string) string {
|
return func(token string) string {
|
||||||
c.SetCookie("session_token", token, 3600, "/", "localhost", false, true)
|
c.SetCookie("session_token", token, 3600, "/", "", false, true)
|
||||||
c.SetCookie("user_id", user.ID, 3600, "/", "localhost", false, true)
|
c.SetCookie("user_id", user.ID, 3600, "/", "", false, true)
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteSessionTokenCookie(c *gin.Context) {
|
func deleteSessionTokenCookie(c *gin.Context) {
|
||||||
c.SetCookie("session_token", "", -1, "/", "localhost", false, true)
|
c.SetCookie("session_token", "", -1, "/", "", false, true)
|
||||||
c.SetCookie("user_id", "", -1, "/", "localhost", false, true)
|
c.SetCookie("user_id", "", -1, "/", "", false, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validatePassword(password string) func(user entities.User) E.Either[error, entities.User] {
|
func validatePassword(password string) func(user entities.User) E.Either[error, entities.User] {
|
||||||
|
@ -3,7 +3,7 @@ package dto
|
|||||||
// TokenResponseDTO represents the response for a token generation or validation.
|
// TokenResponseDTO represents the response for a token generation or validation.
|
||||||
type TokenResponseDTO struct {
|
type TokenResponseDTO struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"userId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoginRequestDTO represents the login request.
|
// LoginRequestDTO represents the login request.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user