@imksh/create 1.0.31 → 1.0.33

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.
Files changed (158) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/src/commands/create.js +2 -0
  4. package/src/index.js +1 -1
  5. package/templates/layers/next/javascript/package.json +8 -2
  6. package/templates/layers/next/typescript/.eslintrc.json +12 -0
  7. package/templates/layers/next/typescript/package.json +14 -3
  8. package/templates/layers/next/typescript/src/components/common/Loading.tsx +1 -1
  9. package/templates/layers/next/typescript/src/components/common/LottieIcon.tsx +2 -2
  10. package/templates/layers/next/typescript/src/components/common/Table.tsx +4 -4
  11. package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +2 -1
  12. package/templates/layers/next/typescript/src/components/ui/Header.tsx +2 -2
  13. package/templates/layers/next/typescript/src/hooks/usePwaInstall.ts +10 -4
  14. package/templates/layers/next/typescript/src/services/authService.ts +1 -1
  15. package/templates/layers/next/typescript/src/store/useAuthStore.ts +24 -9
  16. package/templates/layers/node/javascript/.env.example +3 -1
  17. package/templates/layers/node/javascript/package.json +12 -3
  18. package/templates/layers/node/javascript/src/app.js +2 -1
  19. package/templates/layers/node/javascript/src/controllers/auth.controller.js +106 -168
  20. package/templates/layers/node/javascript/src/controllers/push.controller.js +51 -58
  21. package/templates/layers/node/javascript/src/controllers/upload.controller.js +12 -19
  22. package/templates/layers/node/javascript/src/middleware/auth.middleware.js +5 -11
  23. package/templates/layers/node/javascript/src/middleware/errorHandler.js +6 -4
  24. package/templates/layers/node/javascript/src/routes/auth.route.js +2 -0
  25. package/templates/layers/node/javascript/src/services/auth.service.js +159 -164
  26. package/templates/layers/node/javascript/src/utils/ApiError.js +20 -0
  27. package/templates/layers/node/javascript/src/utils/SuccessResponse.js +15 -0
  28. package/templates/layers/node/javascript/src/utils/apiResponse.js +4 -0
  29. package/templates/layers/node/javascript/src/utils/asyncHandler.js +5 -0
  30. package/templates/layers/node/typescript/.env.example +3 -1
  31. package/templates/layers/node/typescript/.eslintrc.cjs +28 -0
  32. package/templates/layers/node/typescript/package.json +16 -2
  33. package/templates/layers/node/typescript/src/app.ts +3 -2
  34. package/templates/layers/node/typescript/src/config/db.ts +3 -2
  35. package/templates/layers/node/typescript/src/config/socket.ts +5 -4
  36. package/templates/layers/node/typescript/src/controllers/auth.controller.ts +104 -132
  37. package/templates/layers/node/typescript/src/controllers/push.controller.ts +52 -59
  38. package/templates/layers/node/typescript/src/controllers/upload.controller.ts +12 -19
  39. package/templates/layers/node/typescript/src/middleware/auth.middleware.ts +13 -7
  40. package/templates/layers/node/typescript/src/middleware/errorHandler.ts +18 -5
  41. package/templates/layers/node/typescript/src/middleware/rateLimiter.ts +2 -2
  42. package/templates/layers/node/typescript/src/routes/auth.route.ts +2 -0
  43. package/templates/layers/node/typescript/src/services/auth.service.ts +86 -43
  44. package/templates/layers/node/typescript/src/types/express.d.ts +5 -3
  45. package/templates/layers/node/typescript/src/utils/ApiError.ts +25 -0
  46. package/templates/layers/node/typescript/src/utils/SuccessResponse.ts +17 -0
  47. package/templates/layers/node/typescript/src/utils/apiResponse.ts +4 -0
  48. package/templates/layers/node/typescript/src/utils/asyncHandler.ts +13 -0
  49. package/templates/layers/node/typescript/src/utils/cloudinaryUpload.ts +18 -7
  50. package/templates/layers/node/typescript/src/utils/expoNotification.ts +1 -1
  51. package/templates/layers/node/typescript/src/utils/genOtpToken.ts +7 -1
  52. package/templates/layers/node/typescript/src/utils/jwt.ts +8 -2
  53. package/templates/layers/node/typescript/src/utils/logger.ts +2 -1
  54. package/templates/layers/node/typescript/src/utils/pushHelper.ts +13 -9
  55. package/templates/layers/node-prisma/javascript/.env.example +3 -1
  56. package/templates/layers/node-prisma/javascript/package.json +11 -2
  57. package/templates/layers/node-prisma/javascript/src/app.js +2 -1
  58. package/templates/layers/node-prisma/javascript/src/controllers/auth.controller.js +90 -135
  59. package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +64 -76
  60. package/templates/layers/node-prisma/javascript/src/controllers/upload.controller.js +12 -19
  61. package/templates/layers/node-prisma/javascript/src/middleware/auth.middleware.js +5 -4
  62. package/templates/layers/node-prisma/javascript/src/middleware/errorHandler.js +6 -4
  63. package/templates/layers/node-prisma/javascript/src/routes/auth.route.js +2 -0
  64. package/templates/layers/node-prisma/javascript/src/services/auth.service.js +145 -122
  65. package/templates/layers/node-prisma/javascript/src/utils/ApiError.js +20 -0
  66. package/templates/layers/node-prisma/javascript/src/utils/SuccessResponse.js +15 -0
  67. package/templates/layers/node-prisma/javascript/src/utils/apiResponse.js +4 -0
  68. package/templates/layers/node-prisma/javascript/src/utils/asyncHandler.js +5 -0
  69. package/templates/layers/node-prisma/typescript/.env.example +3 -1
  70. package/templates/layers/node-prisma/typescript/.eslintrc.cjs +28 -0
  71. package/templates/layers/node-prisma/typescript/package.json +19 -5
  72. package/templates/layers/node-prisma/typescript/src/app.ts +3 -2
  73. package/templates/layers/node-prisma/typescript/src/config/prisma.ts +3 -2
  74. package/templates/layers/node-prisma/typescript/src/config/socket.ts +5 -4
  75. package/templates/layers/node-prisma/typescript/src/controllers/auth.controller.ts +89 -105
  76. package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -83
  77. package/templates/layers/node-prisma/typescript/src/controllers/upload.controller.ts +12 -19
  78. package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +9 -13
  79. package/templates/layers/node-prisma/typescript/src/middleware/errorHandler.ts +18 -5
  80. package/templates/layers/node-prisma/typescript/src/middleware/rateLimiter.ts +2 -2
  81. package/templates/layers/node-prisma/typescript/src/routes/auth.route.ts +2 -0
  82. package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +83 -31
  83. package/templates/layers/node-prisma/typescript/src/types/express.d.ts +5 -3
  84. package/templates/layers/node-prisma/typescript/src/utils/ApiError.ts +25 -0
  85. package/templates/layers/node-prisma/typescript/src/utils/SuccessResponse.ts +17 -0
  86. package/templates/layers/node-prisma/typescript/src/utils/apiResponse.ts +4 -0
  87. package/templates/layers/node-prisma/typescript/src/utils/asyncHandler.ts +13 -0
  88. package/templates/layers/node-prisma/typescript/src/utils/cloudinaryUpload.ts +18 -7
  89. package/templates/layers/node-prisma/typescript/src/utils/genOtpToken.ts +7 -1
  90. package/templates/layers/node-prisma/typescript/src/utils/jwt.ts +8 -2
  91. package/templates/layers/node-prisma/typescript/src/utils/logger.ts +2 -1
  92. package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +10 -6
  93. package/templates/layers/prisma/javascript/package.json +8 -0
  94. package/templates/layers/prisma/typescript/package.json +14 -1
  95. package/templates/layers/prisma/typescript/src/config/db.ts +3 -2
  96. package/templates/layers/react/javascript/package.json +6 -0
  97. package/templates/layers/react/typescript/.eslintrc.cjs +20 -0
  98. package/templates/layers/react/typescript/package.json +11 -2
  99. package/templates/layers/react/typescript/src/components/common/Table.tsx +4 -4
  100. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +1 -1
  101. package/templates/layers/react/typescript/src/main.tsx +11 -11
  102. package/templates/layers/react/typescript/src/services/authService.ts +1 -1
  103. package/templates/layers/react/typescript/src/store/useAuthStore.ts +24 -9
  104. package/templates/layers/react-native/base/app.json +9 -22
  105. package/templates/layers/react-native/base/eslint.config.js +5 -1
  106. package/templates/layers/react-native/base/package.json +40 -33
  107. package/templates/layers/react-native/base/src/app/(auth)/edit-profile.tsx +7 -7
  108. package/templates/layers/react-native/base/src/app/(auth)/login.tsx +31 -7
  109. package/templates/layers/react-native/base/src/app/(tabs)/_layout.tsx +3 -3
  110. package/templates/layers/react-native/base/src/app/(tabs)/index.tsx +6 -5
  111. package/templates/layers/react-native/base/src/app/_layout.tsx +4 -8
  112. package/templates/layers/react-native/base/src/app/screens/doc-viewer.tsx +378 -0
  113. package/templates/layers/react-native/base/src/components/common/ConfirmationToast.tsx +58 -40
  114. package/templates/layers/react-native/base/src/components/common/GoogleAuthButton.tsx +123 -0
  115. package/templates/layers/react-native/base/src/components/common/LocationLoader.tsx +64 -9
  116. package/templates/layers/react-native/base/src/components/common/Typography.tsx +54 -19
  117. package/templates/layers/react-native/base/src/components/ui/BiometricLock.tsx +3 -3
  118. package/templates/layers/react-native/base/src/components/ui/ClassicTabBar.tsx +15 -12
  119. package/templates/layers/react-native/base/src/components/ui/CustomTabBar.tsx +90 -91
  120. package/templates/layers/react-native/base/src/components/ui/Drawer.tsx +344 -0
  121. package/templates/layers/react-native/base/src/components/ui/Sidebar.tsx +4 -4
  122. package/templates/layers/react-native/base/src/components/ui/buttons/Button.tsx +29 -6
  123. package/templates/layers/react-native/base/src/components/ui/data-display/Accordion.tsx +2 -2
  124. package/templates/layers/react-native/base/src/components/ui/data-display/Badge.tsx +1 -2
  125. package/templates/layers/react-native/base/src/components/ui/data-display/Image.tsx +3 -3
  126. package/templates/layers/react-native/base/src/components/ui/data-display/SwipeableRow.tsx +28 -7
  127. package/templates/layers/react-native/base/src/components/ui/feedback/GestureWrapper.tsx +11 -4
  128. package/templates/layers/react-native/base/src/components/ui/inputs/DatePicker.tsx +3 -3
  129. package/templates/layers/react-native/base/src/components/ui/inputs/ImagePickerModal.tsx +45 -19
  130. package/templates/layers/react-native/base/src/components/ui/inputs/Input.tsx +28 -9
  131. package/templates/layers/react-native/base/src/components/ui/inputs/SearchablePicker.tsx +199 -0
  132. package/templates/layers/react-native/base/src/components/ui/inputs/Switch.tsx +7 -7
  133. package/templates/layers/react-native/base/src/components/ui/inputs/WebLocationPicker.tsx +146 -72
  134. package/templates/layers/react-native/base/src/config/api.ts +5 -5
  135. package/templates/layers/react-native/base/src/services/auth.service.ts +3 -1
  136. package/templates/layers/react-native/base/src/services/permission.service.ts +168 -44
  137. package/templates/layers/react-native/base/src/services/socket.service.ts +0 -7
  138. package/templates/layers/react-native/base/src/store/useAuthStore.ts +61 -16
  139. package/templates/layers/react-native/base/src/store/useNotificationStore.ts +36 -42
  140. package/templates/layers/react-native/base/src/types/images.d.ts +7 -5
  141. package/templates/layers/react-native/base/src/utils/env.ts +47 -0
  142. package/templates/layers/react-native/base/src/utils/fileDownload.ts +206 -0
  143. package/templates/layers/react-native/base/src/utils/storage.ts +1 -1
  144. package/templates/presets/production-fullstack/overrides/client/package.json +9 -0
  145. package/templates/presets/production-fullstack/overrides/mobile/app.json +2 -15
  146. package/templates/presets/production-fullstack/overrides/mobile/package.json +39 -29
  147. package/templates/presets/production-fullstack/overrides/mobile/src/app/_layout.tsx +3 -3
  148. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/Drawer.tsx +344 -0
  149. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/DatePicker.tsx +3 -3
  150. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/ImagePickerModal.tsx +3 -3
  151. package/templates/presets/production-fullstack/overrides/mobile/src/services/permission.service.ts +168 -44
  152. package/templates/presets/production-fullstack/overrides/mobile/src/store/useNotificationStore.ts +16 -0
  153. package/templates/presets/production-fullstack/overrides/mobile/src/utils/env.ts +47 -0
  154. package/templates/presets/production-fullstack/overrides/server/package.json +11 -2
  155. package/templates/layers/react-native/base/src/app/doc-viewer.tsx +0 -193
  156. package/templates/layers/react-native/base/src/components/ui/BottomModal.tsx +0 -176
  157. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/BottomModal.tsx +0 -159
  158. /package/templates/layers/react-native/base/assets/images/logo/{logoWithBg.png → icon.png} +0 -0
