@internetderdinge/api 1.229.10 → 1.229.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/src/accounts/accounts.controller.js +0 -1
  2. package/dist/src/config/config.js +0 -6
  3. package/dist/src/devices/devices.controller.js +1 -31
  4. package/dist/src/devices/devices.route.js +2 -33
  5. package/dist/src/devices/devices.service.js +0 -21
  6. package/dist/src/devices/devices.validation.js +3 -40
  7. package/dist/src/email/email.service.js +46 -39
  8. package/dist/src/index.js +18 -1
  9. package/dist/src/iotdevice/iotdevice.route.js +1 -1
  10. package/dist/src/iotdevice/iotdevice.service.js +23 -113
  11. package/dist/src/middlewares/auth.js +49 -2
  12. package/dist/src/middlewares/validateCurrentUser.js +2 -2
  13. package/dist/src/models/plugins/simplePopulate.js +1 -1
  14. package/dist/src/pdf/pdf.service.js +18 -19
  15. package/dist/src/users/users.schemas.js +2 -46
  16. package/dist/src/users/users.service.js +1 -0
  17. package/package.json +3 -2
  18. package/src/accounts/accounts.controller.ts +0 -1
  19. package/src/config/config.ts +0 -6
  20. package/src/devices/devices.controller.ts +0 -53
  21. package/src/devices/devices.route.ts +0 -39
  22. package/src/devices/devices.service.ts +0 -38
  23. package/src/devices/devices.validation.ts +9 -47
  24. package/src/email/email.service.ts +70 -43
  25. package/src/index.ts +18 -1
  26. package/src/iotdevice/iotdevice.route.ts +1 -1
  27. package/src/iotdevice/iotdevice.service.ts +34 -167
  28. package/src/middlewares/auth.ts +75 -2
  29. package/src/middlewares/validateCurrentUser.ts +2 -2
  30. package/src/models/plugins/simplePopulate.ts +1 -1
  31. package/src/pdf/pdf.service.ts +36 -31
  32. package/src/users/users.schemas.ts +3 -50
  33. package/src/users/users.service.ts +1 -0
@@ -76,7 +76,6 @@ const deleteCurrent = catchAsync(async (req, res) => {
76
76
  });
77
77
  const current = catchAsync(async (req, res) => {
78
78
  const user = await accountsService.getAccountById(req.auth.sub);
79
- console.log("user", user);
80
79
  res.send(user);
81
80
  });
