@internetderdinge/api 1.229.31 → 1.229.37

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 (48) hide show
  1. package/dist/src/accounts/accounts.route.js +18 -5
  2. package/dist/src/admin/adminSearch.controller.js +0 -19
  3. package/dist/src/admin/adminSearch.route.js +5 -5
  4. package/dist/src/devices/devices.controller.js +1 -6
  5. package/dist/src/devices/devices.route.js +13 -13
  6. package/dist/src/devices/devices.validation.js +0 -47
  7. package/dist/src/email/email.service.js +6 -6
  8. package/dist/src/index.js +5 -1
  9. package/dist/src/iotdevice/iotdevice.route.js +3 -1
  10. package/dist/src/middlewares/validateAdminOrSupport.js +20 -0
  11. package/dist/src/organizations/organizations.controller.js +17 -6
  12. package/dist/src/organizations/organizations.route.js +2 -1
  13. package/dist/src/users/users.model.js +4 -2
  14. package/dist/src/users/users.route.js +2 -2
  15. package/dist/src/users/users.service.js +26 -10
  16. package/dist/src/users/users.validation.js +60 -61
  17. package/dist/src/utils/buildRouterAndDocs.js +7 -4
  18. package/package.json +5 -2
  19. package/scripts/release-and-sync-paperless.mjs +60 -28
  20. package/scripts/release-version.mjs +21 -2
  21. package/src/accounts/accounts.route.ts +21 -5
  22. package/src/admin/adminSearch.controller.ts +0 -35
  23. package/src/admin/adminSearch.route.ts +8 -6
  24. package/src/admin/adminSearch.service.ts +6 -10
  25. package/src/devices/devices.controller.ts +0 -12
  26. package/src/devices/devices.route.ts +13 -14
  27. package/src/devices/devices.validation.ts +0 -47
  28. package/src/email/email.service.ts +15 -7
  29. package/src/index.ts +5 -1
  30. package/src/iotdevice/iotdevice.route.ts +3 -1
  31. package/src/middlewares/validateAdminOrSupport.ts +34 -0
  32. package/src/organizations/organizations.controller.ts +38 -7
  33. package/src/organizations/organizations.route.ts +3 -1
  34. package/src/users/users.model.ts +7 -3
  35. package/src/users/users.route.ts +3 -2
  36. package/src/users/users.service.ts +50 -14
  37. package/src/users/users.validation.ts +62 -60
  38. package/src/utils/buildRouterAndDocs.ts +14 -5
  39. package/dist/src/pdf/pdf.controller.js +0 -24
  40. package/dist/src/pdf/pdf.route.js +0 -22
  41. package/dist/src/pdf/pdf.schemas.js +0 -6
  42. package/dist/src/pdf/pdf.service.js +0 -64
  43. package/dist/src/pdf/pdf.validation.js +0 -27
  44. package/src/pdf/pdf.controller.ts +0 -35
  45. package/src/pdf/pdf.route.ts +0 -28
  46. package/src/pdf/pdf.schemas.ts +0 -7
  47. package/src/pdf/pdf.service.ts +0 -103
  48. package/src/pdf/pdf.validation.ts +0 -30
@@ -12,9 +12,9 @@ export const accountsRouteSpecs = [
12
12
  validate: [auth("manageUsers")],
13
13
  requestSchema: accountsValidation.currentAccountSchema,
14
14
  responseSchema: accountResponseSchema,
15
- privateDocs: true,
16
15
  handler: accountsController.current,
17
16
  summary: "Get the current account",
17
+ description: "Fetches the details of the currently authenticated account.",
18
18
  },