@@ -1,124 +1,108 @@
1
- import { Request, Response, NextFunction } from "express";
1
+ import { Request, Response } from "express";
2
2
  import * as authService from "../services/auth.service.js";
3
3
  import { generateToken } from "../utils/jwt.js";
4
4
  import genOtpToken from "../utils/genOtpToken.js";
5
- import logger from "../utils/logger.js";
5
+ import { asyncHandler } from "../utils/asyncHandler.js";
6
+ import { SuccessResponse } from "../utils/SuccessResponse.js";
7
+ import { ApiError } from "../utils/ApiError.js";
6
8
 
7
- export const signup = async (req: Request, res: Response, next: NextFunction) => {
8
- try {
9
- const { name, email, phone, password } = req.body;
10
- if (!email || !password || !name) {
11
- return next({ status: 400, message: "All fields are required." });
12
- }
13
- const newUser = await authService.signup({ name, email, phone, password });
14
- const token = await generateToken(newUser, res);
15
- const safeUser: any = { ...newUser };
16
- delete safeUser.password;
17
- res.status(201).json({ message: "Registration Successful", data: safeUser, token });
18
- } catch (error) {
19
- logger.error("Error in signup controller", error);
20
- next(error);
21
- }
9
+ const toSafeUser = (user: unknown): Record<string, unknown> => {
10
+ const safeUser: Record<string, unknown> = { ...((user as Record<string, unknown>) || {}) };
11
+ delete safeUser.password;
12
+ return safeUser;
22
13
  };
23
14
 
24
- export const login = async (req: Request, res: Response, next: NextFunction) => {
25
- try {
26
- const { email, password } = req.body;
27
- if (!email || !password) {
28
- return next({ status: 400, message: "All fields are required." });
29
- }
30
- const user = await authService.login({ email, password });
31
- const token = await generateToken(user, res);
32
- const safeUser: any = { ...user };
33
- delete safeUser.password;
34
- res.status(200).json({ message: "Login successfully", data: safeUser, token });
35
- } catch (error) {
36
- logger.error("Error in login controller", error);
37
- next(error);
15
+ export const signup = asyncHandler(async (req: Request, res: Response) => {
16
+ const { name, email, phone, password } = req.body;
17
+ if (!email || !password || !name) {
18
+ throw new ApiError(400, "All fields are required.");
38
19
  }
39
- };
20
+ const newUser = await authService.signup({ name, email, phone, password });
21
+ const token = await generateToken(newUser, res);
22
+ const safeUser = toSafeUser(newUser);
23
+ SuccessResponse(res, 201, "Registration Successful", { user: safeUser, token });
24
+ });
40
25
 
41
- export const logout = async (req: Request, res: Response, next: NextFunction) => {
42
- try {
43
- res.clearCookie("token", {
44
- httpOnly: true,
45
- secure: process.env.NODE_ENV === "production",
46
- sameSite: "strict",
47
- path: "/",
48
- });
49
- res.status(200).json({ message: "Logout Successfully" });
50
- } catch (error) {
51
- logger.error("Error in logout controller", error);
52
- next(error);
26
+ export const login = asyncHandler(async (req: Request, res: Response) => {
27
+ const { email, password } = req.body;
28
+ if (!email || !password) {
29
+ throw new ApiError(400, "All fields are required.");
53
30
  }
54
- };
31
+ const user = await authService.login({ email, password });
32
+ const token = await generateToken(user, res);
33
+ const safeUser = toSafeUser(user);
34
+ SuccessResponse(res, 200, "Login successfully", { user: safeUser, token });
35
+ });
55
36
 
56
- export const getMe = async (req: Request, res: Response, next: NextFunction) => {
57
- try {
58
- res.status(200).json({ message: "User Authenticated", data: req.user });
59
- } catch (error) {
60
- logger.error("Error in getMe controller", error);
61
- next(error);
37
+ export const logout = asyncHandler(async (req: Request, res: Response) => {
38
+ res.clearCookie("token", {
39
+ httpOnly: true,
40
+ secure: process.env.NODE_ENV === "production",
41
+ sameSite: "strict",
42
+ path: "/",
43
+ });
44
+ SuccessResponse(res, 200, "Logout Successfully");
45
+ });
46
+
47
+ export const getMe = asyncHandler(async (req: Request, res: Response) => {
48
+ const safeUser = toSafeUser(req.user);
49
+ SuccessResponse(res, 200, "User Authenticated", safeUser);
50
+ });
51
+
52
+ export const genOtp = asyncHandler(async (req: Request, res: Response) => {
53
+ const { email } = req.body;
54
+ if (!email) {
55
+ throw new ApiError(400, "Email is required.");
62
56
  }
63
- };
57
+ await authService.generateOtp(email);
58
+ SuccessResponse(res, 200, "Otp sent successfully");
59
+ });
64
60
 
65
- export const genOtp = async (req: Request, res: Response, next: NextFunction) => {
66
- try {
67
- const { email } = req.body;
68
- if (!email) {
69
- return next({ status: 400, message: "Email is required." });
70
- }
71
- await authService.generateOtp(email);
72
- res.status(200).json({ message: "Otp sent successfully" });
73
- } catch (error) {
74
- logger.error("Error in genOtp", error);
75
- next(error);
61
+ export const verifyOtp = asyncHandler(async (req: Request, res: Response) => {
62
+ const { email, otp } = req.body;
63
+ if (!email || !otp) {
64
+ throw new ApiError(400, "All fields are required.");
76
65
  }
77
- };
66
+ const user = await authService.verifyOtp({ email, otp });
67
+ await genOtpToken(user, res);
68
+ SuccessResponse(res, 200, "Otp verified successfully");
69
+ });
78
70
 
79
- export const verifyOtp = async (req: Request, res: Response, next: NextFunction) => {
80
- try {
81
- const { email, otp } = req.body;
82
- if (!email || !otp) {
83
- return next({ status: 400, message: "All fields are required." });
84
- }
85
- const user = await authService.verifyOtp({ email, otp });
86
- await genOtpToken(user, res);
87
- res.status(200).json({ message: "Otp verified successfully" });
88
- } catch (error) {
89
- logger.error("Error in verifyOtp", error);
90
- next(error);
71
+ export const resetPassword = asyncHandler(async (req: Request, res: Response) => {
72
+ const { newPassword } = req.body;
73
+ const userId = String(req.user?.id || req.user?._id || "");
74
+ if (!newPassword) {
75
+ throw new ApiError(400, "All fields are required.");
91
76
  }
92
- };
77
+ if (!userId) {
78
+ throw new ApiError(401, "Not authorized");
79
+ }
80
+ await authService.resetPassword({ id: userId }, newPassword);
81
+ SuccessResponse(res, 200, "Password updated successfully");
82
+ });
93
83
 
94
- export const resetPassword = async (req: Request, res: Response, next: NextFunction) => {
95
- try {
96
- const { newPassword } = req.body;
97
- const currentUser = req.user;
98
- if (!newPassword) {
99
- return next({ status: 400, message: "All fields are required." });
100
- }
101
- await authService.resetPassword(currentUser, newPassword);
102
- res.status(200).json({ message: "Password updated successfully" });
103
- } catch (error) {
104
- logger.error("Error in resetPassword", error);
105
- next(error);
84
+ export const updateMe = asyncHandler(async (req: Request, res: Response) => {
85
+ const { name, phone } = req.body;
86
+ const userId = String(req.user?.id || req.user?._id || "");
87
+ if (!userId) {
88
+ throw new ApiError(401, "Not authorized");
106
89
  }
107
- };
90
+ const user = await authService.updateProfile(userId, {
91
+ name,
92
+ phone,
93
+ file: req.file,
94
+ });
95
+ const safeUser = toSafeUser(user);
96
+ SuccessResponse(res, 200, "Profile updated successfully", safeUser);
97
+ });
108
98
 
109
- export const updateMe = async (req: Request, res: Response, next: NextFunction) => {
110
- try {
111
- const { name, phone } = req.body;
112
- const user = await authService.updateProfile(req.user.id, {
113
- name,
114
- phone,
115
- file: req.file,
116
- });
117
- const safeUser: any = { ...user };
118
- delete safeUser.password;
119
- res.status(200).json({ success: true, data: safeUser });
120
- } catch (error) {
121
- logger.error("Error updating profile", error);
122
- next(error);
99
+ export const googleLogin = asyncHandler(async (req: Request, res: Response) => {
100
+ const { idToken } = req.body;
101
+ if (!idToken) {
102
+ throw new ApiError(400, "idToken is required.");
123
103
  }
124
- };
104
+ const user = await authService.googleLogin(idToken);
105
+ const token = await generateToken(user, res);
106
+ const safeUser = toSafeUser(user);
107
+ SuccessResponse(res, 200, "Google Login successfully", { user: safeUser, token });
108
+ });
@@ -1,100 +1,84 @@
1
- import { Request, Response, NextFunction } from "express";
1
+ import { Request, Response } from "express";
2
2
  import { prisma } from "../config/prisma.js";
3
- import logger from "../utils/logger.js";
3
+ import { asyncHandler } from "../utils/asyncHandler.js";
4
+ import { SuccessResponse } from "../utils/SuccessResponse.js";
5
+ import { ApiError } from "../utils/ApiError.js";
4
6
 
5
- export const subscribe = async (
6
- req: Request,
7
- res: Response,
8
- next: NextFunction,
9
- ) => {
10
- try {
11
- const subscription = req.body;
12
- const userId = req.user?._id || req.user?.id;
7
+ export const subscribe = asyncHandler(async (req: Request, res: Response) => {
8
+ const subscription = req.body;
9
+ const userId = req.user?._id || req.user?.id;
13
10
 
14
- if (!subscription) {
15
- return next({ status: 400, message: "Invalid subscription payload" });
16
- }
11
+ if (!subscription) {
12
+ throw new ApiError(400, "Invalid subscription payload");
13
+ }
17
14
 
18
- // Determine type: EXPO if token is present, else WEB
19
- const isExpo = !!subscription.token;
20
- const isWeb = !!(subscription.endpoint && subscription.keys);
15
+ // Determine type: EXPO if token is present, else WEB
16
+ const isExpo = !!subscription.token;
17
+ const isWeb = !!(subscription.endpoint && subscription.keys);
21
18
 
22
- if (!isExpo && !isWeb) {
23
- return next({ status: 400, message: "Missing token (EXPO) or endpoint/keys (WEB)" });
24
- }
19
+ if (!isExpo && !isWeb) {
20
+ throw new ApiError(400, "Missing token (EXPO) or endpoint/keys (WEB)");
21
+ }
25
22
 
26
- if (isExpo) {
27
- const existingSub = await prisma.pushSubscription.findUnique({
28
- where: { token: subscription.token },
29
- });
23
+ if (isExpo) {
24
+ const existingSub = await prisma.pushSubscription.findUnique({
25
+ where: { token: subscription.token },
26
+ });
30
27
 
31
- if (existingSub) {
32
- if (userId && existingSub.userId !== userId) {
33
- await prisma.pushSubscription.update({
34
- where: { token: subscription.token },
35
- data: { userId },
36
- });
37
- }
38
- return res.status(200).json({ success: true, message: "Already subscribed." });
28
+ if (existingSub) {
29
+ if (userId && existingSub.userId !== userId) {
30
+ await prisma.pushSubscription.update({
31
+ where: { token: subscription.token },
32
+ data: { userId },
33
+ });
39
34
  }
35
+ return SuccessResponse(res, 200, "Already subscribed.");
36
+ }
40
37
 
41
- await prisma.pushSubscription.create({
42
- data: {
43
- type: "EXPO",
44
- token: subscription.token,
45
- userId: userId || null,
46
- },
47
- });
48
- } else {
49
- const existingSub = await prisma.pushSubscription.findUnique({
50
- where: { endpoint: subscription.endpoint },
51
- });
38
+ await prisma.pushSubscription.create({
39
+ data: {
40
+ type: "EXPO",
41
+ token: subscription.token,
42
+ userId: userId || null,
43
+ },
44
+ });
45
+ } else {
46
+ const existingSub = await prisma.pushSubscription.findUnique({
47
+ where: { endpoint: subscription.endpoint },
48
+ });
52
49
 
53
- if (existingSub) {
54
- if (userId && existingSub.userId !== userId) {
55
- await prisma.pushSubscription.update({
56
- where: { endpoint: subscription.endpoint },
57
- data: { userId },
58
- });
59
- }
60
- return res.status(200).json({ success: true, message: "Already subscribed." });
50
+ if (existingSub) {
51
+ if (userId && existingSub.userId !== userId) {
52
+ await prisma.pushSubscription.update({
53
+ where: { endpoint: subscription.endpoint },
54
+ data: { userId },
55
+ });
61
56
  }
62
-
63
- await prisma.pushSubscription.create({
64
- data: {
65
- type: "WEB",
66
- endpoint: subscription.endpoint,
67
- keys: subscription.keys,
68
- userId: userId || null,
69
- },
70
- });
57
+ return SuccessResponse(res, 200, "Already subscribed.");
71
58
  }
72
59
 
73
- res.status(201).json({ success: true, message: "Subscription created." });
74
- } catch (error) {
75
- logger.error("Error in push subscribe controller", error);
76
- next(error);
60
+ await prisma.pushSubscription.create({
61
+ data: {
62
+ type: "WEB",
63
+ endpoint: subscription.endpoint,
64
+ keys: subscription.keys,
65
+ userId: userId || null,
66
+ },
67
+ });
77
68
  }
78
- };
79
-
80
- export const unsubscribe = async (
81
- req: Request,
82
- res: Response,
83
- next: NextFunction,
84
- ) => {
85
- try {
86
- const { endpoint } = req.body;
87
- if (!endpoint) {
88
- return next({ status: 400, message: "Endpoint required" });
89
- }
90
69
 
91
- await prisma.pushSubscription
92
- .delete({ where: { endpoint } })
93
- .catch(() => {});
70
+ SuccessResponse(res, 201, "Subscription created.");
71
+ });
94
72
 
95
- res.status(200).json({ success: true, message: "Unsubscribed." });
96
- } catch (error) {
97
- logger.error("Error in push unsubscribe controller", error);
98
- next(error);
73
+ export const unsubscribe = asyncHandler(async (req: Request, res: Response) => {
74
+ const { endpoint } = req.body;
75
+ if (!endpoint) {
76
+ throw new ApiError(400, "Endpoint required");
99
77
  }
100
- };
78
+
79
+ await prisma.pushSubscription
80
+ .delete({ where: { endpoint } })
81
+ .catch(() => {});
82
+
83
+ SuccessResponse(res, 200, "Unsubscribed.");
84
+ });
@@ -1,22 +1,15 @@
1
- import { Request, Response, NextFunction } from "express";
1
+ import { Request, Response } from "express";
2
2
  import { uploadSingleToCloudinary } from "../utils/cloudinaryUpload.js";
3
- import logger from "../utils/logger.js";
3
+ import { asyncHandler } from "../utils/asyncHandler.js";
4
+ import { SuccessResponse } from "../utils/SuccessResponse.js";
5
+ import { ApiError } from "../utils/ApiError.js";
4
6
 
5
- export const uploadFile = async (req: Request, res: Response, next: NextFunction) => {
6
- try {
7
- if (!req.file) {
8
- return res.status(400).json({ success: false, message: "No file uploaded" });
9
- }
10
- const result = await uploadSingleToCloudinary(req.file, {
11
- folder: req.body.folder || "general_uploads",
12
- });
13
- res.status(200).json({
14
- success: true,
15
- message: "File uploaded successfully",
16
- data: result,
17
- });
18
- } catch (error) {
19
- logger.error("Upload controller error", error);
20
- next(error);
7
+ export const uploadFile = asyncHandler(async (req: Request, res: Response) => {
8
+ if (!req.file) {
9
+ throw new ApiError(400, "No file uploaded");
21
10
  }
22
- };
11
+ const result = await uploadSingleToCloudinary(req.file, {
12
+ folder: req.body.folder || "general_uploads",
13
+ });
14
+ SuccessResponse(res, 200, "File uploaded successfully", result);
15
+ });
@@ -2,9 +2,11 @@ import { Request, Response, NextFunction } from "express";
2
2
  import { verifyToken } from "../utils/jwt.js";
3
3
  import logger from "../utils/logger.js";
4
4
  import { prisma } from "../config/prisma.js";
5
+ import { ApiError } from "../utils/ApiError.js";
5
6
 
6
7
  export const protect = async (
7
8
  req: Request,
9
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
8
10
  res: Response,
9
11
  next: NextFunction,
10
12
  ) => {
@@ -20,15 +22,13 @@ export const protect = async (
20
22
  }
21
23
 
22
24
  if (!token) {
23
- return res
24
- .status(401)
25
- .json({ message: "Not authorized, no token provided" });
25
+ return next(new ApiError(401, "Not authorized, no token provided"));
26
26
  }
27
27
 
28
28
  try {
29
- const decoded: any = verifyToken(token);
29
+ const decoded = verifyToken(token) as { id: string } | null;
30
30
  if (!decoded) {
31
- return res.status(401).json({ message: "Not authorized, invalid token" });
31
+ return next(new ApiError(401, "Not authorized, invalid token"));
32
32
  }
33
33
 
34
34
  const user = await prisma.user.findUnique({
@@ -36,20 +36,16 @@ export const protect = async (
36
36
  });
37
37
 
38
38
  if (!user) {
39
- return res
40
- .status(401)
41
- .json({ message: "Not authorized, user not found" });
39
+ return next(new ApiError(401, "Not authorized, user not found"));
42
40
  }
43
41
 
44
- const safeUser: any = { ...user };
42
+ const safeUser: Record<string, unknown> = { ...user };
45
43
  delete safeUser.password;
46
44
 
47
45
  req.user = safeUser;
48
46
  return next();
49
- } catch (error: any) {
47
+ } catch (error: unknown) {
50
48
  logger.error("Auth middleware error", error);
51
- return res
52
- .status(401)
53
- .json({ message: "Not authorized, token verification failed" });
49
+ return next(new ApiError(401, "Not authorized, token verification failed"));
54
50
  }
55
51
  };
@@ -1,16 +1,29 @@
1
1
  import { Request, Response, NextFunction } from "express";
2
+ import { ApiError } from "../utils/ApiError.js";
3
+
4
+ export interface HttpError extends Error {
5
+ statusCode?: number;
6
+ status?: number;
7
+ errors?: unknown[];
8
+ }
2
9
 
3
10
  export const notFound = (req: Request, res: Response, next: NextFunction) => {
4
- const error = new Error(`Not Found - ${req.originalUrl}`);
5
- res.status(404);
6
- next(error);
11
+ next(new ApiError(404, `Not Found - ${req.originalUrl}`));
7
12
  };
8
13
 
9
- export const errorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
10
- const statusCode = err.status || err.statusCode || (res.statusCode === 200 ? 500 : res.statusCode);
14
+ export const errorHandler = (
15
+ err: HttpError,
16
+ req: Request,
17
+ res: Response,
18
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
+ next: NextFunction,
20
+ ) => {
21
+ const statusCode = err.statusCode || err.status || (res.statusCode === 200 ? 500 : res.statusCode);
11
22
  res.status(statusCode).json({
12
23
  success: false,
24
+ statusCode,
13
25
  message: err.message || "An unexpected error occurred",
26
+ errors: err.errors || [],
14
27
  stack: process.env.NODE_ENV === "production" ? undefined : err.stack,
15
28
  });
16
29
  };
@@ -21,8 +21,8 @@ const createLimiter = ({
21
21
  legacyHeaders: false,
22
22
  skipSuccessfulRequests,
23
23
 
24
- keyGenerator: (req: any) => {
25
- return req.user?.id || req.ip || req.headers["x-forwarded-for"] || req.socket.remoteAddress;
24
+ keyGenerator: (req: Request) => {
25
+ return String(req.user?.id || req.user?._id || req.ip || req.headers["x-forwarded-for"] || req.socket.remoteAddress || "unknown");
26
26
  },
27
27
 
28
28
  handler: (req: Request, res: Response) => {
@@ -5,6 +5,7 @@ import {
5
5
  getMe,
6
6
  logout,
7
7
  updateMe,
8
+ googleLogin,
8
9
  } from "../controllers/auth.controller.js";
9
10
  import { protect } from "../middleware/auth.middleware.js";
10
11
  import { apiLimiter } from "../middleware/rateLimiter.js";
@@ -15,6 +16,7 @@ router.use(apiLimiter);
15
16
 
16
17
  router.post("/signup", signup);
17
18
  router.post("/login", login);
19
+ router.post("/google", googleLogin);
18
20
  router.post("/logout", logout);
19
21
  router.get("/me", protect, getMe);
20
22
  router.put("/me", protect, updateMe);