stackkit 0.2.8 → 0.3.0
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 +4 -0
- package/bin/stackkit.js +8 -5
- package/dist/cli/add.js +4 -9
- package/dist/cli/create.js +4 -9
- package/dist/cli/doctor.js +11 -32
- package/dist/lib/constants.js +3 -4
- package/dist/lib/conversion/js-conversion.js +20 -16
- package/dist/lib/discovery/installed-detection.js +28 -38
- package/dist/lib/discovery/module-discovery.d.ts +0 -15
- package/dist/lib/discovery/module-discovery.js +15 -50
- package/dist/lib/framework/framework-utils.d.ts +4 -5
- package/dist/lib/framework/framework-utils.js +38 -49
- package/dist/lib/fs/files.js +1 -1
- package/dist/lib/generation/code-generator.d.ts +13 -19
- package/dist/lib/generation/code-generator.js +159 -175
- package/dist/lib/generation/generator-utils.js +3 -15
- package/dist/lib/project/detect.js +11 -19
- package/dist/lib/utils/fs-helpers.d.ts +1 -1
- package/modules/auth/authjs/generator.json +16 -16
- package/modules/auth/better-auth/files/express/middlewares/authorize.ts +178 -40
- package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +264 -0
- package/modules/auth/better-auth/files/express/modules/auth/auth.route.ts +27 -0
- package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +537 -0
- package/modules/auth/better-auth/files/express/modules/auth/auth.type.ts +33 -0
- package/modules/auth/better-auth/files/express/templates/google-redirect.ejs +91 -0
- package/modules/auth/better-auth/files/express/templates/otp.ejs +87 -0
- package/modules/auth/better-auth/files/express/types/express.d.ts +6 -8
- package/modules/auth/better-auth/files/express/utils/cookie.ts +19 -0
- package/modules/auth/better-auth/files/express/utils/jwt.ts +34 -0
- package/modules/auth/better-auth/files/express/utils/token.ts +66 -0
- package/modules/auth/better-auth/files/nextjs/api/auth/[...all]/route.ts +1 -1
- package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +11 -1
- package/modules/auth/better-auth/files/nextjs/templates/email-otp.tsx +74 -0
- package/modules/auth/better-auth/files/shared/config/env.ts +117 -0
- package/modules/auth/better-auth/files/shared/lib/auth-client.ts +1 -1
- package/modules/auth/better-auth/files/shared/lib/auth.ts +167 -79
- package/modules/auth/better-auth/files/shared/mongoose/auth/constants.ts +11 -0
- package/modules/auth/better-auth/files/shared/mongoose/auth/helper.ts +51 -0
- package/modules/auth/better-auth/files/shared/prisma/schema.prisma +22 -11
- package/modules/auth/better-auth/files/shared/utils/email.ts +70 -0
- package/modules/auth/better-auth/generator.json +162 -80
- package/modules/database/mongoose/files/lib/mongoose.ts +28 -3
- package/modules/database/mongoose/generator.json +18 -18
- package/modules/database/prisma/generator.json +44 -44
- package/package.json +2 -2
- package/templates/express/env.example +3 -2
- package/templates/express/eslint.config.mjs +7 -0
- package/templates/express/node_modules/.bin/acorn +17 -0
- package/templates/express/node_modules/.bin/eslint +17 -0
- package/templates/express/node_modules/.bin/tsc +17 -0
- package/templates/express/node_modules/.bin/tsserver +17 -0
- package/templates/express/node_modules/.bin/tsx +17 -0
- package/templates/express/package.json +12 -6
- package/templates/express/src/app.ts +15 -7
- package/templates/express/src/config/cors.ts +8 -7
- package/templates/express/src/config/env.ts +28 -5
- package/templates/express/src/config/logger.ts +2 -2
- package/templates/express/src/config/rate-limit.ts +2 -2
- package/templates/express/src/modules/health/health.controller.ts +13 -11
- package/templates/express/src/routes/index.ts +1 -6
- package/templates/express/src/server.ts +12 -12
- package/templates/express/src/shared/errors/app-error.ts +16 -0
- package/templates/express/src/shared/middlewares/error.middleware.ts +154 -12
- package/templates/express/src/shared/middlewares/not-found.middleware.ts +2 -1
- package/templates/express/src/shared/utils/catch-async.ts +11 -0
- package/templates/express/src/shared/utils/pagination.ts +6 -1
- package/templates/express/src/shared/utils/send-response.ts +25 -0
- package/templates/nextjs/lib/env.ts +19 -8
- package/modules/auth/better-auth/files/shared/lib/email/email-service.ts +0 -33
- package/modules/auth/better-auth/files/shared/lib/email/email-templates.ts +0 -89
- package/templates/express/eslint.config.cjs +0 -42
- package/templates/express/src/config/helmet.ts +0 -5
- package/templates/express/src/modules/health/health.service.ts +0 -6
- package/templates/express/src/shared/errors/error-codes.ts +0 -9
- package/templates/express/src/shared/logger/logger.ts +0 -20
- package/templates/express/src/shared/utils/async-handler.ts +0 -9
- package/templates/express/src/shared/utils/response.ts +0 -9
|
@@ -0,0 +1,264 @@
|
|
|
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
|
+
|
|
12
|
+
const registerUser = catchAsync(async (req: Request, res: Response) => {
|
|
13
|
+
const payload = req.body;
|
|
14
|
+
|
|
15
|
+
const result = await authService.registerUser(payload);
|
|
16
|
+
|
|
17
|
+
const { accessToken, refreshToken, token, ...rest } = result;
|
|
18
|
+
|
|
19
|
+
tokenUtils.setAccessTokenCookie(res, accessToken);
|
|
20
|
+
tokenUtils.setRefreshTokenCookie(res, refreshToken);
|
|
21
|
+
tokenUtils.setBetterAuthSessionCookie(res, token as string);
|
|
22
|
+
|
|
23
|
+
sendResponse(res, {
|
|
24
|
+
status: status.CREATED,
|
|
25
|
+
success: true,
|
|
26
|
+
message: "User registered successfully",
|
|
27
|
+
data: {
|
|
28
|
+
token,
|
|
29
|
+
accessToken,
|
|
30
|
+
refreshToken,
|
|
31
|
+
...rest,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const loginUser = catchAsync(
|
|
37
|
+
async (req: Request, res: Response) => {
|
|
38
|
+
const payload = req.body;
|
|
39
|
+
const result = await authService.loginUser(payload);
|
|
40
|
+
const { accessToken, refreshToken, token, ...rest } = result
|
|
41
|
+
|
|
42
|
+
if (!token) {
|
|
43
|
+
throw new AppError(
|
|
44
|
+
status.INTERNAL_SERVER_ERROR,
|
|
45
|
+
"Session token is missing",
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
tokenUtils.setAccessTokenCookie(res, accessToken);
|
|
50
|
+
tokenUtils.setRefreshTokenCookie(res, refreshToken);
|
|
51
|
+
tokenUtils.setBetterAuthSessionCookie(res, token);
|
|
52
|
+
|
|
53
|
+
sendResponse(res, {
|
|
54
|
+
status: status.OK,
|
|
55
|
+
success: true,
|
|
56
|
+
message: "User logged in successfully",
|
|
57
|
+
data: {
|
|
58
|
+
token,
|
|
59
|
+
accessToken,
|
|
60
|
+
refreshToken,
|
|
61
|
+
...rest,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const getMe = catchAsync(
|
|
68
|
+
async (req: Request, res: Response) => {
|
|
69
|
+
const user = req.user;
|
|
70
|
+
const result = await authService.getMe(user);
|
|
71
|
+
sendResponse(res, {
|
|
72
|
+
status: status.OK,
|
|
73
|
+
success: true,
|
|
74
|
+
message: "User profile fetched successfully",
|
|
75
|
+
data: result,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
const getNewToken = catchAsync(
|
|
81
|
+
async (req: Request, res: Response) => {
|
|
82
|
+
const refreshToken = req.cookies.refreshToken;
|
|
83
|
+
const betterAuthSessionToken = req.cookies["better-auth.session_token"];
|
|
84
|
+
if (!refreshToken) {
|
|
85
|
+
throw new AppError(status.UNAUTHORIZED, "Refresh token is missing");
|
|
86
|
+
}
|
|
87
|
+
const result = await authService.getNewToken(refreshToken, betterAuthSessionToken);
|
|
88
|
+
|
|
89
|
+
const { accessToken, refreshToken: newRefreshToken, sessionToken } = result;
|
|
90
|
+
|
|
91
|
+
tokenUtils.setAccessTokenCookie(res, accessToken);
|
|
92
|
+
tokenUtils.setRefreshTokenCookie(res, newRefreshToken);
|
|
93
|
+
tokenUtils.setBetterAuthSessionCookie(res, sessionToken);
|
|
94
|
+
|
|
95
|
+
sendResponse(res, {
|
|
96
|
+
status: status.OK,
|
|
97
|
+
success: true,
|
|
98
|
+
message: "New tokens generated successfully",
|
|
99
|
+
data: {
|
|
100
|
+
accessToken,
|
|
101
|
+
refreshToken: newRefreshToken,
|
|
102
|
+
sessionToken,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
const changePassword = catchAsync(
|
|
109
|
+
async (req: Request, res: Response) => {
|
|
110
|
+
const payload = req.body;
|
|
111
|
+
const betterAuthSessionToken = req.cookies["better-auth.session_token"];
|
|
112
|
+
|
|
113
|
+
const result = await authService.changePassword(payload, betterAuthSessionToken);
|
|
114
|
+
|
|
115
|
+
const { accessToken, refreshToken, token } = result;
|
|
116
|
+
|
|
117
|
+
tokenUtils.setAccessTokenCookie(res, accessToken);
|
|
118
|
+
tokenUtils.setRefreshTokenCookie(res, refreshToken);
|
|
119
|
+
tokenUtils.setBetterAuthSessionCookie(res, token as string);
|
|
120
|
+
|
|
121
|
+
sendResponse(res, {
|
|
122
|
+
status: status.OK,
|
|
123
|
+
success: true,
|
|
124
|
+
message: "Password changed successfully",
|
|
125
|
+
data: result,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
const logoutUser = catchAsync(
|
|
131
|
+
async (req: Request, res: Response) => {
|
|
132
|
+
const betterAuthSessionToken = req.cookies["better-auth.session_token"];
|
|
133
|
+
const result = await authService.logoutUser(betterAuthSessionToken);
|
|
134
|
+
cookieUtils.clearCookie(res, 'accessToken', {
|
|
135
|
+
httpOnly: true,
|
|
136
|
+
secure: true,
|
|
137
|
+
sameSite: "none",
|
|
138
|
+
});
|
|
139
|
+
cookieUtils.clearCookie(res, 'refreshToken', {
|
|
140
|
+
httpOnly: true,
|
|
141
|
+
secure: true,
|
|
142
|
+
sameSite: "none",
|
|
143
|
+
});
|
|
144
|
+
cookieUtils.clearCookie(res, 'better-auth.session_token', {
|
|
145
|
+
httpOnly: true,
|
|
146
|
+
secure: true,
|
|
147
|
+
sameSite: "none",
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
sendResponse(res, {
|
|
151
|
+
status: status.OK,
|
|
152
|
+
success: true,
|
|
153
|
+
message: "User logged out successfully",
|
|
154
|
+
data: result,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
const verifyEmail = catchAsync(
|
|
160
|
+
async (req: Request, res: Response) => {
|
|
161
|
+
const { email, otp } = req.body;
|
|
162
|
+
await authService.verifyEmail(email, otp);
|
|
163
|
+
|
|
164
|
+
sendResponse(res, {
|
|
165
|
+
status: status.OK,
|
|
166
|
+
success: true,
|
|
167
|
+
message: "Email verified successfully",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
const forgetPassword = catchAsync(
|
|
173
|
+
async (req: Request, res: Response) => {
|
|
174
|
+
const { email } = req.body;
|
|
175
|
+
await authService.forgetPassword(email);
|
|
176
|
+
|
|
177
|
+
sendResponse(res, {
|
|
178
|
+
status: status.OK,
|
|
179
|
+
success: true,
|
|
180
|
+
message: "Password reset OTP sent to email successfully",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
const resetPassword = catchAsync(
|
|
186
|
+
async (req: Request, res: Response) => {
|
|
187
|
+
const { email, otp, newPassword } = req.body;
|
|
188
|
+
await authService.resetPassword(email, otp, newPassword);
|
|
189
|
+
|
|
190
|
+
sendResponse(res, {
|
|
191
|
+
status: status.OK,
|
|
192
|
+
success: true,
|
|
193
|
+
message: "Password reset successfully",
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
const googleLogin = catchAsync((req: Request, res: Response) => {
|
|
199
|
+
const redirectPath = req.query.redirect || "/dashboard";
|
|
200
|
+
|
|
201
|
+
const encodedRedirectPath = encodeURIComponent(redirectPath as string);
|
|
202
|
+
|
|
203
|
+
const callbackURL = `${envVars.BETTER_AUTH_URL}/api/v1/auth/google/success?redirect=${encodedRedirectPath}`;
|
|
204
|
+
|
|
205
|
+
res.render("googleRedirect", {
|
|
206
|
+
callbackURL : callbackURL,
|
|
207
|
+
betterAuthUrl : envVars.BETTER_AUTH_URL,
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
const googleLoginSuccess = catchAsync(async (req: Request, res: Response) => {
|
|
212
|
+
const redirectPath = req.query.redirect as string || "/dashboard";
|
|
213
|
+
|
|
214
|
+
const sessionToken = req.cookies["better-auth.session_token"];
|
|
215
|
+
|
|
216
|
+
if(!sessionToken){
|
|
217
|
+
return res.redirect(`${envVars.FRONTEND_URL}/login?error=oauth_failed`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const session = await auth.api.getSession({
|
|
221
|
+
headers:{
|
|
222
|
+
"Cookie" : `better-auth.session_token=${sessionToken}`
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
if (!session) {
|
|
227
|
+
return res.redirect(`${envVars.FRONTEND_URL}/login?error=no_session_found`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if(session && !session.user){
|
|
231
|
+
return res.redirect(`${envVars.FRONTEND_URL}/login?error=no_user_found`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const result = await authService.googleLoginSuccess(session);
|
|
235
|
+
|
|
236
|
+
const {accessToken, refreshToken} = result;
|
|
237
|
+
|
|
238
|
+
tokenUtils.setAccessTokenCookie(res, accessToken);
|
|
239
|
+
tokenUtils.setRefreshTokenCookie(res, refreshToken);
|
|
240
|
+
const isValidRedirectPath = redirectPath.startsWith("/") && !redirectPath.startsWith("//");
|
|
241
|
+
const finalRedirectPath = isValidRedirectPath ? redirectPath : "/dashboard";
|
|
242
|
+
|
|
243
|
+
res.redirect(`${envVars.FRONTEND_URL}${finalRedirectPath}`);
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
const handleOAuthError = catchAsync((req: Request, res: Response) => {
|
|
247
|
+
const error = req.query.error as string || "oauth_failed";
|
|
248
|
+
res.redirect(`${envVars.FRONTEND_URL}/login?error=${error}`);
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
export const authController = {
|
|
252
|
+
registerUser,
|
|
253
|
+
loginUser,
|
|
254
|
+
getMe,
|
|
255
|
+
getNewToken,
|
|
256
|
+
changePassword,
|
|
257
|
+
logoutUser,
|
|
258
|
+
verifyEmail,
|
|
259
|
+
forgetPassword,
|
|
260
|
+
resetPassword,
|
|
261
|
+
googleLogin,
|
|
262
|
+
googleLoginSuccess,
|
|
263
|
+
handleOAuthError,
|
|
264
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{{#if database == "prisma"}}
|
|
2
|
+
import { Role } from "@prisma/client";
|
|
3
|
+
{{/if}}
|
|
4
|
+
{{#if database == "mongoose"}}
|
|
5
|
+
import { Role } from './auth.constants';
|
|
6
|
+
{{/if}}
|
|
7
|
+
import { Router } from "express";
|
|
8
|
+
import { authorize } from "../../shared/middlewares/authorize.middleware";
|
|
9
|
+
import { authController } from "./auth.controller";
|
|
10
|
+
|
|
11
|
+
const router = Router()
|
|
12
|
+
|
|
13
|
+
router.post("/register", authController.registerUser)
|
|
14
|
+
router.post("/login", authController.loginUser)
|
|
15
|
+
router.get("/me", authorize(Role.ADMIN, Role.USER, Role.SUPER_ADMIN), authController.getMe)
|
|
16
|
+
router.post("/refresh-token", authController.getNewToken)
|
|
17
|
+
router.post("/change-password", authorize(Role.ADMIN, Role.USER, Role.SUPER_ADMIN), authController.changePassword)
|
|
18
|
+
router.post("/logout", authorize(Role.ADMIN, Role.USER, Role.SUPER_ADMIN), authController.logoutUser)
|
|
19
|
+
router.post("/verify-email", authController.verifyEmail)
|
|
20
|
+
router.post("/forget-password", authController.forgetPassword)
|
|
21
|
+
router.post("/reset-password", authController.resetPassword)
|
|
22
|
+
|
|
23
|
+
router.get("/login/google", authController.googleLogin);
|
|
24
|
+
router.get("/google/success", authController.googleLoginSuccess);
|
|
25
|
+
router.get("/oauth/error", authController.handleOAuthError);
|
|
26
|
+
|
|
27
|
+
export const authRoutes = router;
|