stackkit 0.3.5 → 0.3.6

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 (192) hide show
  1. package/README.md +50 -42
  2. package/dist/cli/add.js +122 -56
  3. package/dist/cli/create.d.ts +2 -0
  4. package/dist/cli/create.js +271 -95
  5. package/dist/cli/doctor.js +1 -0
  6. package/dist/cli/list.d.ts +1 -1
  7. package/dist/cli/list.js +6 -4
  8. package/dist/index.js +234 -191
  9. package/dist/lib/constants.d.ts +4 -0
  10. package/dist/lib/constants.js +4 -0
  11. package/dist/lib/discovery/module-discovery.d.ts +4 -0
  12. package/dist/lib/discovery/module-discovery.js +56 -0
  13. package/dist/lib/generation/code-generator.d.ts +11 -2
  14. package/dist/lib/generation/code-generator.js +42 -3
  15. package/dist/lib/generation/generator-utils.js +3 -1
  16. package/dist/lib/pm/package-manager.js +16 -13
  17. package/dist/lib/ui/logger.js +3 -2
  18. package/dist/lib/utils/path-resolver.d.ts +2 -0
  19. package/dist/lib/utils/path-resolver.js +8 -0
  20. package/dist/meta.json +8312 -0
  21. package/modules/auth/better-auth/files/{shared → express}/config/env.ts +48 -50
  22. package/modules/auth/better-auth/files/express/middlewares/authorize.ts +20 -1
  23. package/modules/auth/better-auth/files/express/modules/auth.controller.ts +349 -0
  24. package/modules/auth/better-auth/files/express/modules/{auth/auth.route.ts → auth.route.ts} +9 -4
  25. package/modules/auth/better-auth/files/express/modules/auth.service.ts +664 -0
  26. package/modules/auth/better-auth/files/express/modules/{auth/auth.type.ts → auth.type.ts} +22 -9
  27. package/modules/auth/better-auth/files/{shared/mongoose/auth/helper.ts → express/mongo-modules/auth.helper.ts} +11 -1
  28. package/modules/auth/better-auth/files/express/types/express.d.ts +11 -0
  29. package/modules/auth/better-auth/files/nextjs/api-route.ts +74 -0
  30. package/modules/auth/better-auth/files/nextjs/dashboard/pages/(user)/page.tsx +6 -0
  31. package/modules/auth/better-auth/files/nextjs/dashboard/pages/admin/page.tsx +6 -0
  32. package/modules/auth/better-auth/files/nextjs/dashboard/pages/layout.tsx +48 -0
  33. package/modules/auth/better-auth/files/nextjs/dashboard/pages/my-profile/page.tsx +5 -0
  34. package/modules/auth/better-auth/files/nextjs/features/services/auth.service.ts +102 -0
  35. package/modules/auth/better-auth/files/nextjs/layout/layout.tsx +13 -0
  36. package/modules/auth/better-auth/files/nextjs/lib/axios/http.ts +158 -0
  37. package/modules/auth/better-auth/files/nextjs/lib/env.ts +35 -0
  38. package/modules/auth/better-auth/files/nextjs/lib/utils/auth.ts +75 -0
  39. package/modules/auth/better-auth/files/nextjs/lib/utils/cookie.ts +29 -0
  40. package/modules/auth/better-auth/files/nextjs/lib/utils/jwt.ts +28 -0
  41. package/modules/auth/better-auth/files/nextjs/lib/utils/token.ts +49 -0
  42. package/modules/auth/better-auth/files/nextjs/pages/forgot-password/page.tsx +5 -0
  43. package/modules/auth/better-auth/files/nextjs/pages/layout.tsx +11 -0
  44. package/modules/auth/better-auth/files/nextjs/pages/login/page.tsx +9 -0
  45. package/modules/auth/better-auth/files/nextjs/pages/register/page.tsx +5 -0
  46. package/modules/auth/better-auth/files/nextjs/pages/reset-password/page.tsx +10 -0
  47. package/modules/auth/better-auth/files/nextjs/pages/verify-email/page.tsx +10 -0
  48. package/modules/auth/better-auth/files/nextjs/proxy.ts +154 -42
  49. package/modules/auth/better-auth/files/nextjs/theme/providers/theme-provider.tsx +11 -0
  50. package/modules/auth/better-auth/files/nextjs/types/api.types.ts +18 -0
  51. package/modules/auth/better-auth/files/react/components/protected-route.tsx +39 -0
  52. package/modules/auth/better-auth/files/react/components/route-guards.tsx +13 -0
  53. package/modules/auth/better-auth/files/react/dashboard/admin/pages/overview.tsx +3 -0
  54. package/modules/auth/better-auth/files/react/dashboard/pages/overview.tsx +3 -0
  55. package/modules/auth/better-auth/files/react/features/pages/forgot-password.tsx +5 -0
  56. package/modules/auth/better-auth/files/react/features/pages/login.tsx +5 -0
  57. package/modules/auth/better-auth/files/react/features/pages/my-profile.tsx +5 -0
  58. package/modules/auth/better-auth/files/react/features/pages/oauth-callback.tsx +59 -0
  59. package/modules/auth/better-auth/files/react/features/pages/register.tsx +5 -0
  60. package/modules/auth/better-auth/files/react/features/pages/reset-password.tsx +10 -0
  61. package/modules/auth/better-auth/files/react/features/pages/verify-email.tsx +10 -0
  62. package/modules/auth/better-auth/files/react/layout/dashboard-layout.tsx +54 -0
  63. package/modules/auth/better-auth/files/react/lib/axios/http.ts +68 -0
  64. package/modules/auth/better-auth/files/react/lib/env.ts +25 -0
  65. package/modules/auth/better-auth/files/react/router.tsx +73 -0
  66. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider-context.ts +13 -0
  67. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider.tsx +51 -0
  68. package/modules/auth/better-auth/files/react/theme/hooks/use-theme.ts +8 -0
  69. package/modules/auth/better-auth/files/shared/features/components/change-password-dialog.tsx +113 -0
  70. package/modules/auth/better-auth/files/shared/features/components/forgot-password-form.tsx +84 -0
  71. package/modules/auth/better-auth/files/shared/features/components/login-form.tsx +134 -0
  72. package/modules/auth/better-auth/files/shared/features/components/my-profile.tsx +147 -0
  73. package/modules/auth/better-auth/files/shared/features/components/profile-form.tsx +205 -0
  74. package/modules/auth/better-auth/files/shared/features/components/register-form.tsx +100 -0
  75. package/modules/auth/better-auth/files/shared/features/components/reset-password-form.tsx +111 -0
  76. package/modules/auth/better-auth/files/shared/features/components/social-login-buttons.tsx +47 -0
  77. package/modules/auth/better-auth/files/shared/features/components/user-profile-menu.tsx +106 -0
  78. package/modules/auth/better-auth/files/shared/features/components/verify-email-form.tsx +110 -0
  79. package/modules/auth/better-auth/files/shared/features/queries/auth.mutations.tsx +312 -0
  80. package/modules/auth/better-auth/files/shared/features/queries/auth.querie.ts +19 -0
  81. package/modules/auth/better-auth/files/shared/features/services/auth.api.ts +81 -0
  82. package/modules/auth/better-auth/files/shared/features/types/auth.type.ts +47 -0
  83. package/modules/auth/better-auth/files/shared/features/validators/change-password.validator.ts +18 -0
  84. package/modules/auth/better-auth/files/shared/features/validators/forgot.validator.ts +7 -0
  85. package/modules/auth/better-auth/files/shared/features/validators/login.validator.ts +14 -0
  86. package/modules/auth/better-auth/files/shared/features/validators/profile.validator.ts +8 -0
  87. package/modules/auth/better-auth/files/shared/features/validators/register.validator.ts +9 -0
  88. package/modules/auth/better-auth/files/shared/features/validators/reset.validator.ts +9 -0
  89. package/modules/auth/better-auth/files/shared/features/validators/verify.validator.ts +8 -0
  90. package/modules/auth/better-auth/files/shared/lib/auth-client.ts +2 -1
  91. package/modules/auth/better-auth/files/shared/lib/auth.ts +5 -19
  92. package/modules/auth/better-auth/files/shared/lib/constant/dashboard.ts +90 -0
  93. package/modules/auth/better-auth/files/shared/theme/mode-toggle.tsx +30 -0
  94. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-header.tsx +94 -0
  95. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-sidebar.tsx +255 -0
  96. package/modules/auth/better-auth/files/shared/ui/shadcn/components/footer.tsx +35 -0
  97. package/modules/auth/better-auth/files/shared/ui/shadcn/components/navbar.tsx +145 -0
  98. package/modules/auth/better-auth/files/shared/ui/shadcn/form-field/input-field.tsx +440 -0
  99. package/modules/auth/better-auth/files/shared/utils/email.ts +2 -17
  100. package/modules/auth/better-auth/generator.json +172 -51
  101. package/modules/auth/better-auth/module.json +2 -2
  102. package/modules/components/files/shared/hooks/use-file-upload.ts +412 -0
  103. package/modules/components/files/shared/lib/utils/url-helpers.ts +110 -0
  104. package/modules/components/files/shared/shadcn/dashboard/data-table-column-selector.tsx +52 -0
  105. package/modules/components/files/shared/shadcn/dashboard/data-table-footer.tsx +156 -0
  106. package/modules/components/files/shared/shadcn/dashboard/data-table.tsx +405 -0
  107. package/modules/components/files/shared/shadcn/global/form-field/input-field.tsx +440 -0
  108. package/modules/components/files/shared/shadcn/global/form-field/media-uploader-field.tsx +745 -0
  109. package/modules/components/files/shared/shadcn/global/form-field/multi-select-field.tsx +207 -0
  110. package/modules/components/files/shared/shadcn/global/form-field/select-field.tsx +247 -0
  111. package/modules/components/files/shared/shadcn/global/form-field/textarea-field.tsx +277 -0
  112. package/modules/components/files/shared/shadcn/global/form-field/tiptap-editor-field.tsx +35 -0
  113. package/modules/components/files/shared/shadcn/global/no-results.tsx +41 -0
  114. package/modules/components/files/shared/shadcn/tiptap-editor/editor-menu-bar.tsx +217 -0
  115. package/modules/components/files/shared/shadcn/tiptap-editor/tiptap-editor.tsx +104 -0
  116. package/modules/components/files/shared/url/load-more.tsx +93 -0
  117. package/modules/components/files/shared/url/search-bar.tsx +131 -0
  118. package/modules/components/files/shared/url/sort-select.tsx +118 -0
  119. package/modules/components/files/shared/url/url-tabs.tsx +77 -0
  120. package/modules/components/generator.json +109 -0
  121. package/modules/components/module.json +11 -0
  122. package/modules/database/mongoose/generator.json +3 -14
  123. package/modules/database/mongoose/module.json +2 -2
  124. package/modules/database/prisma/generator.json +6 -12
  125. package/modules/database/prisma/module.json +2 -2
  126. package/modules/storage/cloudinary/files/express/config/env.ts +65 -0
  127. package/modules/storage/cloudinary/files/express/config/media.ts +103 -0
  128. package/modules/storage/cloudinary/files/express/modules/media/media.controller.ts +59 -0
  129. package/modules/storage/cloudinary/files/express/modules/media/media.route.ts +29 -0
  130. package/modules/storage/cloudinary/files/express/modules/media/media.service.ts +113 -0
  131. package/modules/storage/cloudinary/files/express/modules/media/media.type.ts +32 -0
  132. package/modules/storage/cloudinary/generator.json +34 -0
  133. package/modules/storage/cloudinary/module.json +11 -0
  134. package/modules/ui/shadcn/generator.json +21 -0
  135. package/modules/ui/shadcn/module.json +11 -0
  136. package/package.json +24 -26
  137. package/templates/express/README.md +11 -16
  138. package/templates/express/src/config/env.ts +7 -5
  139. package/templates/nextjs/README.md +13 -18
  140. package/templates/nextjs/app/favicon.ico +0 -0
  141. package/templates/nextjs/app/layout.tsx +6 -4
  142. package/templates/nextjs/components/providers/query-provider.tsx +3 -0
  143. package/templates/nextjs/env.example +3 -1
  144. package/templates/nextjs/lib/axios/http.ts +23 -0
  145. package/templates/nextjs/lib/env.ts +7 -5
  146. package/templates/nextjs/package.json +2 -1
  147. package/templates/nextjs/template.json +1 -2
  148. package/templates/react/README.md +9 -14
  149. package/templates/react/index.html +1 -1
  150. package/templates/react/package.json +1 -1
  151. package/templates/react/src/assets/favicon.ico +0 -0
  152. package/templates/react/src/components/providers/query-provider.tsx +38 -0
  153. package/templates/react/src/{shared/components → components}/seo.tsx +4 -8
  154. package/templates/react/src/lib/axios/http.ts +24 -0
  155. package/templates/react/src/main.tsx +8 -11
  156. package/templates/react/src/{features/about/pages → pages}/about.tsx +1 -1
  157. package/templates/react/src/{features/home/pages → pages}/home.tsx +1 -1
  158. package/templates/react/src/router.tsx +6 -6
  159. package/templates/react/src/vite-env.d.ts +2 -1
  160. package/templates/react/template.json +0 -1
  161. package/templates/react/tsconfig.app.json +6 -0
  162. package/templates/react/tsconfig.json +7 -1
  163. package/templates/react/vite.config.ts +12 -0
  164. package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +0 -3
  165. package/modules/auth/authjs/files/nextjs/proxy.ts +0 -1
  166. package/modules/auth/authjs/files/shared/lib/auth.ts +0 -119
  167. package/modules/auth/authjs/files/shared/prisma/schema.prisma +0 -61
  168. package/modules/auth/authjs/generator.json +0 -64
  169. package/modules/auth/authjs/module.json +0 -13
  170. package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +0 -264
  171. package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +0 -549
  172. package/modules/auth/better-auth/files/express/templates/google-redirect.ejs +0 -24
  173. package/modules/auth/better-auth/files/nextjs/api/auth/[...all]/route.ts +0 -4
  174. package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +0 -31
  175. package/modules/auth/better-auth/files/nextjs/templates/email-otp.tsx +0 -74
  176. package/templates/nextjs/lib/api/http.ts +0 -40
  177. package/templates/react/public/vite.svg +0 -1
  178. package/templates/react/src/app/layouts/dashboard-layout.tsx +0 -8
  179. package/templates/react/src/app/layouts/public-layout.tsx +0 -5
  180. package/templates/react/src/app/providers.tsx +0 -20
  181. package/templates/react/src/app/router.tsx +0 -21
  182. package/templates/react/src/assets/react.svg +0 -1
  183. package/templates/react/src/shared/api/http.ts +0 -39
  184. package/templates/react/src/shared/components/loading.tsx +0 -8
  185. package/templates/react/src/shared/lib/query-client.ts +0 -12
  186. package/templates/react/src/utils/storage.ts +0 -35
  187. package/templates/react/src/utils/utils.ts +0 -3
  188. /package/modules/auth/better-auth/files/{shared/mongoose/auth/constants.ts → express/mongo-modules/auth.constants.ts} +0 -0
  189. /package/templates/nextjs/app/{page.tsx → (public)/(root)/page.tsx} +0 -0
  190. /package/templates/react/src/{shared/components → components}/error-boundary.tsx +0 -0
  191. /package/templates/react/src/{shared/components → components}/layout.tsx +0 -0
  192. /package/templates/react/src/{shared/pages → pages}/not-found.tsx +0 -0