82
81
  const mfaEnroll = catchAsync(async (req, res) => {
@@ -1,12 +1,6 @@
1
1
  import dotenv from "dotenv";
2
2
  // Load env from the current working directory
3
3
  dotenv.config();
4
- console.log("Current working directory:", process.cwd());
5
- console.log("Loaded environment variables:", {
6
- NODE_ENV: process.env.NODE_ENV,
7
- PORT: process.env.PORT,
8
- MONGODB_URL: process.env.MONGODB_URL,
9
- });
10
4
  import Joi from "joi";
11
5
  const envVarsSchema = Joi.object()
12
6
  .keys({
@@ -89,13 +89,6 @@ const getEntry = catchAsync(async (req, res) => {
89
89
  }
90
90
  res.send(device);
91
91
  });
92
- const getImageById = catchAsync(async (req, res) => {
93
- const device = await devicesService.getImageById(req.params.deviceId, req.params.uuid);
94
- if (!device) {
95
- throw new ApiError(httpStatus.NOT_FOUND, "Device not found");
96
- }
97
- res.send(device);
98
- });
99
92
  const updateEntry = catchAsync(async (req, res) => {
100
93
  // TODO: Remove in newer versions
101
94
  const { updatedAt, createdAt, payment, iotDevice, ...newBody } = req.body;
@@ -146,27 +139,4 @@ const rebootDevice = catchAsync(async (req, res) => {
146
139
  const reboot = await iotDevicesService.rebootDevice(device.deviceId);
147
140
  res.send({ device, reboot });
148
141
  });
149
- const updateSingleImageMeta = catchAsync(async (req, res) => {
150
- const device = await devicesService.getById(req.params.deviceId);
151
- const { uuid, ...body } = req.body;
152
- const deviceUpdate = await devicesService.updateById(req.params.deviceId, {
153
- meta: { ...device.meta, file: uuid },
154
- });
155
- const deviceMeta = await devicesService.updateSingleImageMeta(device.deviceId, body);
156
- res.send({ deviceMeta, deviceUpdate });
157
- });
158
- const uploadSingleImage = catchAsync(async (req, res) => {
159
- const device = await devicesService.getById(req.params.deviceId);
160
- const files = req.files;
161
- if (!files || files.length === 0) {
162
- throw new ApiError(httpStatus.BAD_REQUEST, "No image file uploaded");
163
- }
164
- const iotUpload = await iotDevicesService.uploadSingleImage({
165
- deviceName: device.deviceId,
166
- buffer: files[0].buffer,
167
- deviceId: req.params.deviceId,
168
- uuid: req.body.uuid,
169
- });
170
- res.send(iotUpload);
171
- });
172
- export { createEntry, getEntries, queryDevicesByUser, getEvents, getImageById, registerDevice, pingDevice, ledLight, rebootDevice, uploadSingleImage, updateSingleImageMeta, getEntry, updateEntry, deleteEntry, };
142
+ export { createEntry, getEntries, queryDevicesByUser, getEvents, registerDevice, pingDevice, ledLight, rebootDevice, getEntry, updateEntry, deleteEntry, };
@@ -1,5 +1,4 @@
1
1
  import { Router } from "express";
2
- import multer from "multer";
3
2
  import buildRouterAndDocs from "../utils/buildRouterAndDocs.js";
4
3
  import auth from "../middlewares/auth.js";
5
4
  import { validateBodyOrganization, } from "../middlewares/validateOrganization.js";
@@ -7,8 +6,8 @@ import { validateQuerySearchUserAndOrganization } from "../middlewares/validateQ
7
6
  import { validateDevice } from "../middlewares/validateDevice.js";
8
7
  import * as devicesController from "./devices.controller.js";
9
8
  import { resetDevice } from "./devices.controller.js";
10
- import { createDeviceSchema, queryDevicesSchema, getDeviceSchema, updateDeviceSchema, deleteDeviceSchema, getEventsSchema, pingDeviceSchema, registerDeviceSchema, ledLightSchema, rebootDeviceSchema, getImageSchema, updateSingleImageMetaSchema, uploadSingleImageSchema, resetDeviceSchema, } from "./devices.validation.js";
11
- import { deviceResponseSchema, devicesResponseSchema, eventResponseSchema, genericResponseSchema, imageResponseSchema, uploadResponseSchema, resetResponseSchema, } from "./devices.schemas.js";
9
+ import { createDeviceSchema, queryDevicesSchema, getDeviceSchema, updateDeviceSchema, deleteDeviceSchema, getEventsSchema, pingDeviceSchema, registerDeviceSchema, ledLightSchema, rebootDeviceSchema, resetDeviceSchema, } from "./devices.validation.js";
10
+ import { deviceResponseSchema, devicesResponseSchema, eventResponseSchema, genericResponseSchema, resetResponseSchema, } from "./devices.schemas.js";
12
11
  import { validateOrganizationDelete, validateOrganizationUpdate, } from "../middlewares/validateAction.js";
13
12
  export const devicesRouteSpecs = [
14
13
  {
@@ -130,36 +129,6 @@ export const devicesRouteSpecs = [
130
129
  summary: "Reboot a device",
131
130
  description: "Initiate a remote reboot of the specified device.",
132
131
  },
133
- {
134
- method: "get",
135
- path: "/image/:deviceId/:uuid",
136
- validate: [auth("getUsers"), validateDevice],
137
- requestSchema: getImageSchema,
138
- responseSchema: imageResponseSchema,
139
- handler: devicesController.getImageById,
140
- summary: "Fetch an image by UUID",
141
- description: "Download a previously uploaded image for the device by its UUID.",
142
- },
143
- {
144
- method: "post",
145
- path: "/updateSingleImageMeta/:deviceId",
146
- validate: [auth("getUsers"), validateDevice],
147
- requestSchema: updateSingleImageMetaSchema,
148
- responseSchema: uploadResponseSchema,
149
- handler: devicesController.updateSingleImageMeta,
150
- summary: "Update image metadata",
151
- description: "Modify metadata (e.g., title, tags) for an existing device image.",
152
- },
153
- {
154
- method: "post",
155
- path: "/uploadSingleImage/:deviceId",
156
- validate: [auth("getUsers"), multer().array("picture", 2), validateDevice],
157
- requestSchema: uploadSingleImageSchema,
158
- responseSchema: uploadResponseSchema,
159
- handler: devicesController.uploadSingleImage,
160
- summary: "Upload one or two images",
161
- description: "Upload up to two image files to the device for processing or storage.",
162
- },
163
132
  ];
164
133
  const router = Router();
165
134
  buildRouterAndDocs(router, devicesRouteSpecs, "/devices", ["Devices"]);
@@ -4,7 +4,6 @@ import Device from "./devices.model.js";
4
4
  import ApiError from "../utils/ApiError.js";
5
5
  import iotDevicesService from "../iotdevice/iotdevice.service.js";
6
6
  import { promisify } from "util";
7
- import { getSignedFileUrl } from "../files/upload.service";
8
7
  import { deviceByDeviceName, deviceKindHasFeature } from "../utils/deviceUtils";
9
8
  import * as usersService from "../users/users.service";
10
9
  const setTimeoutAsync = promisify(setTimeout);
@@ -63,11 +62,6 @@ export const populateDeviceStatus = async (e) => {
63
62
  const deviceStatus = await iotDevicesService.getDeviceStatus(e.deviceId, e.kind);
64
63
  return deviceStatus;
65
64
  };
66
- export const populateDeviceImage = async (deviceId, uuid) => {
67
- const fileName = `ePaperImages/${deviceId}+${uuid}.png`;
68
- const url = await getSignedFileUrl({ fileName });
69
- return url;
70
- };
71
65
  export const populateIotDevices = async (data) => {
72
66
  const nrfList = data.map((e) => e.deviceId);
73
67
  const iotDevices = await iotDevicesService.getDevice(nrfList);
@@ -93,10 +87,6 @@ export const getById = async (id) => {
93
87
  //if (!device) throw new ApiError(httpStatus.NOT_FOUND, `Device not found id: ${id}`);
94
88
  return device;
95
89
  };
96
- export const getImageById = async (id, uuid) => {
97
- const url = await populateDeviceImage(id, uuid);
98
- return { url };
99
- };
100
90
  export const getByIdWithIoT = async (id) => {
101
91
  const device = await getById(id);
102
92
  if (!device) {
@@ -148,15 +138,6 @@ export const updateById = async (userId, updateBody) => {
148
138
  deviceJSON.shadow = shadow;
149
139
  return deviceJSON;
150
140
  };
151
- export const updateSingleImageMeta = async (deviceId, shadowNew) => {
152
- const shadowBody = {
153
- state: {
154
- reported: shadowNew,
155
- },
156
- };
157
- const shadowResult = await iotDevicesService.shadowAlarmUpdate(deviceId, shadowBody, "settings");
158
- return shadowResult;
159
- };
160
141
  export const deleteById = async (userId, deleteFromIotApi = true) => {
161
142
  const device = await getById(userId);
162
143
  if (!device) {
@@ -182,12 +163,10 @@ export default {
182
163
  getByIdWithIoT,
183
164
  getDeviceByUserId,
184
165
  getDeviceByDeviceId,
185
- getImageById,
186
166
  updateById,
187
167
  updatePaymentById,
188
168
  deleteById,
189
169
  populateIotDevices,
190
- populateDeviceImage,
191
170
  populateDeviceStatus,
192
171
  queryDevicesByUser,
193
172
  registerDevice,
@@ -95,14 +95,6 @@ export const createDeviceSchema = {
95
95
  };
96
96
  export const deleteDeviceSchema = zDelete("deviceId");
97
97
  export const getDeviceSchema = zGet("deviceId");
98
- export const getImageSchema = {
99
- params: z.object({
100
- deviceId: zObjectIdFor("deviceId").openapi({
101
- description: "Device ObjectId",
102
- }),
103
- uuid: z.string().openapi({ description: "Image UUID" }),
104
- }),
105
- };
106
98
  export const ledLightSchema = {
107
99
  params: z.object({
108
100
  deviceId: zObjectId.openapi({ description: "Device ObjectId" }),
@@ -181,7 +173,8 @@ export const registerDeviceSchema = {
181
173
  .openapi({
182
174
  example: {
183
175
  enable: true,
184
- organization: process.env.SCHEMA_EXAMPLE_ORGANIZATION_ID || "682fd0d7d4a6325d9d45b86d",
176
+ organization: process.env.SCHEMA_EXAMPLE_ORGANIZATION_ID ||
177
+ "682fd0d7d4a6325d9d45b86d",
185
178
  patient: process.env.SCHEMA_EXAMPLE_USER_ID || "682fd0d7d4a6325d9d45b86d",
186
179
  paper: process.env.SCHEMA_EXAMPLE_PAPER_ID || null,
187
180
  },
@@ -200,7 +193,7 @@ export const queryDevicesSchema = {
200
193
  ...zPagination,
201
194
  query: zPagination.query.extend({
202
195
  patient: zObjectIdFor("patient").optional(),
203
- organization: zObjectIdFor("organization"),
196
+ organization: zObjectIdFor("organization").optional(),
204
197
  }),
205
198
  };
206
199
  export const subscriptionSchema = {
@@ -225,33 +218,3 @@ export const updateDeviceSchema = {
225
218
  payment: z.record(z.string(), z.any()).optional(),
226
219
  }),
227
220
  };
228
- export const updateSingleImageMetaSchema = {
229
- params: z.object({
230
- deviceId: zObjectIdFor("deviceId").openapi({
231
- description: "Device ObjectId",
232
- }),
233
- }),
234
- body: z
235
- .object({
236
- meta: z.record(z.string(), z.any()).optional(),
237
- })
238
- .openapi({ description: "Image metadata updates" }),
239
- };
240
- export const uploadSingleImageSchema = {
241
- params: z.object({
242
- deviceId: zObjectIdFor("deviceId").openapi({
243
- description: "Device ObjectId",
244
- }),
245
- }),
246
- body: z
247
- .object({
248
- uuid: z
249
- .string()
250
- .optional()
251
- .openapi({ description: "Optional image UUID", example: "mock-uuid" }),
252
- })
253
- .openapi({
254
- description: "Multipart body is mocked during tests.",
255
- example: { uuid: "mock-uuid" },
256
- }),
257
- };
@@ -1,10 +1,10 @@
1
- import AWS from 'aws-sdk';
2
- import config from '../../src/config/config';
3
- import i18n from '../../src/i18n/i18n';
1
+ import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2";
2
+ import config from "../../src/config/config";
3
+ import i18n from "../../src/i18n/i18n";
4
4
  function urlStartsWithHttp(url) {
5
- return url.startsWith('http');
5
+ return url.startsWith("http");
6
6
  }
7
- const button = ({ link, text, color = '#0076ff' }) => {
7
+ const button = ({ link, text, color = "#0076ff", }) => {
8
8
  return `
9
9
  <div><!--[if mso]>
10
10
  <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${link}" style="height:40px;v-text-anchor:middle;width:200px;" arcsize="15%" stroke="f" fillcolor="${color}">
@@ -19,7 +19,7 @@ const button = ({ link, text, color = '#0076ff' }) => {
19
19
  <![endif]--></div>
20
20
  `;
21
21
  };
22
- const actionButton = ({ link, text, color = '#0076ff' }) => {
22
+ const actionButton = ({ link, text, color = "#0076ff", }) => {
23
23
  return `<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
24
24
  <tr>
25
25
  <td align="center">
@@ -34,28 +34,32 @@ const actionButton = ({ link, text, color = '#0076ff' }) => {
34
34
  </tr>
35
35
  </table>`;
36
36
  };
37
- export const sendEmail = async ({ title = 'Kein Titel', body = 'Kein Inhalt', url = '', domain = 'memo', image, email, actionButtonText, lng, }) => {
38
- const interactive = '#0076ff';
39
- AWS.config.update({
40
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
41
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
42
- region: 'eu-central-1',
37
+ export const sendEmail = async ({ title = "Kein Titel", body = "Kein Inhalt", url = "", domain = "memo", image, email, actionButtonText, lng, }) => {
38
+ const interactive = "#0076ff";
39
+ const sesClient = new SESv2Client({
40
+ region: "eu-central-1",
41
+ credentials: process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY
42
+ ? {
43
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
44
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
45
+ }
46
+ : undefined,
43
47
  });
44
- const actionButtonTextWithLanguage = i18n.t(actionButtonText || 'Go to Application', { lng });
45
- const ses = new AWS.SES({ apiVersion: '2010-12-01' });
46
- const toEmail = 'notifications@wirewire.de';
47
- const base64ToName = Buffer.from(`Memo ${i18n.t('Notifications', { lng })}`).toString('base64');
48
+ const actionButtonTextWithLanguage = i18n.t(actionButtonText || "Go to Application", { lng });
49
+ const toEmail = "notifications@wirewire.de";
50
+ const base64ToName = Buffer.from(`Memo ${i18n.t("Notifications", { lng })}`).toString("base64");
48
51
  const finalToName = `=?UTF-8?B?${base64ToName}?= <${toEmail}>`;
49
52
  const params = {
50
53
  Destination: {
51
54
  ToAddresses: [email],
52
55
  },
53
- ConfigurationSetName: 'memo-transactional',
54
- Message: {
55
- Body: {
56
- Html: {
57
- Charset: 'UTF-8',
58
- Data: `
56
+ ConfigurationSetName: "memo-transactional",
57
+ Content: {
58
+ Simple: {
59
+ Body: {
60
+ Html: {
61
+ Charset: "UTF-8",
62
+ Data: `
59
63
  <!DOCTYPE html>
60
64
  <html>
61
65
  <head>
@@ -508,7 +512,7 @@ export const sendEmail = async ({ title = 'Kein Titel', body = 'Kein Inhalt', ur
508
512
  <tr>
509
513
  <td class="email-masthead">
510
514
  <a href="https://${domain}.wirewire.de" class="f-fallback email-masthead_name">
511
- ${domain === 'memo' ? 'ANABOX smart' : 'paperlesspaper'}
515
+ ${domain === "memo" ? "ANABOX smart" : "paperlesspaper"}
512
516
  </a>
513
517
  </td>
514
518
  </tr>
@@ -520,14 +524,16 @@ export const sendEmail = async ({ title = 'Kein Titel', body = 'Kein Inhalt', ur
520
524
  <tr>
521
525
  <td class="content-cell align-center">
522
526
  <div class="f-fallback">
523
- ${image ? `<img class="email-image" src="${image}" alt="memo image" />` : ''}
527
+ ${image ? `<img class="email-image" src="${image}" alt="memo image" />` : ""}
524
528
  <h1>${title}</h1>
525
529
  <p>${body}</p>
526
530
  <!-- Action -->
527
531
  ${actionButton({
528
- link: urlStartsWithHttp(url) ? url : `http://${domain}.wirewire.de${url}`,
529
- text: actionButtonTextWithLanguage,
530
- })}
532
+ link: urlStartsWithHttp(url)
533
+ ? url
534
+ : `http://${domain}.wirewire.de${url}`,
535
+ text: actionButtonTextWithLanguage,
536
+ })}
531
537
  </div>
532
538
  </td>
533
539
  </tr>
@@ -540,10 +546,10 @@ export const sendEmail = async ({ title = 'Kein Titel', body = 'Kein Inhalt', ur
540
546
  <tr>
541
547
  <td class="content-cell" align="center">
542
548
  <p class="f-fallback sub align-center">
543
- ${domain === 'web' ? 'The Wire UG' : 'wirewire GmbH'}
549
+ ${domain === "web" ? "The Wire UG" : "wirewire GmbH"}
544
550
  <a href="http://${domain}.wirewire.de/account">Account</a>
545
551
 
546
- ${config.env !== 'production' ? `<br/><br/>Environment: ${config.env}` : ''}
552
+ ${config.env !== "production" ? `<br/><br/>Environment: ${config.env}` : ""}
547
553
  </p>
548
554
  </td>
549
555
  </tr>
@@ -557,22 +563,23 @@ export const sendEmail = async ({ title = 'Kein Titel', body = 'Kein Inhalt', ur
557
563
  </body>
558
564
  </html>
559
565
  `,
566
+ },
567
+ Text: {
568
+ Charset: "UTF-8",
569
+ Data: `${title} ${body}`,
570
+ },
560
571
  },
561
- Text: {
562
- Charset: 'UTF-8',
563
- Data: `${title} ${body}`,
572
+ Subject: {
573
+ Charset: "UTF-8",
574
+ Data: `${title} - Memo App`,
564
575
  },
565
576
  },
566
- Subject: {
567
- Charset: 'UTF-8',
568
- Data: `${title} - Memo App`,
569
- },
570
577
  },
571
- Source: finalToName,
578
+ FromEmailAddress: finalToName,
572
579
  };
573
580
  try {
574
- const data = await ses.sendEmail(params).promise();
575
- console.log('Email submitted to SES', data);
581
+ const data = await sesClient.send(new SendEmailCommand(params));
582
+ console.log("Email submitted to SES", data);
576
583
  }
577
584
  catch (error) {
578
585
  console.error(error);
package/dist/src/index.js CHANGED
@@ -8,6 +8,7 @@ export { validateDevice, validateDeviceIsInOrganization, validateDeviceOrOrganiz
8
8
  export { initI18n } from "../src/i18n/i18n";
9
9
  export { default as i18n } from "../src/i18n/i18n";
10
10
  export { default as usersRoute } from "../src/users/users.route";
11
+ export { default as usersService } from "../src/users/users.service";
11
12
  export { default as accountsRoute } from "../src/accounts/accounts.route";
12
13
  export { default as accountsService } from "../src/accounts/accounts.service";
13
14
  export { auth0 } from "../src/accounts/auth0.service";
@@ -21,12 +22,12 @@ export * from "../src/devices/devices.validation";
21
22
  export { default as devicesNotificationsRoute } from "./devicesNotifications/devicesNotifications.route";
22
23
  export { default as devicesNotificationsService } from "../src/devicesNotifications/devicesNotifications.service";
23
24
  export { default as iotDevicesService } from "../src/iotdevice/iotdevice.service";
25
+ export { default as iotdeviceRoute } from "../src/iotdevice/iotdevice.route";
24
26
  export { SIMILARITY_THRESHOLD } from "../src/iotdevice/iotdevice.service";
25
27
  export { default as pdfRoute } from "../src/pdf/pdf.route";
26
28
  export { default as tokensRoute } from "../src/tokens/tokens.route";
27
29
  export * from "../src/tokens/tokens.service";
28
30
  export { default as Token } from "../src/tokens/tokens.model";
29
- export * as usersService from "../src/users/users.service";
30
31
  export { User } from "../src/users/users.model";
31
32
  export { isAdmin, validateAdmin } from "../src/middlewares/validateAdmin";
32
33
  export { sendEmail } from "../src/email/email.service";
@@ -37,6 +38,7 @@ export { paginate, toJSON } from "../src/models/plugins/index";
37
38
  export { compareImages } from "../src/utils/comparePapers.service";
38
39
  export { resolvePossiblyRelativeUrl } from "../src/utils/urlUtils";
39
40
  export { getSignedFileUrl } from "../src/files/upload.service";
41
+ export * from "../src/middlewares/rateLimiter";
40
42
  export * from "../src/utils/ApiError";
41
43
  export * from "../src/utils/buildRouterAndDocs";
42
44
  export * from "../src/utils/comparePapers.service";
@@ -48,3 +50,18 @@ export * from "../src/utils/registerOpenApi";
48
50
  export * from "../src/utils/urlUtils";
49
51
  export * from "../src/utils/userName";
50
52
  export * from "../src/utils/zValidations";
53
+ export * from "../src/validations/custom.validation";
54
+ export * from "../src/models/plugins/paginate.plugin";
55
+ export * from "../src/models/plugins/simplePopulate";
56
+ export * from "../src/middlewares/validateOrganization";
57
+ export * from "../src/middlewares/validateUser";
58
+ export * from "../src/middlewares/validateCurrentUser";
59
+ export * from "../src/middlewares/validateZod";
60
+ export * from "../src/middlewares/validateAdmin";
61
+ export * from "../src/middlewares/validateAi";
62
+ export * from "../src/middlewares/validateDevice";
63
+ export * from "../src/middlewares/auth";
64
+ export * from "../src/middlewares/error";
65
+ export * from "../src/accounts/auth0.service";
66
+ export * from "../src/middlewares/validateAction";
67
+ export { default as generateUserName } from "../src/utils/userName";
@@ -105,7 +105,7 @@ export const iotdeviceRouteSpecs = [
105
105
  responseSchema: apiStatusSchema,
106
106
  handler: getApiStatus,
107
107
  summary: "Get API status by kind",
108
- description: "Retrieves the API status information for a given status kind.",
108
+ description: "Retrieves the IoT API status information for a given status kind to monitor system health or performance. Can be accessed without authentication for monitoring purposes.",
109
109
  },
110
110
  {
111
111
  method: "get",
@@ -1,59 +1,20 @@
1
1
  // @ts-nocheck
2
2
  import httpStatus from "http-status";
3
3
  import axios from "axios";
4
- import AWS from "aws-sdk";
4
+ import { GetThingShadowCommand, IoTDataPlaneClient, UpdateThingShadowCommand, } from "@aws-sdk/client-iot-data-plane";
5
5
  import { deviceKindHasFeature } from "../utils/deviceUtils";
6
6
  import ApiError from "../utils/ApiError";
7
7
  import { getAuth0Token } from "../accounts/auth0.service";
8
- import { uploadImage, getSignedFileUrl } from "../files/upload.service";
9
- import { compareImages } from "../utils/comparePapers.service";
10
8
  import IotDevice from "./iotdevice.model";
11
- import { fileTypeFromBuffer } from "file-type";
12
9
  import iotsdk from "aws-iot-device-sdk-v2";
13
10
  const iot = iotsdk.iot;
14
11
  const mqtt = iotsdk.mqtt;
15
12
  export const SIMILARITY_THRESHOLD = Number(process.env.EPAPER_SIMILARITY_THRESHOLD ?? 99.995);
16
- const buildUploadResponse = (data, similarityPercentage, skippedUpload) => {
17
- return { /*...data,*/ key: data?.Key, similarityPercentage, skippedUpload };
18
- };
19
- const downloadPreviousOriginalImage = async (id) => {
20
- if (!id) {
21
- return null;
22
- }
23
- try {
24
- const signedUrl = await getSignedFileUrl({
25
- fileName: `ePaperImages/${id}original.png`,
26
- });
27
- const response = await axios.get(signedUrl, {
28
- responseType: "arraybuffer",
29
- });
30
- return Buffer.from(response.data);
31
- }
32
- catch (error) {
33
- console.warn(`Unable to download previous image for ${id}:`, error?.message || error);
34
- return null;
35
- }
36
- };
37
- const evaluateSimilarityBeforeUpload = async (id, bufferOriginal) => {
38
- if (!bufferOriginal) {
39
- return { similarityPercentage: null, skipUpload: false };
40
- }
41
- const previousBuffer = await downloadPreviousOriginalImage(id);
42
- if (!previousBuffer) {
43
- return { similarityPercentage: null, skipUpload: false };
44
- }
45
- try {
46
- const similarityPercentage = await compareImages(previousBuffer, bufferOriginal);
47
- return {
48
- similarityPercentage,
49
- skipUpload: similarityPercentage >= SIMILARITY_THRESHOLD,
50
- };
51
- }
52
- catch (error) {
53
- console.warn(`Similarity comparison failed for ${id}:`, error?.message || error);
54
- return { similarityPercentage: null, skipUpload: false };
55
- }
56
- };
13
+ const IOT_DATA_ENDPOINT = "https://a2vm6rc8xrtk10-ats.iot.eu-central-1.amazonaws.com";
14
+ const iotDataClient = new IoTDataPlaneClient({
15
+ endpoint: IOT_DATA_ENDPOINT,
16
+ region: process.env.AWS_REGION ?? "eu-central-1",
17
+ });
57
18
  /**
58
19
  * Get events for a device
59
20
  * @param {Object} params
@@ -309,18 +270,17 @@ export const getDeviceStatus = async (deviceName, kind) => {
309
270
  * @returns {Promise<Device>}
310
271
  */
311
272
  export const shadowAlarmGet = async (deviceName, shadowName) => {
312
- const iotdata = new AWS.IotData({
313
- endpoint: "a2vm6rc8xrtk10-ats.iot.eu-central-1.amazonaws.com",
314
- });
315
273
  if (!deviceName)
316
274
  return { error: "deviceName is required" };
317
- const params = {
318
- thingName: deviceName,
319
- shadowName,
320
- };
321
275
  try {
322
- const response = await iotdata.getThingShadow(params).promise();
323
- return JSON.parse(response.payload);
276
+ const response = await iotDataClient.send(new GetThingShadowCommand({
277
+ thingName: deviceName,
278
+ shadowName,
279
+ }));
280
+ const payload = response.payload
281
+ ? Buffer.from(response.payload).toString("utf8")
282
+ : "{}";
283
+ return JSON.parse(payload);
324
284
  }
325
285
  catch (e) {
326
286
  // console.log(deviceName, e);
@@ -352,73 +312,24 @@ export const ledLightHint = async (deviceName, body) => {
352
312
  */
353
313
  const shadowAlarmUpdate = async (deviceName, alarms, shadowName) => {
354
314
  const data = alarms;
355
- const iotdata = new AWS.IotData({
356
- endpoint: "a2vm6rc8xrtk10-ats.iot.eu-central-1.amazonaws.com",
357
- });
358
315
  if (!deviceName) {
359
316
  return { error: "no deviceId" };
360
317
  }
361
- const params = {
362
- payload: JSON.stringify(data),
363
- thingName: deviceName,
364
- shadowName,
365
- };
366
318
  try {
367
- const response = await iotdata.updateThingShadow(params).promise();
368
- return JSON.parse(response.payload);
319
+ const response = await iotDataClient.send(new UpdateThingShadowCommand({
320
+ payload: JSON.stringify(data),
321
+ thingName: deviceName,
322
+ shadowName,
323
+ }));
324
+ const payload = response.payload
325
+ ? Buffer.from(response.payload).toString("utf8")
326
+ : "{}";
327
+ return JSON.parse(payload);
369
328
  }
370
329
  catch (e) {
371
330
  return "error";
372
331
  }
373
332
  };
374
- export const uploadSingleImage = async ({ deviceName, buffer, bufferOriginal, bufferEditable, id, paperId, }) => {
375
- try {
376
- const { skipUpload, similarityPercentage } = await evaluateSimilarityBeforeUpload(id, bufferOriginal);
377
- var response = {};
378
- if (skipUpload) {
379
- return buildUploadResponse({ message: "Image skipped due to similarity threshold" }, similarityPercentage, true);
380
- }
381
- if (!bufferOriginal) {
382
- throw new Error("bufferOriginal is required to upload an image");
383
- }
384
- // console.log(`Uploading image for ${id} on ${deviceName}; similarity ${similarityPercentage?.toFixed(5)}%`);
385
- if (deviceName) {
386
- const accessToken = await getAuth0Token();
387
- response = await axios.post(`${process.env.IOT_API_URL_EPAPER}uploads`, { deviceName }, {
388
- headers: { Authorization: `Bearer ${accessToken}` },
389
- });
390
- if (!response.data.uploadURL) {
391
- console.log("No upload URL received", response.data);
392
- }
393
- else {
394
- await axios.put(response.data.uploadURL, buffer, {
395
- // onUploadProgress: (progressEvent) => console.log('file progress', progressEvent.loaded),
396
- headers: { "Content-Type": "text/octet-stream" },
397
- });
398
- }
399
- }
400
- const type = await fileTypeFromBuffer(buffer);
401
- const fileName = `ePaperImages/${id}`;
402
- await uploadImage({ blob: buffer, key: fileName + ".png", type });
403
- await uploadImage({
404
- blob: bufferOriginal,
405
- key: fileName + "original.png",
406
- type,
407
- });
408
- if (bufferEditable) {
409
- await uploadImage({
410
- blob: bufferEditable,
411
- key: fileName + "editable.json",
412
- type,
413
- });
414
- }
415
- return buildUploadResponse(response.data, similarityPercentage, false);
416
- }
417
- catch (error) {
418
- console.error(error);
419
- return null;
420
- }
421
- };
422
333
  /**
423
334
  * Get device by ID
424
335
  * @param {string} id
@@ -570,7 +481,6 @@ export default {
570
481
  deleteById,
571
482
  getApiStatus,
572
483
  getDevice,
573
- uploadSingleImage,
574
484
  liveEventsWs,
575
485
  shadowAlarmGet,
576
486
  shadowAlarmUpdate,