implemented auth repository and service in dart (JWT)

This commit is contained in:
Jean Jacques Avril 2025-01-03 22:28:25 +00:00
parent 6cf055db22
commit 05fdefc3e2
No known key found for this signature in database
45 changed files with 868 additions and 46 deletions

View File

@ -0,0 +1,81 @@
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:dart_jsonwebtoken/dart_jsonwebtoken.dart';
import 'package:fpdart/fpdart.dart';
class InMemoryAuthRepositoryImpl implements AuthRepository {
final String secretKey;
final Map<String, String> sessionCache = {};
final Duration tokenExpiry;
InMemoryAuthRepositoryImpl({
required this.secretKey,
this.tokenExpiry = const Duration(hours: 24),
});
@override
TaskEither<IError, String> generateToken(String userId) {
return TaskEither.tryCatch(
() async {
final jwt = JWT({
'userId': userId,
'exp': DateTime.now().add(tokenExpiry).millisecondsSinceEpoch,
});
final token = jwt.sign(SecretKey(secretKey));
// Add to cache
sessionCache[token] = userId;
return token;
},
(error, _) =>
AppError.authenticationError(message: 'Failed to generate token'),
);
}
@override
TaskEither<IError, String> validateToken(String token) {
return TaskEither.tryCatch(
() async {
final jwt = JWT.verify(token, SecretKey(secretKey));
// Extract userId
final userId = jwt.payload['userId'] as String?;
if (userId == null) {
throw AppError.authenticationError(
message: 'Invalid token payload: userId not found');
}
// Check if the token exists in the cache
if (!sessionCache.containsKey(token)) {
throw AppError.authenticationError(
message: 'Token not found in cache');
}
return userId;
},
(error, _) => error is AppError
? error
: AppError.authenticationError(message: 'Failed to validate token'),
);
}
@override
TaskEither<IError, void> revokeToken(String token) {
return TaskEither.tryCatch(
() async {
if (sessionCache.containsKey(token)) {
sessionCache.remove(token);
return;
} else {
throw AppError.authenticationError(message: 'Token not found');
}
},
(error, _) => error is AppError
? error
: AppError.authenticationError(message: 'Failed to revoke token'),
);
}
}

View File

@ -1,6 +1,6 @@
import 'package:backend_dart/domain/data/database.dart'; import 'package:backend_dart/domain/data/database.dart';
import 'package:backend_dart/domain/entities/project.dart'; import 'package:backend_dart/domain/entities/project.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/domain/repository/project_repository.dart'; import 'package:backend_dart/domain/repository/project_repository.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';

View File

@ -1,6 +1,6 @@
import 'package:backend_dart/domain/data/database.dart'; import 'package:backend_dart/domain/data/database.dart';
import 'package:backend_dart/domain/entities/project_task.dart'; import 'package:backend_dart/domain/entities/project_task.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/domain/repository/project_task_repository.dart'; import 'package:backend_dart/domain/repository/project_task_repository.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';

View File

@ -1,7 +1,9 @@
import 'package:backend_dart/application/repository/in_memory_auth_repository_impl.dart';
import 'package:backend_dart/application/repository/project_repository_impl.dart'; import 'package:backend_dart/application/repository/project_repository_impl.dart';
import 'package:backend_dart/application/repository/project_task_repository_impl.dart'; import 'package:backend_dart/application/repository/project_task_repository_impl.dart';
import 'package:backend_dart/application/repository/time_entry_repository_impl.dart'; import 'package:backend_dart/application/repository/time_entry_repository_impl.dart';
import 'package:backend_dart/application/repository/user_repository_impl.dart'; import 'package:backend_dart/application/repository/user_repository_impl.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:backend_dart/domain/repository/project_task_repository.dart'; import 'package:backend_dart/domain/repository/project_task_repository.dart';
import 'package:backend_dart/domain/repository/time_entry_repository.dart'; import 'package:backend_dart/domain/repository/time_entry_repository.dart';
@ -28,3 +30,7 @@ final timeEntryProvider = Provider<TimeEntryRepository>((ref) {
final database = ref.read(databaseProvider); final database = ref.read(databaseProvider);
return TimeEntryRepositoryImpl(database); return TimeEntryRepositoryImpl(database);
}); });
final authProvider = Provider<AuthRepository>((ref) {
return InMemoryAuthRepositoryImpl(secretKey: "Secret");
});

