@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.
Files changed (171) hide show
  1. package/dist/src/accounts/accounts.controller.js +89 -0
  2. package/dist/src/accounts/accounts.route.js +101 -0
  3. package/dist/src/accounts/accounts.schemas.js +12 -0
  4. package/dist/src/accounts/accounts.service.js +65 -0
  5. package/dist/src/accounts/accounts.validation.js +99 -0
  6. package/dist/src/accounts/auth0.service.js +188 -0
  7. package/dist/src/config/config.js +48 -0
  8. package/dist/src/config/logger.js +27 -0
  9. package/dist/src/config/morgan.js +16 -0
  10. package/dist/src/config/passport.cjs +28 -0
  11. package/dist/src/config/roles.js +11 -0
  12. package/dist/src/config/tokens.cjs +10 -0
  13. package/dist/src/devices/devices.controller.js +172 -0
  14. package/dist/src/devices/devices.model.js +94 -0
  15. package/dist/src/devices/devices.route.js +153 -0
  16. package/dist/src/devices/devices.schemas.js +84 -0
  17. package/dist/src/devices/devices.service.js +198 -0
  18. package/dist/src/devices/devices.types.js +1 -0
  19. package/dist/src/devices/devices.validation.js +257 -0
  20. package/dist/src/devicesNotifications/devicesNotifications.controller.js +69 -0
  21. package/dist/src/devicesNotifications/devicesNotifications.model.js +39 -0
  22. package/dist/src/devicesNotifications/devicesNotifications.route.js +124 -0
  23. package/dist/src/devicesNotifications/devicesNotifications.schemas.js +10 -0
  24. package/dist/src/devicesNotifications/devicesNotifications.service.js +181 -0
  25. package/dist/src/devicesNotifications/devicesNotifications.validation.js +46 -0
  26. package/dist/src/email/email.service.js +580 -0
  27. package/dist/src/files/upload.service.js +124 -0
  28. package/dist/src/i18n/i18n.js +38 -0
  29. package/dist/src/i18n/saveMissingLocalJsonBackend.js +53 -0
  30. package/dist/src/i18n/types.js +1 -0
  31. package/dist/src/index.js +48 -0
  32. package/dist/src/iotdevice/iotdevice.controller.js +96 -0
  33. package/dist/src/iotdevice/iotdevice.model.js +17 -0
  34. package/dist/src/iotdevice/iotdevice.route.js +143 -0
  35. package/dist/src/iotdevice/iotdevice.schemas.js +60 -0
  36. package/dist/src/iotdevice/iotdevice.service.js +579 -0
  37. package/dist/src/iotdevice/iotdevice.types.js +1 -0
  38. package/dist/src/iotdevice/iotdevice.validation.js +54 -0
  39. package/dist/src/middlewares/auth.js +75 -0
  40. package/dist/src/middlewares/checkJwt.cjs +17 -0
  41. package/dist/src/middlewares/error.js +36 -0
  42. package/dist/src/middlewares/mongooseValidations/ensureSameOrganization.js +13 -0
  43. package/dist/src/middlewares/rateLimiter.js +7 -0
  44. package/dist/src/middlewares/validate.js +18 -0
  45. package/dist/src/middlewares/validateAction.js +35 -0
  46. package/dist/src/middlewares/validateAdmin.js +18 -0
  47. package/dist/src/middlewares/validateAi.js +16 -0
  48. package/dist/src/middlewares/validateCurrentAuthUser.js +17 -0
  49. package/dist/src/middlewares/validateCurrentUser.js +20 -0
  50. package/dist/src/middlewares/validateDevice.js +98 -0
  51. package/dist/src/middlewares/validateDeviceUserOrganization.js +26 -0
  52. package/dist/src/middlewares/validateOrganization.js +63 -0
  53. package/dist/src/middlewares/validateQuerySearchUserAndOrganization.js +44 -0
  54. package/dist/src/middlewares/validateTokens.js +23 -0
  55. package/dist/src/middlewares/validateUser.js +38 -0
  56. package/dist/src/middlewares/validateZod.js +33 -0
  57. package/dist/src/models/plugins/index.js +4 -0
  58. package/dist/src/models/plugins/paginate.plugin.js +117 -0
  59. package/dist/src/models/plugins/paginateNew.plugin.js +185 -0
  60. package/dist/src/models/plugins/simplePopulate.js +16 -0
  61. package/dist/src/models/plugins/toJSON.plugin.js +35 -0
  62. package/dist/src/organizations/organizations.controller.js +64 -0
  63. package/dist/src/organizations/organizations.model.js +41 -0
  64. package/dist/src/organizations/organizations.route.js +98 -0
  65. package/dist/src/organizations/organizations.schemas.js +7 -0
  66. package/dist/src/organizations/organizations.service.js +59 -0
  67. package/dist/src/organizations/organizations.validation.js +62 -0
  68. package/dist/src/pdf/pdf.controller.js +24 -0
  69. package/dist/src/pdf/pdf.route.js +22 -0
  70. package/dist/src/pdf/pdf.schemas.js +6 -0
  71. package/dist/src/pdf/pdf.service.js +65 -0
  72. package/dist/src/pdf/pdf.validation.js +27 -0
  73. package/dist/src/tokens/tokens.controller.js +60 -0
  74. package/dist/src/tokens/tokens.model.js +17 -0
  75. package/dist/src/tokens/tokens.route.js +52 -0
  76. package/dist/src/tokens/tokens.schemas.js +14 -0
  77. package/dist/src/tokens/tokens.service.js +30 -0
  78. package/dist/src/tokens/tokens.validation.js +9 -0
  79. package/dist/src/types/routeSpec.js +1 -0
  80. package/dist/src/users/users.controller.js +147 -0
  81. package/dist/src/users/users.model.js +50 -0
  82. package/dist/src/users/users.route.js +137 -0
  83. package/dist/src/users/users.schemas.js +69 -0
  84. package/dist/src/users/users.service.js +295 -0
  85. package/dist/src/users/users.types.js +1 -0
  86. package/dist/src/users/users.validation.js +144 -0
  87. package/dist/src/utils/ApiError.js +16 -0
  88. package/dist/src/utils/buildRouterAndDocs.js +72 -0
  89. package/dist/src/utils/catchAsync.js +4 -0
  90. package/dist/src/utils/comparePapers.service.js +32 -0
  91. package/dist/src/utils/deviceUtils.js +63 -0
  92. package/dist/src/utils/filterOptions.js +24 -0
  93. package/dist/src/utils/medicationName.js +10 -0
  94. package/dist/src/utils/pick.js +16 -0
  95. package/dist/src/utils/registerOpenApi.js +67 -0
  96. package/dist/src/utils/urlUtils.js +15 -0
  97. package/dist/src/utils/userName.js +22 -0
  98. package/dist/src/utils/zValidations.js +143 -0
  99. package/dist/src/validations/auth.validation.cjs +53 -0
  100. package/dist/src/validations/custom.validation.js +19 -0
  101. package/dist/src/validations/index.cjs +3 -0
  102. package/dist/tsconfig.tsbuildinfo +1 -0
  103. package/package.json +97 -80
  104. package/scripts/release-and-sync-paperless.mjs +137 -0
  105. package/src/accounts/accounts.controller.ts +1 -0
  106. package/src/accounts/accounts.service.ts +1 -0
  107. package/src/accounts/accounts.validation.ts +8 -5
  108. package/src/accounts/auth0.service.ts +55 -28
  109. package/src/config/config.ts +6 -0
  110. package/src/config/logger.ts +15 -9
  111. package/src/devices/devices.controller.ts +7 -1
  112. package/src/devices/devices.model.ts +4 -1
  113. package/src/devices/devices.schemas.ts +11 -9
  114. package/src/devices/devices.service.ts +1 -0
  115. package/src/devices/devices.types.ts +1 -0
  116. package/src/devices/devices.validation.ts +93 -32
  117. package/src/devicesNotifications/devicesNotifications.controller.ts +57 -28
  118. package/src/devicesNotifications/devicesNotifications.model.ts +20 -12
  119. package/src/devicesNotifications/devicesNotifications.service.ts +35 -17
  120. package/src/files/upload.service.ts +52 -28
  121. package/src/i18n/i18n.ts +1 -1
  122. package/src/i18n/types.ts +1 -0
  123. package/src/index.ts +47 -0
  124. package/src/iotdevice/iotdevice.controller.ts +1 -0
  125. package/src/iotdevice/iotdevice.model.ts +6 -3
  126. package/src/iotdevice/iotdevice.route.ts +85 -76
  127. package/src/iotdevice/iotdevice.service.ts +4 -3
  128. package/src/iotdevice/iotdevice.types.ts +6 -0
  129. package/src/middlewares/auth.ts +2 -8
  130. package/src/middlewares/error.ts +26 -12
  131. package/src/middlewares/mongooseValidations/ensureSameOrganization.ts +4 -3
  132. package/src/middlewares/validateAi.ts +17 -9
  133. package/src/middlewares/validateDevice.ts +1 -0
  134. package/src/middlewares/validateDeviceUserOrganization.ts +1 -0
  135. package/src/middlewares/validateOrganization.ts +1 -1
  136. package/src/middlewares/validateQuerySearchUserAndOrganization.ts +1 -0
  137. package/src/middlewares/validateTokens.ts +2 -1
  138. package/src/middlewares/validateUser.ts +1 -0
  139. package/src/middlewares/validateZod.ts +5 -5
  140. package/src/models/plugins/index.ts +5 -4
  141. package/src/models/plugins/paginate.plugin.ts +26 -16
  142. package/src/models/plugins/paginateNew.plugin.ts +33 -21
  143. package/src/models/plugins/simplePopulate.ts +8 -3
  144. package/src/models/plugins/toJSON.plugin.ts +12 -5
  145. package/src/organizations/organizations.controller.ts +1 -2
  146. package/src/organizations/organizations.model.ts +4 -4
  147. package/src/organizations/organizations.route.ts +1 -1
  148. package/src/organizations/organizations.service.ts +15 -6
  149. package/src/organizations/organizations.validation.ts +1 -1
  150. package/src/pdf/pdf.controller.ts +18 -1
  151. package/src/pdf/pdf.service.ts +25 -16
  152. package/src/tokens/tokens.controller.ts +6 -8
  153. package/src/tokens/tokens.model.ts +3 -1
  154. package/src/tokens/tokens.service.ts +3 -2
  155. package/src/types/express.d.ts +17 -0
  156. package/src/types/mongoose.d.ts +22 -0
  157. package/src/users/users.controller.ts +8 -9
  158. package/src/users/users.model.ts +6 -5
  159. package/src/users/users.route.ts +0 -1
  160. package/src/users/users.service.ts +16 -0
  161. package/src/users/users.types.ts +1 -0
  162. package/src/users/users.validation.ts +6 -2
  163. package/src/utils/ApiError.ts +8 -1
  164. package/src/utils/buildRouterAndDocs.ts +57 -22
  165. package/src/utils/catchAsync.ts +27 -3
  166. package/src/utils/medicationName.ts +5 -4
  167. package/src/utils/pick.ts +5 -1
  168. package/src/utils/registerOpenApi.ts +75 -24
  169. package/src/utils/userName.ts +1 -0
  170. package/src/utils/zValidations.ts +98 -27
  171. package/tsconfig.json +13 -4