19
19
  {
20
20
  method: "post",
@@ -56,15 +56,26 @@ export const accountsRouteSpecs = [
56
56
  handler: accountsController.avatar,
57
57
  summary: "Fetch account avatar",
58
58
  },
59
+ {
60
+ method: "delete",
61
+ path: "/current",
62
+ validate: [auth("manageUsers")],
63
+ requestSchema: accountsValidation.deleteCurrentSchema,
64
+ responseSchema: accountResponseSchema,
65
+ handler: accountsController.deleteCurrent,
66
+ summary: "Delete the current user's account",
67
+ description: "Permanently deletes the current user's account. Will not delete associated data. This action is irreversible.",
68
+ },
59
69
  {
60
70
  method: "delete",
61
71
  path: "/deleteCurrent",
62
72
  validate: [auth("manageUsers")],
63
73
  requestSchema: accountsValidation.deleteCurrentSchema,
64
74
  responseSchema: accountResponseSchema,
65
- privateDocs: true,
66
75
  handler: accountsController.deleteCurrent,
67
- summary: "Delete the current account",
76
+ summary: "Delete the current user's account",
77
+ privateDocs: true,
78
+ description: "LEGACY: Permanently deletes the current user's account. Will not delete associated data. This action is irreversible. (Replaced by DELETE /current)",
68
79
  },
69
80
  {
70
81
  method: "get",
@@ -74,6 +85,7 @@ export const accountsRouteSpecs = [
74
85
  responseSchema: accountResponseSchema,
75
86
  handler: accountsController.getAccountById,
76
87
  summary: "Get an account by ID",
88
+ description: "Fetches the details of a single account by its ID.",
77
89
  },
78
90
  {
79
91
  method: "post",
@@ -83,7 +95,8 @@ export const accountsRouteSpecs = [
83
95
  responseSchema: accountResponseSchema,
84
96
  privateDocs: true,
85
97
  handler: accountsController.updateEntry,
86
- summary: "Create or replace an account by ID",
98
+ summary: "Update an account by ID",
99
+ description: "LEGACY: Updates an existing account with a specified ID.",
87
100
  },
88
101
  {
89
102
  method: "patch",
@@ -91,9 +104,9 @@ export const accountsRouteSpecs = [
91
104
  validate: [auth("manageUsers"), validateParamsAccount],
92
105
  requestSchema: accountsValidation.updateAccountSchema,
93
106
  responseSchema: accountResponseSchema,
94
- privateDocs: true,
95
107
  handler: accountsController.updateEntry,
96
108
  summary: "Update fields on an account by ID",
109
+ description: "Updates specific fields of an existing account identified by its ID.",
97
110
  },
98
111
  ];
99
112
  const router = Router();
@@ -1,12 +1,5 @@
1
- import httpStatus from "http-status";
2
- import { ApiError } from "../utils/ApiError.js";
3
1
  import catchAsync from "../utils/catchAsync.js";
4
2
  import { getAdminDevices, getAdminIotDevices, getAdminStats, searchAdminCollections, } from "./adminSearch.service.js";
5
- const ADMIN_ROLE_CLAIM = "https://memo.wirewire.de/roles";
6
- const hasAdminRole = (auth) => {
7
- const roles = auth?.[ADMIN_ROLE_CLAIM];
8
- return Array.isArray(roles) && roles.includes("admin");
9
- };
10
3
  const readListQuery = (req) => {
11
4
  const rawPage = Number(req.query.page);
12
5
  const rawPerPage = Number(req.query.perPage);
@@ -20,9 +13,6 @@ const readListQuery = (req) => {
20
13
  };
21
14
  };
22
15
  export const searchAdmin = catchAsync(async (req, res) => {
23
- if (!hasAdminRole(req.auth)) {
24
- throw new ApiError(httpStatus.FORBIDDEN, "User is not part of the admin group");
25
- }
26
16
  const search = String(req.query.search || "").trim();
27
17
  const rawLimit = Number(req.query.limit);
28
18
  const limit = Number.isFinite(rawLimit)
@@ -32,23 +22,14 @@ export const searchAdmin = catchAsync(async (req, res) => {
32
22
  res.send(result);
33
23
  });
34
24
  export const getStats = catchAsync(async (req, res) => {
35
- if (!hasAdminRole(req.auth)) {
36
- throw new ApiError(httpStatus.FORBIDDEN, "User is not part of the admin group");
37
- }
38
25
  const result = await getAdminStats();
39
26
  res.send(result);
40
27
  });
41
28
  export const getIotDevices = catchAsync(async (req, res) => {
42
- if (!hasAdminRole(req.auth)) {
43
- throw new ApiError(httpStatus.FORBIDDEN, "User is not part of the admin group");
44
- }
45
29
  const result = await getAdminIotDevices(readListQuery(req));
46
30
  res.send(result);
47
31
  });
48
32
  export const getDevices = catchAsync(async (req, res) => {
49
- if (!hasAdminRole(req.auth)) {
50
- throw new ApiError(httpStatus.FORBIDDEN, "User is not part of the admin group");
51
- }
52
33
  const result = await getAdminDevices(readListQuery(req));
53
34
  res.send(result);
54
35
  });
@@ -1,7 +1,7 @@
1
1
  import { Router } from "express";
2
2
  import buildRouterAndDocs from "../utils/buildRouterAndDocs.js";
3
3
  import auth from "../middlewares/auth.js";
4
- import { validateAdmin } from "../middlewares/validateAdmin.js";
4
+ import { validateAdminOrSupport } from "../middlewares/validateAdminOrSupport.js";
5
5
  import { getDevices, getIotDevices, getStats, searchAdmin, } from "./adminSearch.controller.js";
6
6
  import { adminDevicesSchema, adminIotDevicesSchema, adminSearchSchema, adminStatsSchema, } from "./adminSearch.validation.js";
7
7
  import { adminDevicesResponseSchema, adminIotDevicesResponseSchema, adminSearchResponseSchema, adminStatsResponseSchema, } from "./adminSearch.schemas.js";
@@ -9,7 +9,7 @@ export const adminSearchRouteSpecs = [
9
9
  {
10
10
  method: "get",
11
11
  path: "/stats",
12
- validate: [auth("getUsers"), validateAdmin],
12
+ validate: [auth("getUsers"), validateAdminOrSupport],
13
13
  requestSchema: adminStatsSchema,
14
14
  responseSchema: adminStatsResponseSchema,
15
15
  handler: getStats,
@@ -19,7 +19,7 @@ export const adminSearchRouteSpecs = [
19
19
  {
20
20
  method: "get",
21
21
  path: "/search",
22
- validate: [auth("getUsers"), validateAdmin],
22
+ validate: [auth("getUsers"), validateAdminOrSupport],
23
23
  requestSchema: adminSearchSchema,
24
24
  responseSchema: adminSearchResponseSchema,
25
25
  handler: searchAdmin,
@@ -29,7 +29,7 @@ export const adminSearchRouteSpecs = [
29
29
  {
30
30
  method: "get",
31
31
  path: "/iotDevices",
32
- validate: [auth("getUsers"), validateAdmin],
32
+ validate: [auth("getUsers"), validateAdminOrSupport],
33
33
  requestSchema: adminIotDevicesSchema,
34
34
  responseSchema: adminIotDevicesResponseSchema,
35
35
  handler: getIotDevices,
@@ -39,7 +39,7 @@ export const adminSearchRouteSpecs = [
39
39
  {
40
40
  method: "get",
41
41
  path: "/devices",
42
- validate: [auth("getUsers"), validateAdmin],
42
+ validate: [auth("getUsers"), validateAdminOrSupport],
43
43
  requestSchema: adminDevicesSchema,
44
44
  responseSchema: adminDevicesResponseSchema,
45
45
  handler: getDevices,
@@ -129,14 +129,9 @@ export const resetDevice = catchAsync(async (req, res) => {
129
129
  const reset = await iotDevicesService.resetDevice(device.deviceId, req.body);
130
130
  res.send({ device, reset });
131
131
  });
132
- const ledLight = catchAsync(async (req, res) => {
133
- const device = await devicesService.getByIdWithIoT(req.params.deviceId);
134
- const ping = await iotDevicesService.ledLightHint(device.deviceId, req.body);
135
- res.send({ device, ping });
136
- });
137
132
  const rebootDevice = catchAsync(async (req, res) => {
138
133
  const device = await devicesService.getByIdWithIoT(req.params.deviceId);
139
134
  const reboot = await iotDevicesService.rebootDevice(device.deviceId);
140
135
  res.send({ device, reboot });
141
136
  });
142
- export { createEntry, getEntries, queryDevicesByUser, getEvents, registerDevice, pingDevice, ledLight, rebootDevice, getEntry, updateEntry, deleteEntry, };
137
+ export { createEntry, getEntries, queryDevicesByUser, getEvents, registerDevice, pingDevice, rebootDevice, getEntry, updateEntry, deleteEntry, };
@@ -6,7 +6,7 @@ import { validateQuerySearchUserAndOrganization } from "../middlewares/validateQ
6
6
  import { validateDevice } from "../middlewares/validateDevice.js";
7
7
  import * as devicesController from "./devices.controller.js";
8
8
  import { resetDevice } from "./devices.controller.js";
9
- import { createDeviceSchema, queryDevicesSchema, getDeviceSchema, updateDeviceSchema, deleteDeviceSchema, getEventsSchema, pingDeviceSchema, registerDeviceSchema, ledLightSchema, rebootDeviceSchema, resetDeviceSchema, } from "./devices.validation.js";
9
+ import { createDeviceSchema, queryDevicesSchema, getDeviceSchema, updateDeviceSchema, deleteDeviceSchema, getEventsSchema, pingDeviceSchema, registerDeviceSchema, rebootDeviceSchema, resetDeviceSchema, } from "./devices.validation.js";
10
10
  import { deviceResponseSchema, devicesResponseSchema, eventResponseSchema, genericResponseSchema, resetResponseSchema, } from "./devices.schemas.js";
11
11
  import { validateOrganizationDelete, validateOrganizationUpdate, } from "../middlewares/validateAction.js";
12
12
  export const devicesRouteSpecs = [
@@ -61,8 +61,19 @@ export const devicesRouteSpecs = [
61
61
  requestSchema: updateDeviceSchema,
62
62
  responseSchema: deviceResponseSchema,
63
63
  handler: devicesController.updateEntry,
64
+ privateDocs: true,
64
65
  summary: "Update a device",
65
- description: "Modify the properties of an existing device identified by its ID.",
66
+ description: "LEGACY: Modify the properties of an existing device identified by its ID.",
67
+ },
68
+ {
69
+ method: "patch",
70
+ path: "/:deviceId",
71
+ validate: [auth("manageUsers"), validateDevice, validateOrganizationUpdate],
72
+ requestSchema: updateDeviceSchema,
73
+ responseSchema: deviceResponseSchema,
74
+ handler: devicesController.updateEntry,
75
+ summary: "Update a device",
76
+ description: "Updates specific fields of an existing device identified by its ID.",
66
77
  },
67
78
  {
68
79
  method: "delete",
@@ -88,17 +99,6 @@ export const devicesRouteSpecs = [
88
99
  summary: "Register a device",
89
100
  description: "Associate an existing device with the authenticated organization.",
90
101
  },
91
- {
92
- method: "post",
93
- path: "/ledlight/:deviceId",
94
- validate: [auth("getUsers"), validateDevice],
95
- requestSchema: ledLightSchema,
96
- responseSchema: genericResponseSchema,
97
- handler: devicesController.ledLight,
98
- summary: "Set LED light on device",
99
- description: "Turn the device’s LED on or off, or set its color/brightness.",
100
- memoOnly: true,
101
- },
102
102
  {
103
103
  method: "get",
104
104
  path: "/ping/:deviceId",
@@ -95,53 +95,6 @@ export const createDeviceSchema = {
95
95
  };
96
96
  export const deleteDeviceSchema = zDelete("deviceId");
97
97
  export const getDeviceSchema = zGet("deviceId");
98
- export const ledLightSchema = {
99
- params: z.object({
100
- deviceId: zObjectId.openapi({ description: "Device ObjectId" }),
101
- }),
102
- body: z
103
- .object({
104
- led: z.array(z.tuple([z.number().int(), z.number().int()])),
105
- timeout: z.number().int(),
106
- })
107
- .openapi({
108
- description: "LED light configuration for device",
109
- example: {
110
- led: [
111
- [0, 0],
112
- [1, 0],
113
- [2, 0],
114
- [3, 0],
115
- [4, 0],
116
- [5, 0],
117
- [6, 0],
118
- [7, 0],
119
- [8, 0],
120
- [9, 0],
121
- [10, 0],
122
- [11, 0],
123
- [12, 0],
124
- [13, 0],
125
- [14, 0],
126
- [15, 100],
127
- [16, 100],
128
- [17, 100],
129
- [18, 100],
130
- [19, 100],
131
- [20, 100],
132
- [21, 100],
133
- [22, 0],
134
- [23, 0],
135
- [24, 0],
136
- [25, 0],
137
- [26, 0],
138
- [27, 0],
139
- [28, 0],
140
- ],
141
- timeout: 40,
142
- },
143
- }),
144
- };
145
98
  export const pingDeviceSchema = {
146
99
  params: z.object({
147
100
  deviceId: zObjectIdFor("deviceId").openapi({
@@ -34,7 +34,7 @@ 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, }) => {
37
+ export const sendEmail = async ({ title = "Kein Titel", body = "Kein Inhalt", url = "", domain = "web", appBaseUrl = `https://${domain}.wirewire.de`, productName = domain, companyName = "The Wire UG", accountUrl = `${appBaseUrl}/account`, image, email, actionButtonText, lng, }) => {
38
38
  const interactive = "#0076ff";
39
39
  const sesClient = new SESv2Client({
40
40
  region: "eu-central-1",
@@ -511,8 +511,8 @@ export const sendEmail = async ({ title = "Kein Titel", body = "Kein Inhalt", ur
511
511
  <table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
512
512
  <tr>
513
513
  <td class="email-masthead">
514
- <a href="https://${domain}.wirewire.de" class="f-fallback email-masthead_name">
515
- ${domain === "memo" ? "ANABOX smart" : "paperlesspaper"}
514
+ <a href="${appBaseUrl}" class="f-fallback email-masthead_name">
515
+ ${productName}
516
516
  </a>
517
517
  </td>
518
518
  </tr>
@@ -531,7 +531,7 @@ export const sendEmail = async ({ title = "Kein Titel", body = "Kein Inhalt", ur
531
531
  ${actionButton({
532
532
  link: urlStartsWithHttp(url)
533
533
  ? url
534
- : `http://${domain}.wirewire.de${url}`,
534
+ : `${appBaseUrl}${url}`,
535
535
  text: actionButtonTextWithLanguage,
536
536
  })}
537
537
  </div>
@@ -546,8 +546,8 @@ export const sendEmail = async ({ title = "Kein Titel", body = "Kein Inhalt", ur
546
546
  <tr>
547
547
  <td class="content-cell" align="center">
548
548
  <p class="f-fallback sub align-center">
549
- ${domain === "web" ? "The Wire UG" : "wirewire GmbH"}
550
- <a href="http://${domain}.wirewire.de/account">Account</a>
549
+ ${companyName}
550
+ <a href="${accountUrl}">Account</a>
551
551
 
552
552
  ${config.env !== "production" ? `<br/><br/>Environment: ${config.env}` : ""}
553
553
  </p>
package/dist/src/index.js CHANGED
@@ -8,12 +8,16 @@ 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 * from "../src/users/users.route";
11
12
  export { default as usersService } from "../src/users/users.service";
13
+ export * from "../src/users/users.service";
14
+ export * from "../src/users/users.validation";
12
15
  export { default as accountsRoute } from "../src/accounts/accounts.route";
13
16
  export { default as adminSearchRoute } from "../src/admin/adminSearch.route";
14
17
  export { default as accountsService } from "../src/accounts/accounts.service";
15
18
  export { auth0 } from "../src/accounts/auth0.service";
16
19
  export { default as organizationsRoute } from "../src/organizations/organizations.route";
20
+ export * from "../src/organizations/organizations.controller";
17
21
  export { default as organizationsService } from "../src/organizations/organizations.service";
18
22
  export { default as Organization } from "../src/organizations/organizations.model";
19
23
  export { default as devicesRoute } from "../src/devices/devices.route";
@@ -25,11 +29,11 @@ export { default as devicesNotificationsService } from "../src/devicesNotificati
25
29
  export { default as iotDevicesService } from "../src/iotdevice/iotdevice.service";
26
30
  export { default as iotdeviceRoute } from "../src/iotdevice/iotdevice.route";
27
31
  export { SIMILARITY_THRESHOLD, alarmsDevice, getDeviceStatus, getDeviceStatusList, shadowAlarmGet, shadowAlarmUpdate, } from "../src/iotdevice/iotdevice.service";
28
- export { default as pdfRoute } from "../src/pdf/pdf.route";
29
32
  export { default as tokensRoute } from "../src/tokens/tokens.route";
30
33
  export * from "../src/tokens/tokens.service";
31
34
  export { default as Token } from "../src/tokens/tokens.model";
32
35
  export { User } from "../src/users/users.model";
36
+ export * from "../src/users/users.model";
33
37
  export { isAdmin, validateAdmin } from "../src/middlewares/validateAdmin";
34
38
  export { sendEmail } from "../src/email/email.service";
35
39
  export { default as catchAsync } from "../src/utils/catchAsync";
@@ -26,6 +26,7 @@ export const iotdeviceRouteSpecs = [
26
26
  requestSchema: getEventsSchema,
27
27
  responseSchema: eventResponseSchema.array(),
28
28
  handler: getEvents,
29
+ privateDocs: true,
29
30
  summary: "Get events for a device",
30
31
  description: "Fetches event records for the specified device by its ID.",
31
32
  },
@@ -104,8 +105,9 @@ export const iotdeviceRouteSpecs = [
104
105
  requestSchema: apiStatusRequestSchema,
105
106
  responseSchema: apiStatusSchema,
106
107
  handler: getApiStatus,
108
+ privateDocs: true,
107
109
  summary: "Get API status by 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.",
110
+ description: "Retrieves the IoT API status information for a given status kind to monitor system health or performance. Can be accessed without authentication for uptime monitoring purposes.",
109
111
  },
110
112
  {
111
113
  method: "get",
@@ -0,0 +1,20 @@
1
+ import httpStatus from "http-status";
2
+ import ApiError from "../utils/ApiError";
3
+ const ROLES_CLAIM = "https://memo.wirewire.de/roles";
4
+ const isAdminOrSupport = (user) => {
5
+ if (!user)
6
+ return false;
7
+ const roles = user[ROLES_CLAIM];
8
+ return Array.isArray(roles)
9
+ ? roles.includes("admin") || roles.includes("support")
10
+ : false;
11
+ };
12
+ const validateAdminOrSupport = async (req, res, next) => {
13
+ if (isAdminOrSupport(req.auth)) {
14
+ next();
15
+ }
16
+ else {
17
+ next(new ApiError(httpStatus.FORBIDDEN, "User is not part of the admin or support group (validateAdminOrSupport)"));
18
+ }
19
+ };
20
+ export { isAdminOrSupport, validateAdminOrSupport };
@@ -7,15 +7,26 @@ import organizationsService, { deleteOrganizationById, } from "./organizations.s
7
7
  import mongoose from "mongoose";
8
8
  import { filterOptions } from "../utils/filterOptions.js";
9
9
  const ObjectId = mongoose.Types.ObjectId;
10
+ let createOrganizationOwnerUserHook = null;
11
+ export const setCreateOrganizationOwnerUserHook = (hook) => {
12
+ createOrganizationOwnerUserHook = hook ?? null;
13
+ };
14
+ const createOrganizationOwnerUserBody = (params) => ({
15
+ organization: params.organization._id,
16
+ owner: params.owner,
17
+ role: "admin",
18
+ status: "accept",
19
+ ...(createOrganizationOwnerUserHook
20
+ ? createOrganizationOwnerUserHook(params)
21
+ : {}),
22
+ });
10
23
  export const createOrganization = catchAsync(async (req, res) => {
11
24
  const organization = await organizationsService.createOrganization(req.body);
12
- const user = await usersService.createUser({
13
- organization: organization._id,
25
+ const user = await usersService.createUser(createOrganizationOwnerUserBody({
26
+ organization,
14
27
  owner: res.req.auth.sub,
15
- role: "admin",
16
- category: "relative",
17
- status: "accept",
18
- });
28
+ request: req,
29
+ }));
19
30
  res.status(httpStatus.CREATED).send(organization);
20
31
  });
21
32
  export const getOrganizations = catchAsync(async (req, res) => {
@@ -74,7 +74,8 @@ export const organizationsRouteSpecs = [
74
74
  responseSchema: organizationResponseSchema,
75
75
  handler: updateOrganization,
76
76
  summary: "Update an organization by ID",
77
- description: "Updates the details of a specific organization by its ID.",
77
+ privateDocs: true,
78
+ description: "LEGACY: Updates the details of a specific organization by its ID.",
78
79
  },
79
80
  {
80
81
  method: "delete",
@@ -1,7 +1,7 @@
1
1
  // @ts-nocheck
2
2
  import mongoose, { Schema } from "mongoose";
3
3
  import { toJSON, paginate } from "../models/plugins/index.js";
4
- const userSchema = new Schema({
4
+ export const userSchema = new Schema({
5
5
  name: { type: String, trim: true },
6
6
  avatar: { type: String },
7
7
  timezone: { type: String, default: "Europe/Berlin" },
@@ -10,7 +10,6 @@ const userSchema = new Schema({
10
10
  inviteCode: { type: String },
11
11
  email: { type: String },
12
12
  role: { type: String, default: "patient" /* , enum: Object.keys(roles) */ },
13
- category: { type: String, default: "patient" },
14
13
  status: { type: String },
15
14
  meta: { type: Schema.Types.Mixed },
16
15
  }, {
@@ -27,6 +26,9 @@ userSchema.virtual("organizationData", {
27
26
  // plugins
28
27
  userSchema.plugin(toJSON);
29
28
  userSchema.plugin(paginate);
29
+ export const extendUserSchema = (definition) => {
30
+ userSchema.add(definition);
31
+ };
30
32
  /**
31
33
  * Check if email is taken
32
34
  * @param {string} email - The user's email
@@ -17,7 +17,6 @@ export const userRouteSpecs = [
17
17
  handler: userController.createUser,
18
18
  summary: "Create a new user",
19
19
  description: "Creates a new user within the current organization.",
20
- memoOnly: true,
21
20
  },
22
21
  {
23
22
  method: "get",
@@ -107,7 +106,8 @@ export const userRouteSpecs = [
107
106
  responseSchema: updateUserResponseSchema,
108
107
  handler: userController.updateUser,
109
108
  summary: "Update a user by ID",
110
- description: "Replaces a user’s full record with the provided data.",
109
+ privateDocs: true,
110
+ description: "LEGACY: Replaces a user’s full record with the provided data. (Replaced by PATCH /:userId)",
111
111
  },
112
112
  {
113
113
  method: "patch",
@@ -10,6 +10,10 @@ let updateTimesByIdHook = null;
10
10
  export const setUpdateTimesByIdHook = (hook) => {
11
11
  updateTimesByIdHook = hook ?? null;
12
12
  };
13
+ let buildInviteEmailHook = null;
14
+ export const setBuildInviteEmailHook = (hook) => {
15
+ buildInviteEmailHook = hook ?? null;
16
+ };
13
17
  /**
14
18
  * Create a new user
15
19
  */
@@ -70,11 +74,8 @@ export const getByIdWithAuth0 = async (id) => {
70
74
  json.auth0User = auth0User;
71
75
  return json;
72
76
  };
73
- /**
74
- * Get all users in a given category (and optional organization)
75
- */
76
- export const getUsersByCategory = async (category, organization) => {
77
- const filter = { category };
77
+ export const getUsersByAppField = async (appName, fieldName, value, organization) => {
78
+ const filter = { [`apps.${appName}.${fieldName}`]: value };
78
79
  if (organization)
79
80
  filter.organization = organization;
80
81
  return User.find(filter);
@@ -123,15 +124,29 @@ export const sendInviteEmail = async (params) => {
123
124
  const organization = await organizationsService.getOrganizationById(user.organization);
124
125
  const auth0User = await auth0Service.getUserById(auth.sub);
125
126
  const lng = auth0User.data?.app_metadata?.language;
126
- const title = `${i18n.t("Invite to ", { lng })}${organization.kind === "private-wirewire" ? "paperlesspaper" : "ANABOX smart"}`;
127
127
  const body = i18n.t("You have been invited to join the group. Click on the link to accept the invitation.", { lng });
128
- await sendEmail({
129
- title,
128
+ const baseEmail = {
129
+ title: `${i18n.t("Invite to ", { lng })}${organization?.name || "Application"}`,
130
130
  body,
131
131
  url: `/${user.organization}/invite/${inviteCode}`,
132
132
  actionButtonText: "Accept invite",
133
- domain: organization.kind === "private-wirewire" ? "web" : "memo",
133
+ domain: "web",
134
+ productName: organization?.name || "Application",
134
135
  email,
136
+ lng,
137
+ };
138
+ await sendEmail({
139
+ ...baseEmail,
140
+ ...(buildInviteEmailHook
141
+ ? buildInviteEmailHook({
142
+ auth,
143
+ user,
144
+ inviteCode,
145
+ email,
146
+ organization,
147
+ lng,
148
+ })
149
+ : {}),
135
150
  });
136
151
  };
137
152
  /**
@@ -270,7 +285,7 @@ export default {
270
285
  createCurrentUser,
271
286
  getById,
272
287
  getByIdWithAuth0,
273
- getUsersByCategory,
288
+ getUsersByAppField,
274
289
  getUsersByOrganization,
275
290
  getUsersByOrganizationAndId,
276
291
  getUsersByOwner,
@@ -293,4 +308,5 @@ export default {
293
308
  populateAuth0User,
294
309
  populateAuth0Users,
295
310
  setUpdateTimesByIdHook,
311
+ setBuildInviteEmailHook,
296
312
  };