View File

@ -1,6 +1,6 @@
import 'package:backend_dart/domain/data/database.dart'; import 'package:backend_dart/domain/data/database.dart';
import 'package:backend_dart/domain/entities/time_entry.dart'; import 'package:backend_dart/domain/entities/time_entry.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/domain/repository/time_entry_repository.dart'; import 'package:backend_dart/domain/repository/time_entry_repository.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';

View File

@ -1,6 +1,6 @@
import 'package:backend_dart/domain/entities/user.dart'; import 'package:backend_dart/domain/entities/user.dart';
import 'package:backend_dart/domain/data/database.dart'; import 'package:backend_dart/domain/data/database.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.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';

View File

@ -0,0 +1,77 @@
import 'dart:convert';
import 'package:backend_dart/application/service/dto/auth_dto.dart';
import 'package:backend_dart/common/request_helper.dart';
import 'package:backend_dart/common/response_helpers.dart';
import 'package:backend_dart/common/secure_hash.dart';
import 'package:backend_dart/common/validation.dart';
import 'package:backend_dart/domain/entities/user.dart';
import 'package:backend_dart/domain/errors/app_error.dart';
import 'package:backend_dart/domain/repository/auth_repository.dart';
import 'package:backend_dart/domain/repository/user_repository.dart';
import 'package:fpdart/fpdart.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf_router/shelf_router.dart';
part 'auth_service.g.dart';
class AuthService {
final AuthRepository authRepository;
final UserRepository users;
AuthService(this.authRepository, this.users);
/// Route to generate a token
@Route.post('/login')
Future<Response> generateToken(Request request) async {
return requestToJson(request)
.flatMap(validateJsonKeys(['email', 'password']))
.flatMap((json) => decodeJson(json, LoginRequestDTO.fromJson))
.flatMap<User>(
(loginRequest) => users.findByEmail(loginRequest.email).flatMap(
(user) => user.passwordHash ==
generateSecureHash(loginRequest.password)
? TaskEither.right(user)
: TaskEither.left(
AppError.authenticationError(
message: 'Invalid email or password'),
),
),
)
.flatMap((user) => authRepository.generateToken(user.id).map((token) =>
TokenResponseDTO(token: token, userId: user.id).toJson()))
.map(jsonEncode)
.toResponse()
.run();
}
/// Route to validate a token
@Route.post('/validate')
Future<Response> validateToken(Request request) async {
return requestToJson(request)
.flatMap(validateJsonKeys(['token']))
.flatMap((json) => decodeJson(json, TokenRequestDTO.fromJson))
.map((tokenRequest) => tokenRequest.token)
.flatMap((token) => authRepository.validateToken(token).map((userId) =>
TokenResponseDTO(token: token, userId: userId).toJson()))
.map(jsonEncode)
.toResponse()
.run();
}
/// Route to revoke a token
@Route.post('/revoke')
Future<Response> revokeToken(Request request) async {
return requestToJson(request)
.flatMap(validateJsonKeys(['token']))
.flatMap((json) => decodeJson(json, TokenRequestDTO.fromJson))
.map((tokenRequest) => tokenRequest.token)
.flatMap(authRepository.revokeToken)
.map((_) => {'message': 'Token revoked successfully'})
.map(jsonEncode)
.toResponse()
.run();
}
/// Router for the AuthService
Router get router => _$AuthServiceRouter(this);
}

View File

@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'auth_service.dart';
// **************************************************************************
// ShelfRouterGenerator
// **************************************************************************
Router _$AuthServiceRouter(AuthService service) {
final router = Router();
router.add(
'POST',
r'/login',
service.generateToken,
);
router.add(
'POST',
r'/validate',
service.validateToken,
);
router.add(
'POST',
r'/revoke',
service.revokeToken,
);
return router;
}

View File

