@intlayer/backend 7.5.9 → 7.5.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/assets/utils/AI/askDocQuestion/PROMPT.md +1 -1
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_fastify.json +9 -0
- package/dist/esm/controllers/ai.controller.mjs +95 -128
- package/dist/esm/controllers/ai.controller.mjs.map +1 -1
- package/dist/esm/controllers/dictionary.controller.mjs +86 -198
- package/dist/esm/controllers/dictionary.controller.mjs.map +1 -1
- package/dist/esm/controllers/eventListener.controller.mjs +13 -19
- package/dist/esm/controllers/eventListener.controller.mjs.map +1 -1
- package/dist/esm/controllers/github.controller.mjs +77 -0
- package/dist/esm/controllers/github.controller.mjs.map +1 -0
- package/dist/esm/controllers/newsletter.controller.mjs +30 -60
- package/dist/esm/controllers/newsletter.controller.mjs.map +1 -1
- package/dist/esm/controllers/oAuth2.controller.mjs +11 -8
- package/dist/esm/controllers/oAuth2.controller.mjs.map +1 -1
- package/dist/esm/controllers/organization.controller.mjs +100 -225
- package/dist/esm/controllers/organization.controller.mjs.map +1 -1
- package/dist/esm/controllers/project.controller.mjs +87 -204
- package/dist/esm/controllers/project.controller.mjs.map +1 -1
- package/dist/esm/controllers/projectAccessKey.controller.mjs +38 -71
- package/dist/esm/controllers/projectAccessKey.controller.mjs.map +1 -1
- package/dist/esm/controllers/search.controller.mjs +3 -3
- package/dist/esm/controllers/search.controller.mjs.map +1 -1
- package/dist/esm/controllers/stripe.controller.mjs +34 -67
- package/dist/esm/controllers/stripe.controller.mjs.map +1 -1
- package/dist/esm/controllers/tag.controller.mjs +51 -113
- package/dist/esm/controllers/tag.controller.mjs.map +1 -1
- package/dist/esm/controllers/user.controller.mjs +64 -113
- package/dist/esm/controllers/user.controller.mjs.map +1 -1
- package/dist/esm/export.mjs +2 -1
- package/dist/esm/index.mjs +101 -41
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/middlewares/oAuth2.middleware.mjs +19 -14
- package/dist/esm/middlewares/oAuth2.middleware.mjs.map +1 -1
- package/dist/esm/middlewares/sessionAuth.middleware.mjs +6 -7
- package/dist/esm/middlewares/sessionAuth.middleware.mjs.map +1 -1
- package/dist/esm/routes/ai.routes.mjs +19 -15
- package/dist/esm/routes/ai.routes.mjs.map +1 -1
- package/dist/esm/routes/dictionary.routes.mjs +10 -10
- package/dist/esm/routes/dictionary.routes.mjs.map +1 -1
- package/dist/esm/routes/eventListener.routes.mjs +3 -3
- package/dist/esm/routes/eventListener.routes.mjs.map +1 -1
- package/dist/esm/routes/github.routes.mjs +43 -0
- package/dist/esm/routes/github.routes.mjs.map +1 -0
- package/dist/esm/routes/newsletter.routes.mjs +5 -5
- package/dist/esm/routes/newsletter.routes.mjs.map +1 -1
- package/dist/esm/routes/organization.routes.mjs +11 -11
- package/dist/esm/routes/organization.routes.mjs.map +1 -1
- package/dist/esm/routes/project.routes.mjs +13 -13
- package/dist/esm/routes/project.routes.mjs.map +1 -1
- package/dist/esm/routes/search.routes.mjs +3 -3
- package/dist/esm/routes/search.routes.mjs.map +1 -1
- package/dist/esm/routes/stripe.routes.mjs +5 -5
- package/dist/esm/routes/stripe.routes.mjs.map +1 -1
- package/dist/esm/routes/tags.routes.mjs +6 -6
- package/dist/esm/routes/tags.routes.mjs.map +1 -1
- package/dist/esm/routes/user.routes.mjs +9 -9
- package/dist/esm/routes/user.routes.mjs.map +1 -1
- package/dist/esm/schemas/project.schema.mjs +35 -1
- package/dist/esm/schemas/project.schema.mjs.map +1 -1
- package/dist/esm/services/email.service.mjs +1 -1
- package/dist/esm/services/email.service.mjs.map +1 -1
- package/dist/esm/services/github.service.mjs +130 -0
- package/dist/esm/services/github.service.mjs.map +1 -0
- package/dist/esm/services/oAuth2.service.mjs +1 -1
- package/dist/esm/services/subscription.service.mjs +1 -1
- package/dist/esm/services/subscription.service.mjs.map +1 -1
- package/dist/esm/utils/auth/getAuth.mjs +14 -8
- package/dist/esm/utils/auth/getAuth.mjs.map +1 -1
- package/dist/esm/utils/cors.mjs +15 -5
- package/dist/esm/utils/cors.mjs.map +1 -1
- package/dist/esm/utils/errors/ErrorHandler.mjs +32 -4
- package/dist/esm/utils/errors/ErrorHandler.mjs.map +1 -1
- package/dist/esm/utils/errors/ErrorsClass.mjs +1 -1
- package/dist/esm/utils/errors/ErrorsClass.mjs.map +1 -1
- package/dist/esm/utils/errors/errorCodes.mjs +78 -0
- package/dist/esm/utils/errors/errorCodes.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getDiscussionFiltersAndPagination.mjs +1 -1
- package/dist/esm/utils/filtersAndPagination/getDiscussionFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getFiltersAndPaginationFromBody.mjs +1 -1
- package/dist/esm/utils/filtersAndPagination/getFiltersAndPaginationFromBody.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getProjectFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getProjectFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getTagFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getTagFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getUserFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getUserFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/mapper/project.mjs +28 -1
- package/dist/esm/utils/mapper/project.mjs.map +1 -1
- package/dist/esm/utils/mongoDB/connectDB.mjs +1 -1
- package/dist/esm/utils/rateLimiter.mjs +40 -30
- package/dist/esm/utils/rateLimiter.mjs.map +1 -1
- package/dist/esm/webhooks/stripe.webhook.mjs +2 -2
- package/dist/esm/webhooks/stripe.webhook.mjs.map +1 -1
- package/dist/types/controllers/ai.controller.d.ts +29 -12
- package/dist/types/controllers/ai.controller.d.ts.map +1 -1
- package/dist/types/controllers/dictionary.controller.d.ts +23 -13
- package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
- package/dist/types/controllers/eventListener.controller.d.ts +4 -2
- package/dist/types/controllers/eventListener.controller.d.ts.map +1 -1
- package/dist/types/controllers/github.controller.d.ts +63 -0
- package/dist/types/controllers/github.controller.d.ts.map +1 -0
- package/dist/types/controllers/newsletter.controller.d.ts +8 -7
- package/dist/types/controllers/newsletter.controller.d.ts.map +1 -1
- package/dist/types/controllers/oAuth2.controller.d.ts +4 -2
- package/dist/types/controllers/oAuth2.controller.d.ts.map +1 -1
- package/dist/types/controllers/organization.controller.d.ts +28 -12
- package/dist/types/controllers/organization.controller.d.ts.map +1 -1
- package/dist/types/controllers/project.controller.d.ts +21 -16
- package/dist/types/controllers/project.controller.d.ts.map +1 -1
- package/dist/types/controllers/projectAccessKey.controller.d.ts +10 -5
- package/dist/types/controllers/projectAccessKey.controller.d.ts.map +1 -1
- package/dist/types/controllers/search.controller.d.ts +4 -2
- package/dist/types/controllers/search.controller.d.ts.map +1 -1
- package/dist/types/controllers/stripe.controller.d.ts +11 -12
- package/dist/types/controllers/stripe.controller.d.ts.map +1 -1
- package/dist/types/controllers/tag.controller.d.ts +14 -9
- package/dist/types/controllers/tag.controller.d.ts.map +1 -1
- package/dist/types/controllers/user.controller.d.ts +22 -9
- package/dist/types/controllers/user.controller.d.ts.map +1 -1
- package/dist/types/emails/InviteUserEmail.d.ts +4 -4
- package/dist/types/emails/MagicLinkEmail.d.ts +4 -4
- package/dist/types/emails/MagicLinkEmail.d.ts.map +1 -1
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts +4 -4
- package/dist/types/emails/PasswordChangeConfirmation.d.ts +4 -4
- package/dist/types/emails/PasswordChangeConfirmation.d.ts.map +1 -1
- package/dist/types/emails/ResetUserPassword.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentError.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentSuccess.d.ts +4 -4
- package/dist/types/emails/ValidateUserEmail.d.ts +4 -4
- package/dist/types/emails/ValidateUserEmail.d.ts.map +1 -1
- package/dist/types/emails/Welcome.d.ts +4 -4
- package/dist/types/export.d.ts +6 -4
- package/dist/types/middlewares/oAuth2.middleware.d.ts +9 -4
- package/dist/types/middlewares/oAuth2.middleware.d.ts.map +1 -1
- package/dist/types/middlewares/sessionAuth.middleware.d.ts +13 -3
- package/dist/types/middlewares/sessionAuth.middleware.d.ts.map +1 -1
- package/dist/types/models/dictionary.model.d.ts +4 -4
- package/dist/types/models/dictionary.model.d.ts.map +1 -1
- package/dist/types/models/discussion.model.d.ts +2 -2
- package/dist/types/models/discussion.model.d.ts.map +1 -1
- package/dist/types/models/oAuth2.model.d.ts +3 -3
- package/dist/types/models/oAuth2.model.d.ts.map +1 -1
- package/dist/types/routes/ai.routes.d.ts +2 -2
- package/dist/types/routes/ai.routes.d.ts.map +1 -1
- package/dist/types/routes/dictionary.routes.d.ts +2 -2
- package/dist/types/routes/dictionary.routes.d.ts.map +1 -1
- package/dist/types/routes/eventListener.routes.d.ts +2 -2
- package/dist/types/routes/eventListener.routes.d.ts.map +1 -1
- package/dist/types/routes/github.routes.d.ts +35 -0
- package/dist/types/routes/github.routes.d.ts.map +1 -0
- package/dist/types/routes/newsletter.routes.d.ts +2 -2
- package/dist/types/routes/newsletter.routes.d.ts.map +1 -1
- package/dist/types/routes/organization.routes.d.ts +2 -2
- package/dist/types/routes/organization.routes.d.ts.map +1 -1
- package/dist/types/routes/project.routes.d.ts +2 -2
- package/dist/types/routes/project.routes.d.ts.map +1 -1
- package/dist/types/routes/search.routes.d.ts +2 -2
- package/dist/types/routes/search.routes.d.ts.map +1 -1
- package/dist/types/routes/stripe.routes.d.ts +2 -2
- package/dist/types/routes/stripe.routes.d.ts.map +1 -1
- package/dist/types/routes/tags.routes.d.ts +2 -2
- package/dist/types/routes/tags.routes.d.ts.map +1 -1
- package/dist/types/routes/user.routes.d.ts +2 -2
- package/dist/types/routes/user.routes.d.ts.map +1 -1
- package/dist/types/schemas/dictionary.schema.d.ts +6 -6
- package/dist/types/schemas/discussion.schema.d.ts +6 -6
- package/dist/types/schemas/oAuth2.schema.d.ts +5 -5
- package/dist/types/schemas/organization.schema.d.ts +6 -6
- package/dist/types/schemas/plans.schema.d.ts +6 -6
- package/dist/types/schemas/plans.schema.d.ts.map +1 -1
- package/dist/types/schemas/project.schema.d.ts +6 -6
- package/dist/types/schemas/project.schema.d.ts.map +1 -1
- package/dist/types/schemas/session.schema.d.ts +6 -6
- package/dist/types/schemas/tag.schema.d.ts +6 -6
- package/dist/types/schemas/user.schema.d.ts +6 -6
- package/dist/types/schemas/user.schema.d.ts.map +1 -1
- package/dist/types/services/email.service.d.ts +11 -11
- package/dist/types/services/github.service.d.ts +21 -0
- package/dist/types/services/github.service.d.ts.map +1 -0
- package/dist/types/types/project.types.d.ts +18 -5
- package/dist/types/types/project.types.d.ts.map +1 -1
- package/dist/types/types/session.types.d.ts +1 -1
- package/dist/types/types/user.types.d.ts +1 -1
- package/dist/types/utils/AI/auditTag/index.d.ts +1 -1
- package/dist/types/utils/auth/getAuth.d.ts.map +1 -1
- package/dist/types/utils/cors.d.ts +2 -2
- package/dist/types/utils/errors/ErrorHandler.d.ts +31 -3
- package/dist/types/utils/errors/ErrorHandler.d.ts.map +1 -1
- package/dist/types/utils/errors/ErrorsClass.d.ts +1 -1
- package/dist/types/utils/errors/errorCodes.d.ts +78 -0
- package/dist/types/utils/errors/errorCodes.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts +8 -4
- package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts +6 -3
- package/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getFiltersAndPaginationFromBody.d.ts +6 -2
- package/dist/types/utils/filtersAndPagination/getFiltersAndPaginationFromBody.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts +8 -4
- package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts +8 -4
- package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts +8 -4
- package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts +6 -2
- package/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/mapper/project.d.ts.map +1 -1
- package/dist/types/utils/mergeFunctionTypes.d.ts.map +1 -1
- package/dist/types/utils/permissions.d.ts +1 -1
- package/dist/types/utils/rateLimiter.d.ts +4 -2
- package/dist/types/utils/rateLimiter.d.ts.map +1 -1
- package/package.json +23 -27
- package/dist/esm/middlewares/request.middleware.mjs +0 -17
- package/dist/esm/middlewares/request.middleware.mjs.map +0 -1
- package/dist/types/middlewares/request.middleware.d.ts +0 -7
- package/dist/types/middlewares/request.middleware.d.ts.map +0 -1
|
@@ -8,32 +8,23 @@ import { SessionModel } from "../models/session.model.mjs";
|
|
|
8
8
|
import { getPlanDetails } from "../utils/plan.mjs";
|
|
9
9
|
import { getProjectFiltersAndPagination } from "../utils/filtersAndPagination/getProjectFiltersAndPagination.mjs";
|
|
10
10
|
import { mapProjectToAPI, mapProjectsToAPI } from "../utils/mapper/project.mjs";
|
|
11
|
-
import { t } from "
|
|
11
|
+
import { t } from "fastify-intlayer";
|
|
12
12
|
|
|
13
13
|
//#region src/controllers/project.controller.ts
|
|
14
14
|
/**
|
|
15
15
|
* Retrieves a list of projects based on filters and pagination.
|
|
16
16
|
*/
|
|
17
|
-
const getProjects = async (
|
|
18
|
-
const { user, organization, roles } =
|
|
19
|
-
const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } = getProjectFiltersAndPagination(
|
|
20
|
-
if (!user)
|
|
21
|
-
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (!organization && !roles.includes("admin")) {
|
|
25
|
-
ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
17
|
+
const getProjects = async (request, reply) => {
|
|
18
|
+
const { user, organization, roles } = request.locals || {};
|
|
19
|
+
const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } = getProjectFiltersAndPagination(request);
|
|
20
|
+
if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
|
|
21
|
+
if (!organization && !roles?.includes("admin")) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
|
|
28
22
|
try {
|
|
29
23
|
const projects = await findProjects(filters, skip, pageSize, sortOptions);
|
|
30
|
-
if (!hasPermission(roles, "project:read")({
|
|
31
|
-
...
|
|
24
|
+
if (!hasPermission(roles || [], "project:read")({
|
|
25
|
+
...request.locals,
|
|
32
26
|
targetProjects: projects
|
|
33
|
-
}))
|
|
34
|
-
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
27
|
+
})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
|
|
37
28
|
const totalItems = await countProjects(filters);
|
|
38
29
|
const responseData = formatPaginatedResponse({
|
|
39
30
|
data: mapProjectsToAPI(projects),
|
|
@@ -42,42 +33,28 @@ const getProjects = async (req, res, _next) => {
|
|
|
42
33
|
totalPages: getNumberOfPages(totalItems),
|
|
43
34
|
totalItems
|
|
44
35
|
});
|
|
45
|
-
|
|
46
|
-
return;
|
|
36
|
+
return reply.send(responseData);
|
|
47
37
|
} catch (error) {
|
|
48
|
-
ErrorHandler.handleAppErrorResponse(
|
|
49
|
-
return;
|
|
38
|
+
return ErrorHandler.handleAppErrorResponse(reply, error);
|
|
50
39
|
}
|
|
51
40
|
};
|
|
52
41
|
/**
|
|
53
42
|
* Adds a new project to the database.
|
|
54
43
|
*/
|
|
55
|
-
const addProject = async (
|
|
56
|
-
const { organization, user, roles } =
|
|
57
|
-
const projectData =
|
|
58
|
-
if (!user)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
if (!projectData) ErrorHandler.handleGenericErrorResponse(res, "PROJECT_DATA_NOT_FOUND");
|
|
67
|
-
if (!hasPermission(roles, "organization:admin")({
|
|
68
|
-
...res.locals,
|
|
44
|
+
const addProject = async (request, reply) => {
|
|
45
|
+
const { organization, user, roles } = request.locals || {};
|
|
46
|
+
const projectData = request.body;
|
|
47
|
+
if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
|
|
48
|
+
if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
|
|
49
|
+
if (!projectData) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_DATA_NOT_FOUND");
|
|
50
|
+
if (!hasPermission(roles || [], "organization:admin")({
|
|
51
|
+
...request.locals,
|
|
69
52
|
targetOrganizations: [organization]
|
|
70
|
-
}))
|
|
71
|
-
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
53
|
+
})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
|
|
74
54
|
const { plan } = organization;
|
|
75
55
|
const planType = getPlanDetails(plan);
|
|
76
56
|
if (planType.numberOfProjects) {
|
|
77
|
-
if (await countProjects({ organizationId: organization.id }) >= planType.numberOfProjects) {
|
|
78
|
-
ErrorHandler.handleGenericErrorResponse(res, "PLAN_PROJECT_LIMIT_REACHED", { organizationId: organization.id });
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
57
|
+
if (await countProjects({ organizationId: organization.id }) >= planType.numberOfProjects) return ErrorHandler.handleGenericErrorResponse(reply, "PLAN_PROJECT_LIMIT_REACHED", { organizationId: organization.id });
|
|
81
58
|
}
|
|
82
59
|
const project = {
|
|
83
60
|
membersIds: [user.id],
|
|
@@ -101,42 +78,25 @@ const addProject = async (req, res, _next) => {
|
|
|
101
78
|
}),
|
|
102
79
|
data: formattedProject
|
|
103
80
|
});
|
|
104
|
-
|
|
105
|
-
return;
|
|
81
|
+
return reply.send(responseData);
|
|
106
82
|
} catch (error) {
|
|
107
|
-
ErrorHandler.handleAppErrorResponse(
|
|
108
|
-
return;
|
|
83
|
+
return ErrorHandler.handleAppErrorResponse(reply, error);
|
|
109
84
|
}
|
|
110
85
|
};
|
|
111
86
|
/**
|
|
112
87
|
* Updates an existing project in the database.
|
|
113
88
|
*/
|
|
114
|
-
const updateProject = async (
|
|
115
|
-
const { organization, project, user, roles } =
|
|
116
|
-
const projectData =
|
|
117
|
-
if (!user)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (!project)
|
|
122
|
-
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
if (!organization) {
|
|
126
|
-
ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
if (String(project.organizationId) !== String(organization.id)) {
|
|
130
|
-
ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_IN_ORGANIZATION");
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
if (!hasPermission(roles, "project:write")({
|
|
134
|
-
...res.locals,
|
|
89
|
+
const updateProject = async (request, reply) => {
|
|
90
|
+
const { organization, project, user, roles } = request.locals || {};
|
|
91
|
+
const projectData = request.body;
|
|
92
|
+
if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
|
|
93
|
+
if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_DATA_NOT_FOUND");
|
|
94
|
+
if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
|
|
95
|
+
if (String(project.organizationId) !== String(organization.id)) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_IN_ORGANIZATION");
|
|
96
|
+
if (!hasPermission(roles || [], "project:write")({
|
|
97
|
+
...request.locals,
|
|
135
98
|
targetProjectIds: [String(project.id)]
|
|
136
|
-
}))
|
|
137
|
-
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
99
|
+
})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
|
|
140
100
|
try {
|
|
141
101
|
const formattedProject = mapProjectToAPI(await updateProjectById(project.id, projectData));
|
|
142
102
|
const responseData = formatResponse({
|
|
@@ -152,46 +112,26 @@ const updateProject = async (req, res, _next) => {
|
|
|
152
112
|
}),
|
|
153
113
|
data: formattedProject
|
|
154
114
|
});
|
|
155
|
-
|
|
156
|
-
return;
|
|
115
|
+
return reply.send(responseData);
|
|
157
116
|
} catch (error) {
|
|
158
|
-
ErrorHandler.handleAppErrorResponse(
|
|
159
|
-
return;
|
|
117
|
+
return ErrorHandler.handleAppErrorResponse(reply, error);
|
|
160
118
|
}
|
|
161
119
|
};
|
|
162
120
|
/**
|
|
163
121
|
* Update members to the dictionary in the database.
|
|
164
122
|
*/
|
|
165
|
-
const updateProjectMembers = async (
|
|
166
|
-
const { user, project, organization, roles } =
|
|
167
|
-
const { membersIds } =
|
|
168
|
-
if (!user)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
if (!organization) {
|
|
177
|
-
ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
if (membersIds?.length === 0) {
|
|
181
|
-
ErrorHandler.handleGenericErrorResponse(res, "PROJECT_MUST_HAVE_MEMBER");
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
if (membersIds?.map((el) => el.isAdmin)?.length === 0) {
|
|
185
|
-
ErrorHandler.handleGenericErrorResponse(res, "PROJECT_MUST_HAVE_ADMIN");
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
if (!hasPermission(roles, "project:write")({
|
|
189
|
-
...res.locals,
|
|
123
|
+
const updateProjectMembers = async (request, reply) => {
|
|
124
|
+
const { user, project, organization, roles } = request.locals || {};
|
|
125
|
+
const { membersIds } = request.body;
|
|
126
|
+
if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
|
|
127
|
+
if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
|
|
128
|
+
if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
|
|
129
|
+
if (membersIds?.length === 0) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_MUST_HAVE_MEMBER");
|
|
130
|
+
if (membersIds?.map((el) => el.isAdmin)?.length === 0) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_MUST_HAVE_ADMIN");
|
|
131
|
+
if (!hasPermission(roles || [], "project:write")({
|
|
132
|
+
...request.locals,
|
|
190
133
|
targetProjectIds: [String(project.id)]
|
|
191
|
-
}))
|
|
192
|
-
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
134
|
+
})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
|
|
195
135
|
try {
|
|
196
136
|
const existingUsers = [];
|
|
197
137
|
if (membersIds) {
|
|
@@ -225,45 +165,29 @@ const updateProjectMembers = async (req, res, _next) => {
|
|
|
225
165
|
}),
|
|
226
166
|
data: formattedProject
|
|
227
167
|
});
|
|
228
|
-
|
|
229
|
-
return;
|
|
168
|
+
return reply.send(responseData);
|
|
230
169
|
} catch (error) {
|
|
231
|
-
ErrorHandler.handleAppErrorResponse(
|
|
232
|
-
return;
|
|
170
|
+
return ErrorHandler.handleAppErrorResponse(reply, error);
|
|
233
171
|
}
|
|
234
172
|
};
|
|
235
173
|
/**
|
|
236
174
|
* Pushes a project configuration to the database.
|
|
237
|
-
* @param req - Express request object.
|
|
238
|
-
* @param res - Express response object.
|
|
239
|
-
* @returns Response confirming the deletion.
|
|
240
175
|
*/
|
|
241
|
-
const pushProjectConfiguration = async (
|
|
242
|
-
const { user, project, roles } =
|
|
243
|
-
const projectConfiguration =
|
|
244
|
-
if (!user)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (!project) {
|
|
249
|
-
ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (!hasPermission(roles, "project:write")({
|
|
253
|
-
...res.locals,
|
|
176
|
+
const pushProjectConfiguration = async (request, reply) => {
|
|
177
|
+
const { user, project, roles } = request.locals || {};
|
|
178
|
+
const projectConfiguration = request.body;
|
|
179
|
+
if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
|
|
180
|
+
if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
|
|
181
|
+
if (!hasPermission(roles || [], "project:write")({
|
|
182
|
+
...request.locals,
|
|
254
183
|
targetProjectIds: [String(project.id)]
|
|
255
|
-
}))
|
|
256
|
-
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
184
|
+
})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
|
|
259
185
|
try {
|
|
260
186
|
const projectObject = await getProjectById(project.id);
|
|
187
|
+
if (projectConfiguration.ai && projectObject.configuration?.ai?.apiKey && (!projectConfiguration.ai.apiKey || projectConfiguration.ai.apiKey.trim() === "")) projectConfiguration.ai.apiKey = projectObject.configuration.ai.apiKey;
|
|
261
188
|
projectObject.configuration = projectConfiguration;
|
|
262
|
-
projectObject.save();
|
|
263
|
-
if (!projectObject.configuration) {
|
|
264
|
-
ErrorHandler.handleGenericErrorResponse(res, "PROJECT_UPDATE_FAILED", { projectId: project.id });
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
189
|
+
await projectObject.save();
|
|
190
|
+
if (!projectObject.configuration) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_UPDATE_FAILED", { projectId: project.id });
|
|
267
191
|
const responseData = formatResponse({
|
|
268
192
|
message: t({
|
|
269
193
|
en: "Project configuration updated successfully",
|
|
@@ -275,57 +199,31 @@ const pushProjectConfiguration = async (req, res, _next) => {
|
|
|
275
199
|
fr: "La configuration du projet a été mise à jour avec succès",
|
|
276
200
|
es: "Su configuración del proyecto ha sido actualizada con éxito"
|
|
277
201
|
}),
|
|
278
|
-
data: projectObject
|
|
202
|
+
data: mapProjectToAPI(projectObject)
|
|
279
203
|
});
|
|
280
|
-
|
|
281
|
-
return;
|
|
204
|
+
return reply.send(responseData);
|
|
282
205
|
} catch (error) {
|
|
283
|
-
ErrorHandler.handleAppErrorResponse(
|
|
284
|
-
return;
|
|
206
|
+
return ErrorHandler.handleAppErrorResponse(reply, error);
|
|
285
207
|
}
|
|
286
208
|
};
|
|
287
209
|
/**
|
|
288
210
|
* Deletes a project from the database by its ID.
|
|
289
|
-
* @param req - Express request object.
|
|
290
|
-
* @param res - Express response object.
|
|
291
|
-
* @returns Response confirming the deletion.
|
|
292
211
|
*/
|
|
293
|
-
const deleteProject = async (
|
|
294
|
-
const { user, organization, project, session, roles } =
|
|
295
|
-
if (!user)
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (!
|
|
300
|
-
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
if (!project) {
|
|
304
|
-
ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
if (typeof session === "undefined") {
|
|
308
|
-
ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
if (!hasPermission(roles, "project:admin")({
|
|
312
|
-
...res.locals,
|
|
212
|
+
const deleteProject = async (_request, reply) => {
|
|
213
|
+
const { user, organization, project, session, roles } = _request.locals || {};
|
|
214
|
+
if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
|
|
215
|
+
if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
|
|
216
|
+
if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
|
|
217
|
+
if (typeof session === "undefined") return ErrorHandler.handleGenericErrorResponse(reply, "SESSION_NOT_DEFINED");
|
|
218
|
+
if (!hasPermission(roles || [], "project:admin")({
|
|
219
|
+
..._request.locals,
|
|
313
220
|
targetProjectIds: [String(project.id)]
|
|
314
|
-
}))
|
|
315
|
-
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
221
|
+
})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
|
|
318
222
|
try {
|
|
319
223
|
const projectToDelete = await getProjectById(project.id);
|
|
320
|
-
if (String(projectToDelete.organizationId) !== String(organization.id))
|
|
321
|
-
ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_IN_ORGANIZATION");
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
224
|
+
if (String(projectToDelete.organizationId) !== String(organization.id)) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_IN_ORGANIZATION");
|
|
324
225
|
const deletedProject = await deleteProjectById(project.id);
|
|
325
|
-
if (!deletedProject) {
|
|
326
|
-
ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED", { projectId: project.id });
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
226
|
+
if (!deletedProject) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED", { projectId: project.id });
|
|
329
227
|
logger.info(`Project deleted: ${String(deletedProject.id)}`);
|
|
330
228
|
const responseData = formatResponse({
|
|
331
229
|
message: t({
|
|
@@ -341,27 +239,19 @@ const deleteProject = async (_req, res, _next) => {
|
|
|
341
239
|
data: mapProjectToAPI(deletedProject)
|
|
342
240
|
});
|
|
343
241
|
await SessionModel.updateOne({ _id: session.id }, { $set: { activeProjectId: null } });
|
|
344
|
-
|
|
345
|
-
return;
|
|
242
|
+
return reply.send(responseData);
|
|
346
243
|
} catch (error) {
|
|
347
|
-
ErrorHandler.handleAppErrorResponse(
|
|
348
|
-
return;
|
|
244
|
+
return ErrorHandler.handleAppErrorResponse(reply, error);
|
|
349
245
|
}
|
|
350
246
|
};
|
|
351
247
|
/**
|
|
352
248
|
* Select a project.
|
|
353
249
|
*/
|
|
354
|
-
const selectProject = async (
|
|
355
|
-
const { projectId } =
|
|
356
|
-
const { session } =
|
|
357
|
-
if (!projectId)
|
|
358
|
-
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
if (typeof session === "undefined") {
|
|
362
|
-
ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
250
|
+
const selectProject = async (request, reply) => {
|
|
251
|
+
const { projectId } = request.params;
|
|
252
|
+
const { session } = request.locals || {};
|
|
253
|
+
if (!projectId) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_ID_NOT_FOUND");
|
|
254
|
+
if (typeof session === "undefined") return ErrorHandler.handleGenericErrorResponse(reply, "SESSION_NOT_DEFINED");
|
|
365
255
|
try {
|
|
366
256
|
const project = await getProjectById(projectId);
|
|
367
257
|
await SessionModel.updateOne({ _id: session.id }, { $set: { activeProjectId: String(projectId) } });
|
|
@@ -378,22 +268,17 @@ const selectProject = async (req, res, _next) => {
|
|
|
378
268
|
}),
|
|
379
269
|
data: mapProjectToAPI(project)
|
|
380
270
|
});
|
|
381
|
-
|
|
382
|
-
return;
|
|
271
|
+
return reply.send(responseData);
|
|
383
272
|
} catch (error) {
|
|
384
|
-
ErrorHandler.handleAppErrorResponse(
|
|
385
|
-
return;
|
|
273
|
+
return ErrorHandler.handleAppErrorResponse(reply, error);
|
|
386
274
|
}
|
|
387
275
|
};
|
|
388
276
|
/**
|
|
389
277
|
* Unselect a project.
|
|
390
278
|
*/
|
|
391
|
-
const unselectProject = async (
|
|
392
|
-
const { session } =
|
|
393
|
-
if (typeof session === "undefined")
|
|
394
|
-
ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
279
|
+
const unselectProject = async (_request, reply) => {
|
|
280
|
+
const { session } = _request.locals || {};
|
|
281
|
+
if (typeof session === "undefined") return ErrorHandler.handleGenericErrorResponse(reply, "SESSION_NOT_DEFINED");
|
|
397
282
|
try {
|
|
398
283
|
await SessionModel.updateOne({ _id: session.id }, { $set: { activeProjectId: null } });
|
|
399
284
|
const responseData = formatResponse({
|
|
@@ -409,11 +294,9 @@ const unselectProject = async (_req, res, _next) => {
|
|
|
409
294
|
}),
|
|
410
295
|
data: null
|
|
411
296
|
});
|
|
412
|
-
|
|
413
|
-
return;
|
|
297
|
+
return reply.send(responseData);
|
|
414
298
|
} catch (error) {
|
|
415
|
-
ErrorHandler.handleAppErrorResponse(
|
|
416
|
-
return;
|
|
299
|
+
return ErrorHandler.handleAppErrorResponse(reply, error);
|
|
417
300
|
}
|
|
418
301
|
};
|
|
419
302
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.controller.mjs","names":["projectService.findProjects","projectService.countProjects","project: ProjectData","projectService.createProject","projectService.updateProjectById","existingUsers: UserAndAdmin[]","userService.getUsersByIds","userMap: UserAndAdmin[]","user","formattedMembers: Types.ObjectId[]","formattedAdmin: Types.ObjectId[]","projectService.getProjectById","projectService.deleteProjectById"],"sources":["../../../src/controllers/project.controller.ts"],"sourcesContent":["import { 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 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 type { Types } from 'mongoose';\nimport type {\n ProjectAPI,\n ProjectConfiguration,\n ProjectCreationData,\n ProjectData,\n} from '@/types/project.types';\nimport type { User } from '@/types/user.types';\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, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getProjectFiltersAndPagination(req, res);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!organization && !roles.includes('admin')) {\n ErrorHandler.handleGenericErrorResponse(res, 'ORGANIZATION_NOT_DEFINED');\n return;\n }\n\n try {\n const projects = await projectService.findProjects(\n filters,\n skip,\n pageSize,\n sortOptions\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<ProjectAPI>;\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 } = 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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"}
|
|
1
|
+
{"version":3,"file":"project.controller.mjs","names":["projectService.findProjects","projectService.countProjects","project: ProjectData","projectService.createProject","projectService.updateProjectById","existingUsers: UserAndAdmin[]","userService.getUsersByIds","userMap: UserAndAdmin[]","user","formattedMembers: Types.ObjectId[]","formattedAdmin: Types.ObjectId[]","projectService.getProjectById","projectService.deleteProjectById"],"sources":["../../../src/controllers/project.controller.ts"],"sourcesContent":["import { 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 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 { FastifyReply, FastifyRequest } from 'fastify';\nimport { t } from 'fastify-intlayer';\nimport type { Types } from 'mongoose';\nimport type {\n ProjectAPI,\n ProjectConfiguration,\n ProjectCreationData,\n ProjectData,\n} from '@/types/project.types';\nimport type { User } from '@/types/user.types';\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 request: FastifyRequest<{ Querystring: GetProjectsParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, organization, roles } = request.locals || {};\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getProjectFiltersAndPagination(request);\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!organization && !roles?.includes('admin')) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n try {\n const projects = await projectService.findProjects(\n filters,\n skip,\n pageSize,\n sortOptions\n );\n\n if (\n !hasPermission(\n roles || [],\n 'project:read'\n )({\n ...request.locals,\n targetProjects: projects,\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\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 return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\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 request: FastifyRequest<{ Body: AddProjectBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, user, roles } = request.locals || {};\n const projectData = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (!projectData) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_DATA_NOT_FOUND'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'organization:admin'\n )({\n ...request.locals,\n targetOrganizations: [organization],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\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 return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PLAN_PROJECT_LIMIT_REACHED',\n {\n organizationId: organization.id,\n }\n );\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 return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type UpdateProjectBody = Partial<ProjectAPI>;\nexport type UpdateProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Updates an existing project in the database.\n */\nexport const updateProject = async (\n request: FastifyRequest<{ Body: UpdateProjectBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, project, user, roles } = request.locals || {};\n const projectData = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_DATA_NOT_FOUND'\n );\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (String(project.organizationId) !== String(organization.id)) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_IN_ORGANIZATION'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.locals,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\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 return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\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 request: FastifyRequest<{ Body: UpdateProjectMembersBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, organization, roles } = request.locals || {};\n const { membersIds } = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (membersIds?.length === 0) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_MUST_HAVE_MEMBER'\n );\n }\n\n if (membersIds?.map((el) => el.isAdmin)?.length === 0) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_MUST_HAVE_ADMIN'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.locals,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\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 return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type PushProjectConfigurationBody = ProjectConfiguration;\nexport type PushProjectConfigurationResult = ResponseData<ProjectConfiguration>;\n\n/**\n * Pushes a project configuration to the database.\n */\nexport const pushProjectConfiguration = async (\n request: FastifyRequest<{ Body: PushProjectConfigurationBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project, roles } = request.locals || {};\n const projectConfiguration = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:write'\n )({\n ...request.locals,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const projectObject = await projectService.getProjectById(project.id);\n\n // Preserve existing API key if not provided in the update\n if (\n projectConfiguration.ai &&\n projectObject.configuration?.ai?.apiKey &&\n (!projectConfiguration.ai.apiKey ||\n projectConfiguration.ai.apiKey.trim() === '')\n ) {\n projectConfiguration.ai.apiKey = projectObject.configuration.ai.apiKey;\n }\n\n projectObject.configuration = projectConfiguration;\n\n await projectObject.save();\n\n if (!projectObject.configuration) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_UPDATE_FAILED',\n {\n projectId: project.id,\n }\n );\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: mapProjectToAPI(projectObject) as ProjectConfiguration,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type DeleteProjectResult = ResponseData<ProjectAPI>;\n\n/**\n * Deletes a project from the database by its ID.\n */\nexport const deleteProject = async (\n _request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n const { user, organization, project, session, roles } = _request.locals || {};\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (!organization) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'ORGANIZATION_NOT_DEFINED'\n );\n }\n\n if (!project) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED'\n );\n }\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\n }\n\n if (\n !hasPermission(\n roles || [],\n 'project:admin'\n )({\n ..._request.locals,\n targetProjectIds: [String(project.id)],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const projectToDelete = await projectService.getProjectById(project.id);\n\n if (String(projectToDelete.organizationId) !== String(organization.id)) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_IN_ORGANIZATION'\n );\n }\n\n const deletedProject = await projectService.deleteProjectById(project.id);\n\n if (!deletedProject) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_NOT_DEFINED',\n {\n projectId: project.id,\n }\n );\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 return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\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 request: FastifyRequest<{ Params: SelectProjectParam }>,\n reply: FastifyReply\n) => {\n const { projectId } = request.params;\n const { session } = request.locals || {};\n\n if (!projectId) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PROJECT_ID_NOT_FOUND'\n );\n }\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\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 return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type UnselectProjectResult = ResponseData<null>;\n\n/**\n * Unselect a project.\n */\nexport const unselectProject = async (\n _request: FastifyRequest,\n reply: FastifyReply\n) => {\n const { session } = _request.locals || {};\n\n if (typeof session === 'undefined') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'SESSION_NOT_DEFINED'\n );\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 return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":"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"}
|