@@ -0,0 +1,11 @@
1
+ const allRoles = {
2
+ user: [],
3
+ admin: ['getUsers', 'manageUsers'],
4
+ patient: [],
5
+ };
6
+ export const roles = Object.keys(allRoles);
7
+ export const roleRights = new Map(Object.entries(allRoles));
8
+ export default {
9
+ roles,
10
+ roleRights,
11
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ const tokenTypes = {
3
+ ACCESS: 'access',
4
+ REFRESH: 'refresh',
5
+ RESET_PASSWORD: 'resetPassword',
6
+ VERIFY_EMAIL: 'verifyEmail',
7
+ };
8
+ module.exports = {
9
+ tokenTypes,
10
+ };
@@ -0,0 +1,172 @@
1
+ // @ts-nocheck
2
+ import httpStatus from "http-status";
3
+ import mongoose from "mongoose";
4
+ import pick from "../utils/pick.js";
5
+ import ApiError from "../utils/ApiError.js";
6
+ import catchAsync from "../utils/catchAsync.js";
7
+ import * as devicesService from "./devices.service.js";
8
+ import * as iotDevicesService from "../iotdevice/iotdevice.service.js";
9
+ import { filterOptions } from "../utils/filterOptions.js";
10
+ import { isAdmin } from "../middlewares/validateAdmin.js";
11
+ const createEntry = catchAsync(async (req, res) => {
12
+ const user = await devicesService.createDevice(req.body);
13
+ res.status(httpStatus.CREATED).send(user);
14
+ });
15
+ const registerDevice = catchAsync(async (req, res) => {
16
+ const checkDevice = await devicesService.getDeviceByDeviceId(req.params.deviceId);
17
+ if (checkDevice) {
18
+ const iotDevices = await iotDevicesService.activateDevice(req.params.deviceId, req.body.organization, true);
19
+ // Get Events if update is happening
20
+ if (iotDevices.activation_status === "success") {
21
+ if (checkDevice.organization.equals(req.body.organization)) {
22
+ res.status(httpStatus.CONFLICT).send({
23
+ message: "Device is already existing and registered in this organization, no hardware reset detected",
24
+ device: checkDevice,
25
+ });
26
+ }
27
+ else {
28
+ throw new ApiError(httpStatus.CONFLICT, "Device is already existing and registered in another organization, no hardware reset detected");
29
+ }
30
+ }
31
+ else {
32
+ // Delete device if already existing but not activation_status
33
+ await devicesService.deleteById(checkDevice.id, false);
34
+ }
35
+ res.status(httpStatus.CREATED).send(iotDevices);
36
+ }
37
+ else {
38
+ const devices = await devicesService.registerDevice({
39
+ id: req.params.deviceId,
40
+ body: req.body,
41
+ });
42
+ res.status(httpStatus.CREATED).send(devices);
43
+ }
44
+ });
45
+ const getEntries = catchAsync(async (req, res) => {
46
+ const filter = pick(req.query, ["name", "role"]);
47
+ const options = pick(req.query, ["sortBy", "limit", "page"]);
48
+ const result = await devicesService.queryDevices(filter, options);
49
+ res.send(result);
50
+ });
51
+ const queryDevicesByUser = catchAsync(async (req, res) => {
52
+ const filter = pick(req.query, ["name", "role", "organization", "patient"]);
53
+ const options = pick(req.query, ["sortBy", "limit", "page"]);
54
+ options.limit = 1000;
55
+ const filteredOptions = filterOptions(req.query, filter, {
56
+ objectIds: ["_id", "patient"],
57
+ search: [
58
+ "meta.name",
59
+ "deviceId",
60
+ "payment.id",
61
+ "payment.customer",
62
+ "kind",
63
+ "patientData.meta.firstName",
64
+ "patientData.meta.lastName",
65
+ ],
66
+ });
67
+ const optionsPopulate = {
68
+ ...options,
69
+ populate: "patientData",
70
+ };
71
+ const result = await devicesService.queryDevicesByUser(filteredOptions, optionsPopulate);
72
+ if (req.currentUser?.role &&
73
+ req.currentUser.role === "onlyself" &&
74
+ result?.results) {
75
+ const currentUserId = new mongoose.Types.ObjectId(req.currentUser.id);
76
+ result.results = result.results.filter((user) => {
77
+ // Prefer value comparison over object reference comparison
78
+ return currentUserId.equals(user.patient);
79
+ // Alternatively:
80
+ // return String(user.patient) === String(req.currentUser.id);
81
+ });
82
+ }
83
+ res.send(result);
84
+ });
85
+ const getEntry = catchAsync(async (req, res) => {
86
+ const device = await devicesService.getByIdWithIoT(req.params.deviceId);
87
+ if (!device) {
88
+ throw new ApiError(httpStatus.NOT_FOUND, "Device not found");
89
+ }
90
+ res.send(device);
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
+ const updateEntry = catchAsync(async (req, res) => {
100
+ // TODO: Remove in newer versions
101
+ const { updatedAt, createdAt, payment, iotDevice, ...newBody } = req.body;
102
+ const body = {
103
+ ...newBody,
104
+ shadow: typeof req.body?.shadow === "object" ? req.body.shadow : undefined,
105
+ };
106
+ if (isAdmin(res.req.auth) && payment) {
107
+ body.payment = payment;
108
+ }
109
+ const device = await devicesService.updateById(req.params.deviceId, body); //TODO: remove shadow
110
+ if (isAdmin(res.req.auth) && iotDevice && device.deviceId) {
111
+ const iotDeviceUpdate = await iotDevicesService.updateDevice(device.deviceId, iotDevice);
112
+ }
113
+ res.send(device);
114
+ });
115
+ const getEvents = catchAsync(async (req, res) => {
116
+ const device = await devicesService.getById(req.params.deviceId);
117
+ const events = await iotDevicesService.getEvents({
118
+ ...req.query,
119
+ createdAt: device.createdAt,
120
+ DeviceId: device.deviceId,
121
+ });
122
+ res.send({ ...events, device });
123
+ });
124
+ const deleteEntry = catchAsync(async (req, res) => {
125
+ const entry = await devicesService.deleteById(req.params.deviceId);
126
+ //res.status(httpStatus.NO_CONTENT).send();
127
+ res.send(entry);
128
+ });
129
+ const pingDevice = catchAsync(async (req, res) => {
130
+ const device = await devicesService.getByIdWithIoT(req.params.deviceId);
131
+ const ping = await iotDevicesService.pingDevice(device.deviceId, req.query);
132
+ res.send({ device, ping });
133
+ });
134
+ export const resetDevice = catchAsync(async (req, res) => {
135
+ const device = await devicesService.getByIdWithIoT(req.params.deviceId);
136
+ const reset = await iotDevicesService.resetDevice(device.deviceId, req.body);
137
+ res.send({ device, reset });
138
+ });
139
+ const ledLight = catchAsync(async (req, res) => {
140
+ const device = await devicesService.getByIdWithIoT(req.params.deviceId);
141
+ const ping = await iotDevicesService.ledLightHint(device.deviceId, req.body);
142
+ res.send({ device, ping });
143
+ });
144
+ const rebootDevice = catchAsync(async (req, res) => {
145
+ const device = await devicesService.getByIdWithIoT(req.params.deviceId);
146
+ const reboot = await iotDevicesService.rebootDevice(device.deviceId);
147
+ res.send({ device, reboot });
148
+ });
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, };
@@ -0,0 +1,94 @@
1
+ // @ts-nocheck
2
+ import mongoose, { Schema } from "mongoose";
3
+ import { toJSON, paginate } from "../models/plugins/index.js";
4
+ import { ensureSameOrganization } from "../middlewares/mongooseValidations/ensureSameOrganization.js";
5
+ const deviceSchema = new Schema({
6
+ name: {
7
+ type: String,
8
+ },
9
+ meta: { type: Object },
10
+ organization: {
11
+ type: Schema.Types.ObjectId,
12
+ ref: "Organization",
13
+ immutable: true,
14
+ },
15
+ patient: { type: Schema.Types.ObjectId, ref: "User" },
16
+ paper: { type: mongoose.Schema.Types.ObjectId, ref: "Paper" },
17
+ timezone: { type: String },
18
+ deviceId: { type: String, immutable: true },
19
+ kind: { type: String, immutable: true },
20
+ eventDate: { type: Date },
21
+ payment: { type: Object },
22
+ }, {
23
+ timestamps: true,
24
+ toJSON: { virtuals: true },
25
+ toObject: { virtuals: true },
26
+ });
27
+ deviceSchema.virtual("patientData", {
28
+ ref: "User",
29
+ localField: "patient",
30
+ foreignField: "_id",
31
+ justOne: true,
32
+ });
33
+ function addIotDevice(schema) {
34
+ schema
35
+ .virtual("loadedAt")
36
+ .get(function () {
37
+ return this._loadedAt;
38
+ })
39
+ .set(function (v) {
40
+ this._loadedAt = v;
41
+ });
42
+ schema.post(["find", "findOne"], function (docs) {
43
+ // nothing was found, bail out
44
+ if (!docs) {
45
+ return;
46
+ }
47
+ // normalize to array
48
+ const docsArray = Array.isArray(docs) ? docs : [docs];
49
+ const now = new Date();
50
+ for (const doc of docsArray) {
51
+ if (doc) {
52
+ doc.loadedAt = now;
53
+ }
54
+ }
55
+ });
56
+ }
57
+ /**
58
+ * Ensure patient is member of the same organization
59
+ */
60
+ deviceSchema.pre("save", async function (next) {
61
+ if (!this.patient)
62
+ return next();
63
+ try {
64
+ await ensureSameOrganization(this.patient, this.organization, mongoose.model("User"));
65
+ next();
66
+ }
67
+ catch (err) {
68
+ next(err);
69
+ }
70
+ });
71
+ deviceSchema.pre("findOneAndUpdate", async function (next) {
72
+ const update = this.getUpdate();
73
+ if (!update?.patient)
74
+ return next();
75
+ try {
76
+ // need org from the existing doc
77
+ const device = await this.model
78
+ .findOne(this.getQuery())
79
+ .select("organization");
80
+ await ensureSameOrganization(update.patient, device.organization, mongoose.model("User"));
81
+ next();
82
+ }
83
+ catch (err) {
84
+ next(err);
85
+ }
86
+ });
87
+ // Add the IoT device functionality
88
+ addIotDevice(deviceSchema);
89
+ // Add plugins that convert mongoose to JSON and enable pagination
90
+ deviceSchema.plugin((schema) => toJSON(schema, true));
91
+ deviceSchema.plugin(paginate);
92
+ const Devices = mongoose.models.Device ||
93
+ mongoose.model("Device", deviceSchema);
94
+ export default Devices;
@@ -0,0 +1,153 @@
1
+ import { Router } from "express";
2
+ import multer from "multer";
3
+ import buildRouterAndDocs from "../utils/buildRouterAndDocs.js";
4
+ import auth from "../middlewares/auth.js";
5
+ import { validateBodyOrganization, } from "../middlewares/validateOrganization.js";
6
+ import { validateQuerySearchUserAndOrganization } from "../middlewares/validateQuerySearchUserAndOrganization.js";
7
+ import { validateDevice } from "../middlewares/validateDevice.js";
8
+ import * as devicesController from "./devices.controller.js";
9
+ import { resetDevice } from "./devices.controller.js";
10
+ import { createDeviceSchema, queryDevicesSchema, getDeviceSchema, 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";
12
+ import { validateOrganizationDelete, } from "../middlewares/validateAction.js";
13
+ export const devicesRouteSpecs = [
14
+ {
15
+ method: "post",
16
+ path: "/",
17
+ validate: [
18
+ auth("manageUsers"),
19
+ validateBodyOrganization,
20
+ validateOrganizationDelete,
21
+ ],
22
+ requestSchema: createDeviceSchema,
23
+ responseSchema: deviceResponseSchema,
24
+ handler: devicesController.createEntry,
25
+ summary: "Create a new device",
26
+ description: "Create a new device and associate it with the current organization.",
27
+ },
28
+ {
29
+ method: "get",
30
+ path: "/",
31
+ validate: [auth("getUsers"), validateQuerySearchUserAndOrganization],
32
+ requestSchema: queryDevicesSchema,
33
+ responseSchema: devicesResponseSchema,
34
+ handler: devicesController.queryDevicesByUser,
35
+ summary: "Query devices by user",
36
+ description: "Retrieve a paginated list of devices visible to the authenticated user.",
37
+ },
38
+ {
39
+ method: "get",
40
+ path: "/events/:deviceId",
41
+ validate: [auth("getUsers"), validateDevice],
42
+ requestSchema: getEventsSchema,
43
+ responseSchema: eventResponseSchema.array(),
44
+ handler: devicesController.getEvents,
45
+ summary: "Get events for a device",
46
+ description: "Fetch a chronological list of events generated by the specified device.",
47
+ },
48
+ {
49
+ method: "get",
50
+ path: "/:deviceId",
51
+ validate: [auth("getUsers"), validateDevice],
52
+ requestSchema: getDeviceSchema,
53
+ responseSchema: deviceResponseSchema,
54
+ handler: devicesController.getEntry,
55
+ summary: "Get a device by ID",
56
+ description: "Retrieve detailed information for the device identified by its ID.",
57
+ },
58
+ {
59
+ method: "post",
60
+ path: "/:deviceId",
61
+ handler: devicesController.deleteEntry,
62
+ summary: "Delete a device",
63
+ description: "Remove the specified device from the system.",
64
+ },
65
+ {
66
+ method: "post",
67
+ path: "/registerdevice/:deviceId",
68
+ validate: [
69
+ auth("getUsers"),
70
+ validateBodyOrganization,
71
+ validateOrganizationDelete,
72
+ ],
73
+ requestSchema: registerDeviceSchema,
74
+ responseSchema: genericResponseSchema,
75
+ handler: devicesController.registerDevice,
76
+ summary: "Register a device",
77
+ description: "Associate an existing device with the authenticated organization.",
78
+ },
79
+ {
80
+ method: "post",
81
+ path: "/ledlight/:deviceId",
82
+ validate: [auth("getUsers"), validateDevice],
83
+ requestSchema: ledLightSchema,
84
+ responseSchema: genericResponseSchema,
85
+ handler: devicesController.ledLight,
86
+ summary: "Set LED light on device",
87
+ description: "Turn the device’s LED on or off, or set its color/brightness.",
88
+ memoOnly: true,
89
+ },
90
+ {
91
+ method: "get",
92
+ path: "/ping/:deviceId",
93
+ validate: [auth("getUsers"), validateDevice],
94
+ requestSchema: pingDeviceSchema,
95
+ responseSchema: genericResponseSchema,
96
+ handler: devicesController.pingDevice,
97
+ summary: "Ping a device",
98
+ description: "Check connectivity and round-trip time to the specified device.",
99
+ },
100
+ {
101
+ method: "post",
102
+ path: "/reset/:deviceId",
103
+ validate: [auth("getUsers"), validateDevice, validateOrganizationDelete],
104
+ requestSchema: resetDeviceSchema,
105
+ responseSchema: resetResponseSchema,
106
+ handler: resetDevice,
107
+ summary: "Reset the sensors of a device",
108
+ description: "Remotely reset all sensors on the target device to factory defaults.",
109
+ },
110
+ {
111
+ method: "post",
112
+ path: "/reboot/:deviceId",
113
+ validate: [auth("getUsers"), validateDevice, validateOrganizationDelete],
114
+ requestSchema: rebootDeviceSchema,
115
+ responseSchema: genericResponseSchema,
116
+ handler: devicesController.rebootDevice,
117
+ summary: "Reboot a device",
118
+ description: "Initiate a remote reboot of the specified device.",
119
+ },
120
+ {
121
+ method: "get",
122
+ path: "/image/:deviceId/:uuid",
123
+ validate: [auth("getUsers"), validateDevice],
124
+ requestSchema: getImageSchema,
125
+ responseSchema: imageResponseSchema,
126
+ handler: devicesController.getImageById,
127
+ summary: "Fetch an image by UUID",
128
+ description: "Download a previously uploaded image for the device by its UUID.",
129
+ },
130
+ {
131
+ method: "post",
132
+ path: "/updateSingleImageMeta/:deviceId",
133
+ validate: [auth("getUsers"), validateDevice],
134
+ requestSchema: updateSingleImageMetaSchema,
135
+ responseSchema: uploadResponseSchema,
136
+ handler: devicesController.updateSingleImageMeta,
137
+ summary: "Update image metadata",
138
+ description: "Modify metadata (e.g., title, tags) for an existing device image.",
139
+ },
140
+ {
141
+ method: "post",
142
+ path: "/uploadSingleImage/:deviceId",
143
+ validate: [auth("getUsers"), multer().array("picture", 2), validateDevice],
144
+ requestSchema: uploadSingleImageSchema,
145
+ responseSchema: uploadResponseSchema,
146
+ handler: devicesController.uploadSingleImage,
147
+ summary: "Upload one or two images",
148
+ description: "Upload up to two image files to the device for processing or storage.",
149
+ },
150
+ ];
151
+ const router = Router();
152
+ buildRouterAndDocs(router, devicesRouteSpecs, "/devices", ["Devices"]);
153
+ export default router;
@@ -0,0 +1,84 @@
1
+ import { z } from "zod";
2
+ import { zObjectId } from "../utils/zValidations.js";
3
+ export const deviceResponseSchema = z.object({
4
+ id: z.string(),
5
+ name: z.string(),
6
+ serialNumber: z.string().optional(),
7
+ status: z.enum(["online", "offline", "error"]),
8
+ createdAt: z.string(), // ISO timestamp
9
+ updatedAt: z.string(), // ISO timestamp
10
+ // ...other device fields...
11
+ });
12
+ export const devicesResponseSchema = deviceResponseSchema.array();
13
+ export const eventResponseSchema = z.object({
14
+ id: z.string(),
15
+ deviceId: z.string(),
16
+ type: z.string(),
17
+ payload: z.record(z.string(), z.any()),
18
+ timestamp: z.string(), // ISO timestamp
19
+ // ...other event fields...
20
+ });
21
+ export const genericResponseSchema = z
22
+ .record(z.string(), z.any())
23
+ .openapi({ description: "Generic response payload" });
24
+ export const imageResponseSchema = z.object({
25
+ uuid: z.string(),
26
+ deviceId: z.string(),
27
+ url: z.string().url(),
28
+ metadata: z
29
+ .object({
30
+ width: z.number().optional(),
31
+ height: z.number().optional(),
32
+ size: z.number().optional(), // bytes
33
+ // ...other metadata fields...
34
+ })
35
+ .optional(),
36
+ uploadedAt: z.string(), // ISO timestamp
37
+ });
38
+ export const checkoutSessionResponseSchema = z.object({
39
+ sessionId: z.string(),
40
+ url: z.string().url(),
41
+ });
42
+ export const customerPortalSessionResponseSchema = z.object({
43
+ url: z.string().url(),
44
+ });
45
+ export const subscriptionResponseSchema = z.object({
46
+ status: z.enum(["active", "past_due", "canceled", "unpaid"]),
47
+ currentPeriodStart: z.string(), // ISO timestamp
48
+ currentPeriodEnd: z.string(), // ISO timestamp
49
+ plan: z.object({
50
+ id: z.string(),
51
+ amount: z.number(),
52
+ currency: z.string(),
53
+ interval: z.enum(["day", "week", "month", "year"]),
54
+ // ...other plan fields...
55
+ }),
56
+ // ...other subscription fields...
57
+ });
58
+ export const uploadResponseSchema = z
59
+ .object({
60
+ key: z.string().optional(),
61
+ similarityPercentage: z.number().nullable().optional(),
62
+ skippedUpload: z.boolean().optional(),
63
+ })
64
+ .openapi({ description: "Upload response payload" });
65
+ export const pingDeviceSchema = {
66
+ params: z.object({
67
+ deviceId: zObjectId.openapi({ description: "Device ObjectId" }),
68
+ }),
69
+ query: z.object({
70
+ dataResponse: z
71
+ .string()
72
+ .openapi({ description: "Data response", example: "false" }),
73
+ }),
74
+ };
75
+ export const resetResponseSchema = z.object({
76
+ message: z.string().openapi({
77
+ example: "Reset all Variables and Memory and reboot Device: nrf-352656106701140",
78
+ description: "Response message from reset operation",
79
+ }),
80
+ success: z.boolean().openapi({
81
+ example: true,
82
+ description: "Indicates if the reset operation was successful",
83
+ }),
84
+ });