@feardread/fear 1.2.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,5 @@
1
1
  const User = require("../../models/user");
2
2
  const TokenService = require('./token');
3
- const handler = require('../../libs/handler');
4
3
  const validator = require('../../libs/validator');
5
4
  const logger = require('../../libs/logger');
6
5
 
@@ -18,15 +17,8 @@ const response = {
18
17
  * @param {string} message - Success message
19
18
  */
20
19
  success: (res, user, token, statusCode = 200, message = "Authentication successful") => {
21
- // Remove sensitive data from user object
22
- const userResponse = {
23
- _id: user._id,
24
- email: user.email,
25
- name: user.name,
26
- role: user.role,
27
- createdAt: user.createdAt,
28
- updatedAt: user.updatedAt
29
- };
20
+ // Use the model's toJSON method which automatically removes sensitive data
21
+ const userResponse = user.toJSON();
30
22
 
31
23
  return res
32
24
  .status(statusCode)
@@ -64,34 +56,72 @@ const response = {
64
56
  * @description Validates user credentials (email/password) and returns JWT token for authenticated user
65
57
  * @tags authentication
66
58
  */
67
- exports.login = async (req, res) => {
59
+ exports.login = (req, res) => {
68
60
  const { email, password } = req.body;
69
61
 
70
62
  // Validate input
71
63
  const validation = validator.input.login({ email, password });
72
64
  if (!validation.isValid) {
73
- return response.sendError(res, 400, validation.message);
65
+ return response.error(res, 400, validation.message);
74
66
  }
75
67
 
76
- console.log('Authentication attempt for:', email);
77
- await User.findOne({ email }).select('+password')
78
- .then((user) => {
79
- if (!user) return response.error(res, 401, "Invalid credentials");
68
+ logger.info('Authentication attempt for:', email);
69
+
70
+ // Find user and include password field
71
+ User.findOne({
72
+ email: email.toLowerCase(),
73
+ status: { $ne: 'deleted' }
74
+ })
75
+ .select('+password')
76
+ .then(user => {
77
+ if (!user) {
78
+ return response.error(res, 401, "Invalid credentials");
79
+ }
80
+
81
+ // Check if account is locked
82
+ if (user.isLocked()) {
83
+ return response.error(
84
+ res,
85
+ 423,
86
+ "Account temporarily locked due to too many failed login attempts. Please try again later."
87
+ );
88
+ }
89
+
90
+ // Check if account is suspended or inactive
91
+ if (user.status === 'suspended') {
92
+ return response.error(res, 403, "Your account has been suspended. Please contact support.");
93
+ }
94
+
95
+ if (user.status === 'inactive') {
96
+ return response.error(res, 403, "Your account is inactive. Please contact support to reactivate.");
97
+ }
80
98
 
81
99
  // Verify password
82
- user.compare(password)
83
- .then((isPasswordValid) => {
100
+ return user.comparePassword(password)
101
+ .then(isPasswordValid => {
84
102
  if (!isPasswordValid) {
85
- return response.error(res, 401, "Invalid credentials");
103
+ return user.incLoginAttempts()
104
+ .then(() => {
105
+ return response.error(res, 401, "Invalid credentials");
106
+ });
86
107
  }
87
108
 
88
- const token = TokenService.generateToken(user);
89
- return response.success(res, user, token, 200, "Login successful");
90
- })
91
- .catch(err => response.error(res, 500, 'Server Error'))
92
-
109
+ // Reset login attempts and update last login
110
+ return user.resetLoginAttempts()
111
+ .then(() => {
112
+ return user.updateOne({
113
+ lastLoginAt: new Date(),
114
+ lastLoginIP: req.ip || req.connection.remoteAddress
115
+ });
116
+ })
117
+ .then(() => {
118
+ // Generate token
119
+ const token = TokenService.generateToken(user);
120
+ return response.success(res, user, token, 200, "Login successful");
121
+ });
122
+ });
93
123
  })
94
- .catch((error) => {
124
+ .catch(error => {
95
125
  logger.error('Login error:', error);
96
126
  return response.error(res, 500, "Authentication failed", error.message);
97
127
  });
@@ -103,50 +133,77 @@ exports.login = async (req, res) => {
103
133
  * @description Creates a new user account with provided information and returns JWT token
104
134
  * @tags authentication
105
135
  */
106
- exports.register = async (req, res) => {
107
- const { email, firstname, lastname, name: providedName, password, ...otherFields } = req.body;
136
+ exports.register = (req, res) => {
137
+ const {
138
+ email,
139
+ password,
140
+ firstName,
141
+ lastName,
142
+ displayName,
143
+ phoneNumber,
144
+ dateOfBirth,
145
+ ...otherFields
146
+ } = req.body;
108
147
 
109
148
  // Validate input
110
149
  const validation = validator.input.register({
111
- email, firstname, lastname, name: providedName, password
150
+ email,
151
+ password,
152
+ firstName,
153
+ lastName
112
154
  });
113
155
 
114
156
  if (!validation.isValid) {
115
157
  return response.error(res, 400, validation.message);
116
158
  }
117
159
 
118
- try {
119
- // Check if user already exists
120
- const existingUser = await User.findOne({ email });
121
- if (existingUser) {
122
- return response.error(res, 409, "User with this email already exists");
123
- }
160
+ // Check if user already exists
161
+ User.findOne({ email: email.toLowerCase() })
162
+ .then(existingUser => {
163
+ if (existingUser) {
164
+ return response.error(res, 409, "User with this email already exists");
165
+ }
166
+
167
+ // Create new user with nested profile structure
168
+ const userData = {
169
+ email: email.toLowerCase(),
170
+ password,
171
+ firstName: firstName.trim(),
172
+ lastName: lastName.trim(),
173
+ displayName: displayName || `${firstName} ${lastName}`,
174
+ mobile: phoneNumber || undefined,
175
+ dateOfBirth: dateOfBirth || undefined,
176
+ role: otherFields.role || 'user',
177
+ status: 'active'
178
+ };
179
+
180
+ return User.create(userData);
181
+ })
182
+ .then(user => {
183
+ if (!user) return; // Already handled in previous then
124
184
 
125
- // Create new user
126
- const userData = {
127
- ...otherFields,
128
- email: email.toLowerCase(),
129
- name: validation.name,
130
- password,
131
- role: otherFields.role || 'user' // Default role
132
- };
185
+ logger.info('New user registered:', user.email);
133
186
 
134
- const user = await User.create(userData);
187
+ // Generate token and send response
188
+ const token = TokenService.generateToken(user);
189
+ return response.success(res, user, token, 201, "Registration successful");
190
+ })
191
+ .catch(error => {
192
+ logger.error('Registration error:', error);
135
193
 
136
- // Generate token and send response
137
- const token = TokenService.generateToken(user);
138
- return response.success(res, user, token, 201, "Registration successful");
194
+ // Handle duplicate key error (in case of race condition)
195
+ if (error.code === 11000) {
196
+ return response.error(res, 409, "User with this email already exists");
197
+ }
139
198
 
140
- } catch (error) {
141
- console.error('Registration error:', error);
199
+ // Handle validation errors
200
+ if (error.name === 'ValidationError') {
201
+ const messages = Object.values(error.errors).map(err => err.message);
202
+ return response.error(res, 400, messages.join(', '));
203
+ }
142
204
 
143
- // Handle duplicate key error (in case of race condition)
144
- if (error.code === 11000) {
145
- return response.error(res, 409, "User with this email already exists");
146
- }
147
-
148
- return response.error(res, 500, "Registration failed", error.message);
149
- }
205
+ return response.error(res, 500, "Registration failed", error.message);
206
+ });
150
207
  };
151
208
 
152
209
  /**
@@ -175,18 +232,12 @@ exports.logout = async (req, res) => {
175
232
  * @description Returns the current user's profile information
176
233
  * @tags authentication
177
234
  */
178
- exports.getCurrentUser = async (req, res) => {
235
+ exports.getCurrentUser = (req, res) => {
179
236
  // req.user is set by isAuthorized middleware
180
237
  const user = req.user;
181
238
 
182
- const userResponse = {
183
- _id: user._id,
184
- email: user.email,
185
- name: user.name,
186
- role: user.role,
187
- createdAt: user.createdAt,
188
- updatedAt: user.updatedAt
189
- };
239
+ // Use toJSON to automatically remove sensitive fields
240
+ const userResponse = user.toJSON();
190
241
 
191
242
  return res.status(200).json({
192
243
  success: true,
@@ -200,24 +251,116 @@ exports.getCurrentUser = async (req, res) => {
200
251
  * @description Generates a new JWT token for authenticated user
201
252
  * @tags authentication
202
253
  */
203
- exports.refreshToken = async (req, res) => {
254
+ exports.refreshToken = (req, res) => {
204
255
  const user = req.user; // Set by isAuthorized middleware
205
256
 
206
257
  const newToken = TokenService.generateToken(user);
207
258
  return response.success(res, user, newToken, 200, "Token refreshed successfully");
208
259
  };
209
260
 
261
+ /**
262
+ * PUT /fear/api/auth/update-profile
263
+ * @summary Update user profile information
264
+ * @description Updates the authenticated user's profile data
265
+ * @tags authentication
266
+ */
267
+ exports.updateProfile = (req, res) => {
268
+ const { firstName, lastName, displayName, phoneNumber, bio, dateOfBirth, avatar } = req.body;
269
+
270
+ User.findById(req.user._id)
271
+ .then(user => {
272
+ if (!user) {
273
+ return response.error(res, 404, "User not found");
274
+ }
275
+
276
+ // Update profile fields if provided
277
+ if (firstName) user.firstName = firstName.trim();
278
+ if (lastName) user.lastName = lastName.trim();
279
+ if (displayName !== undefined) user.displayName = displayName.trim();
280
+ if (phoneNumber !== undefined) user.phoneNumber = phoneNumber;
281
+ if (bio !== undefined) user.bio = bio;
282
+ if (dateOfBirth !== undefined) user.dateOfBirth = dateOfBirth;
283
+ if (avatar !== undefined) user.avatar = avatar;
284
+
285
+ return user.save();
286
+ })
287
+ .then(user => {
288
+ if (!user) return; // Already handled
289
+
290
+ return res.status(200).json({
291
+ success: true,
292
+ message: "Profile updated successfully",
293
+ data: { user: user.toJSON() }
294
+ });
295
+ })
296
+ .catch(error => {
297
+ logger.error('Profile update error:', error);
298
+
299
+ if (error.name === 'ValidationError') {
300
+ const messages = Object.values(error.errors).map(err => err.message);
301
+ return response.error(res, 400, messages.join(', '));
302
+ }
303
+
304
+ return response.error(res, 500, "Profile update failed", error.message);
305
+ });
306
+ };
307
+
308
+ /**
309
+ * PUT /fear/api/auth/update-preferences
310
+ * @summary Update user preferences
311
+ * @description Updates the authenticated user's preferences (language, timezone, notifications, theme)
312
+ * @tags authentication
313
+ */
314
+ exports.updatePreferences = (req, res) => {
315
+ const { language, timezone, notifications, theme } = req.body;
316
+
317
+ User.findById(req.user._id)
318
+ .then(user => {
319
+ if (!user) {
320
+ return response.error(res, 404, "User not found");
321
+ }
322
+
323
+ // Update preferences
324
+ if (language) user.preferences.language = language;
325
+ if (timezone) user.preferences.timezone = timezone;
326
+ if (theme) user.preferences.theme = theme;
327
+ if (notifications) {
328
+ user.preferences.notifications = {
329
+ ...user.preferences.notifications,
330
+ ...notifications
331
+ };
332
+ }
333
+
334
+ return user.save();
335
+ })
336
+ .then(user => {
337
+ if (!user) return; // Already handled
338
+
339
+ return res.status(200).json({
340
+ success: true,
341
+ message: "Preferences updated successfully",
342
+ data: {
343
+ preferences: user.preferences
344
+ }
345
+ });
346
+ })
347
+ .catch(error => {
348
+ logger.error('Preferences update error:', error);
349
+ return response.error(res, 500, "Preferences update failed", error.message);
350
+ });
351
+ };
352
+
210
353
  /**
211
354
  * Middleware: Verify JWT token and authenticate user
212
355
  * @summary Checks if user has valid JWT token in cookies or Authorization header
213
356
  * @description Validates JWT token and attaches user object to request
214
357
  * @tags middleware, authentication
215
358
  */
216
- exports.isAuthorized = async (req, res, next) => {
359
+ exports.isAuthorized = (req, res, next) => {
217
360
  let token;
218
361
 
219
362
  // Check for token in cookies first, then Authorization header
220
- if (req.cookies?.jwt) {
363
+ if (req && req.cookies?.jwt) {
221
364
  token = req.cookies.jwt;
222
365
  } else if (req.headers.authorization?.startsWith('Bearer ')) {
223
366
  token = req.headers.authorization.split(' ')[1];
@@ -232,23 +375,37 @@ exports.isAuthorized = async (req, res, next) => {
232
375
  const decodedToken = TokenService.verifyToken(token);
233
376
 
234
377
  // Get user from database
235
- const user = await User.findById(decodedToken.id);
236
- if (!user) {
237
- return response.error(res, 401, "Invalid token. User not found.");
238
- }
378
+ User.findById(decodedToken.id)
379
+ .then(user => {
380
+ if (!user) {
381
+ return response.error(res, 401, "Invalid token. User not found.");
382
+ }
239
383
 
240
- // Check if user is still active (optional)
241
- if (user.isActive === false) {
242
- return response.error(res, 401, "Account has been deactivated.");
243
- }
384
+ // Check user status
385
+ if (user.status === 'deleted') {
386
+ return response.error(res, 401, "Account has been deleted.");
387
+ }
244
388
 
245
- // Attach user to request
246
- req.user = user;
247
- req.token = token;
248
- next();
389
+ if (user.status === 'suspended') {
390
+ return response.error(res, 403, "Account has been suspended.");
391
+ }
392
+
393
+ if (user.status === 'inactive') {
394
+ return response.error(res, 403, "Account is inactive.");
395
+ }
396
+
397
+ // Attach user to request
398
+ req.user = user;
399
+ req.token = token;
400
+ next();
401
+ })
402
+ .catch(error => {
403
+ logger.error('Authorization error:', error);
404
+ return response.error(res, 500, "Authentication failed", error.message);
405
+ });
249
406
 
250
407
  } catch (error) {
251
- console.error('Authorization error:', error);
408
+ logger.error('Authorization error:', error);
252
409
 
253
410
  if (error.name === 'JsonWebTokenError') {
254
411
  return response.error(res, 401, "Invalid token.");
@@ -268,7 +425,7 @@ exports.isAuthorized = async (req, res, next) => {
268
425
  * @description Checks if req.user.role equals 'admin'. Must be used after isAuthorized middleware
269
426
  * @tags middleware, authorization
270
427
  */
271
- exports.isAdmin = async (req, res, next) => {
428
+ exports.isAdmin = (req, res, next) => {
272
429
  if (!req.user) {
273
430
  return response.error(res, 401, "Authentication required");
274
431
  }
@@ -289,7 +446,7 @@ exports.isAdmin = async (req, res, next) => {
289
446
  * @returns {function} Express middleware function
290
447
  */
291
448
  exports.authorizeRoles = (...roles) => {
292
- return handler.async(async (req, res, next) => {
449
+ return (req, res, next) => {
293
450
  if (!req.user) {
294
451
  return response.error(res, 401, "Authentication required");
295
452
  }
@@ -303,7 +460,7 @@ exports.authorizeRoles = (...roles) => {
303
460
  }
304
461
 
305
462
  next();
306
- });
463
+ };
307
464
  };
308
465
 
309
466
  /**
@@ -312,7 +469,7 @@ exports.authorizeRoles = (...roles) => {
312
469
  * @description Useful for routes that behave differently for authenticated vs anonymous users
313
470
  * @tags middleware, authentication
314
471
  */
315
- exports.optionalAuth = async (req, res, next) => {
472
+ exports.optionalAuth = (req, res, next) => {
316
473
  let token;
317
474
 
318
475
  if (req.cookies?.jwt) {
@@ -327,19 +484,268 @@ exports.optionalAuth = async (req, res, next) => {
327
484
 
328
485
  try {
329
486
  const decodedToken = TokenService.verifyToken(token);
330
- const user = await User.findById(decodedToken.id);
331
-
332
- if (user && user.isActive !== false) {
333
- req.user = user;
334
- req.token = token;
335
- }
487
+
488
+ User.findById(decodedToken.id)
489
+ .then(user => {
490
+ if (user && user.status === 'active') {
491
+ req.user = user;
492
+ req.token = token;
493
+ }
494
+ next();
495
+ })
496
+ .catch(error => {
497
+ // Silently fail for optional auth
498
+ logger.debug('Optional auth failed:', error.message);
499
+ next();
500
+ });
336
501
  } catch (error) {
337
502
  // Silently fail for optional auth
338
- console.log('Optional auth failed:', error.message);
503
+ logger.debug('Optional auth failed:', error.message);
504
+ next();
339
505
  }
506
+ };
507
+ /**
508
+ * POST /fear/api/auth/google
509
+ * @summary Authenticate or register user via Google OAuth
510
+ * @description Handles Google OAuth login/registration. Creates new user if doesn't exist, or logs in existing user
511
+ * @tags authentication, oauth
512
+ */
513
+ exports.googleAuth = (req, res) => {
514
+ const { googleToken, googleId, email, firstName, lastName, avatar } = req.body;
340
515
 
341
- next();
516
+ // Validate required fields
517
+ if (!email || !googleId) {
518
+ return response.error(res, 400, "Google ID and email are required");
519
+ }
520
+
521
+ // Validate email format (assuming validator has email method)
522
+ const emailValidation = validator.input.email ? validator.input.email(email) : { isValid: true };
523
+ if (!emailValidation.isValid) {
524
+ return response.error(res, 400, "Invalid email format");
525
+ }
526
+
527
+ logger.info('Google authentication attempt for:', email);
528
+
529
+ // Find user by email or googleId
530
+ User.findOne({
531
+ $or: [
532
+ { email: email.toLowerCase() },
533
+ { 'oauth.google.id': googleId }
534
+ ],
535
+ status: { $ne: 'deleted' }
536
+ })
537
+ .then(user => {
538
+ // If user exists - login flow
539
+ if (user) {
540
+ // Check account status
541
+ if (user.status === 'suspended') {
542
+ return response.error(res, 403, "Your account has been suspended. Please contact support.");
543
+ }
544
+
545
+ if (user.status === 'inactive') {
546
+ return response.error(res, 403, "Your account is inactive. Please contact support to reactivate.");
547
+ }
548
+
549
+ // Update Google OAuth info if not already set
550
+ if (!user.oauth || !user.oauth.google || !user.oauth.google.id) {
551
+ user.oauth = user.oauth || {};
552
+ user.oauth.google = {
553
+ id: googleId,
554
+ email: email.toLowerCase(),
555
+ connectedAt: new Date()
556
+ };
557
+ }
558
+
559
+ // Update last login info
560
+ user.lastLoginAt = new Date();
561
+ user.lastLoginIP = req.ip || req.connection.remoteAddress;
562
+
563
+ // Update avatar if provided and user doesn't have one
564
+ if (avatar && !user.avatar) {
565
+ user.avatar = avatar;
566
+ }
567
+
568
+ return user.save()
569
+ .then(savedUser => {
570
+ logger.info('Google login successful for:', savedUser.email);
571
+
572
+ // Generate token and send response
573
+ const token = TokenService.generateToken(savedUser);
574
+ return response.success(res, savedUser, token, 200, "Google login successful");
575
+ });
576
+ }
577
+
578
+ // User doesn't exist - registration flow
579
+ const userData = {
580
+ email: email.toLowerCase(),
581
+ firstName: firstName?.trim() || 'User',
582
+ lastName: lastName?.trim() || '',
583
+ displayName: firstName && lastName ? `${firstName} ${lastName}`.trim() : email.split('@')[0],
584
+ avatar: avatar || undefined,
585
+ role: 'user',
586
+ status: 'active',
587
+ oauth: {
588
+ google: {
589
+ id: googleId,
590
+ email: email.toLowerCase(),
591
+ connectedAt: new Date()
592
+ }
593
+ },
594
+ isEmailVerified: true, // Google emails are already verified
595
+ lastLoginAt: new Date(),
596
+ lastLoginIP: req.ip || req.connection.remoteAddress
597
+ };
598
+
599
+ // Create new user (no password required for OAuth users)
600
+ return User.create(userData)
601
+ .then(newUser => {
602
+ logger.info('New user registered via Google:', newUser.email);
603
+
604
+ // Generate token and send response
605
+ const token = TokenService.generateToken(newUser);
606
+ return response.success(res, newUser, token, 201, "Google registration successful");
607
+ });
608
+ })
609
+ .catch(error => {
610
+ logger.error('Google authentication error:', error);
611
+
612
+ // Handle duplicate key error
613
+ if (error.code === 11000) {
614
+ return response.error(res, 409, "User with this email already exists");
615
+ }
616
+
617
+ // Handle validation errors
618
+ if (error.name === 'ValidationError') {
619
+ const messages = Object.values(error.errors).map(err => err.message);
620
+ return response.error(res, 400, messages.join(', '));
621
+ }
622
+
623
+ return response.error(res, 500, "Google authentication failed", error.message);
624
+ });
342
625
  };
343
626
 
627
+ /**
628
+ * POST /fear/api/auth/google/link
629
+ * @summary Link Google account to existing authenticated user
630
+ * @description Connects a Google OAuth account to the currently logged-in user
631
+ * @tags authentication, oauth
632
+ */
633
+ exports.linkGoogleAccount = (req, res) => {
634
+ const { googleId, email } = req.body;
635
+ const userId = req.user._id; // Set by isAuthorized middleware
636
+
637
+ if (!googleId || !email) {
638
+ return response.error(res, 400, "Google ID and email are required");
639
+ }
640
+
641
+ // Check if Google account is already linked to another user
642
+ User.findOne({
643
+ 'oauth.google.id': googleId,
644
+ _id: { $ne: userId }
645
+ })
646
+ .then(existingUser => {
647
+ if (existingUser) {
648
+ return response.error(res, 409, "This Google account is already linked to another user");
649
+ }
650
+
651
+ // Get current user
652
+ return User.findById(userId);
653
+ })
654
+ .then(user => {
655
+ if (!user) {
656
+ return response.error(res, 404, "User not found");
657
+ }
658
+
659
+ // Update user with Google OAuth info
660
+ user.oauth = user.oauth || {};
661
+ user.oauth.google = {
662
+ id: googleId,
663
+ email: email.toLowerCase(),
664
+ connectedAt: new Date()
665
+ };
666
+
667
+ // Verify email if it matches
668
+ if (!user.isEmailVerified && email.toLowerCase() === user.email) {
669
+ user.isEmailVerified = true;
670
+ }
671
+
672
+ return user.save();
673
+ })
674
+ .then(user => {
675
+ if (!user) return; // Already handled
676
+
677
+ logger.info('Google account linked for user:', user.email);
678
+
679
+ return res.status(200).json({
680
+ success: true,
681
+ message: "Google account linked successfully",
682
+ data: { user: user.toJSON() }
683
+ });
684
+ })
685
+ .catch(error => {
686
+ logger.error('Google account linking error:', error);
687
+ return response.error(res, 500, "Failed to link Google account", error.message);
688
+ });
689
+ };
690
+
691
+ /**
692
+ * DELETE /fear/api/auth/google/unlink
693
+ * @summary Unlink Google account from authenticated user
694
+ * @description Removes Google OAuth connection from the user's account
695
+ * @tags authentication, oauth
696
+ */
697
+ exports.unlinkGoogleAccount = (req, res) => {
698
+ const userId = req.user._id;
699
+
700
+ User.findById(userId)
701
+ .select('+password')
702
+ .then(user => {
703
+ if (!user) {
704
+ return response.error(res, 404, "User not found");
705
+ }
706
+
707
+ // Check if user has a password set (don't allow unlinking if it's their only auth method)
708
+ if (!user.password && user.oauth?.google) {
709
+ return response.error(
710
+ res,
711
+ 400,
712
+ "Cannot unlink Google account. Please set a password first to maintain access to your account."
713
+ );
714
+ }
715
+
716
+ // Check if Google account is linked
717
+ if (!user.oauth || !user.oauth.google) {
718
+ return response.error(res, 400, "No Google account is linked to this user");
719
+ }
720
+
721
+ // Remove Google OAuth info
722
+ user.oauth.google = undefined;
723
+
724
+ return user.save();
725
+ })
726
+ .then(user => {
727
+ if (!user) return; // Already handled
728
+
729
+ logger.info('Google account unlinked for user:', user.email);
730
+
731
+ return res.status(200).json({
732
+ success: true,
733
+ message: "Google account unlinked successfully",
734
+ data: { user: user.toJSON() }
735
+ });
736
+ })
737
+ .catch(error => {
738
+ logger.error('Google account unlinking error:', error);
739
+ return response.error(res, 500, "Failed to unlink Google account", error.message);
740
+ });
741
+ };
344
742
  // Export TokenService and other utilities for use in other modules
345
743
  exports.AuthResponse = response;
744
+
745
+ module.exports = {
746
+ login: exports.login,
747
+ register: exports.register,
748
+ logout: exports.logout,
749
+ isAuthorized: exports.isAuthorized,
750
+ googleAuth: exports.googleAuth,
751
+ }