stackkit 0.3.4 → 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 (203) 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 -52
  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} +12 -7
  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/constants.ts → express/mongo-modules/auth.constants.ts} +0 -1
  28. package/modules/auth/better-auth/files/{shared/mongoose/auth/helper.ts → express/mongo-modules/auth.helper.ts} +11 -1
  29. package/modules/auth/better-auth/files/express/types/express.d.ts +11 -0
  30. package/modules/auth/better-auth/files/nextjs/api-route.ts +74 -0
  31. package/modules/auth/better-auth/files/nextjs/dashboard/pages/(user)/page.tsx +6 -0
  32. package/modules/auth/better-auth/files/nextjs/dashboard/pages/admin/page.tsx +6 -0
  33. package/modules/auth/better-auth/files/nextjs/dashboard/pages/layout.tsx +48 -0
  34. package/modules/auth/better-auth/files/nextjs/dashboard/pages/my-profile/page.tsx +5 -0
  35. package/modules/auth/better-auth/files/nextjs/features/services/auth.service.ts +102 -0
  36. package/modules/auth/better-auth/files/nextjs/layout/layout.tsx +13 -0
  37. package/modules/auth/better-auth/files/nextjs/lib/axios/http.ts +158 -0
  38. package/modules/auth/better-auth/files/nextjs/lib/env.ts +35 -0
  39. package/modules/auth/better-auth/files/nextjs/lib/utils/auth.ts +75 -0
  40. package/modules/auth/better-auth/files/nextjs/lib/utils/cookie.ts +29 -0
  41. package/modules/auth/better-auth/files/nextjs/lib/utils/jwt.ts +28 -0
  42. package/modules/auth/better-auth/files/nextjs/lib/utils/token.ts +49 -0
  43. package/modules/auth/better-auth/files/nextjs/pages/forgot-password/page.tsx +5 -0
  44. package/modules/auth/better-auth/files/nextjs/pages/layout.tsx +11 -0
  45. package/modules/auth/better-auth/files/nextjs/pages/login/page.tsx +9 -0
  46. package/modules/auth/better-auth/files/nextjs/pages/register/page.tsx +5 -0
  47. package/modules/auth/better-auth/files/nextjs/pages/reset-password/page.tsx +10 -0
  48. package/modules/auth/better-auth/files/nextjs/pages/verify-email/page.tsx +10 -0
  49. package/modules/auth/better-auth/files/nextjs/proxy.ts +157 -22
  50. package/modules/auth/better-auth/files/nextjs/theme/providers/theme-provider.tsx +11 -0
  51. package/modules/auth/better-auth/files/nextjs/types/api.types.ts +18 -0
  52. package/modules/auth/better-auth/files/react/components/protected-route.tsx +39 -0
  53. package/modules/auth/better-auth/files/react/components/route-guards.tsx +13 -0
  54. package/modules/auth/better-auth/files/react/dashboard/admin/pages/overview.tsx +3 -0
  55. package/modules/auth/better-auth/files/react/dashboard/pages/overview.tsx +3 -0
  56. package/modules/auth/better-auth/files/react/features/pages/forgot-password.tsx +5 -0
  57. package/modules/auth/better-auth/files/react/features/pages/login.tsx +5 -0
  58. package/modules/auth/better-auth/files/react/features/pages/my-profile.tsx +5 -0
  59. package/modules/auth/better-auth/files/react/features/pages/oauth-callback.tsx +59 -0
  60. package/modules/auth/better-auth/files/react/features/pages/register.tsx +5 -0
  61. package/modules/auth/better-auth/files/react/features/pages/reset-password.tsx +10 -0
  62. package/modules/auth/better-auth/files/react/features/pages/verify-email.tsx +10 -0
  63. package/modules/auth/better-auth/files/react/layout/dashboard-layout.tsx +54 -0
  64. package/modules/auth/better-auth/files/react/lib/axios/http.ts +68 -0
  65. package/modules/auth/better-auth/files/react/lib/env.ts +25 -0
  66. package/modules/auth/better-auth/files/react/router.tsx +73 -0
  67. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider-context.ts +13 -0
  68. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider.tsx +51 -0
  69. package/modules/auth/better-auth/files/react/theme/hooks/use-theme.ts +8 -0
  70. package/modules/auth/better-auth/files/shared/features/components/change-password-dialog.tsx +113 -0
  71. package/modules/auth/better-auth/files/shared/features/components/forgot-password-form.tsx +84 -0
  72. package/modules/auth/better-auth/files/shared/features/components/login-form.tsx +134 -0
  73. package/modules/auth/better-auth/files/shared/features/components/my-profile.tsx +147 -0
  74. package/modules/auth/better-auth/files/shared/features/components/profile-form.tsx +205 -0
  75. package/modules/auth/better-auth/files/shared/features/components/register-form.tsx +100 -0
  76. package/modules/auth/better-auth/files/shared/features/components/reset-password-form.tsx +111 -0
  77. package/modules/auth/better-auth/files/shared/features/components/social-login-buttons.tsx +47 -0
  78. package/modules/auth/better-auth/files/shared/features/components/user-profile-menu.tsx +106 -0
  79. package/modules/auth/better-auth/files/shared/features/components/verify-email-form.tsx +110 -0
  80. package/modules/auth/better-auth/files/shared/features/queries/auth.mutations.tsx +312 -0
  81. package/modules/auth/better-auth/files/shared/features/queries/auth.querie.ts +19 -0
  82. package/modules/auth/better-auth/files/shared/features/services/auth.api.ts +81 -0
  83. package/modules/auth/better-auth/files/shared/features/types/auth.type.ts +47 -0
  84. package/modules/auth/better-auth/files/shared/features/validators/change-password.validator.ts +18 -0
  85. package/modules/auth/better-auth/files/shared/features/validators/forgot.validator.ts +7 -0
  86. package/modules/auth/better-auth/files/shared/features/validators/login.validator.ts +14 -0
  87. package/modules/auth/better-auth/files/shared/features/validators/profile.validator.ts +8 -0
  88. package/modules/auth/better-auth/files/shared/features/validators/register.validator.ts +9 -0
  89. package/modules/auth/better-auth/files/shared/features/validators/reset.validator.ts +9 -0
  90. package/modules/auth/better-auth/files/shared/features/validators/verify.validator.ts +8 -0
  91. package/modules/auth/better-auth/files/shared/lib/auth-client.ts +2 -1
  92. package/modules/auth/better-auth/files/shared/lib/auth.ts +10 -29
  93. package/modules/auth/better-auth/files/shared/lib/constant/dashboard.ts +90 -0
  94. package/modules/auth/better-auth/files/shared/prisma/enums.prisma +0 -1
  95. package/modules/auth/better-auth/files/shared/theme/mode-toggle.tsx +30 -0
  96. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-header.tsx +94 -0
  97. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-sidebar.tsx +255 -0
  98. package/modules/auth/better-auth/files/shared/ui/shadcn/components/footer.tsx +35 -0
  99. package/modules/auth/better-auth/files/shared/ui/shadcn/components/navbar.tsx +145 -0
  100. package/modules/auth/better-auth/files/shared/ui/shadcn/form-field/input-field.tsx +440 -0
  101. package/modules/auth/better-auth/files/shared/utils/email.ts +20 -18
  102. package/modules/auth/better-auth/generator.json +174 -53
  103. package/modules/auth/better-auth/module.json +2 -2
  104. package/modules/components/files/shared/hooks/use-file-upload.ts +412 -0
  105. package/modules/components/files/shared/lib/utils/url-helpers.ts +110 -0
  106. package/modules/components/files/shared/shadcn/dashboard/data-table-column-selector.tsx +52 -0
  107. package/modules/components/files/shared/shadcn/dashboard/data-table-footer.tsx +156 -0
  108. package/modules/components/files/shared/shadcn/dashboard/data-table.tsx +405 -0
  109. package/modules/components/files/shared/shadcn/global/form-field/input-field.tsx +440 -0
  110. package/modules/components/files/shared/shadcn/global/form-field/media-uploader-field.tsx +745 -0
  111. package/modules/components/files/shared/shadcn/global/form-field/multi-select-field.tsx +207 -0
  112. package/modules/components/files/shared/shadcn/global/form-field/select-field.tsx +247 -0
  113. package/modules/components/files/shared/shadcn/global/form-field/textarea-field.tsx +277 -0
  114. package/modules/components/files/shared/shadcn/global/form-field/tiptap-editor-field.tsx +35 -0
  115. package/modules/components/files/shared/shadcn/global/no-results.tsx +41 -0
  116. package/modules/components/files/shared/shadcn/tiptap-editor/editor-menu-bar.tsx +217 -0
  117. package/modules/components/files/shared/shadcn/tiptap-editor/tiptap-editor.tsx +104 -0
  118. package/modules/components/files/shared/url/load-more.tsx +93 -0
  119. package/modules/components/files/shared/url/search-bar.tsx +131 -0
  120. package/modules/components/files/shared/url/sort-select.tsx +118 -0
  121. package/modules/components/files/shared/url/url-tabs.tsx +77 -0
  122. package/modules/components/generator.json +109 -0
  123. package/modules/components/module.json +11 -0
  124. package/modules/database/mongoose/generator.json +3 -14
  125. package/modules/database/mongoose/module.json +2 -2
  126. package/modules/database/prisma/generator.json +6 -12
  127. package/modules/database/prisma/module.json +2 -2
  128. package/modules/storage/cloudinary/files/express/config/env.ts +65 -0
  129. package/modules/storage/cloudinary/files/express/config/media.ts +103 -0
  130. package/modules/storage/cloudinary/files/express/modules/media/media.controller.ts +59 -0
  131. package/modules/storage/cloudinary/files/express/modules/media/media.route.ts +29 -0
  132. package/modules/storage/cloudinary/files/express/modules/media/media.service.ts +113 -0
  133. package/modules/storage/cloudinary/files/express/modules/media/media.type.ts +32 -0
  134. package/modules/storage/cloudinary/generator.json +34 -0
  135. package/modules/storage/cloudinary/module.json +11 -0
  136. package/modules/ui/shadcn/generator.json +21 -0
  137. package/modules/ui/shadcn/module.json +11 -0
  138. package/package.json +24 -26
  139. package/templates/express/README.md +11 -16
  140. package/templates/express/src/config/env.ts +7 -5
  141. package/templates/nextjs/README.md +13 -18
  142. package/templates/nextjs/app/favicon.ico +0 -0
  143. package/templates/nextjs/app/layout.tsx +6 -4
  144. package/templates/nextjs/components/providers/query-provider.tsx +3 -0
  145. package/templates/nextjs/env.example +3 -1
  146. package/templates/nextjs/lib/axios/http.ts +23 -0
  147. package/templates/nextjs/lib/env.ts +7 -5
  148. package/templates/nextjs/package.json +2 -1
  149. package/templates/nextjs/template.json +1 -2
  150. package/templates/react/README.md +9 -14
  151. package/templates/react/index.html +1 -1
  152. package/templates/react/package.json +1 -1
  153. package/templates/react/src/assets/favicon.ico +0 -0
  154. package/templates/react/src/components/providers/query-provider.tsx +38 -0
  155. package/templates/react/src/{shared/components → components}/seo.tsx +4 -8
  156. package/templates/react/src/lib/axios/http.ts +24 -0
  157. package/templates/react/src/main.tsx +8 -11
  158. package/templates/react/src/{features/about/pages → pages}/about.tsx +1 -1
  159. package/templates/react/src/{features/home/pages → pages}/home.tsx +1 -1
  160. package/templates/react/src/router.tsx +6 -6
  161. package/templates/react/src/vite-env.d.ts +2 -1
  162. package/templates/react/template.json +0 -1
  163. package/templates/react/tsconfig.app.json +6 -0
  164. package/templates/react/tsconfig.json +7 -1
  165. package/templates/react/vite.config.ts +12 -0
  166. package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +0 -3
  167. package/modules/auth/authjs/files/nextjs/proxy.ts +0 -1
  168. package/modules/auth/authjs/files/shared/lib/auth.ts +0 -119
  169. package/modules/auth/authjs/files/shared/prisma/schema.prisma +0 -61
  170. package/modules/auth/authjs/generator.json +0 -64
  171. package/modules/auth/authjs/module.json +0 -13
  172. package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +0 -264
  173. package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +0 -537
  174. package/modules/auth/better-auth/files/express/templates/google-redirect.ejs +0 -24
  175. package/modules/auth/better-auth/files/nextjs/api/auth/[...all]/route.ts +0 -4
  176. package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +0 -41
  177. package/modules/auth/better-auth/files/nextjs/templates/email-otp.tsx +0 -74
  178. package/templates/express/node_modules/.bin/acorn +0 -17
  179. package/templates/express/node_modules/.bin/eslint +0 -17
  180. package/templates/express/node_modules/.bin/tsc +0 -17
  181. package/templates/express/node_modules/.bin/tsserver +0 -17
  182. package/templates/express/node_modules/.bin/tsx +0 -17
  183. package/templates/nextjs/lib/api/http.ts +0 -40
  184. package/templates/nextjs/next-env.d.ts +0 -6
  185. package/templates/react/dist/assets/index-D4AHT4dU.js +0 -193
  186. package/templates/react/dist/assets/index-rpwj5ZOX.css +0 -1
  187. package/templates/react/dist/index.html +0 -14
  188. package/templates/react/dist/vite.svg +0 -1
  189. package/templates/react/public/vite.svg +0 -1
  190. package/templates/react/src/app/layouts/dashboard-layout.tsx +0 -8
  191. package/templates/react/src/app/layouts/public-layout.tsx +0 -5
  192. package/templates/react/src/app/providers.tsx +0 -20
  193. package/templates/react/src/app/router.tsx +0 -21
  194. package/templates/react/src/assets/react.svg +0 -1
  195. package/templates/react/src/shared/api/http.ts +0 -39
  196. package/templates/react/src/shared/components/loading.tsx +0 -8
  197. package/templates/react/src/shared/lib/query-client.ts +0 -12
  198. package/templates/react/src/utils/storage.ts +0 -35
  199. package/templates/react/src/utils/utils.ts +0 -3
  200. /package/templates/nextjs/app/{page.tsx → (public)/(root)/page.tsx} +0 -0
  201. /package/templates/react/src/{shared/components → components}/error-boundary.tsx +0 -0
  202. /package/templates/react/src/{shared/components → components}/layout.tsx +0 -0
  203. /package/templates/react/src/{shared/pages → pages}/not-found.tsx +0 -0
