@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
@@ -5,6 +5,52 @@ import jwksRsa from "jwks-rsa";
5
5
  import ApiError from "../utils/ApiError";
6
6
  import Token from "../tokens/tokens.model";
7
7
  import { roleRights } from "../config/roles";
8
+ import auth0Service from "../accounts/auth0.service";
9
+ const ROLES_CLAIM = "https://memo.wirewire.de/roles";
10
+ const AUTH0_ROLE_CACHE_TTL_MS = 5 * 60 * 1000;
11
+ const auth0RolesCache = new Map();
12
+ const dedupeRoles = (roles) => Array.from(new Set(roles.map((role) => role.trim()).filter(Boolean)));
13
+ const extractRoleNamesFromManagementResponse = (payload) => {
14
+ const iterablePayload = payload && typeof payload[Symbol.iterator] === "function"
15
+ ? Array.from(payload)
16
+ : [];
17
+ const list = Array.isArray(payload)
18
+ ? payload
19
+ : Array.isArray(payload?.data)
20
+ ? payload.data
21
+ : Array.isArray(payload?.items)
22
+ ? payload.items
23
+ : iterablePayload;
24
+ const roleNames = list
25
+ .map((entry) => typeof entry?.name === "string" ? entry.name : "")
26
+ .filter(Boolean);
27
+ return dedupeRoles(roleNames);
28
+ };
29
+ const getAuth0RolesByOwner = async (ownerId) => {
30
+ const now = Date.now();
31
+ const cached = auth0RolesCache.get(ownerId);
32
+ if (cached && cached.expiresAt > now) {
33
+ // return cached.roles;
34
+ }
35
+ try {
36
+ console.log(`Fetching Auth0 roles for owner ${ownerId} from Management API...`);
37
+ const rolesPayload = await auth0Service.auth0.users.roles.list(ownerId);
38
+ console.log(`Fetched Auth0 roles for owner ${ownerId}:`, rolesPayload);
39
+ const roles = extractRoleNamesFromManagementResponse(rolesPayload);
40
+ auth0RolesCache.set(ownerId, {
41
+ roles,
42
+ expiresAt: now + AUTH0_ROLE_CACHE_TTL_MS,
43
+ });
44
+ return roles;
45
+ }
46
+ catch (error) {
47
+ console.warn("auth middleware: could not fetch Auth0 roles for owner", {
48
+ ownerId,
49
+ error,
50
+ });
51
+ return [];
52
+ }
53
+ };
8
54
  const verifyCallback = (req, resolve, reject, requiredRights) => async (err, user, info) => {
9
55
  if (err || info || !user) {
10
56
  return reject(new ApiError(httpStatus.UNAUTHORIZED, "Please authenticate"));
@@ -35,7 +81,8 @@ const auth = function authFactory(...requiredRights) {
35
81
  }).select("+owner");
36
82
  if (tokenDoc) {
37
83
  const ownerId = tokenDoc.owner;
38
- const roles = ["api"];
84
+ const auth0Roles = await getAuth0RolesByOwner(ownerId);
85
+ console.log(`Authenticated API token request for owner ${ownerId}`, auth0Roles);
39
86
  req.auth = {
40
87
  id: ownerId,
41
88
  tokenId: tokenDoc._id,
@@ -43,7 +90,7 @@ const auth = function authFactory(...requiredRights) {
43
90
  // For API-key auth, we can treat the token owner as the subject.
44
91
  // Avoid fetching user profile from Auth0 Management API on every request.
45
92
  sub: ownerId,
46
- "https://memo.wirewire.de/roles": roles,
93
+ [ROLES_CLAIM]: auth0Roles,
47
94
  };
48
95
  return next();
49
96
  }
@@ -1,7 +1,7 @@
1
1
  import httpStatus from "http-status";
2
2
  import ApiError from "../utils/ApiError";
3
3
  import userService from "../users/users.service";
4
- const getCurrentUser = async (req, res, next) => {
4
+ export const validateCurrentUser = async (req, res, next) => {
5
5
  try {
6
6
  // TODO: Check if the user is logged in
7
7
  const currentUser = await userService.getUserByOwner(res.req.auth.sub, req.body.organization);
@@ -17,4 +17,4 @@ const getCurrentUser = async (req, res, next) => {
17
17
  next(new ApiError(httpStatus.INTERNAL_SERVER_ERROR, "Failed to validate user service"));
18
18
  }
19
19
  };
20
- export default getCurrentUser;
20
+ export default validateCurrentUser;
@@ -1,4 +1,4 @@
1
- function simplePopulate(populate) {
1
+ export function simplePopulate(populate) {
2
2
  let docsPromise = {};
3
3
  populate.split(",").forEach((populateOption) => {
4
4
  docsPromise = populateOption
@@ -1,33 +1,32 @@
1
1
  import puppeteer from "puppeteer";
2
2
  import { v4 as uuidv4 } from "uuid";
3
- import AWS from "aws-sdk";
3
+ import { GetObjectCommand, PutObjectCommand, S3Client, } from "@aws-sdk/client-s3";
4
+ import { getSignedUrl as getS3SignedUrl } from "@aws-sdk/s3-request-presigner";
4
5
  import path from "path";
5
- // Configure AWS SDK
6
- const s3 = new AWS.S3({
7
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
8
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
6
+ const s3 = new S3Client({
9
7
  region: process.env.AWS_REGION,
8
+ credentials: process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY
9
+ ? {
10
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
11
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
12
+ }
13
+ : undefined,
10
14
  });
11
15
  // Function to upload a file to S3
12
16
  const uploadBuffer = async (buffer, fileName) => {
13
- const params = {
17
+ await s3.send(new PutObjectCommand({
14
18
  Bucket: process.env.AWS_S3_BUCKET_NAME,
15
19
  Key: fileName,
16
20
  Body: buffer,
17
21
  ContentType: "application/pdf",
18
- ACL: "private",
19
- };
20
- const data = await s3.upload(params).promise();
21
- return data.Location;
22
+ }));
22
23
  };
23
24
  // Generate a signed URL
24
- const generateSignedUrl = (fileName) => {
25
- const params = {
25
+ const generateSignedUrl = async (fileName) => {
26
+ return getS3SignedUrl(s3, new GetObjectCommand({
26
27
  Bucket: process.env.AWS_S3_BUCKET_NAME,
27
28
  Key: fileName,
28
- Expires: 60 * 60, // URL expiration time in seconds
29
- };
30
- return s3.getSignedUrl("getObject", params);
29
+ }), { expiresIn: 60 * 60 });
31
30
  };
32
31
  const generatePdfFromUrl = async ({ urlPath, token, }) => {
33
32
  const domain = process.env.FRONTEND_URL;
@@ -53,10 +52,10 @@ const generatePdfFromUrl = async ({ urlPath, token, }) => {
53
52
  localStorage.setItem("print-token", "");
54
53
  });
55
54
  await browser.close();
56
- const fileUrl = await uploadBuffer(Buffer.from(pdf), `download-${uuidv4()}.pdf`);
57
- console.log(`File uploaded successfully. File URL: ${fileUrl}`);
58
- const fileName = path.basename(fileUrl);
59
- const signedUrl = generateSignedUrl(fileName);
55
+ const fileName = `download-${uuidv4()}.pdf`;
56
+ await uploadBuffer(Buffer.from(pdf), fileName);
57
+ console.log(`File uploaded successfully. File Name: ${fileName}`);
58
+ const signedUrl = await generateSignedUrl(path.basename(fileName));
60
59
  console.log(`Signed URL: ${signedUrl}`);
61
60
  return signedUrl;
62
61
  };
@@ -1,5 +1,5 @@
1
- import { z } from 'zod';
2
- import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi';
1
+ import { z } from "zod";
2
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
3
3
  extendZodWithOpenApi(z);
4
4
  export const createUserResponseSchema = z.object({
5
5
  id: z.string(),
@@ -23,47 +23,3 @@ export const updateUserResponseSchema = z.object({
23
23
  export const deleteUserResponseSchema = z.object({
24
24
  success: z.boolean(),
25
25
  });
26
- export const updateTimesByIdResponseSchema = z
27
- .array(z.object({
28
- rrule: z
29
- .object({
30
- freq: z.string().optional().openapi({ example: 'DAILY', description: 'Recurrence frequency' }),
31
- byweekday: z
32
- .array(z.number())
33
- .openapi({ example: [0, 1, 2, 6, 3], description: 'Days of week to repeat (0=Sunday)' }),
34
- exclude: z.array(z.string()).openapi({ example: ['2024-03-28T10:45:00.000Z'], description: 'Dates to skip' }),
35
- })
36
- .openapi({ description: 'Recurrence rule object' }),
37
- medication: z.string().optional().openapi({ example: '6152c5f3902e7f91374d9f75', description: 'Medication ObjectId' }),
38
- patient: z.string().openapi({ example: '614fb1d709dd9f6de85d6374', description: 'Patient ObjectId' }),
39
- date: z.string().openapi({ example: '2024-03-25T00:30:00.000Z', description: 'Scheduled date/time (ISO)' }),
40
- timeCategory: z.string().openapi({ example: 'noon', description: 'Time category (e.g. morning, noon)' }),
41
- amount: z.number().openapi({ example: 1, description: 'Dosage amount' }),
42
- emptyStomach: z.boolean().openapi({ example: false, description: 'Whether to take on empty stomach' }),
43
- instruction: z.string().optional().openapi({ example: '', description: 'Additional instructions' }),
44
- unit: z.string().optional().openapi({ example: 'St', description: 'Dosage unit' }),
45
- bake: z.boolean().openapi({ example: false, description: 'Baking flag (if applicable)' }),
46
- id: z.string().openapi({ example: '660079fd11fdc2dd935e43af', description: 'Entry identifier' }),
47
- }))
48
- .openapi({
49
- example: [
50
- {
51
- rrule: {
52
- freq: 'DAILY',
53
- byweekday: [0, 1, 2, 6, 3],
54
- exclude: ['2024-03-28T10:45:00.000Z'],
55
- },
56
- patient: '614fb1d709dd9f6de85d6374',
57
- date: '2024-03-25T00:30:00.000Z',
58
- timeCategory: 'noon',
59
- amount: 1,
60
- emptyStomach: false,
61
- instruction: '',
62
- unit: 'St',
63
- bake: false,
64
- id: '660079fd11fdc2dd935e43af',
65
- },
66
- // ...other items
67
- ],
68
- description: 'Array of updated time entries by ID',
69
- });
@@ -292,4 +292,5 @@ export default {
292
292
  sendEmail,
293
293
  populateAuth0User,
294
294
  populateAuth0Users,
295
+ setUpdateTimesByIdHook,
295
296
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetderdinge/api",
3
- "version": "1.229.10",
3
+ "version": "1.229.17",
4
4
  "description": "Shared OpenIoT API modules",
5
5
  "main": "dist/src/index.js",
6
6
  "type": "module",
@@ -15,11 +15,13 @@
15
15
  "deps:versions": "node --input-type=module -e \"import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); const out = (name) => { const pkgPath = require.resolve(name + '/package.json'); const version = require(pkgPath).version; console.log(name + '@' + version + ' -> ' + pkgPath); }; out('zod'); out('@asteasolutions/zod-to-openapi');\"",
16
16
  "deploy:version": "node ./scripts/release-version.mjs",
17
17
  "release:paperless": "node ./scripts/release-and-sync-paperless.mjs",
18
+ "release:paperless:publish": "node ./scripts/release-and-sync-paperless.mjs --publish",
18
19
  "lint": "eslint .",
19
20
  "lint:fix": "eslint . --fix"
20
21
  },
21
22
  "dependencies": {
22
23
  "@aws-sdk/client-cloudwatch-events": "^3.990.0",
24
+ "@aws-sdk/client-iot-data-plane": "^3.990.0",
23
25
  "@aws-sdk/client-s3": "^3.990.0",
24
26
  "@aws-sdk/client-sesv2": "^3.990.0",
25
27
  "@aws-sdk/credential-provider-node": "^3.972.9",
@@ -33,7 +35,6 @@
33
35
  "auth0": "^5.3.1",
34
36
  "aws-crt": "^1.29.0",
35
37
  "aws-iot-device-sdk-v2": "^1.25.0",
36
- "aws-sdk": "^2.1693.0",
37
38
  "aws4": "^1.13.2",
38
39
  "axios": "^1.13.5",
39
40
  "body-parser": "~2.2.2",
@@ -138,7 +138,6 @@ const deleteCurrent = catchAsync(
138
138
  const current = catchAsync(
139
139
  async (req: AuthenticatedRequest, res: Response): Promise<void> => {
140
140
  const user = await accountsService.getAccountById(req.auth.sub);
141
- console.log("user", user);
142
141
  res.send(user);
143
142
  },
144
143
  );
@@ -3,12 +3,6 @@ import dotenv from "dotenv";
3
3
  // Load env from the current working directory
4
4
  dotenv.config();
5
5
 
6
- console.log("Current working directory:", process.cwd());
7
- console.log("Loaded environment variables:", {
8
- NODE_ENV: process.env.NODE_ENV,
9
- PORT: process.env.PORT,
10
- MONGODB_URL: process.env.MONGODB_URL,
11
- });
12
6
  import Joi from "joi";
13
7
 
14
8
  const envVarsSchema = Joi.object()
@@ -1,7 +1,6 @@
1
1
  // @ts-nocheck
2
2
  import httpStatus from "http-status";
3
3
  import mongoose from "mongoose";
4
- import multer from "multer";
5
4
  import type { Request, Response } from "express";
6
5
  import pick from "../utils/pick.js";
7
6
  import ApiError from "../utils/ApiError.js";
@@ -129,19 +128,6 @@ const getEntry = catchAsync(
129
128
  },
130
129
  );
131
130
 
132
- const getImageById = catchAsync(
133
- async (req: Request, res: Response): Promise<void> => {
134
- const device = await devicesService.getImageById(
135
- req.params.deviceId,
136
- req.params.uuid,
137
- );
138
- if (!device) {
139
- throw new ApiError(httpStatus.NOT_FOUND, "Device not found");
140
- }
141
- res.send(device);
142
- },
143
- );
144
-
145
131
  const updateEntry = catchAsync(
146
132
  async (req: Request, res: Response): Promise<void> => {
147
133
  // TODO: Remove in newer versions
@@ -228,54 +214,15 @@ const rebootDevice = catchAsync(
228
214
  },
229
215
  );
230
216
 
231
- const updateSingleImageMeta = catchAsync(
232
- async (req: Request, res: Response): Promise<void> => {
233
- const device = await devicesService.getById(req.params.deviceId);
234
-
235
- const { uuid, ...body } = req.body;
236
- const deviceUpdate = await devicesService.updateById(req.params.deviceId, {
237
- meta: { ...device.meta, file: uuid },
238
- });
239
- const deviceMeta = await devicesService.updateSingleImageMeta(
240
- device.deviceId,
241
- body,
242
- );
243
- res.send({ deviceMeta, deviceUpdate });
244
- },
245
- );
246
-
247
- const uploadSingleImage = catchAsync(
248
- async (req: Request, res: Response): Promise<void> => {
249
- const device = await devicesService.getById(req.params.deviceId);
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
-
256
- const iotUpload = await iotDevicesService.uploadSingleImage({
257
- deviceName: device.deviceId,
258
- buffer: files[0].buffer,
259
- deviceId: req.params.deviceId,
260
- uuid: req.body.uuid,
261
- });
262
-
263
- res.send(iotUpload);
264
- },
265
- );
266
-
267
217
  export {
268
218
  createEntry,
269
219
  getEntries,
270
220
  queryDevicesByUser,
271
221
  getEvents,
272
- getImageById,
273
222
  registerDevice,
274
223
  pingDevice,
275
224
  ledLight,
276
225
  rebootDevice,
277
- uploadSingleImage,
278
- updateSingleImageMeta,
279
226
  getEntry,
280
227
  updateEntry,
281
228
  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 type { RouteSpec } from "../types/routeSpec";
5
4
  import auth from "../middlewares/auth.js";
@@ -23,9 +22,6 @@ import {
23
22
  registerDeviceSchema,
24
23
  ledLightSchema,
25
24
  rebootDeviceSchema,
26
- getImageSchema,
27
- updateSingleImageMetaSchema,
28
- uploadSingleImageSchema,
29
25
  resetDeviceSchema,
30
26
  } from "./devices.validation.js";
31
27
  import {
@@ -33,8 +29,6 @@ import {
33
29
  devicesResponseSchema,
34
30
  eventResponseSchema,
35
31
  genericResponseSchema,
36
- imageResponseSchema,
37
- uploadResponseSchema,
38
32
  resetResponseSchema,
39
33
  } from "./devices.schemas.js";
40
34
  import {
@@ -171,39 +165,6 @@ export const devicesRouteSpecs: RouteSpec[] = [
171
165
  summary: "Reboot a device",
172
166
  description: "Initiate a remote reboot of the specified device.",
173
167
  },
174
- {
175
- method: "get",
176
- path: "/image/:deviceId/:uuid",
177
- validate: [auth("getUsers"), validateDevice],
178
- requestSchema: getImageSchema,
179
- responseSchema: imageResponseSchema,
180
- handler: devicesController.getImageById,
181
- summary: "Fetch an image by UUID",
182
- description:
183
- "Download a previously uploaded image for the device by its UUID.",
184
- },
185
- {
186
- method: "post",
187
- path: "/updateSingleImageMeta/:deviceId",
188
- validate: [auth("getUsers"), validateDevice],
189
- requestSchema: updateSingleImageMetaSchema,
190
- responseSchema: uploadResponseSchema,
191
- handler: devicesController.updateSingleImageMeta,
192
- summary: "Update image metadata",
193
- description:
194
- "Modify metadata (e.g., title, tags) for an existing device image.",
195
- },
196
- {
197
- method: "post",
198
- path: "/uploadSingleImage/:deviceId",
199
- validate: [auth("getUsers"), multer().array("picture", 2), validateDevice],
200
- requestSchema: uploadSingleImageSchema,
201
- responseSchema: uploadResponseSchema,
202
- handler: devicesController.uploadSingleImage,
203
- summary: "Upload one or two images",
204
- description:
205
- "Upload up to two image files to the device for processing or storage.",
206
- },
207
168
  ];
208
169
 
209
170
  const router: Router = Router();
@@ -5,7 +5,6 @@ import ApiError from "../utils/ApiError.js";
5
5
  import iotDevicesService from "../iotdevice/iotdevice.service.js";
6
6
 
7
7
  import { promisify } from "util";
8
- import { getSignedFileUrl } from "../files/upload.service";
9
8
  import { deviceByDeviceName, deviceKindHasFeature } from "../utils/deviceUtils";
10
9
 
11
10
  import type { DeviceDocument, DeviceInput } from "./devices.model.js";
@@ -107,15 +106,6 @@ export const populateDeviceStatus = async (e: {
107
106
  return deviceStatus;
108
107
  };
109
108
 
110
- export const populateDeviceImage = async (
111
- deviceId: string,
112
- uuid: string,
113
- ): Promise<string> => {
114
- const fileName = `ePaperImages/${deviceId}+${uuid}.png`;
115
- const url = await getSignedFileUrl({ fileName });
116
- return url;
117
- };
118
-
119
109
  export const populateIotDevices = async (
120
110
  data: DeviceDocument[],
121
111
  ): Promise<any[]> => {
@@ -154,14 +144,6 @@ export const getById = async (id: string): Promise<DeviceDocument> => {
154
144
  return device;
155
145
  };
156
146
 
157
- export const getImageById = async (
158
- id: string,
159
- uuid: string,
160
- ): Promise<{ url: string }> => {
161
- const url = await populateDeviceImage(id, uuid);
162
- return { url };
163
- };
164
-
165
147
  export const getByIdWithIoT = async (id: string): Promise<any> => {
166
148
  const device = await getById(id);
167
149
  if (!device) {
@@ -238,24 +220,6 @@ export const updateById = async (
238
220
  return deviceJSON;
239
221
  };
240
222
 
241
- export const updateSingleImageMeta = async (
242
- deviceId: string,
243
- shadowNew: any,
244
- ): Promise<any> => {
245
- const shadowBody = {
246
- state: {
247
- reported: shadowNew,
248
- },
249
- };
250
-
251
- const shadowResult = await iotDevicesService.shadowAlarmUpdate(
252
- deviceId,
253
- shadowBody,
254
- "settings",
255
- );
256
- return shadowResult;
257
- };
258
-
259
223
  export const deleteById = async (
260
224
  userId: string,
261
225
  deleteFromIotApi = true,
@@ -305,12 +269,10 @@ export default {
305
269
  getByIdWithIoT,
306
270
  getDeviceByUserId,
307
271
  getDeviceByDeviceId,
308
- getImageById,
309
272
  updateById,
310
273
  updatePaymentById,
311
274
  deleteById,
312
275
  populateIotDevices,
313
- populateDeviceImage,
314
276
  populateDeviceStatus,
315
277
  queryDevicesByUser,
316
278
  registerDevice,
@@ -112,14 +112,6 @@ export const createDeviceSchema = {
112
112
  };
113
113
  export const deleteDeviceSchema = zDelete("deviceId");
114
114
  export const getDeviceSchema = zGet("deviceId");
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
- };
123
115
  export const ledLightSchema = {
124
116
  params: z.object({
125
117
  deviceId: zObjectId.openapi({ description: "Device ObjectId" }),
@@ -202,17 +194,18 @@ export const registerDeviceSchema = {
202
194
  example: {
203
195
  enable: true,
204
196
  organization:
205
- process.env.SCHEMA_EXAMPLE_ORGANIZATION_ID || "682fd0d7d4a6325d9d45b86d",
206
- patient: process.env.SCHEMA_EXAMPLE_USER_ID || "682fd0d7d4a6325d9d45b86d",
197
+ process.env.SCHEMA_EXAMPLE_ORGANIZATION_ID ||
198
+ "682fd0d7d4a6325d9d45b86d",
199
+ patient:
200
+ process.env.SCHEMA_EXAMPLE_USER_ID || "682fd0d7d4a6325d9d45b86d",
207
201
  paper: process.env.SCHEMA_EXAMPLE_PAPER_ID || null,
208
202
  },
209
203
  }),
210
204
  params: z.object({
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()
205
+ deviceId: (process.env.SCHEMA_STRICT_EXAMPLES === "true" &&
206
+ process.env.SCHEMA_EXAMPLE_DEVICE_SERIAL
207
+ ? z.literal(process.env.SCHEMA_EXAMPLE_DEVICE_SERIAL)
208
+ : z.string()
216
209
  ).openapi({
217
210
  description: "Device serial",
218
211
  example: process.env.SCHEMA_EXAMPLE_DEVICE_SERIAL || "DEVICE-EXAMPLE",
@@ -224,7 +217,7 @@ export const queryDevicesSchema = {
224
217
  ...zPagination,
225
218
  query: zPagination.query.extend({
226
219
  patient: zObjectIdFor("patient").optional(),
227
- organization: zObjectIdFor("organization"),
220
+ organization: zObjectIdFor("organization").optional(),
228
221
  }),
229
222
  };
230
223
  export const subscriptionSchema = {
@@ -249,34 +242,3 @@ export const updateDeviceSchema = {
249
242
  payment: z.record(z.string(), z.any()).optional(),
250
243
  }),
251
244
  };
252
-
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
- };
265
- export const uploadSingleImageSchema = {
266
- params: z.object({
267
- deviceId: zObjectIdFor("deviceId").openapi({
268
- description: "Device ObjectId",
269
- }),
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
- }),
282
- };