@imksh/create 1.0.30 → 1.0.32

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 (247) 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/src/utils/askTemplate.js +2 -1
  6. package/templates/layers/electron/base/electron/main.js +262 -0
  7. package/templates/layers/electron/base/electron/preload.js +37 -0
  8. package/templates/layers/electron/base/electron-builder.json5 +68 -0
  9. package/templates/layers/electron/javascript/_gitignore +38 -0
  10. package/templates/layers/electron/javascript/package.json +49 -0
  11. package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
  12. package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
  13. package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
  14. package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
  15. package/templates/layers/electron/javascript/src/main.jsx +17 -0
  16. package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
  17. package/templates/layers/electron/javascript/vite.config.js +29 -0
  18. package/templates/layers/electron/typescript/_gitignore +38 -0
  19. package/templates/layers/electron/typescript/electron/main.ts +213 -0
  20. package/templates/layers/electron/typescript/electron/preload.ts +52 -0
  21. package/templates/layers/electron/typescript/package.json +52 -0
  22. package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
  23. package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
  24. package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
  25. package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
  26. package/templates/layers/electron/typescript/src/main.tsx +17 -0
  27. package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
  28. package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
  29. package/templates/layers/electron/typescript/tsconfig.json +25 -0
  30. package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
  31. package/templates/layers/electron/typescript/vite.config.ts +29 -0
  32. package/templates/layers/next/javascript/package.json +8 -2
  33. package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
  34. package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
  35. package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
  36. package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
  37. package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
  38. package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
  39. package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
  40. package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
  41. package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
  42. package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
  43. package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
  44. package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
  45. package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
  46. package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
  47. package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
  48. package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
  49. package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
  50. package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
  51. package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
  52. package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
  53. package/templates/layers/next/typescript/.eslintrc.json +12 -0
  54. package/templates/layers/next/typescript/package.json +14 -3
  55. package/templates/layers/next/typescript/src/components/common/Breadcrumbs.tsx +57 -8
  56. package/templates/layers/next/typescript/src/components/common/Loading.tsx +2 -1
  57. package/templates/layers/next/typescript/src/components/common/LottieIcon.tsx +2 -2
  58. package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
  59. package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
  60. package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
  61. package/templates/layers/next/typescript/src/components/common/Table.tsx +205 -39
  62. package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
  63. package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
  64. package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
  65. package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
  66. package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +3 -1
  67. package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
  68. package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
  69. package/templates/layers/next/typescript/src/components/ui/Header.tsx +2 -2
  70. package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
  71. package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
  72. package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
  73. package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
  74. package/templates/layers/next/typescript/src/hooks/usePwaInstall.ts +10 -4
  75. package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
  76. package/templates/layers/next/typescript/src/services/authService.ts +1 -1
  77. package/templates/layers/next/typescript/src/store/useAuthStore.ts +24 -9
  78. package/templates/layers/next/typescript/src/store/useUiStore.ts +14 -4
  79. package/templates/layers/node/javascript/.env.example +3 -1
  80. package/templates/layers/node/javascript/package.json +12 -3
  81. package/templates/layers/node/javascript/src/app.js +2 -1
  82. package/templates/layers/node/javascript/src/controllers/auth.controller.js +106 -168
  83. package/templates/layers/node/javascript/src/controllers/push.controller.js +51 -58
  84. package/templates/layers/node/javascript/src/controllers/upload.controller.js +12 -19
  85. package/templates/layers/node/javascript/src/middleware/auth.middleware.js +5 -11
  86. package/templates/layers/node/javascript/src/middleware/errorHandler.js +6 -4
  87. package/templates/layers/node/javascript/src/routes/auth.route.js +2 -0
  88. package/templates/layers/node/javascript/src/services/auth.service.js +159 -164
  89. package/templates/layers/node/javascript/src/utils/ApiError.js +20 -0
  90. package/templates/layers/node/javascript/src/utils/SuccessResponse.js +15 -0
  91. package/templates/layers/node/javascript/src/utils/apiResponse.js +4 -0
  92. package/templates/layers/node/javascript/src/utils/asyncHandler.js +5 -0
  93. package/templates/layers/node/typescript/.env.example +3 -1
  94. package/templates/layers/node/typescript/.eslintrc.cjs +28 -0
  95. package/templates/layers/node/typescript/package-lock.json +2 -2
  96. package/templates/layers/node/typescript/package.json +17 -3
  97. package/templates/layers/node/typescript/src/app.ts +3 -2
  98. package/templates/layers/node/typescript/src/config/db.ts +3 -2
  99. package/templates/layers/node/typescript/src/config/socket.ts +5 -4
  100. package/templates/layers/node/typescript/src/controllers/auth.controller.ts +104 -132
  101. package/templates/layers/node/typescript/src/controllers/push.controller.ts +52 -59
  102. package/templates/layers/node/typescript/src/controllers/upload.controller.ts +12 -19
  103. package/templates/layers/node/typescript/src/middleware/auth.middleware.ts +13 -7
  104. package/templates/layers/node/typescript/src/middleware/errorHandler.ts +18 -5
  105. package/templates/layers/node/typescript/src/middleware/rateLimiter.ts +2 -2
  106. package/templates/layers/node/typescript/src/routes/auth.route.ts +2 -0
  107. package/templates/layers/node/typescript/src/services/auth.service.ts +86 -43
  108. package/templates/layers/node/typescript/src/types/express.d.ts +5 -3
  109. package/templates/layers/node/typescript/src/utils/ApiError.ts +25 -0
  110. package/templates/layers/node/typescript/src/utils/SuccessResponse.ts +17 -0
  111. package/templates/layers/node/typescript/src/utils/apiResponse.ts +4 -0
  112. package/templates/layers/node/typescript/src/utils/asyncHandler.ts +13 -0
  113. package/templates/layers/node/typescript/src/utils/cloudinaryUpload.ts +18 -7
  114. package/templates/layers/node/typescript/src/utils/expoNotification.ts +1 -1
  115. package/templates/layers/node/typescript/src/utils/genOtpToken.ts +7 -1
  116. package/templates/layers/node/typescript/src/utils/jwt.ts +8 -2
  117. package/templates/layers/node/typescript/src/utils/logger.ts +2 -1
  118. package/templates/layers/node/typescript/src/utils/pushHelper.ts +13 -9
  119. package/templates/layers/node-prisma/javascript/.env.example +3 -1
  120. package/templates/layers/node-prisma/javascript/package.json +11 -2
  121. package/templates/layers/node-prisma/javascript/src/app.js +2 -1
  122. package/templates/layers/node-prisma/javascript/src/controllers/auth.controller.js +90 -135
  123. package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +64 -76
  124. package/templates/layers/node-prisma/javascript/src/controllers/upload.controller.js +12 -19
  125. package/templates/layers/node-prisma/javascript/src/middleware/auth.middleware.js +5 -4
  126. package/templates/layers/node-prisma/javascript/src/middleware/errorHandler.js +6 -4
  127. package/templates/layers/node-prisma/javascript/src/routes/auth.route.js +2 -0
  128. package/templates/layers/node-prisma/javascript/src/services/auth.service.js +145 -122
  129. package/templates/layers/node-prisma/javascript/src/utils/ApiError.js +20 -0
  130. package/templates/layers/node-prisma/javascript/src/utils/SuccessResponse.js +15 -0
  131. package/templates/layers/node-prisma/javascript/src/utils/apiResponse.js +4 -0
  132. package/templates/layers/node-prisma/javascript/src/utils/asyncHandler.js +5 -0
  133. package/templates/layers/node-prisma/typescript/.env.example +3 -1
  134. package/templates/layers/node-prisma/typescript/.eslintrc.cjs +28 -0
  135. package/templates/layers/node-prisma/typescript/package.json +19 -5
  136. package/templates/layers/node-prisma/typescript/src/app.ts +3 -2
  137. package/templates/layers/node-prisma/typescript/src/config/prisma.ts +3 -2
  138. package/templates/layers/node-prisma/typescript/src/config/socket.ts +5 -4
  139. package/templates/layers/node-prisma/typescript/src/controllers/auth.controller.ts +89 -105
  140. package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -83
  141. package/templates/layers/node-prisma/typescript/src/controllers/upload.controller.ts +12 -19
  142. package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +9 -13
  143. package/templates/layers/node-prisma/typescript/src/middleware/errorHandler.ts +18 -5
  144. package/templates/layers/node-prisma/typescript/src/middleware/rateLimiter.ts +2 -2
  145. package/templates/layers/node-prisma/typescript/src/routes/auth.route.ts +2 -0
  146. package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +83 -31
  147. package/templates/layers/node-prisma/typescript/src/types/express.d.ts +5 -3
  148. package/templates/layers/node-prisma/typescript/src/utils/ApiError.ts +25 -0
  149. package/templates/layers/node-prisma/typescript/src/utils/SuccessResponse.ts +17 -0
  150. package/templates/layers/node-prisma/typescript/src/utils/apiResponse.ts +4 -0
  151. package/templates/layers/node-prisma/typescript/src/utils/asyncHandler.ts +13 -0
  152. package/templates/layers/node-prisma/typescript/src/utils/cloudinaryUpload.ts +18 -7
  153. package/templates/layers/node-prisma/typescript/src/utils/genOtpToken.ts +7 -1
  154. package/templates/layers/node-prisma/typescript/src/utils/jwt.ts +8 -2
  155. package/templates/layers/node-prisma/typescript/src/utils/logger.ts +2 -1
  156. package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +10 -6
  157. package/templates/layers/prisma/javascript/package.json +8 -0
  158. package/templates/layers/prisma/typescript/package.json +14 -1
  159. package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
  160. package/templates/layers/prisma/typescript/src/config/db.ts +3 -2
  161. package/templates/layers/react/javascript/package.json +6 -0
  162. package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
  163. package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
  164. package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
  165. package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
  166. package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
  167. package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
  168. package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
  169. package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
  170. package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
  171. package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
  172. package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
  173. package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
  174. package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
  175. package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
  176. package/templates/layers/react/typescript/.eslintrc.cjs +20 -0
  177. package/templates/layers/react/typescript/package.json +11 -2
  178. package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
  179. package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
  180. package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
  181. package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
  182. package/templates/layers/react/typescript/src/components/common/Table.tsx +201 -38
  183. package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
  184. package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
  185. package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
  186. package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
  187. package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
  188. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
  189. package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
  190. package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
  191. package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
  192. package/templates/layers/react/typescript/src/main.tsx +11 -11
  193. package/templates/layers/react/typescript/src/services/authService.ts +1 -1
  194. package/templates/layers/react/typescript/src/store/useAuthStore.ts +24 -9
  195. package/templates/layers/react-native/base/app.json +9 -22
  196. package/templates/layers/react-native/base/eslint.config.js +5 -1
  197. package/templates/layers/react-native/base/package.json +11 -1
  198. package/templates/layers/react-native/base/src/app/(auth)/edit-profile.tsx +7 -7
  199. package/templates/layers/react-native/base/src/app/(auth)/login.tsx +31 -7
  200. package/templates/layers/react-native/base/src/app/(tabs)/_layout.tsx +3 -3
  201. package/templates/layers/react-native/base/src/app/(tabs)/index.tsx +6 -5
  202. package/templates/layers/react-native/base/src/app/_layout.tsx +3 -3
  203. package/templates/layers/react-native/base/src/app/doc-viewer.tsx +3 -3
  204. package/templates/layers/react-native/base/src/components/common/ConfirmationToast.tsx +58 -40
  205. package/templates/layers/react-native/base/src/components/common/GoogleAuthButton.tsx +144 -0
  206. package/templates/layers/react-native/base/src/components/common/LocationLoader.tsx +64 -9
  207. package/templates/layers/react-native/base/src/components/common/Typography.tsx +54 -19
  208. package/templates/layers/react-native/base/src/components/ui/ClassicTabBar.tsx +15 -12
  209. package/templates/layers/react-native/base/src/components/ui/CustomTabBar.tsx +84 -90
  210. package/templates/layers/react-native/base/src/components/ui/Drawer.tsx +344 -0
  211. package/templates/layers/react-native/base/src/components/ui/Sidebar.tsx +4 -4
  212. package/templates/layers/react-native/base/src/components/ui/buttons/Button.tsx +29 -6
  213. package/templates/layers/react-native/base/src/components/ui/data-display/Accordion.tsx +2 -2
  214. package/templates/layers/react-native/base/src/components/ui/data-display/Badge.tsx +1 -2
  215. package/templates/layers/react-native/base/src/components/ui/data-display/Image.tsx +3 -3
  216. package/templates/layers/react-native/base/src/components/ui/data-display/SwipeableRow.tsx +28 -7
  217. package/templates/layers/react-native/base/src/components/ui/feedback/GestureWrapper.tsx +11 -4
  218. package/templates/layers/react-native/base/src/components/ui/inputs/DatePicker.tsx +3 -3
  219. package/templates/layers/react-native/base/src/components/ui/inputs/ImagePickerModal.tsx +45 -19
  220. package/templates/layers/react-native/base/src/components/ui/inputs/Input.tsx +28 -9
  221. package/templates/layers/react-native/base/src/components/ui/inputs/SearchablePicker.tsx +199 -0
  222. package/templates/layers/react-native/base/src/components/ui/inputs/Switch.tsx +7 -7
  223. package/templates/layers/react-native/base/src/components/ui/inputs/WebLocationPicker.tsx +146 -72
  224. package/templates/layers/react-native/base/src/config/api.ts +5 -5
  225. package/templates/layers/react-native/base/src/config/appConfig.ts +1 -1
  226. package/templates/layers/react-native/base/src/services/auth.service.ts +3 -1
  227. package/templates/layers/react-native/base/src/services/permission.service.ts +0 -2
  228. package/templates/layers/react-native/base/src/services/socket.service.ts +0 -7
  229. package/templates/layers/react-native/base/src/store/useAuthStore.ts +61 -16
  230. package/templates/layers/react-native/base/src/store/useNotificationStore.ts +36 -42
  231. package/templates/layers/react-native/base/src/types/images.d.ts +7 -5
  232. package/templates/layers/react-native/base/src/utils/fileDownload.ts +206 -0
  233. package/templates/layers/react-native/base/src/utils/storage.ts +1 -1
  234. package/templates/presets/electron/preset.json +9 -0
  235. package/templates/presets/electron-ts/preset.json +9 -0
  236. package/templates/presets/production-fullstack/overrides/client/package.json +9 -0
  237. package/templates/presets/production-fullstack/overrides/mobile/app.json +2 -15
  238. package/templates/presets/production-fullstack/overrides/mobile/package.json +11 -1
  239. package/templates/presets/production-fullstack/overrides/mobile/src/app/_layout.tsx +3 -3
  240. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/Drawer.tsx +344 -0
  241. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/DatePicker.tsx +3 -3
  242. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/ImagePickerModal.tsx +3 -3
  243. package/templates/presets/production-fullstack/overrides/server/package.json +11 -2
  244. package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
  245. package/templates/layers/react-native/base/src/components/ui/BottomModal.tsx +0 -176
  246. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/BottomModal.tsx +0 -159
  247. /package/templates/layers/react-native/base/assets/images/logo/{logoWithBg.png → icon.png} +0 -0
