@imksh/create 1.0.30 → 1.0.32
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/src/utils/askTemplate.js +2 -1
- package/templates/layers/electron/base/electron/main.js +262 -0
- package/templates/layers/electron/base/electron/preload.js +37 -0
- package/templates/layers/electron/base/electron-builder.json5 +68 -0
- package/templates/layers/electron/javascript/_gitignore +38 -0
- package/templates/layers/electron/javascript/package.json +49 -0
- package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
- package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
- package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
- package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
- package/templates/layers/electron/javascript/src/main.jsx +17 -0
- package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
- package/templates/layers/electron/javascript/vite.config.js +29 -0
- package/templates/layers/electron/typescript/_gitignore +38 -0
- package/templates/layers/electron/typescript/electron/main.ts +213 -0
- package/templates/layers/electron/typescript/electron/preload.ts +52 -0
- package/templates/layers/electron/typescript/package.json +52 -0
- package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
- package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
- package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
- package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
- package/templates/layers/electron/typescript/src/main.tsx +17 -0
- package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
- package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
- package/templates/layers/electron/typescript/tsconfig.json +25 -0
- package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
- package/templates/layers/electron/typescript/vite.config.ts +29 -0
- package/templates/layers/next/javascript/package.json +8 -2
- package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
- package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
- package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
- package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
- package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
- package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
- package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
- package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
- package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
- package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
- package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
- package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
- package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
- package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
- package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
- package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
- package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
- package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
- package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
- package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
- 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/Breadcrumbs.tsx +57 -8
- package/templates/layers/next/typescript/src/components/common/Loading.tsx +2 -1
- package/templates/layers/next/typescript/src/components/common/LottieIcon.tsx +2 -2
- package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
- package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
- package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
- package/templates/layers/next/typescript/src/components/common/Table.tsx +205 -39
- package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
- package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
- package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
- package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
- package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +3 -1
- package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
- package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
- package/templates/layers/next/typescript/src/components/ui/Header.tsx +2 -2
- package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
- package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
- package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
- package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
- package/templates/layers/next/typescript/src/hooks/usePwaInstall.ts +10 -4
- package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
- 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/next/typescript/src/store/useUiStore.ts +14 -4
- 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-lock.json +2 -2
- package/templates/layers/node/typescript/package.json +17 -3
- 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/prisma/schema.prisma +35 -10
- package/templates/layers/prisma/typescript/src/config/db.ts +3 -2
- package/templates/layers/react/javascript/package.json +6 -0
- package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
- package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
- package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
- package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
- package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
- package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
- package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
- package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
- package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
- package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
- package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
- package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
- package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
- package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
- 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/Breadcrumbs.tsx +32 -6
- package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
- package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
- package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
- package/templates/layers/react/typescript/src/components/common/Table.tsx +201 -38
- package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
- package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
- package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
- package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
- package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
- package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
- package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
- package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
- package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
- 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 +11 -1
- 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 +3 -3
- package/templates/layers/react-native/base/src/app/doc-viewer.tsx +3 -3
- package/templates/layers/react-native/base/src/components/common/ConfirmationToast.tsx +58 -40
- package/templates/layers/react-native/base/src/components/common/GoogleAuthButton.tsx +144 -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/ClassicTabBar.tsx +15 -12
- package/templates/layers/react-native/base/src/components/ui/CustomTabBar.tsx +84 -90
- 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/config/appConfig.ts +1 -1
- package/templates/layers/react-native/base/src/services/auth.service.ts +3 -1
- package/templates/layers/react-native/base/src/services/permission.service.ts +0 -2
- 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/fileDownload.ts +206 -0
- package/templates/layers/react-native/base/src/utils/storage.ts +1 -1
- package/templates/presets/electron/preset.json +9 -0
- package/templates/presets/electron-ts/preset.json +9 -0
- 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 +11 -1
- 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/server/package.json +11 -2
- package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
- 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
|
@@ -7,6 +7,7 @@ export interface User {
|
|
|
7
7
|
email: string;
|
|
8
8
|
phone?: string;
|
|
9
9
|
role: string;
|
|
10
|
+
orgRole?: string;
|
|
10
11
|
avatar?: {
|
|
11
12
|
url?: string;
|
|
12
13
|
publicId?: string;
|
|
@@ -15,6 +16,20 @@ export interface User {
|
|
|
15
16
|
updatedAt?: string;
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
interface ApiErrorResponse {
|
|
20
|
+
response?: {
|
|
21
|
+
data?: {
|
|
22
|
+
message?: string;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
message?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const getErrorMessage = (err: unknown): string => {
|
|
29
|
+
const apiErr = err as ApiErrorResponse;
|
|
30
|
+
return apiErr.response?.data?.message || apiErr.message || "An unexpected error occurred";
|
|
31
|
+
};
|
|
32
|
+
|
|
18
33
|
export interface AuthState {
|
|
19
34
|
user: User | null;
|
|
20
35
|
loading: boolean;
|
|
@@ -39,9 +54,9 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
39
54
|
try {
|
|
40
55
|
await authService.sendOtp(email);
|
|
41
56
|
return true;
|
|
42
|
-
} catch (err:
|
|
57
|
+
} catch (err: unknown) {
|
|
43
58
|
set({
|
|
44
|
-
error: err
|
|
59
|
+
error: getErrorMessage(err),
|
|
45
60
|
loading: false,
|
|
46
61
|
});
|
|
47
62
|
return false;
|
|
@@ -62,9 +77,9 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
62
77
|
loading: false,
|
|
63
78
|
});
|
|
64
79
|
return true;
|
|
65
|
-
} catch (err:
|
|
80
|
+
} catch (err: unknown) {
|
|
66
81
|
set({
|
|
67
|
-
error: err
|
|
82
|
+
error: getErrorMessage(err),
|
|
68
83
|
loading: false,
|
|
69
84
|
});
|
|
70
85
|
return false;
|
|
@@ -85,9 +100,9 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
85
100
|
loading: false,
|
|
86
101
|
});
|
|
87
102
|
return true;
|
|
88
|
-
} catch (err:
|
|
103
|
+
} catch (err: unknown) {
|
|
89
104
|
set({
|
|
90
|
-
error: err
|
|
105
|
+
error: getErrorMessage(err),
|
|
91
106
|
loading: false,
|
|
92
107
|
});
|
|
93
108
|
return false;
|
|
@@ -101,7 +116,7 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
101
116
|
try {
|
|
102
117
|
const { data } = await authService.getMe();
|
|
103
118
|
set({ user: data.data, isCheckingAuth: false });
|
|
104
|
-
} catch
|
|
119
|
+
} catch {
|
|
105
120
|
set({ isCheckingAuth: false });
|
|
106
121
|
} finally {
|
|
107
122
|
set({ isCheckingAuth: false });
|
|
@@ -114,9 +129,9 @@ export const useAuthStore = create<AuthState>((set) => ({
|
|
|
114
129
|
const { data } = await authService.updateMe(updates);
|
|
115
130
|
set({ user: data.data, loading: false });
|
|
116
131
|
return true;
|
|
117
|
-
} catch (err:
|
|
132
|
+
} catch (err: unknown) {
|
|
118
133
|
set({
|
|
119
|
-
error: err
|
|
134
|
+
error: getErrorMessage(err),
|
|
120
135
|
loading: false,
|
|
121
136
|
});
|
|
122
137
|
return false;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { create } from "zustand";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
|
+
import type { BreadcrumbItem } from "../components/common/Breadcrumbs";
|
|
3
4
|
|
|
4
5
|
export interface UiState {
|
|
5
6
|
open: boolean;
|
|
@@ -7,6 +8,8 @@ export interface UiState {
|
|
|
7
8
|
isMobileMenuOpened: boolean;
|
|
8
9
|
headerTitle: string;
|
|
9
10
|
headerActions: ReactNode;
|
|
11
|
+
breadcrumbs: BreadcrumbItem[] | null;
|
|
12
|
+
setBreadcrumbs: (items: BreadcrumbItem[] | null) => void;
|
|
10
13
|
setHeaderTitle: (title: string) => void;
|
|
11
14
|
setHeaderActions: (actions: ReactNode) => void;
|
|
12
15
|
toggleCollapsed: () => void;
|
|
@@ -18,6 +21,8 @@ export interface UiState {
|
|
|
18
21
|
toggleSidebar: () => void;
|
|
19
22
|
openSidebar: () => void;
|
|
20
23
|
close: () => void;
|
|
24
|
+
modalOpen: boolean;
|
|
25
|
+
setModalOpen: (open: boolean) => void;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
export const useUiStore = create<UiState>((set, get) => ({
|
|
@@ -26,6 +31,8 @@ export const useUiStore = create<UiState>((set, get) => ({
|
|
|
26
31
|
isMobileMenuOpened: false,
|
|
27
32
|
headerTitle: "Dashboard",
|
|
28
33
|
headerActions: null,
|
|
34
|
+
breadcrumbs: null,
|
|
35
|
+
setBreadcrumbs: (items) => set({ breadcrumbs: items }),
|
|
29
36
|
setHeaderTitle: (title: string) => set({ headerTitle: title }),
|
|
30
37
|
setHeaderActions: (actions: ReactNode) => set({ headerActions: actions }),
|
|
31
38
|
toggleCollapsed: () => set((state) => ({ collapsed: !state.collapsed })),
|
|
@@ -37,7 +44,10 @@ export const useUiStore = create<UiState>((set, get) => ({
|
|
|
37
44
|
set((state) => ({ isMobileMenuOpened: !state.isMobileMenuOpened }));
|
|
38
45
|
},
|
|
39
46
|
|
|
40
|
-
theme:
|
|
47
|
+
theme:
|
|
48
|
+
typeof window !== "undefined"
|
|
49
|
+
? localStorage.getItem("theme") || "light"
|
|
50
|
+
: "light",
|
|
41
51
|
|
|
42
52
|
setTheme: (theme) => {
|
|
43
53
|
if (typeof document !== "undefined") {
|
|
@@ -46,20 +56,17 @@ export const useUiStore = create<UiState>((set, get) => ({
|
|
|
46
56
|
if (typeof window !== "undefined") {
|
|
47
57
|
localStorage.setItem("theme", theme);
|
|
48
58
|
}
|
|
49
|
-
|
|
50
59
|
set({ theme });
|
|
51
60
|
},
|
|
52
61
|
|
|
53
62
|
toggleTheme: () => {
|
|
54
63
|
const nextTheme = get().theme === "light" ? "dark" : "light";
|
|
55
|
-
|
|
56
64
|
if (typeof document !== "undefined") {
|
|
57
65
|
document.documentElement.setAttribute("data-theme", nextTheme);
|
|
58
66
|
}
|
|
59
67
|
if (typeof window !== "undefined") {
|
|
60
68
|
localStorage.setItem("theme", nextTheme);
|
|
61
69
|
}
|
|
62
|
-
|
|
63
70
|
set({ theme: nextTheme });
|
|
64
71
|
},
|
|
65
72
|
|
|
@@ -77,4 +84,7 @@ export const useUiStore = create<UiState>((set, get) => ({
|
|
|
77
84
|
set({
|
|
78
85
|
open: false,
|
|
79
86
|
}),
|
|
87
|
+
|
|
88
|
+
modalOpen: false,
|
|
89
|
+
setModalOpen: (open) => set({ modalOpen: open }),
|
|
80
90
|
}));
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
"scripts": {
|
|
7
7
|
"start": "node src/index.js",
|
|
8
8
|
"dev": "nodemon src/index.js",
|
|
9
|
+
"lint": "eslint src/**/*.js",
|
|
10
|
+
"lint:fix": "eslint src/**/*.js --fix",
|
|
11
|
+
"format": "prettier --write \"src/**/*.{js,json}\"",
|
|
12
|
+
"format:check": "prettier --check \"src/**/*.{js,json}\"",
|
|
13
|
+
"circular-check": "dpdm --circular --no-warning --no-tree --exit-code circular:1 \"src/**/*.js\"",
|
|
9
14
|
"generate:vapid": "node ./src/utils/generateVapidKeys.js"
|
|
10
15
|
},
|
|
11
16
|
"dependencies": {
|
|
@@ -25,9 +30,13 @@
|
|
|
25
30
|
"nodemailer": "^6.9.7",
|
|
26
31
|
"socket.io": "^4.7.2",
|
|
27
32
|
"web-push": "^3.6.6",
|
|
28
|
-
"expo-server-sdk": "^3.10.0"
|
|
33
|
+
"expo-server-sdk": "^3.10.0",
|
|
34
|
+
"google-auth-library": "^9.14.2"
|
|
29
35
|
},
|
|
30
36
|
"devDependencies": {
|
|
31
|
-
"
|
|
37
|
+
"dpdm": "^3.14.0",
|
|
38
|
+
"eslint": "^8.53.0",
|
|
39
|
+
"nodemon": "^3.0.1",
|
|
40
|
+
"prettier": "^3.4.2"
|
|
32
41
|
}
|
|
33
|
-
}
|
|
42
|
+
}
|
|
@@ -7,6 +7,7 @@ import morgan from "morgan";
|
|
|
7
7
|
import apiRoutes from "./routes/index.js";
|
|
8
8
|
import { notFound, errorHandler } from "./middleware/errorHandler.js";
|
|
9
9
|
import { apiLimiter } from "./middleware/rateLimiter.js";
|
|
10
|
+
import { SuccessResponse } from "./utils/SuccessResponse.js";
|
|
10
11
|
|
|
11
12
|
const app = express();
|
|
12
13
|
|
|
@@ -32,7 +33,7 @@ app.use("/api", apiRoutes);
|
|
|
32
33
|
|
|
33
34
|
// Example health route
|
|
34
35
|
app.get("/api/health", (req, res) => {
|
|
35
|
-
res
|
|
36
|
+
SuccessResponse(res, 200, "API is running");
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
// Error handling middlewares
|
|
@@ -1,181 +1,119 @@
|
|
|
1
1
|
import * as authService from "../services/auth.service.js";
|
|
2
2
|
import { generateToken } from "../utils/jwt.js";
|
|
3
3
|
import genOtpToken from "../utils/genOtpToken.js";
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const newUser = await authService.signup({ name, email, phone, password, otp });
|
|
15
|
-
const token = await generateToken(newUser, res);
|
|
16
|
-
const safeUser = newUser.toObject();
|
|
17
|
-
delete safeUser.password;
|
|
18
|
-
|
|
19
|
-
res
|
|
20
|
-
.status(201)
|
|
21
|
-
.json({ message: "Registration Successful", data: safeUser, token });
|
|
22
|
-
} catch (error) {
|
|
23
|
-
logger.error("Error in signup controller", error);
|
|
24
|
-
next(error);
|
|
4
|
+
import { asyncHandler } from "../utils/asyncHandler.js";
|
|
5
|
+
import { SuccessResponse } from "../utils/SuccessResponse.js";
|
|
6
|
+
import { ApiError } from "../utils/ApiError.js";
|
|
7
|
+
|
|
8
|
+
export const signup = asyncHandler(async (req, res) => {
|
|
9
|
+
const { name, email, phone, password, otp } = req.body;
|
|
10
|
+
if (!email || !password || !name || !otp) {
|
|
11
|
+
throw new ApiError(400, "All fields are required.");
|
|
25
12
|
}
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const user = await authService.login({ email, password });
|
|
39
|
-
const token = await generateToken(user, res);
|
|
40
|
-
const safeUser = user.toObject();
|
|
41
|
-
delete safeUser.password;
|
|
42
|
-
|
|
43
|
-
res
|
|
44
|
-
.status(200)
|
|
45
|
-
.json({ message: "Login successfully", data: safeUser, token });
|
|
46
|
-
} catch (error) {
|
|
47
|
-
logger.error("Error in login controller", error);
|
|
48
|
-
next(error);
|
|
13
|
+
const newUser = await authService.signup({ name, email, phone, password, otp });
|
|
14
|
+
const token = await generateToken(newUser, res);
|
|
15
|
+
const safeUser = newUser.toObject();
|
|
16
|
+
delete safeUser.password;
|
|
17
|
+
SuccessResponse(res, 201, "Registration Successful", { user: safeUser, token });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const login = asyncHandler(async (req, res) => {
|
|
21
|
+
const { email, password } = req.body;
|
|
22
|
+
if (!email || !password) {
|
|
23
|
+
throw new ApiError(400, "All fields are required.");
|
|
49
24
|
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
25
|
+
const user = await authService.login({ email, password });
|
|
26
|
+
const token = await generateToken(user, res);
|
|
27
|
+
const safeUser = user.toObject();
|
|
28
|
+
delete safeUser.password;
|
|
29
|
+
SuccessResponse(res, 200, "Login successfully", { user: safeUser, token });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export const logout = asyncHandler(async (_req, res) => {
|
|
33
|
+
res.clearCookie("token", {
|
|
34
|
+
httpOnly: true,
|
|
35
|
+
secure: process.env.NODE_ENV === "production",
|
|
36
|
+
sameSite: "strict",
|
|
37
|
+
path: "/",
|
|
38
|
+
});
|
|
39
|
+
SuccessResponse(res, 200, "Logout Successfully");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const getMe = asyncHandler(async (req, res) => {
|
|
43
|
+
const safeUser = typeof req.user?.toObject === "function" ? req.user.toObject() : { ...req.user };
|
|
44
|
+
delete safeUser.password;
|
|
45
|
+
SuccessResponse(res, 200, "User Authenticated", safeUser);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const genSignupOtp = asyncHandler(async (req, res) => {
|
|
49
|
+
const { email } = req.body;
|
|
50
|
+
if (!email) {
|
|
51
|
+
throw new ApiError(400, "Email is required.");
|
|
65
52
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
logger.error("Error in getMe controller", error);
|
|
75
|
-
next(error);
|
|
53
|
+
await authService.generateSignupOtp(email);
|
|
54
|
+
SuccessResponse(res, 200, "Signup Otp sent successfully");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export const genOtp = asyncHandler(async (req, res) => {
|
|
58
|
+
const { email } = req.body;
|
|
59
|
+
if (!email) {
|
|
60
|
+
throw new ApiError(400, "Email is required.");
|
|
76
61
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
await authService.generateSignupOtp(email);
|
|
88
|
-
res.status(200).json({ message: "Signup Otp sent successfully" });
|
|
89
|
-
} catch (error) {
|
|
90
|
-
logger.error("Error in genSignupOtp", error);
|
|
91
|
-
next(error);
|
|
62
|
+
await authService.generateOtp(email);
|
|
63
|
+
SuccessResponse(res, 200, "Otp sent successfully");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const verifyOtp = asyncHandler(async (req, res) => {
|
|
67
|
+
const { email, otp } = req.body;
|
|
68
|
+
if (!email || !otp) {
|
|
69
|
+
throw new ApiError(400, "All fields are required.");
|
|
92
70
|
}
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
await authService.generateOtp(email);
|
|
106
|
-
res.status(200).json({ message: "Otp sent successfully" });
|
|
107
|
-
} catch (error) {
|
|
108
|
-
logger.error("Error in genOtp", error);
|
|
109
|
-
next(error);
|
|
71
|
+
const user = await authService.verifyOtp({ email, otp });
|
|
72
|
+
await genOtpToken(user, res);
|
|
73
|
+
SuccessResponse(res, 200, "Otp verified successfully");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export const resetPassword = asyncHandler(async (req, res) => {
|
|
77
|
+
const { newPassword } = req.body;
|
|
78
|
+
const currentUser = req.user;
|
|
79
|
+
if (!newPassword) {
|
|
80
|
+
throw new ApiError(400, "All fields are required.");
|
|
110
81
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
82
|
+
await authService.resetPassword(currentUser, newPassword);
|
|
83
|
+
SuccessResponse(res, 200, "Password updated successfully");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const updateMe = asyncHandler(async (req, res) => {
|
|
87
|
+
const { name, phone } = req.body;
|
|
88
|
+
const user = await authService.updateProfile(req.user._id, {
|
|
89
|
+
name,
|
|
90
|
+
phone,
|
|
91
|
+
file: req.file,
|
|
92
|
+
});
|
|
93
|
+
const safeUser = user.toObject();
|
|
94
|
+
delete safeUser.password;
|
|
95
|
+
SuccessResponse(res, 200, "Profile updated successfully", safeUser);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export const savePushToken = asyncHandler(async (req, res) => {
|
|
99
|
+
const { token } = req.body;
|
|
100
|
+
if (!token) {
|
|
101
|
+
throw new ApiError(400, "Token is required.");
|
|
130
102
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
status: 400,
|
|
140
|
-
message: "All fields are required.",
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
await authService.resetPassword(currentUser, newPassword);
|
|
145
|
-
res.status(200).json({ message: "Password updated successfully" });
|
|
146
|
-
} catch (error) {
|
|
147
|
-
logger.error("Error in resetPassword", error);
|
|
148
|
-
next(error);
|
|
103
|
+
await authService.savePushToken(req.user._id, token);
|
|
104
|
+
SuccessResponse(res, 200, "Push token saved successfully");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export const googleLogin = asyncHandler(async (req, res) => {
|
|
108
|
+
const { idToken } = req.body;
|
|
109
|
+
if (!idToken) {
|
|
110
|
+
throw new ApiError(400, "idToken is required.");
|
|
149
111
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const { name, phone } = req.body;
|
|
155
|
-
const user = await authService.updateProfile(req.user._id, {
|
|
156
|
-
name,
|
|
157
|
-
phone,
|
|
158
|
-
file: req.file,
|
|
159
|
-
});
|
|
160
|
-
const safeUser = user.toObject();
|
|
112
|
+
const user = await authService.googleLogin(idToken);
|
|
113
|
+
const token = await generateToken(user, res);
|
|
114
|
+
const safeUser = typeof user.toObject === "function" ? user.toObject() : { ...user };
|
|
115
|
+
if (safeUser.password !== undefined) {
|
|
161
116
|
delete safeUser.password;
|
|
162
|
-
res.status(200).json({ success: true, data: safeUser });
|
|
163
|
-
} catch (error) {
|
|
164
|
-
logger.error("Error updating profile", error);
|
|
165
|
-
next(error);
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
export const savePushToken = async (req, res, next) => {
|
|
170
|
-
try {
|
|
171
|
-
const { token } = req.body;
|
|
172
|
-
if (!token) {
|
|
173
|
-
return next({ status: 400, message: "Token is required." });
|
|
174
|
-
}
|
|
175
|
-
await authService.savePushToken(req.user._id, token);
|
|
176
|
-
res.status(200).json({ success: true, message: "Push token saved successfully" });
|
|
177
|
-
} catch (error) {
|
|
178
|
-
logger.error("Error saving push token", error);
|
|
179
|
-
next(error);
|
|
180
117
|
}
|
|
181
|
-
};
|
|
118
|
+
SuccessResponse(res, 200, "Google Login successfully", { user: safeUser, token });
|
|
119
|
+
});
|
|
@@ -1,72 +1,65 @@
|
|
|
1
1
|
import PushSubscription from "../models/pushSubscription.model.js";
|
|
2
|
-
import
|
|
2
|
+
import { asyncHandler } from "../utils/asyncHandler.js";
|
|
3
|
+
import { SuccessResponse } from "../utils/SuccessResponse.js";
|
|
4
|
+
import { ApiError } from "../utils/ApiError.js";
|
|
3
5
|
|
|
4
|
-
export const subscribe = async (req, res
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const userId = req.user?._id || req.user?.id; // supports both mongoose and prisma IDs
|
|
8
|
-
|
|
9
|
-
if (!subscription) {
|
|
10
|
-
return next({ status: 400, message: "Invalid subscription payload" });
|
|
11
|
-
}
|
|
6
|
+
export const subscribe = asyncHandler(async (req, res) => {
|
|
7
|
+
const subscription = req.body;
|
|
8
|
+
const userId = req.user?._id || req.user?.id; // supports both mongoose and prisma IDs
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
if (!subscription) {
|
|
11
|
+
throw new ApiError(400, "Invalid subscription payload");
|
|
12
|
+
}
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
14
|
+
const isExpo = !!subscription.token;
|
|
15
|
+
const isWeb = !!(subscription.endpoint && subscription.keys);
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (userId && String(existingSub.user) !== String(userId)) {
|
|
24
|
-
existingSub.user = userId; await existingSub.save();
|
|
25
|
-
}
|
|
26
|
-
return res.status(200).json({ success: true, message: "Already subscribed." });
|
|
27
|
-
}
|
|
17
|
+
if (!isExpo && !isWeb) {
|
|
18
|
+
throw new ApiError(400, "Missing token (EXPO) or endpoint/keys (WEB)");
|
|
19
|
+
}
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const existingSub = await PushSubscription.findOne({ endpoint: subscription.endpoint });
|
|
36
|
-
if (existingSub) {
|
|
37
|
-
if (userId && String(existingSub.user) !== String(userId)) {
|
|
38
|
-
existingSub.user = userId; await existingSub.save();
|
|
39
|
-
}
|
|
40
|
-
return res.status(200).json({ success: true, message: "Already subscribed." });
|
|
21
|
+
if (isExpo) {
|
|
22
|
+
const existingSub = await PushSubscription.findOne({ expoPushToken: subscription.token });
|
|
23
|
+
if (existingSub) {
|
|
24
|
+
if (userId && String(existingSub.user) !== String(userId)) {
|
|
25
|
+
existingSub.user = userId;
|
|
26
|
+
await existingSub.save();
|
|
41
27
|
}
|
|
28
|
+
return SuccessResponse(res, 200, "Already subscribed.");
|
|
29
|
+
}
|
|
42
30
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
await PushSubscription.create({
|
|
32
|
+
type: "EXPO",
|
|
33
|
+
expoPushToken: subscription.token,
|
|
34
|
+
user: userId || undefined,
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
const existingSub = await PushSubscription.findOne({ endpoint: subscription.endpoint });
|
|
38
|
+
if (existingSub) {
|
|
39
|
+
if (userId && String(existingSub.user) !== String(userId)) {
|
|
40
|
+
existingSub.user = userId;
|
|
41
|
+
await existingSub.save();
|
|
42
|
+
}
|
|
43
|
+
return SuccessResponse(res, 200, "Already subscribed.");
|
|
49
44
|
}
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
await PushSubscription.create({
|
|
47
|
+
type: "WEB",
|
|
48
|
+
endpoint: subscription.endpoint,
|
|
49
|
+
keys: subscription.keys,
|
|
50
|
+
user: userId || undefined,
|
|
51
|
+
});
|
|
55
52
|
}
|
|
56
|
-
};
|
|
57
53
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const { endpoint } = req.body;
|
|
61
|
-
if (!endpoint) {
|
|
62
|
-
return next({ status: 400, message: "Endpoint required" });
|
|
63
|
-
}
|
|
54
|
+
SuccessResponse(res, 201, "Subscription created.");
|
|
55
|
+
});
|
|
64
56
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
logger.error("Error in push unsubscribe controller", error);
|
|
70
|
-
next(error);
|
|
57
|
+
export const unsubscribe = asyncHandler(async (req, res) => {
|
|
58
|
+
const { endpoint } = req.body;
|
|
59
|
+
if (!endpoint) {
|
|
60
|
+
throw new ApiError(400, "Endpoint required");
|
|
71
61
|
}
|
|
72
|
-
|
|
62
|
+
|
|
63
|
+
await PushSubscription.deleteOne({ endpoint });
|
|
64
|
+
SuccessResponse(res, 200, "Unsubscribed.");
|
|
65
|
+
});
|
|
@@ -1,26 +1,19 @@
|
|
|
1
1
|
import { uploadSingleToCloudinary } from "../utils/cloudinaryUpload.js";
|
|
2
|
-
import
|
|
2
|
+
import { asyncHandler } from "../utils/asyncHandler.js";
|
|
3
|
+
import { SuccessResponse } from "../utils/SuccessResponse.js";
|
|
4
|
+
import { ApiError } from "../utils/ApiError.js";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Handle single file uploads to Cloudinary
|
|
6
8
|
*/
|
|
7
|
-
export const uploadFile = async (req, res
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
9
|
+
export const uploadFile = asyncHandler(async (req, res) => {
|
|
10
|
+
if (!req.file) {
|
|
11
|
+
throw new ApiError(400, "No file uploaded");
|
|
12
|
+
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const result = await uploadSingleToCloudinary(req.file, {
|
|
15
|
+
folder: req.body.folder || "general_uploads",
|
|
16
|
+
});
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
message: "File uploaded successfully",
|
|
20
|
-
data: result,
|
|
21
|
-
});
|
|
22
|
-
} catch (error) {
|
|
23
|
-
logger.error("Upload controller error", error);
|
|
24
|
-
next(error);
|
|
25
|
-
}
|
|
26
|
-
};
|
|
18
|
+
SuccessResponse(res, 200, "File uploaded successfully", result);
|
|
19
|
+
});
|