@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,4 +1,5 @@
1
- import User from "../models/user.model.js";
1
+ import { OAuth2Client } from "google-auth-library";
2
+ import User, { type IUser } from "../models/user.model.js";
2
3
  import Otp from "../models/otp.model.js";
3
4
  import bcrypt from "bcrypt";
4
5
  import { generateToken } from "../utils/jwt.js";
@@ -6,28 +7,55 @@ import { sendOtpEmail } from "../utils/emailOtp.js";
6
7
  import {
7
8
  uploadSingleToCloudinary,
8
9
  deleteFromCloudinary,
10
+ type CloudinaryUploadFile,
9
11
  } from "../utils/cloudinaryUpload.js";
10
12
  import logger from "../utils/logger.js";
13
+ import { ApiError } from "../utils/ApiError.js";
11
14
 
12
- export const signup = async ({ name, email, phone, password, otp }: any) => {
15
+ const googleClient = new OAuth2Client(process.env.GOOGLE_CLIENT_ID || "");
16
+
17
+ export interface SignupDTO {
18
+ name: string;
19
+ email: string;
20
+ phone?: string;
21
+ password: string;
22
+ otp: string;
23
+ }
24
+
25
+ export interface LoginDTO {
26
+ email: string;
27
+ password: string;
28
+ }
29
+
30
+ export interface VerifyOtpDTO {
31
+ email: string;
32
+ otp: string;
33
+ }
34
+
35
+ export interface UpdateProfileDTO {
36
+ name?: string;
37
+ phone?: string;
38
+ file?: CloudinaryUploadFile;
39
+ }
40
+
41
+ export interface UserWithSave {
42
+ password?: string;
43
+ save: () => Promise<unknown>;
44
+ }
45
+
46
+ export const signup = async ({ name, email, phone, password, otp }: SignupDTO) => {
13
47
  const existingUser = await User.findOne({ email });
14
48
  if (existingUser) {
15
- const err: any = new Error("User already exists");
16
- err.status = 409;
17
- throw err;
49
+ throw new ApiError(409, "User already exists");
18
50
  }
19
51
 
20
52
  const existingOtp = await Otp.findOne({ email });
21
53
  if (!existingOtp) {
22
- const err: any = new Error("OTP match failed");
23
- err.status = 400;
24
- throw err;
54
+ throw new ApiError(400, "OTP match failed");
25
55
  }
26
56
  const isMatched = await bcrypt.compare(otp, existingOtp.otp);
27
57
  if (!isMatched) {
28
- const err: any = new Error("OTP match failed");
29
- err.status = 400;
30
- throw err;
58
+ throw new ApiError(400, "OTP match failed");
31
59
  }
32
60
  const salt = await bcrypt.genSalt(10);
33
61
  const hashedPassword = await bcrypt.hash(password, salt);
@@ -44,27 +72,23 @@ export const signup = async ({ name, email, phone, password, otp }: any) => {
44
72
  await existingOtp.deleteOne(); // delete OTP after successful signup
45
73
  return newUser;
46
74
  };
47
- export const login = async ({ email, password }: any) => {
75
+
76
+ export const login = async ({ email, password }: LoginDTO) => {
48
77
  const user = await User.findOne({ email });
49
78
  if (!user || !user.password) {
50
- const err: any = new Error("Invalid Credentials");
51
- err.status = 401;
52
- throw err;
79
+ throw new ApiError(401, "Invalid Credentials");
53
80
  }
54
81
  const isValid = await bcrypt.compare(password, user.password);
55
82
  if (!isValid) {
56
- const err: any = new Error("Invalid Credentials");
57
- err.status = 401;
58
- throw err;
83
+ throw new ApiError(401, "Invalid Credentials");
59
84
  }
60
85
  return user;
61
86
  };
87
+
62
88
  export const generateSignupOtp = async (email: string) => {
63
89
  const user = await User.findOne({ email });
64
90
  if (user) {
65
- const err: any = new Error("User already exists");
66
- err.status = 409;
67
- throw err;
91
+ throw new ApiError(409, "User already exists");
68
92
  }
69
93
  const existingOtp = await Otp.findOne({ email });
70
94
  if (existingOtp) {
@@ -76,12 +100,11 @@ export const generateSignupOtp = async (email: string) => {
76
100
  await sendOtpEmail(email, otp);
77
101
  return true;
78
102
  };
103
+
79
104
  export const generateOtp = async (email: string) => {
80
105
  const user = await User.findOne({ email });
81
106
  if (!user) {
82
- const err: any = new Error("User not found");
83
- err.status = 404;
84
- throw err;
107
+ throw new ApiError(404, "User not found");
85
108
  }
86
109
  const existingOtp = await Otp.findOne({ email });
87
110
  if (existingOtp) {
@@ -93,42 +116,37 @@ export const generateOtp = async (email: string) => {
93
116
  await sendOtpEmail(email, otp);
94
117
  return true;
95
118
  };
96
- export const verifyOtp = async ({ email, otp }: any) => {
119
+
120
+ export const verifyOtp = async ({ email, otp }: VerifyOtpDTO) => {
97
121
  const user = await User.findOne({ email });
98
122
  if (!user) {
99
- const err: any = new Error("User not found");
100
- err.status = 404;
101
- throw err;
123
+ throw new ApiError(404, "User not found");
102
124
  }
103
125
  const existingOtp = await Otp.findOne({ email });
104
126
  if (!existingOtp) {
105
- const err: any = new Error("OTP match failed");
106
- err.status = 400;
107
- throw err;
127
+ throw new ApiError(400, "OTP match failed");
108
128
  }
109
129
  const isMatched = await bcrypt.compare(otp, existingOtp.otp);
110
130
  if (!isMatched) {
111
- const err: any = new Error("OTP match failed");
112
- err.status = 400;
113
- throw err;
131
+ throw new ApiError(400, "OTP match failed");
114
132
  }
115
133
  return user;
116
134
  };
117
- export const resetPassword = async (user: any, newPassword: any) => {
135
+
136
+ export const resetPassword = async (user: UserWithSave, newPassword: string) => {
118
137
  const hashedPassword = await bcrypt.hash(newPassword, 10);
119
138
  user.password = hashedPassword;
120
139
  await user.save();
121
140
  return true;
122
141
  };
142
+
123
143
  export const updateProfile = async (
124
- userId: any,
125
- { name, phone, file }: any,
144
+ userId: string,
145
+ { name, phone, file }: UpdateProfileDTO,
126
146
  ) => {
127
147
  const user = await User.findById(userId);
128
148
  if (!user) {
129
- const err: any = new Error("User not found");
130
- err.status = 404;
131
- throw err;
149
+ throw new ApiError(404, "User not found");
132
150
  }
133
151
  if (name) user.name = name;
134
152
  if (phone) user.phone = phone;
@@ -153,9 +171,7 @@ export const updateProfile = async (
153
171
  export const savePushToken = async (userId: string, token: string) => {
154
172
  const user = await User.findById(userId);
155
173
  if (!user) {
156
- const err: any = new Error("User not found");
157
- err.status = 404;
158
- throw err;
174
+ throw new ApiError(404, "User not found");
159
175
  }
160
176
  if (!user.expoPushTokens.includes(token)) {
161
177
  user.expoPushTokens.push(token);
@@ -163,3 +179,30 @@ export const savePushToken = async (userId: string, token: string) => {
163
179
  }
164
180
  return user;
165
181
  };
182
+
183
+ export const googleLogin = async (idToken: string) => {
184
+ const ticket = await googleClient.verifyIdToken({
185
+ idToken,
186
+ audience: process.env.GOOGLE_CLIENT_ID,
187
+ });
188
+ const payload = ticket.getPayload();
189
+ if (!payload) {
190
+ throw new ApiError(401, "Invalid Google Token");
191
+ }
192
+ const { email, name, picture } = payload;
193
+ if (!email) {
194
+ throw new ApiError(400, "Email not found in Google Token");
195
+ }
196
+
197
+ let user = await User.findOne({ email });
198
+ if (!user) {
199
+ user = await User.create({
200
+ name: name || "User",
201
+ email: email.toLowerCase(),
202
+ role: "user",
203
+ password: "",
204
+ avatar: { url: picture || `https://placehold.co/600x400?text=${name?.charAt(0).toUpperCase() || 'U'}`, publicId: "" },
205
+ });
206
+ }
207
+ return user;
208
+ };
@@ -1,10 +1,12 @@
1
- import { Express } from "express";
1
+ import type { CloudinaryUploadFile } from "../utils/cloudinaryUpload.js";
2
+
3
+ export {};
2
4
 
3
5
  declare global {
4
6
  namespace Express {
5
7
  interface Request {
6
- user?: any;
7
- file?: any;
8
+ user?: Record<string, unknown> & { _id?: unknown; id?: unknown; toObject?: () => Record<string, unknown> };
9
+ file?: CloudinaryUploadFile;
8
10
  }
9
11
  }
10
12
  }
@@ -0,0 +1,25 @@
1
+ export class ApiError extends Error {
2
+ public statusCode: number;
3
+ public data: unknown;
4
+ public success: boolean;
5
+ public errors: unknown[];
6
+
7
+ constructor(
8
+ statusCode: number,
9
+ message: string = "Something went wrong",
10
+ errors: unknown[] = [],
11
+ stack: string = "",
12
+ ) {
13
+ super(message);
14
+ this.statusCode = statusCode;
15
+ this.data = null;
16
+ this.success = false;
17
+ this.errors = errors;
18
+
19
+ if (stack) {
20
+ this.stack = stack;
21
+ } else if (typeof (Error as unknown as { captureStackTrace?: (target: object, constructor: unknown) => void }).captureStackTrace === "function") {
22
+ (Error as unknown as { captureStackTrace: (target: object, constructor: unknown) => void }).captureStackTrace(this, this.constructor);
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,17 @@
1
+ import { Response } from "express";
2
+
3
+ export const SuccessResponse = (
4
+ res: Response,
5
+ statusCode: number,
6
+ message: string = "Success",
7
+ data: unknown = null,
8
+ pagination?: unknown,
9
+ ) => {
10
+ return res.status(statusCode).json({
11
+ success: statusCode < 400,
12
+ statusCode,
13
+ message,
14
+ data,
15
+ ...(pagination ? { pagination } : {}),
16
+ });
17
+ };
@@ -0,0 +1,4 @@
1
+ import { SuccessResponse } from "./SuccessResponse.js";
2
+
3
+ export { SuccessResponse };
4
+ export const ApiResponse = SuccessResponse;
@@ -0,0 +1,13 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+
3
+ export const asyncHandler = (
4
+ requestHandler: (
5
+ req: Request,
6
+ res: Response,
7
+ next: NextFunction,
8
+ ) => Promise<unknown>,
9
+ ) => {
10
+ return (req: Request, res: Response, next: NextFunction) => {
11
+ Promise.resolve(requestHandler(req, res, next)).catch((err) => next(err));
12
+ };
13
+ };
@@ -1,11 +1,22 @@
1
1
  import cloudinary from "../config/cloudinary.js";
2
2
  import logger from "./logger.js";
3
3
 
4
- const uploadToCloudinary = async (file: any, options: any = {}) => {
4
+ export interface CloudinaryUploadFile {
5
+ mimetype?: string;
6
+ buffer?: Buffer;
7
+ [key: string]: unknown;
8
+ }
9
+
10
+ export interface CloudinaryOptions {
11
+ folder?: string;
12
+ [key: string]: unknown;
13
+ }
14
+
15
+ const uploadToCloudinary = async (file: CloudinaryUploadFile, options: CloudinaryOptions = {}) => {
5
16
  if (!file?.buffer) {
6
17
  throw new Error("Invalid file.");
7
18
  }
8
- const dataURI = `data:${file.mimetype};base64,${file.buffer.toString("base64")}`;
19
+ const dataURI = `data:${file.mimetype || "application/octet-stream"};base64,${file.buffer.toString("base64")}`;
9
20
  const result = await cloudinary.uploader.upload(dataURI, {
10
21
  folder: options.folder || "uploads",
11
22
  ...options,
@@ -16,19 +27,19 @@ const uploadToCloudinary = async (file: any, options: any = {}) => {
16
27
  };
17
28
  };
18
29
 
19
- export const uploadSingleToCloudinary = async (file: any, options: any = {}) => {
30
+ export const uploadSingleToCloudinary = async (file: CloudinaryUploadFile, options: CloudinaryOptions = {}) => {
20
31
  try {
21
32
  return await uploadToCloudinary(file, options);
22
- } catch (error) {
33
+ } catch (error: unknown) {
23
34
  logger.error("Cloudinary Upload Error", error);
24
35
  throw new Error("Failed to upload image.");
25
36
  }
26
37
  };
27
38
 
28
- export const uploadMultipleToCloudinary = async (files: any[] = [], options: any = {}) => {
39
+ export const uploadMultipleToCloudinary = async (files: CloudinaryUploadFile[] = [], options: CloudinaryOptions = {}) => {
29
40
  try {
30
41
  return await Promise.all(files.map((file) => uploadToCloudinary(file, options)));
31
- } catch (error) {
42
+ } catch (error: unknown) {
32
43
  logger.error("Cloudinary Upload Error", error);
33
44
  throw new Error("Failed to upload images.");
34
45
  }
@@ -37,7 +48,7 @@ export const uploadMultipleToCloudinary = async (files: any[] = [], options: any
37
48
  export const deleteFromCloudinary = async (publicId: string) => {
38
49
  try {
39
50
  return await cloudinary.uploader.destroy(publicId);
40
- } catch (error) {
51
+ } catch (error: unknown) {
41
52
  logger.error("Cloudinary Delete Error", error);
42
53
  throw new Error("Failed to delete image.");
43
54
  }
@@ -10,7 +10,7 @@ const expo = new Expo();
10
10
  export interface PushMessagePayload {
11
11
  title: string;
12
12
  body: string;
13
- data?: Record<string, any>;
13
+ data?: Record<string, unknown>;
14
14
  }
15
15
 
16
16
  /**
@@ -1,7 +1,13 @@
1
1
  import jwt from "jsonwebtoken";
2
2
  import { Response } from "express";
3
3
 
4
- const genOtpToken = (user: any, res?: Response) => {
4
+ export interface UserOtpTokenPayload {
5
+ _id?: unknown;
6
+ id?: unknown;
7
+ [key: string]: unknown;
8
+ }
9
+
10
+ const genOtpToken = (user: UserOtpTokenPayload, res?: Response) => {
5
11
  const JWT_SECRET = process.env.JWT_SECRET || "fallbacksecret";
6
12
  const token = jwt.sign({ id: user._id || user.id }, JWT_SECRET, {
7
13
  expiresIn: "10m",
@@ -1,7 +1,13 @@
1
1
  import jwt from "jsonwebtoken";
2
2
  import { Response } from "express";
3
3
 
4
- export const generateToken = (user: any, res?: Response) => {
4
+ export interface UserTokenPayload {
5
+ _id?: unknown;
6
+ id?: unknown;
7
+ [key: string]: unknown;
8
+ }
9
+
10
+ export const generateToken = (user: UserTokenPayload, res?: Response) => {
5
11
  const payload = { id: user._id || user.id };
6
12
  const JWT_SECRET = process.env.JWT_SECRET || "fallbacksecret";
7
13
  const token = jwt.sign(payload, JWT_SECRET, { expiresIn: "30d" });
@@ -21,7 +27,7 @@ export const verifyToken = (token: string) => {
21
27
  try {
22
28
  const JWT_SECRET = process.env.JWT_SECRET || "fallbacksecret";
23
29
  return jwt.verify(token, JWT_SECRET);
24
- } catch (error) {
30
+ } catch {
25
31
  return null;
26
32
  }
27
33
  };
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-console */
1
2
  const colors = {
2
3
  reset: "\x1b[0m",
3
4
  info: "\x1b[36m", // Cyan
@@ -18,7 +19,7 @@ export const logger = {
18
19
  info: (msg: string) => console.log(formatMessage("info", msg)),
19
20
  success: (msg: string) => console.log(formatMessage("success", msg)),
20
21
  warn: (msg: string) => console.warn(formatMessage("warn", msg)),
21
- error: (msg: string, err?: any) => {
22
+ error: (msg: string, err?: unknown) => {
22
23
  console.error(formatMessage("error", msg));
23
24
  if (err) console.error(err);
24
25
  },
@@ -1,7 +1,7 @@
1
1
  import webpush from "../config/webPush.js";
2
- import { Expo, ExpoPushMessage, ExpoPushTicket } from "expo-server-sdk";
2
+ import { Expo, ExpoPushMessage } from "expo-server-sdk";
3
3
  import logger from "./logger.js";
4
- import PushSubscription from "../models/pushSubscription.model.js";
4
+ import PushSubscription, { type IPushSubscription } from "../models/pushSubscription.model.js";
5
5
 
6
6
  const expo = new Expo();
7
7
 
@@ -19,7 +19,7 @@ export const sendPushToUser = async (
19
19
  url: string = "/",
20
20
  ) => {
21
21
  try {
22
- const subscriptions = await PushSubscription.find({ user: userId });
22
+ const subscriptions: IPushSubscription[] = await PushSubscription.find({ user: userId });
23
23
 
24
24
  if (subscriptions.length === 0) return;
25
25
 
@@ -30,13 +30,17 @@ export const sendPushToUser = async (
30
30
  if (webSubs.length > 0) {
31
31
  const payload = JSON.stringify({ title, body, url });
32
32
  await Promise.allSettled(
33
- webSubs.map(async (sub) => {
33
+ webSubs.map(async (sub: IPushSubscription) => {
34
34
  if (!sub.endpoint || !sub.keys) return;
35
- const pushSub = { endpoint: sub.endpoint, keys: sub.keys as any };
35
+ const pushSub = {
36
+ endpoint: sub.endpoint,
37
+ keys: sub.keys as { p256dh: string; auth: string },
38
+ };
36
39
  try {
37
40
  await webpush.sendNotification(pushSub, payload);
38
- } catch (err: any) {
39
- if (err.statusCode === 404 || err.statusCode === 410) {
41
+ } catch (err: unknown) {
42
+ const error = err as { statusCode?: number };
43
+ if (error.statusCode === 404 || error.statusCode === 410) {
40
44
  await sub.deleteOne();
41
45
  } else {
42
46
  logger.error("Web Push Error", err);
@@ -64,12 +68,12 @@ export const sendPushToUser = async (
64
68
  for (const chunk of chunks) {
65
69
  try {
66
70
  await expo.sendPushNotificationsAsync(chunk);
67
- } catch (error) {
71
+ } catch (error: unknown) {
68
72
  logger.error("Error sending push notification chunk", error);
69
73
  }
70
74
  }
71
75
  }
72
- } catch (error) {
76
+ } catch (error: unknown) {
73
77
  logger.error("Error sending push notification to user", error);
74
78
  }
75
79
  };
@@ -60,4 +60,6 @@ RATE_LIMIT_MAX=100
60
60
  # ==========================
61
61
  VAPID_PRIVATE_KEY=
62
62
  VAPID_PUBLIC_KEY=
63
- VAPID_SUBJECT=mailto:noreply@example.com
63
+ VAPID_SUBJECT=mailto:noreply@example.com
64
+ # Google Auth
65
+ GOOGLE_CLIENT_ID=your-google-client-id
@@ -6,6 +6,11 @@
6
6
  "scripts": {
7
7
  "start": "node src/index.js",
8
8
  "dev": "nodemon src/index.js",
9
+ "lint": "eslint src/**/*.js",
10
+ "lint:fix": "eslint src/**/*.js --fix",
11
+ "format": "prettier --write \"src/**/*.{js,json}\"",
12
+ "format:check": "prettier --check \"src/**/*.{js,json}\"",
13
+ "circular-check": "dpdm --circular --no-warning --no-tree --exit-code circular:1 \"src/**/*.js\"",
9
14
  "db:push": "prisma db push",
10
15
  "db:generate": "prisma generate",
11
16
  "generate:vapid": "node ./src/utils/generateVapidKeys.js"
@@ -29,10 +34,14 @@
29
34
  "web-push": "^3.6.6",
30
35
  "pg": "^8.11.3",
31
36
  "@prisma/adapter-pg": "^7.0.0",
32
- "expo-server-sdk": "^3.10.0"
37
+ "expo-server-sdk": "^3.10.0",
38
+ "google-auth-library": "^9.14.2"
33
39
  },
34
40
  "devDependencies": {
41
+ "dpdm": "^3.14.0",
42
+ "eslint": "^8.53.0",
35
43
  "nodemon": "^3.0.1",
44
+ "prettier": "^3.4.2",
36
45
  "prisma": "^7.0.0"
37
46
  }
38
- }
47
+ }
@@ -7,6 +7,7 @@ import morgan from "morgan";
7
7
  import apiRoutes from "./routes/index.js";
8
8
  import { notFound, errorHandler } from "./middleware/errorHandler.js";
9
9
  import { apiLimiter } from "./middleware/rateLimiter.js";
10
+ import { SuccessResponse } from "./utils/SuccessResponse.js";
10
11
 
11
12
  const app = express();
12
13
 
@@ -32,7 +33,7 @@ app.use("/api", apiRoutes);
32
33
 
33
34
  // Example health route
34
35
  app.get("/api/health", (req, res) => {
35
- res.status(200).json({ success: true, message: "API is running" });
36
+ SuccessResponse(res, 200, "API is running");
36
37
  });
37
38
 
38
39
  // Error handling middlewares