@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
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/* eslint-disable no-param-reassign */
|
|
3
|
+
import mongoose from "mongoose";
|
|
4
|
+
const paginate = (schema) => {
|
|
5
|
+
/**
|
|
6
|
+
* Query for documents with pagination
|
|
7
|
+
* @param {Object} [filter] - Mongo filter
|
|
8
|
+
* @param {PaginateOptions} [options] - Query options
|
|
9
|
+
* @returns {Promise<QueryResult>}
|
|
10
|
+
*/
|
|
11
|
+
schema.statics.paginate = async function (filter = {}, options = {}, plugin) {
|
|
12
|
+
// Parse sorting options
|
|
13
|
+
const sort = options.sortBy
|
|
14
|
+
? options.sortBy
|
|
15
|
+
.split(",")
|
|
16
|
+
.reduce((acc, sortOption) => {
|
|
17
|
+
const [key, order] = sortOption.split(":");
|
|
18
|
+
acc[key] = order === "desc" ? -1 : 1;
|
|
19
|
+
return acc;
|
|
20
|
+
}, {})
|
|
21
|
+
: { createdAt: -1 };
|
|
22
|
+
const limit = options.limit && parseInt(options.limit.toString(), 10) > 0
|
|
23
|
+
? parseInt(options.limit.toString(), 10)
|
|
24
|
+
: 10000;
|
|
25
|
+
const page = options.page && parseInt(options.page.toString(), 10) > 0
|
|
26
|
+
? parseInt(options.page.toString(), 10)
|
|
27
|
+
: 1;
|
|
28
|
+
const skip = (page - 1) * limit;
|
|
29
|
+
// Build aggregation pipeline
|
|
30
|
+
const pipeline = [];
|
|
31
|
+
let mainMatch = {};
|
|
32
|
+
let virtualMatch = {};
|
|
33
|
+
let hasVirtualFields = false;
|
|
34
|
+
// Helper function to determine if a path is a virtual field
|
|
35
|
+
const isVirtualField = (path) => {
|
|
36
|
+
const rootPath = path.split(".")[0];
|
|
37
|
+
return !!schema.virtuals[rootPath];
|
|
38
|
+
};
|
|
39
|
+
// Separate filter into main collection fields and virtual fields
|
|
40
|
+
if (filter) {
|
|
41
|
+
const separateFilter = (filterObj) => {
|
|
42
|
+
const main = {};
|
|
43
|
+
const virtual = {};
|
|
44
|
+
for (const key in filterObj) {
|
|
45
|
+
if (filterObj.hasOwnProperty(key)) {
|
|
46
|
+
if (key === "$or" || key === "$and") {
|
|
47
|
+
const mainArray = [];
|
|
48
|
+
const virtualArray = [];
|
|
49
|
+
filterObj[key].forEach((item) => {
|
|
50
|
+
const { main: itemMain, virtual: itemVirtual } = separateFilter(item);
|
|
51
|
+
if (Object.keys(itemMain).length > 0)
|
|
52
|
+
mainArray.push(itemMain);
|
|
53
|
+
if (Object.keys(itemVirtual).length > 0)
|
|
54
|
+
virtualArray.push(itemVirtual);
|
|
55
|
+
});
|
|
56
|
+
if (mainArray.length > 0)
|
|
57
|
+
main[key] = mainArray;
|
|
58
|
+
if (virtualArray.length > 0)
|
|
59
|
+
virtual[key] = virtualArray;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
if (isVirtualField(key)) {
|
|
63
|
+
virtual[key] = filterObj[key];
|
|
64
|
+
hasVirtualFields = true;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
main[key] = filterObj[key];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { main, virtual };
|
|
73
|
+
};
|
|
74
|
+
const { main, virtual } = separateFilter(filter);
|
|
75
|
+
mainMatch = main;
|
|
76
|
+
virtualMatch = virtual;
|
|
77
|
+
}
|
|
78
|
+
// Add main collection $match stage
|
|
79
|
+
if (Object.keys(mainMatch).length > 0) {
|
|
80
|
+
pipeline.push({ $match: mainMatch });
|
|
81
|
+
}
|
|
82
|
+
// Handle virtual fields population
|
|
83
|
+
if (options.populate) {
|
|
84
|
+
options.populate.split(",").forEach((populateOption) => {
|
|
85
|
+
const paths = populateOption.split(".");
|
|
86
|
+
const localField = paths[0];
|
|
87
|
+
const virtual = schema.virtuals[localField];
|
|
88
|
+
if (!virtual) {
|
|
89
|
+
throw new Error(`Cannot populate unknown field: ${localField}`);
|
|
90
|
+
}
|
|
91
|
+
const refModel = virtual.options.ref;
|
|
92
|
+
const localFieldOption = virtual.options.localField;
|
|
93
|
+
const foreignFieldOption = virtual.options.foreignField;
|
|
94
|
+
const asField = localField;
|
|
95
|
+
const lookupStage = {
|
|
96
|
+
$lookup: {
|
|
97
|
+
from: mongoose.model(refModel).collection.name,
|
|
98
|
+
localField: localFieldOption,
|
|
99
|
+
foreignField: foreignFieldOption,
|
|
100
|
+
as: asField,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
pipeline.push(lookupStage);
|
|
104
|
+
if (virtual.options.justOne) {
|
|
105
|
+
pipeline.push({
|
|
106
|
+
$unwind: {
|
|
107
|
+
path: `$${asField}`,
|
|
108
|
+
preserveNullAndEmptyArrays: true,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// Add $addFields stage to rename nested _id to id in populated documents
|
|
113
|
+
pipeline.push({
|
|
114
|
+
$addFields: {
|
|
115
|
+
[`${asField}.id`]: `$${asField}._id`,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
pipeline.push({
|
|
119
|
+
$project: {
|
|
120
|
+
[`${asField}._id`]: 0,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// Add virtual fields $match stage
|
|
126
|
+
if (hasVirtualFields && Object.keys(virtualMatch).length > 0) {
|
|
127
|
+
pipeline.push({ $match: virtualMatch });
|
|
128
|
+
}
|
|
129
|
+
// Handle fuzzy search (if applicable)
|
|
130
|
+
if (this.fuzzySearch && options.fuzzySearch) {
|
|
131
|
+
throw new Error("Fuzzy search is not supported with aggregation in this paginate function.");
|
|
132
|
+
}
|
|
133
|
+
// Add sorting, skipping, and limiting stages
|
|
134
|
+
pipeline.push({ $sort: sort });
|
|
135
|
+
pipeline.push({ $skip: skip });
|
|
136
|
+
pipeline.push({ $limit: limit });
|
|
137
|
+
// Rename root _id to id
|
|
138
|
+
pipeline.push({
|
|
139
|
+
$addFields: {
|
|
140
|
+
id: "$_id",
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
pipeline.push({
|
|
144
|
+
$project: {
|
|
145
|
+
_id: 0,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
// Use $facet to get both the results and the total count
|
|
149
|
+
const facetPipeline = [
|
|
150
|
+
{
|
|
151
|
+
$facet: {
|
|
152
|
+
metadata: [
|
|
153
|
+
{
|
|
154
|
+
$count: "totalResults",
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
data: pipeline,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
$addFields: {
|
|
162
|
+
totalResults: { $arrayElemAt: ["$metadata.totalResults", 0] },
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
// Execute the aggregation pipeline
|
|
167
|
+
const aggResult = await this.aggregate(facetPipeline).exec();
|
|
168
|
+
// Extract results and total count
|
|
169
|
+
let totalResults = 0;
|
|
170
|
+
let results = [];
|
|
171
|
+
if (aggResult && aggResult.length > 0) {
|
|
172
|
+
totalResults = aggResult[0].totalResults || 0;
|
|
173
|
+
results = aggResult[0].data || [];
|
|
174
|
+
}
|
|
175
|
+
const totalPages = Math.ceil(totalResults / limit) || 1;
|
|
176
|
+
return {
|
|
177
|
+
results,
|
|
178
|
+
page,
|
|
179
|
+
limit,
|
|
180
|
+
totalPages,
|
|
181
|
+
totalResults,
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
export default paginate;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function simplePopulate(populate) {
|
|
2
|
+
let docsPromise = {};
|
|
3
|
+
populate.split(",").forEach((populateOption) => {
|
|
4
|
+
docsPromise = populateOption
|
|
5
|
+
.split(".")
|
|
6
|
+
.reverse()
|
|
7
|
+
.reduce((acc, key) => {
|
|
8
|
+
if (Object.keys(acc).length === 0) {
|
|
9
|
+
return { path: key };
|
|
10
|
+
}
|
|
11
|
+
return { path: key, populate: acc };
|
|
12
|
+
}, {});
|
|
13
|
+
});
|
|
14
|
+
return docsPromise;
|
|
15
|
+
}
|
|
16
|
+
export default simplePopulate;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
const deleteAtPath = (obj, path, index) => {
|
|
3
|
+
if (index === path.length - 1) {
|
|
4
|
+
delete obj[path[index]];
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
deleteAtPath(obj[path[index]], path, index + 1);
|
|
8
|
+
};
|
|
9
|
+
const toJSON = (schema, timestamps = false) => {
|
|
10
|
+
let transform;
|
|
11
|
+
if (schema.options.toJSON && schema.options.toJSON.transform) {
|
|
12
|
+
transform = schema.options.toJSON.transform;
|
|
13
|
+
}
|
|
14
|
+
schema.options.toJSON = {
|
|
15
|
+
...schema.options.toJSON,
|
|
16
|
+
transform(doc, ret, options) {
|
|
17
|
+
Object.keys(schema.paths).forEach((path) => {
|
|
18
|
+
if (schema.paths[path].options && schema.paths[path].options.private) {
|
|
19
|
+
deleteAtPath(ret, path.split("."), 0);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
ret.id = ret._id.toString();
|
|
23
|
+
delete ret._id;
|
|
24
|
+
delete ret.__v;
|
|
25
|
+
if (!timestamps) {
|
|
26
|
+
delete ret.createdAt;
|
|
27
|
+
delete ret.updatedAt;
|
|
28
|
+
}
|
|
29
|
+
if (transform) {
|
|
30
|
+
return transform(doc, ret, options);
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
export default toJSON;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import httpStatus from "http-status";
|
|
2
|
+
import pick from "../utils/pick.js";
|
|
3
|
+
import ApiError from "../utils/ApiError.js";
|
|
4
|
+
import catchAsync from "../utils/catchAsync.js";
|
|
5
|
+
import usersService from "../users/users.service";
|
|
6
|
+
import organizationsService, { deleteOrganizationById, } from "./organizations.service.js";
|
|
7
|
+
import mongoose from "mongoose";
|
|
8
|
+
import { filterOptions } from "../utils/filterOptions.js";
|
|
9
|
+
const ObjectId = mongoose.Types.ObjectId;
|
|
10
|
+
export const createOrganization = catchAsync(async (req, res) => {
|
|
11
|
+
const organization = await organizationsService.createOrganization(req.body);
|
|
12
|
+
const user = await usersService.createUser({
|
|
13
|
+
organization: organization._id,
|
|
14
|
+
owner: res.req.auth.sub,
|
|
15
|
+
role: "admin",
|
|
16
|
+
category: "relative",
|
|
17
|
+
status: "accept",
|
|
18
|
+
});
|
|
19
|
+
res.status(httpStatus.CREATED).send(organization);
|
|
20
|
+
});
|
|
21
|
+
export const getOrganizations = catchAsync(async (req, res) => {
|
|
22
|
+
const filter = pick(req.query, ["name", "kind"]);
|
|
23
|
+
const options = pick(req.query, ["sortBy", "limit", "page"]);
|
|
24
|
+
console.log("getOrganizations", req.query, filter, options);
|
|
25
|
+
const filteredOptions = filterOptions(req.query, filter, {
|
|
26
|
+
objectIds: ["_id", "patient"],
|
|
27
|
+
search: ["name", "kind"],
|
|
28
|
+
});
|
|
29
|
+
const optionsPopulate = {
|
|
30
|
+
...options,
|
|
31
|
+
// fuzzySearch: req.query.search ? { search: req.query.search, fields: ['name', 'kind'] } : undefined,
|
|
32
|
+
populate: "usersData,devicesData",
|
|
33
|
+
};
|
|
34
|
+
const result = await organizationsService.queryOrganizations(filteredOptions, optionsPopulate);
|
|
35
|
+
res.send(result);
|
|
36
|
+
});
|
|
37
|
+
export const queryOrganizationsByUser = catchAsync(async (req, res) => {
|
|
38
|
+
const users = await usersService.getUsersByOwner(res.req.auth.sub);
|
|
39
|
+
const result = await organizationsService.queryOrganizationsByUser(users);
|
|
40
|
+
res.send(result);
|
|
41
|
+
});
|
|
42
|
+
export const getOrganizationById = catchAsync(async (req, res) => {
|
|
43
|
+
const organization = await organizationsService.getOrganizationById(req.params.organizationId);
|
|
44
|
+
if (!organization) {
|
|
45
|
+
throw new ApiError(httpStatus.NOT_FOUND, "Organization not found");
|
|
46
|
+
}
|
|
47
|
+
res.send(organization);
|
|
48
|
+
});
|
|
49
|
+
export const updateOrganization = catchAsync(async (req, res) => {
|
|
50
|
+
const user = await organizationsService.updateOrganizationById(req.params.organizationId, req.body);
|
|
51
|
+
res.send(user);
|
|
52
|
+
});
|
|
53
|
+
export const deleteOrganization = catchAsync(async (req, res) => {
|
|
54
|
+
const entry = await deleteOrganizationById(req.params.organizationId);
|
|
55
|
+
res.send(entry);
|
|
56
|
+
});
|
|
57
|
+
export default {
|
|
58
|
+
createOrganization,
|
|
59
|
+
getOrganizations,
|
|
60
|
+
queryOrganizationsByUser,
|
|
61
|
+
getOrganizationById,
|
|
62
|
+
updateOrganization,
|
|
63
|
+
deleteOrganization,
|
|
64
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import mongoose, { Schema } from "mongoose";
|
|
3
|
+
import { toJSON, paginate } from "../models/plugins/index.js";
|
|
4
|
+
// Define the schema
|
|
5
|
+
const organizationSchema = new Schema({
|
|
6
|
+
name: {
|
|
7
|
+
type: String,
|
|
8
|
+
trim: true,
|
|
9
|
+
// required: true,
|
|
10
|
+
},
|
|
11
|
+
meta: { type: Object },
|
|
12
|
+
kind: { type: String },
|
|
13
|
+
}, {
|
|
14
|
+
timestamps: true,
|
|
15
|
+
toObject: {
|
|
16
|
+
virtuals: true,
|
|
17
|
+
},
|
|
18
|
+
toJSON: {
|
|
19
|
+
virtuals: true,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
// Virtuals
|
|
23
|
+
organizationSchema.virtual("usersData", {
|
|
24
|
+
ref: "User",
|
|
25
|
+
localField: "_id",
|
|
26
|
+
foreignField: "organization",
|
|
27
|
+
justOne: false,
|
|
28
|
+
});
|
|
29
|
+
organizationSchema.virtual("devicesData", {
|
|
30
|
+
ref: "Device",
|
|
31
|
+
localField: "_id",
|
|
32
|
+
foreignField: "organization",
|
|
33
|
+
justOne: false,
|
|
34
|
+
});
|
|
35
|
+
// Add plugins
|
|
36
|
+
organizationSchema.plugin(toJSON);
|
|
37
|
+
organizationSchema.plugin(paginate);
|
|
38
|
+
// Define the model
|
|
39
|
+
const Organization = mongoose.models.Organization ||
|
|
40
|
+
mongoose.model("Organization", organizationSchema);
|
|
41
|
+
export default Organization;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import buildRouterAndDocs from "../utils/buildRouterAndDocs.js";
|
|
3
|
+
import { createOrganizationSchema, updateOrganizationSchema, getOrganizationByIdSchema, queryOrganizationsSchema, } from "./organizations.validation.js";
|
|
4
|
+
import { organizationResponseSchema } from "./organizations.schemas.js";
|
|
5
|
+
import { createOrganization, getOrganizations, queryOrganizationsByUser, getOrganizationById, updateOrganization, deleteOrganization, } from "./organizations.controller.js";
|
|
6
|
+
import auth from "../middlewares/auth.js";
|
|
7
|
+
import { validateAdmin } from "../middlewares/validateAdmin.js";
|
|
8
|
+
import { validateOrganization } from "../middlewares/validateOrganization.js";
|
|
9
|
+
import { validateOrganizationDelete, validateOrganizationUpdate, } from "../middlewares/validateAction.js";
|
|
10
|
+
export const organizationsRouteSpecs = [
|
|
11
|
+
{
|
|
12
|
+
method: "post",
|
|
13
|
+
path: "/",
|
|
14
|
+
validate: [auth("manageUsers")],
|
|
15
|
+
requestSchema: createOrganizationSchema,
|
|
16
|
+
responseSchema: organizationResponseSchema,
|
|
17
|
+
handler: createOrganization,
|
|
18
|
+
summary: "Create a new organization",
|
|
19
|
+
description: "Creates a new organization with the provided details.",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
method: "get",
|
|
23
|
+
path: "/",
|
|
24
|
+
validate: [auth("getUsers")],
|
|
25
|
+
requestSchema: {},
|
|
26
|
+
responseSchema: organizationResponseSchema.array(),
|
|
27
|
+
handler: queryOrganizationsByUser,
|
|
28
|
+
summary: "Get all organizations",
|
|
29
|
+
description: "Retrieves all organizations accessible to the current user.",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
method: "get",
|
|
33
|
+
path: "/all",
|
|
34
|
+
validate: [auth("getUsers"), validateAdmin],
|
|
35
|
+
requestSchema: queryOrganizationsSchema,
|
|
36
|
+
responseSchema: organizationResponseSchema.array(),
|
|
37
|
+
handler: getOrganizations,
|
|
38
|
+
summary: "Get all organizations",
|
|
39
|
+
description: "Retrieves all organizations in the system.",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
method: "get",
|
|
43
|
+
path: "/:organizationId",
|
|
44
|
+
validate: [auth("getUsers"), validateOrganization],
|
|
45
|
+
requestSchema: getOrganizationByIdSchema,
|
|
46
|
+
responseSchema: organizationResponseSchema,
|
|
47
|
+
handler: getOrganizationById,
|
|
48
|
+
summary: "Get an organization by ID",
|
|
49
|
+
description: "Retrieves the details of a specific organization by its ID.",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
method: "patch",
|
|
53
|
+
path: "/:organizationId",
|
|
54
|
+
validate: [
|
|
55
|
+
auth("manageUsers"),
|
|
56
|
+
validateOrganization,
|
|
57
|
+
validateOrganizationUpdate,
|
|
58
|
+
],
|
|
59
|
+
requestSchema: updateOrganizationSchema,
|
|
60
|
+
responseSchema: organizationResponseSchema,
|
|
61
|
+
handler: updateOrganization,
|
|
62
|
+
summary: "Update an organization by ID",
|
|
63
|
+
description: "Updates the details of a specific organization by its ID.",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
method: "post",
|
|
67
|
+
path: "/:organizationId",
|
|
68
|
+
validate: [
|
|
69
|
+
auth("manageUsers"),
|
|
70
|
+
validateOrganization,
|
|
71
|
+
validateOrganizationUpdate,
|
|
72
|
+
],
|
|
73
|
+
requestSchema: updateOrganizationSchema,
|
|
74
|
+
responseSchema: organizationResponseSchema,
|
|
75
|
+
handler: updateOrganization,
|
|
76
|
+
summary: "Update an organization by ID",
|
|
77
|
+
description: "Updates the details of a specific organization by its ID.",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
method: "delete",
|
|
81
|
+
path: "/:organizationId",
|
|
82
|
+
validate: [
|
|
83
|
+
auth("manageUsers"),
|
|
84
|
+
validateOrganization,
|
|
85
|
+
validateOrganizationDelete,
|
|
86
|
+
],
|
|
87
|
+
requestSchema: getOrganizationByIdSchema,
|
|
88
|
+
responseSchema: organizationResponseSchema,
|
|
89
|
+
handler: deleteOrganization,
|
|
90
|
+
summary: "Delete an organization by ID",
|
|
91
|
+
description: "Deletes a specific organization by its ID.",
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
const router = Router();
|
|
95
|
+
buildRouterAndDocs(router, organizationsRouteSpecs, "/organizations", [
|
|
96
|
+
"Organizations",
|
|
97
|
+
]);
|
|
98
|
+
export default router;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import httpStatus from "http-status";
|
|
3
|
+
import Organization from "./organizations.model.js";
|
|
4
|
+
import ApiError from "../utils/ApiError.js";
|
|
5
|
+
const createOrganization = async (organizationBody) => {
|
|
6
|
+
const organization = await Organization.create(organizationBody);
|
|
7
|
+
return organization;
|
|
8
|
+
};
|
|
9
|
+
const queryOrganizations = async (filter, options) => {
|
|
10
|
+
const organizations = await Organization.paginate(filter, options);
|
|
11
|
+
return organizations;
|
|
12
|
+
};
|
|
13
|
+
const queryOrganizationsByUser = async (organizationsList) => {
|
|
14
|
+
if (!organizationsList)
|
|
15
|
+
return false;
|
|
16
|
+
const organizationIds = organizationsList
|
|
17
|
+
.map((e) => e.organization)
|
|
18
|
+
.filter((id) => Boolean(id));
|
|
19
|
+
const organizations = await Organization.paginate({
|
|
20
|
+
_id: { $in: organizationIds },
|
|
21
|
+
}, {});
|
|
22
|
+
return organizations;
|
|
23
|
+
};
|
|
24
|
+
export const getOrganizationById = async (id) => {
|
|
25
|
+
return Organization.findById(id);
|
|
26
|
+
};
|
|
27
|
+
export const getOrganizationByEmail = async (email) => {
|
|
28
|
+
return Organization.findOne({ email });
|
|
29
|
+
};
|
|
30
|
+
const updateOrganizationById = async (organizationId, updateBody) => {
|
|
31
|
+
const organization = await getOrganizationById(organizationId);
|
|
32
|
+
if (!organization) {
|
|
33
|
+
throw new ApiError(httpStatus.NOT_FOUND, "Organization not found");
|
|
34
|
+
}
|
|
35
|
+
// Legacy: Remove organization field if present in updateBody
|
|
36
|
+
if ("organization" in updateBody) {
|
|
37
|
+
delete updateBody.organization;
|
|
38
|
+
}
|
|
39
|
+
Object.assign(organization, updateBody);
|
|
40
|
+
await organization.save();
|
|
41
|
+
return organization;
|
|
42
|
+
};
|
|
43
|
+
export const deleteOrganizationById = async (organizationId) => {
|
|
44
|
+
const organization = await getOrganizationById(organizationId);
|
|
45
|
+
if (!organization) {
|
|
46
|
+
throw new ApiError(httpStatus.NOT_FOUND, "Organization not found");
|
|
47
|
+
}
|
|
48
|
+
await organization.deleteOne();
|
|
49
|
+
return organization;
|
|
50
|
+
};
|
|
51
|
+
export default {
|
|
52
|
+
createOrganization,
|
|
53
|
+
queryOrganizations,
|
|
54
|
+
getOrganizationById,
|
|
55
|
+
queryOrganizationsByUser,
|
|
56
|
+
getOrganizationByEmail,
|
|
57
|
+
updateOrganizationById,
|
|
58
|
+
deleteOrganizationById,
|
|
59
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { zPagination, zGet, zObjectId, zUpdate, zDelete, } from "../utils/zValidations.js";
|
|
4
|
+
extendZodWithOpenApi(z);
|
|
5
|
+
export const createOrganizationSchema = {
|
|
6
|
+
body: z.object({
|
|
7
|
+
/* name: z.string().openapi({
|
|
8
|
+
example: 'Acme Inc.',
|
|
9
|
+
description: 'The name of the organization',
|
|
10
|
+
}),
|
|
11
|
+
email: z.string().email().openapi({
|
|
12
|
+
example: 'contact@acme.com',
|
|
13
|
+
description: 'Contact email for the organization',
|
|
14
|
+
}), */
|
|
15
|
+
kind: z.string().openapi({
|
|
16
|
+
example: "private",
|
|
17
|
+
description: "The type or category of the organization",
|
|
18
|
+
}),
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
export const queryOrganizationsSchema = zPagination;
|
|
22
|
+
export const getOrganizationByIdSchema = zGet("organizationId");
|
|
23
|
+
export const updateOrganizationSchema = {
|
|
24
|
+
...zUpdate("organizationId"),
|
|
25
|
+
body: z.object({
|
|
26
|
+
name: z
|
|
27
|
+
.string()
|
|
28
|
+
.openapi({
|
|
29
|
+
example: "Acme Inc.",
|
|
30
|
+
description: "The name of the organization",
|
|
31
|
+
})
|
|
32
|
+
.optional(),
|
|
33
|
+
organization: zObjectId, // Legacy field, to be removed later
|
|
34
|
+
kind: z
|
|
35
|
+
.string()
|
|
36
|
+
.openapi({
|
|
37
|
+
example: "private",
|
|
38
|
+
description: "The type or category of the organization",
|
|
39
|
+
})
|
|
40
|
+
.optional(),
|
|
41
|
+
meta: z
|
|
42
|
+
.record(z.string(), z.any())
|
|
43
|
+
.openapi({
|
|
44
|
+
example: { key: "value" },
|
|
45
|
+
description: "Additional metadata for the entry",
|
|
46
|
+
})
|
|
47
|
+
.optional(),
|
|
48
|
+
}),
|
|
49
|
+
//...zUpdate('organizationId'),
|
|
50
|
+
/* body: z.object({
|
|
51
|
+
meta: z.any,
|
|
52
|
+
/* organization: zObjectId,
|
|
53
|
+
kind: z
|
|
54
|
+
.string()
|
|
55
|
+
.openapi({
|
|
56
|
+
example: 'private',
|
|
57
|
+
description: 'The type or category of the organization',
|
|
58
|
+
})
|
|
59
|
+
.optional(),
|
|
60
|
+
}),*/
|
|
61
|
+
};
|
|
62
|
+
export const deleteOrganizationSchema = zDelete("organizationId");
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import httpStatus from "http-status";
|
|
2
|
+
import catchAsync from "../utils/catchAsync.js";
|
|
3
|
+
import pdfService from "./pdf.service.js";
|
|
4
|
+
import ApiError from "../utils/ApiError.js";
|
|
5
|
+
export const generatePdfFromUrl = catchAsync(async (req, res) => {
|
|
6
|
+
const fileName = "memo-print";
|
|
7
|
+
const authHeader = req.headers["authorization"];
|
|
8
|
+
if (!authHeader) {
|
|
9
|
+
throw new ApiError(httpStatus.UNAUTHORIZED, "Missing Authorization header");
|
|
10
|
+
}
|
|
11
|
+
const token = authHeader.split(" ")[1];
|
|
12
|
+
if (!token) {
|
|
13
|
+
throw new ApiError(httpStatus.UNAUTHORIZED, "Missing bearer token");
|
|
14
|
+
}
|
|
15
|
+
const urlPath = typeof req.query.urlPath === "string" ? req.query.urlPath : undefined;
|
|
16
|
+
if (!urlPath) {
|
|
17
|
+
throw new ApiError(httpStatus.BAD_REQUEST, "Missing urlPath query parameter");
|
|
18
|
+
}
|
|
19
|
+
const result = await pdfService.generatePdfFromUrl({ urlPath, token });
|
|
20
|
+
res.status(httpStatus.CREATED).send({ signed: result });
|
|
21
|
+
});
|
|
22
|
+
export default {
|
|
23
|
+
generatePdfFromUrl,
|
|
24
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Router } from "express";
|
|
2
|
+
import buildRouterAndDocs from "../utils/buildRouterAndDocs.js";
|
|
3
|
+
import { generatePdfSchema } from "./pdf.validation.js";
|
|
4
|
+
import { pdfResponseSchema } from "./pdf.schemas.js";
|
|
5
|
+
import { generatePdfFromUrl } from "./pdf.controller.js";
|
|
6
|
+
import auth from "../middlewares/auth.js";
|
|
7
|
+
export const pdfRouteSpecs = [
|
|
8
|
+
{
|
|
9
|
+
method: "get",
|
|
10
|
+
path: "/",
|
|
11
|
+
validate: [auth("manageUsers")],
|
|
12
|
+
requestSchema: generatePdfSchema,
|
|
13
|
+
responseSchema: pdfResponseSchema,
|
|
14
|
+
handler: generatePdfFromUrl,
|
|
15
|
+
summary: "Generate a PDF from a provided URL",
|
|
16
|
+
description: "This endpoint allows users to generate a PDF document from a specified URL.",
|
|
17
|
+
memoOnly: true,
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
const router = Router();
|
|
21
|
+
buildRouterAndDocs(router, pdfRouteSpecs, "/pdf", ["PDF"]);
|
|
22
|
+
export default router;
|