@internetderdinge/api 1.229.0 → 1.229.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +17 -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 +67 -0
- package/dist/src/utils/urlUtils.js +15 -0
- package/dist/src/utils/userName.js +22 -0
- package/dist/src/utils/zValidations.js +143 -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/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +97 -80
- package/scripts/release-and-sync-paperless.mjs +137 -0
- package/src/accounts/accounts.controller.ts +1 -0
- package/src/accounts/accounts.service.ts +1 -0
- package/src/accounts/accounts.validation.ts +8 -5
- 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 +11 -9
- package/src/devices/devices.service.ts +1 -0
- package/src/devices/devices.types.ts +1 -0
- package/src/devices/devices.validation.ts +93 -32
- 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/middlewares/validateZod.ts +5 -5
- 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/organizations/organizations.validation.ts +1 -1
- 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 +57 -22
- package/src/utils/catchAsync.ts +27 -3
- package/src/utils/medicationName.ts +5 -4
- package/src/utils/pick.ts +5 -1
- package/src/utils/registerOpenApi.ts +75 -24
- package/src/utils/userName.ts +1 -0
- package/src/utils/zValidations.ts +98 -27
- package/tsconfig.json +13 -4
|
@@ -1,24 +1,32 @@
|
|
|
1
|
-
import httpStatus from
|
|
2
|
-
import ApiError from
|
|
3
|
-
import
|
|
4
|
-
const { Request, Response, NextFunction } = expressPkg;
|
|
1
|
+
import httpStatus from "http-status";
|
|
2
|
+
import ApiError from "../utils/ApiError"; // keep .cjs import
|
|
3
|
+
import type { Request, Response, NextFunction } from "express";
|
|
5
4
|
|
|
6
5
|
interface User {
|
|
7
|
-
|
|
6
|
+
"https://memo.wirewire.de/roles"?: string[];
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
// you can adjust the User source if your auth payload differs
|
|
11
10
|
export const isAiRole = (user?: User): boolean => {
|
|
12
11
|
if (!user) return false;
|
|
13
|
-
return user[
|
|
12
|
+
return user["https://memo.wirewire.de/roles"]?.includes("ai") ?? false;
|
|
14
13
|
};
|
|
15
14
|
|
|
16
|
-
export const validateAiRole = async (
|
|
15
|
+
export const validateAiRole = async (
|
|
16
|
+
req: Request & { auth?: User },
|
|
17
|
+
res: Response,
|
|
18
|
+
next: NextFunction,
|
|
19
|
+
): Promise<void> => {
|
|
17
20
|
// assuming the auth payload is attached to req.auth
|
|
18
21
|
|
|
19
|
-
console.log(
|
|
22
|
+
console.log("Validating AI role for user:", req.auth);
|
|
20
23
|
if (isAiRole(req.auth)) {
|
|
21
24
|
return next();
|
|
22
25
|
}
|
|
23
|
-
return next(
|
|
26
|
+
return next(
|
|
27
|
+
new ApiError(
|
|
28
|
+
httpStatus.FORBIDDEN,
|
|
29
|
+
"User is not part of the ai group (validateAi)",
|
|
30
|
+
),
|
|
31
|
+
);
|
|
24
32
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
import type { Request, Response, NextFunction } from "express";
|
|
2
3
|
import type { UserService } from "../users/users.service";
|
|
3
4
|
import userService from "../users/users.service";
|
|
4
|
-
|
|
5
5
|
import httpStatus from "http-status";
|
|
6
6
|
import ApiError from "../utils/ApiError";
|
|
7
7
|
import { isAdmin } from "./validateAdmin";
|
|
@@ -21,7 +21,8 @@ export async function validateParamsToken(
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
// compare owner vs. authenticated sub
|
|
24
|
-
|
|
24
|
+
const tokenOwner = (token as { owner?: string }).owner;
|
|
25
|
+
if (!tokenOwner || tokenOwner !== res.req.auth?.sub) {
|
|
25
26
|
return next(
|
|
26
27
|
new ApiError(
|
|
27
28
|
httpStatus.FORBIDDEN,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ZodObject, ZodRawShape } from 'zod';
|
|
3
3
|
import ApiError from '../utils/ApiError';
|
|
4
4
|
import httpStatus from 'http-status';
|
|
5
|
-
import z from 'zod';
|
|
5
|
+
import { z } from 'zod';
|
|
6
6
|
|
|
7
7
|
interface Schema {
|
|
8
|
-
body?:
|
|
9
|
-
query?:
|
|
10
|
-
params?:
|
|
8
|
+
body?: ZodObject<ZodRawShape>;
|
|
9
|
+
query?: ZodObject<ZodRawShape>;
|
|
10
|
+
params?: ZodObject<ZodRawShape>;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export const validateZod = (schema: Schema) => (req: Request, res: Response, next: NextFunction) => {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import type {
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import type { ToJSONPlugin } from "./toJSON.plugin";
|
|
3
|
+
import type { PaginatePlugin } from "./paginate.plugin";
|
|
3
4
|
// import type { PaginateNewPlugin } from './paginateNew.plugin';
|
|
4
5
|
|
|
5
|
-
export { default as toJSON } from
|
|
6
|
-
export { default as paginate } from
|
|
6
|
+
export { default as toJSON } from "./toJSON.plugin";
|
|
7
|
+
export { default as paginate } from "./paginate.plugin";
|
|
7
8
|
// export { default as paginateNew } from './paginateNew.plugin';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
/* eslint-disable no-param-reassign */
|
|
2
|
-
import { Document, Model, Query, Types } from
|
|
3
|
+
import { Document, Model, Query, Types } from "mongoose";
|
|
3
4
|
|
|
4
5
|
export interface QueryResult<T> {
|
|
5
6
|
results: T[];
|
|
@@ -26,20 +27,26 @@ function paginate<T extends Document>(schema: any) {
|
|
|
26
27
|
options: PaginateOptions = {},
|
|
27
28
|
plugin?: PluginFunction,
|
|
28
29
|
): Promise<QueryResult<T>> {
|
|
29
|
-
let sort =
|
|
30
|
+
let sort = "";
|
|
30
31
|
if (options.sortBy) {
|
|
31
32
|
const sortingCriteria: string[] = [];
|
|
32
|
-
options.sortBy.split(
|
|
33
|
-
const [key, order] = sortOption.split(
|
|
34
|
-
sortingCriteria.push((order ===
|
|
33
|
+
options.sortBy.split(",").forEach((sortOption) => {
|
|
34
|
+
const [key, order] = sortOption.split(":");
|
|
35
|
+
sortingCriteria.push((order === "desc" ? "-" : "") + key);
|
|
35
36
|
});
|
|
36
|
-
sort = sortingCriteria.join(
|
|
37
|
+
sort = sortingCriteria.join(" ");
|
|
37
38
|
} else {
|
|
38
|
-
sort =
|
|
39
|
+
sort = "createdAt";
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
const limit =
|
|
42
|
-
|
|
42
|
+
const limit =
|
|
43
|
+
options.limit && parseInt(String(options.limit), 10) > 0
|
|
44
|
+
? parseInt(String(options.limit), 10)
|
|
45
|
+
: 10000;
|
|
46
|
+
const page =
|
|
47
|
+
options.page && parseInt(String(options.page), 10) > 0
|
|
48
|
+
? parseInt(String(options.page), 10)
|
|
49
|
+
: 1;
|
|
43
50
|
const skip = (page - 1) * limit;
|
|
44
51
|
|
|
45
52
|
let results: any[] = [];
|
|
@@ -51,7 +58,7 @@ function paginate<T extends Document>(schema: any) {
|
|
|
51
58
|
|
|
52
59
|
const fuzzyFields = options.fuzzySearch.fields;
|
|
53
60
|
const mustClauses = Object.entries(filter).map(([key, value]) => {
|
|
54
|
-
if (typeof value ===
|
|
61
|
+
if (typeof value === "string" && value.match(/^[a-fA-F0-9]{24}$/)) {
|
|
55
62
|
return { equals: { path: key, value: new Types.ObjectId(value) } };
|
|
56
63
|
}
|
|
57
64
|
return { equals: { path: key, value } };
|
|
@@ -81,7 +88,7 @@ function paginate<T extends Document>(schema: any) {
|
|
|
81
88
|
{
|
|
82
89
|
$facet: {
|
|
83
90
|
results: [],
|
|
84
|
-
totalCount: [{ $count:
|
|
91
|
+
totalCount: [{ $count: "count" }],
|
|
85
92
|
},
|
|
86
93
|
},
|
|
87
94
|
];
|
|
@@ -93,12 +100,15 @@ function paginate<T extends Document>(schema: any) {
|
|
|
93
100
|
// Regular find branch
|
|
94
101
|
const countPromise = this.countDocuments(filter).exec();
|
|
95
102
|
|
|
96
|
-
let docsPromise: any = this.find(filter)
|
|
103
|
+
let docsPromise: any = this.find(filter)
|
|
104
|
+
.sort(sort)
|
|
105
|
+
.skip(skip)
|
|
106
|
+
.limit(limit);
|
|
97
107
|
if (options.populate) {
|
|
98
|
-
options.populate.split(
|
|
108
|
+
options.populate.split(",").forEach((populateOption) => {
|
|
99
109
|
docsPromise = docsPromise.populate(
|
|
100
110
|
populateOption
|
|
101
|
-
.split(
|
|
111
|
+
.split(".")
|
|
102
112
|
.reverse()
|
|
103
113
|
.reduce((a, b) => ({ path: b, populate: a })),
|
|
104
114
|
);
|
|
@@ -117,9 +127,9 @@ function paginate<T extends Document>(schema: any) {
|
|
|
117
127
|
if (options.populate) {
|
|
118
128
|
results = await this.populate(
|
|
119
129
|
results,
|
|
120
|
-
options.populate.split(
|
|
130
|
+
options.populate.split(",").map((populateOption) =>
|
|
121
131
|
populateOption
|
|
122
|
-
.split(
|
|
132
|
+
.split(".")
|
|
123
133
|
.reverse()
|
|
124
134
|
.reduce((a, b) => ({ path: b, populate: a })),
|
|
125
135
|
),
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
/* eslint-disable no-param-reassign */
|
|
2
|
-
import mongoose from
|
|
3
|
-
import type { Schema, Document, Model, PipelineStage } from
|
|
4
|
-
import type { PaginateOptions, QueryResult } from
|
|
3
|
+
import mongoose from "mongoose";
|
|
4
|
+
import type { Schema, Document, Model, PipelineStage } from "mongoose";
|
|
5
|
+
import type { PaginateOptions, QueryResult } from "./paginate.plugin.js";
|
|
5
6
|
|
|
6
7
|
const paginate = (schema: Schema): void => {
|
|
7
8
|
/**
|
|
@@ -14,19 +15,26 @@ const paginate = (schema: Schema): void => {
|
|
|
14
15
|
filter: Record<string, any> = {},
|
|
15
16
|
options: PaginateOptions = {},
|
|
16
17
|
plugin?: any,
|
|
17
|
-
): Promise<QueryResult
|
|
18
|
+
): Promise<QueryResult<any>> {
|
|
18
19
|
// Parse sorting options
|
|
19
20
|
const sort = options.sortBy
|
|
20
|
-
? options.sortBy
|
|
21
|
-
|
|
22
|
-
acc
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
? options.sortBy
|
|
22
|
+
.split(",")
|
|
23
|
+
.reduce((acc: Record<string, number>, sortOption: string) => {
|
|
24
|
+
const [key, order] = sortOption.split(":");
|
|
25
|
+
acc[key] = order === "desc" ? -1 : 1;
|
|
26
|
+
return acc;
|
|
27
|
+
}, {})
|
|
25
28
|
: { createdAt: -1 };
|
|
26
29
|
|
|
27
30
|
const limit =
|
|
28
|
-
options.limit && parseInt(options.limit.toString(), 10) > 0
|
|
29
|
-
|
|
31
|
+
options.limit && parseInt(options.limit.toString(), 10) > 0
|
|
32
|
+
? parseInt(options.limit.toString(), 10)
|
|
33
|
+
: 10000;
|
|
34
|
+
const page =
|
|
35
|
+
options.page && parseInt(options.page.toString(), 10) > 0
|
|
36
|
+
? parseInt(options.page.toString(), 10)
|
|
37
|
+
: 1;
|
|
30
38
|
const skip = (page - 1) * limit;
|
|
31
39
|
|
|
32
40
|
// Build aggregation pipeline
|
|
@@ -38,7 +46,7 @@ const paginate = (schema: Schema): void => {
|
|
|
38
46
|
|
|
39
47
|
// Helper function to determine if a path is a virtual field
|
|
40
48
|
const isVirtualField = (path: string): boolean => {
|
|
41
|
-
const rootPath = path.split(
|
|
49
|
+
const rootPath = path.split(".")[0];
|
|
42
50
|
return !!schema.virtuals[rootPath];
|
|
43
51
|
};
|
|
44
52
|
|
|
@@ -50,14 +58,16 @@ const paginate = (schema: Schema): void => {
|
|
|
50
58
|
|
|
51
59
|
for (const key in filterObj) {
|
|
52
60
|
if (filterObj.hasOwnProperty(key)) {
|
|
53
|
-
if (key ===
|
|
61
|
+
if (key === "$or" || key === "$and") {
|
|
54
62
|
const mainArray: Record<string, any>[] = [];
|
|
55
63
|
const virtualArray: Record<string, any>[] = [];
|
|
56
64
|
|
|
57
65
|
filterObj[key].forEach((item: Record<string, any>) => {
|
|
58
|
-
const { main: itemMain, virtual: itemVirtual } =
|
|
66
|
+
const { main: itemMain, virtual: itemVirtual } =
|
|
67
|
+
separateFilter(item);
|
|
59
68
|
if (Object.keys(itemMain).length > 0) mainArray.push(itemMain);
|
|
60
|
-
if (Object.keys(itemVirtual).length > 0)
|
|
69
|
+
if (Object.keys(itemVirtual).length > 0)
|
|
70
|
+
virtualArray.push(itemVirtual);
|
|
61
71
|
});
|
|
62
72
|
|
|
63
73
|
if (mainArray.length > 0) main[key] = mainArray;
|
|
@@ -87,8 +97,8 @@ const paginate = (schema: Schema): void => {
|
|
|
87
97
|
|
|
88
98
|
// Handle virtual fields population
|
|
89
99
|
if (options.populate) {
|
|
90
|
-
options.populate.split(
|
|
91
|
-
const paths = populateOption.split(
|
|
100
|
+
options.populate.split(",").forEach((populateOption: string) => {
|
|
101
|
+
const paths = populateOption.split(".");
|
|
92
102
|
const localField = paths[0];
|
|
93
103
|
const virtual = schema.virtuals[localField];
|
|
94
104
|
if (!virtual) {
|
|
@@ -140,7 +150,9 @@ const paginate = (schema: Schema): void => {
|
|
|
140
150
|
|
|
141
151
|
// Handle fuzzy search (if applicable)
|
|
142
152
|
if (this.fuzzySearch && options.fuzzySearch) {
|
|
143
|
-
throw new Error(
|
|
153
|
+
throw new Error(
|
|
154
|
+
"Fuzzy search is not supported with aggregation in this paginate function.",
|
|
155
|
+
);
|
|
144
156
|
}
|
|
145
157
|
|
|
146
158
|
// Add sorting, skipping, and limiting stages
|
|
@@ -151,7 +163,7 @@ const paginate = (schema: Schema): void => {
|
|
|
151
163
|
// Rename root _id to id
|
|
152
164
|
pipeline.push({
|
|
153
165
|
$addFields: {
|
|
154
|
-
id:
|
|
166
|
+
id: "$_id",
|
|
155
167
|
},
|
|
156
168
|
});
|
|
157
169
|
pipeline.push({
|
|
@@ -166,7 +178,7 @@ const paginate = (schema: Schema): void => {
|
|
|
166
178
|
$facet: {
|
|
167
179
|
metadata: [
|
|
168
180
|
{
|
|
169
|
-
$count:
|
|
181
|
+
$count: "totalResults",
|
|
170
182
|
},
|
|
171
183
|
],
|
|
172
184
|
data: pipeline,
|
|
@@ -174,7 +186,7 @@ const paginate = (schema: Schema): void => {
|
|
|
174
186
|
},
|
|
175
187
|
{
|
|
176
188
|
$addFields: {
|
|
177
|
-
totalResults: { $arrayElemAt: [
|
|
189
|
+
totalResults: { $arrayElemAt: ["$metadata.totalResults", 0] },
|
|
178
190
|
},
|
|
179
191
|
},
|
|
180
192
|
];
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
function simplePopulate(populate: string): Record<string, any> {
|
|
2
2
|
let docsPromise: Record<string, any> = {};
|
|
3
|
-
populate.split(
|
|
3
|
+
populate.split(",").forEach((populateOption) => {
|
|
4
4
|
docsPromise = populateOption
|
|
5
|
-
.split(
|
|
5
|
+
.split(".")
|
|
6
6
|
.reverse()
|
|
7
|
-
.reduce((
|
|
7
|
+
.reduce<Record<string, any>>((acc, key) => {
|
|
8
|
+
if (Object.keys(acc).length === 0) {
|
|
9
|
+
return { path: key };
|
|
10
|
+
}
|
|
11
|
+
return { path: key, populate: acc };
|
|
12
|
+
}, {});
|
|
8
13
|
});
|
|
9
14
|
return docsPromise;
|
|
10
15
|
}
|
|
@@ -6,9 +6,14 @@
|
|
|
6
6
|
* - replaces _id with id
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// @ts-nocheck
|
|
10
|
+
import type { Schema, Document } from "mongoose";
|
|
11
|
+
|
|
12
|
+
const deleteAtPath = (
|
|
13
|
+
obj: Record<string, any>,
|
|
14
|
+
path: string[],
|
|
15
|
+
index: number,
|
|
16
|
+
): void => {
|
|
12
17
|
if (index === path.length - 1) {
|
|
13
18
|
delete obj[path[index]];
|
|
14
19
|
return;
|
|
@@ -17,7 +22,9 @@ const deleteAtPath = (obj: Record<string, any>, path: string[], index: number):
|
|
|
17
22
|
};
|
|
18
23
|
|
|
19
24
|
const toJSON = (schema: Schema, timestamps: boolean = false): void => {
|
|
20
|
-
let transform:
|
|
25
|
+
let transform:
|
|
26
|
+
| ((doc: Document, ret: Record<string, any>, options: any) => any)
|
|
27
|
+
| undefined;
|
|
21
28
|
|
|
22
29
|
if (schema.options.toJSON && schema.options.toJSON.transform) {
|
|
23
30
|
transform = schema.options.toJSON.transform;
|
|
@@ -28,7 +35,7 @@ const toJSON = (schema: Schema, timestamps: boolean = false): void => {
|
|
|
28
35
|
transform(doc: Document, ret: Record<string, any>, options: any): any {
|
|
29
36
|
Object.keys(schema.paths).forEach((path) => {
|
|
30
37
|
if (schema.paths[path].options && schema.paths[path].options.private) {
|
|
31
|
-
deleteAtPath(ret, path.split(
|
|
38
|
+
deleteAtPath(ret, path.split("."), 0);
|
|
32
39
|
}
|
|
33
40
|
});
|
|
34
41
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
import mongoose, { Schema, Document, Model } from "mongoose";
|
|
2
3
|
import { toJSON, paginate } from "../models/plugins/index.js";
|
|
3
4
|
|
|
@@ -54,9 +55,8 @@ organizationSchema.plugin(toJSON);
|
|
|
54
55
|
organizationSchema.plugin(paginate);
|
|
55
56
|
|
|
56
57
|
// Define the model
|
|
57
|
-
const Organization: Model<IOrganization> =
|
|
58
|
-
|
|
59
|
-
organizationSchema
|
|
60
|
-
);
|
|
58
|
+
const Organization: Model<IOrganization> =
|
|
59
|
+
(mongoose.models.Organization as Model<IOrganization>) ||
|
|
60
|
+
mongoose.model<IOrganization>("Organization", organizationSchema);
|
|
61
61
|
|
|
62
62
|
export default Organization;
|
|
@@ -31,7 +31,7 @@ export const organizationsRouteSpecs: RouteSpec[] = [
|
|
|
31
31
|
path: "/",
|
|
32
32
|
validate: [auth("manageUsers")],
|
|
33
33
|
requestSchema: createOrganizationSchema,
|
|
34
|
-
responseSchema:
|
|
34
|
+
responseSchema: organizationResponseSchema,
|
|
35
35
|
handler: createOrganization,
|
|
36
36
|
summary: "Create a new organization",
|
|
37
37
|
description: "Creates a new organization with the provided details.",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
import httpStatus from "http-status";
|
|
2
3
|
import { ObjectId } from "mongoose";
|
|
3
4
|
import Organization from "./organizations.model.js";
|
|
4
|
-
import type { IOrganization
|
|
5
|
+
import type { IOrganization } from "./organizations.model.js";
|
|
6
|
+
import type { QueryResult } from "../models/plugins/paginate.plugin.js";
|
|
5
7
|
import ApiError from "../utils/ApiError.js";
|
|
6
8
|
|
|
7
9
|
const createOrganization = async (
|
|
@@ -13,17 +15,24 @@ const createOrganization = async (
|
|
|
13
15
|
|
|
14
16
|
const queryOrganizations = async (
|
|
15
17
|
filter: Record<string, any>,
|
|
16
|
-
options: {
|
|
17
|
-
|
|
18
|
+
options: {
|
|
19
|
+
sortBy?: string;
|
|
20
|
+
limit?: number;
|
|
21
|
+
page?: number;
|
|
22
|
+
populate?: string;
|
|
23
|
+
},
|
|
24
|
+
): Promise<QueryResult<IOrganization>> => {
|
|
18
25
|
const organizations = await Organization.paginate(filter, options);
|
|
19
26
|
return organizations;
|
|
20
27
|
};
|
|
21
28
|
|
|
22
29
|
const queryOrganizationsByUser = async (
|
|
23
|
-
organizationsList: Array<{ organization
|
|
24
|
-
): Promise<QueryResult | false> => {
|
|
30
|
+
organizationsList: Array<{ organization?: ObjectId | null }>,
|
|
31
|
+
): Promise<QueryResult<IOrganization> | false> => {
|
|
25
32
|
if (!organizationsList) return false;
|
|
26
|
-
const organizationIds = organizationsList
|
|
33
|
+
const organizationIds = organizationsList
|
|
34
|
+
.map((e) => e.organization)
|
|
35
|
+
.filter((id): id is ObjectId => Boolean(id));
|
|
27
36
|
const organizations = await Organization.paginate(
|
|
28
37
|
{
|
|
29
38
|
_id: { $in: organizationIds },
|
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
import httpStatus from "http-status";
|
|
2
2
|
import catchAsync from "../utils/catchAsync.js";
|
|
3
3
|
import pdfService from "./pdf.service.js";
|
|
4
|
+
import ApiError from "../utils/ApiError.js";
|
|
4
5
|
|
|
5
6
|
export const generatePdfFromUrl = catchAsync(async (req, res) => {
|
|
6
7
|
const fileName = "memo-print";
|
|
7
8
|
|
|
8
9
|
const authHeader = req.headers["authorization"];
|
|
10
|
+
if (!authHeader) {
|
|
11
|
+
throw new ApiError(httpStatus.UNAUTHORIZED, "Missing Authorization header");
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
const token = authHeader.split(" ")[1];
|
|
15
|
+
if (!token) {
|
|
16
|
+
throw new ApiError(httpStatus.UNAUTHORIZED, "Missing bearer token");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const urlPath =
|
|
20
|
+
typeof req.query.urlPath === "string" ? req.query.urlPath : undefined;
|
|
21
|
+
if (!urlPath) {
|
|
22
|
+
throw new ApiError(
|
|
23
|
+
httpStatus.BAD_REQUEST,
|
|
24
|
+
"Missing urlPath query parameter",
|
|
25
|
+
);
|
|
26
|
+
}
|
|
10
27
|
|
|
11
|
-
const result = await pdfService.generatePdfFromUrl({
|
|
28
|
+
const result = await pdfService.generatePdfFromUrl({ urlPath, token });
|
|
12
29
|
|
|
13
30
|
res.status(httpStatus.CREATED).send({ signed: result });
|
|
14
31
|
});
|
package/src/pdf/pdf.service.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import puppeteer from
|
|
2
|
-
import { v4 as uuidv4 } from
|
|
3
|
-
import AWS from
|
|
4
|
-
import path from
|
|
1
|
+
import puppeteer from "puppeteer";
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import AWS from "aws-sdk";
|
|
4
|
+
import path from "path";
|
|
5
5
|
|
|
6
6
|
// Configure AWS SDK
|
|
7
7
|
const s3 = new AWS.S3({
|
|
@@ -11,13 +11,16 @@ const s3 = new AWS.S3({
|
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
// Function to upload a file to S3
|
|
14
|
-
const uploadBuffer = async (
|
|
14
|
+
const uploadBuffer = async (
|
|
15
|
+
buffer: Buffer,
|
|
16
|
+
fileName: string,
|
|
17
|
+
): Promise<string> => {
|
|
15
18
|
const params = {
|
|
16
19
|
Bucket: process.env.AWS_S3_BUCKET_NAME!,
|
|
17
20
|
Key: fileName,
|
|
18
21
|
Body: buffer,
|
|
19
|
-
ContentType:
|
|
20
|
-
ACL:
|
|
22
|
+
ContentType: "application/pdf",
|
|
23
|
+
ACL: "private",
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
const data = await s3.upload(params).promise();
|
|
@@ -32,7 +35,7 @@ const generateSignedUrl = (fileName: string): string => {
|
|
|
32
35
|
Expires: 60 * 60, // URL expiration time in seconds
|
|
33
36
|
};
|
|
34
37
|
|
|
35
|
-
return s3.getSignedUrl(
|
|
38
|
+
return s3.getSignedUrl("getObject", params);
|
|
36
39
|
};
|
|
37
40
|
|
|
38
41
|
interface GeneratePdfOptions {
|
|
@@ -40,7 +43,10 @@ interface GeneratePdfOptions {
|
|
|
40
43
|
token: string;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
const generatePdfFromUrl = async ({
|
|
46
|
+
const generatePdfFromUrl = async ({
|
|
47
|
+
urlPath,
|
|
48
|
+
token,
|
|
49
|
+
}: GeneratePdfOptions): Promise<string> => {
|
|
44
50
|
const domain = process.env.FRONTEND_URL!;
|
|
45
51
|
const browser = await puppeteer.launch({
|
|
46
52
|
defaultViewport: {
|
|
@@ -49,7 +55,7 @@ const generatePdfFromUrl = async ({ urlPath, token }: GeneratePdfOptions): Promi
|
|
|
49
55
|
deviceScaleFactor: 1,
|
|
50
56
|
},
|
|
51
57
|
executablePath: process.env.CHROME_BIN,
|
|
52
|
-
args: [
|
|
58
|
+
args: ["--no-sandbox"],
|
|
53
59
|
});
|
|
54
60
|
|
|
55
61
|
const page = await browser.newPage();
|
|
@@ -58,22 +64,25 @@ const generatePdfFromUrl = async ({ urlPath, token }: GeneratePdfOptions): Promi
|
|
|
58
64
|
|
|
59
65
|
// Set the token in local storage
|
|
60
66
|
await page.evaluate((token) => {
|
|
61
|
-
localStorage.setItem(
|
|
67
|
+
localStorage.setItem("print-token", token);
|
|
62
68
|
}, token);
|
|
63
69
|
|
|
64
|
-
await page.goto(domain + urlPath, { waitUntil:
|
|
70
|
+
await page.goto(domain + urlPath, { waitUntil: "networkidle2" });
|
|
65
71
|
|
|
66
|
-
await page.waitForSelector(
|
|
72
|
+
await page.waitForSelector(".pdf-render-complete");
|
|
67
73
|
|
|
68
|
-
const pdf = await page.pdf({ format:
|
|
74
|
+
const pdf = await page.pdf({ format: "A4", printBackground: true });
|
|
69
75
|
|
|
70
76
|
await page.evaluate(() => {
|
|
71
|
-
localStorage.setItem(
|
|
77
|
+
localStorage.setItem("print-token", "");
|
|
72
78
|
});
|
|
73
79
|
|
|
74
80
|
await browser.close();
|
|
75
81
|
|
|
76
|
-
const fileUrl = await uploadBuffer(
|
|
82
|
+
const fileUrl = await uploadBuffer(
|
|
83
|
+
Buffer.from(pdf),
|
|
84
|
+
`download-${uuidv4()}.pdf`,
|
|
85
|
+
);
|
|
77
86
|
|
|
78
87
|
console.log(`File uploaded successfully. File URL: ${fileUrl}`);
|
|
79
88
|
|
|
@@ -38,16 +38,15 @@ export const createToken = async (
|
|
|
38
38
|
|
|
39
39
|
// Get a token by ID
|
|
40
40
|
export const getToken = async (
|
|
41
|
-
req: Request
|
|
41
|
+
req: Request<{ tokenId: string }>,
|
|
42
42
|
res: Response,
|
|
43
43
|
next: NextFunction,
|
|
44
44
|
): Promise<void> => {
|
|
45
45
|
try {
|
|
46
46
|
const token = await tokensService.getTokenById(req.params.tokenId);
|
|
47
47
|
if (!token) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.send({ message: "Token not found" });
|
|
48
|
+
res.status(httpStatus.NOT_FOUND).send({ message: "Token not found" });
|
|
49
|
+
return;
|
|
51
50
|
}
|
|
52
51
|
res.send(token);
|
|
53
52
|
} catch (error) {
|
|
@@ -57,16 +56,15 @@ export const getToken = async (
|
|
|
57
56
|
|
|
58
57
|
// Delete a token by ID
|
|
59
58
|
export const deleteToken = async (
|
|
60
|
-
req: Request
|
|
59
|
+
req: Request<{ tokenId: string }>,
|
|
61
60
|
res: Response,
|
|
62
61
|
next: NextFunction,
|
|
63
62
|
): Promise<void> => {
|
|
64
63
|
try {
|
|
65
64
|
const token = await tokensService.deleteTokenById(req.params.tokenId);
|
|
66
65
|
if (!token) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
.send({ message: "Token not found" });
|
|
66
|
+
res.status(httpStatus.NOT_FOUND).send({ message: "Token not found" });
|
|
67
|
+
return;
|
|
70
68
|
}
|
|
71
69
|
res.status(httpStatus.NO_CONTENT).send();
|
|
72
70
|
} catch (error) {
|