@@ -0,0 +1,664 @@
1
+ import status from "http-status";
2
+ import { JwtPayload } from "jsonwebtoken";
3
+ import { envVars } from "../../config/env";
4
+ {{#if database == "prisma"}}
5
+ import { prisma } from "../../database/prisma";
6
+ {{/if}}
7
+ {{#if database == "mongoose"}}
8
+ import { Types } from "mongoose";
9
+ import { getAuthCollections } from "./auth.helper";
10
+ {{/if}}
11
+ import { auth } from "../../lib/auth";
12
+ import { AppError } from "../../shared/errors/app-error";
13
+ import { jwtUtils } from "../../shared/utils/jwt";
14
+ import { tokenUtils } from "../../shared/utils/token";
15
+ import {
16
+ IChangePasswordPayload,
17
+ ILoginUserPayload,
18
+ IRegisterUserPayload,
19
+ IRequestUser,
20
+ ISocialLoginSession,
21
+ type NeedsVerification,
22
+ } from "./auth.type";
23
+
24
+ const registerUser = async (payload: IRegisterUserPayload) => {
25
+ const { name, email, password } = payload;
26
+
27
+ if (email) {
28
+ {{#if database == "prisma"}}
29
+ const existingUser = await prisma.user.findUnique({
30
+ where: {
31
+ email: email,
32
+ },
33
+ });
34
+ {{/if}}
35
+ {{#if database == "mongoose"}}
36
+ const { users } = await getAuthCollections();
37
+ const existingUser = await users.findOne({ email });
38
+ {{/if}}
39
+
40
+ if (existingUser) {
41
+ throw new AppError(status.CONFLICT, "Email already exists");
42
+ }
43
+ }
44
+
45
+ const data = await auth.api.signUpEmail({
46
+ body: {
47
+ name,
48
+ email,
49
+ password,
50
+ },
51
+ });
52
+
53
+ if (!data.user) {
54
+ throw new AppError(status.BAD_REQUEST, "Failed to register user");
55
+ }
56
+
57
+ try {
58
+ const accessToken = tokenUtils.getAccessToken({
59
+ userId: data.user.id,
60
+ role: data.user.role,
61
+ name: data.user.name,
62
+ email: data.user.email,
63
+ status: data.user.status,
64
+ isDeleted: data.user.isDeleted,
65
+ emailVerified: data.user.emailVerified,
66
+ });
67
+
68
+ const refreshToken = tokenUtils.getRefreshToken({
69
+ userId: data.user.id,
70
+ role: data.user.role,
71
+ name: data.user.name,
72
+ email: data.user.email,
73
+ status: data.user.status,
74
+ isDeleted: data.user.isDeleted,
75
+ emailVerified: data.user.emailVerified,
76
+ });
77
+
78
+ return {
79
+ ...data,
80
+ accessToken,
81
+ refreshToken,
82
+ user: data.user,
83
+ };
84
+ } catch (error) {
85
+ {{#if database == "prisma"}}
86
+ await prisma.user.delete({
87
+ where: {
88
+ id: data.user.id,
89
+ },
90
+ });
91
+ {{/if}}
92
+ {{#if database == "mongoose"}}
93
+ const { users: rollbackUsers } = await getAuthCollections();
94
+ await rollbackUsers.deleteOne({ _id: new Types.ObjectId(data.user.id) });
95
+ {{/if}}
96
+ throw error;
97
+ }
98
+
99
+ }
100
+
101
+ const loginUser = async (payload: ILoginUserPayload) => {
102
+ const { email, password } = payload;
103
+
104
+ let data;
105
+ try {
106
+ data = await auth.api.signInEmail({
107
+ body: {
108
+ email,
109
+ password,
110
+ },
111
+ });
112
+ } catch (err: unknown) {
113
+ let msg = "";
114
+ if (err instanceof Error) msg = err.message;
115
+ else if (err && typeof err === "object") {
116
+ try {
117
+ msg = JSON.stringify(err);
118
+ } catch {
119
+ msg = String(err);
120
+ }
121
+ } else {
122
+ msg = String(err);
123
+ }
124
+
125
+ if (
126
+ msg.includes("Email not verified") ||
127
+ msg.toLowerCase().includes("email not verified")
128
+ ) {
129
+ const needsVerification = { needsVerification: true, email };
130
+ return needsVerification as NeedsVerification;
131
+ }
132
+ throw err;
133
+ }
134
+
135
+ if (data.user.status === "BLOCKED") {
136
+ throw new AppError(status.FORBIDDEN, "User is blocked");
137
+ }
138
+
139
+ if (data.user.isDeleted || data.user.status === "DELETED") {
140
+ throw new AppError(status.NOT_FOUND, "User is deleted");
141
+ }
142
+
143
+ const accessToken = tokenUtils.getAccessToken({
144
+ userId: data.user.id,
145
+ role: data.user.role,
146
+ name: data.user.name,
147
+ email: data.user.email,
148
+ status: data.user.status,
149
+ isDeleted: data.user.isDeleted,
150
+ emailVerified: data.user.emailVerified,
151
+ });
152
+
153
+ const refreshToken = tokenUtils.getRefreshToken({
154
+ userId: data.user.id,
155
+ role: data.user.role,
156
+ name: data.user.name,
157
+ email: data.user.email,
158
+ status: data.user.status,
159
+ isDeleted: data.user.isDeleted,
160
+ emailVerified: data.user.emailVerified,
161
+ });
162
+
163
+ return {
164
+ ...data,
165
+ accessToken,
166
+ refreshToken,
167
+ };
168
+ }
169
+
170
+ const resendOTP = async (email: string) => {
171
+ {{#if database == "prisma"}}
172
+ const isUserExist = await prisma.user.findUnique({ where: { email } });
173
+ {{/if}}
174
+ {{#if database == "mongoose"}}
175
+ const { users, verifications } = await getAuthCollections();
176
+ const isUserExist = await users.findOne({ email });
177
+ {{/if}}
178
+ if (!isUserExist) {
179
+ throw new AppError(status.NOT_FOUND, "User not found");
180
+ }
181
+ if (isUserExist.emailVerified) {
182
+ throw new AppError(status.BAD_REQUEST, "Email already verified");
183
+ }
184
+
185
+ {{#if database == "prisma"}}
186
+ const existingVerification = await prisma.verification.findFirst({
187
+ where: {
188
+ identifier: email,
189
+ expiresAt: {
190
+ gt: new Date(),
191
+ },
192
+ },
193
+ });
194
+ {{/if}}
195
+ {{#if database == "mongoose"}}
196
+ const existingVerification = await verifications.findOne({
197
+ identifier: email,
198
+ expiresAt: { $gt: new Date() },
199
+ });
200
+ {{/if}}
201
+
202
+ if (existingVerification) {
203
+ throw new AppError(
204
+ status.TOO_MANY_REQUESTS,
205
+ "A verification email was already sent recently. Please check your inbox or try again later.",
206
+ );
207
+ }
208
+
209
+ try {
210
+ const sdk = auth.api as unknown as Record<
211
+ string,
212
+ (...args: unknown[]) => Promise<unknown>
213
+ >;
214
+
215
+ if (typeof sdk.requestEmailVerificationOTP === "function") {
216
+ await sdk.requestEmailVerificationOTP({ body: { email } } as unknown);
217
+ } else if (typeof sdk.requestVerificationEmailOTP === "function") {
218
+ await sdk.requestVerificationEmailOTP({ body: { email } } as unknown);
219
+ } else if (typeof sdk.requestEmailOTP === "function") {
220
+ await sdk.requestEmailOTP({
221
+ body: { email, type: "email-verification" },
222
+ } as unknown);
223
+ } else if (typeof sdk["requestSignInOTP"] === "function") {
224
+ await sdk["requestSignInOTP"]({ body: { email } } as unknown);
225
+ } else {
226
+ throw new Error(
227
+ "No suitable method available on auth SDK to request verification OTP",
228
+ );
229
+ }
230
+ } catch {
231
+ throw new AppError(
232
+ status.INTERNAL_SERVER_ERROR,
233
+ "Failed to resend verification OTP",
234
+ );
235
+ }
236
+ };
237
+
238
+ const getMe = async (user : IRequestUser) => {
239
+ {{#if database == "prisma"}}
240
+ const isUserExists = await prisma.user.findUnique({
241
+ where: {
242
+ id: user.id,
243
+ },
244
+ });
245
+ {{/if}}
246
+ {{#if database == "mongoose"}}
247
+ const { users } = await getAuthCollections();
248
+ const isUserExists = await users.findOne({ _id: new Types.ObjectId(user.id) });
249
+ {{/if}}
250
+
251
+ if (!isUserExists) {
252
+ throw new AppError(status.NOT_FOUND, "User not found");
253
+ }
254
+
255
+ return isUserExists;
256
+ }
257
+
258
+ const updateProfile = async (
259
+ user: IRequestUser,
260
+ payload: { name?: string; image?: string },
261
+ ) => {
262
+ {{#if database == "prisma"}}
263
+ const isUserExists = await prisma.user.findUnique({
264
+ where: {
265
+ id: user.id,
266
+ },
267
+ });
268
+ {{/if}}
269
+ {{#if database == "mongoose"}}
270
+ const { users } = await getAuthCollections();
271
+ const isUserExists = await users.findOne({ _id: new Types.ObjectId(user.id) });
272
+ {{/if}}
273
+
274
+ if (!isUserExists) {
275
+ throw new AppError(status.NOT_FOUND, "User not found");
276
+ }
277
+
278
+ if (isUserExists.isDeleted || isUserExists.status === "DELETED") {
279
+ throw new AppError(status.NOT_FOUND, "User not found");
280
+ }
281
+
282
+ {{#if database == "prisma"}}
283
+ const updated = await prisma.user.update({
284
+ where: { id: user.id },
285
+ data: {
286
+ name: payload.name || isUserExists.name,
287
+ image: payload.image || isUserExists.image,
288
+ updatedAt: new Date(),
289
+ },
290
+ });
291
+ {{/if}}
292
+ {{#if database == "mongoose"}}
293
+ await users.updateOne(
294
+ { _id: new Types.ObjectId(user.id) },
295
+ {
296
+ $set: {
297
+ name: payload.name || isUserExists.name,
298
+ image: payload.image || isUserExists.image,
299
+ updatedAt: new Date(),
300
+ },
301
+ }
302
+ );
303
+ const updated = await users.findOne({ _id: new Types.ObjectId(user.id) });
304
+ {{/if}}
305
+
306
+ return updated;
307
+ };
308
+
309
+ const getNewToken = async (refreshToken : string, sessionToken : string) => {
310
+ {{#if database == "prisma"}}
311
+ const isSessionTokenExists = await prisma.session.findUnique({
312
+ where: {
313
+ token: sessionToken,
314
+ },
315
+ include: {
316
+ user: true,
317
+ },
318
+ });
319
+ {{/if}}
320
+ {{#if database == "mongoose"}}
321
+ const { sessions } = await getAuthCollections();
322
+ const isSessionTokenExists = await sessions.findOne({ token: sessionToken });
323
+ {{/if}}
324
+
325
+ if(!isSessionTokenExists){
326
+ throw new AppError(status.UNAUTHORIZED, "Invalid session token");
327
+ }
328
+
329
+ const verifiedRefreshToken = jwtUtils.verifyToken(refreshToken, envVars.REFRESH_TOKEN_SECRET)
330
+
331
+ if(!verifiedRefreshToken.success && verifiedRefreshToken.error){
332
+ throw new AppError(status.UNAUTHORIZED, "Invalid refresh token");
333
+ }
334
+
335
+ const data = verifiedRefreshToken.data as JwtPayload;
336
+
337
+ const newAccessToken = tokenUtils.getAccessToken({
338
+ userId: data.userId,
339
+ role: data.role,
340
+ name: data.name,
341
+ email: data.email,
342
+ status: data.status,
343
+ isDeleted: data.isDeleted,
344
+ emailVerified: data.emailVerified,
345
+ });
346
+
347
+ const newRefreshToken = tokenUtils.getRefreshToken({
348
+ userId: data.userId,
349
+ role: data.role,
350
+ name: data.name,
351
+ email: data.email,
352
+ status: data.status,
353
+ isDeleted: data.isDeleted,
354
+ emailVerified: data.emailVerified,
355
+ });
356
+
357
+ {{#if database == "prisma"}}
358
+ const { token } = await prisma.session.update({
359
+ where: {
360
+ token: sessionToken,
361
+ },
362
+ data: {
363
+ token: sessionToken,
364
+ expiresAt: new Date(Date.now() + 60 * 60 * 60 * 24 * 1000),
365
+ updatedAt: new Date(),
366
+ },
367
+ });
368
+ {{/if}}
369
+ {{#if database == "mongoose"}}
370
+ await sessions.updateOne(
371
+ { token: sessionToken },
372
+ {
373
+ $set: {
374
+ expiresAt: new Date(Date.now() + 60 * 60 * 60 * 24 * 1000),
375
+ updatedAt: new Date(),
376
+ },
377
+ }
378
+ );
379
+ const token = sessionToken;
380
+ {{/if}}
381
+
382
+ return {
383
+ accessToken : newAccessToken,
384
+ refreshToken : newRefreshToken,
385
+ sessionToken : token,
386
+ };
387
+ }
388
+
389
+ const changePassword = async (payload : IChangePasswordPayload, sessionToken : string) =>{
390
+ const session = await auth.api.getSession({
391
+ headers : new Headers({
392
+ Authorization : `Bearer ${sessionToken}`
393
+ })
394
+ })
395
+
396
+ if(!session){
397
+ throw new AppError(status.UNAUTHORIZED, "Invalid session token");
398
+ }
399
+
400
+ const {currentPassword, newPassword} = payload;
401
+
402
+ const result = await auth.api.changePassword({
403
+ body :{
404
+ currentPassword,
405
+ newPassword,
406
+ revokeOtherSessions: true,
407
+ },
408
+ headers : new Headers({
409
+ Authorization : `Bearer ${sessionToken}`
410
+ })
411
+ })
412
+
413
+ if(session.user.needPasswordChange){
414
+ {{#if database == "prisma"}}
415
+ await prisma.user.update({
416
+ where: {
417
+ id: session.user.id,
418
+ },
419
+ data: {
420
+ needPasswordChange: false,
421
+ }
422
+ });
423
+ {{/if}}
424
+ {{#if database == "mongoose"}}
425
+ const { users } = await getAuthCollections();
426
+ await users.updateOne(
427
+ { _id: new Types.ObjectId(session.user.id) },
428
+ { $set: { needPasswordChange: false } }
429
+ );
430
+ {{/if}}
431
+ }
432
+
433
+ const accessToken = tokenUtils.getAccessToken({
434
+ userId: session.user.id,
435
+ role: session.user.role,
436
+ name: session.user.name,
437
+ email: session.user.email,
438
+ status: session.user.status,
439
+ isDeleted: session.user.isDeleted,
440
+ emailVerified: session.user.emailVerified,
441
+ });
442
+
443
+ const refreshToken = tokenUtils.getRefreshToken({
444
+ userId: session.user.id,
445
+ role: session.user.role,
446
+ name: session.user.name,
447
+ email: session.user.email,
448
+ status: session.user.status,
449
+ isDeleted: session.user.isDeleted,
450
+ emailVerified: session.user.emailVerified,
451
+ });
452
+
453
+
454
+ return {
455
+ ...result,
456
+ accessToken,
457
+ refreshToken,
458
+ }
459
+ }
460
+
461
+ const logoutUser = async (sessionToken : string) => {
462
+ const result = await auth.api.signOut({
463
+ headers : new Headers({
464
+ Authorization : `Bearer ${sessionToken}`
465
+ })
466
+ })
467
+
468
+ return result;
469
+ }
470
+
471
+ const verifyEmail = async (email : string, otp : string) => {
472
+
473
+ const result = await auth.api.verifyEmailOTP({
474
+ body:{
475
+ email,
476
+ otp,
477
+ }
478
+ })
479
+
480
+ if(result.status && !result.user.emailVerified){
481
+ {{#if database == "prisma"}}
482
+ await prisma.user.update({
483
+ where : {
484
+ email,
485
+ },
486
+ data : {
487
+ emailVerified: true,
488
+ }
489
+ });
490
+ {{/if}}
491
+ {{#if database == "mongoose"}}
492
+ const { users } = await getAuthCollections();
493
+ await users.updateOne(
494
+ { email },
495
+ { $set: { emailVerified: true } }
496
+ );
497
+ {{/if}}
498
+ }
499
+ }
500
+
501
+ const forgetPassword = async (email : string) => {
502
+ {{#if database == "prisma"}}
503
+ const isUserExist = await prisma.user.findUnique({
504
+ where : {
505
+ email,
506
+ }
507
+ });
508
+ {{/if}}
509
+ {{#if database == "mongoose"}}
510
+ const { users } = await getAuthCollections();
511
+ const isUserExist = await users.findOne({ email });
512
+ {{/if}}
513
+
514
+ if(!isUserExist){
515
+ throw new AppError(status.NOT_FOUND, "User not found");
516
+ }
517
+
518
+ if(!isUserExist.emailVerified){
519
+ throw new AppError(status.BAD_REQUEST, "Email not verified");
520
+ }
521
+
522
+ if (isUserExist.isDeleted || isUserExist.status === "DELETED") {
523
+ throw new AppError(status.NOT_FOUND, "User not found");
524
+ }
525
+
526
+ await auth.api.requestPasswordResetEmailOTP({
527
+ body:{
528
+ email,
529
+ }
530
+ })
531
+ }
532
+
533
+ const resetPassword = async (email : string, otp : string, newPassword : string) => {
534
+ {{#if database == "prisma"}}
535
+ const isUserExist = await prisma.user.findUnique({
536
+ where : {
537
+ email,
538
+ }
539
+ });
540
+ {{/if}}
541
+ {{#if database == "mongoose"}}
542
+ const { users, sessions } = await getAuthCollections();
543
+ const isUserExist = await users.findOne({ email });
544
+ {{/if}}
545
+
546
+ if (!isUserExist) {
547
+ throw new AppError(status.NOT_FOUND, "User not found");
548
+ }
549
+
550
+ if (!isUserExist.emailVerified) {
551
+ throw new AppError(status.BAD_REQUEST, "Email not verified");
552
+ }
553
+
554
+ if (isUserExist.isDeleted || isUserExist.status === "DELETED") {
555
+ throw new AppError(status.NOT_FOUND, "User not found");
556
+ }
557
+
558
+ await auth.api.resetPasswordEmailOTP({
559
+ body:{
560
+ email,
561
+ otp,
562
+ password : newPassword,
563
+ }
564
+ })
565
+
566
+ if (isUserExist.needPasswordChange) {
567
+ {{#if database == "prisma"}}
568
+ await prisma.user.update({
569
+ where: {
570
+ email,
571
+ },
572
+ data: {
573
+ needPasswordChange: false,
574
+ }
575
+ });
576
+ {{/if}}
577
+ {{#if database == "mongoose"}}
578
+ await users.updateOne(
579
+ { email },
580
+ { $set: { needPasswordChange: false } }
581
+ );
582
+ {{/if}}
583
+ }
584
+
585
+ {{#if database == "prisma"}}
586
+ await prisma.session.deleteMany({
587
+ where:{
588
+ userId : isUserExist.id,
589
+ }
590
+ });
591
+ {{/if}}
592
+ {{#if database == "mongoose"}}
593
+ await sessions.deleteMany({ userId: isUserExist.id });
594
+ {{/if}}
595
+ }
596
+
597
+ const socialLoginSuccess = async (session: ISocialLoginSession) => {
598
+ {{#if database == "prisma"}}
599
+ const user = await prisma.user.findUnique({
600
+ where: { id: session.user.id },
601
+ });
602
+ {{/if}}
603
+ {{#if database == "mongoose"}}
604
+ const { users } = await getAuthCollections();
605
+ const user = await users.findOne({ _id: new Types.ObjectId(session.user.id) });
606
+ {{/if}}
607
+
608
+ if (!user) {
609
+ throw new AppError(
610
+ status.NOT_FOUND,
611
+ "User not found after social login. Please try again.",
612
+ );
613
+ }
614
+
615
+ if (user.status === "BLOCKED") {
616
+ throw new AppError(status.FORBIDDEN, "Your account has been blocked.");
617
+ }
618
+
619
+ if (user.isDeleted || user.status === "DELETED") {
620
+ throw new AppError(status.FORBIDDEN, "Your account has been deleted.");
621
+ }
622
+
623
+ const accessToken = tokenUtils.getAccessToken({
624
+ userId: user.id,
625
+ role: user.role,
626
+ name: user.name,
627
+ email: user.email,
628
+ status: user.status,
629
+ isDeleted: user.isDeleted,
630
+ emailVerified: user.emailVerified,
631
+ });
632
+
633
+ const refreshToken = tokenUtils.getRefreshToken({
634
+ userId: user.id,
635
+ role: user.role,
636
+ name: user.name,
637
+ email: user.email,
638
+ status: user.status,
639
+ isDeleted: user.isDeleted,
640
+ emailVerified: user.emailVerified,
641
+ });
642
+
643
+ return { accessToken, refreshToken };
644
+ };
645
+
646
+ const googleLoginSuccess = async (session: ISocialLoginSession) => {
647
+ return socialLoginSuccess(session);
648
+ };
649
+
650
+ export const authService = {
651
+ registerUser,
652
+ loginUser,
653
+ getMe,
654
+ updateProfile,
655
+ getNewToken,
656
+ changePassword,
657
+ logoutUser,
658
+ verifyEmail,
659
+ resendOTP,
660
+ forgetPassword,
661
+ resetPassword,
662
+ socialLoginSuccess,
663
+ googleLoginSuccess,
664
+ };
@@ -1,20 +1,15 @@
1
1
  {{#if database == "prisma"}}
2
2
  import { Role } from "@prisma/client";
3
-
4
- export interface IRequestUser {
5
- id: string;
6
- role: Role | string;
7
- email: string;
8
- }
9
3
  {{/if}}
10
4
  {{#if database == "mongoose"}}
11
- import { Role } from './auth.constants';
5
+ import { Role } from "./auth.constants";
6
+ {{/if}}
7
+
12
8
  export interface IRequestUser {
13
9
  id: string;
14
- role: typeof Role | string;
10
+ role: Role | string;
15
11
  email: string;
16
12
  }
17
- {{/if}}
18
13
 
19
14
  export interface ILoginUserPayload {
20
15
  email: string;
@@ -31,3 +26,21 @@ export interface IChangePasswordPayload {
31
26
  currentPassword: string;
32
27
  newPassword: string;
33
28
  }
29
+
30
+ export type NeedsVerification = {
31
+ needsVerification: true;
32
+ email: string;
33
+ };
34
+
35
+ export type SocialProvider =
36
+ | "google"
37
+ | "github"
38
+ | "facebook"
39
+ | "twitter"
40
+ | "discord"
41
+ | "linkedin"
42
+ | "apple";
43
+
44
+ export type ISocialLoginSession = {
45
+ user: { id: string };
46
+ };