@@ -1,19 +1,46 @@
1
+ import { OAuth2Client } from "google-auth-library";
1
2
  import bcrypt from "bcrypt";
2
3
  import { prisma } from "../config/prisma.js";
3
4
  import { sendOtpEmail } from "../utils/emailOtp.js";
4
5
  import {
5
6
  uploadSingleToCloudinary,
6
7
  deleteFromCloudinary,
8
+ type CloudinaryUploadFile,
7
9
  } from "../utils/cloudinaryUpload.js";
8
10
  import logger from "../utils/logger.js";
9
- export const signup = async ({ name, email, phone, password }: any) => {
11
+ import { ApiError } from "../utils/ApiError.js";
12
+
13
+ const googleClient = new OAuth2Client(process.env.GOOGLE_CLIENT_ID || "");
14
+
15
+ export interface SignupDTO {
16
+ name: string;
17
+ email: string;
18
+ phone?: string;
19
+ password: string;
20
+ }
21
+
22
+ export interface LoginDTO {
23
+ email: string;
24
+ password: string;
25
+ }
26
+
27
+ export interface VerifyOtpDTO {
28
+ email: string;
29
+ otp: string;
30
+ }
31
+
32
+ export interface UpdateProfileDTO {
33
+ name?: string;
34
+ phone?: string;
35
+ file?: CloudinaryUploadFile;
36
+ }
37
+
38
+ export const signup = async ({ name, email, phone, password }: SignupDTO) => {
10
39
  const existingUser = await prisma.user.findUnique({
11
40
  where: { email },
12
41
  });
13
42
  if (existingUser) {
14
- const err: any = new Error("User already exists");
15
- err.status = 409;
16
- throw err;
43
+ throw new ApiError(409, "User already exists");
17
44
  }
18
45
 
19
46
  const salt = await bcrypt.genSalt(10);
@@ -33,33 +60,29 @@ export const signup = async ({ name, email, phone, password }: any) => {
33
60
 
34
61
  return newUser;
35
62
  };
36
- export const login = async ({ email, password }: any) => {
63
+
64
+ export const login = async ({ email, password }: LoginDTO) => {
37
65
  const user = await prisma.user.findUnique({
38
66
  where: { email },
39
67
  });
40
68
  if (!user) {
41
- const err: any = new Error("User not found");
42
- err.status = 404;
43
- throw err;
69
+ throw new ApiError(404, "User not found");
44
70
  }
45
71
 
46
72
  const isValid = await bcrypt.compare(password, user.password);
47
73
  if (!isValid) {
48
- const err: any = new Error("Invalid Credentials");
49
- err.status = 401;
50
- throw err;
74
+ throw new ApiError(401, "Invalid Credentials");
51
75
  }
52
76
 
53
77
  return user;
54
78
  };
79
+
55
80
  export const generateOtp = async (email: string) => {
56
81
  const user = await prisma.user.findUnique({
57
82
  where: { email },
58
83
  });
59
84
  if (!user) {
60
- const err: any = new Error("User not found");
61
- err.status = 404;
62
- throw err;
85
+ throw new ApiError(404, "User not found");
63
86
  }
64
87
 
65
88
  const existingOtp = await prisma.otp.findUnique({
@@ -84,35 +107,31 @@ export const generateOtp = async (email: string) => {
84
107
  await sendOtpEmail(email, otp);
85
108
  return true;
86
109
  };
87
- export const verifyOtp = async ({ email, otp }: any) => {
110
+
111
+ export const verifyOtp = async ({ email, otp }: VerifyOtpDTO) => {
88
112
  const user = await prisma.user.findUnique({
89
113
  where: { email },
90
114
  });
91
115
  if (!user) {
92
- const err: any = new Error("User not found");
93
- err.status = 404;
94
- throw err;
116
+ throw new ApiError(404, "User not found");
95
117
  }
96
118
 
97
119
  const existingOtp = await prisma.otp.findUnique({
98
120
  where: { email },
99
121
  });
100
122
  if (!existingOtp) {
101
- const err: any = new Error("OTP match failed");
102
- err.status = 400;
103
- throw err;
123
+ throw new ApiError(400, "OTP match failed");
104
124
  }
105
125
 
106
126
  const isMatched = await bcrypt.compare(otp, existingOtp.otp);
107
127
  if (!isMatched) {
108
- const err: any = new Error("OTP match failed");
109
- err.status = 400;
110
- throw err;
128
+ throw new ApiError(400, "OTP match failed");
111
129
  }
112
130
 
113
131
  return user;
114
132
  };
115
- export const resetPassword = async (user: any, newPassword: any) => {
133
+
134
+ export const resetPassword = async (user: { id: string }, newPassword: string) => {
116
135
  const hashedPassword = await bcrypt.hash(newPassword, 10);
117
136
  await prisma.user.update({
118
137
  where: { id: user.id },
@@ -120,20 +139,24 @@ export const resetPassword = async (user: any, newPassword: any) => {
120
139
  });
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 prisma.user.findUnique({
128
148
  where: { id: userId },
129
149
  });
130
150
  if (!user) {
131
- const err: any = new Error("User not found");
132
- err.status = 404;
133
- throw err;
151
+ throw new ApiError(404, "User not found");
134
152
  }
135
153
 
136
- const updateData: any = {};
154
+ const updateData: {
155
+ name?: string;
156
+ phone?: string;
157
+ avatarUrl?: string;
158
+ avatarPublicId?: string;
159
+ } = {};
137
160
  if (name) updateData.name = name;
138
161
  if (phone) updateData.phone = phone;
139
162
 
@@ -159,3 +182,32 @@ export const updateProfile = async (
159
182
 
160
183
  return updatedUser;
161
184
  };
185
+
186
+ export const googleLogin = async (idToken: string) => {
187
+ const ticket = await googleClient.verifyIdToken({
188
+ idToken,
189
+ audience: process.env.GOOGLE_CLIENT_ID,
190
+ });
191
+ const payload = ticket.getPayload();
192
+ if (!payload) {
193
+ throw new ApiError(401, "Invalid Google Token");
194
+ }
195
+ const { email, name, picture } = payload;
196
+ if (!email) {
197
+ throw new ApiError(400, "Email not found in Google Token");
198
+ }
199
+
200
+ let user = await prisma.user.findUnique({ where: { email } });
201
+ if (!user) {
202
+ user = await prisma.user.create({
203
+ data: {
204
+ name: name || "User",
205
+ email: email.toLowerCase(),
206
+ password: "",
207
+ avatarUrl: picture || `https://placehold.co/600x400?text=${name?.charAt(0).toUpperCase() || 'U'}`,
208
+ avatarPublicId: "",
209
+ }
210
+ });
211
+ }
212
+ return user;
213
+ };
@@ -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
  }
@@ -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,5 +1,5 @@
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
4
  import { prisma } from "../config/prisma.js";
5
5
 
@@ -34,11 +34,15 @@ export const sendPushToUser = async (
34
34
  await Promise.allSettled(
35
35
  webSubs.map(async (sub) => {
36
36
  if (!sub.endpoint || !sub.keys) return;
37
- const pushSub = { endpoint: sub.endpoint, keys: sub.keys as any };
37
+ const pushSub = {
38
+ endpoint: sub.endpoint,
39
+ keys: sub.keys as unknown as { p256dh: string; auth: string },
40
+ };
38
41
  try {
39
42
  await webpush.sendNotification(pushSub, payload);
40
- } catch (err: any) {
41
- if (err.statusCode === 404 || err.statusCode === 410) {
43
+ } catch (err: unknown) {
44
+ const error = err as { statusCode?: number };
45
+ if (error.statusCode === 404 || error.statusCode === 410) {
42
46
  await prisma.pushSubscription.delete({ where: { id: sub.id } }).catch(() => {});
43
47
  } else {
44
48
  logger.error("Web Push Error", err);
@@ -66,12 +70,12 @@ export const sendPushToUser = async (
66
70
  for (const chunk of chunks) {
67
71
  try {
68
72
  await expo.sendPushNotificationsAsync(chunk);
69
- } catch (error) {
73
+ } catch (error: unknown) {
70
74
  logger.error("Error sending push notification chunk", error);
71
75
  }
72
76
  }
73
77
  }
74
- } catch (error) {
78
+ } catch (error: unknown) {
75
79
  logger.error("Error sending push notification to user", error);
76
80
  }
77
81
  };
@@ -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,7 +34,10 @@
29
34
  "web-push": "^3.6.6"
30
35
  },
31
36
  "devDependencies": {
37
+ "dpdm": "^3.14.0",
38
+ "eslint": "^8.53.0",
32
39
  "nodemon": "^3.0.1",
40
+ "prettier": "^3.4.2",
33
41
  "prisma": "^5.6.0"
34
42
  }
35
43
  }
@@ -7,6 +7,13 @@
7
7
  "start": "tsx src/index.ts",
8
8
  "dev": "tsx watch src/index.ts",
9
9
  "build": "tsc",
10
+ "lint": "eslint . --ext .ts --max-warnings 0",
11
+ "lint:fix": "eslint . --ext .ts --fix",
12
+ "format": "prettier --write \"src/**/*.{ts,json}\"",
13
+ "format:check": "prettier --check \"src/**/*.{ts,json}\"",
14
+ "circular-check": "dpdm --circular --no-warning --no-tree --exit-code circular:1 \"src/**/*.ts\"",
15
+ "ts-prune": "ts-prune",
16
+ "ts-purns": "ts-prune",
10
17
  "db:push": "prisma db push",
11
18
  "db:generate": "prisma generate",
12
19
  "generate:vapid": "node ./src/utils/generateVapidKeys.js"
@@ -38,10 +45,16 @@
38
45
  "@types/jsonwebtoken": "^9.0.5",
39
46
  "@types/morgan": "^1.9.9",
40
47
  "@types/multer": "^1.4.11",
48
+ "@types/node": "^20.9.0",
41
49
  "@types/nodemailer": "^6.4.14",
42
50
  "@types/web-push": "^3.6.3",
43
- "@types/node": "^20.9.0",
51
+ "@typescript-eslint/eslint-plugin": "^6.10.0",
52
+ "@typescript-eslint/parser": "^6.10.0",
53
+ "dpdm": "^3.14.0",
54
+ "eslint": "^8.53.0",
55
+ "prettier": "^3.4.2",
44
56
  "prisma": "^5.6.0",
57
+ "ts-prune": "^0.10.3",
45
58
  "tsx": "^4.23.1",
46
59
  "typescript": "^5.2.2"
47
60
  }
@@ -7,17 +7,42 @@ datasource db {
7
7
  url = env("DATABASE_URL")
8
8
  }
9
9
 
10
+ enum AttachmentType {
11
+ IMAGE
12
+ DOCUMENT
13
+ VIDEO
14
+ PDF
15
+ OTHER
16
+ }
17
+
18
+ model Attachment {
19
+ id String @id @default(uuid())
20
+ publicId String
21
+ url String
22
+ name String
23
+ type AttachmentType
24
+
25
+ createdAt DateTime @default(now())
26
+ updatedAt DateTime @updatedAt
27
+ deletedAt DateTime?
28
+
29
+ // Back-relations
30
+ avatar User? @relation("avatar")
31
+ }
32
+
10
33
  model User {
11
- id String @id @default(uuid())
12
- email String @unique
13
- name String
14
- phone String?
15
- password String
16
- role String @default("user")
17
- avatarUrl String?
18
- avatarPublicId String?
19
- createdAt DateTime @default(now())
20
- updatedAt DateTime @updatedAt
34
+ id String @id @default(uuid())
35
+ email String @unique
36
+ name String
37
+ phone String?
38
+ password String
39
+ role String @default("user")
40
+
41
+ avatarId String? @unique
42
+ avatar Attachment? @relation("avatar", fields: [avatarId], references: [id])
43
+
44
+ createdAt DateTime @default(now())
45
+ updatedAt DateTime @updatedAt
21
46
  pushSubscriptions PushSubscription[]
22
47
  }
23
48
 
@@ -7,8 +7,9 @@ const connectDB = async () => {
7
7
  try {
8
8
  await prisma.$connect();
9
9
  logger.success("Prisma Database connected successfully");
10
- } catch (error: any) {
11
- logger.error(`Prisma Database connection failed: ${error.message}`, error);
10
+ } catch (error: unknown) {
11
+ const err = error as Error;
12
+ logger.error(`Prisma Database connection failed: ${err.message}`, error);
12
13
  process.exit(1);
13
14
  }
14
15
  };
@@ -7,6 +7,10 @@
7
7
  "dev": "vite",
8
8
  "build": "vite build",
9
9
  "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
10
+ "lint:fix": "eslint . --ext js,jsx --fix",
11
+ "format": "prettier --write \"src/**/*.{js,jsx,css,json}\"",
12
+ "format:check": "prettier --check \"src/**/*.{js,jsx,css,json}\"",
13
+ "circular-check": "dpdm --circular --no-warning --no-tree --exit-code circular:1 \"src/**/*.{js,jsx}\"",
10
14
  "preview": "vite preview"
11
15
  },
12
16
  "dependencies": {
@@ -29,10 +33,12 @@
29
33
  "@types/react": "^18.2.37",
30
34
  "@types/react-dom": "^18.2.15",
31
35
  "@vitejs/plugin-react": "^4.2.0",
36
+ "dpdm": "^3.14.0",
32
37
  "eslint": "^8.53.0",
33
38
  "eslint-plugin-react": "^7.33.2",
34
39
  "eslint-plugin-react-hooks": "^4.6.0",
35
40
  "eslint-plugin-react-refresh": "^0.4.4",
41
+ "prettier": "^3.4.2",
36
42
  "vite": "^5.0.0"
37
43
  }
38
44
  }