@intlayer/backend 5.7.2 → 5.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/dist/cjs/controllers/ai.controller.cjs.map +1 -1
  2. package/dist/cjs/controllers/dictionary.controller.cjs +39 -20
  3. package/dist/cjs/controllers/dictionary.controller.cjs.map +1 -1
  4. package/dist/cjs/controllers/newsletter.controller.cjs +12 -13
  5. package/dist/cjs/controllers/newsletter.controller.cjs.map +1 -1
  6. package/dist/cjs/controllers/organization.controller.cjs +72 -53
  7. package/dist/cjs/controllers/organization.controller.cjs.map +1 -1
  8. package/dist/cjs/controllers/project.controller.cjs +48 -12
  9. package/dist/cjs/controllers/project.controller.cjs.map +1 -1
  10. package/dist/cjs/controllers/projectAccessKey.controller.cjs +30 -6
  11. package/dist/cjs/controllers/projectAccessKey.controller.cjs.map +1 -1
  12. package/dist/cjs/controllers/search.controller.cjs.map +1 -1
  13. package/dist/cjs/controllers/stripe.controller.cjs +15 -3
  14. package/dist/cjs/controllers/stripe.controller.cjs.map +1 -1
  15. package/dist/cjs/controllers/tag.controller.cjs +46 -17
  16. package/dist/cjs/controllers/tag.controller.cjs.map +1 -1
  17. package/dist/cjs/controllers/user.controller.cjs +19 -8
  18. package/dist/cjs/controllers/user.controller.cjs.map +1 -1
  19. package/dist/cjs/emails/OAuthTokenCreatedEmail.cjs +5 -7
  20. package/dist/cjs/emails/OAuthTokenCreatedEmail.cjs.map +1 -1
  21. package/dist/cjs/middlewares/sessionAuth.middleware.cjs.map +1 -1
  22. package/dist/cjs/routes/ai.routes.cjs.map +1 -1
  23. package/dist/cjs/routes/dictionary.routes.cjs.map +1 -1
  24. package/dist/cjs/routes/eventListener.routes.cjs.map +1 -1
  25. package/dist/cjs/routes/newsletter.routes.cjs.map +1 -1
  26. package/dist/cjs/routes/organization.routes.cjs.map +1 -1
  27. package/dist/cjs/routes/project.routes.cjs +3 -12
  28. package/dist/cjs/routes/project.routes.cjs.map +1 -1
  29. package/dist/cjs/routes/search.routes.cjs.map +1 -1
  30. package/dist/cjs/routes/stripe.routes.cjs.map +1 -1
  31. package/dist/cjs/routes/tags.routes.cjs.map +1 -1
  32. package/dist/cjs/routes/user.routes.cjs.map +1 -1
  33. package/dist/cjs/schemas/dictionary.schema.cjs +1 -1
  34. package/dist/cjs/schemas/dictionary.schema.cjs.map +1 -1
  35. package/dist/cjs/schemas/discussion.schema.cjs +1 -1
  36. package/dist/cjs/schemas/discussion.schema.cjs.map +1 -1
  37. package/dist/cjs/schemas/oAuth2.schema.cjs +1 -1
  38. package/dist/cjs/schemas/oAuth2.schema.cjs.map +1 -1
  39. package/dist/cjs/schemas/organization.schema.cjs +1 -1
  40. package/dist/cjs/schemas/organization.schema.cjs.map +1 -1
  41. package/dist/cjs/schemas/plans.schema.cjs +1 -1
  42. package/dist/cjs/schemas/plans.schema.cjs.map +1 -1
  43. package/dist/cjs/schemas/project.schema.cjs +1 -1
  44. package/dist/cjs/schemas/project.schema.cjs.map +1 -1
  45. package/dist/cjs/schemas/session.schema.cjs +1 -1
  46. package/dist/cjs/schemas/session.schema.cjs.map +1 -1
  47. package/dist/cjs/schemas/tag.schema.cjs +6 -1
  48. package/dist/cjs/schemas/tag.schema.cjs.map +1 -1
  49. package/dist/cjs/schemas/user.schema.cjs +1 -1
  50. package/dist/cjs/schemas/user.schema.cjs.map +1 -1
  51. package/dist/cjs/services/dictionary.service.cjs +0 -8
  52. package/dist/cjs/services/dictionary.service.cjs.map +1 -1
  53. package/dist/cjs/services/organization.service.cjs +0 -9
  54. package/dist/cjs/services/organization.service.cjs.map +1 -1
  55. package/dist/cjs/types/organization.types.cjs.map +1 -1
  56. package/dist/cjs/types/project.types.cjs.map +1 -1
  57. package/dist/cjs/types/tag.types.cjs.map +1 -1
  58. package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs +3 -0
  59. package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs.map +1 -1
  60. package/dist/cjs/utils/auth/getAuth.cjs +9 -5
  61. package/dist/cjs/utils/auth/getAuth.cjs.map +1 -1
  62. package/dist/cjs/utils/errors/errorCodes.cjs +13 -0
  63. package/dist/cjs/utils/errors/errorCodes.cjs.map +1 -1
  64. package/dist/cjs/utils/mapper/project.cjs +1 -2
  65. package/dist/cjs/utils/mapper/project.cjs.map +1 -1
  66. package/dist/cjs/utils/mapper/session.cjs +37 -0
  67. package/dist/cjs/utils/mapper/session.cjs.map +1 -0
  68. package/dist/cjs/utils/permissions.cjs +133 -38
  69. package/dist/cjs/utils/permissions.cjs.map +1 -1
  70. package/dist/cjs/utils/validation/validateArray.cjs +5 -1
  71. package/dist/cjs/utils/validation/validateArray.cjs.map +1 -1
  72. package/dist/esm/controllers/ai.controller.mjs.map +1 -1
  73. package/dist/esm/controllers/dictionary.controller.mjs +39 -20
  74. package/dist/esm/controllers/dictionary.controller.mjs.map +1 -1
  75. package/dist/esm/controllers/newsletter.controller.mjs +12 -13
  76. package/dist/esm/controllers/newsletter.controller.mjs.map +1 -1
  77. package/dist/esm/controllers/organization.controller.mjs +72 -53
  78. package/dist/esm/controllers/organization.controller.mjs.map +1 -1
  79. package/dist/esm/controllers/project.controller.mjs +48 -12
  80. package/dist/esm/controllers/project.controller.mjs.map +1 -1
  81. package/dist/esm/controllers/projectAccessKey.controller.mjs +31 -7
  82. package/dist/esm/controllers/projectAccessKey.controller.mjs.map +1 -1
  83. package/dist/esm/controllers/search.controller.mjs.map +1 -1
  84. package/dist/esm/controllers/stripe.controller.mjs +15 -3
  85. package/dist/esm/controllers/stripe.controller.mjs.map +1 -1
  86. package/dist/esm/controllers/tag.controller.mjs +46 -17
  87. package/dist/esm/controllers/tag.controller.mjs.map +1 -1
  88. package/dist/esm/controllers/user.controller.mjs +19 -8
  89. package/dist/esm/controllers/user.controller.mjs.map +1 -1
  90. package/dist/esm/emails/OAuthTokenCreatedEmail.mjs +5 -7
  91. package/dist/esm/emails/OAuthTokenCreatedEmail.mjs.map +1 -1
  92. package/dist/esm/middlewares/sessionAuth.middleware.mjs.map +1 -1
  93. package/dist/esm/routes/ai.routes.mjs.map +1 -1
  94. package/dist/esm/routes/dictionary.routes.mjs.map +1 -1
  95. package/dist/esm/routes/eventListener.routes.mjs.map +1 -1
  96. package/dist/esm/routes/newsletter.routes.mjs.map +1 -1
  97. package/dist/esm/routes/organization.routes.mjs.map +1 -1
  98. package/dist/esm/routes/project.routes.mjs +3 -12
  99. package/dist/esm/routes/project.routes.mjs.map +1 -1
  100. package/dist/esm/routes/search.routes.mjs.map +1 -1
  101. package/dist/esm/routes/stripe.routes.mjs.map +1 -1
  102. package/dist/esm/routes/tags.routes.mjs.map +1 -1
  103. package/dist/esm/routes/user.routes.mjs.map +1 -1
  104. package/dist/esm/schemas/dictionary.schema.mjs +1 -1
  105. package/dist/esm/schemas/dictionary.schema.mjs.map +1 -1
  106. package/dist/esm/schemas/discussion.schema.mjs +1 -1
  107. package/dist/esm/schemas/discussion.schema.mjs.map +1 -1
  108. package/dist/esm/schemas/oAuth2.schema.mjs +1 -1
  109. package/dist/esm/schemas/oAuth2.schema.mjs.map +1 -1
  110. package/dist/esm/schemas/organization.schema.mjs +1 -1
  111. package/dist/esm/schemas/organization.schema.mjs.map +1 -1
  112. package/dist/esm/schemas/plans.schema.mjs +1 -1
  113. package/dist/esm/schemas/plans.schema.mjs.map +1 -1
  114. package/dist/esm/schemas/project.schema.mjs +1 -1
  115. package/dist/esm/schemas/project.schema.mjs.map +1 -1
  116. package/dist/esm/schemas/session.schema.mjs +1 -1
  117. package/dist/esm/schemas/session.schema.mjs.map +1 -1
  118. package/dist/esm/schemas/tag.schema.mjs +6 -1
  119. package/dist/esm/schemas/tag.schema.mjs.map +1 -1
  120. package/dist/esm/schemas/user.schema.mjs +1 -1
  121. package/dist/esm/schemas/user.schema.mjs.map +1 -1
  122. package/dist/esm/services/dictionary.service.mjs +0 -7
  123. package/dist/esm/services/dictionary.service.mjs.map +1 -1
  124. package/dist/esm/services/organization.service.mjs +0 -9
  125. package/dist/esm/services/organization.service.mjs.map +1 -1
  126. package/dist/esm/utils/AI/askDocQuestion/askDocQuestion.mjs +3 -0
  127. package/dist/esm/utils/AI/askDocQuestion/askDocQuestion.mjs.map +1 -1
  128. package/dist/esm/utils/auth/getAuth.mjs +9 -5
  129. package/dist/esm/utils/auth/getAuth.mjs.map +1 -1
  130. package/dist/esm/utils/errors/errorCodes.mjs +13 -0
  131. package/dist/esm/utils/errors/errorCodes.mjs.map +1 -1
  132. package/dist/esm/utils/mapper/project.mjs +1 -2
  133. package/dist/esm/utils/mapper/project.mjs.map +1 -1
  134. package/dist/esm/utils/mapper/session.mjs +13 -0
  135. package/dist/esm/utils/mapper/session.mjs.map +1 -0
  136. package/dist/esm/utils/permissions.mjs +133 -38
  137. package/dist/esm/utils/permissions.mjs.map +1 -1
  138. package/dist/esm/utils/validation/validateArray.mjs +5 -1
  139. package/dist/esm/utils/validation/validateArray.mjs.map +1 -1
  140. package/dist/types/controllers/ai.controller.d.ts +10 -9
  141. package/dist/types/controllers/ai.controller.d.ts.map +1 -1
  142. package/dist/types/controllers/dictionary.controller.d.ts +9 -8
  143. package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
  144. package/dist/types/controllers/newsletter.controller.d.ts +5 -4
  145. package/dist/types/controllers/newsletter.controller.d.ts.map +1 -1
  146. package/dist/types/controllers/organization.controller.d.ts +14 -15
  147. package/dist/types/controllers/organization.controller.d.ts.map +1 -1
  148. package/dist/types/controllers/project.controller.d.ts +10 -9
  149. package/dist/types/controllers/project.controller.d.ts.map +1 -1
  150. package/dist/types/controllers/projectAccessKey.controller.d.ts +5 -4
  151. package/dist/types/controllers/projectAccessKey.controller.d.ts.map +1 -1
  152. package/dist/types/controllers/search.controller.d.ts.map +1 -1
  153. package/dist/types/controllers/stripe.controller.d.ts +9 -5
  154. package/dist/types/controllers/stripe.controller.d.ts.map +1 -1
  155. package/dist/types/controllers/tag.controller.d.ts +6 -5
  156. package/dist/types/controllers/tag.controller.d.ts.map +1 -1
  157. package/dist/types/controllers/user.controller.d.ts +9 -8
  158. package/dist/types/controllers/user.controller.d.ts.map +1 -1
  159. package/dist/types/emails/OAuthTokenCreatedEmail.d.ts.map +1 -1
  160. package/dist/types/middlewares/sessionAuth.middleware.d.ts +2 -0
  161. package/dist/types/middlewares/sessionAuth.middleware.d.ts.map +1 -1
  162. package/dist/types/schemas/tag.schema.d.ts.map +1 -1
  163. package/dist/types/services/dictionary.service.d.ts +0 -1
  164. package/dist/types/services/dictionary.service.d.ts.map +1 -1
  165. package/dist/types/services/organization.service.d.ts.map +1 -1
  166. package/dist/types/types/organization.types.d.ts +1 -3
  167. package/dist/types/types/organization.types.d.ts.map +1 -1
  168. package/dist/types/types/project.types.d.ts +1 -3
  169. package/dist/types/types/project.types.d.ts.map +1 -1
  170. package/dist/types/types/tag.types.d.ts +2 -0
  171. package/dist/types/types/tag.types.d.ts.map +1 -1
  172. package/dist/types/utils/AI/askDocQuestion/askDocQuestion.d.ts.map +1 -1
  173. package/dist/types/utils/auth/getAuth.d.ts +2 -2
  174. package/dist/types/utils/auth/getAuth.d.ts.map +1 -1
  175. package/dist/types/utils/errors/errorCodes.d.ts +13 -0
  176. package/dist/types/utils/errors/errorCodes.d.ts.map +1 -1
  177. package/dist/types/utils/mapper/project.d.ts.map +1 -1
  178. package/dist/types/utils/mapper/session.d.ts +4 -0
  179. package/dist/types/utils/mapper/session.d.ts.map +1 -0
  180. package/dist/types/utils/permissions.d.ts +83 -38
  181. package/dist/types/utils/permissions.d.ts.map +1 -1
  182. package/dist/types/utils/validation/validateArray.d.ts.map +1 -1
  183. package/package.json +10 -10
