stackkit 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/README.md +50 -42
  2. package/dist/cli/add.js +122 -56
  3. package/dist/cli/create.d.ts +2 -0
  4. package/dist/cli/create.js +271 -95
  5. package/dist/cli/doctor.js +1 -0
  6. package/dist/cli/list.d.ts +1 -1
  7. package/dist/cli/list.js +6 -4
  8. package/dist/index.js +234 -191
  9. package/dist/lib/constants.d.ts +4 -0
  10. package/dist/lib/constants.js +4 -0
  11. package/dist/lib/discovery/module-discovery.d.ts +4 -0
  12. package/dist/lib/discovery/module-discovery.js +56 -0
  13. package/dist/lib/generation/code-generator.d.ts +11 -2
  14. package/dist/lib/generation/code-generator.js +42 -3
  15. package/dist/lib/generation/generator-utils.js +3 -1
  16. package/dist/lib/pm/package-manager.js +16 -13
  17. package/dist/lib/ui/logger.js +3 -2
  18. package/dist/lib/utils/path-resolver.d.ts +2 -0
  19. package/dist/lib/utils/path-resolver.js +8 -0
  20. package/dist/meta.json +8312 -0
  21. package/modules/auth/better-auth/files/{shared → express}/config/env.ts +48 -50
  22. package/modules/auth/better-auth/files/express/middlewares/authorize.ts +20 -1
  23. package/modules/auth/better-auth/files/express/modules/auth.controller.ts +349 -0
  24. package/modules/auth/better-auth/files/express/modules/{auth/auth.route.ts → auth.route.ts} +9 -4
  25. package/modules/auth/better-auth/files/express/modules/auth.service.ts +664 -0
  26. package/modules/auth/better-auth/files/express/modules/{auth/auth.type.ts → auth.type.ts} +22 -9
  27. package/modules/auth/better-auth/files/{shared/mongoose/auth/helper.ts → express/mongo-modules/auth.helper.ts} +11 -1
  28. package/modules/auth/better-auth/files/express/types/express.d.ts +11 -0
  29. package/modules/auth/better-auth/files/nextjs/api-route.ts +74 -0
  30. package/modules/auth/better-auth/files/nextjs/dashboard/pages/(user)/page.tsx +6 -0
  31. package/modules/auth/better-auth/files/nextjs/dashboard/pages/admin/page.tsx +6 -0
  32. package/modules/auth/better-auth/files/nextjs/dashboard/pages/layout.tsx +48 -0
  33. package/modules/auth/better-auth/files/nextjs/dashboard/pages/my-profile/page.tsx +5 -0
  34. package/modules/auth/better-auth/files/nextjs/features/services/auth.service.ts +102 -0
  35. package/modules/auth/better-auth/files/nextjs/layout/layout.tsx +13 -0
  36. package/modules/auth/better-auth/files/nextjs/lib/axios/http.ts +158 -0
  37. package/modules/auth/better-auth/files/nextjs/lib/env.ts +35 -0
  38. package/modules/auth/better-auth/files/nextjs/lib/utils/auth.ts +75 -0
  39. package/modules/auth/better-auth/files/nextjs/lib/utils/cookie.ts +29 -0
  40. package/modules/auth/better-auth/files/nextjs/lib/utils/jwt.ts +28 -0
  41. package/modules/auth/better-auth/files/nextjs/lib/utils/token.ts +49 -0
  42. package/modules/auth/better-auth/files/nextjs/pages/forgot-password/page.tsx +5 -0
  43. package/modules/auth/better-auth/files/nextjs/pages/layout.tsx +11 -0
  44. package/modules/auth/better-auth/files/nextjs/pages/login/page.tsx +9 -0
  45. package/modules/auth/better-auth/files/nextjs/pages/register/page.tsx +5 -0
  46. package/modules/auth/better-auth/files/nextjs/pages/reset-password/page.tsx +10 -0
  47. package/modules/auth/better-auth/files/nextjs/pages/verify-email/page.tsx +10 -0
  48. package/modules/auth/better-auth/files/nextjs/proxy.ts +154 -42
  49. package/modules/auth/better-auth/files/nextjs/theme/providers/theme-provider.tsx +11 -0
  50. package/modules/auth/better-auth/files/nextjs/types/api.types.ts +18 -0
  51. package/modules/auth/better-auth/files/react/components/protected-route.tsx +39 -0
  52. package/modules/auth/better-auth/files/react/components/route-guards.tsx +13 -0
  53. package/modules/auth/better-auth/files/react/dashboard/admin/pages/overview.tsx +3 -0
  54. package/modules/auth/better-auth/files/react/dashboard/pages/overview.tsx +3 -0
  55. package/modules/auth/better-auth/files/react/features/pages/forgot-password.tsx +5 -0
  56. package/modules/auth/better-auth/files/react/features/pages/login.tsx +5 -0
  57. package/modules/auth/better-auth/files/react/features/pages/my-profile.tsx +5 -0
  58. package/modules/auth/better-auth/files/react/features/pages/oauth-callback.tsx +59 -0
  59. package/modules/auth/better-auth/files/react/features/pages/register.tsx +5 -0
  60. package/modules/auth/better-auth/files/react/features/pages/reset-password.tsx +10 -0
  61. package/modules/auth/better-auth/files/react/features/pages/verify-email.tsx +10 -0
  62. package/modules/auth/better-auth/files/react/layout/dashboard-layout.tsx +54 -0
  63. package/modules/auth/better-auth/files/react/lib/axios/http.ts +68 -0
  64. package/modules/auth/better-auth/files/react/lib/env.ts +25 -0
  65. package/modules/auth/better-auth/files/react/router.tsx +73 -0
  66. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider-context.ts +13 -0
  67. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider.tsx +51 -0
  68. package/modules/auth/better-auth/files/react/theme/hooks/use-theme.ts +8 -0
  69. package/modules/auth/better-auth/files/shared/features/components/change-password-dialog.tsx +113 -0
  70. package/modules/auth/better-auth/files/shared/features/components/forgot-password-form.tsx +84 -0
  71. package/modules/auth/better-auth/files/shared/features/components/login-form.tsx +134 -0
  72. package/modules/auth/better-auth/files/shared/features/components/my-profile.tsx +147 -0
  73. package/modules/auth/better-auth/files/shared/features/components/profile-form.tsx +205 -0
  74. package/modules/auth/better-auth/files/shared/features/components/register-form.tsx +100 -0
  75. package/modules/auth/better-auth/files/shared/features/components/reset-password-form.tsx +111 -0
  76. package/modules/auth/better-auth/files/shared/features/components/social-login-buttons.tsx +47 -0
  77. package/modules/auth/better-auth/files/shared/features/components/user-profile-menu.tsx +106 -0
  78. package/modules/auth/better-auth/files/shared/features/components/verify-email-form.tsx +110 -0
  79. package/modules/auth/better-auth/files/shared/features/queries/auth.mutations.tsx +312 -0
  80. package/modules/auth/better-auth/files/shared/features/queries/auth.querie.ts +19 -0
  81. package/modules/auth/better-auth/files/shared/features/services/auth.api.ts +81 -0
  82. package/modules/auth/better-auth/files/shared/features/types/auth.type.ts +47 -0
  83. package/modules/auth/better-auth/files/shared/features/validators/change-password.validator.ts +18 -0
  84. package/modules/auth/better-auth/files/shared/features/validators/forgot.validator.ts +7 -0
  85. package/modules/auth/better-auth/files/shared/features/validators/login.validator.ts +14 -0
  86. package/modules/auth/better-auth/files/shared/features/validators/profile.validator.ts +8 -0
  87. package/modules/auth/better-auth/files/shared/features/validators/register.validator.ts +9 -0
  88. package/modules/auth/better-auth/files/shared/features/validators/reset.validator.ts +9 -0
  89. package/modules/auth/better-auth/files/shared/features/validators/verify.validator.ts +8 -0
  90. package/modules/auth/better-auth/files/shared/lib/auth-client.ts +2 -1
  91. package/modules/auth/better-auth/files/shared/lib/auth.ts +5 -19
  92. package/modules/auth/better-auth/files/shared/lib/constant/dashboard.ts +90 -0
  93. package/modules/auth/better-auth/files/shared/theme/mode-toggle.tsx +30 -0
  94. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-header.tsx +94 -0
  95. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-sidebar.tsx +255 -0
  96. package/modules/auth/better-auth/files/shared/ui/shadcn/components/footer.tsx +35 -0
  97. package/modules/auth/better-auth/files/shared/ui/shadcn/components/navbar.tsx +145 -0
  98. package/modules/auth/better-auth/files/shared/ui/shadcn/form-field/input-field.tsx +440 -0
  99. package/modules/auth/better-auth/files/shared/utils/email.ts +2 -17
  100. package/modules/auth/better-auth/generator.json +172 -51
  101. package/modules/auth/better-auth/module.json +2 -2
  102. package/modules/components/files/shared/hooks/use-file-upload.ts +412 -0
  103. package/modules/components/files/shared/lib/utils/url-helpers.ts +110 -0
  104. package/modules/components/files/shared/shadcn/dashboard/data-table-column-selector.tsx +52 -0
  105. package/modules/components/files/shared/shadcn/dashboard/data-table-footer.tsx +156 -0
  106. package/modules/components/files/shared/shadcn/dashboard/data-table.tsx +405 -0
  107. package/modules/components/files/shared/shadcn/global/form-field/input-field.tsx +440 -0
  108. package/modules/components/files/shared/shadcn/global/form-field/media-uploader-field.tsx +745 -0
  109. package/modules/components/files/shared/shadcn/global/form-field/multi-select-field.tsx +207 -0
  110. package/modules/components/files/shared/shadcn/global/form-field/select-field.tsx +247 -0
  111. package/modules/components/files/shared/shadcn/global/form-field/textarea-field.tsx +277 -0
  112. package/modules/components/files/shared/shadcn/global/form-field/tiptap-editor-field.tsx +35 -0
  113. package/modules/components/files/shared/shadcn/global/no-results.tsx +41 -0
  114. package/modules/components/files/shared/shadcn/tiptap-editor/editor-menu-bar.tsx +217 -0
  115. package/modules/components/files/shared/shadcn/tiptap-editor/tiptap-editor.tsx +104 -0
  116. package/modules/components/files/shared/url/load-more.tsx +93 -0
  117. package/modules/components/files/shared/url/search-bar.tsx +131 -0
  118. package/modules/components/files/shared/url/sort-select.tsx +118 -0
  119. package/modules/components/files/shared/url/url-tabs.tsx +77 -0
  120. package/modules/components/generator.json +109 -0
  121. package/modules/components/module.json +11 -0
  122. package/modules/database/mongoose/generator.json +3 -14
  123. package/modules/database/mongoose/module.json +2 -2
  124. package/modules/database/prisma/generator.json +6 -12
  125. package/modules/database/prisma/module.json +2 -2
  126. package/modules/storage/cloudinary/files/express/config/env.ts +65 -0
  127. package/modules/storage/cloudinary/files/express/config/media.ts +103 -0
  128. package/modules/storage/cloudinary/files/express/modules/media/media.controller.ts +59 -0
  129. package/modules/storage/cloudinary/files/express/modules/media/media.route.ts +29 -0
  130. package/modules/storage/cloudinary/files/express/modules/media/media.service.ts +113 -0
  131. package/modules/storage/cloudinary/files/express/modules/media/media.type.ts +32 -0
  132. package/modules/storage/cloudinary/generator.json +34 -0
  133. package/modules/storage/cloudinary/module.json +11 -0
  134. package/modules/ui/shadcn/generator.json +21 -0
  135. package/modules/ui/shadcn/module.json +11 -0
  136. package/package.json +24 -26
  137. package/templates/express/README.md +11 -16
  138. package/templates/express/src/config/env.ts +7 -5
  139. package/templates/nextjs/README.md +13 -18
  140. package/templates/nextjs/app/favicon.ico +0 -0
  141. package/templates/nextjs/app/layout.tsx +6 -4
  142. package/templates/nextjs/components/providers/query-provider.tsx +3 -0
  143. package/templates/nextjs/env.example +3 -1
  144. package/templates/nextjs/lib/axios/http.ts +23 -0
  145. package/templates/nextjs/lib/env.ts +7 -5
  146. package/templates/nextjs/package.json +2 -1
  147. package/templates/nextjs/template.json +1 -2
  148. package/templates/react/README.md +9 -14
  149. package/templates/react/index.html +1 -1
  150. package/templates/react/package.json +1 -1
  151. package/templates/react/src/assets/favicon.ico +0 -0
  152. package/templates/react/src/components/providers/query-provider.tsx +38 -0
  153. package/templates/react/src/{shared/components → components}/seo.tsx +4 -8
  154. package/templates/react/src/lib/axios/http.ts +24 -0
  155. package/templates/react/src/main.tsx +8 -11
  156. package/templates/react/src/{features/about/pages → pages}/about.tsx +1 -1
  157. package/templates/react/src/{features/home/pages → pages}/home.tsx +1 -1
  158. package/templates/react/src/router.tsx +6 -6
  159. package/templates/react/src/vite-env.d.ts +2 -1
  160. package/templates/react/template.json +0 -1
  161. package/templates/react/tsconfig.app.json +6 -0
  162. package/templates/react/tsconfig.json +7 -1
  163. package/templates/react/vite.config.ts +12 -0
  164. package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +0 -3
  165. package/modules/auth/authjs/files/nextjs/proxy.ts +0 -1
  166. package/modules/auth/authjs/files/shared/lib/auth.ts +0 -119
  167. package/modules/auth/authjs/files/shared/prisma/schema.prisma +0 -61
  168. package/modules/auth/authjs/generator.json +0 -64
  169. package/modules/auth/authjs/module.json +0 -13
  170. package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +0 -264
  171. package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +0 -549
  172. package/modules/auth/better-auth/files/express/templates/google-redirect.ejs +0 -24
  173. package/modules/auth/better-auth/files/nextjs/api/auth/[...all]/route.ts +0 -4
  174. package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +0 -31
  175. package/modules/auth/better-auth/files/nextjs/templates/email-otp.tsx +0 -74
  176. package/templates/nextjs/lib/api/http.ts +0 -40
  177. package/templates/react/public/vite.svg +0 -1
  178. package/templates/react/src/app/layouts/dashboard-layout.tsx +0 -8
  179. package/templates/react/src/app/layouts/public-layout.tsx +0 -5
  180. package/templates/react/src/app/providers.tsx +0 -20
  181. package/templates/react/src/app/router.tsx +0 -21
  182. package/templates/react/src/assets/react.svg +0 -1
  183. package/templates/react/src/shared/api/http.ts +0 -39
  184. package/templates/react/src/shared/components/loading.tsx +0 -8
  185. package/templates/react/src/shared/lib/query-client.ts +0 -12
  186. package/templates/react/src/utils/storage.ts +0 -35
  187. package/templates/react/src/utils/utils.ts +0 -3
  188. /package/modules/auth/better-auth/files/{shared/mongoose/auth/constants.ts → express/mongo-modules/auth.constants.ts} +0 -0
  189. /package/templates/nextjs/app/{page.tsx → (public)/(root)/page.tsx} +0 -0
  190. /package/templates/react/src/{shared/components → components}/error-boundary.tsx +0 -0
  191. /package/templates/react/src/{shared/components → components}/layout.tsx +0 -0
  192. /package/templates/react/src/{shared/pages → pages}/not-found.tsx +0 -0
