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.
- package/README.md +50 -42
- package/dist/cli/add.js +122 -56
- package/dist/cli/create.d.ts +2 -0
- package/dist/cli/create.js +271 -95
- package/dist/cli/doctor.js +1 -0
- package/dist/cli/list.d.ts +1 -1
- package/dist/cli/list.js +6 -4
- package/dist/index.js +234 -191
- package/dist/lib/constants.d.ts +4 -0
- package/dist/lib/constants.js +4 -0
- package/dist/lib/discovery/module-discovery.d.ts +4 -0
- package/dist/lib/discovery/module-discovery.js +56 -0
- package/dist/lib/generation/code-generator.d.ts +11 -2
- package/dist/lib/generation/code-generator.js +42 -3
- package/dist/lib/generation/generator-utils.js +3 -1
- package/dist/lib/pm/package-manager.js +16 -13
- package/dist/lib/ui/logger.js +3 -2
- package/dist/lib/utils/path-resolver.d.ts +2 -0
- package/dist/lib/utils/path-resolver.js +8 -0
- package/dist/meta.json +8312 -0
- package/modules/auth/better-auth/files/{shared → express}/config/env.ts +48 -50
- package/modules/auth/better-auth/files/express/middlewares/authorize.ts +20 -1
- package/modules/auth/better-auth/files/express/modules/auth.controller.ts +349 -0
- package/modules/auth/better-auth/files/express/modules/{auth/auth.route.ts → auth.route.ts} +9 -4
- package/modules/auth/better-auth/files/express/modules/auth.service.ts +664 -0
- package/modules/auth/better-auth/files/express/modules/{auth/auth.type.ts → auth.type.ts} +22 -9
- package/modules/auth/better-auth/files/{shared/mongoose/auth/helper.ts → express/mongo-modules/auth.helper.ts} +11 -1
- package/modules/auth/better-auth/files/express/types/express.d.ts +11 -0
- package/modules/auth/better-auth/files/nextjs/api-route.ts +74 -0
- package/modules/auth/better-auth/files/nextjs/dashboard/pages/(user)/page.tsx +6 -0
- package/modules/auth/better-auth/files/nextjs/dashboard/pages/admin/page.tsx +6 -0
- package/modules/auth/better-auth/files/nextjs/dashboard/pages/layout.tsx +48 -0
- package/modules/auth/better-auth/files/nextjs/dashboard/pages/my-profile/page.tsx +5 -0
- package/modules/auth/better-auth/files/nextjs/features/services/auth.service.ts +102 -0
- package/modules/auth/better-auth/files/nextjs/layout/layout.tsx +13 -0
- package/modules/auth/better-auth/files/nextjs/lib/axios/http.ts +158 -0
- package/modules/auth/better-auth/files/nextjs/lib/env.ts +35 -0
- package/modules/auth/better-auth/files/nextjs/lib/utils/auth.ts +75 -0
- package/modules/auth/better-auth/files/nextjs/lib/utils/cookie.ts +29 -0
- package/modules/auth/better-auth/files/nextjs/lib/utils/jwt.ts +28 -0
- package/modules/auth/better-auth/files/nextjs/lib/utils/token.ts +49 -0
- package/modules/auth/better-auth/files/nextjs/pages/forgot-password/page.tsx +5 -0
- package/modules/auth/better-auth/files/nextjs/pages/layout.tsx +11 -0
- package/modules/auth/better-auth/files/nextjs/pages/login/page.tsx +9 -0
- package/modules/auth/better-auth/files/nextjs/pages/register/page.tsx +5 -0
- package/modules/auth/better-auth/files/nextjs/pages/reset-password/page.tsx +10 -0
- package/modules/auth/better-auth/files/nextjs/pages/verify-email/page.tsx +10 -0
- package/modules/auth/better-auth/files/nextjs/proxy.ts +154 -42
- package/modules/auth/better-auth/files/nextjs/theme/providers/theme-provider.tsx +11 -0
- package/modules/auth/better-auth/files/nextjs/types/api.types.ts +18 -0
- package/modules/auth/better-auth/files/react/components/protected-route.tsx +39 -0
- package/modules/auth/better-auth/files/react/components/route-guards.tsx +13 -0
- package/modules/auth/better-auth/files/react/dashboard/admin/pages/overview.tsx +3 -0
- package/modules/auth/better-auth/files/react/dashboard/pages/overview.tsx +3 -0
- package/modules/auth/better-auth/files/react/features/pages/forgot-password.tsx +5 -0
- package/modules/auth/better-auth/files/react/features/pages/login.tsx +5 -0
- package/modules/auth/better-auth/files/react/features/pages/my-profile.tsx +5 -0
- package/modules/auth/better-auth/files/react/features/pages/oauth-callback.tsx +59 -0
- package/modules/auth/better-auth/files/react/features/pages/register.tsx +5 -0
- package/modules/auth/better-auth/files/react/features/pages/reset-password.tsx +10 -0
- package/modules/auth/better-auth/files/react/features/pages/verify-email.tsx +10 -0
- package/modules/auth/better-auth/files/react/layout/dashboard-layout.tsx +54 -0
- package/modules/auth/better-auth/files/react/lib/axios/http.ts +68 -0
- package/modules/auth/better-auth/files/react/lib/env.ts +25 -0
- package/modules/auth/better-auth/files/react/router.tsx +73 -0
- package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider-context.ts +13 -0
- package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider.tsx +51 -0
- package/modules/auth/better-auth/files/react/theme/hooks/use-theme.ts +8 -0
- package/modules/auth/better-auth/files/shared/features/components/change-password-dialog.tsx +113 -0
- package/modules/auth/better-auth/files/shared/features/components/forgot-password-form.tsx +84 -0
- package/modules/auth/better-auth/files/shared/features/components/login-form.tsx +134 -0
- package/modules/auth/better-auth/files/shared/features/components/my-profile.tsx +147 -0
- package/modules/auth/better-auth/files/shared/features/components/profile-form.tsx +205 -0
- package/modules/auth/better-auth/files/shared/features/components/register-form.tsx +100 -0
- package/modules/auth/better-auth/files/shared/features/components/reset-password-form.tsx +111 -0
- package/modules/auth/better-auth/files/shared/features/components/social-login-buttons.tsx +47 -0
- package/modules/auth/better-auth/files/shared/features/components/user-profile-menu.tsx +106 -0
- package/modules/auth/better-auth/files/shared/features/components/verify-email-form.tsx +110 -0
- package/modules/auth/better-auth/files/shared/features/queries/auth.mutations.tsx +312 -0
- package/modules/auth/better-auth/files/shared/features/queries/auth.querie.ts +19 -0
- package/modules/auth/better-auth/files/shared/features/services/auth.api.ts +81 -0
- package/modules/auth/better-auth/files/shared/features/types/auth.type.ts +47 -0
- package/modules/auth/better-auth/files/shared/features/validators/change-password.validator.ts +18 -0
- package/modules/auth/better-auth/files/shared/features/validators/forgot.validator.ts +7 -0
- package/modules/auth/better-auth/files/shared/features/validators/login.validator.ts +14 -0
- package/modules/auth/better-auth/files/shared/features/validators/profile.validator.ts +8 -0
- package/modules/auth/better-auth/files/shared/features/validators/register.validator.ts +9 -0
- package/modules/auth/better-auth/files/shared/features/validators/reset.validator.ts +9 -0
- package/modules/auth/better-auth/files/shared/features/validators/verify.validator.ts +8 -0
- package/modules/auth/better-auth/files/shared/lib/auth-client.ts +2 -1
- package/modules/auth/better-auth/files/shared/lib/auth.ts +5 -19
- package/modules/auth/better-auth/files/shared/lib/constant/dashboard.ts +90 -0
- package/modules/auth/better-auth/files/shared/theme/mode-toggle.tsx +30 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-header.tsx +94 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-sidebar.tsx +255 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/components/footer.tsx +35 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/components/navbar.tsx +145 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/form-field/input-field.tsx +440 -0
- package/modules/auth/better-auth/files/shared/utils/email.ts +2 -17
- package/modules/auth/better-auth/generator.json +172 -51
- package/modules/auth/better-auth/module.json +2 -2
- package/modules/components/files/shared/hooks/use-file-upload.ts +412 -0
- package/modules/components/files/shared/lib/utils/url-helpers.ts +110 -0
- package/modules/components/files/shared/shadcn/dashboard/data-table-column-selector.tsx +52 -0
- package/modules/components/files/shared/shadcn/dashboard/data-table-footer.tsx +156 -0
- package/modules/components/files/shared/shadcn/dashboard/data-table.tsx +405 -0
- package/modules/components/files/shared/shadcn/global/form-field/input-field.tsx +440 -0
- package/modules/components/files/shared/shadcn/global/form-field/media-uploader-field.tsx +745 -0
- package/modules/components/files/shared/shadcn/global/form-field/multi-select-field.tsx +207 -0
- package/modules/components/files/shared/shadcn/global/form-field/select-field.tsx +247 -0
- package/modules/components/files/shared/shadcn/global/form-field/textarea-field.tsx +277 -0
- package/modules/components/files/shared/shadcn/global/form-field/tiptap-editor-field.tsx +35 -0
- package/modules/components/files/shared/shadcn/global/no-results.tsx +41 -0
- package/modules/components/files/shared/shadcn/tiptap-editor/editor-menu-bar.tsx +217 -0
- package/modules/components/files/shared/shadcn/tiptap-editor/tiptap-editor.tsx +104 -0
- package/modules/components/files/shared/url/load-more.tsx +93 -0
- package/modules/components/files/shared/url/search-bar.tsx +131 -0
- package/modules/components/files/shared/url/sort-select.tsx +118 -0
- package/modules/components/files/shared/url/url-tabs.tsx +77 -0
- package/modules/components/generator.json +109 -0
- package/modules/components/module.json +11 -0
- package/modules/database/mongoose/generator.json +3 -14
- package/modules/database/mongoose/module.json +2 -2
- package/modules/database/prisma/generator.json +6 -12
- package/modules/database/prisma/module.json +2 -2
- package/modules/storage/cloudinary/files/express/config/env.ts +65 -0
- package/modules/storage/cloudinary/files/express/config/media.ts +103 -0
- package/modules/storage/cloudinary/files/express/modules/media/media.controller.ts +59 -0
- package/modules/storage/cloudinary/files/express/modules/media/media.route.ts +29 -0
- package/modules/storage/cloudinary/files/express/modules/media/media.service.ts +113 -0
- package/modules/storage/cloudinary/files/express/modules/media/media.type.ts +32 -0
- package/modules/storage/cloudinary/generator.json +34 -0
- package/modules/storage/cloudinary/module.json +11 -0
- package/modules/ui/shadcn/generator.json +21 -0
- package/modules/ui/shadcn/module.json +11 -0
- package/package.json +24 -26
- package/templates/express/README.md +11 -16
- package/templates/express/src/config/env.ts +7 -5
- package/templates/nextjs/README.md +13 -18
- package/templates/nextjs/app/favicon.ico +0 -0
- package/templates/nextjs/app/layout.tsx +6 -4
- package/templates/nextjs/components/providers/query-provider.tsx +3 -0
- package/templates/nextjs/env.example +3 -1
- package/templates/nextjs/lib/axios/http.ts +23 -0
- package/templates/nextjs/lib/env.ts +7 -5
- package/templates/nextjs/package.json +2 -1
- package/templates/nextjs/template.json +1 -2
- package/templates/react/README.md +9 -14
- package/templates/react/index.html +1 -1
- package/templates/react/package.json +1 -1
- package/templates/react/src/assets/favicon.ico +0 -0
- package/templates/react/src/components/providers/query-provider.tsx +38 -0
- package/templates/react/src/{shared/components → components}/seo.tsx +4 -8
- package/templates/react/src/lib/axios/http.ts +24 -0
- package/templates/react/src/main.tsx +8 -11
- package/templates/react/src/{features/about/pages → pages}/about.tsx +1 -1
- package/templates/react/src/{features/home/pages → pages}/home.tsx +1 -1
- package/templates/react/src/router.tsx +6 -6
- package/templates/react/src/vite-env.d.ts +2 -1
- package/templates/react/template.json +0 -1
- package/templates/react/tsconfig.app.json +6 -0
- package/templates/react/tsconfig.json +7 -1
- package/templates/react/vite.config.ts +12 -0
- package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +0 -3
- package/modules/auth/authjs/files/nextjs/proxy.ts +0 -1
- package/modules/auth/authjs/files/shared/lib/auth.ts +0 -119
- package/modules/auth/authjs/files/shared/prisma/schema.prisma +0 -61
- package/modules/auth/authjs/generator.json +0 -64
- package/modules/auth/authjs/module.json +0 -13
- package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +0 -264
- package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +0 -549
- package/modules/auth/better-auth/files/express/templates/google-redirect.ejs +0 -24
- package/modules/auth/better-auth/files/nextjs/api/auth/[...all]/route.ts +0 -4
- package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +0 -31
- package/modules/auth/better-auth/files/nextjs/templates/email-otp.tsx +0 -74
- package/templates/nextjs/lib/api/http.ts +0 -40
- package/templates/react/public/vite.svg +0 -1
- package/templates/react/src/app/layouts/dashboard-layout.tsx +0 -8
- package/templates/react/src/app/layouts/public-layout.tsx +0 -5
- package/templates/react/src/app/providers.tsx +0 -20
- package/templates/react/src/app/router.tsx +0 -21
- package/templates/react/src/assets/react.svg +0 -1
- package/templates/react/src/shared/api/http.ts +0 -39
- package/templates/react/src/shared/components/loading.tsx +0 -8
- package/templates/react/src/shared/lib/query-client.ts +0 -12
- package/templates/react/src/utils/storage.ts +0 -35
- package/templates/react/src/utils/utils.ts +0 -3
- /package/modules/auth/better-auth/files/{shared/mongoose/auth/constants.ts → express/mongo-modules/auth.constants.ts} +0 -0
- /package/templates/nextjs/app/{page.tsx → (public)/(root)/page.tsx} +0 -0
- /package/templates/react/src/{shared/components → components}/error-boundary.tsx +0 -0
- /package/templates/react/src/{shared/components → components}/layout.tsx +0 -0
- /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
|
+
};
|
package/modules/auth/better-auth/files/shared/features/validators/change-password.validator.ts
ADDED
|
@@ -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,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,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>;
|
|
@@ -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:
|
|
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();
|
|
@@ -187,20 +173,20 @@ export const auth = betterAuth({
|
|
|
187
173
|
signIn: `${envVars.BETTER_AUTH_URL}/api/v1/auth/google/success`,
|
|
188
174
|
},
|
|
189
175
|
advanced: {
|
|
190
|
-
useSecureCookies:
|
|
176
|
+
useSecureCookies: envVars.NODE_ENV === "production",
|
|
191
177
|
cookies: {
|
|
192
178
|
state: {
|
|
193
179
|
attributes: {
|
|
194
|
-
sameSite: "none",
|
|
195
|
-
secure:
|
|
180
|
+
sameSite: envVars.NODE_ENV === "production" ? "none" : "lax",
|
|
181
|
+
secure: envVars.NODE_ENV === "production",
|
|
196
182
|
httpOnly: true,
|
|
197
183
|
path: "/",
|
|
198
184
|
},
|
|
199
185
|
},
|
|
200
186
|
sessionToken: {
|
|
201
187
|
attributes: {
|
|
202
|
-
sameSite: "none",
|
|
203
|
-
secure:
|
|
188
|
+
sameSite: envVars.NODE_ENV === "production" ? "none" : "lax",
|
|
189
|
+
secure: envVars.NODE_ENV === "production",
|
|
204
190
|
httpOnly: true,
|
|
205
191
|
path: "/",
|
|
206
192
|
},
|