@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.
- package/dist/src/accounts/accounts.controller.js +89 -0
- package/dist/src/accounts/accounts.route.js +101 -0
- package/dist/src/accounts/accounts.schemas.js +12 -0
- package/dist/src/accounts/accounts.service.js +65 -0
- package/dist/src/accounts/accounts.validation.js +99 -0
- package/dist/src/accounts/auth0.service.js +188 -0
- package/dist/src/config/config.js +48 -0
- package/dist/src/config/logger.js +27 -0
- package/dist/src/config/morgan.js +16 -0
- package/dist/src/config/passport.cjs +28 -0
- package/dist/src/config/roles.js +11 -0
- package/dist/src/config/tokens.cjs +10 -0
- package/dist/src/devices/devices.controller.js +172 -0
- package/dist/src/devices/devices.model.js +94 -0
- package/dist/src/devices/devices.route.js +153 -0
- package/dist/src/devices/devices.schemas.js +84 -0
- package/dist/src/devices/devices.service.js +198 -0
- package/dist/src/devices/devices.types.js +1 -0
- package/dist/src/devices/devices.validation.js +257 -0
- package/dist/src/devicesNotifications/devicesNotifications.controller.js +69 -0
- package/dist/src/devicesNotifications/devicesNotifications.model.js +39 -0
- package/dist/src/devicesNotifications/devicesNotifications.route.js +124 -0
- package/dist/src/devicesNotifications/devicesNotifications.schemas.js +10 -0
- package/dist/src/devicesNotifications/devicesNotifications.service.js +181 -0
- package/dist/src/devicesNotifications/devicesNotifications.validation.js +46 -0
- package/dist/src/email/email.service.js +580 -0
- package/dist/src/files/upload.service.js +124 -0
- package/dist/src/i18n/i18n.js +38 -0
- package/dist/src/i18n/saveMissingLocalJsonBackend.js +53 -0
- package/dist/src/i18n/types.js +1 -0
- package/dist/src/index.js +48 -0
- package/dist/src/iotdevice/iotdevice.controller.js +96 -0
- package/dist/src/iotdevice/iotdevice.model.js +17 -0
- package/dist/src/iotdevice/iotdevice.route.js +143 -0
- package/dist/src/iotdevice/iotdevice.schemas.js +60 -0
- package/dist/src/iotdevice/iotdevice.service.js +579 -0
- package/dist/src/iotdevice/iotdevice.types.js +1 -0
- package/dist/src/iotdevice/iotdevice.validation.js +54 -0
- package/dist/src/middlewares/auth.js +75 -0
- package/dist/src/middlewares/checkJwt.cjs +17 -0
- package/dist/src/middlewares/error.js +36 -0
- package/dist/src/middlewares/mongooseValidations/ensureSameOrganization.js +13 -0
- package/dist/src/middlewares/rateLimiter.js +7 -0
- package/dist/src/middlewares/validate.js +18 -0
- package/dist/src/middlewares/validateAction.js +35 -0
- package/dist/src/middlewares/validateAdmin.js +18 -0
- package/dist/src/middlewares/validateAi.js +16 -0
- package/dist/src/middlewares/validateCurrentAuthUser.js +17 -0
- package/dist/src/middlewares/validateCurrentUser.js +20 -0
- package/dist/src/middlewares/validateDevice.js +98 -0
- package/dist/src/middlewares/validateDeviceUserOrganization.js +26 -0
- package/dist/src/middlewares/validateOrganization.js +63 -0
- package/dist/src/middlewares/validateQuerySearchUserAndOrganization.js +44 -0
- package/dist/src/middlewares/validateTokens.js +23 -0
- package/dist/src/middlewares/validateUser.js +38 -0
- package/dist/src/middlewares/validateZod.js +33 -0
- package/dist/src/models/plugins/index.js +4 -0
- package/dist/src/models/plugins/paginate.plugin.js +117 -0
- package/dist/src/models/plugins/paginateNew.plugin.js +185 -0
- package/dist/src/models/plugins/simplePopulate.js +16 -0
- package/dist/src/models/plugins/toJSON.plugin.js +35 -0
- package/dist/src/organizations/organizations.controller.js +64 -0
- package/dist/src/organizations/organizations.model.js +41 -0
- package/dist/src/organizations/organizations.route.js +98 -0
- package/dist/src/organizations/organizations.schemas.js +7 -0
- package/dist/src/organizations/organizations.service.js +59 -0
- package/dist/src/organizations/organizations.validation.js +62 -0
- package/dist/src/pdf/pdf.controller.js +24 -0
- package/dist/src/pdf/pdf.route.js +22 -0
- package/dist/src/pdf/pdf.schemas.js +6 -0
- package/dist/src/pdf/pdf.service.js +65 -0
- package/dist/src/pdf/pdf.validation.js +27 -0
- package/dist/src/tokens/tokens.controller.js +60 -0
- package/dist/src/tokens/tokens.model.js +18 -0
- package/dist/src/tokens/tokens.route.js +52 -0
- package/dist/src/tokens/tokens.schemas.js +14 -0
- package/dist/src/tokens/tokens.service.js +30 -0
- package/dist/src/tokens/tokens.validation.js +9 -0
- package/dist/src/types/routeSpec.js +1 -0
- package/dist/src/users/users.controller.js +147 -0
- package/dist/src/users/users.model.js +50 -0
- package/dist/src/users/users.route.js +137 -0
- package/dist/src/users/users.schemas.js +69 -0
- package/dist/src/users/users.service.js +295 -0
- package/dist/src/users/users.types.js +1 -0
- package/dist/src/users/users.validation.js +144 -0
- package/dist/src/utils/ApiError.js +16 -0
- package/dist/src/utils/buildRouterAndDocs.js +72 -0
- package/dist/src/utils/catchAsync.js +4 -0
- package/dist/src/utils/comparePapers.service.js +32 -0
- package/dist/src/utils/deviceUtils.js +63 -0
- package/dist/src/utils/filterOptions.js +24 -0
- package/dist/src/utils/medicationName.js +10 -0
- package/dist/src/utils/pick.js +16 -0
- package/dist/src/utils/registerOpenApi.js +28 -0
- package/dist/src/utils/urlUtils.js +15 -0
- package/dist/src/utils/userName.js +22 -0
- package/dist/src/utils/zValidations.js +124 -0
- package/dist/src/validations/auth.validation.cjs +53 -0
- package/dist/src/validations/custom.validation.js +19 -0
- package/dist/src/validations/index.cjs +3 -0
- package/package.json +13 -3
- package/scripts/release-and-sync-paperless.mjs +135 -0
- package/src/accounts/accounts.controller.ts +1 -0
- package/src/accounts/accounts.service.ts +1 -0
- package/src/accounts/accounts.validation.ts +6 -3
- package/src/accounts/auth0.service.ts +55 -28
- package/src/config/config.ts +6 -0
- package/src/config/logger.ts +15 -9
- package/src/devices/devices.controller.ts +7 -1
- package/src/devices/devices.model.ts +4 -1
- package/src/devices/devices.schemas.ts +10 -8
- package/src/devices/devices.service.ts +1 -0
- package/src/devices/devices.types.ts +1 -0
- package/src/devices/devices.validation.ts +85 -23
- package/src/devicesNotifications/devicesNotifications.controller.ts +57 -28
- package/src/devicesNotifications/devicesNotifications.model.ts +20 -12
- package/src/devicesNotifications/devicesNotifications.service.ts +35 -17
- package/src/files/upload.service.ts +52 -28
- package/src/i18n/i18n.ts +1 -1
- package/src/i18n/types.ts +1 -0
- package/src/index.ts +47 -0
- package/src/iotdevice/iotdevice.controller.ts +1 -0
- package/src/iotdevice/iotdevice.model.ts +6 -3
- package/src/iotdevice/iotdevice.route.ts +85 -76
- package/src/iotdevice/iotdevice.service.ts +4 -3
- package/src/iotdevice/iotdevice.types.ts +6 -0
- package/src/middlewares/auth.ts +2 -8
- package/src/middlewares/error.ts +26 -12
- package/src/middlewares/mongooseValidations/ensureSameOrganization.ts +4 -3
- package/src/middlewares/validateAi.ts +17 -9
- package/src/middlewares/validateDevice.ts +1 -0
- package/src/middlewares/validateDeviceUserOrganization.ts +1 -0
- package/src/middlewares/validateOrganization.ts +1 -1
- package/src/middlewares/validateQuerySearchUserAndOrganization.ts +1 -0
- package/src/middlewares/validateTokens.ts +2 -1
- package/src/middlewares/validateUser.ts +1 -0
- package/src/models/plugins/index.ts +5 -4
- package/src/models/plugins/paginate.plugin.ts +26 -16
- package/src/models/plugins/paginateNew.plugin.ts +33 -21
- package/src/models/plugins/simplePopulate.ts +8 -3
- package/src/models/plugins/toJSON.plugin.ts +12 -5
- package/src/organizations/organizations.controller.ts +1 -2
- package/src/organizations/organizations.model.ts +4 -4
- package/src/organizations/organizations.route.ts +1 -1
- package/src/organizations/organizations.service.ts +15 -6
- package/src/pdf/pdf.controller.ts +18 -1
- package/src/pdf/pdf.service.ts +25 -16
- package/src/tokens/tokens.controller.ts +6 -8
- package/src/tokens/tokens.model.ts +3 -1
- package/src/tokens/tokens.service.ts +3 -2
- package/src/types/express.d.ts +17 -0
- package/src/types/mongoose.d.ts +22 -0
- package/src/users/users.controller.ts +8 -9
- package/src/users/users.model.ts +6 -5
- package/src/users/users.route.ts +0 -1
- package/src/users/users.service.ts +16 -0
- package/src/users/users.types.ts +1 -0
- package/src/users/users.validation.ts +6 -2
- package/src/utils/ApiError.ts +8 -1
- package/src/utils/buildRouterAndDocs.ts +56 -21
- package/src/utils/catchAsync.ts +27 -3
- package/src/utils/medicationName.ts +5 -4
- package/src/utils/pick.ts +5 -1
- package/src/utils/userName.ts +1 -0
- package/src/utils/zValidations.ts +78 -26
- package/tsconfig.json +13 -4
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { AuthenticationClient, ManagementClient } from "auth0";
|
|
3
|
+
import type { ManagementClientOptions, User } from "auth0";
|
|
4
|
+
import { promises as fs, readFileSync } from "fs";
|
|
5
|
+
import { config } from "process";
|
|
5
6
|
|
|
6
7
|
interface TokenManagementClient {
|
|
7
8
|
clientId?: string;
|
|
@@ -21,12 +22,12 @@ let tokenManagementClient: TokenManagementClient = {
|
|
|
21
22
|
|
|
22
23
|
//if (config.env !== 'production') {
|
|
23
24
|
try {
|
|
24
|
-
console.warn(
|
|
25
|
-
const token = readFileSync(
|
|
26
|
-
console.warn(
|
|
25
|
+
console.warn("Auth0 client: use local token from cache try");
|
|
26
|
+
const token = readFileSync("./token.txt", "utf8");
|
|
27
|
+
console.warn("Auth0 client: use local token from cache");
|
|
27
28
|
tokenManagementClient = { token };
|
|
28
29
|
} catch (error) {
|
|
29
|
-
console.log(
|
|
30
|
+
console.log("Auth0 Client: use new token");
|
|
30
31
|
}
|
|
31
32
|
//}
|
|
32
33
|
|
|
@@ -56,14 +57,16 @@ let cachedManagementToken: string | null = null;
|
|
|
56
57
|
let managementTokenExpiresAt: number | null = null;
|
|
57
58
|
let pendingManagementTokenPromise: Promise<string> | null = null;
|
|
58
59
|
|
|
59
|
-
const TOKEN_FILE_PATH =
|
|
60
|
-
const MANAGEMENT_TOKEN_FILE_PATH =
|
|
60
|
+
const TOKEN_FILE_PATH = "./token.txt";
|
|
61
|
+
const MANAGEMENT_TOKEN_FILE_PATH = "./token.management.txt";
|
|
61
62
|
const TOKEN_BUFFER_SECONDS = 60; // refresh a minute before expiry
|
|
62
63
|
const TOKEN_FALLBACK_TTL_SECONDS = 3300; // ~55 minutes when no expiry metadata exists
|
|
63
64
|
|
|
64
|
-
const loadTokenFromFile = async (
|
|
65
|
+
const loadTokenFromFile = async (
|
|
66
|
+
filePath: string,
|
|
67
|
+
): Promise<CachedToken | null> => {
|
|
65
68
|
try {
|
|
66
|
-
const raw = await fs.readFile(filePath,
|
|
69
|
+
const raw = await fs.readFile(filePath, "utf8");
|
|
67
70
|
try {
|
|
68
71
|
const parsed = JSON.parse(raw);
|
|
69
72
|
if (parsed?.token && parsed?.expiresAt) {
|
|
@@ -80,22 +83,34 @@ const loadTokenFromFile = async (filePath: string): Promise<CachedToken | null>
|
|
|
80
83
|
return null;
|
|
81
84
|
};
|
|
82
85
|
|
|
83
|
-
const writeTokenFile = async (
|
|
86
|
+
const writeTokenFile = async (
|
|
87
|
+
filePath: string,
|
|
88
|
+
token: string,
|
|
89
|
+
expiresAt: number,
|
|
90
|
+
): Promise<void> => {
|
|
84
91
|
const payload = JSON.stringify({ token, expiresAt });
|
|
85
|
-
await fs.writeFile(filePath, payload,
|
|
92
|
+
await fs.writeFile(filePath, payload, "utf8");
|
|
86
93
|
};
|
|
87
94
|
|
|
88
|
-
const tokenIsValid = (
|
|
95
|
+
const tokenIsValid = (
|
|
96
|
+
token: string | null,
|
|
97
|
+
expiresAt: number | null,
|
|
98
|
+
now: number,
|
|
99
|
+
): boolean => {
|
|
89
100
|
if (!token || !expiresAt) return false;
|
|
90
101
|
return now < expiresAt - TOKEN_BUFFER_SECONDS;
|
|
91
102
|
};
|
|
92
103
|
|
|
93
104
|
export const getAuth0Token = async (): Promise<string> => {
|
|
94
|
-
const audience =
|
|
105
|
+
const audience =
|
|
106
|
+
process.env.AUTH0_AUDIENCE ||
|
|
107
|
+
process.env.AUTH0_MANAGEMENT_AUDIENCE ||
|
|
108
|
+
"localhost:3000/";
|
|
95
109
|
const grantOpts = { audience };
|
|
96
110
|
|
|
97
111
|
const now = Math.floor(Date.now() / 1000);
|
|
98
|
-
if (tokenIsValid(cachedToken, tokenExpiresAt, now))
|
|
112
|
+
if (tokenIsValid(cachedToken, tokenExpiresAt, now))
|
|
113
|
+
return cachedToken as string;
|
|
99
114
|
if (pendingTokenPromise) return pendingTokenPromise;
|
|
100
115
|
|
|
101
116
|
pendingTokenPromise = (async () => {
|
|
@@ -110,7 +125,8 @@ export const getAuth0Token = async (): Promise<string> => {
|
|
|
110
125
|
}
|
|
111
126
|
//}
|
|
112
127
|
|
|
113
|
-
const tokenResponse =
|
|
128
|
+
const tokenResponse =
|
|
129
|
+
await auth0AuthClient.oauth.clientCredentialsGrant(grantOpts);
|
|
114
130
|
const expiresIn = tokenResponse.data.expires_in || 3600;
|
|
115
131
|
cachedToken = tokenResponse.data.access_token;
|
|
116
132
|
tokenExpiresAt = now + expiresIn;
|
|
@@ -129,13 +145,16 @@ export const getAuth0Token = async (): Promise<string> => {
|
|
|
129
145
|
export const getAuth0ManagementToken = async (): Promise<string> => {
|
|
130
146
|
const audience = process.env.AUTH0_MANAGEMENT_AUDIENCE;
|
|
131
147
|
if (!audience) {
|
|
132
|
-
throw new Error(
|
|
148
|
+
throw new Error(
|
|
149
|
+
"Missing AUTH0_MANAGEMENT_AUDIENCE; cannot mint Management API token",
|
|
150
|
+
);
|
|
133
151
|
}
|
|
134
152
|
|
|
135
153
|
const grantOpts = { audience };
|
|
136
154
|
const now = Math.floor(Date.now() / 1000);
|
|
137
155
|
|
|
138
|
-
if (tokenIsValid(cachedManagementToken, managementTokenExpiresAt, now))
|
|
156
|
+
if (tokenIsValid(cachedManagementToken, managementTokenExpiresAt, now))
|
|
157
|
+
return cachedManagementToken as string;
|
|
139
158
|
if (pendingManagementTokenPromise) return pendingManagementTokenPromise;
|
|
140
159
|
|
|
141
160
|
pendingManagementTokenPromise = (async () => {
|
|
@@ -147,12 +166,17 @@ export const getAuth0ManagementToken = async (): Promise<string> => {
|
|
|
147
166
|
return cachedManagementToken;
|
|
148
167
|
}
|
|
149
168
|
|
|
150
|
-
const tokenResponse =
|
|
169
|
+
const tokenResponse =
|
|
170
|
+
await auth0AuthClient.oauth.clientCredentialsGrant(grantOpts);
|
|
151
171
|
const expiresIn = tokenResponse.data.expires_in || 3600;
|
|
152
172
|
cachedManagementToken = tokenResponse.data.access_token;
|
|
153
173
|
managementTokenExpiresAt = now + expiresIn;
|
|
154
174
|
|
|
155
|
-
await writeTokenFile(
|
|
175
|
+
await writeTokenFile(
|
|
176
|
+
MANAGEMENT_TOKEN_FILE_PATH,
|
|
177
|
+
cachedManagementToken,
|
|
178
|
+
managementTokenExpiresAt,
|
|
179
|
+
);
|
|
156
180
|
|
|
157
181
|
pendingManagementTokenPromise = null;
|
|
158
182
|
return cachedManagementToken;
|
|
@@ -172,8 +196,8 @@ export const getUserIdByEmail = async (email: string): Promise<User[]> => {
|
|
|
172
196
|
return auth0.usersByEmail.getByEmail({ email });
|
|
173
197
|
};
|
|
174
198
|
|
|
175
|
-
export const sendVerificationEmail = async (userID: string): Promise<
|
|
176
|
-
|
|
199
|
+
export const sendVerificationEmail = async (userID: string): Promise<any> => {
|
|
200
|
+
return auth0.jobs.verifyEmail({ user_id: userID });
|
|
177
201
|
};
|
|
178
202
|
|
|
179
203
|
export const getUserById = async (userId: string): Promise<User> => {
|
|
@@ -184,7 +208,10 @@ export const avatar = async (userId: string): Promise<User> => {
|
|
|
184
208
|
return auth0.users.get({ id: userId });
|
|
185
209
|
};
|
|
186
210
|
|
|
187
|
-
export const mfaEnrollAccount = async (
|
|
211
|
+
export const mfaEnrollAccount = async (
|
|
212
|
+
userId: string,
|
|
213
|
+
mfaToken: string,
|
|
214
|
+
): Promise<any> => {
|
|
188
215
|
const ticketResponse = await auth0.guardian.createEnrollmentTicket({
|
|
189
216
|
user_id: userId,
|
|
190
217
|
send_mail: false,
|
|
@@ -199,13 +226,13 @@ export const mfaDisableAccount = async (userId: string): Promise<any> => {
|
|
|
199
226
|
};
|
|
200
227
|
|
|
201
228
|
export const getUsersByIds = async (postIDs: string[]): Promise<User[]> => {
|
|
202
|
-
let q =
|
|
229
|
+
let q = "";
|
|
203
230
|
postIDs.forEach((e, i) => {
|
|
204
|
-
if (e) q = `${q} ${i >= 2 ?
|
|
231
|
+
if (e) q = `${q} ${i >= 2 ? " OR " : ""} user_id:"${e}"`;
|
|
205
232
|
});
|
|
206
233
|
|
|
207
234
|
const params = {
|
|
208
|
-
search_engine:
|
|
235
|
+
search_engine: "v3",
|
|
209
236
|
q,
|
|
210
237
|
per_page: 100,
|
|
211
238
|
page: 0,
|
package/src/config/config.ts
CHANGED
|
@@ -3,6 +3,12 @@ import dotenv from "dotenv";
|
|
|
3
3
|
// Load env from the current working directory
|
|
4
4
|
dotenv.config();
|
|
5
5
|
|
|
6
|
+
console.log("Current working directory:", process.cwd());
|
|
7
|
+
console.log("Loaded environment variables:", {
|
|
8
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
9
|
+
PORT: process.env.PORT,
|
|
10
|
+
MONGODB_URL: process.env.MONGODB_URL,
|
|
11
|
+
});
|
|
6
12
|
import Joi from "joi";
|
|
7
13
|
|
|
8
14
|
const envVarsSchema = Joi.object()
|
package/src/config/logger.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import winston from "winston";
|
|
3
|
+
import type { Format } from "logform";
|
|
4
|
+
import config from "./config";
|
|
4
5
|
|
|
5
6
|
const enumerateErrorFormat: Format = winston.format((info) => {
|
|
6
7
|
if (info instanceof Error) {
|
|
@@ -10,22 +11,27 @@ const enumerateErrorFormat: Format = winston.format((info) => {
|
|
|
10
11
|
});
|
|
11
12
|
|
|
12
13
|
const logger = winston.createLogger({
|
|
13
|
-
level: config.env ===
|
|
14
|
+
level: config.env === "development" ? "debug" : "info",
|
|
14
15
|
format: winston.format.combine(
|
|
15
16
|
enumerateErrorFormat(),
|
|
16
|
-
config.env ===
|
|
17
|
+
config.env === "development"
|
|
18
|
+
? winston.format.colorize()
|
|
19
|
+
: winston.format.uncolorize(),
|
|
17
20
|
winston.format.splat(),
|
|
18
21
|
winston.format.printf((info) => {
|
|
19
|
-
const { level, message, stack, ...rest } = info as Record<
|
|
22
|
+
const { level, message, stack, ...rest } = info as Record<
|
|
23
|
+
string,
|
|
24
|
+
unknown
|
|
25
|
+
>;
|
|
20
26
|
const restKeys = Object.keys(rest);
|
|
21
|
-
const restText = restKeys.length ? ` ${JSON.stringify(rest)}` :
|
|
22
|
-
const stackText = stack ? `\n${stack}` :
|
|
27
|
+
const restText = restKeys.length ? ` ${JSON.stringify(rest)}` : "";
|
|
28
|
+
const stackText = stack ? `\n${stack}` : "";
|
|
23
29
|
return `${level}: ${message}${stackText}${restText}`;
|
|
24
30
|
}),
|
|
25
31
|
),
|
|
26
32
|
transports: [
|
|
27
33
|
new winston.transports.Console({
|
|
28
|
-
stderrLevels: [
|
|
34
|
+
stderrLevels: ["error"],
|
|
29
35
|
}),
|
|
30
36
|
],
|
|
31
37
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
import httpStatus from "http-status";
|
|
2
3
|
import mongoose from "mongoose";
|
|
3
4
|
import multer from "multer";
|
|
@@ -247,9 +248,14 @@ const uploadSingleImage = catchAsync(
|
|
|
247
248
|
async (req: Request, res: Response): Promise<void> => {
|
|
248
249
|
const device = await devicesService.getById(req.params.deviceId);
|
|
249
250
|
|
|
251
|
+
const files = req.files as Array<{ buffer: Buffer }> | undefined;
|
|
252
|
+
if (!files || files.length === 0) {
|
|
253
|
+
throw new ApiError(httpStatus.BAD_REQUEST, "No image file uploaded");
|
|
254
|
+
}
|
|
255
|
+
|
|
250
256
|
const iotUpload = await iotDevicesService.uploadSingleImage({
|
|
251
257
|
deviceName: device.deviceId,
|
|
252
|
-
buffer:
|
|
258
|
+
buffer: files[0].buffer,
|
|
253
259
|
deviceId: req.params.deviceId,
|
|
254
260
|
uuid: req.body.uuid,
|
|
255
261
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
import mongoose, { Schema, Document, Model, Types } from "mongoose";
|
|
2
3
|
import { toJSON, paginate } from "../models/plugins/index.js";
|
|
3
4
|
import { ensureSameOrganization } from "../middlewares/mongooseValidations/ensureSameOrganization.js";
|
|
@@ -121,6 +122,8 @@ addIotDevice(deviceSchema);
|
|
|
121
122
|
deviceSchema.plugin((schema: Schema) => toJSON(schema, true));
|
|
122
123
|
deviceSchema.plugin(paginate);
|
|
123
124
|
|
|
124
|
-
const Devices: Model<IDevice> =
|
|
125
|
+
const Devices: Model<IDevice> =
|
|
126
|
+
(mongoose.models.Device as Model<IDevice>) ||
|
|
127
|
+
mongoose.model<IDevice>("Device", deviceSchema);
|
|
125
128
|
|
|
126
129
|
export default Devices;
|
|
@@ -22,9 +22,9 @@ export const eventResponseSchema = z.object({
|
|
|
22
22
|
// ...other event fields...
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
export const genericResponseSchema = z
|
|
26
|
-
|
|
27
|
-
});
|
|
25
|
+
export const genericResponseSchema = z
|
|
26
|
+
.record(z.any())
|
|
27
|
+
.openapi({ description: "Generic response payload" });
|
|
28
28
|
|
|
29
29
|
export const imageResponseSchema = z.object({
|
|
30
30
|
uuid: z.string(),
|
|
@@ -64,11 +64,13 @@ export const subscriptionResponseSchema = z.object({
|
|
|
64
64
|
// ...other subscription fields...
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
export const uploadResponseSchema = z
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
export const uploadResponseSchema = z
|
|
68
|
+
.object({
|
|
69
|
+
key: z.string().optional(),
|
|
70
|
+
similarityPercentage: z.number().nullable().optional(),
|
|
71
|
+
skippedUpload: z.boolean().optional(),
|
|
72
|
+
})
|
|
73
|
+
.openapi({ description: "Upload response payload" });
|
|
72
74
|
|
|
73
75
|
export const pingDeviceSchema = {
|
|
74
76
|
params: z.object({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Device = any;
|
|
@@ -4,6 +4,7 @@ import { objectId } from "../validations/custom.validation.js";
|
|
|
4
4
|
import {
|
|
5
5
|
zGet,
|
|
6
6
|
zObjectId,
|
|
7
|
+
zObjectIdFor,
|
|
7
8
|
zPatchBody,
|
|
8
9
|
zUpdate,
|
|
9
10
|
zDelete,
|
|
@@ -66,6 +67,11 @@ export const updateEntrySchema = {
|
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
export const getEventsSchema = {
|
|
70
|
+
params: z.object({
|
|
71
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
72
|
+
description: "Device ObjectId",
|
|
73
|
+
}),
|
|
74
|
+
}),
|
|
69
75
|
query: z
|
|
70
76
|
.object({
|
|
71
77
|
DateStart: z.string(),
|
|
@@ -92,14 +98,29 @@ export const createCustomerPortalSessionSchema = {
|
|
|
92
98
|
export const createDeviceSchema = {
|
|
93
99
|
body: z.object({
|
|
94
100
|
kind: z.string().optional(),
|
|
95
|
-
patient:
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
patient: zObjectIdFor("patient")
|
|
102
|
+
.optional()
|
|
103
|
+
.nullable()
|
|
104
|
+
.openapi({ description: "Patient ObjectId", example: null }),
|
|
105
|
+
paper: zObjectIdFor("paper")
|
|
106
|
+
.optional()
|
|
107
|
+
.nullable()
|
|
108
|
+
.openapi({ description: "Paper ObjectId", example: null }),
|
|
109
|
+
organization: zObjectIdFor("organization").openapi({
|
|
110
|
+
description: "Organization ObjectId",
|
|
111
|
+
}),
|
|
98
112
|
}),
|
|
99
113
|
};
|
|
100
114
|
export const deleteDeviceSchema = zDelete("deviceId");
|
|
101
115
|
export const getDeviceSchema = zGet("deviceId");
|
|
102
|
-
export const getImageSchema =
|
|
116
|
+
export const getImageSchema = {
|
|
117
|
+
params: z.object({
|
|
118
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
119
|
+
description: "Device ObjectId",
|
|
120
|
+
}),
|
|
121
|
+
uuid: z.string().openapi({ description: "Image UUID" }),
|
|
122
|
+
}),
|
|
123
|
+
};
|
|
103
124
|
export const ledLightSchema = {
|
|
104
125
|
params: z.object({
|
|
105
126
|
deviceId: zObjectId.openapi({ description: "Device ObjectId" }),
|
|
@@ -149,7 +170,9 @@ export const ledLightSchema = {
|
|
|
149
170
|
};
|
|
150
171
|
export const pingDeviceSchema = {
|
|
151
172
|
params: z.object({
|
|
152
|
-
deviceId:
|
|
173
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
174
|
+
description: "Device ObjectId",
|
|
175
|
+
}),
|
|
153
176
|
}),
|
|
154
177
|
query: z.object({
|
|
155
178
|
dataResponse: z
|
|
@@ -160,34 +183,49 @@ export const pingDeviceSchema = {
|
|
|
160
183
|
|
|
161
184
|
export const resetDeviceSchema = {
|
|
162
185
|
params: z.object({
|
|
163
|
-
deviceId:
|
|
186
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
187
|
+
description: "Device ObjectId",
|
|
188
|
+
}),
|
|
164
189
|
}),
|
|
165
190
|
};
|
|
166
191
|
|
|
167
192
|
export const rebootDeviceSchema = zGet("deviceId");
|
|
168
193
|
|
|
169
194
|
export const registerDeviceSchema = {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
195
|
+
body: z
|
|
196
|
+
.object({
|
|
197
|
+
enable: z.boolean(),
|
|
198
|
+
organization: zObjectIdFor("organization"),
|
|
199
|
+
patient: zObjectId.optional().nullable(),
|
|
200
|
+
paper: zObjectId.optional().nullable(),
|
|
201
|
+
})
|
|
202
|
+
.openapi({
|
|
203
|
+
example: {
|
|
204
|
+
enable: true,
|
|
205
|
+
organization:
|
|
206
|
+
process.env.SCHEMA_EXAMPLE_ORGANIZATION_ID || "682fd0d7d4a6325d9d45b86d",
|
|
207
|
+
patient: process.env.SCHEMA_EXAMPLE_USER_ID || "682fd0d7d4a6325d9d45b86d",
|
|
208
|
+
paper: process.env.SCHEMA_EXAMPLE_PAPER_ID || null,
|
|
209
|
+
},
|
|
210
|
+
}),
|
|
177
211
|
params: z.object({
|
|
178
|
-
deviceId:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
212
|
+
deviceId: (
|
|
213
|
+
process.env.SCHEMA_STRICT_EXAMPLES === "true" &&
|
|
214
|
+
process.env.SCHEMA_EXAMPLE_DEVICE_SERIAL
|
|
215
|
+
? z.literal(process.env.SCHEMA_EXAMPLE_DEVICE_SERIAL)
|
|
216
|
+
: z.string()
|
|
217
|
+
).openapi({
|
|
218
|
+
description: "Device serial",
|
|
219
|
+
example: process.env.SCHEMA_EXAMPLE_DEVICE_SERIAL || "DEVICE-EXAMPLE",
|
|
220
|
+
}),
|
|
183
221
|
}),
|
|
184
222
|
};
|
|
185
223
|
|
|
186
224
|
export const queryDevicesSchema = {
|
|
187
225
|
...zPagination,
|
|
188
226
|
query: zPagination.query.extend({
|
|
189
|
-
patient:
|
|
190
|
-
organization:
|
|
227
|
+
patient: zObjectIdFor("patient").optional(),
|
|
228
|
+
organization: zObjectIdFor("organization"),
|
|
191
229
|
}),
|
|
192
230
|
};
|
|
193
231
|
export const subscriptionSchema = {
|
|
@@ -213,9 +251,33 @@ export const updateDeviceSchema = {
|
|
|
213
251
|
}),
|
|
214
252
|
};
|
|
215
253
|
|
|
216
|
-
export const updateSingleImageMetaSchema = {
|
|
254
|
+
export const updateSingleImageMetaSchema = {
|
|
255
|
+
params: z.object({
|
|
256
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
257
|
+
description: "Device ObjectId",
|
|
258
|
+
}),
|
|
259
|
+
}),
|
|
260
|
+
body: z
|
|
261
|
+
.object({
|
|
262
|
+
meta: z.record(z.any()).optional(),
|
|
263
|
+
})
|
|
264
|
+
.openapi({ description: "Image metadata updates" }),
|
|
265
|
+
};
|
|
217
266
|
export const uploadSingleImageSchema = {
|
|
218
|
-
|
|
219
|
-
|
|
267
|
+
params: z.object({
|
|
268
|
+
deviceId: zObjectIdFor("deviceId").openapi({
|
|
269
|
+
description: "Device ObjectId",
|
|
270
|
+
}),
|
|
220
271
|
}),
|
|
272
|
+
body: z
|
|
273
|
+
.object({
|
|
274
|
+
uuid: z
|
|
275
|
+
.string()
|
|
276
|
+
.optional()
|
|
277
|
+
.openapi({ description: "Optional image UUID", example: "mock-uuid" }),
|
|
278
|
+
})
|
|
279
|
+
.openapi({
|
|
280
|
+
description: "Multipart body is mocked during tests.",
|
|
281
|
+
example: { uuid: "mock-uuid" },
|
|
282
|
+
}),
|
|
221
283
|
};
|
|
@@ -1,56 +1,85 @@
|
|
|
1
|
-
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import httpStatus from "http-status";
|
|
2
3
|
|
|
3
|
-
import pick from
|
|
4
|
-
import ApiError from
|
|
5
|
-
import catchAsync from
|
|
6
|
-
import devicesNotificationsService from
|
|
4
|
+
import pick from "../../src/utils/pick";
|
|
5
|
+
import ApiError from "../../src/utils/ApiError";
|
|
6
|
+
import catchAsync from "../../src/utils/catchAsync";
|
|
7
|
+
import devicesNotificationsService from "./devicesNotifications.service";
|
|
7
8
|
|
|
8
|
-
import type { Request, Response } from
|
|
9
|
+
import type { Request, Response } from "express";
|
|
9
10
|
|
|
10
11
|
export const createEntry = catchAsync(async (req: Request, res: Response) => {
|
|
11
|
-
const user = await devicesNotificationsService.createNotification({
|
|
12
|
+
const user = await devicesNotificationsService.createNotification({
|
|
13
|
+
user: res.req.auth.sub,
|
|
14
|
+
token: req.body.token,
|
|
15
|
+
});
|
|
12
16
|
res.status(httpStatus.CREATED).send(user);
|
|
13
17
|
});
|
|
14
18
|
|
|
15
|
-
export const setDeviceToken = catchAsync(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
export const setDeviceToken = catchAsync(
|
|
20
|
+
async (req: Request, res: Response) => {
|
|
21
|
+
const user = await devicesNotificationsService.setDeviceToken({
|
|
22
|
+
user: res.req.auth.sub,
|
|
23
|
+
body: req.body,
|
|
24
|
+
});
|
|
25
|
+
res.status(httpStatus.CREATED).send(user);
|
|
26
|
+
},
|
|
27
|
+
);
|
|
19
28
|
|
|
20
|
-
export const removeDeviceToken = catchAsync(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
export const removeDeviceToken = catchAsync(
|
|
30
|
+
async (req: Request, res: Response) => {
|
|
31
|
+
const user = await devicesNotificationsService.removeDeviceToken({
|
|
32
|
+
user: res.req.auth.sub,
|
|
33
|
+
body: req.body,
|
|
34
|
+
});
|
|
35
|
+
res.status(httpStatus.CREATED).send(user);
|
|
36
|
+
},
|
|
37
|
+
);
|
|
24
38
|
|
|
25
39
|
export const getEntries = catchAsync(async (req: Request, res: Response) => {
|
|
26
|
-
const filter = pick(req.query, [
|
|
27
|
-
const options = pick(req.query, [
|
|
28
|
-
const result = await devicesNotificationsService.queryNotifications(
|
|
40
|
+
const filter = pick(req.query, ["name", "role"]);
|
|
41
|
+
const options = pick(req.query, ["sortBy", "limit", "page"]);
|
|
42
|
+
const result = await devicesNotificationsService.queryNotifications(
|
|
43
|
+
filter,
|
|
44
|
+
options,
|
|
45
|
+
);
|
|
29
46
|
res.send(result);
|
|
30
47
|
});
|
|
31
48
|
|
|
32
|
-
export const queryNotificationsByUser = catchAsync(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
export const queryNotificationsByUser = catchAsync(
|
|
50
|
+
async (req: Request, res: Response) => {
|
|
51
|
+
const filter = pick({ user: res.req.auth.sub }, ["user"]);
|
|
52
|
+
const options = pick(req.query, ["sortBy", "limit", "page"]);
|
|
53
|
+
const result = await devicesNotificationsService.queryNotificationsByUser(
|
|
54
|
+
filter,
|
|
55
|
+
options,
|
|
56
|
+
);
|
|
57
|
+
res.send(result);
|
|
58
|
+
},
|
|
59
|
+
);
|
|
38
60
|
|
|
39
61
|
export const getEntry = catchAsync(async (req: Request, res: Response) => {
|
|
40
|
-
const notification = await devicesNotificationsService.getById(
|
|
62
|
+
const notification = await devicesNotificationsService.getById(
|
|
63
|
+
req.params.notificationId,
|
|
64
|
+
);
|
|
41
65
|
if (!notification) {
|
|
42
|
-
throw new ApiError(httpStatus.NOT_FOUND,
|
|
66
|
+
throw new ApiError(httpStatus.NOT_FOUND, "Notification not found");
|
|
43
67
|
}
|
|
44
68
|
res.send(notification);
|
|
45
69
|
});
|
|
46
70
|
|
|
47
71
|
export const updateEntry = catchAsync(async (req: Request, res: Response) => {
|
|
48
|
-
const user = await devicesNotificationsService.updateById(
|
|
72
|
+
const user = await devicesNotificationsService.updateById(
|
|
73
|
+
req.params.notificationId,
|
|
74
|
+
req.body,
|
|
75
|
+
);
|
|
49
76
|
res.send(user);
|
|
50
77
|
});
|
|
51
78
|
|
|
52
79
|
export const deleteEntry = catchAsync(async (req: Request, res: Response) => {
|
|
53
|
-
const entry = await devicesNotificationsService.deleteById(
|
|
80
|
+
const entry = await devicesNotificationsService.deleteById(
|
|
81
|
+
req.params.notificationId,
|
|
82
|
+
);
|
|
54
83
|
res.send(entry);
|
|
55
84
|
});
|
|
56
85
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import mongoose, { Schema, Document, Model } from "mongoose";
|
|
3
|
+
import type { PaginateModel } from "mongoose";
|
|
4
|
+
import { toJSON, paginate } from "../../src/models/plugins/index";
|
|
4
5
|
|
|
5
6
|
interface Token {
|
|
6
7
|
token: string;
|
|
@@ -11,7 +12,7 @@ interface Token {
|
|
|
11
12
|
|
|
12
13
|
interface Settings {
|
|
13
14
|
intake: { email: boolean; push: boolean };
|
|
14
|
-
|
|
15
|
+
"intake-reminder": { email: boolean; push: boolean };
|
|
15
16
|
battery: { email: boolean; push: boolean };
|
|
16
17
|
}
|
|
17
18
|
|
|
@@ -24,7 +25,8 @@ export interface DeviceNotificationDocument extends Document {
|
|
|
24
25
|
updatedAt: Date;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
export type DeviceNotificationModel = Model<DeviceNotificationDocument> &
|
|
28
|
+
export type DeviceNotificationModel = Model<DeviceNotificationDocument> &
|
|
29
|
+
PaginateModel<DeviceNotificationDocument>;
|
|
28
30
|
|
|
29
31
|
const deviceNotificationSchema = new Schema<DeviceNotificationDocument>(
|
|
30
32
|
{
|
|
@@ -42,12 +44,18 @@ const deviceNotificationSchema = new Schema<DeviceNotificationDocument>(
|
|
|
42
44
|
],
|
|
43
45
|
bounceEmail: { type: String },
|
|
44
46
|
settings: {
|
|
45
|
-
intake: {
|
|
46
|
-
|
|
47
|
+
intake: {
|
|
48
|
+
email: { type: Boolean, default: true },
|
|
49
|
+
push: { type: Boolean, default: true },
|
|
50
|
+
},
|
|
51
|
+
"intake-reminder": {
|
|
52
|
+
email: { type: Boolean, default: true },
|
|
53
|
+
push: { type: Boolean, default: true },
|
|
54
|
+
},
|
|
55
|
+
battery: {
|
|
47
56
|
email: { type: Boolean, default: true },
|
|
48
57
|
push: { type: Boolean, default: true },
|
|
49
58
|
},
|
|
50
|
-
battery: { email: { type: Boolean, default: true }, push: { type: Boolean, default: true } },
|
|
51
59
|
},
|
|
52
60
|
},
|
|
53
61
|
{
|
|
@@ -59,9 +67,9 @@ const deviceNotificationSchema = new Schema<DeviceNotificationDocument>(
|
|
|
59
67
|
deviceNotificationSchema.plugin(toJSON);
|
|
60
68
|
deviceNotificationSchema.plugin(paginate);
|
|
61
69
|
|
|
62
|
-
const DeviceNotifications = mongoose.model<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
);
|
|
70
|
+
const DeviceNotifications = mongoose.model<
|
|
71
|
+
DeviceNotificationDocument,
|
|
72
|
+
DeviceNotificationModel
|
|
73
|
+
>("DeviceNotification", deviceNotificationSchema);
|
|
66
74
|
|
|
67
75
|
export default DeviceNotifications;
|