@internetderdinge/api 1.229.0 → 1.229.2
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/dist/src/accounts/accounts.controller.js +89 -0
- package/dist/src/accounts/accounts.route.js +101 -0
- package/dist/src/accounts/accounts.schemas.js +12 -0
- package/dist/src/accounts/accounts.service.js +65 -0
- package/dist/src/accounts/accounts.validation.js +99 -0
- package/dist/src/accounts/auth0.service.js +188 -0
- package/dist/src/config/config.js +48 -0
- package/dist/src/config/logger.js +27 -0
- package/dist/src/config/morgan.js +16 -0
- package/dist/src/config/passport.cjs +28 -0
- package/dist/src/config/roles.js +11 -0
- package/dist/src/config/tokens.cjs +10 -0
- package/dist/src/devices/devices.controller.js +172 -0
- package/dist/src/devices/devices.model.js +94 -0
- package/dist/src/devices/devices.route.js +153 -0
- package/dist/src/devices/devices.schemas.js +84 -0
- package/dist/src/devices/devices.service.js +198 -0
- package/dist/src/devices/devices.types.js +1 -0
- package/dist/src/devices/devices.validation.js +257 -0
- package/dist/src/devicesNotifications/devicesNotifications.controller.js +69 -0
- package/dist/src/devicesNotifications/devicesNotifications.model.js +39 -0
- package/dist/src/devicesNotifications/devicesNotifications.route.js +124 -0
- package/dist/src/devicesNotifications/devicesNotifications.schemas.js +10 -0
- package/dist/src/devicesNotifications/devicesNotifications.service.js +181 -0
- package/dist/src/devicesNotifications/devicesNotifications.validation.js +46 -0
- package/dist/src/email/email.service.js +580 -0
- package/dist/src/files/upload.service.js +124 -0
- package/dist/src/i18n/i18n.js +38 -0
- package/dist/src/i18n/saveMissingLocalJsonBackend.js +53 -0
- package/dist/src/i18n/types.js +1 -0
- package/dist/src/index.js +48 -0
- package/dist/src/iotdevice/iotdevice.controller.js +96 -0
- package/dist/src/iotdevice/iotdevice.model.js +17 -0
- package/dist/src/iotdevice/iotdevice.route.js +143 -0
- package/dist/src/iotdevice/iotdevice.schemas.js +60 -0
- package/dist/src/iotdevice/iotdevice.service.js +579 -0
- package/dist/src/iotdevice/iotdevice.types.js +1 -0
- package/dist/src/iotdevice/iotdevice.validation.js +54 -0
- package/dist/src/middlewares/auth.js +75 -0
- package/dist/src/middlewares/checkJwt.cjs +17 -0
- package/dist/src/middlewares/error.js +36 -0
- package/dist/src/middlewares/mongooseValidations/ensureSameOrganization.js +13 -0
- package/dist/src/middlewares/rateLimiter.js +7 -0
- package/dist/src/middlewares/validate.js +18 -0
- package/dist/src/middlewares/validateAction.js +35 -0
- package/dist/src/middlewares/validateAdmin.js +18 -0
- package/dist/src/middlewares/validateAi.js +16 -0
- package/dist/src/middlewares/validateCurrentAuthUser.js +17 -0
- package/dist/src/middlewares/validateCurrentUser.js +20 -0
- package/dist/src/middlewares/validateDevice.js +98 -0
- package/dist/src/middlewares/validateDeviceUserOrganization.js +26 -0
- package/dist/src/middlewares/validateOrganization.js +63 -0
- package/dist/src/middlewares/validateQuerySearchUserAndOrganization.js +44 -0
- package/dist/src/middlewares/validateTokens.js +23 -0
- package/dist/src/middlewares/validateUser.js +38 -0
- package/dist/src/middlewares/validateZod.js +33 -0
- package/dist/src/models/plugins/index.js +4 -0
- package/dist/src/models/plugins/paginate.plugin.js +117 -0
- package/dist/src/models/plugins/paginateNew.plugin.js +185 -0
- package/dist/src/models/plugins/simplePopulate.js +16 -0
- package/dist/src/models/plugins/toJSON.plugin.js +35 -0
- package/dist/src/organizations/organizations.controller.js +64 -0
- package/dist/src/organizations/organizations.model.js +41 -0
- package/dist/src/organizations/organizations.route.js +98 -0
- package/dist/src/organizations/organizations.schemas.js +7 -0
- package/dist/src/organizations/organizations.service.js +59 -0
- package/dist/src/organizations/organizations.validation.js +62 -0
- package/dist/src/pdf/pdf.controller.js +24 -0
- package/dist/src/pdf/pdf.route.js +22 -0
- package/dist/src/pdf/pdf.schemas.js +6 -0
- package/dist/src/pdf/pdf.service.js +65 -0
- package/dist/src/pdf/pdf.validation.js +27 -0
- package/dist/src/tokens/tokens.controller.js +60 -0
- package/dist/src/tokens/tokens.model.js +17 -0
- package/dist/src/tokens/tokens.route.js +52 -0
- package/dist/src/tokens/tokens.schemas.js +14 -0
- package/dist/src/tokens/tokens.service.js +30 -0
- package/dist/src/tokens/tokens.validation.js +9 -0
- package/dist/src/types/routeSpec.js +1 -0
- package/dist/src/users/users.controller.js +147 -0
- package/dist/src/users/users.model.js +50 -0
- package/dist/src/users/users.route.js +137 -0
- package/dist/src/users/users.schemas.js +69 -0
- package/dist/src/users/users.service.js +295 -0
- package/dist/src/users/users.types.js +1 -0
- package/dist/src/users/users.validation.js +144 -0
- package/dist/src/utils/ApiError.js +16 -0
- package/dist/src/utils/buildRouterAndDocs.js +72 -0
- package/dist/src/utils/catchAsync.js +4 -0
- package/dist/src/utils/comparePapers.service.js +32 -0
- package/dist/src/utils/deviceUtils.js +63 -0
- package/dist/src/utils/filterOptions.js +24 -0
- package/dist/src/utils/medicationName.js +10 -0
- package/dist/src/utils/pick.js +16 -0
- package/dist/src/utils/registerOpenApi.js +67 -0
- package/dist/src/utils/urlUtils.js +15 -0
- package/dist/src/utils/userName.js +22 -0
- package/dist/src/utils/zValidations.js +143 -0
- package/dist/src/validations/auth.validation.cjs +53 -0
- package/dist/src/validations/custom.validation.js +19 -0
- package/dist/src/validations/index.cjs +3 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +97 -80
- package/scripts/release-and-sync-paperless.mjs +137 -0
- package/src/accounts/accounts.controller.ts +1 -0
- package/src/accounts/accounts.service.ts +1 -0
- package/src/accounts/accounts.validation.ts +8 -5
- package/src/accounts/auth0.service.ts +55 -28
- package/src/config/config.ts +6 -0
- package/src/config/logger.ts +15 -9
- package/src/devices/devices.controller.ts +7 -1
- package/src/devices/devices.model.ts +4 -1
- package/src/devices/devices.schemas.ts +11 -9
- package/src/devices/devices.service.ts +1 -0
- package/src/devices/devices.types.ts +1 -0
- package/src/devices/devices.validation.ts +93 -32
- package/src/devicesNotifications/devicesNotifications.controller.ts +57 -28
- package/src/devicesNotifications/devicesNotifications.model.ts +20 -12
- package/src/devicesNotifications/devicesNotifications.service.ts +35 -17
- package/src/files/upload.service.ts +52 -28
- package/src/i18n/i18n.ts +1 -1
- package/src/i18n/types.ts +1 -0
- package/src/index.ts +47 -0
- package/src/iotdevice/iotdevice.controller.ts +1 -0
- package/src/iotdevice/iotdevice.model.ts +6 -3
- package/src/iotdevice/iotdevice.route.ts +85 -76
- package/src/iotdevice/iotdevice.service.ts +4 -3
- package/src/iotdevice/iotdevice.types.ts +6 -0
- package/src/middlewares/auth.ts +2 -8
- package/src/middlewares/error.ts +26 -12
- package/src/middlewares/mongooseValidations/ensureSameOrganization.ts +4 -3
- package/src/middlewares/validateAi.ts +17 -9
- package/src/middlewares/validateDevice.ts +1 -0
- package/src/middlewares/validateDeviceUserOrganization.ts +1 -0
- package/src/middlewares/validateOrganization.ts +1 -1
- package/src/middlewares/validateQuerySearchUserAndOrganization.ts +1 -0
- package/src/middlewares/validateTokens.ts +2 -1
- package/src/middlewares/validateUser.ts +1 -0
- package/src/middlewares/validateZod.ts +5 -5
- package/src/models/plugins/index.ts +5 -4
- package/src/models/plugins/paginate.plugin.ts +26 -16
- package/src/models/plugins/paginateNew.plugin.ts +33 -21
- package/src/models/plugins/simplePopulate.ts +8 -3
- package/src/models/plugins/toJSON.plugin.ts +12 -5
- package/src/organizations/organizations.controller.ts +1 -2
- package/src/organizations/organizations.model.ts +4 -4
- package/src/organizations/organizations.route.ts +1 -1
- package/src/organizations/organizations.service.ts +15 -6
- package/src/organizations/organizations.validation.ts +1 -1
- package/src/pdf/pdf.controller.ts +18 -1
- package/src/pdf/pdf.service.ts +25 -16
- package/src/tokens/tokens.controller.ts +6 -8
- package/src/tokens/tokens.model.ts +3 -1
- package/src/tokens/tokens.service.ts +3 -2
- package/src/types/express.d.ts +17 -0
- package/src/types/mongoose.d.ts +22 -0
- package/src/users/users.controller.ts +8 -9
- package/src/users/users.model.ts +6 -5
- package/src/users/users.route.ts +0 -1
- package/src/users/users.service.ts +16 -0
- package/src/users/users.types.ts +1 -0
- package/src/users/users.validation.ts +6 -2
- package/src/utils/ApiError.ts +8 -1
- package/src/utils/buildRouterAndDocs.ts +57 -22
- package/src/utils/catchAsync.ts +27 -3
- package/src/utils/medicationName.ts +5 -4
- package/src/utils/pick.ts +5 -1
- package/src/utils/registerOpenApi.ts +75 -24
- package/src/utils/userName.ts +1 -0
- package/src/utils/zValidations.ts +98 -27
- package/tsconfig.json +13 -4
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
import httpStatus from "http-status";
|
|
2
3
|
import mongoose from "mongoose";
|
|
3
4
|
import multer from "multer";
|
|
@@ -247,9 +248,14 @@ const uploadSingleImage = catchAsync(
|
|
|
247
248
|
async (req: Request, res: Response): Promise<void> => {
|
|
248
249
|
const device = await devicesService.getById(req.params.deviceId);
|
|
249
250
|
|
|
251
|
+
const files = req.files as Array<{ buffer: Buffer }> | undefined;
|
|
252
|
+
if (!files || files.length === 0) {
|
|
253
|
+
throw new ApiError(httpStatus.BAD_REQUEST, "No image file uploaded");
|
|
254
|
+
}
|
|
255
|
+
|
|
250
256
|
const iotUpload = await iotDevicesService.uploadSingleImage({
|
|
251
257
|
deviceName: device.deviceId,
|
|
252
|
-
buffer:
|
|
258
|
+
buffer: files[0].buffer,
|
|
253
259
|
deviceId: req.params.deviceId,
|
|
254
260
|
uuid: req.body.uuid,
|
|
255
261
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
import mongoose, { Schema, Document, Model, Types } from "mongoose";
|
|
2
3
|
import { toJSON, paginate } from "../models/plugins/index.js";
|
|
3
4
|
import { ensureSameOrganization } from "../middlewares/mongooseValidations/ensureSameOrganization.js";
|
|
@@ -121,6 +122,8 @@ addIotDevice(deviceSchema);
|
|
|
121
122
|
deviceSchema.plugin((schema: Schema) => toJSON(schema, true));
|
|
122
123
|
deviceSchema.plugin(paginate);
|
|
123
124
|
|
|
124
|
-
const Devices: Model<IDevice> =
|
|
125
|
+
const Devices: Model<IDevice> =
|
|
126
|
+
(mongoose.models.Device as Model<IDevice>) ||
|
|
127
|
+
mongoose.model<IDevice>("Device", deviceSchema);
|
|
125
128
|
|
|
126
129
|
export default Devices;
|
|
@@ -17,14 +17,14 @@ export const eventResponseSchema = z.object({
|
|
|
17
17
|
id: z.string(),
|
|
18
18
|
deviceId: z.string(),
|
|
19
19
|
type: z.string(),
|
|
20
|
-
payload: z.record(z.any()),
|
|
20
|
+
payload: z.record(z.string(), z.any()),
|
|
21
21
|
timestamp: z.string(), // ISO timestamp
|
|
22
22
|
// ...other event fields...
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
export const genericResponseSchema = z
|
|
26
|
-
|
|
27
|
-
});
|
|
25
|
+
export const genericResponseSchema = z
|
|
26
|
+
.record(z.string(), z.any())
|
|
27
|
+
.openapi({ description: "Generic response payload" });
|
|
28
28
|
|
|
29
29
|
export const imageResponseSchema = z.object({
|
|
30
30
|
uuid: z.string(),
|
|
@@ -64,11 +64,13 @@ export const subscriptionResponseSchema = z.object({
|
|
|
64
64
|
// ...other subscription fields...
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
export const uploadResponseSchema = z
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
export const uploadResponseSchema = z
|
|
68
|
+
.object({
|
|
69
|
+
key: z.string().optional(),
|
|
70
|
+
similarityPercentage: z.number().nullable().optional(),
|
|
71
|
+
skippedUpload: z.boolean().optional(),
|
|
72
|
+
})
|
|
73
|
+
.openapi({ description: "Upload response payload" });
|
|
72
74
|
|
|
73
75
|
export const pingDeviceSchema = {
|
|
74
76
|
params: z.object({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Device = any;
|
|
@@ -4,12 +4,12 @@ import { objectId } from "../validations/custom.validation.js";
|
|
|
4
4
|
import {
|
|
5
5
|
zGet,
|
|
6
6
|
zObjectId,
|
|
7
|
+
zObjectIdFor,
|
|
7
8
|
zPatchBody,
|
|
8
9
|
zUpdate,
|
|
9
10
|
zDelete,
|
|
10
11
|
zPagination,
|
|
11
12
|
} from "../utils/zValidations.js";
|
|
12
|
-
import { enable } from "agenda/dist/job/enable";
|
|
13
13
|
|
|
14
14
|
extendZodWithOpenApi(z);
|
|
15
15
|
|
|
@@ -19,7 +19,7 @@ export const createEntrySchema = {
|
|
|
19
19
|
name: z.string().optional(),
|
|
20
20
|
deviceId: z.string().optional(),
|
|
21
21
|
kind: z.string().optional(),
|
|
22
|
-
meta: z.record(z.any()).optional(),
|
|
22
|
+
meta: z.record(z.string(), z.any()).optional(),
|
|
23
23
|
organization: zObjectId,
|
|
24
24
|
patient: zObjectId.optional(),
|
|
25
25
|
paper: zObjectId.optional().nullable(),
|
|
@@ -51,14 +51,14 @@ export const updateEntrySchema = {
|
|
|
51
51
|
organization: zObjectId.optional(),
|
|
52
52
|
patient: zObjectId.optional().nullable(),
|
|
53
53
|
paper: zObjectId.optional().nullable(),
|
|
54
|
-
meta: z.record(z.any()).optional(),
|
|
55
|
-
iotDevice: z.record(z.any()).optional(),
|
|
54
|
+
meta: z.record(z.string(), z.any()).optional(),
|
|
55
|
+
iotDevice: z.record(z.string(), z.any()).optional(),
|
|
56
56
|
shadow: z.union([z.string(), z.number()]).optional(),
|
|
57
57
|
alarmEnable: z.number().int().optional(),
|
|
58
58
|
takeOffsetTime: z.number().int().optional(),
|
|
59
59
|
updatedAt: z.string().datetime().optional(),
|
|
60
60
|
createdAt: z.string().datetime().optional(),
|
|
61
|
-
payment: z.record(z.any()).optional(),
|
|
61
|
+
payment: z.record(z.string(), z.any()).optional(),
|
|
62
62
|
lut: z.string().optional(),
|
|
63
63
|
sleepTime: z.string().optional(),
|
|
64
64
|
clearScreen: z.boolean().optional(),
|
|
@@ -66,6 +66,11 @@ export const updateEntrySchema = {
|
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
export const getEventsSchema = {
|
|
69
|
+
params: z.object({
|
|
70
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
71
|
+
description: "Device ObjectId",
|
|
72
|
+
}),
|
|
73
|
+
}),
|
|
69
74
|
query: z
|
|
70
75
|
.object({
|
|
71
76
|
DateStart: z.string(),
|
|
@@ -92,14 +97,29 @@ export const createCustomerPortalSessionSchema = {
|
|
|
92
97
|
export const createDeviceSchema = {
|
|
93
98
|
body: z.object({
|
|
94
99
|
kind: z.string().optional(),
|
|
95
|
-
patient:
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
patient: zObjectIdFor("patient")
|
|
101
|
+
.optional()
|
|
102
|
+
.nullable()
|
|
103
|
+
.openapi({ description: "Patient ObjectId", example: null }),
|
|
104
|
+
paper: zObjectIdFor("paper")
|
|
105
|
+
.optional()
|
|
106
|
+
.nullable()
|
|
107
|
+
.openapi({ description: "Paper ObjectId", example: null }),
|
|
108
|
+
organization: zObjectIdFor("organization").openapi({
|
|
109
|
+
description: "Organization ObjectId",
|
|
110
|
+
}),
|
|
98
111
|
}),
|
|
99
112
|
};
|
|
100
113
|
export const deleteDeviceSchema = zDelete("deviceId");
|
|
101
114
|
export const getDeviceSchema = zGet("deviceId");
|
|
102
|
-
export const getImageSchema =
|
|
115
|
+
export const getImageSchema = {
|
|
116
|
+
params: z.object({
|
|
117
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
118
|
+
description: "Device ObjectId",
|
|
119
|
+
}),
|
|
120
|
+
uuid: z.string().openapi({ description: "Image UUID" }),
|
|
121
|
+
}),
|
|
122
|
+
};
|
|
103
123
|
export const ledLightSchema = {
|
|
104
124
|
params: z.object({
|
|
105
125
|
deviceId: zObjectId.openapi({ description: "Device ObjectId" }),
|
|
@@ -149,7 +169,9 @@ export const ledLightSchema = {
|
|
|
149
169
|
};
|
|
150
170
|
export const pingDeviceSchema = {
|
|
151
171
|
params: z.object({
|
|
152
|
-
deviceId:
|
|
172
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
173
|
+
description: "Device ObjectId",
|
|
174
|
+
}),
|
|
153
175
|
}),
|
|
154
176
|
query: z.object({
|
|
155
177
|
dataResponse: z
|
|
@@ -160,34 +182,49 @@ export const pingDeviceSchema = {
|
|
|
160
182
|
|
|
161
183
|
export const resetDeviceSchema = {
|
|
162
184
|
params: z.object({
|
|
163
|
-
deviceId:
|
|
185
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
186
|
+
description: "Device ObjectId",
|
|
187
|
+
}),
|
|
164
188
|
}),
|
|
165
189
|
};
|
|
166
190
|
|
|
167
191
|
export const rebootDeviceSchema = zGet("deviceId");
|
|
168
192
|
|
|
169
193
|
export const registerDeviceSchema = {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
194
|
+
body: z
|
|
195
|
+
.object({
|
|
196
|
+
enable: z.boolean(),
|
|
197
|
+
organization: zObjectIdFor("organization"),
|
|
198
|
+
patient: zObjectId.optional().nullable(),
|
|
199
|
+
paper: zObjectId.optional().nullable(),
|
|
200
|
+
})
|
|
201
|
+
.openapi({
|
|
202
|
+
example: {
|
|
203
|
+
enable: true,
|
|
204
|
+
organization:
|
|
205
|
+
process.env.SCHEMA_EXAMPLE_ORGANIZATION_ID || "682fd0d7d4a6325d9d45b86d",
|
|
206
|
+
patient: process.env.SCHEMA_EXAMPLE_USER_ID || "682fd0d7d4a6325d9d45b86d",
|
|
207
|
+
paper: process.env.SCHEMA_EXAMPLE_PAPER_ID || null,
|
|
208
|
+
},
|
|
209
|
+
}),
|
|
177
210
|
params: z.object({
|
|
178
|
-
deviceId:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
211
|
+
deviceId: (
|
|
212
|
+
process.env.SCHEMA_STRICT_EXAMPLES === "true" &&
|
|
213
|
+
process.env.SCHEMA_EXAMPLE_DEVICE_SERIAL
|
|
214
|
+
? z.literal(process.env.SCHEMA_EXAMPLE_DEVICE_SERIAL)
|
|
215
|
+
: z.string()
|
|
216
|
+
).openapi({
|
|
217
|
+
description: "Device serial",
|
|
218
|
+
example: process.env.SCHEMA_EXAMPLE_DEVICE_SERIAL || "DEVICE-EXAMPLE",
|
|
219
|
+
}),
|
|
183
220
|
}),
|
|
184
221
|
};
|
|
185
222
|
|
|
186
223
|
export const queryDevicesSchema = {
|
|
187
224
|
...zPagination,
|
|
188
225
|
query: zPagination.query.extend({
|
|
189
|
-
patient:
|
|
190
|
-
organization:
|
|
226
|
+
patient: zObjectIdFor("patient").optional(),
|
|
227
|
+
organization: zObjectIdFor("organization"),
|
|
191
228
|
}),
|
|
192
229
|
};
|
|
193
230
|
export const subscriptionSchema = {
|
|
@@ -199,8 +236,8 @@ export const uploadSingleImageFromWebsiteSchema = {};
|
|
|
199
236
|
export const updateDeviceSchema = {
|
|
200
237
|
...zUpdate("deviceId"),
|
|
201
238
|
body: zPatchBody({
|
|
202
|
-
intake: z.record(z.any()).optional(),
|
|
203
|
-
meta: z.record(z.any()).optional(),
|
|
239
|
+
intake: z.record(z.string(), z.any()).optional(),
|
|
240
|
+
meta: z.record(z.string(), z.any()).optional(),
|
|
204
241
|
organization: zObjectId.optional(),
|
|
205
242
|
patient: zObjectId.optional().nullable(),
|
|
206
243
|
paper: zObjectId.optional().nullable(),
|
|
@@ -208,14 +245,38 @@ export const updateDeviceSchema = {
|
|
|
208
245
|
deviceId: z.string().optional(),
|
|
209
246
|
alarmEnable: z.number().int().optional(),
|
|
210
247
|
takeOffsetTime: z.number().int().optional(),
|
|
211
|
-
iotDevice: z.record(z.any()).optional(),
|
|
212
|
-
payment: z.record(z.any()).optional(),
|
|
248
|
+
iotDevice: z.record(z.string(), z.any()).optional(),
|
|
249
|
+
payment: z.record(z.string(), z.any()).optional(),
|
|
213
250
|
}),
|
|
214
251
|
};
|
|
215
252
|
|
|
216
|
-
export const updateSingleImageMetaSchema = {
|
|
253
|
+
export const updateSingleImageMetaSchema = {
|
|
254
|
+
params: z.object({
|
|
255
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
256
|
+
description: "Device ObjectId",
|
|
257
|
+
}),
|
|
258
|
+
}),
|
|
259
|
+
body: z
|
|
260
|
+
.object({
|
|
261
|
+
meta: z.record(z.string(), z.any()).optional(),
|
|
262
|
+
})
|
|
263
|
+
.openapi({ description: "Image metadata updates" }),
|
|
264
|
+
};
|
|
217
265
|
export const uploadSingleImageSchema = {
|
|
218
|
-
|
|
219
|
-
|
|
266
|
+
params: z.object({
|
|
267
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
268
|
+
description: "Device ObjectId",
|
|
269
|
+
}),
|
|
220
270
|
}),
|
|
271
|
+
body: z
|
|
272
|
+
.object({
|
|
273
|
+
uuid: z
|
|
274
|
+
.string()
|
|
275
|
+
.optional()
|
|
276
|
+
.openapi({ description: "Optional image UUID", example: "mock-uuid" }),
|
|
277
|
+
})
|
|
278
|
+
.openapi({
|
|
279
|
+
description: "Multipart body is mocked during tests.",
|
|
280
|
+
example: { uuid: "mock-uuid" },
|
|
281
|
+
}),
|
|
221
282
|
};
|
|
@@ -1,56 +1,85 @@
|
|
|
1
|
-
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import httpStatus from "http-status";
|
|
2
3
|
|
|
3
|
-
import pick from
|
|
4
|
-
import ApiError from
|
|
5
|
-
import catchAsync from
|
|
6
|
-
import devicesNotificationsService from
|
|
4
|
+
import pick from "../../src/utils/pick";
|
|
5
|
+
import ApiError from "../../src/utils/ApiError";
|
|
6
|
+
import catchAsync from "../../src/utils/catchAsync";
|
|
7
|
+
import devicesNotificationsService from "./devicesNotifications.service";
|
|
7
8
|
|
|
8
|
-
import type { Request, Response } from
|
|
9
|
+
import type { Request, Response } from "express";
|
|
9
10
|
|
|
10
11
|
export const createEntry = catchAsync(async (req: Request, res: Response) => {
|
|
11
|
-
const user = await devicesNotificationsService.createNotification({
|
|
12
|
+
const user = await devicesNotificationsService.createNotification({
|
|
13
|
+
user: res.req.auth.sub,
|
|
14
|
+
token: req.body.token,
|
|
15
|
+
});
|
|
12
16
|
res.status(httpStatus.CREATED).send(user);
|
|
13
17
|
});
|
|
14
18
|
|
|
15
|
-
export const setDeviceToken = catchAsync(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
export const setDeviceToken = catchAsync(
|
|
20
|
+
async (req: Request, res: Response) => {
|
|
21
|
+
const user = await devicesNotificationsService.setDeviceToken({
|
|
22
|
+
user: res.req.auth.sub,
|
|
23
|
+
body: req.body,
|
|
24
|
+
});
|
|
25
|
+
res.status(httpStatus.CREATED).send(user);
|
|
26
|
+
},
|
|
27
|
+
);
|
|
19
28
|
|
|
20
|
-
export const removeDeviceToken = catchAsync(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
export const removeDeviceToken = catchAsync(
|
|
30
|
+
async (req: Request, res: Response) => {
|
|
31
|
+
const user = await devicesNotificationsService.removeDeviceToken({
|
|
32
|
+
user: res.req.auth.sub,
|
|
33
|
+
body: req.body,
|
|
34
|
+
});
|
|
35
|
+
res.status(httpStatus.CREATED).send(user);
|
|
36
|
+
},
|
|
37
|
+
);
|
|
24
38
|
|
|
25
39
|
export const getEntries = catchAsync(async (req: Request, res: Response) => {
|
|
26
|
-
const filter = pick(req.query, [
|
|
27
|
-
const options = pick(req.query, [
|
|
28
|
-
const result = await devicesNotificationsService.queryNotifications(
|
|
40
|
+
const filter = pick(req.query, ["name", "role"]);
|
|
41
|
+
const options = pick(req.query, ["sortBy", "limit", "page"]);
|
|
42
|
+
const result = await devicesNotificationsService.queryNotifications(
|
|
43
|
+
filter,
|
|
44
|
+
options,
|
|
45
|
+
);
|
|
29
46
|
res.send(result);
|
|
30
47
|
});
|
|
31
48
|
|
|
32
|
-
export const queryNotificationsByUser = catchAsync(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
export const queryNotificationsByUser = catchAsync(
|
|
50
|
+
async (req: Request, res: Response) => {
|
|
51
|
+
const filter = pick({ user: res.req.auth.sub }, ["user"]);
|
|
52
|
+
const options = pick(req.query, ["sortBy", "limit", "page"]);
|
|
53
|
+
const result = await devicesNotificationsService.queryNotificationsByUser(
|
|
54
|
+
filter,
|
|
55
|
+
options,
|
|
56
|
+
);
|
|
57
|
+
res.send(result);
|
|
58
|
+
},
|
|
59
|
+
);
|
|
38
60
|
|
|
39
61
|
export const getEntry = catchAsync(async (req: Request, res: Response) => {
|
|
40
|
-
const notification = await devicesNotificationsService.getById(
|
|
62
|
+
const notification = await devicesNotificationsService.getById(
|
|
63
|
+
req.params.notificationId,
|
|
64
|
+
);
|
|
41
65
|
if (!notification) {
|
|
42
|
-
throw new ApiError(httpStatus.NOT_FOUND,
|
|
66
|
+
throw new ApiError(httpStatus.NOT_FOUND, "Notification not found");
|
|
43
67
|
}
|
|
44
68
|
res.send(notification);
|
|
45
69
|
});
|
|
46
70
|
|
|
47
71
|
export const updateEntry = catchAsync(async (req: Request, res: Response) => {
|
|
48
|
-
const user = await devicesNotificationsService.updateById(
|
|
72
|
+
const user = await devicesNotificationsService.updateById(
|
|
73
|
+
req.params.notificationId,
|
|
74
|
+
req.body,
|
|
75
|
+
);
|
|
49
76
|
res.send(user);
|
|
50
77
|
});
|
|
51
78
|
|
|
52
79
|
export const deleteEntry = catchAsync(async (req: Request, res: Response) => {
|
|
53
|
-
const entry = await devicesNotificationsService.deleteById(
|
|
80
|
+
const entry = await devicesNotificationsService.deleteById(
|
|
81
|
+
req.params.notificationId,
|
|
82
|
+
);
|
|
54
83
|
res.send(entry);
|
|
55
84
|
});
|
|
56
85
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import mongoose, { Schema, Document, Model } from "mongoose";
|
|
3
|
+
import type { PaginateModel } from "mongoose";
|
|
4
|
+
import { toJSON, paginate } from "../../src/models/plugins/index";
|
|
4
5
|
|
|
5
6
|
interface Token {
|
|
6
7
|
token: string;
|
|
@@ -11,7 +12,7 @@ interface Token {
|
|
|
11
12
|
|
|
12
13
|
interface Settings {
|
|
13
14
|
intake: { email: boolean; push: boolean };
|
|
14
|
-
|
|
15
|
+
"intake-reminder": { email: boolean; push: boolean };
|
|
15
16
|
battery: { email: boolean; push: boolean };
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -24,7 +25,8 @@ export interface DeviceNotificationDocument extends Document {
|
|
|
24
25
|
updatedAt: Date;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
export type DeviceNotificationModel = Model<DeviceNotificationDocument> &
|
|
28
|
+
export type DeviceNotificationModel = Model<DeviceNotificationDocument> &
|
|
29
|
+
PaginateModel<DeviceNotificationDocument>;
|
|
28
30
|
|
|
29
31
|
const deviceNotificationSchema = new Schema<DeviceNotificationDocument>(
|
|
30
32
|
{
|
|
@@ -42,12 +44,18 @@ const deviceNotificationSchema = new Schema<DeviceNotificationDocument>(
|
|
|
42
44
|
],
|
|
43
45
|
bounceEmail: { type: String },
|
|
44
46
|
settings: {
|
|
45
|
-
intake: {
|
|
46
|
-
|
|
47
|
+
intake: {
|
|
48
|
+
email: { type: Boolean, default: true },
|
|
49
|
+
push: { type: Boolean, default: true },
|
|
50
|
+
},
|
|
51
|
+
"intake-reminder": {
|
|
52
|
+
email: { type: Boolean, default: true },
|
|
53
|
+
push: { type: Boolean, default: true },
|
|
54
|
+
},
|
|
55
|
+
battery: {
|
|
47
56
|
email: { type: Boolean, default: true },
|
|
48
57
|
push: { type: Boolean, default: true },
|
|
49
58
|
},
|
|
50
|
-
battery: { email: { type: Boolean, default: true }, push: { type: Boolean, default: true } },
|
|
51
59
|
},
|
|
52
60
|
},
|
|
53
61
|
{
|
|
@@ -59,9 +67,9 @@ const deviceNotificationSchema = new Schema<DeviceNotificationDocument>(
|
|
|
59
67
|
deviceNotificationSchema.plugin(toJSON);
|
|
60
68
|
deviceNotificationSchema.plugin(paginate);
|
|
61
69
|
|
|
62
|
-
const DeviceNotifications = mongoose.model<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
);
|
|
70
|
+
const DeviceNotifications = mongoose.model<
|
|
71
|
+
DeviceNotificationDocument,
|
|
72
|
+
DeviceNotificationModel
|
|
73
|
+
>("DeviceNotification", deviceNotificationSchema);
|
|
66
74
|
|
|
67
75
|
export default DeviceNotifications;
|
|
@@ -1,20 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
8
|
-
import
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import httpStatus from "http-status";
|
|
3
|
+
import {
|
|
4
|
+
SESv2Client,
|
|
5
|
+
ListSuppressedDestinationsCommand,
|
|
6
|
+
} from "@aws-sdk/client-sesv2";
|
|
7
|
+
import DeviceNotification from "./devicesNotifications.model";
|
|
8
|
+
import ApiError from "../../src/utils/ApiError";
|
|
9
|
+
import { auth0 } from "../accounts/auth0.service";
|
|
10
|
+
import type { Notification } from "./devicesNotifications.model";
|
|
11
|
+
import type { FilterQuery, PaginateOptions, QueryResult } from "mongoose";
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
14
|
* Create a user
|
|
12
15
|
* @param {Object} userBody
|
|
13
16
|
* @returns {Promise<Notification>}
|
|
14
17
|
*/
|
|
15
|
-
export const createNotificationUser = async ({
|
|
18
|
+
export const createNotificationUser = async ({
|
|
19
|
+
user,
|
|
20
|
+
}: {
|
|
21
|
+
user: string;
|
|
22
|
+
}): Promise<Notification> => {
|
|
16
23
|
const result = await DeviceNotification.findOne({ user });
|
|
17
|
-
if (result)
|
|
24
|
+
if (result)
|
|
25
|
+
throw new ApiError(
|
|
26
|
+
httpStatus.NOT_FOUND,
|
|
27
|
+
"DevicesNotifications user existing",
|
|
28
|
+
);
|
|
18
29
|
const devicesNotification = await DeviceNotification.create({ user });
|
|
19
30
|
return devicesNotification;
|
|
20
31
|
};
|
|
@@ -37,7 +48,9 @@ export const setDeviceToken = async ({
|
|
|
37
48
|
result = await createNotificationUser({ user });
|
|
38
49
|
}
|
|
39
50
|
|
|
40
|
-
result.tokens = [
|
|
51
|
+
result.tokens = [
|
|
52
|
+
...new Map(result.tokens.map((m) => [m.deviceId, m])).values(),
|
|
53
|
+
];
|
|
41
54
|
|
|
42
55
|
if (result.tokens.find((t) => t.token === token)) return result;
|
|
43
56
|
|
|
@@ -120,7 +133,7 @@ const getByUser = async (user) => {
|
|
|
120
133
|
const updateById = async (notificationId, updateBody) => {
|
|
121
134
|
const user = await getById(notificationId);
|
|
122
135
|
if (!user) {
|
|
123
|
-
throw new ApiError(httpStatus.NOT_FOUND,
|
|
136
|
+
throw new ApiError(httpStatus.NOT_FOUND, "Notification not found");
|
|
124
137
|
}
|
|
125
138
|
Object.assign(user, updateBody);
|
|
126
139
|
await user.save();
|
|
@@ -148,7 +161,7 @@ const updateByUserId = async (user, updateBody) => {
|
|
|
148
161
|
const deleteById = async (userId) => {
|
|
149
162
|
const user = await getById(userId);
|
|
150
163
|
if (!user) {
|
|
151
|
-
throw new ApiError(httpStatus.NOT_FOUND,
|
|
164
|
+
throw new ApiError(httpStatus.NOT_FOUND, "Notification not found");
|
|
152
165
|
}
|
|
153
166
|
await user.deleteOne();
|
|
154
167
|
return user;
|
|
@@ -164,17 +177,22 @@ const cleanup = async () => {
|
|
|
164
177
|
|
|
165
178
|
const auth0Users = await auth0.getUsers();
|
|
166
179
|
|
|
167
|
-
const sesV2 = new SESv2Client({ region:
|
|
180
|
+
const sesV2 = new SESv2Client({ region: "eu-central-1" });
|
|
168
181
|
const input = {
|
|
169
|
-
Reasons: [
|
|
182
|
+
Reasons: ["BOUNCE" || "COMPLAINT"],
|
|
170
183
|
PageSize: 300,
|
|
171
184
|
};
|
|
172
185
|
|
|
173
186
|
const command = new ListSuppressedDestinationsCommand(input);
|
|
174
187
|
const suppressedDestinations = await sesV2.send(command);
|
|
175
188
|
|
|
176
|
-
const suppressedEmailList =
|
|
177
|
-
|
|
189
|
+
const suppressedEmailList =
|
|
190
|
+
suppressedDestinations.SuppressedDestinationSummaries.map(
|
|
191
|
+
(a) => a.EmailAddress,
|
|
192
|
+
);
|
|
193
|
+
const suppressedEmailAuth0Users = auth0Users.filter((a) =>
|
|
194
|
+
suppressedEmailList.includes(a.email),
|
|
195
|
+
);
|
|
178
196
|
await Promise.all(
|
|
179
197
|
suppressedEmailAuth0Users.map(async (a) => {
|
|
180
198
|
updateByUserId(a.user_id, { bounceEmail: a.email });
|