@internetderdinge/api 1.229.32 → 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.
- package/dist/src/accounts/accounts.route.js +18 -5
- package/dist/src/admin/adminSearch.controller.js +0 -19
- package/dist/src/admin/adminSearch.route.js +5 -5
- package/dist/src/devices/devices.controller.js +1 -6
- package/dist/src/devices/devices.route.js +13 -13
- package/dist/src/devices/devices.validation.js +0 -47
- package/dist/src/email/email.service.js +6 -6
- package/dist/src/index.js +5 -1
- package/dist/src/iotdevice/iotdevice.route.js +3 -1
- package/dist/src/middlewares/validateAdminOrSupport.js +20 -0
- package/dist/src/organizations/organizations.controller.js +17 -6
- package/dist/src/organizations/organizations.route.js +2 -1
- package/dist/src/users/users.model.js +4 -2
- package/dist/src/users/users.route.js +2 -2
- package/dist/src/users/users.service.js +26 -10
- package/dist/src/users/users.validation.js +60 -61
- package/dist/src/utils/buildRouterAndDocs.js +7 -4
- package/package.json +5 -2
- package/scripts/release-and-sync-paperless.mjs +21 -2
- package/scripts/release-version.mjs +21 -2
- package/src/accounts/accounts.route.ts +21 -5
- package/src/admin/adminSearch.controller.ts +0 -35
- package/src/admin/adminSearch.route.ts +8 -6
- package/src/admin/adminSearch.service.ts +6 -10
- package/src/devices/devices.controller.ts +0 -12
- package/src/devices/devices.route.ts +13 -14
- package/src/devices/devices.validation.ts +0 -47
- package/src/email/email.service.ts +15 -7
- package/src/index.ts +5 -1
- package/src/iotdevice/iotdevice.route.ts +3 -1
- package/src/middlewares/validateAdminOrSupport.ts +34 -0
- package/src/organizations/organizations.controller.ts +38 -7
- package/src/organizations/organizations.route.ts +3 -1
- package/src/users/users.model.ts +7 -3
- package/src/users/users.route.ts +3 -2
- package/src/users/users.service.ts +50 -14
- package/src/users/users.validation.ts +62 -60
- package/src/utils/buildRouterAndDocs.ts +14 -5
- package/dist/src/pdf/pdf.controller.js +0 -24
- package/dist/src/pdf/pdf.route.js +0 -22
- package/dist/src/pdf/pdf.schemas.js +0 -6
- package/dist/src/pdf/pdf.service.js +0 -64
- package/dist/src/pdf/pdf.validation.js +0 -27
- package/src/pdf/pdf.controller.ts +0 -35
- package/src/pdf/pdf.route.ts +0 -28
- package/src/pdf/pdf.schemas.ts +0 -7
- package/src/pdf/pdf.service.ts +0 -103
- 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: "
|
|
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 {
|
|
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"),
|
|
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"),
|
|
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"),
|
|
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"),
|
|
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,
|
|
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,
|
|
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 = "
|
|
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="
|
|
515
|
-
${
|
|
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
|
-
:
|
|
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
|
-
${
|
|
550
|
-
<a href="
|
|
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
|
|
25
|
+
const user = await usersService.createUser(createOrganizationOwnerUserBody({
|
|
26
|
+
organization,
|
|
14
27
|
owner: res.req.auth.sub,
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
};
|