@internetderdinge/api 1.229.0 → 1.229.1

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 (167) 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 +18 -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 +28 -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 +124 -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/package.json +13 -3
  103. package/scripts/release-and-sync-paperless.mjs +135 -0
  104. package/src/accounts/accounts.controller.ts +1 -0
  105. package/src/accounts/accounts.service.ts +1 -0
  106. package/src/accounts/accounts.validation.ts +6 -3
  107. package/src/accounts/auth0.service.ts +55 -28
  108. package/src/config/config.ts +6 -0
  109. package/src/config/logger.ts +15 -9
  110. package/src/devices/devices.controller.ts +7 -1
  111. package/src/devices/devices.model.ts +4 -1
  112. package/src/devices/devices.schemas.ts +10 -8
  113. package/src/devices/devices.service.ts +1 -0
  114. package/src/devices/devices.types.ts +1 -0
  115. package/src/devices/devices.validation.ts +85 -23
  116. package/src/devicesNotifications/devicesNotifications.controller.ts +57 -28
  117. package/src/devicesNotifications/devicesNotifications.model.ts +20 -12
  118. package/src/devicesNotifications/devicesNotifications.service.ts +35 -17
  119. package/src/files/upload.service.ts +52 -28
  120. package/src/i18n/i18n.ts +1 -1
  121. package/src/i18n/types.ts +1 -0
  122. package/src/index.ts +47 -0
  123. package/src/iotdevice/iotdevice.controller.ts +1 -0
  124. package/src/iotdevice/iotdevice.model.ts +6 -3
  125. package/src/iotdevice/iotdevice.route.ts +85 -76
  126. package/src/iotdevice/iotdevice.service.ts +4 -3
  127. package/src/iotdevice/iotdevice.types.ts +6 -0
  128. package/src/middlewares/auth.ts +2 -8
  129. package/src/middlewares/error.ts +26 -12
  130. package/src/middlewares/mongooseValidations/ensureSameOrganization.ts +4 -3
  131. package/src/middlewares/validateAi.ts +17 -9
  132. package/src/middlewares/validateDevice.ts +1 -0
  133. package/src/middlewares/validateDeviceUserOrganization.ts +1 -0
  134. package/src/middlewares/validateOrganization.ts +1 -1
  135. package/src/middlewares/validateQuerySearchUserAndOrganization.ts +1 -0
  136. package/src/middlewares/validateTokens.ts +2 -1
  137. package/src/middlewares/validateUser.ts +1 -0
  138. package/src/models/plugins/index.ts +5 -4
  139. package/src/models/plugins/paginate.plugin.ts +26 -16
  140. package/src/models/plugins/paginateNew.plugin.ts +33 -21
  141. package/src/models/plugins/simplePopulate.ts +8 -3
  142. package/src/models/plugins/toJSON.plugin.ts +12 -5
  143. package/src/organizations/organizations.controller.ts +1 -2
  144. package/src/organizations/organizations.model.ts +4 -4
  145. package/src/organizations/organizations.route.ts +1 -1
  146. package/src/organizations/organizations.service.ts +15 -6
  147. package/src/pdf/pdf.controller.ts +18 -1
  148. package/src/pdf/pdf.service.ts +25 -16
  149. package/src/tokens/tokens.controller.ts +6 -8
  150. package/src/tokens/tokens.model.ts +3 -1
  151. package/src/tokens/tokens.service.ts +3 -2
  152. package/src/types/express.d.ts +17 -0
  153. package/src/types/mongoose.d.ts +22 -0
  154. package/src/users/users.controller.ts +8 -9
  155. package/src/users/users.model.ts +6 -5
  156. package/src/users/users.route.ts +0 -1
  157. package/src/users/users.service.ts +16 -0
  158. package/src/users/users.types.ts +1 -0
  159. package/src/users/users.validation.ts +6 -2
  160. package/src/utils/ApiError.ts +8 -1
  161. package/src/utils/buildRouterAndDocs.ts +56 -21
  162. package/src/utils/catchAsync.ts +27 -3
  163. package/src/utils/medicationName.ts +5 -4
  164. package/src/utils/pick.ts +5 -1
  165. package/src/utils/userName.ts +1 -0
  166. package/src/utils/zValidations.ts +78 -26
  167. package/tsconfig.json +13 -4
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ const jwt = require('express-jwt');
3
+ const jwtAuthz = require('express-jwt-authz');
4
+ const jwksRsa = require('jwks-rsa');
5
+ const checkJwt = jwt({
6
+ secret: jwksRsa.expressJwtSecret({
7
+ cache: true,
8
+ rateLimit: true,
9
+ jwksRequestsPerMinute: 5,
10
+ jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`,
11
+ }),
12
+ // Validate the audience and the issuer.
13
+ audience: process.env.AUTH0_AUDIENCE,
14
+ issuer: `https://${process.env.AUTH0_DOMAIN}/`,
15
+ algorithms: ['RS256'],
16
+ });
17
+ module.exports = checkJwt;
@@ -0,0 +1,36 @@
1
+ // @ts-nocheck
2
+ import mongoose from "mongoose";
3
+ import httpStatus from "http-status";
4
+ import config from "../config/config";
5
+ import logger from "../config/logger";
6
+ import ApiError from "../utils/ApiError";
7
+ export const errorConverter = (err, req, res, next) => {
8
+ let error = err;
9
+ if (!(error instanceof ApiError)) {
10
+ const statusCode = error.statusCode ||
11
+ (error instanceof mongoose.Error
12
+ ? httpStatus.BAD_REQUEST
13
+ : httpStatus.INTERNAL_SERVER_ERROR);
14
+ const message = error.message || httpStatus[statusCode];
15
+ error = new ApiError(statusCode, message, false, err.stack);
16
+ }
17
+ next(error);
18
+ };
19
+ // eslint-disable-next-line no-unused-vars
20
+ export const errorHandler = (err, req, res, next) => {
21
+ let { statusCode, message } = err;
22
+ if (config.env === "production" && !err.isOperational) {
23
+ statusCode = httpStatus.INTERNAL_SERVER_ERROR;
24
+ message = httpStatus[httpStatus.INTERNAL_SERVER_ERROR];
25
+ }
26
+ res.locals.errorMessage = err.message;
27
+ const response = {
28
+ code: statusCode,
29
+ message,
30
+ ...(config.env === "development" && { stack: err.stack }),
31
+ };
32
+ if (config.env === "development") {
33
+ logger.error(err);
34
+ }
35
+ res.status(statusCode).send({ ...response, raw: err.raw || null });
36
+ };
@@ -0,0 +1,13 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Throws if patient doesn’t exist or isn’t in the given org.
4
+ */
5
+ export async function ensureSameOrganization(patientId, organizationId, UserModel) {
6
+ // console.log('ensureSameOrganization', patientId, organizationId);
7
+ const user = await UserModel.findById(patientId).select("organization");
8
+ if (!user)
9
+ throw new Error("Patient not found");
10
+ if (!user.organization.equals(organizationId)) {
11
+ throw new Error("Patient must belong to the same organization");
12
+ }
13
+ }
@@ -0,0 +1,7 @@
1
+ import rateLimit from 'express-rate-limit';
2
+ const authLimiter = rateLimit({
3
+ windowMs: 15 * 60 * 1000,
4
+ max: 20,
5
+ skipSuccessfulRequests: true,
6
+ });
7
+ export { authLimiter };
@@ -0,0 +1,18 @@
1
+ import Joi from 'joi';
2
+ import httpStatus from 'http-status';
3
+ import pick from '../utils/pick';
4
+ import ApiError from '../utils/ApiError';
5
+ const validate = (schema) => (req, res, next) => {
6
+ const validSchema = pick(schema, ['params', 'query', 'body']);
7
+ const object = pick(req, Object.keys(validSchema));
8
+ const { value, error } = Joi.compile(validSchema)
9
+ .prefs({ errors: { label: 'key' } })
10
+ .validate(object);
11
+ if (error) {
12
+ const errorMessage = error.details.map((details) => details.message).join(', ');
13
+ return next(new ApiError(httpStatus.BAD_REQUEST, errorMessage));
14
+ }
15
+ Object.assign(req, value);
16
+ return next();
17
+ };
18
+ export default validate;
@@ -0,0 +1,35 @@
1
+ import httpStatus from 'http-status';
2
+ import ApiError from '../utils/ApiError';
3
+ import { isAdmin } from './validateAdmin';
4
+ export const validateOrganizationUpdate = async (req, res, next) => {
5
+ if (isAdmin(res.req.auth)) {
6
+ next();
7
+ }
8
+ else {
9
+ if (!req.currentUser) {
10
+ next(new ApiError(httpStatus.INTERNAL_SERVER_ERROR, 'Current user not set in request'));
11
+ return;
12
+ }
13
+ if (req.currentUser.role && req.currentUser.role === 'onlyself') {
14
+ next(new ApiError(httpStatus.FORBIDDEN, 'User does not have sufficient permissions in the organization to update'));
15
+ return;
16
+ }
17
+ next();
18
+ }
19
+ };
20
+ export const validateOrganizationDelete = async (req, res, next) => {
21
+ if (isAdmin(res.req.auth)) {
22
+ next();
23
+ }
24
+ else {
25
+ if (!req.currentUser) {
26
+ next(new ApiError(httpStatus.INTERNAL_SERVER_ERROR, 'Current user not set in request'));
27
+ return;
28
+ }
29
+ if (req.currentUser.role && req.currentUser.role === 'onlyself') {
30
+ next(new ApiError(httpStatus.FORBIDDEN, 'User does not have sufficient permissions in the organization to update'));
31
+ return;
32
+ }
33
+ next();
34
+ }
35
+ };
@@ -0,0 +1,18 @@
1
+ import httpStatus from 'http-status';
2
+ import ApiError from '../utils/ApiError';
3
+ const isAdmin = (user) => {
4
+ //return false;
5
+ if (!user)
6
+ return false;
7
+ // return false; // TODO: Remove this line when the user object is properly defined
8
+ return user['https://memo.wirewire.de/roles'] ? user['https://memo.wirewire.de/roles'].includes('admin') : false;
9
+ };
10
+ const validateAdmin = async (req, res, next) => {
11
+ if (isAdmin(req.auth)) {
12
+ next();
13
+ }
14
+ else {
15
+ next(new ApiError(httpStatus.FORBIDDEN, 'User is not part of the admin group (validateAdmin)'));
16
+ }
17
+ };
18
+ export { isAdmin, validateAdmin };
@@ -0,0 +1,16 @@
1
+ import httpStatus from "http-status";
2
+ import ApiError from "../utils/ApiError"; // keep .cjs import
3
+ // you can adjust the User source if your auth payload differs
4
+ export const isAiRole = (user) => {
5
+ if (!user)
6
+ return false;
7
+ return user["https://memo.wirewire.de/roles"]?.includes("ai") ?? false;
8
+ };
9
+ export const validateAiRole = async (req, res, next) => {
10
+ // assuming the auth payload is attached to req.auth
11
+ console.log("Validating AI role for user:", req.auth);
12
+ if (isAiRole(req.auth)) {
13
+ return next();
14
+ }
15
+ return next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the ai group (validateAi)"));
16
+ };
@@ -0,0 +1,17 @@
1
+ import httpStatus from 'http-status';
2
+ import ApiError from '../utils/ApiError';
3
+ const getCurrentAuthUser = async (req, res, next) => {
4
+ if (res.req.auth.sub !== req.params.notificationId) {
5
+ next(new ApiError(httpStatus.BAD_REQUEST, 'Not allowed to access'));
6
+ return;
7
+ }
8
+ next();
9
+ };
10
+ export const validateParamsAccount = async (req, res, next) => {
11
+ if (res.req.auth.sub !== req.params.accountId) {
12
+ next(new ApiError(httpStatus.BAD_REQUEST, 'Not allowed to access'));
13
+ return;
14
+ }
15
+ next();
16
+ };
17
+ export default getCurrentAuthUser;
@@ -0,0 +1,20 @@
1
+ import httpStatus from "http-status";
2
+ import ApiError from "../utils/ApiError";
3
+ import userService from "../users/users.service";
4
+ const getCurrentUser = async (req, res, next) => {
5
+ try {
6
+ // TODO: Check if the user is logged in
7
+ const currentUser = await userService.getUserByOwner(res.req.auth.sub, req.body.organization);
8
+ if (!currentUser) {
9
+ next(new ApiError(httpStatus.BAD_REQUEST, "User does not exist"));
10
+ return;
11
+ }
12
+ req.currentUser = currentUser;
13
+ next();
14
+ }
15
+ catch (error) {
16
+ console.error("Error validating user service:", error);
17
+ next(new ApiError(httpStatus.INTERNAL_SERVER_ERROR, "Failed to validate user service"));
18
+ }
19
+ };
20
+ export default getCurrentUser;
@@ -0,0 +1,98 @@
1
+ // @ts-nocheck
2
+ import httpStatus from "http-status";
3
+ import ApiError from "../utils/ApiError";
4
+ import devicesService from "../devices/devices.service";
5
+ import { isAdmin } from "./validateAdmin";
6
+ import usersService from "../users/users.service";
7
+ const validateDeviceIsInOrganization = async (req, res, next) => {
8
+ const deviceId = (req.body?.deviceId ||
9
+ req.params?.deviceId ||
10
+ req.query?.deviceId);
11
+ if (!deviceId) {
12
+ next();
13
+ return;
14
+ }
15
+ const device = await devicesService.getById(deviceId);
16
+ if (!device) {
17
+ next(new ApiError(httpStatus.NOT_FOUND, "Device not found"));
18
+ return;
19
+ }
20
+ if (isAdmin(res.req.auth)) {
21
+ next();
22
+ return;
23
+ }
24
+ const currentUser = await usersService.getUserByOwner(res.req.auth.sub, device.organization);
25
+ if (!currentUser) {
26
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization for this device"));
27
+ return;
28
+ }
29
+ if (req.body?.organization &&
30
+ req.body.organization.toString() !== device.organization?.toString()) {
31
+ next(new ApiError(httpStatus.FORBIDDEN, "Device is not part of the provided organization"));
32
+ return;
33
+ }
34
+ req.currentUser = currentUser;
35
+ next();
36
+ };
37
+ const validateDevice = async (req, res, next) => {
38
+ if (isAdmin(res.req.auth)) {
39
+ next();
40
+ }
41
+ else {
42
+ const currentDevice = await devicesService.getById(req.params.deviceId);
43
+ if (!currentDevice) {
44
+ next(new ApiError(httpStatus.NOT_FOUND, "Device not found"));
45
+ return;
46
+ }
47
+ const currentUser = await usersService.getUserByOwner(res.req.auth.sub, currentDevice.organization);
48
+ if (!currentUser) {
49
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization which has access to the device (validateDevice)"));
50
+ return;
51
+ }
52
+ req.currentUser = currentUser;
53
+ next();
54
+ }
55
+ };
56
+ const validateDeviceQuery = async (req, res, next) => {
57
+ if (isAdmin(res.req.auth)) {
58
+ next();
59
+ }
60
+ else {
61
+ // console.log('Validating device query for user:', res.req.auth.sub, req.query);
62
+ const currentDevice = await devicesService.getById(req.query.deviceId);
63
+ if (!currentDevice) {
64
+ next(new ApiError(httpStatus.NOT_FOUND, "Device not found"));
65
+ return;
66
+ }
67
+ const currentUser = await usersService.getUserByOwner(res.req.auth.sub, currentDevice.organization);
68
+ if (!currentUser) {
69
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization (validateDeviceOrOrganizationQuery)"));
70
+ return;
71
+ }
72
+ req.currentUser = currentUser;
73
+ next();
74
+ }
75
+ };
76
+ const validateDeviceOrOrganizationQuery = async (req, res, next) => {
77
+ if (isAdmin(res.req.auth)) {
78
+ next();
79
+ return;
80
+ }
81
+ const deviceId = req.query.deviceId;
82
+ const organizationId = req.query.organization;
83
+ if (deviceId) {
84
+ return validateDeviceQuery(req, res, next);
85
+ }
86
+ if (organizationId) {
87
+ const currentUser = await usersService.getUserByOwner(res.req.auth.sub, organizationId);
88
+ if (!currentUser) {
89
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization which has access to the device (validateDeviceQuery)"));
90
+ return;
91
+ }
92
+ req.currentUser = currentUser;
93
+ next();
94
+ return;
95
+ }
96
+ next(new ApiError(httpStatus.BAD_REQUEST, "deviceId or organization is required"));
97
+ };
98
+ export { validateDevice, validateDeviceQuery, validateDeviceOrOrganizationQuery, validateDeviceIsInOrganization, };
@@ -0,0 +1,26 @@
1
+ // @ts-nocheck
2
+ import httpStatus from "http-status";
3
+ import ApiError from "../utils/ApiError";
4
+ import devicesService from "../devices/devices.service";
5
+ import { isAdmin } from "./validateAdmin";
6
+ import userService from "../users/users.service";
7
+ export const validateDeviceUserOrganization = async (req, res, next) => {
8
+ if (isAdmin(res.req.auth)) {
9
+ next();
10
+ return;
11
+ }
12
+ if (req.body.patient) {
13
+ const currentDevice = await devicesService.getById(req.params.deviceId);
14
+ if (!currentDevice) {
15
+ next(new ApiError(httpStatus.FORBIDDEN, "Device was not found (validateDeviceUserOrganization)"));
16
+ return;
17
+ }
18
+ const currentUser = await userService.getUsersByOrganizationAndId(currentDevice.organization, req.body.patient);
19
+ if (!currentUser) {
20
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization which has access to the device (validateDeviceUserOrganization)"));
21
+ return;
22
+ }
23
+ req.currentUser = currentUser;
24
+ }
25
+ next();
26
+ };
@@ -0,0 +1,63 @@
1
+ import userService from "../users/users.service";
2
+ import httpStatus from "http-status";
3
+ import ApiError from "../utils/ApiError";
4
+ import { isAdmin } from "./validateAdmin";
5
+ export const validateUser = async (req, res, next) => {
6
+ next();
7
+ };
8
+ export const validateOrganization = async (req, res, next) => {
9
+ if (isAdmin(res.req.auth)) {
10
+ next();
11
+ }
12
+ else {
13
+ const currentUser = await userService.getUserByOwner(res.req.auth.sub, req.params.organizationId);
14
+ if (!currentUser) {
15
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization (validateOrganization)"));
16
+ return;
17
+ }
18
+ /* if (currentUser.role && currentUser.role === 'onlyself') {
19
+ next(new ApiError(httpStatus.FORBIDDEN, 'User does not have sufficient permissions in the organization'));
20
+ return;
21
+ } */
22
+ req.currentUser = currentUser;
23
+ next();
24
+ }
25
+ };
26
+ export const validateQueryOrganization = async (req, res, next) => {
27
+ if (isAdmin(res.req.auth)) {
28
+ next();
29
+ }
30
+ else {
31
+ const currentUser = await userService.getUserByOwner(res.req.auth.sub, req.query.organization);
32
+ if (!currentUser) {
33
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization (validateQueryOrganization)"));
34
+ return;
35
+ }
36
+ /* if (currentUser.role && currentUser.role === 'onlyself') {
37
+ next(new ApiError(httpStatus.FORBIDDEN, 'User does not have sufficient permissions in the organization'));
38
+ return;
39
+ } */
40
+ req.currentUser = currentUser;
41
+ next();
42
+ }
43
+ };
44
+ export const validateBodyOrganization = async (req, res, next) => {
45
+ if (isAdmin(res.req.auth)) {
46
+ next();
47
+ }
48
+ else {
49
+ const currentUser = await userService.getUserByOwner(res.req.auth.sub, req.body.organization);
50
+ if (!currentUser) {
51
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization (validateBodyOrganization)"));
52
+ return;
53
+ }
54
+ req.currentUser = currentUser;
55
+ next();
56
+ }
57
+ };
58
+ export default {
59
+ validateOrganization,
60
+ validateQueryOrganization,
61
+ validateBodyOrganization,
62
+ validateUser,
63
+ };
@@ -0,0 +1,44 @@
1
+ // @ts-nocheck
2
+ import httpStatus from "http-status";
3
+ import ApiError from "../utils/ApiError";
4
+ import { isAdmin } from "./validateAdmin";
5
+ import userService from "../users/users.service";
6
+ const validateQuerySearchUserAndOrganization = async (req, res, next) => {
7
+ if (isAdmin(res.req.auth)) {
8
+ next();
9
+ }
10
+ else {
11
+ console.log("validateQuerySearchUserAndOrganization", req.query);
12
+ if (req.query.organization) {
13
+ const currentUser = await userService.getUserByOwner(res.req.auth.sub, req.query.organization);
14
+ if (!currentUser) {
15
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization (validateQuerySearchUserAndOrganization)"));
16
+ return;
17
+ }
18
+ /* if (currentUser.role && currentUser.role === 'onlyself') {
19
+ next(new ApiError(httpStatus.FORBIDDEN, 'User does not have sufficient permissions in the organization'));
20
+ return;
21
+ } */
22
+ req.currentUser = currentUser;
23
+ next();
24
+ }
25
+ else if (req.query.patient) {
26
+ const activeUser = await userService.getById(req.query.patient);
27
+ if (!activeUser) {
28
+ next(new ApiError(httpStatus.FORBIDDEN, "User not found (validateQuerySearchUserAndOrganization)"));
29
+ return;
30
+ }
31
+ const currentUser = await userService.getUserByOwner(res.req.auth.sub, activeUser.organization);
32
+ if (!currentUser) {
33
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the organization which has access to the device (validateDevice)"));
34
+ return;
35
+ }
36
+ req.currentUser = currentUser;
37
+ next();
38
+ }
39
+ else {
40
+ next(new ApiError(httpStatus.FORBIDDEN, "No filter defined (validateQuerySearchUserAndOrganization)"));
41
+ }
42
+ }
43
+ };
44
+ export { validateQuerySearchUserAndOrganization };
@@ -0,0 +1,23 @@
1
+ import httpStatus from "http-status";
2
+ import ApiError from "../utils/ApiError";
3
+ import { getTokenById } from "../tokens/tokens.service";
4
+ export async function validateParamsToken(req, res, next) {
5
+ // assume tokenId comes from req.params
6
+ const tokenId = req.params.tokenId;
7
+ if (!tokenId) {
8
+ return next(new ApiError(httpStatus.BAD_REQUEST, "Token ID is required"));
9
+ }
10
+ // fetch your token entity
11
+ const token = await getTokenById(tokenId);
12
+ if (!token) {
13
+ return next(new ApiError(httpStatus.NOT_FOUND, "Token not found"));
14
+ }
15
+ // compare owner vs. authenticated sub
16
+ const tokenOwner = token.owner;
17
+ if (!tokenOwner || tokenOwner !== res.req.auth?.sub) {
18
+ return next(new ApiError(httpStatus.FORBIDDEN, "You are not allowed to access this token"));
19
+ }
20
+ // attach for downstream handlers and continue
21
+ req.token = token;
22
+ next();
23
+ }
@@ -0,0 +1,38 @@
1
+ // @ts-nocheck
2
+ import httpStatus from "http-status";
3
+ import ApiError from "../utils/ApiError";
4
+ import usersService from "../users/users.service";
5
+ import { isAdmin } from "./validateAdmin";
6
+ // extend makeValidateUser to accept an optional key (defaulting to 'patient')
7
+ const makeValidateUser = (source, key = "patient") => async (req, res, next) => {
8
+ const context = `validate${source.charAt(0).toUpperCase() + source.slice(1)}User`;
9
+ if (isAdmin(res.req.auth)) {
10
+ return next();
11
+ }
12
+ // pull the configured key instead of hard‐coded 'patient'
13
+ const id = req[source][key];
14
+ if (!id) {
15
+ return next(new ApiError(httpStatus.FORBIDDEN, `No user (${context})`));
16
+ }
17
+ const patient = await usersService.getById(id);
18
+ if (!patient) {
19
+ return next(new ApiError(httpStatus.FORBIDDEN, `User was with id ${id} not found (${context})`));
20
+ }
21
+ const currentUser = await usersService.getUserByOwner(res.req.auth.sub, patient.organization);
22
+ if (!currentUser) {
23
+ return next(new ApiError(httpStatus.FORBIDDEN, `Current user is not part of the organization as the requested user (${context})`));
24
+ }
25
+ req.currentUser = currentUser;
26
+ next();
27
+ };
28
+ // query and body still use the default 'patient' key
29
+ export function validateQueryUser(req, res, next) {
30
+ return makeValidateUser("query")(req, res, next);
31
+ }
32
+ export function validateBodyUser(req, res, next) {
33
+ return makeValidateUser("body")(req, res, next);
34
+ }
35
+ // params now pulls from req.params.userId
36
+ export function validateParamsUser(req, res, next) {
37
+ return makeValidateUser("params", "userId")(req, res, next);
38
+ }
@@ -0,0 +1,33 @@
1
+ import ApiError from '../utils/ApiError';
2
+ import httpStatus from 'http-status';
3
+ import z from 'zod';
4
+ export const validateZod = (schema) => (req, res, next) => {
5
+ try {
6
+ schema.body || (schema.body = z.object({}));
7
+ schema.query || (schema.query = z.object({}));
8
+ schema.params || (schema.params = z.object({}));
9
+ // 1) run safeParse on each
10
+ const result = {
11
+ body: schema.body.strict().safeParse(req.body || {}),
12
+ query: schema.query.strict().safeParse(req.query),
13
+ params: schema.params.strict().safeParse(req.params),
14
+ };
15
+ // 2) if any failure, short-circuit
16
+ if (!result.body.success || !result.query.success || !result.params.success) {
17
+ if (process.env.NODE_ENV === 'development') {
18
+ return res.status(400).send(result);
19
+ }
20
+ return next(new ApiError(httpStatus.BAD_REQUEST, 'Validation error'));
21
+ }
22
+ // 3) merge parsed data back in
23
+ req.body = result.body.data;
24
+ Object.assign(req.query, result.query.data);
25
+ Object.assign(req.params, result.params.data);
26
+ return next();
27
+ }
28
+ catch (err) {
29
+ console.error('Zod validation error:', err);
30
+ return next(new ApiError(httpStatus.BAD_REQUEST, 'Validation error', undefined, undefined, process.env.NODE_ENV === 'development' ? err : undefined));
31
+ }
32
+ };
33
+ export default validateZod;
@@ -0,0 +1,4 @@
1
+ // import type { PaginateNewPlugin } from './paginateNew.plugin';
2
+ export { default as toJSON } from "./toJSON.plugin";
3
+ export { default as paginate } from "./paginate.plugin";
4
+ // export { default as paginateNew } from './paginateNew.plugin';
@@ -0,0 +1,117 @@
1
+ // @ts-nocheck
2
+ /* eslint-disable no-param-reassign */
3
+ import { Types } from "mongoose";
4
+ function paginate(schema) {
5
+ schema.statics.paginate = async function (filter, options = {}, plugin) {
6
+ let sort = "";
7
+ if (options.sortBy) {
8
+ const sortingCriteria = [];
9
+ options.sortBy.split(",").forEach((sortOption) => {
10
+ const [key, order] = sortOption.split(":");
11
+ sortingCriteria.push((order === "desc" ? "-" : "") + key);
12
+ });
13
+ sort = sortingCriteria.join(" ");
14
+ }
15
+ else {
16
+ sort = "createdAt";
17
+ }
18
+ const limit = options.limit && parseInt(String(options.limit), 10) > 0
19
+ ? parseInt(String(options.limit), 10)
20
+ : 10000;
21
+ const page = options.page && parseInt(String(options.page), 10) > 0
22
+ ? parseInt(String(options.page), 10)
23
+ : 1;
24
+ const skip = (page - 1) * limit;
25
+ let results = [];
26
+ let totalResults = 0;
27
+ let totalPages = 0;
28
+ if (options.fuzzySearch && options.fuzzySearch.search) {
29
+ // Fuzzy search branch
30
+ const fuzzyFields = options.fuzzySearch.fields;
31
+ const mustClauses = Object.entries(filter).map(([key, value]) => {
32
+ if (typeof value === "string" && value.match(/^[a-fA-F0-9]{24}$/)) {
33
+ return { equals: { path: key, value: new Types.ObjectId(value) } };
34
+ }
35
+ return { equals: { path: key, value } };
36
+ });
37
+ const pipeline = [
38
+ {
39
+ $search: {
40
+ index: options.fuzzySearch.index,
41
+ compound: {
42
+ must: mustClauses,
43
+ should: [
44
+ {
45
+ text: {
46
+ query: options.fuzzySearch.search,
47
+ path: fuzzyFields,
48
+ fuzzy: {},
49
+ },
50
+ },
51
+ ],
52
+ minimumShouldMatch: 1,
53
+ },
54
+ },
55
+ },
56
+ { $sort: { createdAt: -1 } },
57
+ { $skip: skip },
58
+ { $limit: limit },
59
+ {
60
+ $facet: {
61
+ results: [],
62
+ totalCount: [{ $count: "count" }],
63
+ },
64
+ },
65
+ ];
66
+ const aggResult = await this.aggregate(pipeline).exec();
67
+ results = (aggResult[0]?.results || []).map((doc) => new this(doc));
68
+ totalResults = aggResult[0]?.totalCount[0]?.count || 0;
69
+ totalPages = Math.ceil(totalResults / limit);
70
+ }
71
+ else {
72
+ // Regular find branch
73
+ const countPromise = this.countDocuments(filter).exec();
74
+ let docsPromise = this.find(filter)
75
+ .sort(sort)
76
+ .skip(skip)
77
+ .limit(limit);
78
+ if (options.populate) {
79
+ options.populate.split(",").forEach((populateOption) => {
80
+ docsPromise = docsPromise.populate(populateOption
81
+ .split(".")
82
+ .reverse()
83
+ .reduce((a, b) => ({ path: b, populate: a })));
84
+ });
85
+ }
86
+ if (plugin)
87
+ docsPromise = plugin(docsPromise);
88
+ docsPromise = docsPromise.exec();
89
+ const values = await Promise.all([countPromise, docsPromise]);
90
+ totalResults = values[0];
91
+ results = values[1];
92
+ totalPages = Math.ceil(totalResults / limit);
93
+ }
94
+ // Populate and plugin for both branches (if not already applied)
95
+ if (options.fuzzySearch && options.fuzzySearch.search) {
96
+ if (options.populate) {
97
+ results = await this.populate(results, options.populate.split(",").map((populateOption) => populateOption
98
+ .split(".")
99
+ .reverse()
100
+ .reduce((a, b) => ({ path: b, populate: a }))));
101
+ }
102
+ if (plugin) {
103
+ // plugin expects a Query, so wrap results in a Query if needed
104
+ // Not possible for array, so skip plugin for fuzzySearch unless you have a custom handler
105
+ }
106
+ }
107
+ const result = {
108
+ results,
109
+ page,
110
+ limit,
111
+ totalPages,
112
+ totalResults,
113
+ };
114
+ return Promise.resolve(result);
115
+ };
116
+ }
117
+ export default paginate;