@@ -27,10 +27,6 @@ const getOrganizations = async (req, res, _next) => {
27
27
  ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
28
28
  return;
29
29
  }
30
- if (!hasPermission(roles, "organization:read")()) {
31
- ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
32
- return;
33
- }
34
30
  const restrictedFilter = {
35
31
  ...filters,
36
32
  membersIds: { $in: [...filters?.membersIds ?? [], String(user.id)] }
@@ -41,6 +37,16 @@ const getOrganizations = async (req, res, _next) => {
41
37
  skip,
42
38
  pageSize
43
39
  );
40
+ if (!hasPermission(
41
+ roles,
42
+ "organization:read"
43
+ )({
44
+ ...res.locals,
45
+ targetOrganizations: organizations
46
+ })) {
47
+ ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
48
+ return;
49
+ }
44
50
  const totalItems = await organizationService.countOrganizations(filters);
45
51
  const responseData = formatPaginatedResponse({
46
52
  data: mapOrganizationsToAPI(organizations),
@@ -63,12 +69,18 @@ const getOrganization = async (req, res, _next) => {
63
69
  ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_ID_NOT_FOUND");
64
70
  return;
65
71
  }
66
- if (!hasPermission(roles, "organization:read")()) {
67
- ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
68
- return;
69
- }
70
72
  try {
71
73
  const organization = await organizationService.getOrganizationById(organizationId);
74
+ if (!hasPermission(
75
+ roles,
76
+ "organization:read"
77
+ )({
78
+ ...res.locals,
79
+ targetOrganizations: [organization]
80
+ })) {
81
+ ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
82
+ return;
83
+ }
72
84
  const responseData = formatResponse({
73
85
  data: mapOrganizationToAPI(organization)
74
86
  });
@@ -90,10 +102,6 @@ const addOrganization = async (req, res, _next) => {
90
102
  ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
91
103
  return;
92
104
  }
93
- if (!hasPermission(roles, "organization:write")()) {
94
- ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
95
- return;
96
- }
97
105
  try {
98
106
  const newOrganization = await organizationService.createOrganization(
99
107
  organization,
@@ -130,7 +138,13 @@ const updateOrganization = async (req, res, _next) => {
130
138
  ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
131
139
  return;
132
140
  }
133
- if (!hasPermission(roles, "organization:write")()) {
141
+ if (!hasPermission(
142
+ roles,
143
+ "organization:write"
144
+ )({
145
+ ...res.locals,
146
+ targetOrganizations: [organization]
147
+ })) {
134
148
  ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
135
149
  return;
136
150
  }
@@ -170,13 +184,13 @@ const addOrganizationMember = async (req, res, _next) => {
170
184
  ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
171
185
  return;
172
186
  }
173
- if (!organization.plan) {
174
- ErrorHandler.handleGenericErrorResponse(res, "PLAN_NOT_FOUND", {
175
- organizationId: organization.id
176
- });
177
- return;
178
- }
179
- if (!hasPermission(roles, "organization:write")()) {
187
+ if (!hasPermission(
188
+ roles,
189
+ "organization:admin"
190
+ )({
191
+ ...res.locals,
192
+ targetOrganizations: [organization]
193
+ })) {
180
194
  ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
181
195
  return;
182
196
  }
@@ -206,7 +220,7 @@ const addOrganizationMember = async (req, res, _next) => {
206
220
  invitedByUsername: user.name,
207
221
  invitedByEmail: user.email,
208
222
  organizationName: organization.name,
209
- inviteLink: `${process.env.FRONTEND_URL}/login?email=${newMember.email}`,
223
+ inviteLink: `${process.env.CLIENT_URL}/auth/login?email=${newMember.email}`,
210
224
  inviteFromIp: req.ip ?? "",
211
225
  inviteFromLocation: req.hostname
212
226
  });
@@ -236,11 +250,25 @@ const addOrganizationMember = async (req, res, _next) => {
236
250
  };
237
251
  const updateOrganizationMembers = async (req, res, _next) => {
238
252
  const { organization, roles } = res.locals;
239
- const { membersIds } = req.body;
253
+ const { membersIds, adminsIds } = req.body;
240
254
  if (!organization) {
241
255
  ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
242
256
  return;
243
257
  }
258
+ if (!hasPermission(
259
+ roles,
260
+ "organization:admin"
261
+ )({
262
+ ...res.locals,
263
+ targetOrganizations: [organization]
264
+ })) {
265
+ ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
266
+ return;
267
+ }
268
+ if (!membersIds) {
269
+ ErrorHandler.handleGenericErrorResponse(res, "INVALID_REQUEST_BODY");
270
+ return;
271
+ }
244
272
  if (membersIds?.length === 0) {
245
273
  ErrorHandler.handleGenericErrorResponse(
246
274
  res,
@@ -248,43 +276,28 @@ const updateOrganizationMembers = async (req, res, _next) => {
248
276
  );
249
277
  return;
250
278
  }
251
- if (membersIds?.map((el) => el.isAdmin)?.length === 0) {
279
+ if (adminsIds?.filter((id) => membersIds?.includes(id)).length === 0) {
252
280
  ErrorHandler.handleGenericErrorResponse(
253
281
  res,
254
282
  "ORGANIZATION_MUST_HAVE_ADMIN"
255
283
  );
256
284
  return;
257
285
  }
258
- if (!hasPermission(roles, "organization:write")()) {
259
- ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
260
- return;
261
- }
262
286
  try {
263
- let existingUsers = [];
264
- if (membersIds) {
265
- const userIdList = membersIds?.map((member) => member.userId);
266
- const users = await userService.getUsersByIds(userIdList);
267
- if (users) {
268
- const userMap = users.map((user) => {
269
- const isAdmin = membersIds.find(
270
- (member) => String(member.userId) === String(user.id)
271
- )?.isAdmin ?? false;
272
- return {
273
- user,
274
- isAdmin
275
- };
276
- });
277
- existingUsers = userMap;
278
- }
287
+ const existingUsers = await userService.getUsersByIds(membersIds);
288
+ if (!existingUsers) {
289
+ ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_FOUND");
290
+ return;
291
+ }
292
+ const existingAdmins = await userService.getUsersByIds(adminsIds);
293
+ if (!existingAdmins) {
294
+ ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_FOUND");
295
+ return;
279
296
  }
280
- const formattedMembers = existingUsers.map(
281
- (user) => user.user.id
282
- );
283
- const formattedAdmin = existingUsers.filter((el) => el.isAdmin).map((user) => user.user.id);
284
297
  const updatedOrganization = await organizationService.updateOrganizationById(organization.id, {
285
298
  ...organization,
286
- membersIds: formattedMembers,
287
- adminsIds: formattedAdmin
299
+ membersIds: existingUsers.map((user) => user.id),
300
+ adminsIds: existingAdmins.map((user) => user.id)
288
301
  });
289
302
  const responseData = formatResponse({
290
303
  message: t({
@@ -322,7 +335,13 @@ const deleteOrganization = async (_req, res, _next) => {
322
335
  });
323
336
  return;
324
337
  }
325
- if (!hasPermission(roles, "organization:admin")()) {
338
+ if (!hasPermission(
339
+ roles,
340
+ "organization:admin"
341
+ )({
342
+ ...res.locals,
343
+ targetOrganizations: [organization]
344
+ })) {
326
345
  ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
327
346
  return;
328
347
  }
@@ -365,7 +384,7 @@ const selectOrganization = async (req, res, _next) => {
365
384
  ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_ID_NOT_FOUND");
366
385
  return;
367
386
  }
368
- if (!session) {
387
+ if (typeof session === "undefined") {
369
388
  ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
370
389
  return;
371
390
  }
@@ -401,9 +420,9 @@ const selectOrganization = async (req, res, _next) => {
401
420
  }
402
421
  };
403
422
  const unselectOrganization = async (_req, res, _next) => {
404
- const { session, roles } = res.locals;
423
+ const { session } = res.locals;
405
424
  try {
406
- if (!session) {
425
+ if (typeof session === "undefined") {
407
426
  ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
408
427
  return;
409
428
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/controllers/organization.controller.ts"],"sourcesContent":["import type {\n Organization,\n OrganizationAPI,\n OrganizationCreationData,\n} from '@/types/organization.types';\nimport type { User } from '@/types/user.types';\nimport { logger } from '@logger';\nimport { SessionModel } from '@models/session.model';\nimport { sendEmail } from '@services/email.service';\nimport * as organizationService from '@services/organization.service';\nimport * as projectService from '@services/project.service';\nimport * as userService from '@services/user.service';\nimport { ErrorHandler, type AppError } from '@utils/errors';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport {\n getOrganizationFiltersAndPagination,\n type OrganizationFilters,\n type OrganizationFiltersParams,\n} from '@utils/filtersAndPagination/getOrganizationFiltersAndPagination';\nimport {\n mapOrganizationToAPI,\n mapOrganizationsToAPI,\n} from '@utils/mapper/organization';\nimport { hasPermission } from '@utils/permissions';\nimport { getPLanDetails } from '@utils/plan';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { NextFunction, Request, Response } from 'express';\nimport { t } from 'express-intlayer';\nimport { Types } from 'mongoose';\nimport { Stripe } from 'stripe';\n\nexport type GetOrganizationsParams =\n FiltersAndPagination<OrganizationFiltersParams>;\nexport type GetOrganizationsResult = PaginatedResponse<OrganizationAPI>;\n\n/**\n * Retrieves a list of organizations based on filters and pagination.\n */\nexport const getOrganizations = async (\n req: Request<GetOrganizationsParams>,\n res: Response<GetOrganizationsResult>,\n _next: NextFunction\n) => {\n const { user, roles } = res.locals;\n const { filters, pageSize, skip, page, getNumberOfPages } =\n getOrganizationFiltersAndPagination(req);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'organization:read')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const restrictedFilter: OrganizationFilters = {\n ...filters,\n membersIds: { $in: [...(filters?.membersIds ?? []), String(user.id)] },\n };\n\n try {\n const organizations = await organizationService.findOrganizations(\n restrictedFilter,\n skip,\n pageSize\n );\n\n const totalItems = await organizationService.countOrganizations(filters);\n\n const responseData = formatPaginatedResponse<OrganizationAPI>({\n data: mapOrganizationsToAPI(organizations),\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n res.status(200).json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type GetOrganizationParam = { organizationId: string };\nexport type GetOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Retrieves an organization by its ID.\n */\nexport const getOrganization = async (\n req: Request<GetOrganizationParam, any, any>,\n res: Response<GetOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { roles } = res.locals;\n const { organizationId } = req.params as Partial<GetOrganizationParam>;\n\n if (!organizationId) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_ID_NOT_FOUND');\n return;\n }\n\n if (!hasPermission(roles, 'organization:read')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const organization =\n await organizationService.getOrganizationById(organizationId);\n\n const responseData = formatResponse<OrganizationAPI>({\n data: mapOrganizationToAPI(organization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AddOrganizationBody = OrganizationCreationData;\nexport type AddOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Adds a new organization to the database.\n */\nexport const addOrganization = async (\n req: Request<any, any, AddOrganizationBody>,\n res: Response<AddOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, roles } = res.locals;\n const organization = req.body;\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_DATA_NOT_FOUND');\n return;\n }\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'organization:write')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const newOrganization = await organizationService.createOrganization(\n organization,\n user.id\n );\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization created successfully',\n fr: 'Organisation créée avec succès',\n es: 'Organización creada con éxito',\n }),\n description: t({\n en: 'Your organization has been created successfully',\n fr: 'Votre organisation a été créée avec succès',\n es: 'Su organización ha sido creada con éxito',\n }),\n data: mapOrganizationToAPI(newOrganization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UpdateOrganizationBody = Partial<Organization>;\nexport type UpdateOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Updates an existing organization in the database.\n */\nexport const updateOrganization = async (\n req: Request<undefined, undefined, UpdateOrganizationBody>,\n res: Response<UpdateOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, roles } = res.locals;\n const organizationFields = req.body;\n\n if (!organizationFields) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_DATA_NOT_FOUND');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'organization:write')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const updatedOrganization =\n await organizationService.updateOrganizationById(\n organization.id,\n organizationFields\n );\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization updated successfully',\n fr: 'Organisation mise à jour avec succès',\n es: 'Organización actualizada con éxito',\n }),\n description: t({\n en: 'Your organization has been updated successfully',\n fr: 'Votre organisation a été mise à jour avec succès',\n es: 'Su organización ha sido actualizada con éxito',\n }),\n data: mapOrganizationToAPI(updatedOrganization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\ntype UserAndAdmin = { user: User; isAdmin: boolean };\n\nexport type OrganizationMemberByIdOption = {\n userId: string | Types.ObjectId;\n isAdmin?: boolean;\n};\n\nexport type AddOrganizationMemberBody = {\n userEmail: string;\n};\nexport type AddOrganizationMemberResult = ResponseData<OrganizationAPI>;\n\n/**\n * Add member to the organization in the database.\n */\nexport const addOrganizationMember = async (\n req: Request<any, any, AddOrganizationMemberBody>,\n res: Response<AddOrganizationMemberResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, user, roles } = res.locals;\n const { userEmail } = req.body;\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!organization.plan) {\n ErrorHandler.handleGenericErrorResponse(res, 'PLAN_NOT_FOUND', {\n organizationId: organization.id,\n });\n return;\n }\n\n if (!hasPermission(roles, 'organization:write')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const planType = getPLanDetails(organization.plan);\n\n if (\n planType.numberOfOrganizationUsers &&\n organization.membersIds.length >= planType.numberOfOrganizationUsers\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PLAN_USER_LIMIT_REACHED', {\n organizationId: organization.id,\n });\n return;\n }\n\n try {\n let newMember = await userService.getUserByEmail(userEmail);\n\n if (!newMember) {\n // Create user if not found\n const newUser = await userService.createUser({ email: userEmail });\n if (!newUser) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_CREATION_FAILED', {\n email: userEmail,\n });\n return;\n }\n\n newMember = newUser;\n }\n\n await sendEmail({\n type: 'invite',\n to: userEmail,\n username: newMember.email.slice(0, newMember.email.indexOf('@')),\n invitedByUsername: user.name,\n invitedByEmail: user.email,\n organizationName: organization.name,\n inviteLink: `${process.env.FRONTEND_URL}/login?email=${newMember.email}`,\n inviteFromIp: req.ip ?? '',\n inviteFromLocation: req.hostname,\n });\n\n const updatedOrganization =\n await organizationService.updateOrganizationById(organization.id, {\n ...organization,\n membersIds: [...organization.membersIds, newMember.id],\n });\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization updated successfully',\n fr: 'Organisation mise à jour avec succès',\n es: 'Organización actualizada con éxito',\n }),\n description: t({\n en: 'Your organization has been updated successfully',\n fr: 'Votre organisation a été mise à jour avec succès',\n es: 'Su organización ha sido actualizada con éxito',\n }),\n data: mapOrganizationToAPI(updatedOrganization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UpdateOrganizationMembersBody = Partial<{\n membersIds: OrganizationMemberByIdOption[];\n}>;\nexport type UpdateOrganizationMembersResult = ResponseData<OrganizationAPI>;\n\n/**\n * Update members to the organization in the database.\n */\nexport const updateOrganizationMembers = async (\n req: Request<any, any, UpdateOrganizationMembersBody>,\n res: Response<UpdateOrganizationMembersResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, roles } = res.locals;\n const { membersIds } = req.body;\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (membersIds?.length === 0) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'ORGANIZATION_MUST_HAVE_MEMBER'\n );\n return;\n }\n\n if (membersIds?.map((el) => el.isAdmin)?.length === 0) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'ORGANIZATION_MUST_HAVE_ADMIN'\n );\n return;\n }\n\n if (!hasPermission(roles, 'organization:write')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n let existingUsers: UserAndAdmin[] = [];\n\n if (membersIds) {\n const userIdList = membersIds?.map((member) => member.userId);\n const users = await userService.getUsersByIds(userIdList);\n\n if (users) {\n const userMap: UserAndAdmin[] = users.map((user) => {\n const isAdmin =\n membersIds.find(\n (member) => String(member.userId) === String(user.id)\n )?.isAdmin ?? false;\n\n return {\n user,\n isAdmin,\n };\n });\n\n existingUsers = userMap;\n }\n }\n\n const formattedMembers: Types.ObjectId[] = existingUsers.map(\n (user) => user.user.id\n );\n const formattedAdmin: Types.ObjectId[] = existingUsers\n .filter((el) => el.isAdmin)\n .map((user) => user.user.id);\n\n const updatedOrganization =\n await organizationService.updateOrganizationById(organization.id, {\n ...organization,\n membersIds: formattedMembers,\n adminsIds: formattedAdmin,\n });\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization updated successfully',\n fr: 'Organisation mise à jour avec succès',\n es: 'Organización actualizada con éxito',\n }),\n description: t({\n en: 'Your organization has been updated successfully',\n fr: 'Votre organisation a été mise à jour avec succès',\n es: 'Su organización ha sido actualizada con éxito',\n }),\n data: mapOrganizationToAPI(updatedOrganization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type DeleteOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Deletes an organization from the database by its ID.\n */\nexport const deleteOrganization = async (\n _req: Request,\n res: Response,\n _next: NextFunction\n): Promise<void> => {\n const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);\n const { organization, roles } = res.locals;\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n const projects = await projectService.findProjects({\n organizationId: organization.id,\n });\n\n if (projects.length > 0) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECTS_EXIST', {\n organizationId: organization.id,\n });\n return;\n }\n\n if (!hasPermission(roles, 'organization:admin')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n // Cancel the subscription on Stripe if it exists\n if (organization.plan?.subscriptionId) {\n await stripe.subscriptions.cancel(organization.plan.subscriptionId);\n }\n\n const deletedOrganization =\n await organizationService.deleteOrganizationById(organization.id);\n\n if (!deletedOrganization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_FOUND', {\n organizationId: organization.id,\n });\n return;\n }\n\n logger.info(`Organization deleted: ${String(deletedOrganization.id)}`);\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization deleted successfully',\n fr: 'Organisation supprimée avec succès',\n es: 'Organización eliminada con éxito',\n }),\n description: t({\n en: 'Your organization has been deleted successfully',\n fr: 'Votre organisation a été supprimée avec succès',\n es: 'Su organización ha sido eliminada con éxito',\n }),\n data: mapOrganizationToAPI(deletedOrganization),\n });\n\n // No need to update session here, as it's a delete operation\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type SelectOrganizationParam = {\n organizationId: string | Types.ObjectId;\n};\nexport type SelectOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Select an organization.\n */\nexport const selectOrganization = async (\n req: Request<SelectOrganizationParam>,\n res: Response<SelectOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organizationId } = req.params as Partial<SelectOrganizationParam>;\n const { session } = res.locals;\n\n if (!organizationId) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_ID_NOT_FOUND');\n return;\n }\n\n if (!session) {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n try {\n const organization =\n await organizationService.getOrganizationById(organizationId);\n\n // Update session to set activeOrganizationId\n await SessionModel.updateOne(\n { _id: session.id },\n {\n $set: {\n activeOrganizationId: String(organization.id),\n activeProjectId: null,\n },\n }\n );\n\n // No need to update session here, as it's a select operation\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization retrieved successfully',\n fr: 'Organisation récupérée avec succès',\n es: 'Organización recuperada con éxito',\n }),\n description: t({\n en: 'Your organization has been retrieved successfully',\n fr: 'Votre organisation a été récupérée avec succès',\n es: 'Su organización ha sido recuperada con éxito',\n }),\n data: mapOrganizationToAPI(organization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UnselectOrganizationResult = ResponseData<null>;\n\n/**\n * Unselect an organization.\n */\nexport const unselectOrganization = async (\n _req: Request,\n res: Response<UnselectOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { session, roles } = res.locals;\n try {\n // Update session to clear activeOrganizationId and activeProjectId\n\n if (!session) {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n await SessionModel.updateOne(\n { _id: session.id },\n {\n $set: {\n activeOrganizationId: null,\n activeProjectId: null,\n },\n }\n );\n\n const responseData = formatResponse<null>({\n message: t({\n en: 'Organization unselected successfully',\n fr: 'Organisation désélectionnée avec succès',\n es: 'Organización deseleccionada con éxito',\n }),\n description: t({\n en: 'Your organization has been unselected successfully',\n fr: 'Votre organisation a été désélectionnée avec succès',\n es: 'Su organización ha sido deseleccionada con éxito',\n }),\n data: null,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/controllers/organization.controller.ts"],"sourcesContent":["import type {\n Organization,\n OrganizationAPI,\n OrganizationCreationData,\n} from '@/types/organization.types';\nimport type { User, UserAPI } from '@/types/user.types';\nimport { logger } from '@logger';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { SessionModel } from '@models/session.model';\nimport { sendEmail } from '@services/email.service';\nimport * as organizationService from '@services/organization.service';\nimport * as projectService from '@services/project.service';\nimport * as userService from '@services/user.service';\nimport { ErrorHandler, type AppError } from '@utils/errors';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport {\n getOrganizationFiltersAndPagination,\n type OrganizationFilters,\n type OrganizationFiltersParams,\n} from '@utils/filtersAndPagination/getOrganizationFiltersAndPagination';\nimport {\n mapOrganizationToAPI,\n mapOrganizationsToAPI,\n} from '@utils/mapper/organization';\nimport { hasPermission } from '@utils/permissions';\nimport { getPLanDetails } from '@utils/plan';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport { t } from 'express-intlayer';\nimport { Types } from 'mongoose';\nimport { Stripe } from 'stripe';\n\nexport type GetOrganizationsParams =\n FiltersAndPagination<OrganizationFiltersParams>;\nexport type GetOrganizationsResult = PaginatedResponse<OrganizationAPI>;\n\n/**\n * Retrieves a list of organizations based on filters and pagination.\n */\nexport const getOrganizations = async (\n req: Request<GetOrganizationsParams>,\n res: ResponseWithSession<GetOrganizationsResult>,\n _next: NextFunction\n) => {\n const { user, roles } = res.locals;\n const { filters, pageSize, skip, page, getNumberOfPages } =\n getOrganizationFiltersAndPagination(req);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n const restrictedFilter: OrganizationFilters = {\n ...filters,\n membersIds: { $in: [...(filters?.membersIds ?? []), String(user.id)] },\n };\n\n try {\n const organizations = await organizationService.findOrganizations(\n restrictedFilter,\n skip,\n pageSize\n );\n\n if (\n !hasPermission(\n roles,\n 'organization:read'\n )({\n ...res.locals,\n targetOrganizations: organizations,\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const totalItems = await organizationService.countOrganizations(filters);\n\n const responseData = formatPaginatedResponse<OrganizationAPI>({\n data: mapOrganizationsToAPI(organizations),\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n res.status(200).json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type GetOrganizationParam = { organizationId: string };\nexport type GetOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Retrieves an organization by its ID.\n */\nexport const getOrganization = async (\n req: Request<GetOrganizationParam, any, any>,\n res: ResponseWithSession<GetOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { roles } = res.locals;\n const { organizationId } = req.params as Partial<GetOrganizationParam>;\n\n if (!organizationId) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_ID_NOT_FOUND');\n return;\n }\n\n try {\n const organization =\n await organizationService.getOrganizationById(organizationId);\n\n if (\n !hasPermission(\n roles,\n 'organization:read'\n )({\n ...res.locals,\n targetOrganizations: [organization],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const responseData = formatResponse<OrganizationAPI>({\n data: mapOrganizationToAPI(organization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AddOrganizationBody = OrganizationCreationData;\nexport type AddOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Adds a new organization to the database.\n */\nexport const addOrganization = async (\n req: Request<any, any, AddOrganizationBody>,\n res: ResponseWithSession<AddOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, roles } = res.locals;\n const organization = req.body;\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_DATA_NOT_FOUND');\n return;\n }\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n try {\n const newOrganization = await organizationService.createOrganization(\n organization,\n user.id\n );\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization created successfully',\n fr: 'Organisation créée avec succès',\n es: 'Organización creada con éxito',\n }),\n description: t({\n en: 'Your organization has been created successfully',\n fr: 'Votre organisation a été créée avec succès',\n es: 'Su organización ha sido creada con éxito',\n }),\n data: mapOrganizationToAPI(newOrganization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UpdateOrganizationBody = Partial<Organization>;\nexport type UpdateOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Updates an existing organization in the database.\n */\nexport const updateOrganization = async (\n req: Request<undefined, undefined, UpdateOrganizationBody>,\n res: ResponseWithSession<UpdateOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, roles } = res.locals;\n const organizationFields = req.body;\n\n if (!organizationFields) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_DATA_NOT_FOUND');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'organization:write'\n )({\n ...res.locals,\n targetOrganizations: [organization],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const updatedOrganization =\n await organizationService.updateOrganizationById(\n organization.id,\n organizationFields\n );\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization updated successfully',\n fr: 'Organisation mise à jour avec succès',\n es: 'Organización actualizada con éxito',\n }),\n description: t({\n en: 'Your organization has been updated successfully',\n fr: 'Votre organisation a été mise à jour avec succès',\n es: 'Su organización ha sido actualizada con éxito',\n }),\n data: mapOrganizationToAPI(updatedOrganization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AddOrganizationMemberBody = {\n userEmail: string;\n};\nexport type AddOrganizationMemberResult = ResponseData<OrganizationAPI>;\n\n/**\n * Add member to the organization in the database.\n */\nexport const addOrganizationMember = async (\n req: Request<any, any, AddOrganizationMemberBody>,\n res: ResponseWithSession<AddOrganizationMemberResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, user, roles } = res.locals;\n const { userEmail } = req.body;\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'organization:admin'\n )({\n ...res.locals,\n targetOrganizations: [organization],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const planType = getPLanDetails(organization.plan);\n\n if (\n planType.numberOfOrganizationUsers &&\n organization.membersIds.length >= planType.numberOfOrganizationUsers\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PLAN_USER_LIMIT_REACHED', {\n organizationId: organization.id,\n });\n return;\n }\n\n try {\n let newMember = await userService.getUserByEmail(userEmail);\n\n if (!newMember) {\n // Create user if not found\n const newUser = await userService.createUser({ email: userEmail });\n if (!newUser) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_CREATION_FAILED', {\n email: userEmail,\n });\n return;\n }\n\n newMember = newUser;\n }\n\n await sendEmail({\n type: 'invite',\n to: userEmail,\n username: newMember.email.slice(0, newMember.email.indexOf('@')),\n invitedByUsername: user.name,\n invitedByEmail: user.email,\n organizationName: organization.name,\n inviteLink: `${process.env.CLIENT_URL}/auth/login?email=${newMember.email}`,\n inviteFromIp: req.ip ?? '',\n inviteFromLocation: req.hostname,\n });\n\n const updatedOrganization =\n await organizationService.updateOrganizationById(organization.id, {\n ...organization,\n membersIds: [...organization.membersIds, newMember.id],\n });\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization updated successfully',\n fr: 'Organisation mise à jour avec succès',\n es: 'Organización actualizada con éxito',\n }),\n description: t({\n en: 'Your organization has been updated successfully',\n fr: 'Votre organisation a été mise à jour avec succès',\n es: 'Su organización ha sido actualizada con éxito',\n }),\n data: mapOrganizationToAPI(updatedOrganization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UpdateOrganizationMembersBody = Partial<{\n membersIds: (User | UserAPI)['id'][];\n adminsIds: (User | UserAPI)['id'][];\n}>;\nexport type UpdateOrganizationMembersResult = ResponseData<OrganizationAPI>;\n\n/**\n * Update members to the organization in the database.\n */\nexport const updateOrganizationMembers = async (\n req: Request<any, any, UpdateOrganizationMembersBody>,\n res: ResponseWithSession<UpdateOrganizationMembersResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, roles } = res.locals;\n const { membersIds, adminsIds } = req.body;\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'organization:admin'\n )({\n ...res.locals,\n targetOrganizations: [organization],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n if (!membersIds) {\n ErrorHandler.handleGenericErrorResponse(res, 'INVALID_REQUEST_BODY');\n return;\n }\n\n if (membersIds?.length === 0) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'ORGANIZATION_MUST_HAVE_MEMBER'\n );\n return;\n }\n\n if (adminsIds?.filter((id) => membersIds?.includes(id)).length === 0) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'ORGANIZATION_MUST_HAVE_ADMIN'\n );\n return;\n }\n\n try {\n const existingUsers = await userService.getUsersByIds(membersIds);\n\n if (!existingUsers) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_FOUND');\n return;\n }\n\n const existingAdmins = await userService.getUsersByIds(adminsIds!);\n\n if (!existingAdmins) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_FOUND');\n return;\n }\n\n const updatedOrganization =\n await organizationService.updateOrganizationById(organization.id, {\n ...organization,\n membersIds: existingUsers.map((user) => user.id),\n adminsIds: existingAdmins.map((user) => user.id),\n });\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization updated successfully',\n fr: 'Organisation mise à jour avec succès',\n es: 'Organización actualizada con éxito',\n }),\n description: t({\n en: 'Your organization has been updated successfully',\n fr: 'Votre organisation a été mise à jour avec succès',\n es: 'Su organización ha sido actualizada con éxito',\n }),\n data: mapOrganizationToAPI(updatedOrganization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type DeleteOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Deletes an organization from the database by its ID.\n */\nexport const deleteOrganization = async (\n _req: Request,\n res: ResponseWithSession,\n _next: NextFunction\n): Promise<void> => {\n const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);\n const { organization, roles } = res.locals;\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n const projects = await projectService.findProjects({\n organizationId: organization.id,\n });\n\n if (projects.length > 0) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECTS_EXIST', {\n organizationId: organization.id,\n });\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'organization:admin'\n )({\n ...res.locals,\n targetOrganizations: [organization],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n // Cancel the subscription on Stripe if it exists\n if (organization.plan?.subscriptionId) {\n await stripe.subscriptions.cancel(organization.plan.subscriptionId);\n }\n\n const deletedOrganization =\n await organizationService.deleteOrganizationById(organization.id);\n\n if (!deletedOrganization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_FOUND', {\n organizationId: organization.id,\n });\n return;\n }\n\n logger.info(`Organization deleted: ${String(deletedOrganization.id)}`);\n\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization deleted successfully',\n fr: 'Organisation supprimée avec succès',\n es: 'Organización eliminada con éxito',\n }),\n description: t({\n en: 'Your organization has been deleted successfully',\n fr: 'Votre organisation a été supprimée avec succès',\n es: 'Su organización ha sido eliminada con éxito',\n }),\n data: mapOrganizationToAPI(deletedOrganization),\n });\n\n // No need to update session here, as it's a delete operation\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type SelectOrganizationParam = {\n organizationId: string | Types.ObjectId;\n};\nexport type SelectOrganizationResult = ResponseData<OrganizationAPI>;\n\n/**\n * Select an organization.\n */\nexport const selectOrganization = async (\n req: Request<SelectOrganizationParam>,\n res: ResponseWithSession<SelectOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organizationId } = req.params as Partial<SelectOrganizationParam>;\n const { session } = res.locals;\n\n if (!organizationId) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_ID_NOT_FOUND');\n return;\n }\n\n if (typeof session === 'undefined') {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n try {\n const organization =\n await organizationService.getOrganizationById(organizationId);\n\n // Update session to set activeOrganizationId\n await SessionModel.updateOne(\n { _id: session.id },\n {\n $set: {\n activeOrganizationId: String(organization.id),\n activeProjectId: null,\n },\n }\n );\n\n // No need to update session here, as it's a select operation\n const responseData = formatResponse<OrganizationAPI>({\n message: t({\n en: 'Organization retrieved successfully',\n fr: 'Organisation récupérée avec succès',\n es: 'Organización recuperada con éxito',\n }),\n description: t({\n en: 'Your organization has been retrieved successfully',\n fr: 'Votre organisation a été récupérée avec succès',\n es: 'Su organización ha sido recuperada con éxito',\n }),\n data: mapOrganizationToAPI(organization),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UnselectOrganizationResult = ResponseData<null>;\n\n/**\n * Unselect an organization.\n */\nexport const unselectOrganization = async (\n _req: Request,\n res: ResponseWithSession<UnselectOrganizationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { session } = res.locals;\n try {\n // Update session to clear activeOrganizationId and activeProjectId\n\n if (typeof session === 'undefined') {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n await SessionModel.updateOne(\n { _id: session.id },\n {\n $set: {\n activeOrganizationId: null,\n activeProjectId: null,\n },\n }\n );\n\n const responseData = formatResponse<null>({\n message: t({\n en: 'Organization unselected successfully',\n fr: 'Organisation désélectionnée avec succès',\n es: 'Organización deseleccionada con éxito',\n }),\n description: t({\n en: 'Your organization has been unselected successfully',\n fr: 'Votre organisation a été désélectionnée avec succès',\n es: 'Su organización ha sido deseleccionada con éxito',\n }),\n data: null,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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","names":[]}
@@ -25,10 +25,6 @@ const getProjects = async (req, res, _next) => {
25
25
  ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
26
26
  return;
27
27
  }
28
- if (!hasPermission(roles, "project:read")(res.locals)) {
29
- ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
30
- return;
31
- }
32
28
  const restrictedFilter = {
33
29
  ...filters,
34
30
  membersIds: { $in: [...filters.membersIds ?? [], String(user.id)] },
@@ -40,6 +36,16 @@ const getProjects = async (req, res, _next) => {
40
36
  skip,
41
37
  pageSize
42
38
  );
39
+ if (!hasPermission(
40
+ roles,
41
+ "project:read"
42
+ )({
43
+ ...res.locals,
44
+ targetProjects: projects
45
+ })) {
46
+ ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
47
+ return;
48
+ }
43
49
  const totalItems = await projectService.countProjects(filters);
44
50
  const formattedProjects = mapProjectsToAPI(projects);
45
51
  const responseData = formatPaginatedResponse({
@@ -70,7 +76,13 @@ const addProject = async (req, res, _next) => {
70
76
  if (!projectData) {
71
77
  ErrorHandler.handleGenericErrorResponse(res, "PROJECT_DATA_NOT_FOUND");
72
78
  }
73
- if (!hasPermission(roles, "project:admin")()) {
79
+ if (!hasPermission(
80
+ roles,
81
+ "organization:admin"
82
+ )({
83
+ ...res.locals,
84
+ targetOrganizations: [organization]
85
+ })) {
74
86
  ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
75
87
  return;
76
88
  }
@@ -140,7 +152,13 @@ const updateProject = async (req, res, _next) => {
140
152
  ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_IN_ORGANIZATION");
141
153
  return;
142
154
  }
143
- if (!hasPermission(roles, "project:write")()) {
155
+ if (!hasPermission(
156
+ roles,
157
+ "project:write"
158
+ )({
159
+ ...res.locals,
160
+ targetProjectIds: [String(project.id)]
161
+ })) {
144
162
  ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
145
163
  return;
146
164
  }
@@ -193,7 +211,13 @@ const updateProjectMembers = async (req, res, _next) => {
193
211
  ErrorHandler.handleGenericErrorResponse(res, "PROJECT_MUST_HAVE_ADMIN");
194
212
  return;
195
213
  }
196
- if (!hasPermission(roles, "project:write")()) {
214
+ if (!hasPermission(
215
+ roles,
216
+ "project:write"
217
+ )({
218
+ ...res.locals,
219
+ targetProjectIds: [String(project.id)]
220
+ })) {
197
221
  ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
198
222
  return;
199
223
  }
@@ -261,7 +285,13 @@ const pushProjectConfiguration = async (req, res, _next) => {
261
285
  ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
262
286
  return;
263
287
  }
264
- if (!hasPermission(roles, "project:write")()) {
288
+ if (!hasPermission(
289
+ roles,
290
+ "project:write"
291
+ )({
292
+ ...res.locals,
293
+ targetProjectIds: [String(project.id)]
294
+ })) {
265
295
  ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
266
296
  return;
267
297
  }
@@ -309,11 +339,17 @@ const deleteProject = async (_req, res, _next) => {
309
339
  ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
310
340
  return;
311
341
  }
312
- if (!session) {
342
+ if (typeof session === "undefined") {
313
343
  ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
314
344
  return;
315
345
  }
316
- if (!hasPermission(roles, "project:admin")()) {
346
+ if (!hasPermission(
347
+ roles,
348
+ "project:admin"
349
+ )({
350
+ ...res.locals,
351
+ targetProjectIds: [String(project.id)]
352
+ })) {
317
353
  ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
318
354
  return;
319
355
  }
@@ -365,7 +401,7 @@ const selectProject = async (req, res, _next) => {
365
401
  ErrorHandler.handleGenericErrorResponse(res, "PROJECT_ID_NOT_FOUND");
366
402
  return;
367
403
  }
368
- if (!session) {
404
+ if (typeof session === "undefined") {
369
405
  ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
370
406
  return;
371
407
  }
@@ -397,7 +433,7 @@ const selectProject = async (req, res, _next) => {
397
433
  };
398
434
  const unselectProject = async (_req, res, _next) => {
399
435
  const { session } = res.locals;
400
- if (!session) {
436
+ if (typeof session === "undefined") {
401
437
  ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
402
438
  return;
403
439
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/controllers/project.controller.ts"],"sourcesContent":["import type {\n Project,\n ProjectAPI,\n ProjectConfiguration,\n ProjectCreationData,\n ProjectData,\n} from '@/types/project.types';\nimport type { User } from '@/types/user.types';\nimport { logger } from '@logger';\nimport { SessionModel } from '@models/session.model';\nimport * as projectService from '@services/project.service';\nimport * as userService from '@services/user.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport {\n getProjectFiltersAndPagination,\n type ProjectFilters,\n type ProjectFiltersParams,\n} from '@utils/filtersAndPagination/getProjectFiltersAndPagination';\nimport { mapProjectsToAPI, mapProjectToAPI } from '@utils/mapper/project';\nimport { hasPermission } from '@utils/permissions';\nimport { getPLanDetails } from '@utils/plan';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { NextFunction, Request, Response } from 'express';\nimport { t } from 'express-intlayer';\nimport { Types } from 'mongoose';\n\nexport type GetProjectsParams = FiltersAndPagination<ProjectFiltersParams>;\nexport type GetProjectsResult = PaginatedResponse<ProjectAPI>;\n\n/**\n * Retrieves a list of projects based on filters and pagination.\n */\nexport const getProjects = async (\n req: Request<GetProjectsParams>,\n res: Response<GetProjectsResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, organization, roles } = res.locals;\n const { filters, pageSize, skip, page, getNumberOfPages } =\n getProjectFiltersAndPagination(req);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'project:read')(res.locals)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const restrictedFilter: ProjectFilters = {\n ...filters,\n membersIds: { $in: [...(filters.membersIds ?? []), String(user.id)] },\n organizationId: String(organization.id),\n };\n\n try {\n const projects = await projectService.findProjects(\n restrictedFilter,\n skip,\n pageSize\n );\n const totalItems = await projectService.countProjects(filters);\n\n const formattedProjects = mapProjectsToAPI(projects);\n\n const responseData = formatPaginatedResponse<ProjectAPI>({\n data: formattedProjects,\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AddProjectBody = ProjectCreationData;\nexport type AddProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Adds a new project to the database.\n */\nexport const addProject = async (\n req: Request<any, any, AddProjectBody>,\n res: Response<AddProjectResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, user, roles } = res.locals;\n const projectData = req.body;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (!projectData) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_DATA_NOT_FOUND');\n }\n\n if (!hasPermission(roles, 'project:admin')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const { plan } = organization;\n\n const planType = getPLanDetails(plan);\n\n if (planType.numberOfProjects) {\n const projectCount = await projectService.countProjects({\n organizationId: organization.id,\n });\n\n if (projectCount >= planType.numberOfProjects) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'PLAN_PROJECT_LIMIT_REACHED',\n {\n organizationId: organization.id,\n }\n );\n return;\n }\n }\n\n const project: ProjectData = {\n membersIds: [user.id],\n adminsIds: [user.id],\n creatorId: user.id,\n organizationId: organization.id,\n ...projectData,\n };\n\n try {\n const newProject = await projectService.createProject(project);\n\n const formattedProject = mapProjectToAPI(newProject);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project created successfully',\n fr: 'Projet créé avec succès',\n es: 'Proyecto creado con éxito',\n }),\n description: t({\n en: 'Your project has been created successfully',\n fr: 'Votre projet a été créé avec succès',\n es: 'Su proyecto ha sido creado con éxito',\n }),\n data: formattedProject,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UpdateProjectBody = Partial<Project>;\nexport type UpdateProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Updates an existing project in the database.\n */\nexport const updateProject = async (\n req: Request<any, any, UpdateProjectBody>,\n res: Response<UpdateProjectResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, project, user, roles } = res.locals;\n const projectData = req.body;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_DATA_NOT_FOUND');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (String(project.organizationId) !== String(organization.id)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_IN_ORGANIZATION');\n return;\n }\n\n if (!hasPermission(roles, 'project:write')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const updatedProject = await projectService.updateProjectById(\n project.id,\n projectData\n );\n\n const formattedProject = mapProjectToAPI(updatedProject);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project updated successfully',\n fr: 'Projet mis à jour avec succès',\n es: 'Proyecto actualizado con éxito',\n }),\n description: t({\n en: 'Your project has been updated successfully',\n fr: 'Votre projet a été mis à jour avec succès',\n es: 'Su proyecto ha sido actualizado con éxito',\n }),\n data: formattedProject,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\ntype UserAndAdmin = { user: User; isAdmin: boolean };\nexport type ProjectMemberByIdOption = {\n userId: string | Types.ObjectId;\n isAdmin?: boolean;\n};\n\nexport type UpdateProjectMembersBody = Partial<{\n membersIds: ProjectMemberByIdOption[];\n}>;\nexport type UpdateProjectMembersResult = ResponseData<ProjectAPI>;\n\n/**\n * Update members to the dictionary in the database.\n */\nexport const updateProjectMembers = async (\n req: Request<any, any, UpdateProjectMembersBody>,\n res: Response<UpdateProjectMembersResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, project, organization, roles } = res.locals;\n const { membersIds } = req.body;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (membersIds?.length === 0) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_MUST_HAVE_MEMBER');\n return;\n }\n\n if (membersIds?.map((el) => el.isAdmin)?.length === 0) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_MUST_HAVE_ADMIN');\n return;\n }\n\n if (!hasPermission(roles, 'project:write')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const existingUsers: UserAndAdmin[] = [];\n\n if (membersIds) {\n const userIdList = membersIds\n ?.filter(\n (member) =>\n // Remove members that are not in the organization\n !organization?.membersIds.includes(member.userId as Types.ObjectId)\n )\n .map((member) => member.userId);\n\n const users = await userService.getUsersByIds(userIdList);\n\n if (users) {\n const userMap: UserAndAdmin[] = users.map((user) => ({\n user,\n isAdmin:\n membersIds.find(\n (member) => String(member.userId) === String(user.id)\n )?.isAdmin ?? false,\n }));\n\n existingUsers.push(...userMap);\n }\n }\n\n const formattedMembers: Types.ObjectId[] = existingUsers.map(\n (user) => user.user.id\n );\n const formattedAdmin: Types.ObjectId[] = existingUsers\n .filter((el) => el.isAdmin)\n .map((user) => user.user.id);\n\n const updatedOrganization = await projectService.updateProjectById(\n project.id,\n {\n ...project,\n membersIds: formattedMembers,\n adminsIds: formattedAdmin,\n }\n );\n\n const formattedProject = mapProjectToAPI(updatedOrganization);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project members updated successfully',\n fr: 'Membres du projet mis à jour avec succès',\n es: 'Miembros del proyecto actualizados con éxito',\n }),\n description: t({\n en: 'Your project members have been updated successfully',\n fr: 'Les membres de votre projet ont été mis à jour avec succès',\n es: 'Los miembros de su proyecto han sido actualizados con éxito',\n }),\n data: formattedProject,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type PushProjectConfigurationBody = ProjectConfiguration;\nexport type PushProjectConfigurationResult = ResponseData<ProjectConfiguration>;\n\n/**\n * Pushes a project configuration to the database.\n * @param req - Express request object.\n * @param res - Express response object.\n * @returns Response confirming the deletion.\n */\nexport const pushProjectConfiguration = async (\n req: Request<any, any, PushProjectConfigurationBody>,\n res: Response<PushProjectConfigurationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, project, roles } = res.locals;\n const projectConfiguration = req.body;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'project:write')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const projectObject = await projectService.getProjectById(project.id);\n projectObject.configuration = projectConfiguration;\n\n projectObject.save();\n\n if (!projectObject.configuration) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_UPDATE_FAILED', {\n projectId: project.id,\n });\n return;\n }\n\n const responseData = formatResponse<ProjectConfiguration>({\n message: t({\n en: 'Project configuration updated successfully',\n fr: 'Configuration du projet mise à jour avec succès',\n es: 'Configuración del proyecto actualizada con éxito',\n }),\n description: t({\n en: 'Your project configuration has been updated successfully',\n fr: 'La configuration du projet a été mise à jour avec succès',\n es: 'Su configuración del proyecto ha sido actualizada con éxito',\n }),\n data: projectObject.configuration,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type DeleteProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Deletes a project from the database by its ID.\n * @param req - Express request object.\n * @param res - Express response object.\n * @returns Response confirming the deletion.\n */\nexport const deleteProject = async (\n _req: Request,\n res: Response<DeleteProjectResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, organization, project, session, roles } = res.locals;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!session) {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'project:admin')()) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const projectToDelete = await projectService.getProjectById(project.id);\n\n if (String(projectToDelete.organizationId) !== String(organization.id)) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'PROJECT_NOT_IN_ORGANIZATION'\n );\n return;\n }\n\n const deletedProject = await projectService.deleteProjectById(project.id);\n\n if (!deletedProject) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED', {\n projectId: project.id,\n });\n\n return;\n }\n\n logger.info(`Project deleted: ${String(deletedProject.id)}`);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project deleted successfully',\n fr: 'Projet supprimé avec succès',\n es: 'Proyecto eliminado con éxito',\n }),\n description: t({\n en: 'Your project has been deleted successfully',\n fr: 'Votre projet a été supprimé avec succès',\n es: 'Su proyecto ha sido eliminado con éxito',\n }),\n data: mapProjectToAPI(deletedProject),\n });\n\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: null } }\n );\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type SelectProjectParam = { projectId: string | Types.ObjectId };\nexport type SelectProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Select a project.\n */\nexport const selectProject = async (\n req: Request<SelectProjectParam>,\n res: Response<SelectProjectResult>,\n _next: NextFunction\n) => {\n const { projectId } = req.params;\n const { session, roles } = res.locals;\n\n if (!projectId) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_ID_NOT_FOUND');\n return;\n }\n\n if (!session) {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n try {\n const project = await projectService.getProjectById(projectId);\n\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: String(projectId) } }\n );\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project selected successfully',\n fr: 'Projet sélectionné avec succès',\n es: 'Proyecto seleccionado con éxito',\n }),\n description: t({\n en: 'Your project has been selected successfully',\n fr: 'Votre projet a été sélectionné avec succès',\n es: 'Su proyecto ha sido seleccionado con éxito',\n }),\n data: mapProjectToAPI(project),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UnselectProjectResult = ResponseData<null>;\n\n/**\n * Unselect a project.\n */\nexport const unselectProject = async (\n _req: Request,\n res: Response<UnselectProjectResult>,\n _next: NextFunction\n) => {\n const { session } = res.locals;\n\n if (!session) {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n try {\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: null } }\n );\n\n const responseData = formatResponse<null>({\n message: t({\n en: 'Project unselected successfully',\n fr: 'Projet désélectionné avec succès',\n es: 'Proyecto deseleccionado con éxito',\n }),\n description: t({\n en: 'Your project has been unselected successfully',\n fr: 'Votre projet a été désélectionné avec succès',\n es: 'Su proyecto ha sido deseleccionado con éxito',\n }),\n data: null,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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","names":["user"]}
1
+ {"version":3,"sources":["../../../src/controllers/project.controller.ts"],"sourcesContent":["import type {\n Project,\n ProjectAPI,\n ProjectConfiguration,\n ProjectCreationData,\n ProjectData,\n} from '@/types/project.types';\nimport type { User } from '@/types/user.types';\nimport { logger } from '@logger';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { SessionModel } from '@models/session.model';\nimport * as projectService from '@services/project.service';\nimport * as userService from '@services/user.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport {\n getProjectFiltersAndPagination,\n type ProjectFilters,\n type ProjectFiltersParams,\n} from '@utils/filtersAndPagination/getProjectFiltersAndPagination';\nimport { mapProjectsToAPI, mapProjectToAPI } from '@utils/mapper/project';\nimport { hasPermission } from '@utils/permissions';\nimport { getPLanDetails } from '@utils/plan';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport { t } from 'express-intlayer';\nimport { Types } from 'mongoose';\n\nexport type GetProjectsParams = FiltersAndPagination<ProjectFiltersParams>;\nexport type GetProjectsResult = PaginatedResponse<ProjectAPI>;\n\n/**\n * Retrieves a list of projects based on filters and pagination.\n */\nexport const getProjects = async (\n req: Request<GetProjectsParams>,\n res: ResponseWithSession<GetProjectsResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, organization, roles } = res.locals;\n const { filters, pageSize, skip, page, getNumberOfPages } =\n getProjectFiltersAndPagination(req);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n const restrictedFilter: ProjectFilters = {\n ...filters,\n membersIds: { $in: [...(filters.membersIds ?? []), String(user.id)] },\n organizationId: String(organization.id),\n };\n\n try {\n const projects = await projectService.findProjects(\n restrictedFilter,\n skip,\n pageSize\n );\n\n if (\n !hasPermission(\n roles,\n 'project:read'\n )({\n ...res.locals,\n targetProjects: projects,\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const totalItems = await projectService.countProjects(filters);\n\n const formattedProjects = mapProjectsToAPI(projects);\n\n const responseData = formatPaginatedResponse<ProjectAPI>({\n data: formattedProjects,\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type AddProjectBody = ProjectCreationData;\nexport type AddProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Adds a new project to the database.\n */\nexport const addProject = async (\n req: Request<any, any, AddProjectBody>,\n res: ResponseWithSession<AddProjectResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, user, roles } = res.locals;\n const projectData = req.body;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (!projectData) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_DATA_NOT_FOUND');\n }\n\n if (\n !hasPermission(\n roles,\n 'organization:admin'\n )({\n ...res.locals,\n targetOrganizations: [organization],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const { plan } = organization;\n\n const planType = getPLanDetails(plan);\n\n if (planType.numberOfProjects) {\n const projectCount = await projectService.countProjects({\n organizationId: organization.id,\n });\n\n if (projectCount >= planType.numberOfProjects) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'PLAN_PROJECT_LIMIT_REACHED',\n {\n organizationId: organization.id,\n }\n );\n return;\n }\n }\n\n const project: ProjectData = {\n membersIds: [user.id],\n adminsIds: [user.id],\n creatorId: user.id,\n organizationId: organization.id,\n ...projectData,\n };\n\n try {\n const newProject = await projectService.createProject(project);\n\n const formattedProject = mapProjectToAPI(newProject);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project created successfully',\n fr: 'Projet créé avec succès',\n es: 'Proyecto creado con éxito',\n }),\n description: t({\n en: 'Your project has been created successfully',\n fr: 'Votre projet a été créé avec succès',\n es: 'Su proyecto ha sido creado con éxito',\n }),\n data: formattedProject,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UpdateProjectBody = Partial<Project>;\nexport type UpdateProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Updates an existing project in the database.\n */\nexport const updateProject = async (\n req: Request<any, any, UpdateProjectBody>,\n res: ResponseWithSession<UpdateProjectResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization, project, user, roles } = res.locals;\n const projectData = req.body;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_DATA_NOT_FOUND');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (String(project.organizationId) !== String(organization.id)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_IN_ORGANIZATION');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'project:write'\n )({\n ...res.locals,\n targetProjectIds: [String(project.id)],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const updatedProject = await projectService.updateProjectById(\n project.id,\n projectData\n );\n\n const formattedProject = mapProjectToAPI(updatedProject);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project updated successfully',\n fr: 'Projet mis à jour avec succès',\n es: 'Proyecto actualizado con éxito',\n }),\n description: t({\n en: 'Your project has been updated successfully',\n fr: 'Votre projet a été mis à jour avec succès',\n es: 'Su proyecto ha sido actualizado con éxito',\n }),\n data: formattedProject,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\ntype UserAndAdmin = { user: User; isAdmin: boolean };\nexport type ProjectMemberByIdOption = {\n userId: string | Types.ObjectId;\n isAdmin?: boolean;\n};\n\nexport type UpdateProjectMembersBody = Partial<{\n membersIds: ProjectMemberByIdOption[];\n}>;\nexport type UpdateProjectMembersResult = ResponseData<ProjectAPI>;\n\n/**\n * Update members to the dictionary in the database.\n */\nexport const updateProjectMembers = async (\n req: Request<any, any, UpdateProjectMembersBody>,\n res: ResponseWithSession<UpdateProjectMembersResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, project, organization, roles } = res.locals;\n const { membersIds } = req.body;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (membersIds?.length === 0) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_MUST_HAVE_MEMBER');\n return;\n }\n\n if (membersIds?.map((el) => el.isAdmin)?.length === 0) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_MUST_HAVE_ADMIN');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'project:write'\n )({\n ...res.locals,\n targetProjectIds: [String(project.id)],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const existingUsers: UserAndAdmin[] = [];\n\n if (membersIds) {\n const userIdList = membersIds\n ?.filter(\n (member) =>\n // Remove members that are not in the organization\n !organization?.membersIds.includes(member.userId as Types.ObjectId)\n )\n .map((member) => member.userId);\n\n const users = await userService.getUsersByIds(userIdList);\n\n if (users) {\n const userMap: UserAndAdmin[] = users.map((user) => ({\n user,\n isAdmin:\n membersIds.find(\n (member) => String(member.userId) === String(user.id)\n )?.isAdmin ?? false,\n }));\n\n existingUsers.push(...userMap);\n }\n }\n\n const formattedMembers: Types.ObjectId[] = existingUsers.map(\n (user) => user.user.id\n );\n const formattedAdmin: Types.ObjectId[] = existingUsers\n .filter((el) => el.isAdmin)\n .map((user) => user.user.id);\n\n const updatedOrganization = await projectService.updateProjectById(\n project.id,\n {\n ...project,\n membersIds: formattedMembers,\n adminsIds: formattedAdmin,\n }\n );\n\n const formattedProject = mapProjectToAPI(updatedOrganization);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project members updated successfully',\n fr: 'Membres du projet mis à jour avec succès',\n es: 'Miembros del proyecto actualizados con éxito',\n }),\n description: t({\n en: 'Your project members have been updated successfully',\n fr: 'Les membres de votre projet ont été mis à jour avec succès',\n es: 'Los miembros de su proyecto han sido actualizados con éxito',\n }),\n data: formattedProject,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type PushProjectConfigurationBody = ProjectConfiguration;\nexport type PushProjectConfigurationResult = ResponseData<ProjectConfiguration>;\n\n/**\n * Pushes a project configuration to the database.\n * @param req - Express request object.\n * @param res - Express response object.\n * @returns Response confirming the deletion.\n */\nexport const pushProjectConfiguration = async (\n req: Request<any, any, PushProjectConfigurationBody>,\n res: ResponseWithSession<PushProjectConfigurationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, project, roles } = res.locals;\n const projectConfiguration = req.body;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'project:write'\n )({\n ...res.locals,\n targetProjectIds: [String(project.id)],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const projectObject = await projectService.getProjectById(project.id);\n projectObject.configuration = projectConfiguration;\n\n projectObject.save();\n\n if (!projectObject.configuration) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_UPDATE_FAILED', {\n projectId: project.id,\n });\n return;\n }\n\n const responseData = formatResponse<ProjectConfiguration>({\n message: t({\n en: 'Project configuration updated successfully',\n fr: 'Configuration du projet mise à jour avec succès',\n es: 'Configuración del proyecto actualizada con éxito',\n }),\n description: t({\n en: 'Your project configuration has been updated successfully',\n fr: 'La configuration du projet a été mise à jour avec succès',\n es: 'Su configuración del proyecto ha sido actualizada con éxito',\n }),\n data: projectObject.configuration,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type DeleteProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Deletes a project from the database by its ID.\n * @param req - Express request object.\n * @param res - Express response object.\n * @returns Response confirming the deletion.\n */\nexport const deleteProject = async (\n _req: Request,\n res: ResponseWithSession<DeleteProjectResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, organization, project, session, roles } = res.locals;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!organization) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (typeof session === 'undefined') {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'project:admin'\n )({\n ...res.locals,\n targetProjectIds: [String(project.id)],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const projectToDelete = await projectService.getProjectById(project.id);\n\n if (String(projectToDelete.organizationId) !== String(organization.id)) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'PROJECT_NOT_IN_ORGANIZATION'\n );\n return;\n }\n\n const deletedProject = await projectService.deleteProjectById(project.id);\n\n if (!deletedProject) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED', {\n projectId: project.id,\n });\n\n return;\n }\n\n logger.info(`Project deleted: ${String(deletedProject.id)}`);\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project deleted successfully',\n fr: 'Projet supprimé avec succès',\n es: 'Proyecto eliminado con éxito',\n }),\n description: t({\n en: 'Your project has been deleted successfully',\n fr: 'Votre projet a été supprimé avec succès',\n es: 'Su proyecto ha sido eliminado con éxito',\n }),\n data: mapProjectToAPI(deletedProject),\n });\n\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: null } }\n );\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type SelectProjectParam = { projectId: string | Types.ObjectId };\nexport type SelectProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Select a project.\n */\nexport const selectProject = async (\n req: Request<SelectProjectParam>,\n res: ResponseWithSession<SelectProjectResult>,\n _next: NextFunction\n) => {\n const { projectId } = req.params;\n const { session, roles } = res.locals;\n\n if (!projectId) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_ID_NOT_FOUND');\n return;\n }\n\n if (typeof session === 'undefined') {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n try {\n const project = await projectService.getProjectById(projectId);\n\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: String(projectId) } }\n );\n\n const responseData = formatResponse<ProjectAPI>({\n message: t({\n en: 'Project selected successfully',\n fr: 'Projet sélectionné avec succès',\n es: 'Proyecto seleccionado con éxito',\n }),\n description: t({\n en: 'Your project has been selected successfully',\n fr: 'Votre projet a été sélectionné avec succès',\n es: 'Su proyecto ha sido seleccionado con éxito',\n }),\n data: mapProjectToAPI(project),\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UnselectProjectResult = ResponseData<null>;\n\n/**\n * Unselect a project.\n */\nexport const unselectProject = async (\n _req: Request,\n res: ResponseWithSession<UnselectProjectResult>,\n _next: NextFunction\n) => {\n const { session } = res.locals;\n\n if (typeof session === 'undefined') {\n ErrorHandler.handleGenericErrorResponse(res, 'SESSION_NOT_DEFINED');\n return;\n }\n\n try {\n await SessionModel.updateOne(\n { _id: session.id },\n { $set: { activeProjectId: null } }\n );\n\n const responseData = formatResponse<null>({\n message: t({\n en: 'Project unselected successfully',\n fr: 'Projet désélectionné avec succès',\n es: 'Proyecto deseleccionado con éxito',\n }),\n description: t({\n en: 'Your project has been unselected successfully',\n fr: 'Votre projet a été désélectionné avec succès',\n es: 'Su proyecto ha sido deseleccionado con éxito',\n }),\n data: null,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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","names":["user"]}