@imksh/create 1.0.31 → 1.0.33
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 +2 -0
- package/package.json +1 -1
- package/src/commands/create.js +2 -0
- package/src/index.js +1 -1
- package/templates/layers/next/javascript/package.json +8 -2
- package/templates/layers/next/typescript/.eslintrc.json +12 -0
- package/templates/layers/next/typescript/package.json +14 -3
- package/templates/layers/next/typescript/src/components/common/Loading.tsx +1 -1
- package/templates/layers/next/typescript/src/components/common/LottieIcon.tsx +2 -2
- package/templates/layers/next/typescript/src/components/common/Table.tsx +4 -4
- package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +2 -1
- package/templates/layers/next/typescript/src/components/ui/Header.tsx +2 -2
- package/templates/layers/next/typescript/src/hooks/usePwaInstall.ts +10 -4
- package/templates/layers/next/typescript/src/services/authService.ts +1 -1
- package/templates/layers/next/typescript/src/store/useAuthStore.ts +24 -9
- package/templates/layers/node/javascript/.env.example +3 -1
- package/templates/layers/node/javascript/package.json +12 -3
- package/templates/layers/node/javascript/src/app.js +2 -1
- package/templates/layers/node/javascript/src/controllers/auth.controller.js +106 -168
- package/templates/layers/node/javascript/src/controllers/push.controller.js +51 -58
- package/templates/layers/node/javascript/src/controllers/upload.controller.js +12 -19
- package/templates/layers/node/javascript/src/middleware/auth.middleware.js +5 -11
- package/templates/layers/node/javascript/src/middleware/errorHandler.js +6 -4
- package/templates/layers/node/javascript/src/routes/auth.route.js +2 -0
- package/templates/layers/node/javascript/src/services/auth.service.js +159 -164
- package/templates/layers/node/javascript/src/utils/ApiError.js +20 -0
- package/templates/layers/node/javascript/src/utils/SuccessResponse.js +15 -0
- package/templates/layers/node/javascript/src/utils/apiResponse.js +4 -0
- package/templates/layers/node/javascript/src/utils/asyncHandler.js +5 -0
- package/templates/layers/node/typescript/.env.example +3 -1
- package/templates/layers/node/typescript/.eslintrc.cjs +28 -0
- package/templates/layers/node/typescript/package.json +16 -2
- package/templates/layers/node/typescript/src/app.ts +3 -2
- package/templates/layers/node/typescript/src/config/db.ts +3 -2
- package/templates/layers/node/typescript/src/config/socket.ts +5 -4
- package/templates/layers/node/typescript/src/controllers/auth.controller.ts +104 -132
- package/templates/layers/node/typescript/src/controllers/push.controller.ts +52 -59
- package/templates/layers/node/typescript/src/controllers/upload.controller.ts +12 -19
- package/templates/layers/node/typescript/src/middleware/auth.middleware.ts +13 -7
- package/templates/layers/node/typescript/src/middleware/errorHandler.ts +18 -5
- package/templates/layers/node/typescript/src/middleware/rateLimiter.ts +2 -2
- package/templates/layers/node/typescript/src/routes/auth.route.ts +2 -0
- package/templates/layers/node/typescript/src/services/auth.service.ts +86 -43
- package/templates/layers/node/typescript/src/types/express.d.ts +5 -3
- package/templates/layers/node/typescript/src/utils/ApiError.ts +25 -0
- package/templates/layers/node/typescript/src/utils/SuccessResponse.ts +17 -0
- package/templates/layers/node/typescript/src/utils/apiResponse.ts +4 -0
- package/templates/layers/node/typescript/src/utils/asyncHandler.ts +13 -0
- package/templates/layers/node/typescript/src/utils/cloudinaryUpload.ts +18 -7
- package/templates/layers/node/typescript/src/utils/expoNotification.ts +1 -1
- package/templates/layers/node/typescript/src/utils/genOtpToken.ts +7 -1
- package/templates/layers/node/typescript/src/utils/jwt.ts +8 -2
- package/templates/layers/node/typescript/src/utils/logger.ts +2 -1
- package/templates/layers/node/typescript/src/utils/pushHelper.ts +13 -9
- package/templates/layers/node-prisma/javascript/.env.example +3 -1
- package/templates/layers/node-prisma/javascript/package.json +11 -2
- package/templates/layers/node-prisma/javascript/src/app.js +2 -1
- package/templates/layers/node-prisma/javascript/src/controllers/auth.controller.js +90 -135
- package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +64 -76
- package/templates/layers/node-prisma/javascript/src/controllers/upload.controller.js +12 -19
- package/templates/layers/node-prisma/javascript/src/middleware/auth.middleware.js +5 -4
- package/templates/layers/node-prisma/javascript/src/middleware/errorHandler.js +6 -4
- package/templates/layers/node-prisma/javascript/src/routes/auth.route.js +2 -0
- package/templates/layers/node-prisma/javascript/src/services/auth.service.js +145 -122
- package/templates/layers/node-prisma/javascript/src/utils/ApiError.js +20 -0
- package/templates/layers/node-prisma/javascript/src/utils/SuccessResponse.js +15 -0
- package/templates/layers/node-prisma/javascript/src/utils/apiResponse.js +4 -0
- package/templates/layers/node-prisma/javascript/src/utils/asyncHandler.js +5 -0
- package/templates/layers/node-prisma/typescript/.env.example +3 -1
- package/templates/layers/node-prisma/typescript/.eslintrc.cjs +28 -0
- package/templates/layers/node-prisma/typescript/package.json +19 -5
- package/templates/layers/node-prisma/typescript/src/app.ts +3 -2
- package/templates/layers/node-prisma/typescript/src/config/prisma.ts +3 -2
- package/templates/layers/node-prisma/typescript/src/config/socket.ts +5 -4
- package/templates/layers/node-prisma/typescript/src/controllers/auth.controller.ts +89 -105
- package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -83
- package/templates/layers/node-prisma/typescript/src/controllers/upload.controller.ts +12 -19
- package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +9 -13
- package/templates/layers/node-prisma/typescript/src/middleware/errorHandler.ts +18 -5
- package/templates/layers/node-prisma/typescript/src/middleware/rateLimiter.ts +2 -2
- package/templates/layers/node-prisma/typescript/src/routes/auth.route.ts +2 -0
- package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +83 -31
- package/templates/layers/node-prisma/typescript/src/types/express.d.ts +5 -3
- package/templates/layers/node-prisma/typescript/src/utils/ApiError.ts +25 -0
- package/templates/layers/node-prisma/typescript/src/utils/SuccessResponse.ts +17 -0
- package/templates/layers/node-prisma/typescript/src/utils/apiResponse.ts +4 -0
- package/templates/layers/node-prisma/typescript/src/utils/asyncHandler.ts +13 -0
- package/templates/layers/node-prisma/typescript/src/utils/cloudinaryUpload.ts +18 -7
- package/templates/layers/node-prisma/typescript/src/utils/genOtpToken.ts +7 -1
- package/templates/layers/node-prisma/typescript/src/utils/jwt.ts +8 -2
- package/templates/layers/node-prisma/typescript/src/utils/logger.ts +2 -1
- package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +10 -6
- package/templates/layers/prisma/javascript/package.json +8 -0
- package/templates/layers/prisma/typescript/package.json +14 -1
- package/templates/layers/prisma/typescript/src/config/db.ts +3 -2
- package/templates/layers/react/javascript/package.json +6 -0
- package/templates/layers/react/typescript/.eslintrc.cjs +20 -0
- package/templates/layers/react/typescript/package.json +11 -2
- package/templates/layers/react/typescript/src/components/common/Table.tsx +4 -4
- package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +1 -1
- package/templates/layers/react/typescript/src/main.tsx +11 -11
- package/templates/layers/react/typescript/src/services/authService.ts +1 -1
- package/templates/layers/react/typescript/src/store/useAuthStore.ts +24 -9
- package/templates/layers/react-native/base/app.json +9 -22
- package/templates/layers/react-native/base/eslint.config.js +5 -1
- package/templates/layers/react-native/base/package.json +40 -33
- package/templates/layers/react-native/base/src/app/(auth)/edit-profile.tsx +7 -7
- package/templates/layers/react-native/base/src/app/(auth)/login.tsx +31 -7
- package/templates/layers/react-native/base/src/app/(tabs)/_layout.tsx +3 -3
- package/templates/layers/react-native/base/src/app/(tabs)/index.tsx +6 -5
- package/templates/layers/react-native/base/src/app/_layout.tsx +4 -8
- package/templates/layers/react-native/base/src/app/screens/doc-viewer.tsx +378 -0
- package/templates/layers/react-native/base/src/components/common/ConfirmationToast.tsx +58 -40
- package/templates/layers/react-native/base/src/components/common/GoogleAuthButton.tsx +123 -0
- package/templates/layers/react-native/base/src/components/common/LocationLoader.tsx +64 -9
- package/templates/layers/react-native/base/src/components/common/Typography.tsx +54 -19
- package/templates/layers/react-native/base/src/components/ui/BiometricLock.tsx +3 -3
- package/templates/layers/react-native/base/src/components/ui/ClassicTabBar.tsx +15 -12
- package/templates/layers/react-native/base/src/components/ui/CustomTabBar.tsx +90 -91
- package/templates/layers/react-native/base/src/components/ui/Drawer.tsx +344 -0
- package/templates/layers/react-native/base/src/components/ui/Sidebar.tsx +4 -4
- package/templates/layers/react-native/base/src/components/ui/buttons/Button.tsx +29 -6
- package/templates/layers/react-native/base/src/components/ui/data-display/Accordion.tsx +2 -2
- package/templates/layers/react-native/base/src/components/ui/data-display/Badge.tsx +1 -2
- package/templates/layers/react-native/base/src/components/ui/data-display/Image.tsx +3 -3
- package/templates/layers/react-native/base/src/components/ui/data-display/SwipeableRow.tsx +28 -7
- package/templates/layers/react-native/base/src/components/ui/feedback/GestureWrapper.tsx +11 -4
- package/templates/layers/react-native/base/src/components/ui/inputs/DatePicker.tsx +3 -3
- package/templates/layers/react-native/base/src/components/ui/inputs/ImagePickerModal.tsx +45 -19
- package/templates/layers/react-native/base/src/components/ui/inputs/Input.tsx +28 -9
- package/templates/layers/react-native/base/src/components/ui/inputs/SearchablePicker.tsx +199 -0
- package/templates/layers/react-native/base/src/components/ui/inputs/Switch.tsx +7 -7
- package/templates/layers/react-native/base/src/components/ui/inputs/WebLocationPicker.tsx +146 -72
- package/templates/layers/react-native/base/src/config/api.ts +5 -5
- package/templates/layers/react-native/base/src/services/auth.service.ts +3 -1
- package/templates/layers/react-native/base/src/services/permission.service.ts +168 -44
- package/templates/layers/react-native/base/src/services/socket.service.ts +0 -7
- package/templates/layers/react-native/base/src/store/useAuthStore.ts +61 -16
- package/templates/layers/react-native/base/src/store/useNotificationStore.ts +36 -42
- package/templates/layers/react-native/base/src/types/images.d.ts +7 -5
- package/templates/layers/react-native/base/src/utils/env.ts +47 -0
- package/templates/layers/react-native/base/src/utils/fileDownload.ts +206 -0
- package/templates/layers/react-native/base/src/utils/storage.ts +1 -1
- package/templates/presets/production-fullstack/overrides/client/package.json +9 -0
- package/templates/presets/production-fullstack/overrides/mobile/app.json +2 -15
- package/templates/presets/production-fullstack/overrides/mobile/package.json +39 -29
- package/templates/presets/production-fullstack/overrides/mobile/src/app/_layout.tsx +3 -3
- package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/Drawer.tsx +344 -0
- package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/DatePicker.tsx +3 -3
- package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/ImagePickerModal.tsx +3 -3
- package/templates/presets/production-fullstack/overrides/mobile/src/services/permission.service.ts +168 -44
- package/templates/presets/production-fullstack/overrides/mobile/src/store/useNotificationStore.ts +16 -0
- package/templates/presets/production-fullstack/overrides/mobile/src/utils/env.ts +47 -0
- package/templates/presets/production-fullstack/overrides/server/package.json +11 -2
- package/templates/layers/react-native/base/src/app/doc-viewer.tsx +0 -193
- package/templates/layers/react-native/base/src/components/ui/BottomModal.tsx +0 -176
- package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/BottomModal.tsx +0 -159
- /package/templates/layers/react-native/base/assets/images/logo/{logoWithBg.png → icon.png} +0 -0
|
@@ -1,124 +1,108 @@
|
|
|
1
|
-
import { Request, Response
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
2
|
import * as authService from "../services/auth.service.js";
|
|
3
3
|
import { generateToken } from "../utils/jwt.js";
|
|
4
4
|
import genOtpToken from "../utils/genOtpToken.js";
|
|
5
|
-
import
|
|
5
|
+
import { asyncHandler } from "../utils/asyncHandler.js";
|
|
6
|
+
import { SuccessResponse } from "../utils/SuccessResponse.js";
|
|
7
|
+
import { ApiError } from "../utils/ApiError.js";
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return next({ status: 400, message: "All fields are required." });
|
|
12
|
-
}
|
|
13
|
-
const newUser = await authService.signup({ name, email, phone, password });
|
|
14
|
-
const token = await generateToken(newUser, res);
|
|
15
|
-
const safeUser: any = { ...newUser };
|
|
16
|
-
delete safeUser.password;
|
|
17
|
-
res.status(201).json({ message: "Registration Successful", data: safeUser, token });
|
|
18
|
-
} catch (error) {
|
|
19
|
-
logger.error("Error in signup controller", error);
|
|
20
|
-
next(error);
|
|
21
|
-
}
|
|
9
|
+
const toSafeUser = (user: unknown): Record<string, unknown> => {
|
|
10
|
+
const safeUser: Record<string, unknown> = { ...((user as Record<string, unknown>) || {}) };
|
|
11
|
+
delete safeUser.password;
|
|
12
|
+
return safeUser;
|
|
22
13
|
};
|
|
23
14
|
|
|
24
|
-
export const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return next({ status: 400, message: "All fields are required." });
|
|
29
|
-
}
|
|
30
|
-
const user = await authService.login({ email, password });
|
|
31
|
-
const token = await generateToken(user, res);
|
|
32
|
-
const safeUser: any = { ...user };
|
|
33
|
-
delete safeUser.password;
|
|
34
|
-
res.status(200).json({ message: "Login successfully", data: safeUser, token });
|
|
35
|
-
} catch (error) {
|
|
36
|
-
logger.error("Error in login controller", error);
|
|
37
|
-
next(error);
|
|
15
|
+
export const signup = asyncHandler(async (req: Request, res: Response) => {
|
|
16
|
+
const { name, email, phone, password } = req.body;
|
|
17
|
+
if (!email || !password || !name) {
|
|
18
|
+
throw new ApiError(400, "All fields are required.");
|
|
38
19
|
}
|
|
39
|
-
};
|
|
20
|
+
const newUser = await authService.signup({ name, email, phone, password });
|
|
21
|
+
const token = await generateToken(newUser, res);
|
|
22
|
+
const safeUser = toSafeUser(newUser);
|
|
23
|
+
SuccessResponse(res, 201, "Registration Successful", { user: safeUser, token });
|
|
24
|
+
});
|
|
40
25
|
|
|
41
|
-
export const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
secure: process.env.NODE_ENV === "production",
|
|
46
|
-
sameSite: "strict",
|
|
47
|
-
path: "/",
|
|
48
|
-
});
|
|
49
|
-
res.status(200).json({ message: "Logout Successfully" });
|
|
50
|
-
} catch (error) {
|
|
51
|
-
logger.error("Error in logout controller", error);
|
|
52
|
-
next(error);
|
|
26
|
+
export const login = asyncHandler(async (req: Request, res: Response) => {
|
|
27
|
+
const { email, password } = req.body;
|
|
28
|
+
if (!email || !password) {
|
|
29
|
+
throw new ApiError(400, "All fields are required.");
|
|
53
30
|
}
|
|
54
|
-
};
|
|
31
|
+
const user = await authService.login({ email, password });
|
|
32
|
+
const token = await generateToken(user, res);
|
|
33
|
+
const safeUser = toSafeUser(user);
|
|
34
|
+
SuccessResponse(res, 200, "Login successfully", { user: safeUser, token });
|
|
35
|
+
});
|
|
55
36
|
|
|
56
|
-
export const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
37
|
+
export const logout = asyncHandler(async (req: Request, res: Response) => {
|
|
38
|
+
res.clearCookie("token", {
|
|
39
|
+
httpOnly: true,
|
|
40
|
+
secure: process.env.NODE_ENV === "production",
|
|
41
|
+
sameSite: "strict",
|
|
42
|
+
path: "/",
|
|
43
|
+
});
|
|
44
|
+
SuccessResponse(res, 200, "Logout Successfully");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export const getMe = asyncHandler(async (req: Request, res: Response) => {
|
|
48
|
+
const safeUser = toSafeUser(req.user);
|
|
49
|
+
SuccessResponse(res, 200, "User Authenticated", safeUser);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export const genOtp = asyncHandler(async (req: Request, res: Response) => {
|
|
53
|
+
const { email } = req.body;
|
|
54
|
+
if (!email) {
|
|
55
|
+
throw new ApiError(400, "Email is required.");
|
|
62
56
|
}
|
|
63
|
-
|
|
57
|
+
await authService.generateOtp(email);
|
|
58
|
+
SuccessResponse(res, 200, "Otp sent successfully");
|
|
59
|
+
});
|
|
64
60
|
|
|
65
|
-
export const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return next({ status: 400, message: "Email is required." });
|
|
70
|
-
}
|
|
71
|
-
await authService.generateOtp(email);
|
|
72
|
-
res.status(200).json({ message: "Otp sent successfully" });
|
|
73
|
-
} catch (error) {
|
|
74
|
-
logger.error("Error in genOtp", error);
|
|
75
|
-
next(error);
|
|
61
|
+
export const verifyOtp = asyncHandler(async (req: Request, res: Response) => {
|
|
62
|
+
const { email, otp } = req.body;
|
|
63
|
+
if (!email || !otp) {
|
|
64
|
+
throw new ApiError(400, "All fields are required.");
|
|
76
65
|
}
|
|
77
|
-
};
|
|
66
|
+
const user = await authService.verifyOtp({ email, otp });
|
|
67
|
+
await genOtpToken(user, res);
|
|
68
|
+
SuccessResponse(res, 200, "Otp verified successfully");
|
|
69
|
+
});
|
|
78
70
|
|
|
79
|
-
export const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
const user = await authService.verifyOtp({ email, otp });
|
|
86
|
-
await genOtpToken(user, res);
|
|
87
|
-
res.status(200).json({ message: "Otp verified successfully" });
|
|
88
|
-
} catch (error) {
|
|
89
|
-
logger.error("Error in verifyOtp", error);
|
|
90
|
-
next(error);
|
|
71
|
+
export const resetPassword = asyncHandler(async (req: Request, res: Response) => {
|
|
72
|
+
const { newPassword } = req.body;
|
|
73
|
+
const userId = String(req.user?.id || req.user?._id || "");
|
|
74
|
+
if (!newPassword) {
|
|
75
|
+
throw new ApiError(400, "All fields are required.");
|
|
91
76
|
}
|
|
92
|
-
|
|
77
|
+
if (!userId) {
|
|
78
|
+
throw new ApiError(401, "Not authorized");
|
|
79
|
+
}
|
|
80
|
+
await authService.resetPassword({ id: userId }, newPassword);
|
|
81
|
+
SuccessResponse(res, 200, "Password updated successfully");
|
|
82
|
+
});
|
|
93
83
|
|
|
94
|
-
export const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return next({ status: 400, message: "All fields are required." });
|
|
100
|
-
}
|
|
101
|
-
await authService.resetPassword(currentUser, newPassword);
|
|
102
|
-
res.status(200).json({ message: "Password updated successfully" });
|
|
103
|
-
} catch (error) {
|
|
104
|
-
logger.error("Error in resetPassword", error);
|
|
105
|
-
next(error);
|
|
84
|
+
export const updateMe = asyncHandler(async (req: Request, res: Response) => {
|
|
85
|
+
const { name, phone } = req.body;
|
|
86
|
+
const userId = String(req.user?.id || req.user?._id || "");
|
|
87
|
+
if (!userId) {
|
|
88
|
+
throw new ApiError(401, "Not authorized");
|
|
106
89
|
}
|
|
107
|
-
|
|
90
|
+
const user = await authService.updateProfile(userId, {
|
|
91
|
+
name,
|
|
92
|
+
phone,
|
|
93
|
+
file: req.file,
|
|
94
|
+
});
|
|
95
|
+
const safeUser = toSafeUser(user);
|
|
96
|
+
SuccessResponse(res, 200, "Profile updated successfully", safeUser);
|
|
97
|
+
});
|
|
108
98
|
|
|
109
|
-
export const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
name,
|
|
114
|
-
phone,
|
|
115
|
-
file: req.file,
|
|
116
|
-
});
|
|
117
|
-
const safeUser: any = { ...user };
|
|
118
|
-
delete safeUser.password;
|
|
119
|
-
res.status(200).json({ success: true, data: safeUser });
|
|
120
|
-
} catch (error) {
|
|
121
|
-
logger.error("Error updating profile", error);
|
|
122
|
-
next(error);
|
|
99
|
+
export const googleLogin = asyncHandler(async (req: Request, res: Response) => {
|
|
100
|
+
const { idToken } = req.body;
|
|
101
|
+
if (!idToken) {
|
|
102
|
+
throw new ApiError(400, "idToken is required.");
|
|
123
103
|
}
|
|
124
|
-
|
|
104
|
+
const user = await authService.googleLogin(idToken);
|
|
105
|
+
const token = await generateToken(user, res);
|
|
106
|
+
const safeUser = toSafeUser(user);
|
|
107
|
+
SuccessResponse(res, 200, "Google Login successfully", { user: safeUser, token });
|
|
108
|
+
});
|
|
@@ -1,100 +1,84 @@
|
|
|
1
|
-
import { Request, Response
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
2
|
import { prisma } from "../config/prisma.js";
|
|
3
|
-
import
|
|
3
|
+
import { asyncHandler } from "../utils/asyncHandler.js";
|
|
4
|
+
import { SuccessResponse } from "../utils/SuccessResponse.js";
|
|
5
|
+
import { ApiError } from "../utils/ApiError.js";
|
|
4
6
|
|
|
5
|
-
export const subscribe = async (
|
|
6
|
-
req
|
|
7
|
-
|
|
8
|
-
next: NextFunction,
|
|
9
|
-
) => {
|
|
10
|
-
try {
|
|
11
|
-
const subscription = req.body;
|
|
12
|
-
const userId = req.user?._id || req.user?.id;
|
|
7
|
+
export const subscribe = asyncHandler(async (req: Request, res: Response) => {
|
|
8
|
+
const subscription = req.body;
|
|
9
|
+
const userId = req.user?._id || req.user?.id;
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
if (!subscription) {
|
|
12
|
+
throw new ApiError(400, "Invalid subscription payload");
|
|
13
|
+
}
|
|
17
14
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
// Determine type: EXPO if token is present, else WEB
|
|
16
|
+
const isExpo = !!subscription.token;
|
|
17
|
+
const isWeb = !!(subscription.endpoint && subscription.keys);
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
if (!isExpo && !isWeb) {
|
|
20
|
+
throw new ApiError(400, "Missing token (EXPO) or endpoint/keys (WEB)");
|
|
21
|
+
}
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
if (isExpo) {
|
|
24
|
+
const existingSub = await prisma.pushSubscription.findUnique({
|
|
25
|
+
where: { token: subscription.token },
|
|
26
|
+
});
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
return res.status(200).json({ success: true, message: "Already subscribed." });
|
|
28
|
+
if (existingSub) {
|
|
29
|
+
if (userId && existingSub.userId !== userId) {
|
|
30
|
+
await prisma.pushSubscription.update({
|
|
31
|
+
where: { token: subscription.token },
|
|
32
|
+
data: { userId },
|
|
33
|
+
});
|
|
39
34
|
}
|
|
35
|
+
return SuccessResponse(res, 200, "Already subscribed.");
|
|
36
|
+
}
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
await prisma.pushSubscription.create({
|
|
39
|
+
data: {
|
|
40
|
+
type: "EXPO",
|
|
41
|
+
token: subscription.token,
|
|
42
|
+
userId: userId || null,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
} else {
|
|
46
|
+
const existingSub = await prisma.pushSubscription.findUnique({
|
|
47
|
+
where: { endpoint: subscription.endpoint },
|
|
48
|
+
});
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
return res.status(200).json({ success: true, message: "Already subscribed." });
|
|
50
|
+
if (existingSub) {
|
|
51
|
+
if (userId && existingSub.userId !== userId) {
|
|
52
|
+
await prisma.pushSubscription.update({
|
|
53
|
+
where: { endpoint: subscription.endpoint },
|
|
54
|
+
data: { userId },
|
|
55
|
+
});
|
|
61
56
|
}
|
|
62
|
-
|
|
63
|
-
await prisma.pushSubscription.create({
|
|
64
|
-
data: {
|
|
65
|
-
type: "WEB",
|
|
66
|
-
endpoint: subscription.endpoint,
|
|
67
|
-
keys: subscription.keys,
|
|
68
|
-
userId: userId || null,
|
|
69
|
-
},
|
|
70
|
-
});
|
|
57
|
+
return SuccessResponse(res, 200, "Already subscribed.");
|
|
71
58
|
}
|
|
72
59
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
60
|
+
await prisma.pushSubscription.create({
|
|
61
|
+
data: {
|
|
62
|
+
type: "WEB",
|
|
63
|
+
endpoint: subscription.endpoint,
|
|
64
|
+
keys: subscription.keys,
|
|
65
|
+
userId: userId || null,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
77
68
|
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export const unsubscribe = async (
|
|
81
|
-
req: Request,
|
|
82
|
-
res: Response,
|
|
83
|
-
next: NextFunction,
|
|
84
|
-
) => {
|
|
85
|
-
try {
|
|
86
|
-
const { endpoint } = req.body;
|
|
87
|
-
if (!endpoint) {
|
|
88
|
-
return next({ status: 400, message: "Endpoint required" });
|
|
89
|
-
}
|
|
90
69
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
.catch(() => {});
|
|
70
|
+
SuccessResponse(res, 201, "Subscription created.");
|
|
71
|
+
});
|
|
94
72
|
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
73
|
+
export const unsubscribe = asyncHandler(async (req: Request, res: Response) => {
|
|
74
|
+
const { endpoint } = req.body;
|
|
75
|
+
if (!endpoint) {
|
|
76
|
+
throw new ApiError(400, "Endpoint required");
|
|
99
77
|
}
|
|
100
|
-
|
|
78
|
+
|
|
79
|
+
await prisma.pushSubscription
|
|
80
|
+
.delete({ where: { endpoint } })
|
|
81
|
+
.catch(() => {});
|
|
82
|
+
|
|
83
|
+
SuccessResponse(res, 200, "Unsubscribed.");
|
|
84
|
+
});
|
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
import { Request, Response
|
|
1
|
+
import { Request, Response } from "express";
|
|
2
2
|
import { uploadSingleToCloudinary } from "../utils/cloudinaryUpload.js";
|
|
3
|
-
import
|
|
3
|
+
import { asyncHandler } from "../utils/asyncHandler.js";
|
|
4
|
+
import { SuccessResponse } from "../utils/SuccessResponse.js";
|
|
5
|
+
import { ApiError } from "../utils/ApiError.js";
|
|
4
6
|
|
|
5
|
-
export const uploadFile = async (req: Request, res: Response
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return res.status(400).json({ success: false, message: "No file uploaded" });
|
|
9
|
-
}
|
|
10
|
-
const result = await uploadSingleToCloudinary(req.file, {
|
|
11
|
-
folder: req.body.folder || "general_uploads",
|
|
12
|
-
});
|
|
13
|
-
res.status(200).json({
|
|
14
|
-
success: true,
|
|
15
|
-
message: "File uploaded successfully",
|
|
16
|
-
data: result,
|
|
17
|
-
});
|
|
18
|
-
} catch (error) {
|
|
19
|
-
logger.error("Upload controller error", error);
|
|
20
|
-
next(error);
|
|
7
|
+
export const uploadFile = asyncHandler(async (req: Request, res: Response) => {
|
|
8
|
+
if (!req.file) {
|
|
9
|
+
throw new ApiError(400, "No file uploaded");
|
|
21
10
|
}
|
|
22
|
-
|
|
11
|
+
const result = await uploadSingleToCloudinary(req.file, {
|
|
12
|
+
folder: req.body.folder || "general_uploads",
|
|
13
|
+
});
|
|
14
|
+
SuccessResponse(res, 200, "File uploaded successfully", result);
|
|
15
|
+
});
|
|
@@ -2,9 +2,11 @@ import { Request, Response, NextFunction } from "express";
|
|
|
2
2
|
import { verifyToken } from "../utils/jwt.js";
|
|
3
3
|
import logger from "../utils/logger.js";
|
|
4
4
|
import { prisma } from "../config/prisma.js";
|
|
5
|
+
import { ApiError } from "../utils/ApiError.js";
|
|
5
6
|
|
|
6
7
|
export const protect = async (
|
|
7
8
|
req: Request,
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
8
10
|
res: Response,
|
|
9
11
|
next: NextFunction,
|
|
10
12
|
) => {
|
|
@@ -20,15 +22,13 @@ export const protect = async (
|
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
if (!token) {
|
|
23
|
-
return
|
|
24
|
-
.status(401)
|
|
25
|
-
.json({ message: "Not authorized, no token provided" });
|
|
25
|
+
return next(new ApiError(401, "Not authorized, no token provided"));
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
|
-
const decoded
|
|
29
|
+
const decoded = verifyToken(token) as { id: string } | null;
|
|
30
30
|
if (!decoded) {
|
|
31
|
-
return
|
|
31
|
+
return next(new ApiError(401, "Not authorized, invalid token"));
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
const user = await prisma.user.findUnique({
|
|
@@ -36,20 +36,16 @@ export const protect = async (
|
|
|
36
36
|
});
|
|
37
37
|
|
|
38
38
|
if (!user) {
|
|
39
|
-
return
|
|
40
|
-
.status(401)
|
|
41
|
-
.json({ message: "Not authorized, user not found" });
|
|
39
|
+
return next(new ApiError(401, "Not authorized, user not found"));
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
const safeUser:
|
|
42
|
+
const safeUser: Record<string, unknown> = { ...user };
|
|
45
43
|
delete safeUser.password;
|
|
46
44
|
|
|
47
45
|
req.user = safeUser;
|
|
48
46
|
return next();
|
|
49
|
-
} catch (error:
|
|
47
|
+
} catch (error: unknown) {
|
|
50
48
|
logger.error("Auth middleware error", error);
|
|
51
|
-
return
|
|
52
|
-
.status(401)
|
|
53
|
-
.json({ message: "Not authorized, token verification failed" });
|
|
49
|
+
return next(new ApiError(401, "Not authorized, token verification failed"));
|
|
54
50
|
}
|
|
55
51
|
};
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from "express";
|
|
2
|
+
import { ApiError } from "../utils/ApiError.js";
|
|
3
|
+
|
|
4
|
+
export interface HttpError extends Error {
|
|
5
|
+
statusCode?: number;
|
|
6
|
+
status?: number;
|
|
7
|
+
errors?: unknown[];
|
|
8
|
+
}
|
|
2
9
|
|
|
3
10
|
export const notFound = (req: Request, res: Response, next: NextFunction) => {
|
|
4
|
-
|
|
5
|
-
res.status(404);
|
|
6
|
-
next(error);
|
|
11
|
+
next(new ApiError(404, `Not Found - ${req.originalUrl}`));
|
|
7
12
|
};
|
|
8
13
|
|
|
9
|
-
export const errorHandler = (
|
|
10
|
-
|
|
14
|
+
export const errorHandler = (
|
|
15
|
+
err: HttpError,
|
|
16
|
+
req: Request,
|
|
17
|
+
res: Response,
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
19
|
+
next: NextFunction,
|
|
20
|
+
) => {
|
|
21
|
+
const statusCode = err.statusCode || err.status || (res.statusCode === 200 ? 500 : res.statusCode);
|
|
11
22
|
res.status(statusCode).json({
|
|
12
23
|
success: false,
|
|
24
|
+
statusCode,
|
|
13
25
|
message: err.message || "An unexpected error occurred",
|
|
26
|
+
errors: err.errors || [],
|
|
14
27
|
stack: process.env.NODE_ENV === "production" ? undefined : err.stack,
|
|
15
28
|
});
|
|
16
29
|
};
|
|
@@ -21,8 +21,8 @@ const createLimiter = ({
|
|
|
21
21
|
legacyHeaders: false,
|
|
22
22
|
skipSuccessfulRequests,
|
|
23
23
|
|
|
24
|
-
keyGenerator: (req:
|
|
25
|
-
return req.user?.id || req.ip || req.headers["x-forwarded-for"] || req.socket.remoteAddress;
|
|
24
|
+
keyGenerator: (req: Request) => {
|
|
25
|
+
return String(req.user?.id || req.user?._id || req.ip || req.headers["x-forwarded-for"] || req.socket.remoteAddress || "unknown");
|
|
26
26
|
},
|
|
27
27
|
|
|
28
28
|
handler: (req: Request, res: Response) => {
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
getMe,
|
|
6
6
|
logout,
|
|
7
7
|
updateMe,
|
|
8
|
+
googleLogin,
|
|
8
9
|
} from "../controllers/auth.controller.js";
|
|
9
10
|
import { protect } from "../middleware/auth.middleware.js";
|
|
10
11
|
import { apiLimiter } from "../middleware/rateLimiter.js";
|
|
@@ -15,6 +16,7 @@ router.use(apiLimiter);
|
|
|
15
16
|
|
|
16
17
|
router.post("/signup", signup);
|
|
17
18
|
router.post("/login", login);
|
|
19
|
+
router.post("/google", googleLogin);
|
|
18
20
|
router.post("/logout", logout);
|
|
19
21
|
router.get("/me", protect, getMe);
|
|
20
22
|
router.put("/me", protect, updateMe);
|