@@ -1,22 +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
11
  APP_NAME?: string;
12
12
  APP_URL: string;
13
- {{#if framework == "nextjs"}}
14
- API_URL: string;
15
- {{/if}}
16
13
  DATABASE_URL: string;
17
14
  FRONTEND_URL: string;
18
15
  BETTER_AUTH_URL: string;
19
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;
20
23
  GOOGLE_CLIENT_ID: string;
21
24
  GOOGLE_CLIENT_SECRET: string;
22
25
  GOOGLE_CALLBACK_URL: string;
@@ -27,28 +30,32 @@ interface EnvConfig {
27
30
  SMTP_PORT: string;
28
31
  SMTP_FROM: string;
29
32
  };
30
- {{#if framework == "express" }}
31
- NODE_ENV: string;
32
- PORT: string;
33
- BETTER_AUTH_SESSION_TOKEN_EXPIRES_IN: string;
34
- BETTER_AUTH_SESSION_TOKEN_UPDATE_AGE: string;
35
- ACCESS_TOKEN_SECRET: string;
36
- ACCESS_TOKEN_EXPIRES_IN: string;
37
- REFRESH_TOKEN_SECRET: string;
38
- REFRESH_TOKEN_EXPIRES_IN: 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
+ };
39
40
  {{/if}}
40
41
  }
41
42
 
42
43
  const loadEnvVars = (): EnvConfig => {
43
44
  const requiredEnvVars = [
45
+ "NODE_ENV",
46
+ "PORT",
47
+ "APP_NAME",
44
48
  "APP_URL",
45
- {{#if framework == "nextjs"}}
46
- "API_URL",
47
- {{/if}}
48
49
  "DATABASE_URL",
49
50
  "FRONTEND_URL",
50
51
  "BETTER_AUTH_URL",
51
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",
52
59
  "GOOGLE_CLIENT_ID",
53
60
  "GOOGLE_CLIENT_SECRET",
54
61
  "GOOGLE_CALLBACK_URL",
@@ -57,43 +64,40 @@ const loadEnvVars = (): EnvConfig => {
57
64
  "EMAIL_SENDER_SMTP_HOST",
58
65
  "EMAIL_SENDER_SMTP_PORT",
59
66
  "EMAIL_SENDER_SMTP_FROM",
60
- {{#if framework == "express" }}
61
- "NODE_ENV",
62
- "PORT",
63
- "BETTER_AUTH_SESSION_TOKEN_EXPIRES_IN",
64
- "BETTER_AUTH_SESSION_TOKEN_UPDATE_AGE",
65
- "ACCESS_TOKEN_SECRET",
66
- "ACCESS_TOKEN_EXPIRES_IN",
67
- "REFRESH_TOKEN_SECRET",
68
- "REFRESH_TOKEN_EXPIRES_IN",
67
+ {{#if storageProvider == "cloudinary"}}
68
+ "CLOUDINARY_CLOUD_NAME",
69
+ "CLOUDINARY_API_KEY",
70
+ "CLOUDINARY_API_SECRET",
71
+ "CLOUDINARY_UPLOAD_PRESET",
69
72
  {{/if}}
70
73
  ];
71
74
 
72
75
  requiredEnvVars.forEach((varName) => {
73
76
  if (!process.env[varName]) {
74
- {{#if framework == "express" }}
75
77
  throw new AppError(
76
78
  status.INTERNAL_SERVER_ERROR,
77
79
  `Environment variable ${varName} is required but not set in .env file.`,
78
80
  );
79
- {{else}}
80
- throw new Error(
81
- `Environment variable ${varName} is required but not defined.`,
82
- );
83
- {{/if}}
84
81
  }
85
82
  });
86
83
 
87
84
  return {
88
- {{#if framework == "nextjs"}}
89
- APP_NAME: process.env.NEXT_PUBLIC_APP_NAME ?? "Your App",
90
- APP_URL: process.env.NEXT_PUBLIC_APP_URL as string,
91
- API_URL: process.env.NEXT_PUBLIC_API_URL as string,
92
- {{/if}}
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",
88
+ APP_URL: process.env.APP_URL as string,
93
89
  DATABASE_URL: process.env.DATABASE_URL as string,
94
90
  FRONTEND_URL: process.env.FRONTEND_URL as string,
95
91
  BETTER_AUTH_URL: process.env.BETTER_AUTH_URL as string,
96
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,
97
101
  GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID as string,
98
102
  GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET as string,
99
103
  GOOGLE_CALLBACK_URL: process.env.GOOGLE_CALLBACK_URL as string,
@@ -104,19 +108,13 @@ const loadEnvVars = (): EnvConfig => {
104
108
  SMTP_PORT: process.env.EMAIL_SENDER_SMTP_PORT as string,
105
109
  SMTP_FROM: process.env.EMAIL_SENDER_SMTP_FROM as string,
106
110
  },
107
- {{#if framework == "express" }}
108
- APP_NAME: process.env.APP_NAME ?? "Your App",
109
- APP_URL: process.env.APP_URL as string,
110
- NODE_ENV: process.env.NODE_ENV as string,
111
- PORT: process.env.PORT as string,
112
- BETTER_AUTH_SESSION_TOKEN_EXPIRES_IN: process.env
113
- .BETTER_AUTH_SESSION_TOKEN_EXPIRES_IN as string,
114
- BETTER_AUTH_SESSION_TOKEN_UPDATE_AGE: process.env
115
- .BETTER_AUTH_SESSION_TOKEN_UPDATE_AGE as string,
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,
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
+ },
120
118
  {{/if}}
121
119
  };
122
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
- id: sessionExists.userId,
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 './auth.constants';
5
+ import { Role } from "./auth.constants";
6
6
  {{/if}}
7
7
  import { Router } from "express";
8
8
  import { authorize } from "../../shared/middlewares/authorize.middleware";
@@ -19,9 +19,14 @@ router.post("/logout", authorize(Role.ADMIN, Role.USER), authController.logoutUs
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/google", authController.googleLogin);
24
- router.get("/google/success", authController.googleLoginSuccess);
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;