Skip to content

Commit 6105afa

Browse files
committed
refactor(auth): Use exceptions for input validation
- Throws InvalidInputException - Using shared exception types - Improved email regex pattern - Centralized error handling
1 parent 6040abf commit 6105afa

File tree

2 files changed

+46
-45
lines changed

2 files changed

+46
-45
lines changed

routes/api/v1/auth/request-code.dart

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,34 +22,29 @@ Future<Response> onRequest(RequestContext context) async {
2222
try {
2323
body = await context.request.json();
2424
} catch (_) {
25-
// Handle JSON parsing errors
26-
return Response(
27-
statusCode: HttpStatus.badRequest,
28-
body: 'Invalid JSON format in request body.',
29-
);
25+
// Handle JSON parsing errors by throwing
26+
throw const InvalidInputException('Invalid JSON format in request body.');
3027
}
3128

3229
if (body is! Map<String, dynamic>) {
33-
return Response(
34-
statusCode: HttpStatus.badRequest,
35-
body: 'Request body must be a JSON object.',
36-
);
30+
throw const InvalidInputException('Request body must be a JSON object.');
3731
}
3832

3933
final email = body['email'] as String?;
4034
if (email == null || email.isEmpty) {
41-
return Response(
42-
statusCode: HttpStatus.badRequest,
43-
body: 'Missing or empty "email" field in request body.',
35+
throw const InvalidInputException(
36+
'Missing or empty "email" field in request body.',
4437
);
4538
}
4639

4740
// Basic email format check (more robust validation can be added)
48-
if (!RegExp(r'^.+@.+\..+$').hasMatch(email)) {
49-
return Response(
50-
statusCode: HttpStatus.badRequest,
51-
body: 'Invalid email format provided.',
52-
);
41+
// Using a slightly more common regex pattern
42+
final emailRegex = RegExp(
43+
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@"
44+
r'[a-zA-Z0-9]+\.[a-zA-Z]+',
45+
);
46+
if (!emailRegex.hasMatch(email)) {
47+
throw const InvalidInputException('Invalid email format provided.');
5348
}
5449

5550
try {

routes/api/v1/auth/verify-code.dart

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import 'dart:io';
22

33
import 'package:dart_frog/dart_frog.dart';
44
import 'package:ht_api/src/services/auth_service.dart';
5-
import 'package:ht_shared/ht_shared.dart'; // For exceptions and models
5+
// Import exceptions, User, SuccessApiResponse, AND AuthSuccessResponse
6+
import 'package:ht_shared/ht_shared.dart';
67

78
/// Handles POST requests to `/api/v1/auth/verify-code`.
89
///
@@ -22,60 +23,65 @@ Future<Response> onRequest(RequestContext context) async {
2223
try {
2324
body = await context.request.json();
2425
} catch (_) {
25-
return Response(
26-
statusCode: HttpStatus.badRequest,
27-
body: 'Invalid JSON format in request body.',
28-
);
26+
// Handle JSON parsing errors by throwing
27+
throw const InvalidInputException('Invalid JSON format in request body.');
2928
}
3029

3130
if (body is! Map<String, dynamic>) {
32-
return Response(
33-
statusCode: HttpStatus.badRequest,
34-
body: 'Request body must be a JSON object.',
35-
);
31+
throw const InvalidInputException('Request body must be a JSON object.');
3632
}
3733

3834
// Extract and validate email
3935
final email = body['email'] as String?;
4036
if (email == null || email.isEmpty) {
41-
return Response(
42-
statusCode: HttpStatus.badRequest,
43-
body: 'Missing or empty "email" field in request body.',
37+
throw const InvalidInputException(
38+
'Missing or empty "email" field in request body.',
4439
);
4540
}
46-
if (!RegExp(r'^.+@.+\..+$').hasMatch(email)) {
47-
return Response(
48-
statusCode: HttpStatus.badRequest,
49-
body: 'Invalid email format provided.',
50-
);
41+
// Using a slightly more common regex pattern
42+
final emailRegex = RegExp(
43+
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@"
44+
r'[a-zA-Z0-9]+\.[a-zA-Z]+',
45+
);
46+
if (!emailRegex.hasMatch(email)) {
47+
throw const InvalidInputException('Invalid email format provided.');
5148
}
5249

5350
// Extract and validate code
5451
final code = body['code'] as String?;
5552
if (code == null || code.isEmpty) {
56-
return Response(
57-
statusCode: HttpStatus.badRequest,
58-
body: 'Missing or empty "code" field in request body.',
53+
throw const InvalidInputException(
54+
'Missing or empty "code" field in request body.',
5955
);
6056
}
6157
// Basic validation (e.g., check if it's 6 digits)
6258
if (!RegExp(r'^\d{6}$').hasMatch(code)) {
63-
return Response(
64-
statusCode: HttpStatus.badRequest,
65-
body: 'Invalid code format. Code must be 6 digits.',
59+
throw const InvalidInputException(
60+
'Invalid code format. Code must be 6 digits.',
6661
);
6762
}
6863

6964
try {
7065
// Call the AuthService to handle the verification and sign-in logic
7166
final result = await authService.completeEmailSignIn(email, code);
7267

73-
// Return 200 OK with the user and token
68+
// Create the specific payload containing user and token
69+
final authPayload = AuthSuccessResponse(
70+
user: result.user,
71+
token: result.token,
72+
);
73+
74+
// Wrap the payload in the standard SuccessApiResponse
75+
final responsePayload = SuccessApiResponse<AuthSuccessResponse>(
76+
data: authPayload,
77+
// Optionally add metadata if needed/available
78+
// metadata: ResponseMetadata(timestamp: DateTime.now().toUtc()),
79+
);
80+
81+
// Return 200 OK with the standardized, serialized response
7482
return Response.json(
75-
body: {
76-
'user': result.user.toJson(), // Serialize the User object
77-
'token': result.token,
78-
},
83+
// Use the toJson method, providing the toJson factory for the inner type
84+
body: responsePayload.toJson((authSuccess) => authSuccess.toJson()),
7985
);
8086
} on HtHttpException catch (_) {
8187
// Let the central errorHandler middleware handle known exceptions

0 commit comments

Comments
 (0)