@@ -0,0 +1,312 @@
1
+ {{#if framework == "nextjs"}}
2
+ "use client";
3
+
4
+ import { setTokens } from "@/features/auth/services/auth.service";
5
+ {{/if}}
6
+ import {
7
+ forgetPasswordRequest,
8
+ loginRequest,
9
+ logoutRequest,
10
+ registerRequest,
11
+ resendOTPRequest,
12
+ resetPasswordRequest,
13
+ verifyEmailRequest,
14
+ } from "@/features/auth/services/auth.api";
15
+ import { envVars } from "@/lib/env";
16
+ import { useMutation, useQueryClient } from "@tanstack/react-query";
17
+ {{#if framework == "nextjs"}}
18
+ import { useRouter } from "next/navigation";
19
+ {{else}}
20
+ import { useNavigate } from "react-router";
21
+ {{/if}}
22
+ import { toast } from "sonner";
23
+ import type { ILoginResponse, SocialProvider } from "../types/auth.type";
24
+ import type { ILoginPayload } from "../validators/login.validator";
25
+ import { ChangePassword, updateProfile } from "../services/auth.api";
26
+
27
+ export const AUTH_QUERY_KEYS = {
28
+ me: ["auth", "me"] as const,
29
+ };
30
+
31
+ export const AUTH_MUTATION_KEYS = {
32
+ register: ["auth", "register"] as const,
33
+ login: ["auth", "login"] as const,
34
+ forgotPassword: ["auth", "forgot-password"] as const,
35
+ resetPassword: ["auth", "reset-password"] as const,
36
+ verifyEmail: ["auth", "verify-email"] as const,
37
+ resendOTP: ["auth", "resend-otp"] as const,
38
+ socialLogin: ["auth", "social-login"] as const,
39
+ changePassword: ["auth", "change-password"] as const,
40
+ updateProfile: ["auth", "update-profile"] as const,
41
+ logout: ["auth", "logout"] as const,
42
+ };
43
+
44
+ export const useRegisterMutation = () => {
45
+ {{#if framework == "nextjs"}}
46
+ const router = useRouter();
47
+ const navigate = (path: string) => router.push(path);
48
+ {{else}}
49
+ const navigate = useNavigate();
50
+ {{/if}}
51
+
52
+ return useMutation({
53
+ mutationKey: AUTH_MUTATION_KEYS.register,
54
+ mutationFn: registerRequest,
55
+ onSuccess: (_data, variables) => {
56
+ navigate(`/verify-email?email=${encodeURIComponent(variables.email)}`);
57
+ toast.success("Registration successful! Please verify your email.");
58
+ },
59
+ onError: (error: unknown) => {
60
+ toast.error(
61
+ error instanceof Error
62
+ ? error.message
63
+ : "Registration failed. Please check your details and try again.",
64
+ );
65
+ },
66
+ });
67
+ };
68
+
69
+ export const useLoginMutation = () => {
70
+ {{#if framework == "nextjs"}}
71
+ const router = useRouter();
72
+ const navigate = (path: string) => router.push(path);
73
+ {{else}}
74
+ const navigate = useNavigate();
75
+ {{/if}}
76
+ const queryClient = useQueryClient();
77
+
78
+ return useMutation<ILoginResponse, unknown, ILoginPayload & { redirectPath?: string }>(
79
+ {
80
+ mutationKey: AUTH_MUTATION_KEYS.login,
81
+ mutationFn: loginRequest,
82
+ onSuccess: async (data, variables) => {
83
+ toast.success("Login successful!");
84
+
85
+ {{#if framework == "nextjs"}}
86
+ try {
87
+ await setTokens({
88
+ accessToken: data?.accessToken,
89
+ refreshToken: data?.refreshToken,
90
+ token: data?.token,
91
+ });
92
+ } catch (err) {
93
+ console.error("Failed to set tokens in cookies:", err);
94
+ }
95
+ {{/if}}
96
+
97
+ await queryClient.invalidateQueries({ queryKey: AUTH_QUERY_KEYS.me });
98
+ await new Promise((resolve) => setTimeout(resolve, 100));
99
+
100
+ const defaultRoute = data?.user?.role === "ADMIN" ? "/dashboard/admin" : "/dashboard";
101
+ navigate(variables?.redirectPath || defaultRoute);
102
+ },
103
+ onError: (error: unknown, variables) => {
104
+ if (error instanceof Error && error.message === "Email not verified") {
105
+ navigate(`/verify-email?email=${encodeURIComponent(variables.email)}`);
106
+ return;
107
+ }
108
+ toast.error(
109
+ error instanceof Error
110
+ ? error.message
111
+ : "Login failed. Please check your credentials and try again.",
112
+ );
113
+ },
114
+ },
115
+ );
116
+ };
117
+
118
+ export const useForgotPasswordMutation = () => {
119
+ {{#if framework == "nextjs"}}
120
+ const router = useRouter();
121
+ const navigate = (path: string) => router.push(path);
122
+ {{else}}
123
+ const navigate = useNavigate();
124
+ {{/if}}
125
+ const queryClient = useQueryClient();
126
+
127
+ return useMutation({
128
+ mutationKey: AUTH_MUTATION_KEYS.forgotPassword,
129
+ mutationFn: forgetPasswordRequest,
130
+ onSuccess: async () => {
131
+ toast.success("Password reset OTP sent to your email!");
132
+ await queryClient.invalidateQueries({ queryKey: AUTH_QUERY_KEYS.me });
133
+ navigate("/reset-password");
134
+ },
135
+ onError: (error: Error) => {
136
+ toast.error(
137
+ error.message ||
138
+ "Failed to send password reset email. Please check the email and try again.",
139
+ );
140
+ },
141
+ });
142
+ };
143
+
144
+ export const useResetPasswordMutation = () => {
145
+ {{#if framework == "nextjs"}}
146
+ const router = useRouter();
147
+ const navigate = (path: string) => router.push(path);
148
+ {{else}}
149
+ const navigate = useNavigate();
150
+ {{/if}}
151
+
152
+ return useMutation({
153
+ mutationKey: AUTH_MUTATION_KEYS.resetPassword,
154
+ mutationFn: resetPasswordRequest,
155
+ onSuccess: () => {
156
+ toast.success("Password reset successful! Please log in with your new password.");
157
+ navigate("/login");
158
+ },
159
+ onError: (error: Error) => {
160
+ toast.error(
161
+ error.message || "Failed to reset password. Please check your details and try again.",
162
+ );
163
+ },
164
+ });
165
+ };
166
+
167
+ export const useVerifyEmailMutation = () => {
168
+ {{#if framework == "nextjs"}}
169
+ const router = useRouter();
170
+ const navigate = (path: string) => router.push(path);
171
+ {{else}}
172
+ const navigate = useNavigate();
173
+ {{/if}}
174
+
175
+ return useMutation({
176
+ mutationKey: AUTH_MUTATION_KEYS.verifyEmail,
177
+ mutationFn: verifyEmailRequest,
178
+ onSuccess: () => {
179
+ toast.success("Email verified successfully! Please log in.");
180
+ navigate("/login");
181
+ },
182
+ onError: (error: Error) => {
183
+ toast.error(
184
+ error.message || "Email verification failed. Please check your details and try again.",
185
+ );
186
+ },
187
+ });
188
+ };
189
+
190
+ export const useResendOTPMutation = () => {
191
+ return useMutation({
192
+ mutationKey: AUTH_MUTATION_KEYS.resendOTP,
193
+ mutationFn: resendOTPRequest,
194
+ onSuccess: () => {
195
+ toast.success("Verification OTP resent successfully!");
196
+ },
197
+ onError: (error) => {
198
+ toast.error(
199
+ error.message ||
200
+ "Failed to resend verification OTP. Please check the email and try again.",
201
+ );
202
+ },
203
+ });
204
+ };
205
+
206
+ export const useSocialLoginMutation = () => {
207
+ return useMutation<string, Error, SocialProvider>({
208
+ mutationKey: AUTH_MUTATION_KEYS.socialLogin,
209
+ mutationFn: async (provider) => {
210
+ const payloadRes = await fetch(
211
+ `${envVars.API_URL}/v1/auth/login/${provider}?redirect=/dashboard`,
212
+ );
213
+ if (!payloadRes.ok) throw new Error("Failed to initiate social login.");
214
+ const { data: payload } = await payloadRes.json();
215
+
216
+ const authRes = await fetch(
217
+ `${envVars.BETTER_AUTH_URL}${payload.signInEndpoint}`,
218
+ {
219
+ method: "POST",
220
+ headers: { "Content-Type": "application/json" },
221
+ credentials: "include",
222
+ body: JSON.stringify({
223
+ provider: payload.provider,
224
+ callbackURL: payload.callbackURL,
225
+ }),
226
+ },
227
+ );
228
+ if (!authRes.ok) throw new Error("Social login request failed.");
229
+
230
+ const json = await authRes.json();
231
+ const redirectUrl: string | undefined = json?.url || json?.redirectUrl;
232
+
233
+ if (!redirectUrl)
234
+ throw new Error("No redirect URL returned from social login.");
235
+
236
+ return redirectUrl;
237
+ },
238
+ onSuccess: (redirectUrl) => {
239
+ window.location.href = redirectUrl;
240
+ },
241
+ onError: (error) => {
242
+ toast.error(error.message || "Social login failed. Please try again.");
243
+ },
244
+ });
245
+ };
246
+
247
+ export const useChangePasswordMutation = () => {
248
+ {{#if framework == "nextjs"}}
249
+ const router = useRouter();
250
+ const navigate = (path: string) => router.push(path);
251
+ {{else}}
252
+ const navigate = useNavigate();
253
+ {{/if}}
254
+
255
+ return useMutation({
256
+ mutationKey: AUTH_MUTATION_KEYS.changePassword,
257
+ mutationFn: ChangePassword,
258
+ onSuccess: () => {
259
+ toast.success(
260
+ "Password changed successfully! Please log in with your new password.",
261
+ );
262
+ navigate("/login");
263
+ },
264
+ onError: (error) => {
265
+ toast.error(
266
+ error.message ||
267
+ "Failed to change password. Please check your details and try again.",
268
+ );
269
+ },
270
+ });
271
+ };
272
+
273
+ export const useUpdateProfileMutation = () => {
274
+ const queryClient = useQueryClient();
275
+
276
+ return useMutation({
277
+ mutationKey: AUTH_MUTATION_KEYS.updateProfile,
278
+ mutationFn: updateProfile,
279
+ onSuccess: async () => {
280
+ await queryClient.invalidateQueries({ queryKey: AUTH_QUERY_KEYS.me });
281
+ toast.success("Profile updated successfully");
282
+ },
283
+ onError: (error) => {
284
+ toast.error(
285
+ error.message || "Failed to update profile. Please check your details and try again.",
286
+ );
287
+ },
288
+ });
289
+ };
290
+
291
+ export const useLogoutMutation = () => {
292
+ {{#if framework == "nextjs"}}
293
+ const router = useRouter();
294
+ const navigate = (path: string) => router.push(path);
295
+ {{else}}
296
+ const navigate = useNavigate();
297
+ {{/if}}
298
+ const queryClient = useQueryClient();
299
+
300
+ return useMutation({
301
+ mutationKey: AUTH_MUTATION_KEYS.logout,
302
+ mutationFn: logoutRequest,
303
+ onSuccess: async () => {
304
+ queryClient.clear();
305
+ toast.success("Logged out successfully");
306
+ navigate("/login");
307
+ },
308
+ onError: (error: Error) => {
309
+ toast.error(error.message || "Failed to log out. Please try again.");
310
+ },
311
+ });
312
+ };
@@ -0,0 +1,19 @@
1
+ {{#if framework == "nextjs"}}
2
+ "use client";
3
+ {{/if}}
4
+
5
+ import type { IUserResponse } from "../types/auth.type";
6
+ import { useQuery } from "@tanstack/react-query";
7
+ import { getMeRequest } from "../services/auth.api";
8
+
9
+ export const AUTH_QUERY_KEYS = {
10
+ me: ["auth", "me"] as const,
11
+ };
12
+
13
+ export const useMeQuery = () => {
14
+ return useQuery<IUserResponse>({
15
+ queryKey: AUTH_QUERY_KEYS.me,
16
+ queryFn: getMeRequest,
17
+ retry: false,
18
+ });
19
+ };
@@ -0,0 +1,81 @@
1
+ {{#if framework == "nextjs"}}
2
+ "use server";
3
+
4
+ import { deleteCookie } from "@/lib/utils/cookie";
5
+ {{/if}}
6
+ import type { ILoginResponse, IUserResponse } from "../types/auth.type";
7
+ import { api } from "@/lib/axios/http";
8
+
9
+ export async function loginRequest(payload: {
10
+ email: string;
11
+ password: string;
12
+ }) {
13
+ const res = await api.post<ILoginResponse>("/v1/auth/login", payload);
14
+ return res.data;
15
+ }
16
+
17
+ export async function registerRequest(payload: {
18
+ name: string;
19
+ email: string;
20
+ password: string;
21
+ }) {
22
+ const res = await api.post("/v1/auth/register", payload);
23
+ return res.data;
24
+ }
25
+
26
+ export async function forgetPasswordRequest(payload: { email: string }) {
27
+ const res = await api.post("/v1/auth/forget-password", payload);
28
+ return res.data;
29
+ }
30
+
31
+ export async function resetPasswordRequest(payload: {
32
+ email: string;
33
+ otp: string;
34
+ newPassword: string;
35
+ }) {
36
+ const res = await api.post("/v1/auth/reset-password", payload);
37
+ return res.data;
38
+ }
39
+
40
+ export async function verifyEmailRequest(payload: {
41
+ email: string;
42
+ otp: string;
43
+ }) {
44
+ const res = await api.post("/v1/auth/verify-email", payload);
45
+ return res.data;
46
+ }
47
+
48
+ export async function resendOTPRequest(payload: { email: string }) {
49
+ const res = await api.post("/v1/auth/resend-otp", payload);
50
+ return res.data;
51
+ }
52
+
53
+ export async function ChangePassword(payload: {
54
+ currentPassword: string;
55
+ newPassword: string;
56
+ }) {
57
+ const res = await api.post("/v1/auth/change-password", payload);
58
+ return res.data;
59
+ }
60
+
61
+ export async function updateProfile(payload: { name: string; image?: string }) {
62
+ const res = await api.patch("/v1/auth/profile", payload);
63
+ return res.data;
64
+ }
65
+
66
+ export async function logoutRequest() {
67
+ const res = await api.post("/v1/auth/logout", {});
68
+ {{#if framework == "nextjs"}}
69
+ // remove local cookies used for auth
70
+ await deleteCookie("accessToken");
71
+ await deleteCookie("refreshToken");
72
+ await deleteCookie("better-auth.session_token");
73
+ await deleteCookie("better-auth.session_data");
74
+ {{/if}}
75
+ return res.data;
76
+ }
77
+
78
+ export async function getMeRequest(): Promise<IUserResponse> {
79
+ const res = await api.get<IUserResponse>("/v1/auth/me");
80
+ return res.data;
81
+ }
@@ -0,0 +1,47 @@
1
+ export type SocialProvider =
2
+ | "google"
3
+ | "github"
4
+ | "facebook"
5
+ | "twitter"
6
+ | "discord";
7
+
8
+ export interface ILoginResponse {
9
+ token: string;
10
+ accessToken: string;
11
+ refreshToken: string;
12
+ user: {
13
+ needPasswordChange: boolean;
14
+ email: string;
15
+ name: string;
16
+ role: string;
17
+ image: string;
18
+ status: string;
19
+ isDeleted: boolean;
20
+ emailVerified: boolean;
21
+ };
22
+ }
23
+
24
+ export interface IUserResponse {
25
+ id: string;
26
+ needPasswordChange: boolean;
27
+ email: string;
28
+ name: string;
29
+ role: string;
30
+ image: string;
31
+ status: string;
32
+ isDeleted: boolean;
33
+ emailVerified: boolean;
34
+ createdAt?: string;
35
+ }
36
+
37
+ export type OAuthLoginPayload = {
38
+ provider: SocialProvider;
39
+ callbackURL: string;
40
+ signInEndpoint: string;
41
+ };
42
+
43
+ export type OAuthPayloadResponse = {
44
+ success: boolean;
45
+ message: string;
46
+ data: OAuthLoginPayload;
47
+ };
@@ -0,0 +1,18 @@
1
+ import { z } from "zod";
2
+
3
+ export const changePasswordZodSchema = z
4
+ .object({
5
+ currentPassword: z.string().min(1, "Current password is required"),
6
+ newPassword: z.string().min(8, "Password must be at least 8 characters"),
7
+ confirmPassword: z.string().min(1, "Please confirm your password"),
8
+ })
9
+ .refine((data) => data.newPassword === data.confirmPassword, {
10
+ message: "Passwords do not match",
11
+ path: ["confirmPassword"],
12
+ })
13
+ .refine((data) => data.currentPassword !== data.newPassword, {
14
+ message: "New password must be different from current password",
15
+ path: ["newPassword"],
16
+ });
17
+
18
+ export type IChangePasswordPayload = z.infer<typeof changePasswordZodSchema>;
@@ -0,0 +1,7 @@
1
+ import { z } from "zod";
2
+
3
+ export const forgotZodSchema = z.object({
4
+ email: z.string().email("Invalid email address"),
5
+ });
6
+
7
+ export type IForgotPayload = z.infer<typeof forgotZodSchema>;
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+
3
+ export const loginZodSchema = z.object({
4
+ email: z.string().email("Invalid email address"),
5
+ password: z
6
+ .string()
7
+ .min(1, "Password is required")
8
+ .min(8, "Password must be at least 8 characters long")
9
+ .regex(/[A-Z]/, "Password must contain at least one uppercase letter")
10
+ .regex(/[a-z]/, "Password must contain at least one lowercase letter")
11
+ .regex(/[0-9]/, "Password must contain at least one number"),
12
+ });
13
+
14
+ export type ILoginPayload = z.infer<typeof loginZodSchema>;
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+
3
+ export const profileZodSchema = z.object({
4
+ name: z.string().min(1, "Name is required"),
5
+ image: z.string().optional(),
6
+ });
7
+
8
+ export type IProfilePayload = z.infer<typeof profileZodSchema>;
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+
3
+ export const registerZodSchema = z.object({
4
+ name: z.string().min(1, "Name is required"),
5
+ email: z.string().email("Invalid email address"),
6
+ password: z.string().min(8, "Password must be at least 8 characters"),
7
+ });
8
+
9
+ export type IRegisterPayload = z.infer<typeof registerZodSchema>;
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+
3
+ export const resetZodSchema = z.object({
4
+ email: z.string().email("Invalid email address"),
5
+ otp: z.string().min(1, "OTP is required"),
6
+ newPassword: z.string().min(8, "Password must be at least 8 characters"),
7
+ });
8
+
9
+ export type IResetPayload = z.infer<typeof resetZodSchema>;
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+
3
+ export const verifyZodSchema = z.object({
4
+ email: z.string().email("Invalid email address"),
5
+ otp: z.string().min(1, "OTP is required"),
6
+ });
7
+
8
+ export type IVerifyPayload = z.infer<typeof verifyZodSchema>;
@@ -1,7 +1,8 @@
1
1
  import { createAuthClient } from "better-auth/react";
2
+ import { envVars } from "../env";
2
3
 
3
4
  export const authClient = createAuthClient({
4
- baseURL: process.env.BETTER_AUTH_URL || "http://localhost:5000",
5
+ baseURL: envVars.BETTER_AUTH_URL || "http://localhost:5000",
5
6
  });
6
7
 
7
8
  export const { signIn, signUp, signOut, useSession } = authClient;
@@ -7,13 +7,6 @@ import { sendEmail } from "../shared/utils/email";
7
7
  import { prisma } from "../database/prisma";
8
8
  import { prismaAdapter } from "better-auth/adapters/prisma";
9
9
  {{/if}}
10
- {{#if combo == "prisma:nextjs"}}
11
- import { Role, UserStatus } from "@prisma/client";
12
- import { sendEmail } from "@/lib/utils/email";
13
- import { prisma } from "../database/prisma";
14
- import { envVars } from "@/lib/env";
15
- import { prismaAdapter } from "better-auth/adapters/prisma";
16
- {{/if}}
17
10
  {{#if combo == "mongoose:express"}}
18
11
  import { envVars } from "../config/env";
19
12
  import { Role, UserStatus } from "../modules/auth/auth.constants";
@@ -21,13 +14,6 @@ import { sendEmail } from "../shared/utils/email";
21
14
  import { getMongoClient, getMongoDb, mongoose } from "../database/mongoose";
22
15
  import { mongodbAdapter } from "better-auth/adapters/mongodb";
23
16
  {{/if}}
24
- {{#if combo == "mongoose:nextjs"}}
25
- import { sendEmail } from "../service/email/email-service";
26
- import { getMongoClient, getMongoDb, mongoose } from "../database/mongoose";
27
- import { Role, UserStatus } from "@/lib/auth/auth-constants";
28
- import { envVars } from "@/lib/env";
29
- import { mongodbAdapter } from "better-auth/adapters/mongodb";
30
- {{/if}}
31
17
 
32
18
  {{#if database == "mongoose"}}
33
19
  await mongoose();
@@ -133,20 +119,14 @@ export const auth = betterAuth({
133
119
  return;
134
120
  }
135
121
 
136
- if (user && user.role === Role.SUPER_ADMIN) {
137
- console.log(
138
- `User with email ${email} is a super admin. Skipping sending verification OTP.`,
139
- );
140
- return;
141
- }
142
-
143
122
  if (user && !user.emailVerified) {
144
123
  sendEmail({
145
124
  to: email,
146
125
  subject: "Verify your email",
147
126
  templateName: "otp",
148
127
  templateData: {
149
- name: user.name,
128
+ userName: user.name,
129
+ appName: envVars.APP_NAME as string,
150
130
  otp,
151
131
  },
152
132
  });
@@ -169,14 +149,15 @@ export const auth = betterAuth({
169
149
  subject: "Password Reset OTP",
170
150
  templateName: "otp",
171
151
  templateData: {
172
- name: user.name,
152
+ userName: user.name,
153
+ appName: envVars.APP_NAME as string,
173
154
  otp,
174
155
  },
175
156
  });
176
157
  }
177
158
  }
178
159
  },
179
- expiresIn: 2 * 60, // 2 minutes in seconds
160
+ expiresIn: 5 * 60, // 5 minutes in seconds
180
161
  otpLength: 6,
181
162
  }),
182
163
  ],
@@ -192,20 +173,20 @@ export const auth = betterAuth({
192
173
  signIn: `${envVars.BETTER_AUTH_URL}/api/v1/auth/google/success`,
193
174
  },
194
175
  advanced: {
195
- useSecureCookies: false,
176
+ useSecureCookies: envVars.NODE_ENV === "production",
196
177
  cookies: {
197
178
  state: {
198
179
  attributes: {
199
- sameSite: "none",
200
- secure: true,
180
+ sameSite: envVars.NODE_ENV === "production" ? "none" : "lax",
181
+ secure: envVars.NODE_ENV === "production",
201
182
  httpOnly: true,
202
183
  path: "/",
203
184
  },
204
185
  },
205
186
  sessionToken: {
206
187
  attributes: {
207
- sameSite: "none",
208
- secure: true,
188
+ sameSite: envVars.NODE_ENV === "production" ? "none" : "lax",
189
+ secure: envVars.NODE_ENV === "production",
209
190
  httpOnly: true,
210
191
  path: "/",
211
192
  },