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.
- 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 -52
- 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} +12 -7
- 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/constants.ts → express/mongo-modules/auth.constants.ts} +0 -1
- 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 +157 -22
- 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 +10 -29
- package/modules/auth/better-auth/files/shared/lib/constant/dashboard.ts +90 -0
- package/modules/auth/better-auth/files/shared/prisma/enums.prisma +0 -1
- 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 +20 -18
- package/modules/auth/better-auth/generator.json +174 -53
- 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 -537
- 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 -41
- package/modules/auth/better-auth/files/nextjs/templates/email-otp.tsx +0 -74
- package/templates/express/node_modules/.bin/acorn +0 -17
- package/templates/express/node_modules/.bin/eslint +0 -17
- package/templates/express/node_modules/.bin/tsc +0 -17
- package/templates/express/node_modules/.bin/tsserver +0 -17
- package/templates/express/node_modules/.bin/tsx +0 -17
- package/templates/nextjs/lib/api/http.ts +0 -40
- package/templates/nextjs/next-env.d.ts +0 -6
- package/templates/react/dist/assets/index-D4AHT4dU.js +0 -193
- package/templates/react/dist/assets/index-rpwj5ZOX.css +0 -1
- package/templates/react/dist/index.html +0 -14
- package/templates/react/dist/vite.svg +0 -1
- 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/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
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
{{#if framework == "express" }}
|
|
2
1
|
import dotenv from "dotenv";
|
|
3
2
|
import status from "http-status";
|
|
4
3
|
import path from "path";
|
|
5
4
|
import { AppError } from "../shared/errors/app-error";
|
|
6
5
|
|
|
7
6
|
dotenv.config({ path: path.join(process.cwd(), ".env") });
|
|
8
|
-
{{/if}}
|
|
9
7
|
|
|
10
8
|
interface EnvConfig {
|
|
9
|
+
NODE_ENV: string;
|
|
10
|
+
PORT: string;
|
|
11
|
+
APP_NAME?: string;
|
|
11
12
|
APP_URL: string;
|
|
12
|
-
{{#if framework == "nextjs"}}
|
|
13
|
-
API_URL: string;
|
|
14
|
-
{{/if}}
|
|
15
13
|
DATABASE_URL: string;
|
|
16
14
|
FRONTEND_URL: string;
|
|
17
15
|
BETTER_AUTH_URL: string;
|
|
18
16
|
BETTER_AUTH_SECRET: string;
|
|
17
|
+
BETTER_AUTH_SESSION_TOKEN_EXPIRES_IN: string;
|
|
18
|
+
BETTER_AUTH_SESSION_TOKEN_UPDATE_AGE: string;
|
|
19
|
+
ACCESS_TOKEN_SECRET: string;
|
|
20
|
+
ACCESS_TOKEN_EXPIRES_IN: string;
|
|
21
|
+
REFRESH_TOKEN_SECRET: string;
|
|
22
|
+
REFRESH_TOKEN_EXPIRES_IN: string;
|
|
19
23
|
GOOGLE_CLIENT_ID: string;
|
|
20
24
|
GOOGLE_CLIENT_SECRET: string;
|
|
21
25
|
GOOGLE_CALLBACK_URL: string;
|
|
@@ -26,30 +30,32 @@ interface EnvConfig {
|
|
|
26
30
|
SMTP_PORT: string;
|
|
27
31
|
SMTP_FROM: string;
|
|
28
32
|
};
|
|
29
|
-
{{#if
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
REFRESH_TOKEN_SECRET: string;
|
|
37
|
-
REFRESH_TOKEN_EXPIRES_IN: string;
|
|
38
|
-
SUPER_ADMIN_EMAIL: string;
|
|
39
|
-
SUPER_ADMIN_PASSWORD: string;
|
|
33
|
+
{{#if storageProvider == "cloudinary"}}
|
|
34
|
+
CLOUDINARY: {
|
|
35
|
+
CLOUDINARY_CLOUD_NAME: string;
|
|
36
|
+
CLOUDINARY_API_KEY: string;
|
|
37
|
+
CLOUDINARY_API_SECRET: string;
|
|
38
|
+
CLOUDINARY_UPLOAD_PRESET: string;
|
|
39
|
+
};
|
|
40
40
|
{{/if}}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const loadEnvVars = (): EnvConfig => {
|
|
44
44
|
const requiredEnvVars = [
|
|
45
|
+
"NODE_ENV",
|
|
46
|
+
"PORT",
|
|
47
|
+
"APP_NAME",
|
|
45
48
|
"APP_URL",
|
|
46
|
-
{{#if framework == "nextjs"}}
|
|
47
|
-
"API_URL",
|
|
48
|
-
{{/if}}
|
|
49
49
|
"DATABASE_URL",
|
|
50
50
|
"FRONTEND_URL",
|
|
51
51
|
"BETTER_AUTH_URL",
|
|
52
52
|
"BETTER_AUTH_SECRET",
|
|
53
|
+
"BETTER_AUTH_SESSION_TOKEN_EXPIRES_IN",
|
|
54
|
+
"BETTER_AUTH_SESSION_TOKEN_UPDATE_AGE",
|
|
55
|
+
"ACCESS_TOKEN_SECRET",
|
|
56
|
+
"ACCESS_TOKEN_EXPIRES_IN",
|
|
57
|
+
"REFRESH_TOKEN_SECRET",
|
|
58
|
+
"REFRESH_TOKEN_EXPIRES_IN",
|
|
53
59
|
"GOOGLE_CLIENT_ID",
|
|
54
60
|
"GOOGLE_CLIENT_SECRET",
|
|
55
61
|
"GOOGLE_CALLBACK_URL",
|
|
@@ -58,44 +64,40 @@ const loadEnvVars = (): EnvConfig => {
|
|
|
58
64
|
"EMAIL_SENDER_SMTP_HOST",
|
|
59
65
|
"EMAIL_SENDER_SMTP_PORT",
|
|
60
66
|
"EMAIL_SENDER_SMTP_FROM",
|
|
61
|
-
{{#if
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"ACCESS_TOKEN_SECRET",
|
|
67
|
-
"ACCESS_TOKEN_EXPIRES_IN",
|
|
68
|
-
"REFRESH_TOKEN_SECRET",
|
|
69
|
-
"REFRESH_TOKEN_EXPIRES_IN",
|
|
70
|
-
"SUPER_ADMIN_EMAIL",
|
|
71
|
-
"SUPER_ADMIN_PASSWORD",
|
|
67
|
+
{{#if storageProvider == "cloudinary"}}
|
|
68
|
+
"CLOUDINARY_CLOUD_NAME",
|
|
69
|
+
"CLOUDINARY_API_KEY",
|
|
70
|
+
"CLOUDINARY_API_SECRET",
|
|
71
|
+
"CLOUDINARY_UPLOAD_PRESET",
|
|
72
72
|
{{/if}}
|
|
73
73
|
];
|
|
74
74
|
|
|
75
75
|
requiredEnvVars.forEach((varName) => {
|
|
76
76
|
if (!process.env[varName]) {
|
|
77
|
-
{{#if framework == "express" }}
|
|
78
77
|
throw new AppError(
|
|
79
78
|
status.INTERNAL_SERVER_ERROR,
|
|
80
79
|
`Environment variable ${varName} is required but not set in .env file.`,
|
|
81
80
|
);
|
|
82
|
-
{{else}}
|
|
83
|
-
throw new Error(
|
|
84
|
-
`Environment variable ${varName} is required but not defined.`,
|
|
85
|
-
);
|
|
86
|
-
{{/if}}
|
|
87
81
|
}
|
|
88
82
|
});
|
|
89
83
|
|
|
90
84
|
return {
|
|
85
|
+
NODE_ENV: process.env.NODE_ENV as string,
|
|
86
|
+
PORT: process.env.PORT as string,
|
|
87
|
+
APP_NAME: process.env.APP_NAME ?? "Your App",
|
|
91
88
|
APP_URL: process.env.APP_URL as string,
|
|
92
|
-
{{#if framework == "nextjs"}}
|
|
93
|
-
API_URL: process.env.API_URL as string,
|
|
94
|
-
{{/if}}
|
|
95
89
|
DATABASE_URL: process.env.DATABASE_URL as string,
|
|
96
90
|
FRONTEND_URL: process.env.FRONTEND_URL as string,
|
|
97
91
|
BETTER_AUTH_URL: process.env.BETTER_AUTH_URL as string,
|
|
98
92
|
BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET as string,
|
|
93
|
+
BETTER_AUTH_SESSION_TOKEN_EXPIRES_IN: process.env
|
|
94
|
+
.BETTER_AUTH_SESSION_TOKEN_EXPIRES_IN as string,
|
|
95
|
+
BETTER_AUTH_SESSION_TOKEN_UPDATE_AGE: process.env
|
|
96
|
+
.BETTER_AUTH_SESSION_TOKEN_UPDATE_AGE as string,
|
|
97
|
+
ACCESS_TOKEN_SECRET: process.env.ACCESS_TOKEN_SECRET as string,
|
|
98
|
+
ACCESS_TOKEN_EXPIRES_IN: process.env.ACCESS_TOKEN_EXPIRES_IN as string,
|
|
99
|
+
REFRESH_TOKEN_SECRET: process.env.REFRESH_TOKEN_SECRET as string,
|
|
100
|
+
REFRESH_TOKEN_EXPIRES_IN: process.env.REFRESH_TOKEN_EXPIRES_IN as string,
|
|
99
101
|
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID as string,
|
|
100
102
|
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET as string,
|
|
101
103
|
GOOGLE_CALLBACK_URL: process.env.GOOGLE_CALLBACK_URL as string,
|
|
@@ -106,19 +108,13 @@ const loadEnvVars = (): EnvConfig => {
|
|
|
106
108
|
SMTP_PORT: process.env.EMAIL_SENDER_SMTP_PORT as string,
|
|
107
109
|
SMTP_FROM: process.env.EMAIL_SENDER_SMTP_FROM as string,
|
|
108
110
|
},
|
|
109
|
-
{{#if
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
ACCESS_TOKEN_SECRET: process.env.ACCESS_TOKEN_SECRET as string,
|
|
117
|
-
ACCESS_TOKEN_EXPIRES_IN: process.env.ACCESS_TOKEN_EXPIRES_IN as string,
|
|
118
|
-
REFRESH_TOKEN_SECRET: process.env.REFRESH_TOKEN_SECRET as string,
|
|
119
|
-
REFRESH_TOKEN_EXPIRES_IN: process.env.REFRESH_TOKEN_EXPIRES_IN as string,
|
|
120
|
-
SUPER_ADMIN_EMAIL: process.env.SUPER_ADMIN_EMAIL as string,
|
|
121
|
-
SUPER_ADMIN_PASSWORD: process.env.SUPER_ADMIN_PASSWORD as string,
|
|
111
|
+
{{#if storageProvider == "cloudinary"}}
|
|
112
|
+
CLOUDINARY: {
|
|
113
|
+
CLOUDINARY_CLOUD_NAME: process.env.CLOUDINARY_CLOUD_NAME as string,
|
|
114
|
+
CLOUDINARY_API_KEY: process.env.CLOUDINARY_API_KEY as string,
|
|
115
|
+
CLOUDINARY_API_SECRET: process.env.CLOUDINARY_API_SECRET as string,
|
|
116
|
+
CLOUDINARY_UPLOAD_PRESET: process.env.CLOUDINARY_UPLOAD_PRESET as string,
|
|
117
|
+
},
|
|
122
118
|
{{/if}}
|
|
123
119
|
};
|
|
124
120
|
};
|
|
@@ -9,6 +9,7 @@ import { Role, UserStatus } from "@prisma/client";
|
|
|
9
9
|
import { prisma } from "../../database/prisma";
|
|
10
10
|
{{/if}}
|
|
11
11
|
{{#if database == "mongoose"}}
|
|
12
|
+
import { Types } from "mongoose";
|
|
12
13
|
import { Role, UserStatus } from "../../modules/auth/auth.constants";
|
|
13
14
|
import { getAuthCollections } from "../../modules/auth/auth.helper";
|
|
14
15
|
{{/if}}
|
|
@@ -56,7 +57,7 @@ export const authorize = (...authRoles: AuthRole[]) =>
|
|
|
56
57
|
|
|
57
58
|
const user = sessionExists
|
|
58
59
|
? await users.findOne({
|
|
59
|
-
|
|
60
|
+
_id: new Types.ObjectId(sessionExists.userId),
|
|
60
61
|
})
|
|
61
62
|
: null;
|
|
62
63
|
{{/if}}
|
|
@@ -185,6 +186,24 @@ export const authorize = (...authRoles: AuthRole[]) =>
|
|
|
185
186
|
);
|
|
186
187
|
}
|
|
187
188
|
|
|
189
|
+
if (!req.user) {
|
|
190
|
+
const tokenData = verifiedToken.data!;
|
|
191
|
+
|
|
192
|
+
req.user = {
|
|
193
|
+
id: String(tokenData.userId || tokenData.id || ""),
|
|
194
|
+
name: String(tokenData.name || ""),
|
|
195
|
+
email: String(tokenData.email || ""),
|
|
196
|
+
role: (tokenData.role as Role) || Role.USER,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!req.user?.id) {
|
|
201
|
+
throw new AppError(
|
|
202
|
+
status.UNAUTHORIZED,
|
|
203
|
+
"Unauthorized access! User information is missing in the token.",
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
188
207
|
next();
|
|
189
208
|
} catch (error: unknown) {
|
|
190
209
|
next(error);
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
|
+
import status from "http-status";
|
|
3
|
+
import { envVars } from "../../config/env";
|
|
4
|
+
import { auth } from "../../lib/auth";
|
|
5
|
+
import { AppError } from "../../shared/errors/app-error";
|
|
6
|
+
import { catchAsync } from "../../shared/utils/catch-async";
|
|
7
|
+
import { cookieUtils } from "../../shared/utils/cookie";
|
|
8
|
+
import { sendResponse } from "../../shared/utils/send-response";
|
|
9
|
+
import { tokenUtils } from "../../shared/utils/token";
|
|
10
|
+
import { authService } from "./auth.service";
|
|
11
|
+
import type { NeedsVerification, SocialProvider } from "./auth.type";
|
|
12
|
+
|
|
13
|
+
const getSocialAuthPayload = (
|
|
14
|
+
provider: SocialProvider,
|
|
15
|
+
redirectPath: string,
|
|
16
|
+
) => {
|
|
17
|
+
const authBaseUrl = envVars.APP_URL || envVars.BETTER_AUTH_URL;
|
|
18
|
+
const encodedRedirectPath = encodeURIComponent(redirectPath);
|
|
19
|
+
const callbackURL = `${authBaseUrl}/api/v1/auth/${provider}/success?redirect=${encodedRedirectPath}`;
|
|
20
|
+
const signInEndpoint = `/api/auth/sign-in/social`;
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
provider,
|
|
24
|
+
callbackURL,
|
|
25
|
+
signInEndpoint,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const registerUser = catchAsync(async (req: Request, res: Response) => {
|
|
30
|
+
const payload = req.body;
|
|
31
|
+
|
|
32
|
+
const result = await authService.registerUser(payload);
|
|
33
|
+
|
|
34
|
+
const { accessToken, refreshToken, token, ...rest } = result;
|
|
35
|
+
|
|
36
|
+
tokenUtils.setAccessTokenCookie(res, accessToken);
|
|
37
|
+
tokenUtils.setRefreshTokenCookie(res, refreshToken);
|
|
38
|
+
tokenUtils.setBetterAuthSessionCookie(res, token as string);
|
|
39
|
+
|
|
40
|
+
sendResponse(res, {
|
|
41
|
+
status: status.CREATED,
|
|
42
|
+
success: true,
|
|
43
|
+
message: "User registered successfully",
|
|
44
|
+
data: {
|
|
45
|
+
token,
|
|
46
|
+
accessToken,
|
|
47
|
+
refreshToken,
|
|
48
|
+
...rest,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const loginUser = catchAsync(async (req: Request, res: Response) => {
|
|
54
|
+
const payload = req.body;
|
|
55
|
+
const result = await authService.loginUser(payload);
|
|
56
|
+
if ((result as NeedsVerification)?.needsVerification) {
|
|
57
|
+
const n = result as NeedsVerification;
|
|
58
|
+
return sendResponse(res, {
|
|
59
|
+
status: status.FORBIDDEN,
|
|
60
|
+
success: false,
|
|
61
|
+
message: "Email not verified",
|
|
62
|
+
data: { email: n.email },
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { accessToken, refreshToken, token, ...rest } =
|
|
67
|
+
result as unknown as Record<string, unknown>;
|
|
68
|
+
|
|
69
|
+
if (typeof token !== "string") {
|
|
70
|
+
throw new AppError(
|
|
71
|
+
status.INTERNAL_SERVER_ERROR,
|
|
72
|
+
"Session token is missing",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (typeof accessToken !== "string" || typeof refreshToken !== "string") {
|
|
77
|
+
throw new AppError(status.INTERNAL_SERVER_ERROR, "Auth tokens are missing");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
tokenUtils.setAccessTokenCookie(res, accessToken);
|
|
81
|
+
tokenUtils.setRefreshTokenCookie(res, refreshToken);
|
|
82
|
+
tokenUtils.setBetterAuthSessionCookie(res, token);
|
|
83
|
+
|
|
84
|
+
sendResponse(res, {
|
|
85
|
+
status: status.OK,
|
|
86
|
+
success: true,
|
|
87
|
+
message: "User logged in successfully",
|
|
88
|
+
data: {
|
|
89
|
+
token,
|
|
90
|
+
accessToken,
|
|
91
|
+
refreshToken,
|
|
92
|
+
...rest,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const getMe = catchAsync(
|
|
98
|
+
async (req: Request, res: Response) => {
|
|
99
|
+
const user = req.user;
|
|
100
|
+
const result = await authService.getMe(user);
|
|
101
|
+
sendResponse(res, {
|
|
102
|
+
status: status.OK,
|
|
103
|
+
success: true,
|
|
104
|
+
message: "User profile fetched successfully",
|
|
105
|
+
data: result,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
const updateProfile = catchAsync(async (req: Request, res: Response) => {
|
|
111
|
+
const { name, image } = req.body as { name?: string; image?: string };
|
|
112
|
+
const user = req.user;
|
|
113
|
+
|
|
114
|
+
const result = await authService.updateProfile(user, { name, image });
|
|
115
|
+
|
|
116
|
+
sendResponse(res, {
|
|
117
|
+
status: status.OK,
|
|
118
|
+
success: true,
|
|
119
|
+
message: "Profile updated successfully",
|
|
120
|
+
data: result,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const getNewToken = catchAsync(
|
|
125
|
+
async (req: Request, res: Response) => {
|
|
126
|
+
const refreshToken = req.cookies.refreshToken;
|
|
127
|
+
const betterAuthSessionToken = req.cookies["better-auth.session_token"];
|
|
128
|
+
if (!refreshToken) {
|
|
129
|
+
throw new AppError(status.UNAUTHORIZED, "Refresh token is missing");
|
|
130
|
+
}
|
|
131
|
+
const result = await authService.getNewToken(refreshToken, betterAuthSessionToken);
|
|
132
|
+
|
|
133
|
+
const { accessToken, refreshToken: newRefreshToken, sessionToken } = result;
|
|
134
|
+
|
|
135
|
+
tokenUtils.setAccessTokenCookie(res, accessToken);
|
|
136
|
+
tokenUtils.setRefreshTokenCookie(res, newRefreshToken);
|
|
137
|
+
tokenUtils.setBetterAuthSessionCookie(res, sessionToken);
|
|
138
|
+
|
|
139
|
+
sendResponse(res, {
|
|
140
|
+
status: status.OK,
|
|
141
|
+
success: true,
|
|
142
|
+
message: "New tokens generated successfully",
|
|
143
|
+
data: {
|
|
144
|
+
accessToken,
|
|
145
|
+
refreshToken: newRefreshToken,
|
|
146
|
+
sessionToken,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
const changePassword = catchAsync(
|
|
153
|
+
async (req: Request, res: Response) => {
|
|
154
|
+
const payload = req.body;
|
|
155
|
+
const betterAuthSessionToken = req.cookies["better-auth.session_token"];
|
|
156
|
+
|
|
157
|
+
const result = await authService.changePassword(payload, betterAuthSessionToken);
|
|
158
|
+
|
|
159
|
+
const { accessToken, refreshToken, token } = result;
|
|
160
|
+
|
|
161
|
+
tokenUtils.setAccessTokenCookie(res, accessToken);
|
|
162
|
+
tokenUtils.setRefreshTokenCookie(res, refreshToken);
|
|
163
|
+
tokenUtils.setBetterAuthSessionCookie(res, token as string);
|
|
164
|
+
|
|
165
|
+
sendResponse(res, {
|
|
166
|
+
status: status.OK,
|
|
167
|
+
success: true,
|
|
168
|
+
message: "Password changed successfully",
|
|
169
|
+
data: result,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
const logoutUser = catchAsync(
|
|
175
|
+
async (req: Request, res: Response) => {
|
|
176
|
+
const betterAuthSessionToken = req.cookies["better-auth.session_token"];
|
|
177
|
+
const result = await authService.logoutUser(betterAuthSessionToken);
|
|
178
|
+
cookieUtils.clearCookie(res, 'accessToken', {
|
|
179
|
+
httpOnly: true,
|
|
180
|
+
secure: true,
|
|
181
|
+
sameSite: "none",
|
|
182
|
+
});
|
|
183
|
+
cookieUtils.clearCookie(res, 'refreshToken', {
|
|
184
|
+
httpOnly: true,
|
|
185
|
+
secure: true,
|
|
186
|
+
sameSite: "none",
|
|
187
|
+
});
|
|
188
|
+
cookieUtils.clearCookie(res, 'better-auth.session_token', {
|
|
189
|
+
httpOnly: true,
|
|
190
|
+
secure: true,
|
|
191
|
+
sameSite: "none",
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
sendResponse(res, {
|
|
195
|
+
status: status.OK,
|
|
196
|
+
success: true,
|
|
197
|
+
message: "User logged out successfully",
|
|
198
|
+
data: result,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
const verifyEmail = catchAsync(
|
|
204
|
+
async (req: Request, res: Response) => {
|
|
205
|
+
const { email, otp } = req.body;
|
|
206
|
+
await authService.verifyEmail(email, otp);
|
|
207
|
+
|
|
208
|
+
sendResponse(res, {
|
|
209
|
+
status: status.OK,
|
|
210
|
+
success: true,
|
|
211
|
+
message: "Email verified successfully",
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
const resendOTP = catchAsync(async (req: Request, res: Response) => {
|
|
217
|
+
const { email } = req.body;
|
|
218
|
+
await authService.resendOTP(email);
|
|
219
|
+
|
|
220
|
+
sendResponse(res, {
|
|
221
|
+
status: status.OK,
|
|
222
|
+
success: true,
|
|
223
|
+
message: "Verification OTP resent successfully",
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const forgetPassword = catchAsync(
|
|
228
|
+
async (req: Request, res: Response) => {
|
|
229
|
+
const { email } = req.body;
|
|
230
|
+
await authService.forgetPassword(email);
|
|
231
|
+
|
|
232
|
+
sendResponse(res, {
|
|
233
|
+
status: status.OK,
|
|
234
|
+
success: true,
|
|
235
|
+
message: "Password reset OTP sent to email successfully",
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
const resetPassword = catchAsync(async (req: Request, res: Response) => {
|
|
241
|
+
const { email, otp, newPassword } = req.body;
|
|
242
|
+
await authService.resetPassword(email, otp, newPassword);
|
|
243
|
+
|
|
244
|
+
sendResponse(res, {
|
|
245
|
+
status: status.OK,
|
|
246
|
+
success: true,
|
|
247
|
+
message: "Password reset successfully",
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const SUPPORTED_PROVIDERS: SocialProvider[] = [
|
|
252
|
+
"google",
|
|
253
|
+
"github",
|
|
254
|
+
"facebook",
|
|
255
|
+
"twitter",
|
|
256
|
+
"discord",
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
const socialLogin = catchAsync((req: Request, res: Response) => {
|
|
260
|
+
const provider = req.params.provider as SocialProvider;
|
|
261
|
+
const redirectPath = (req.query.redirect as string) || "/dashboard";
|
|
262
|
+
|
|
263
|
+
if (!SUPPORTED_PROVIDERS.includes(provider)) {
|
|
264
|
+
throw new AppError(
|
|
265
|
+
status.BAD_REQUEST,
|
|
266
|
+
`Unsupported social provider: ${provider}`,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const payload = getSocialAuthPayload(provider, redirectPath);
|
|
271
|
+
|
|
272
|
+
return sendResponse(res, {
|
|
273
|
+
status: status.OK,
|
|
274
|
+
success: true,
|
|
275
|
+
message: `${provider} login payload generated successfully`,
|
|
276
|
+
data: payload,
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const socialLoginSuccess = catchAsync(async (req: Request, res: Response) => {
|
|
281
|
+
const provider = req.params.provider || "google";
|
|
282
|
+
const redirectPath = (req.query.redirect as string) || "/dashboard";
|
|
283
|
+
const isValidRedirectPath =
|
|
284
|
+
redirectPath.startsWith("/") && !redirectPath.startsWith("//");
|
|
285
|
+
const finalRedirectPath = isValidRedirectPath ? redirectPath : "/dashboard";
|
|
286
|
+
|
|
287
|
+
const sessionToken = req.cookies["better-auth.session_token"];
|
|
288
|
+
|
|
289
|
+
if (!sessionToken) {
|
|
290
|
+
return res.redirect(`${envVars.FRONTEND_URL}/login?error=oauth_failed`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const session = await auth.api.getSession({
|
|
294
|
+
headers: {
|
|
295
|
+
Cookie: `better-auth.session_token=${sessionToken}`,
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
if (!session?.user) {
|
|
300
|
+
return res.redirect(`${envVars.FRONTEND_URL}/login?error=oauth_failed`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let accessToken: string;
|
|
304
|
+
let refreshToken: string;
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
const result = await authService.socialLoginSuccess(session);
|
|
308
|
+
accessToken = result.accessToken;
|
|
309
|
+
refreshToken = result.refreshToken;
|
|
310
|
+
} catch (error) {
|
|
311
|
+
const message =
|
|
312
|
+
error instanceof AppError
|
|
313
|
+
? encodeURIComponent(error?.message)
|
|
314
|
+
: "oauth_failed";
|
|
315
|
+
return res.redirect(`${envVars.FRONTEND_URL}/login?error=${message}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const callbackUrl = new URL(
|
|
319
|
+
`${envVars.FRONTEND_URL}/api/auth/callback/${provider}`,
|
|
320
|
+
);
|
|
321
|
+
callbackUrl.searchParams.set("accessToken", accessToken);
|
|
322
|
+
callbackUrl.searchParams.set("refreshToken", refreshToken);
|
|
323
|
+
callbackUrl.searchParams.set("token", sessionToken);
|
|
324
|
+
callbackUrl.searchParams.set("redirect", finalRedirectPath);
|
|
325
|
+
|
|
326
|
+
res.redirect(callbackUrl.toString());
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const handleOAuthError = catchAsync((req: Request, res: Response) => {
|
|
330
|
+
const error = req.query.error as string || "oauth_failed";
|
|
331
|
+
res.redirect(`${envVars.FRONTEND_URL}/login?error=${error}`);
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
export const authController = {
|
|
335
|
+
registerUser,
|
|
336
|
+
loginUser,
|
|
337
|
+
getMe,
|
|
338
|
+
updateProfile,
|
|
339
|
+
getNewToken,
|
|
340
|
+
changePassword,
|
|
341
|
+
logoutUser,
|
|
342
|
+
verifyEmail,
|
|
343
|
+
resendOTP,
|
|
344
|
+
forgetPassword,
|
|
345
|
+
resetPassword,
|
|
346
|
+
socialLogin,
|
|
347
|
+
socialLoginSuccess,
|
|
348
|
+
handleOAuthError,
|
|
349
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Role } from "@prisma/client";
|
|
3
3
|
{{/if}}
|
|
4
4
|
{{#if database == "mongoose"}}
|
|
5
|
-
import { Role } from
|
|
5
|
+
import { Role } from "./auth.constants";
|
|
6
6
|
{{/if}}
|
|
7
7
|
import { Router } from "express";
|
|
8
8
|
import { authorize } from "../../shared/middlewares/authorize.middleware";
|
|
@@ -12,16 +12,21 @@ const router = Router()
|
|
|
12
12
|
|
|
13
13
|
router.post("/register", authController.registerUser)
|
|
14
14
|
router.post("/login", authController.loginUser)
|
|
15
|
-
router.get("/me", authorize(Role.ADMIN, Role.USER
|
|
15
|
+
router.get("/me", authorize(Role.ADMIN, Role.USER), authController.getMe)
|
|
16
16
|
router.post("/refresh-token", authController.getNewToken)
|
|
17
|
-
router.post("/change-password", authorize(Role.ADMIN, Role.USER
|
|
18
|
-
router.post("/logout", authorize(Role.ADMIN, Role.USER
|
|
17
|
+
router.post("/change-password", authorize(Role.ADMIN, Role.USER), authController.changePassword)
|
|
18
|
+
router.post("/logout", authorize(Role.ADMIN, Role.USER), authController.logoutUser)
|
|
19
19
|
router.post("/verify-email", authController.verifyEmail)
|
|
20
20
|
router.post("/forget-password", authController.forgetPassword)
|
|
21
21
|
router.post("/reset-password", authController.resetPassword)
|
|
22
|
-
|
|
23
|
-
router.get("/login
|
|
24
|
-
router.get("/
|
|
22
|
+
router.post("/resend-otp", authController.resendOTP);
|
|
23
|
+
router.get("/login/:provider", authController.socialLogin);
|
|
24
|
+
router.get("/:provider/success", authController.socialLoginSuccess);
|
|
25
25
|
router.get("/oauth/error", authController.handleOAuthError);
|
|
26
|
+
router.patch(
|
|
27
|
+
"/profile",
|
|
28
|
+
authorize(Role.ADMIN, Role.USER),
|
|
29
|
+
authController.updateProfile,
|
|
30
|
+
);
|
|
26
31
|
|
|
27
32
|
export const authRoutes = router;
|