@ -0,0 +1,42 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'auth_dto.freezed.dart';
part 'auth_dto.g.dart';
/// TokenResponseDTO represents the response for a token generation or validation.
@freezed
class TokenResponseDTO with _$TokenResponseDTO {
const factory TokenResponseDTO({
required String token,
required String userId,
}) = _TokenResponseDTO;
/// JSON serialization
factory TokenResponseDTO.fromJson(Map<String, dynamic> 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.
@freezed
class LoginRequestDTO with _$LoginRequestDTO {
const factory LoginRequestDTO({
required String email,
required String password,
}) = _LoginRequestDTO;
/// JSON serialization
factory LoginRequestDTO.fromJson(Map<String, dynamic> json) =>
_$LoginRequestDTOFromJson(json);
}

View File

@ -0,0 +1,503 @@
// 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_dto.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');
TokenResponseDTO _$TokenResponseDTOFromJson(Map<String, dynamic> json) {
return _TokenResponseDTO.fromJson(json);
}
/// @nodoc
mixin _$TokenResponseDTO {
String get token => throw _privateConstructorUsedError;
String get userId => throw _privateConstructorUsedError;
/// Serializes this TokenResponseDTO to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of TokenResponseDTO
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$TokenResponseDTOCopyWith<TokenResponseDTO> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $TokenResponseDTOCopyWith<$Res> {
factory $TokenResponseDTOCopyWith(
TokenResponseDTO value, $Res Function(TokenResponseDTO) then) =
_$TokenResponseDTOCopyWithImpl<$Res, TokenResponseDTO>;
@useResult
$Res call({String token, String userId});
}
/// @nodoc
class _$TokenResponseDTOCopyWithImpl<$Res, $Val extends TokenResponseDTO>
implements $TokenResponseDTOCopyWith<$Res> {
_$TokenResponseDTOCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of TokenResponseDTO
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? token = null,
Object? userId = null,
}) {
return _then(_value.copyWith(
token: null == token
? _value.token
: token // ignore: cast_nullable_to_non_nullable
as String,
userId: null == userId
? _value.userId
: userId // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$TokenResponseDTOImplCopyWith<$Res>
implements $TokenResponseDTOCopyWith<$Res> {
factory _$$TokenResponseDTOImplCopyWith(_$TokenResponseDTOImpl value,
$Res Function(_$TokenResponseDTOImpl) then) =
__$$TokenResponseDTOImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String token, String userId});
}
/// @nodoc
class __$$TokenResponseDTOImplCopyWithImpl<$Res>
extends _$TokenResponseDTOCopyWithImpl<$Res, _$TokenResponseDTOImpl>
implements _$$TokenResponseDTOImplCopyWith<$Res> {
__$$TokenResponseDTOImplCopyWithImpl(_$TokenResponseDTOImpl _value,
$Res Function(_$TokenResponseDTOImpl) _then)
: super(_value, _then);
/// Create a copy of TokenResponseDTO
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? token = null,
Object? userId = null,
}) {
return _then(_$TokenResponseDTOImpl(
token: null == token
? _value.token
: token // ignore: cast_nullable_to_non_nullable
as String,
userId: null == userId
? _value.userId
: userId // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$TokenResponseDTOImpl implements _TokenResponseDTO {
const _$TokenResponseDTOImpl({required this.token, required this.userId});
factory _$TokenResponseDTOImpl.fromJson(Map<String, dynamic> json) =>
_$$TokenResponseDTOImplFromJson(json);
@override
final String token;
@override
final String userId;
@override
String toString() {
return 'TokenResponseDTO(token: $token, userId: $userId)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$TokenResponseDTOImpl &&
(identical(other.token, token) || other.token == token) &&
(identical(other.userId, userId) || other.userId == userId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, token, userId);
/// Create a copy of TokenResponseDTO
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$TokenResponseDTOImplCopyWith<_$TokenResponseDTOImpl> get copyWith =>
__$$TokenResponseDTOImplCopyWithImpl<_$TokenResponseDTOImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$TokenResponseDTOImplToJson(
this,
);
}
}
abstract class _TokenResponseDTO implements TokenResponseDTO {
const factory _TokenResponseDTO(
{required final String token,
required final String userId}) = _$TokenResponseDTOImpl;
factory _TokenResponseDTO.fromJson(Map<String, dynamic> json) =
_$TokenResponseDTOImpl.fromJson;
@override
String get token;
@override
String get userId;
/// Create a copy of TokenResponseDTO
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$TokenResponseDTOImplCopyWith<_$TokenResponseDTOImpl> get copyWith =>
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) {
return _LoginRequestDTO.fromJson(json);
}
/// @nodoc
mixin _$LoginRequestDTO {
String get email => throw _privateConstructorUsedError;
String get password => throw _privateConstructorUsedError;
/// Serializes this LoginRequestDTO to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of LoginRequestDTO
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$LoginRequestDTOCopyWith<LoginRequestDTO> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LoginRequestDTOCopyWith<$Res> {
factory $LoginRequestDTOCopyWith(
LoginRequestDTO value, $Res Function(LoginRequestDTO) then) =
_$LoginRequestDTOCopyWithImpl<$Res, LoginRequestDTO>;
@useResult
$Res call({String email, String password});
}
/// @nodoc
class _$LoginRequestDTOCopyWithImpl<$Res, $Val extends LoginRequestDTO>
implements $LoginRequestDTOCopyWith<$Res> {
_$LoginRequestDTOCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LoginRequestDTO
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? email = null,
Object? password = null,
}) {
return _then(_value.copyWith(
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
password: null == password
? _value.password
: password // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$LoginRequestDTOImplCopyWith<$Res>
implements $LoginRequestDTOCopyWith<$Res> {
factory _$$LoginRequestDTOImplCopyWith(_$LoginRequestDTOImpl value,
$Res Function(_$LoginRequestDTOImpl) then) =
__$$LoginRequestDTOImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String email, String password});
}
/// @nodoc
class __$$LoginRequestDTOImplCopyWithImpl<$Res>
extends _$LoginRequestDTOCopyWithImpl<$Res, _$LoginRequestDTOImpl>
implements _$$LoginRequestDTOImplCopyWith<$Res> {
__$$LoginRequestDTOImplCopyWithImpl(
_$LoginRequestDTOImpl _value, $Res Function(_$LoginRequestDTOImpl) _then)
: super(_value, _then);
/// Create a copy of LoginRequestDTO
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? email = null,
Object? password = null,
}) {
return _then(_$LoginRequestDTOImpl(
email: null == email
? _value.email
: email // ignore: cast_nullable_to_non_nullable
as String,
password: null == password
? _value.password
: password // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$LoginRequestDTOImpl implements _LoginRequestDTO {
const _$LoginRequestDTOImpl({required this.email, required this.password});
factory _$LoginRequestDTOImpl.fromJson(Map<String, dynamic> json) =>
_$$LoginRequestDTOImplFromJson(json);
@override
final String email;
@override
final String password;
@override
String toString() {
return 'LoginRequestDTO(email: $email, password: $password)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LoginRequestDTOImpl &&
(identical(other.email, email) || other.email == email) &&
(identical(other.password, password) ||
other.password == password));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, email, password);
/// Create a copy of LoginRequestDTO
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LoginRequestDTOImplCopyWith<_$LoginRequestDTOImpl> get copyWith =>
__$$LoginRequestDTOImplCopyWithImpl<_$LoginRequestDTOImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$LoginRequestDTOImplToJson(
this,
);
}
}
abstract class _LoginRequestDTO implements LoginRequestDTO {
const factory _LoginRequestDTO(
{required final String email,
required final String password}) = _$LoginRequestDTOImpl;
factory _LoginRequestDTO.fromJson(Map<String, dynamic> json) =
_$LoginRequestDTOImpl.fromJson;
@override
String get email;
@override
String get password;
/// Create a copy of LoginRequestDTO
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LoginRequestDTOImplCopyWith<_$LoginRequestDTOImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,47 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'auth_dto.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$TokenResponseDTOImpl _$$TokenResponseDTOImplFromJson(
Map<String, dynamic> json) =>
_$TokenResponseDTOImpl(
token: json['token'] as String,
userId: json['userId'] as String,
);
Map<String, dynamic> _$$TokenResponseDTOImplToJson(
_$TokenResponseDTOImpl instance) =>
<String, dynamic>{
'token': instance.token,
'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(
Map<String, dynamic> json) =>
_$LoginRequestDTOImpl(
email: json['email'] as String,
password: json['password'] as String,
);
Map<String, dynamic> _$$LoginRequestDTOImplToJson(
_$LoginRequestDTOImpl instance) =>
<String, dynamic>{
'email': instance.email,
'password': instance.password,
};

View File

@ -1,6 +1,6 @@
import 'package:backend_dart/application/service/dto/project_dto.dart'; import 'package:backend_dart/application/service/dto/project_dto.dart';
import 'package:backend_dart/domain/entities/project.dart'; import 'package:backend_dart/domain/entities/project.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
class ProjectDtoMapper { class ProjectDtoMapper {

View File

@ -1,6 +1,6 @@
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/domain/entities/project_task.dart'; import 'package:backend_dart/domain/entities/project_task.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
class ProjectTaskDtoMapper { class ProjectTaskDtoMapper {

View File

@ -1,6 +1,6 @@
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/domain/entities/time_entry.dart'; import 'package:backend_dart/domain/entities/time_entry.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
class TimeEntryDtoMapper { class TimeEntryDtoMapper {

View File

@ -1,6 +1,6 @@
import 'package:backend_dart/application/service/dto/user_dto.dart'; import 'package:backend_dart/application/service/dto/user_dto.dart';
import 'package:backend_dart/domain/entities/user.dart'; import 'package:backend_dart/domain/entities/user.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
class UserDtoMapper { class UserDtoMapper {

View File

@ -1,4 +1,5 @@
import 'package:backend_dart/application/repository/provider.dart'; import 'package:backend_dart/application/repository/provider.dart';
import 'package:backend_dart/application/service/auth_service.dart';
import 'package:backend_dart/application/service/project_service.dart'; import 'package:backend_dart/application/service/project_service.dart';
import 'package:backend_dart/application/service/project_task_service.dart'; import 'package:backend_dart/application/service/project_task_service.dart';
import 'package:backend_dart/application/service/time_entry_service.dart'; import 'package:backend_dart/application/service/time_entry_service.dart';
@ -24,3 +25,9 @@ final timeEntryServiceProvider = Provider<TimeEntryService>((ref) {
final database = ref.read(timeEntryProvider); final database = ref.read(timeEntryProvider);
return TimeEntryService(database); return TimeEntryService(database);
}); });
final authServiceProvider = Provider<AuthService>((ref) {
final authRepository = ref.read(authProvider);
final users = ref.read(userRepoProvider);
return AuthService(authRepository, users);
});

View File

@ -1,4 +1,4 @@
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
TaskEither<IError, T> Function(T?) errorOnNull<T>(IError error) { TaskEither<IError, T> Function(T?) errorOnNull<T>(IError error) {

View File

@ -2,7 +2,7 @@ import 'dart:convert';
import 'dart:core'; import 'dart:core';
import 'package:backend_dart/domain/errors/app_error.dart'; import 'package:backend_dart/domain/errors/app_error.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
import 'package:shelf/shelf.dart'; import 'package:shelf/shelf.dart';

View File

@ -1,6 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:backend_dart/domain/errors/error_code.dart'; import 'package:backend_dart/domain/errors/error_code.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
import 'package:shelf/shelf.dart'; import 'package:shelf/shelf.dart';

View File

@ -1,5 +1,5 @@
import 'package:backend_dart/domain/entities/project.dart'; import 'package:backend_dart/domain/entities/project.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
/// Interface for managing project data interactions. /// Interface for managing project data interactions.

View File

@ -1,5 +1,5 @@
import 'package:backend_dart/domain/entities/project_task.dart'; import 'package:backend_dart/domain/entities/project_task.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
/// Interface for managing task data interactions. /// Interface for managing task data interactions.

View File

@ -1,5 +1,5 @@
import 'package:backend_dart/domain/entities/time_entry.dart'; import 'package:backend_dart/domain/entities/time_entry.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
/// Interface for managing time entry data interactions. /// Interface for managing time entry data interactions.

View File

@ -1,5 +1,5 @@
import 'package:backend_dart/domain/entities/user.dart'; import 'package:backend_dart/domain/entities/user.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
abstract class UserDataSource { abstract class UserDataSource {

View File

@ -1,4 +1,4 @@
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'error_code.dart'; import 'error_code.dart';
@ -48,4 +48,8 @@ class AppError with _$AppError implements IError {
factory AppError.inputError({String? message}) { factory AppError.inputError({String? message}) {
return AppError.fromErrorCode(ErrorCode.inputError, message: message); return AppError.fromErrorCode(ErrorCode.inputError, message: message);
} }
factory AppError.authenticationError({String? message}) {
return AppError.fromErrorCode(ErrorCode.authenticationError, message: message);
}
} }

View File

@ -3,6 +3,7 @@ enum ErrorCode {
validationError, // Eingabevalidierungsfehler validationError, // Eingabevalidierungsfehler
unexpectedError, // Unerwarteter Fehler unexpectedError, // Unerwarteter Fehler
authenticationError, // Authentifizierungsfehler authenticationError, // Authentifizierungsfehler
authorizationError, // Autorisierungsfehler
permissionDenied, // Berechtigungsfehler permissionDenied, // Berechtigungsfehler
notFound, // Ressource nicht gefunden notFound, // Ressource nicht gefunden
exception, // Ausnahme exception, // Ausnahme

View File

@ -1,16 +0,0 @@
import 'package:backend_dart/domain/interface/error.dart';
import 'package:fpdart/fpdart.dart';
abstract class IMapper<U, V> {
/// Konvertiert von Typ U (Origin) zu Typ V (Target)
TaskEither<IError, V> to(U origin);
/// Konvertiert von Typ V (Target) zu Typ U (Origin)
TaskEither<IError, U> from(V target);
/// Konvertiert eine Liste von Typ U (Origin) zu einer Liste von Typ V (Target)
TaskEither<IError, List<V>> listTo(Iterable<U> origins);
/// Konvertiert eine Liste von Typ V (Target) zu einer Liste von Typ U (Origin)
TaskEither<IError, List<U>> listFrom(Iterable<V> targets);
}

View File

@ -0,0 +1,8 @@
import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart';
abstract class AuthRepository {
TaskEither<IError, String> generateToken(String userId);
TaskEither<IError, String> validateToken(String token);
TaskEither<IError, void> revokeToken(String token);
}

View File

@ -1,6 +1,6 @@
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
import 'package:backend_dart/domain/entities/project.dart'; import 'package:backend_dart/domain/entities/project.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
abstract class ProjectRepository { abstract class ProjectRepository {
/// Creates a new project. /// Creates a new project.

View File

@ -1,6 +1,6 @@
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
import 'package:backend_dart/domain/entities/project_task.dart'; import 'package:backend_dart/domain/entities/project_task.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
abstract class ProjectTaskRepository { abstract class ProjectTaskRepository {
/// Creates a new project task. /// Creates a new project task.

View File

@ -1,6 +1,6 @@
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
import 'package:backend_dart/domain/entities/time_entry.dart'; import 'package:backend_dart/domain/entities/time_entry.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
abstract class TimeEntryRepository { abstract class TimeEntryRepository {
/// Creates a new time entry. /// Creates a new time entry.

View File

@ -1,5 +1,5 @@
import 'package:backend_dart/domain/entities/user.dart'; import 'package:backend_dart/domain/entities/user.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';
abstract class UserRepository { abstract class UserRepository {

View File

@ -1,6 +1,6 @@
import 'package:backend_dart/common/extensions.dart'; import 'package:backend_dart/common/extensions.dart';
import 'package:backend_dart/domain/entities/project.dart'; import 'package:backend_dart/domain/entities/project.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/infrastructure/persistence/db/model.dart'; import 'package:backend_dart/infrastructure/persistence/db/model.dart';
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart'; import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';

View File

@ -1,6 +1,6 @@
import 'package:backend_dart/common/extensions.dart'; import 'package:backend_dart/common/extensions.dart';
import 'package:backend_dart/domain/entities/project_task.dart'; import 'package:backend_dart/domain/entities/project_task.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/infrastructure/persistence/db/model.dart'; import 'package:backend_dart/infrastructure/persistence/db/model.dart';
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart'; import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';

View File

@ -1,6 +1,6 @@
import 'package:backend_dart/common/extensions.dart'; import 'package:backend_dart/common/extensions.dart';
import 'package:backend_dart/domain/entities/time_entry.dart'; import 'package:backend_dart/domain/entities/time_entry.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/infrastructure/persistence/db/model.dart'; import 'package:backend_dart/infrastructure/persistence/db/model.dart';
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart'; import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';

View File

@ -1,7 +1,7 @@
import 'package:backend_dart/common/extensions.dart'; import 'package:backend_dart/common/extensions.dart';
import 'package:backend_dart/common/secure_hash.dart'; import 'package:backend_dart/common/secure_hash.dart';
import 'package:backend_dart/domain/entities/user.dart'; import 'package:backend_dart/domain/entities/user.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/infrastructure/persistence/db/model.dart'; import 'package:backend_dart/infrastructure/persistence/db/model.dart';
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart'; import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';
import 'package:fpdart/fpdart.dart'; import 'package:fpdart/fpdart.dart';

View File

@ -2,7 +2,7 @@ import 'package:backend_dart/common/error_on_null.dart';
import 'package:backend_dart/domain/data/project_data_source.dart'; import 'package:backend_dart/domain/data/project_data_source.dart';
import 'package:backend_dart/domain/entities/project.dart'; import 'package:backend_dart/domain/entities/project.dart';
import 'package:backend_dart/domain/errors/app_error.dart'; import 'package:backend_dart/domain/errors/app_error.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/infrastructure/persistence/db/client.dart'; import 'package:backend_dart/infrastructure/persistence/db/client.dart';
import 'package:backend_dart/infrastructure/persistence/db/model.dart'; import 'package:backend_dart/infrastructure/persistence/db/model.dart';
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart'; import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';

View File

@ -2,7 +2,7 @@ import 'package:backend_dart/common/error_on_null.dart';
import 'package:backend_dart/domain/data/project_task_data_source.dart'; import 'package:backend_dart/domain/data/project_task_data_source.dart';
import 'package:backend_dart/domain/entities/project_task.dart'; import 'package:backend_dart/domain/entities/project_task.dart';
import 'package:backend_dart/domain/errors/app_error.dart'; import 'package:backend_dart/domain/errors/app_error.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/infrastructure/persistence/db/client.dart'; import 'package:backend_dart/infrastructure/persistence/db/client.dart';
import 'package:backend_dart/infrastructure/persistence/db/model.dart'; import 'package:backend_dart/infrastructure/persistence/db/model.dart';
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart'; import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';

View File

@ -2,7 +2,7 @@ import 'package:backend_dart/common/error_on_null.dart';
import 'package:backend_dart/domain/data/time_entry_data_source.dart'; import 'package:backend_dart/domain/data/time_entry_data_source.dart';
import 'package:backend_dart/domain/entities/time_entry.dart'; import 'package:backend_dart/domain/entities/time_entry.dart';
import 'package:backend_dart/domain/errors/app_error.dart'; import 'package:backend_dart/domain/errors/app_error.dart';
import 'package:backend_dart/domain/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/infrastructure/persistence/db/client.dart'; import 'package:backend_dart/infrastructure/persistence/db/client.dart';
import 'package:backend_dart/infrastructure/persistence/db/model.dart'; import 'package:backend_dart/infrastructure/persistence/db/model.dart';
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart'; import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';

View File

@ -2,7 +2,7 @@ import 'package:backend_dart/common/error_on_null.dart';
import 'package:backend_dart/domain/data/user_data_source.dart'; import 'package:backend_dart/domain/data/user_data_source.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/interface/error.dart'; import 'package:backend_dart/domain/errors/error.dart';
import 'package:backend_dart/infrastructure/persistence/db/model.dart'; import 'package:backend_dart/infrastructure/persistence/db/model.dart';
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart'; import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';
import 'package:backend_dart/infrastructure/persistence/mapper/user_dbo_mapper.dart'; import 'package:backend_dart/infrastructure/persistence/mapper/user_dbo_mapper.dart';

View File

@ -20,12 +20,14 @@ Router getRouter(ProviderContainer container) {
final projectService = container.read(projectServiceProvider); final projectService = container.read(projectServiceProvider);
final projectTaskService = container.read(projectTaskServiceProvider); final projectTaskService = container.read(projectTaskServiceProvider);
final timeEntryService = container.read(timeEntryServiceProvider); final timeEntryService = container.read(timeEntryServiceProvider);
final authService = container.read(authServiceProvider);
// UserService-Router // UserService-Router
router.mount('/api/users/', userService.router.call); router.mount('/api/users/', userService.router.call);
router.mount('/api/projects/', projectService.router.call); router.mount('/api/projects/', projectService.router.call);
router.mount('/api/project-tasks/', projectTaskService.router.call); router.mount('/api/project-tasks/', projectTaskService.router.call);
router.mount('/api/time-entries/', timeEntryService.router.call); router.mount('/api/time-entries/', timeEntryService.router.call);
router.mount('/api/auth/', authService.router.call);
return router; return router;
} }

View File

@ -14,6 +14,14 @@ packages:
description: dart description: dart
source: sdk source: sdk
version: "0.3.3" version: "0.3.3"
adaptive_number:
dependency: transitive
description:
name: adaptive_number
sha256: "3a567544e9b5c9c803006f51140ad544aedc79604fd4f3f2c1380003f97c1d77"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
@ -238,6 +246,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0+6.11.0" version: "1.0.0+6.11.0"
dart_jsonwebtoken:
dependency: "direct main"
description:
name: dart_jsonwebtoken
sha256: "866787dc17afaef46a9ea7dd33eefe60c6d82084b4a36d70e8e788d091cd04ef"
url: "https://pub.dev"
source: hosted
version: "2.14.2"
dart_style: dart_style:
dependency: transitive dependency: transitive
description: description:
@ -254,6 +270,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "3.0.2"
ed25519_edwards:
dependency: transitive
description:
name: ed25519_edwards
sha256: "6ce0112d131327ec6d42beede1e5dfd526069b18ad45dcf654f15074ad9276cd"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
file: file:
dependency: transitive dependency: transitive
description: description:
@ -478,6 +502,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.dev"
source: hosted
version: "3.9.1"
pool: pool:
dependency: transitive dependency: transitive
description: description:

View File

@ -22,6 +22,7 @@ dependencies:
freezed_annotation: ^2.4.4 freezed_annotation: ^2.4.4
uuid: ^4.5.1 uuid: ^4.5.1
crypto: ^3.0.6 crypto: ^3.0.6
dart_jsonwebtoken: ^2.14.2
dev_dependencies: dev_dependencies:
lints: ^3.0.0 lints: ^3.0.0

View File

@ -28,7 +28,7 @@ func main() {
projectRepo := repository.NewProjectRepository(database.Projects()) projectRepo := repository.NewProjectRepository(database.Projects())
projectTaskRepo := repository.NewProjectTaskRepository(database.ProjectTasks()) projectTaskRepo := repository.NewProjectTaskRepository(database.ProjectTasks())
timeEntryRepo := repository.NewTimeEntryRepository(database.TimeEntries()) timeEntryRepo := repository.NewTimeEntryRepository(database.TimeEntries())
authRepo := repository.NewInMemoryAuthRepository("secret") authRepo := repository.NewInMemoryAuthRepositoryImpl("secret")
// Initialize services // Initialize services
userService := services.NewUserService(userRepo) userService := services.NewUserService(userRepo)

View File

@ -19,7 +19,7 @@ type InMemoryAuthRepository struct {
mu sync.RWMutex mu sync.RWMutex
} }
func NewInMemoryAuthRepository(secretKey string) repository.AuthRepository { func NewInMemoryAuthRepositoryImpl(secretKey string) repository.AuthRepository {
return &InMemoryAuthRepository{ return &InMemoryAuthRepository{
secretKey: secretKey, secretKey: secretKey,
sessionCache: make(map[string]string), sessionCache: make(map[string]string),