@intlayer/backend 7.5.9 → 7.5.11
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/cli/ci.json +3080 -0
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/cli/list_projects.json +1 -0
- 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/bitbucket.controller.mjs +77 -0
- package/dist/esm/controllers/bitbucket.controller.mjs.map +1 -0
- package/dist/esm/controllers/dictionary.controller.mjs +106 -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/gitlab.controller.mjs +77 -0
- package/dist/esm/controllers/gitlab.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 +194 -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 +4 -1
- package/dist/esm/index.mjs +105 -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/bitbucket.routes.mjs +43 -0
- package/dist/esm/routes/bitbucket.routes.mjs.map +1 -0
- 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/gitlab.routes.mjs +43 -0
- package/dist/esm/routes/gitlab.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 +38 -14
- 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 +70 -1
- package/dist/esm/schemas/project.schema.mjs.map +1 -1
- package/dist/esm/services/bitbucket.service.mjs +173 -0
- package/dist/esm/services/bitbucket.service.mjs.map +1 -0
- package/dist/esm/services/ci.service.mjs +134 -0
- package/dist/esm/services/ci.service.mjs.map +1 -0
- 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 +218 -0
- package/dist/esm/services/github.service.mjs.map +1 -0
- package/dist/esm/services/gitlab.service.mjs +217 -0
- package/dist/esm/services/gitlab.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/services/webhook.service.mjs +164 -0
- package/dist/esm/services/webhook.service.mjs.map +1 -0
- package/dist/esm/utils/auth/getAuth.mjs +28 -16
- 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 +234 -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/bitbucket.controller.d.ts +62 -0
- package/dist/types/controllers/bitbucket.controller.d.ts.map +1 -0
- 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/gitlab.controller.d.ts +67 -0
- package/dist/types/controllers/gitlab.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 +60 -17
- 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/OAuthTokenCreatedEmail.d.ts +4 -4
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts.map +1 -1
- package/dist/types/emails/PasswordChangeConfirmation.d.ts +4 -4
- package/dist/types/emails/ResetUserPassword.d.ts +4 -4
- package/dist/types/emails/ResetUserPassword.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts +4 -4
- 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/Welcome.d.ts +4 -4
- package/dist/types/export.d.ts +11 -5
- 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/discussion.model.d.ts +3 -3
- package/dist/types/models/oAuth2.model.d.ts +3 -3
- 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/bitbucket.routes.d.ts +35 -0
- package/dist/types/routes/bitbucket.routes.d.ts.map +1 -0
- 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/gitlab.routes.d.ts +35 -0
- package/dist/types/routes/gitlab.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 +22 -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/oAuth2.schema.d.ts.map +1 -1
- package/dist/types/schemas/plans.schema.d.ts +6 -6
- 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/bitbucket.service.d.ts +71 -0
- package/dist/types/services/bitbucket.service.d.ts.map +1 -0
- package/dist/types/services/ci.service.d.ts +27 -0
- package/dist/types/services/ci.service.d.ts.map +1 -0
- package/dist/types/services/github.service.d.ts +40 -0
- package/dist/types/services/github.service.d.ts.map +1 -0
- package/dist/types/services/gitlab.service.d.ts +58 -0
- package/dist/types/services/gitlab.service.d.ts.map +1 -0
- package/dist/types/services/webhook.service.d.ts +19 -0
- package/dist/types/services/webhook.service.d.ts.map +1 -0
- package/dist/types/types/project.types.d.ts +46 -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 +234 -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 +6 -2
- 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/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 +24 -28
- 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
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { logger } from "../logger/index.mjs";
|
|
2
|
+
import { createHmac } from "node:crypto";
|
|
3
|
+
import { Octokit } from "@octokit/rest";
|
|
4
|
+
|
|
5
|
+
//#region src/services/webhook.service.ts
|
|
6
|
+
/**
|
|
7
|
+
* Main entry point to trigger all configured CI pipelines for a project
|
|
8
|
+
*/
|
|
9
|
+
const triggerAll = async (project) => {
|
|
10
|
+
const results = [];
|
|
11
|
+
if (project.repository && project.webhooks?.autoTriggerBuilds) try {
|
|
12
|
+
await triggerGitPipeline(project);
|
|
13
|
+
results.push({
|
|
14
|
+
target: project.repository.provider,
|
|
15
|
+
success: true
|
|
16
|
+
});
|
|
17
|
+
} catch (error) {
|
|
18
|
+
logger.error(`Failed to trigger ${project.repository.provider}`, error);
|
|
19
|
+
results.push({
|
|
20
|
+
target: project.repository.provider,
|
|
21
|
+
success: false,
|
|
22
|
+
message: error.message || String(error)
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const webhooks = project.webhooks?.webhooks || [];
|
|
26
|
+
for (const hook of webhooks) {
|
|
27
|
+
if (!hook.enabled) continue;
|
|
28
|
+
try {
|
|
29
|
+
await triggerGenericWebhook(hook);
|
|
30
|
+
results.push({
|
|
31
|
+
target: hook.name,
|
|
32
|
+
success: true
|
|
33
|
+
});
|
|
34
|
+
} catch (error) {
|
|
35
|
+
logger.error(`Failed to trigger webhook ${hook.name}`, error);
|
|
36
|
+
results.push({
|
|
37
|
+
target: hook.name,
|
|
38
|
+
success: false,
|
|
39
|
+
message: error.message || String(error)
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return results;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Triggers a single webhook by index
|
|
47
|
+
*/
|
|
48
|
+
const triggerSingleWebhook = async (project, webhookIndex) => {
|
|
49
|
+
const webhooks = project.webhooks?.webhooks || [];
|
|
50
|
+
if (webhookIndex < 0 || webhookIndex >= webhooks.length) throw new Error(`Webhook index ${webhookIndex} is out of range`);
|
|
51
|
+
const hook = webhooks[webhookIndex];
|
|
52
|
+
if (!hook.enabled) throw new Error(`Webhook "${hook.name}" is disabled`);
|
|
53
|
+
try {
|
|
54
|
+
await triggerGenericWebhook(hook);
|
|
55
|
+
return {
|
|
56
|
+
target: hook.name,
|
|
57
|
+
success: true
|
|
58
|
+
};
|
|
59
|
+
} catch (error) {
|
|
60
|
+
logger.error(`Failed to trigger webhook ${hook.name}`, error);
|
|
61
|
+
return {
|
|
62
|
+
target: hook.name,
|
|
63
|
+
success: false,
|
|
64
|
+
message: error.message || String(error)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const triggerGitPipeline = async (project) => {
|
|
69
|
+
const { repository, oAuth2Access } = project;
|
|
70
|
+
if (!repository) throw new Error("No repository configured");
|
|
71
|
+
const token = oAuth2Access?.[0]?.accessToken?.[0];
|
|
72
|
+
if (!token) throw new Error("No valid OAuth token found");
|
|
73
|
+
const { provider } = repository;
|
|
74
|
+
switch (provider) {
|
|
75
|
+
case "github": return triggerGithub(repository, token);
|
|
76
|
+
case "gitlab": return triggerGitlab(repository, token);
|
|
77
|
+
case "bitbucket": return triggerBitbucket(repository, token);
|
|
78
|
+
default: throw new Error(`Unknown provider: ${provider}`);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const triggerGithub = async (repo, token) => {
|
|
82
|
+
await new Octokit({ auth: token }).repos.createDispatchEvent({
|
|
83
|
+
owner: repo.owner,
|
|
84
|
+
repo: repo.repository,
|
|
85
|
+
event_type: "intlayer_cms_update",
|
|
86
|
+
client_payload: {
|
|
87
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
88
|
+
source: "intlayer-cms"
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
logger.info(`Successfully triggered GitHub Action for ${repo.owner}/${repo.repository}`);
|
|
92
|
+
};
|
|
93
|
+
const triggerGitlab = async (repo, token) => {
|
|
94
|
+
const projectId = encodeURIComponent(`${repo.owner}/${repo.repository}`);
|
|
95
|
+
const branch = repo.branch || "main";
|
|
96
|
+
const url = `${repo.instanceUrl || "https://gitlab.com"}/api/v4/projects/${projectId}/trigger/pipeline`;
|
|
97
|
+
const formData = new FormData();
|
|
98
|
+
formData.append("token", token);
|
|
99
|
+
formData.append("ref", branch);
|
|
100
|
+
formData.append("variables[INTLAYER_UPDATE]", "true");
|
|
101
|
+
const res = await fetch(url, {
|
|
102
|
+
method: "POST",
|
|
103
|
+
body: formData
|
|
104
|
+
});
|
|
105
|
+
if (!res.ok) {
|
|
106
|
+
const errorText = await res.text();
|
|
107
|
+
throw new Error(`GitLab error: ${res.status} - ${errorText}`);
|
|
108
|
+
}
|
|
109
|
+
logger.info(`Successfully triggered GitLab pipeline for ${repo.owner}/${repo.repository}`);
|
|
110
|
+
};
|
|
111
|
+
const triggerBitbucket = async (repo, token) => {
|
|
112
|
+
const workspace = repo.workspace || repo.owner;
|
|
113
|
+
const branch = repo.branch || "main";
|
|
114
|
+
const url = `https://api.bitbucket.org/2.0/repositories/${workspace}/${repo.repository}/pipelines/`;
|
|
115
|
+
const body = {
|
|
116
|
+
target: {
|
|
117
|
+
ref_type: "branch",
|
|
118
|
+
type: "pipeline_ref_target",
|
|
119
|
+
ref_name: branch
|
|
120
|
+
},
|
|
121
|
+
variables: [{
|
|
122
|
+
key: "INTLAYER_UPDATE",
|
|
123
|
+
value: "true",
|
|
124
|
+
secured: false
|
|
125
|
+
}]
|
|
126
|
+
};
|
|
127
|
+
const res = await fetch(url, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers: {
|
|
130
|
+
Authorization: `Bearer ${token}`,
|
|
131
|
+
"Content-Type": "application/json"
|
|
132
|
+
},
|
|
133
|
+
body: JSON.stringify(body)
|
|
134
|
+
});
|
|
135
|
+
if (!res.ok) {
|
|
136
|
+
const errorText = await res.text();
|
|
137
|
+
throw new Error(`Bitbucket error: ${res.status} - ${errorText}`);
|
|
138
|
+
}
|
|
139
|
+
logger.info(`Successfully triggered Bitbucket pipeline for ${workspace}/${repo.repository}`);
|
|
140
|
+
};
|
|
141
|
+
const triggerGenericWebhook = async (hook) => {
|
|
142
|
+
const headers = { "Content-Type": "application/json" };
|
|
143
|
+
if (hook.secret) {
|
|
144
|
+
const payload = JSON.stringify({ event: "intlayer_cms_update" });
|
|
145
|
+
headers["X-Intlayer-Signature"] = createHmac("sha256", hook.secret).update(payload).digest("hex");
|
|
146
|
+
}
|
|
147
|
+
const res = await fetch(hook.url, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers,
|
|
150
|
+
body: JSON.stringify({
|
|
151
|
+
event: "intlayer_cms_update",
|
|
152
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
153
|
+
})
|
|
154
|
+
});
|
|
155
|
+
if (!res.ok) {
|
|
156
|
+
const errorText = await res.text();
|
|
157
|
+
throw new Error(`Webhook ${hook.name} failed: ${res.status} - ${errorText}`);
|
|
158
|
+
}
|
|
159
|
+
logger.info(`Successfully triggered webhook: ${hook.name}`);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
//#endregion
|
|
163
|
+
export { triggerAll, triggerSingleWebhook };
|
|
164
|
+
//# sourceMappingURL=webhook.service.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook.service.mjs","names":["results: TriggerResult[]","error: any","headers: Record<string, string>"],"sources":["../../../src/services/webhook.service.ts"],"sourcesContent":["import { createHmac } from 'node:crypto';\nimport { logger } from '@logger';\nimport { Octokit } from '@octokit/rest';\nimport type { Project } from '@/types/project.types';\n\nexport type TriggerResult = {\n target: string;\n success: boolean;\n message?: string;\n};\n\n/**\n * Main entry point to trigger all configured CI pipelines for a project\n */\nexport const triggerAll = async (\n project: Project\n): Promise<TriggerResult[]> => {\n const results: TriggerResult[] = [];\n\n // Trigger Git Provider Pipeline (if configured)\n if (project.repository && project.webhooks?.autoTriggerBuilds) {\n try {\n await triggerGitPipeline(project);\n results.push({\n target: project.repository.provider,\n success: true,\n });\n } catch (error: any) {\n logger.error(`Failed to trigger ${project.repository.provider}`, error);\n results.push({\n target: project.repository.provider,\n success: false,\n message: error.message || String(error),\n });\n }\n }\n\n // Trigger Generic Webhooks (Vercel, etc.)\n const webhooks = project.webhooks?.webhooks || [];\n\n // Using Promise.all is often better here, but keeping your sequential loop logic for safety\n for (const hook of webhooks) {\n if (!hook.enabled) continue;\n try {\n await triggerGenericWebhook(hook);\n results.push({ target: hook.name, success: true });\n } catch (error: any) {\n logger.error(`Failed to trigger webhook ${hook.name}`, error);\n results.push({\n target: hook.name,\n success: false,\n message: error.message || String(error),\n });\n }\n }\n\n return results;\n};\n\n/**\n * Triggers a single webhook by index\n */\nexport const triggerSingleWebhook = async (\n project: Project,\n webhookIndex: number\n): Promise<TriggerResult> => {\n const webhooks = project.webhooks?.webhooks || [];\n\n if (webhookIndex < 0 || webhookIndex >= webhooks.length) {\n throw new Error(`Webhook index ${webhookIndex} is out of range`);\n }\n\n const hook = webhooks[webhookIndex];\n\n if (!hook.enabled) {\n throw new Error(`Webhook \"${hook.name}\" is disabled`);\n }\n\n try {\n await triggerGenericWebhook(hook);\n return { target: hook.name, success: true };\n } catch (error: any) {\n logger.error(`Failed to trigger webhook ${hook.name}`, error);\n return {\n target: hook.name,\n success: false,\n message: error.message || String(error),\n };\n }\n};\n\n// Internal Helper Functions (equivalent to private static methods)\n\nconst triggerGitPipeline = async (project: Project) => {\n const { repository, oAuth2Access } = project;\n\n if (!repository) throw new Error('No repository configured');\n\n const token = oAuth2Access?.[0]?.accessToken?.[0]; // Get the first valid token\n\n if (!token) throw new Error('No valid OAuth token found');\n\n const { provider } = repository;\n\n switch (provider) {\n case 'github':\n return triggerGithub(repository, token);\n case 'gitlab':\n return triggerGitlab(repository, token);\n case 'bitbucket':\n return triggerBitbucket(repository, token);\n default:\n throw new Error(`Unknown provider: ${provider as string}`);\n }\n};\n\nconst triggerGithub = async (repo: any, token: string) => {\n const octokit = new Octokit({ auth: token });\n\n // Triggers a 'repository_dispatch' event\n // Workflow must listen to: types: [intlayer_cms_update]\n await octokit.repos.createDispatchEvent({\n owner: repo.owner,\n repo: repo.repository,\n event_type: 'intlayer_cms_update',\n client_payload: {\n timestamp: new Date().toISOString(),\n source: 'intlayer-cms',\n },\n });\n\n logger.info(\n `Successfully triggered GitHub Action for ${repo.owner}/${repo.repository}`\n );\n};\n\n// GitLab\nconst triggerGitlab = async (repo: any, token: string) => {\n // GitLab needs Project ID (int) or URL-encoded path \"owner/repo\"\n const projectId = encodeURIComponent(`${repo.owner}/${repo.repository}`);\n const branch = repo.branch || 'main';\n const baseUrl = repo.instanceUrl || 'https://gitlab.com';\n\n const url = `${baseUrl}/api/v4/projects/${projectId}/trigger/pipeline`;\n\n const formData = new FormData();\n formData.append('token', token); // Or a specific trigger token if stored separately\n formData.append('ref', branch);\n formData.append('variables[INTLAYER_UPDATE]', 'true');\n\n const res = await fetch(url, { method: 'POST', body: formData });\n if (!res.ok) {\n const errorText = await res.text();\n throw new Error(`GitLab error: ${res.status} - ${errorText}`);\n }\n\n logger.info(\n `Successfully triggered GitLab pipeline for ${repo.owner}/${repo.repository}`\n );\n};\n\n// Bitbucket\nconst triggerBitbucket = async (repo: any, token: string) => {\n const workspace = repo.workspace || repo.owner; // Bitbucket uses 'workspace'\n const branch = repo.branch || 'main';\n const url = `https://api.bitbucket.org/2.0/repositories/${workspace}/${repo.repository}/pipelines/`;\n\n const body = {\n target: {\n ref_type: 'branch',\n type: 'pipeline_ref_target',\n ref_name: branch,\n // Optional: Target a custom pipeline for security\n // selector: { type: 'custom', pattern: 'intlayer-update' }\n },\n variables: [{ key: 'INTLAYER_UPDATE', value: 'true', secured: false }],\n };\n\n const res = await fetch(url, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n\n if (!res.ok) {\n const errorText = await res.text();\n throw new Error(`Bitbucket error: ${res.status} - ${errorText}`);\n }\n\n logger.info(\n `Successfully triggered Bitbucket pipeline for ${workspace}/${repo.repository}`\n );\n};\n\n// Generic Webhook\nconst triggerGenericWebhook = async (hook: any) => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // Add secret signature if provided (for webhook verification)\n if (hook.secret) {\n // Simple HMAC-SHA256 signature (can be enhanced)\n const payload = JSON.stringify({ event: 'intlayer_cms_update' });\n const signature = createHmac('sha256', hook.secret)\n .update(payload)\n .digest('hex');\n headers['X-Intlayer-Signature'] = signature;\n }\n\n const res = await fetch(hook.url, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n event: 'intlayer_cms_update',\n timestamp: new Date().toISOString(),\n }),\n });\n\n if (!res.ok) {\n const errorText = await res.text();\n throw new Error(\n `Webhook ${hook.name} failed: ${res.status} - ${errorText}`\n );\n }\n\n logger.info(`Successfully triggered webhook: ${hook.name}`);\n};\n"],"mappings":"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"}
|
|
@@ -34,7 +34,7 @@ const formatSession = (session) => {
|
|
|
34
34
|
};
|
|
35
35
|
const getAuth = (dbClient) => {
|
|
36
36
|
if (!dbClient) throw new Error("MongoDB connection not established");
|
|
37
|
-
|
|
37
|
+
return betterAuth({
|
|
38
38
|
appName: "Intlayer",
|
|
39
39
|
database: mongodbAdapter(dbClient.db()),
|
|
40
40
|
user: { modelName: "users" },
|
|
@@ -44,7 +44,7 @@ const getAuth = (dbClient) => {
|
|
|
44
44
|
type: "welcome",
|
|
45
45
|
to: user.email,
|
|
46
46
|
username: user.name ?? user.email.split("@")[0],
|
|
47
|
-
loginLink: `${process.env.
|
|
47
|
+
loginLink: `${process.env.APP_URL}/auth/login`,
|
|
48
48
|
locale: user.lang
|
|
49
49
|
});
|
|
50
50
|
logger.info("Welcome e‑mail delivered", { email: user.email });
|
|
@@ -65,7 +65,7 @@ const getAuth = (dbClient) => {
|
|
|
65
65
|
type: "welcome",
|
|
66
66
|
to: user.email,
|
|
67
67
|
username: user.name ?? user.email.split("@")[0],
|
|
68
|
-
loginLink: `${process.env.
|
|
68
|
+
loginLink: `${process.env.APP_URL}/auth/login`,
|
|
69
69
|
locale: user.lang
|
|
70
70
|
});
|
|
71
71
|
logger.info("Welcome e‑mail delivered", { email: user.email });
|
|
@@ -95,7 +95,6 @@ const getAuth = (dbClient) => {
|
|
|
95
95
|
plugins: [
|
|
96
96
|
customSession(async ({ session }) => {
|
|
97
97
|
const typedSession = session;
|
|
98
|
-
await auth.api.callbackSSOSAML;
|
|
99
98
|
let userAPI = null;
|
|
100
99
|
let organizationAPI = null;
|
|
101
100
|
let projectAPI = null;
|
|
@@ -159,19 +158,11 @@ const getAuth = (dbClient) => {
|
|
|
159
158
|
type: "resetPassword",
|
|
160
159
|
to: user.email,
|
|
161
160
|
username: user.name ?? user.email.split("@")[0],
|
|
162
|
-
resetLink: `${process.env.
|
|
161
|
+
resetLink: `${process.env.APP_URL}/auth/password/reset?token=${token}`
|
|
163
162
|
});
|
|
164
163
|
},
|
|
165
164
|
resetPasswordTokenExpiresIn: 3600
|
|
166
165
|
},
|
|
167
|
-
accountLinking: {
|
|
168
|
-
enabled: true,
|
|
169
|
-
trustedProviders: [
|
|
170
|
-
"google",
|
|
171
|
-
"github",
|
|
172
|
-
"linkedin"
|
|
173
|
-
]
|
|
174
|
-
},
|
|
175
166
|
emailVerification: {
|
|
176
167
|
autoSignInAfterVerification: true,
|
|
177
168
|
sendOnSignIn: true,
|
|
@@ -188,7 +179,7 @@ const getAuth = (dbClient) => {
|
|
|
188
179
|
crossSubDomainCookies: {
|
|
189
180
|
enabled: true,
|
|
190
181
|
additionalCookies: ["session_token"],
|
|
191
|
-
domain: process.env.
|
|
182
|
+
domain: process.env.DOMAIN
|
|
192
183
|
},
|
|
193
184
|
cookiePrefix: "intlayer",
|
|
194
185
|
cookies: { session_token: {
|
|
@@ -198,7 +189,21 @@ const getAuth = (dbClient) => {
|
|
|
198
189
|
secure: true
|
|
199
190
|
}
|
|
200
191
|
} },
|
|
201
|
-
trustedOrigins: [process.env.
|
|
192
|
+
trustedOrigins: [process.env.WEBSITE_URL, process.env.APP_URL],
|
|
193
|
+
accountLinking: {
|
|
194
|
+
enabled: true,
|
|
195
|
+
trustedProviders: [
|
|
196
|
+
"google",
|
|
197
|
+
"github",
|
|
198
|
+
"linkedin",
|
|
199
|
+
"gitlab",
|
|
200
|
+
"atlassian",
|
|
201
|
+
"microsoft",
|
|
202
|
+
"email-password",
|
|
203
|
+
"magic-link",
|
|
204
|
+
"passkey"
|
|
205
|
+
]
|
|
206
|
+
},
|
|
202
207
|
socialProviders: {
|
|
203
208
|
google: {
|
|
204
209
|
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
@@ -208,6 +213,14 @@ const getAuth = (dbClient) => {
|
|
|
208
213
|
clientId: process.env.GITHUB_CLIENT_ID,
|
|
209
214
|
clientSecret: process.env.GITHUB_CLIENT_SECRET
|
|
210
215
|
},
|
|
216
|
+
atlassian: {
|
|
217
|
+
clientId: process.env.ATLASSIAN_CLIENT_ID,
|
|
218
|
+
clientSecret: process.env.ATLASSIAN_CLIENT_SECRET
|
|
219
|
+
},
|
|
220
|
+
gitlab: {
|
|
221
|
+
clientId: process.env.GITLAB_CLIENT_ID,
|
|
222
|
+
clientSecret: process.env.GITLAB_CLIENT_SECRET
|
|
223
|
+
},
|
|
211
224
|
linkedin: {
|
|
212
225
|
clientId: process.env.LINKEDIN_CLIENT_ID,
|
|
213
226
|
clientSecret: process.env.LINKEDIN_CLIENT_SECRET
|
|
@@ -219,7 +232,6 @@ const getAuth = (dbClient) => {
|
|
|
219
232
|
},
|
|
220
233
|
logger: { log: (level, message, ...args) => logger[level](message, ...args) }
|
|
221
234
|
});
|
|
222
|
-
return auth;
|
|
223
235
|
};
|
|
224
236
|
|
|
225
237
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getAuth.mjs","names":["userAPI: UserAPI | null","organizationAPI: OrganizationAPI | null","projectAPI: ProjectAPI | null"],"sources":["../../../../src/utils/auth/getAuth.ts"],"sourcesContent":["import { passkey } from '@better-auth/passkey';\nimport { sso } from '@better-auth/sso';\nimport { sendVerificationUpdate } from '@controllers/user.controller';\nimport { logger } from '@logger';\nimport { OrganizationModel } from '@models/organization.model';\nimport { sendEmail } from '@services/email.service';\nimport { getOrganizationById } from '@services/organization.service';\nimport { getProjectById } from '@services/project.service';\nimport { getUserById } from '@services/user.service';\nimport { mapOrganizationToAPI } from '@utils/mapper/organization';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { mapSessionToAPI } from '@utils/mapper/session';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport {\n computeEffectivePermission,\n getSessionRoles,\n intersectPermissions,\n} from '@utils/permissions';\nimport { betterAuth, type OmitId } from 'better-auth';\nimport { mongodbAdapter } from 'better-auth/adapters/mongodb';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport { customSession, lastLoginMethod, twoFactor } from 'better-auth/plugins';\nimport { magicLink } from 'better-auth/plugins/magic-link';\nimport type { MongoClient } from 'mongodb';\nimport type { OrganizationAPI } from '@/types/organization.types';\nimport type { ProjectAPI } from '@/types/project.types';\nimport type {\n Session,\n SessionContext,\n SessionDataApi,\n} from '@/types/session.types';\nimport type { User, UserAPI } from '@/types/user.types';\n\nexport type Auth = ReturnType<typeof betterAuth>;\n\nexport const formatSession = (session: SessionContext): OmitId<Session> => {\n const roles = getSessionRoles(session);\n let permissions = computeEffectivePermission(roles);\n\n // Intersect in the case a Access Token try to override the permissions\n if (session.permissions) {\n permissions = intersectPermissions(permissions, session.permissions);\n }\n\n const resultSession = {\n session: session.session,\n user: session.user,\n organization: session.organization,\n project: session.project,\n authType: 'session',\n permissions,\n roles,\n } as OmitId<Session>;\n\n return resultSession;\n};\n\nexport const getAuth = (dbClient: MongoClient): Auth => {\n if (!dbClient) {\n throw new Error('MongoDB connection not established');\n }\n\n const auth = betterAuth({\n appName: 'Intlayer',\n\n database: mongodbAdapter(dbClient.db()),\n\n /**\n * User model\n */\n user: {\n modelName: 'users',\n },\n\n databaseHooks: {\n user: {\n create: {\n // Runs once, immediately after the INSERT\n after: async (user) => {\n if (!user?.emailVerified) return;\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n },\n },\n },\n },\n\n hooks: {\n after: createAuthMiddleware(async (ctx) => {\n const { path, context } = ctx;\n\n const newUser = context.newSession?.user;\n const existingUser = context.session?.user;\n const user = newUser ?? existingUser;\n\n if (!user) return;\n\n if (path.includes('/verify-email')) {\n sendVerificationUpdate(user as unknown as User);\n logger.info('SSE verification update sent', {\n email: user.email,\n userId: user.id,\n });\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n }\n }),\n },\n\n advanced: {\n // 1️⃣ Change or drop the global prefix\n // cookiePrefix: \"intlayer\", // => intlayer.session_token\n cookiePrefix: 'intlayer', // => session_token (no prefix)\n\n // 2️⃣ Override just the session‑token cookie\n cookies: {\n session_token: {\n // name: 'intlayer_session_token', // final name depends on the prefix above\n // attributes: { sameSite: \"lax\", maxAge: 60 * 60 * 24 } // optional\n },\n },\n\n // 3️⃣ (optional) turn off the automatic __Secure‑ prefix in non‑prod\n // useSecureCookies: false,\n },\n\n secret: process.env.BETTER_AUTH_SECRET as string,\n session: {\n modelName: 'sessions',\n id: 'id',\n\n additionalFields: {\n activeOrganizationId: { type: 'string', nullable: true, input: false },\n activeProjectId: { type: 'string', nullable: true, input: false },\n },\n },\n\n plugins: [\n customSession(async ({ session }) => {\n const typedSession = session as unknown as SessionDataApi;\n\n await auth.api.callbackSSOSAML;\n\n let userAPI: UserAPI | null = null;\n let organizationAPI: OrganizationAPI | null = null;\n let projectAPI: ProjectAPI | null = null;\n\n if (typedSession.userId) {\n const userData = await getUserById(typedSession.userId);\n\n if (userData) {\n userAPI = mapUserToAPI(userData);\n }\n }\n\n if (typedSession.activeOrganizationId) {\n const orgData = await getOrganizationById(\n typedSession.activeOrganizationId\n );\n\n if (orgData) {\n organizationAPI = mapOrganizationToAPI(orgData);\n }\n }\n if (typedSession.activeProjectId) {\n const projectData = await getProjectById(\n typedSession.activeProjectId\n );\n\n if (projectData) {\n projectAPI = mapProjectToAPI(projectData);\n }\n }\n\n const sessionWithNoPermission: SessionContext = {\n session: typedSession,\n user: userAPI!,\n organization: organizationAPI ?? null,\n project: projectAPI ?? null,\n authType: 'session',\n };\n\n const formattedSession = formatSession(sessionWithNoPermission);\n\n return mapSessionToAPI(formattedSession);\n }),\n lastLoginMethod({\n storeInDatabase: true, // adds user.lastLoginMethod in DB and session\n schema: {\n user: {\n lastLoginMethod: 'lastLoginMethod', // Custom field name\n },\n },\n customResolveMethod: (context) => {\n // When user clicks the magic link\n if (context.path === '/magic-link/verify') {\n return 'magic-link';\n }\n\n // Fallback to default behavior for everything else\n return null;\n },\n }),\n passkey({\n rpID: process.env.DOMAIN,\n rpName: 'Intlayer',\n }),\n twoFactor(),\n magicLink({\n sendMagicLink: async ({ email, url }) => {\n logger.info('sending magic link', { email, url });\n await sendEmail({\n type: 'magicLink',\n to: email,\n username: email.split('@')[0],\n magicLink: url,\n });\n },\n }),\n sso({\n organizationProvisioning: {},\n }),\n ],\n\n emailAndPassword: {\n enabled: true,\n disableSignUp: false,\n requireEmailVerification: true,\n minPasswordLength: 8,\n maxPasswordLength: 128,\n autoSignIn: true,\n sendResetPassword: async ({ user, token }) => {\n logger.info('sending reset password email', { email: user.email });\n await sendEmail({\n type: 'resetPassword',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n resetLink: `${process.env.CLIENT_URL}/auth/password/reset?token=${token}`,\n });\n },\n resetPasswordTokenExpiresIn: 3600,\n },\n accountLinking: {\n enabled: true, // allow linking in general\n trustedProviders: ['google', 'github', 'linkedin'], // optional: auto‑link when Google verifies the e‑mail\n },\n emailVerification: {\n autoSignInAfterVerification: true,\n sendOnSignIn: true,\n sendVerificationEmail: async ({ user, url }) => {\n logger.info('sending verification email', { email: user.email });\n await sendEmail({\n type: 'validate',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n validationLink: url,\n });\n },\n },\n\n crossSubDomainCookies: {\n enabled: true,\n additionalCookies: ['session_token'],\n domain: process.env.CLIENT_URL as string,\n },\n cookiePrefix: 'intlayer',\n cookies: {\n session_token: {\n name: 'session_token',\n attributes: {\n httpOnly: true,\n secure: true,\n },\n },\n },\n\n trustedOrigins: [process.env.CLIENT_URL as string],\n\n socialProviders: {\n google: {\n clientId: process.env.GOOGLE_CLIENT_ID as string,\n clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,\n },\n github: {\n clientId: process.env.GITHUB_CLIENT_ID as string,\n clientSecret: process.env.GITHUB_CLIENT_SECRET as string,\n },\n linkedin: {\n clientId: process.env.LINKEDIN_CLIENT_ID as string,\n clientSecret: process.env.LINKEDIN_CLIENT_SECRET as string,\n },\n microsoft: {\n clientId: process.env.MICROSOFT_CLIENT_ID as string,\n clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string,\n },\n // socialProviders: {\n // apple: {\n // clientId: process.env.APPLE_CLIENT_ID as string,\n // clientSecret: process.env.APPLE_CLIENT_SECRET as string,\n // // Optional\n // appBundleIdentifier: process.env\n // .APPLE_APP_BUNDLE_IDENTIFIER as string,\n // },\n // },\n // // Add appleid.apple.com to trustedOrigins for Sign In with Apple flows\n // trustedOrigins: ['https://appleid.apple.com'],\n },\n\n logger: {\n log: (level, message, ...args) => logger[level](message, ...args),\n },\n });\n\n return auth;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"getAuth.mjs","names":["userAPI: UserAPI | null","organizationAPI: OrganizationAPI | null","projectAPI: ProjectAPI | null"],"sources":["../../../../src/utils/auth/getAuth.ts"],"sourcesContent":["import { passkey } from '@better-auth/passkey';\nimport { sso } from '@better-auth/sso';\nimport { sendVerificationUpdate } from '@controllers/user.controller';\nimport { logger } from '@logger';\nimport { sendEmail } from '@services/email.service';\nimport { getOrganizationById } from '@services/organization.service';\nimport { getProjectById } from '@services/project.service';\nimport { getUserById } from '@services/user.service';\nimport { mapOrganizationToAPI } from '@utils/mapper/organization';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { mapSessionToAPI } from '@utils/mapper/session';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport {\n computeEffectivePermission,\n getSessionRoles,\n intersectPermissions,\n} from '@utils/permissions';\nimport { betterAuth, type OmitId } from 'better-auth';\nimport { mongodbAdapter } from 'better-auth/adapters/mongodb';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport { customSession, lastLoginMethod, twoFactor } from 'better-auth/plugins';\nimport { magicLink } from 'better-auth/plugins/magic-link';\nimport type { MongoClient } from 'mongodb';\nimport type { OrganizationAPI } from '@/types/organization.types';\nimport type { ProjectAPI } from '@/types/project.types';\nimport type {\n Session,\n SessionContext,\n SessionDataApi,\n} from '@/types/session.types';\nimport type { User, UserAPI } from '@/types/user.types';\n\nexport type Auth = ReturnType<typeof betterAuth>;\n\nexport const formatSession = (session: SessionContext): OmitId<Session> => {\n const roles = getSessionRoles(session);\n let permissions = computeEffectivePermission(roles);\n\n // Intersect in the case a Access Token try to override the permissions\n if (session.permissions) {\n permissions = intersectPermissions(permissions, session.permissions);\n }\n\n const resultSession = {\n session: session.session,\n user: session.user,\n organization: session.organization,\n project: session.project,\n authType: 'session',\n permissions,\n roles,\n } as OmitId<Session>;\n\n return resultSession;\n};\n\nexport const getAuth = (dbClient: MongoClient): Auth => {\n if (!dbClient) {\n throw new Error('MongoDB connection not established');\n }\n\n const auth = betterAuth({\n appName: 'Intlayer',\n\n database: mongodbAdapter(dbClient.db()),\n\n /**\n * User model\n */\n user: {\n modelName: 'users',\n },\n\n databaseHooks: {\n user: {\n create: {\n // Runs once, immediately after the INSERT\n after: async (user) => {\n if (!user?.emailVerified) return;\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.APP_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n },\n },\n },\n },\n\n hooks: {\n after: createAuthMiddleware(async (ctx) => {\n const { path, context } = ctx;\n\n const newUser = context.newSession?.user;\n const existingUser = context.session?.user;\n const user = newUser ?? existingUser;\n\n if (!user) return;\n\n if (path.includes('/verify-email')) {\n sendVerificationUpdate(user as unknown as User);\n logger.info('SSE verification update sent', {\n email: user.email,\n userId: user.id,\n });\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.APP_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n }\n }),\n },\n\n advanced: {\n cookiePrefix: 'intlayer', // => session_token (no prefix)\n\n // Override just the session‑token cookie\n cookies: {\n session_token: {\n // name: 'intlayer_session_token', // final name depends on the prefix above\n // attributes: { sameSite: \"lax\", maxAge: 60 * 60 * 24 } // optional\n },\n },\n },\n\n secret: process.env.BETTER_AUTH_SECRET as string,\n session: {\n modelName: 'sessions',\n id: 'id',\n\n additionalFields: {\n activeOrganizationId: { type: 'string', nullable: true, input: false },\n activeProjectId: { type: 'string', nullable: true, input: false },\n },\n },\n\n plugins: [\n customSession(async ({ session }) => {\n const typedSession = session as unknown as SessionDataApi;\n\n let userAPI: UserAPI | null = null;\n let organizationAPI: OrganizationAPI | null = null;\n let projectAPI: ProjectAPI | null = null;\n\n if (typedSession.userId) {\n const userData = await getUserById(typedSession.userId);\n\n if (userData) {\n userAPI = mapUserToAPI(userData);\n }\n }\n\n if (typedSession.activeOrganizationId) {\n const orgData = await getOrganizationById(\n typedSession.activeOrganizationId\n );\n\n if (orgData) {\n organizationAPI = mapOrganizationToAPI(orgData);\n }\n }\n if (typedSession.activeProjectId) {\n const projectData = await getProjectById(\n typedSession.activeProjectId\n );\n\n if (projectData) {\n projectAPI = mapProjectToAPI(projectData);\n }\n }\n\n const sessionWithNoPermission: SessionContext = {\n session: typedSession,\n user: userAPI!,\n organization: organizationAPI ?? null,\n project: projectAPI ?? null,\n authType: 'session',\n };\n\n const formattedSession = formatSession(sessionWithNoPermission);\n\n return mapSessionToAPI(formattedSession);\n }),\n lastLoginMethod({\n storeInDatabase: true, // adds user.lastLoginMethod in DB and session\n schema: {\n user: {\n lastLoginMethod: 'lastLoginMethod', // Custom field name\n },\n },\n customResolveMethod: (context) => {\n // When user clicks the magic link\n if (context.path === '/magic-link/verify') {\n return 'magic-link';\n }\n\n // Fallback to default behavior for everything else\n return null;\n },\n }),\n passkey({\n rpID: process.env.DOMAIN,\n rpName: 'Intlayer',\n }),\n twoFactor(),\n magicLink({\n sendMagicLink: async ({ email, url }) => {\n logger.info('sending magic link', { email, url });\n await sendEmail({\n type: 'magicLink',\n to: email,\n username: email.split('@')[0],\n magicLink: url,\n });\n },\n }),\n sso({\n organizationProvisioning: {},\n }),\n ],\n\n emailAndPassword: {\n enabled: true,\n disableSignUp: false,\n requireEmailVerification: true,\n minPasswordLength: 8,\n maxPasswordLength: 128,\n autoSignIn: true,\n sendResetPassword: async ({ user, token }) => {\n logger.info('sending reset password email', { email: user.email });\n await sendEmail({\n type: 'resetPassword',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n resetLink: `${process.env.APP_URL}/auth/password/reset?token=${token}`,\n });\n },\n resetPasswordTokenExpiresIn: 3600,\n },\n\n emailVerification: {\n autoSignInAfterVerification: true,\n sendOnSignIn: true,\n sendVerificationEmail: async ({ user, url }) => {\n logger.info('sending verification email', { email: user.email });\n await sendEmail({\n type: 'validate',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n validationLink: url,\n });\n },\n },\n\n crossSubDomainCookies: {\n enabled: true,\n additionalCookies: ['session_token'],\n domain: process.env.DOMAIN as string,\n },\n cookiePrefix: 'intlayer',\n cookies: {\n session_token: {\n name: 'session_token',\n attributes: {\n httpOnly: true,\n secure: true,\n },\n },\n },\n\n trustedOrigins: [\n process.env.WEBSITE_URL as string,\n process.env.APP_URL as string,\n ],\n\n accountLinking: {\n enabled: true, // allow linking in general\n trustedProviders: [\n 'google',\n 'github',\n 'linkedin',\n 'gitlab',\n 'atlassian',\n 'microsoft',\n 'email-password',\n 'magic-link',\n 'passkey',\n ],\n },\n socialProviders: {\n google: {\n clientId: process.env.GOOGLE_CLIENT_ID as string,\n clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,\n },\n github: {\n clientId: process.env.GITHUB_CLIENT_ID as string,\n clientSecret: process.env.GITHUB_CLIENT_SECRET as string,\n },\n atlassian: {\n clientId: process.env.ATLASSIAN_CLIENT_ID as string,\n clientSecret: process.env.ATLASSIAN_CLIENT_SECRET as string,\n },\n gitlab: {\n clientId: process.env.GITLAB_CLIENT_ID as string,\n clientSecret: process.env.GITLAB_CLIENT_SECRET as string,\n },\n linkedin: {\n clientId: process.env.LINKEDIN_CLIENT_ID as string,\n clientSecret: process.env.LINKEDIN_CLIENT_SECRET as string,\n },\n microsoft: {\n clientId: process.env.MICROSOFT_CLIENT_ID as string,\n clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string,\n },\n // socialProviders: {\n // apple: {\n // clientId: process.env.APPLE_CLIENT_ID as string,\n // clientSecret: process.env.APPLE_CLIENT_SECRET as string,\n // // Optional\n // appBundleIdentifier: process.env\n // .APPLE_APP_BUNDLE_IDENTIFIER as string,\n // },\n // },\n // // Add appleid.apple.com to trustedOrigins for Sign In with Apple flows\n // trustedOrigins: ['https://appleid.apple.com'],\n },\n\n logger: {\n log: (level, message, ...args) => logger[level](message, ...args),\n },\n });\n\n return auth;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,iBAAiB,YAA6C;CACzE,MAAM,QAAQ,gBAAgB,QAAQ;CACtC,IAAI,cAAc,2BAA2B,MAAM;AAGnD,KAAI,QAAQ,YACV,eAAc,qBAAqB,aAAa,QAAQ,YAAY;AAatE,QAVsB;EACpB,SAAS,QAAQ;EACjB,MAAM,QAAQ;EACd,cAAc,QAAQ;EACtB,SAAS,QAAQ;EACjB,UAAU;EACV;EACA;EACD;;AAKH,MAAa,WAAW,aAAgC;AACtD,KAAI,CAAC,SACH,OAAM,IAAI,MAAM,qCAAqC;AA+RvD,QA5Ra,WAAW;EACtB,SAAS;EAET,UAAU,eAAe,SAAS,IAAI,CAAC;EAKvC,MAAM,EACJ,WAAW,SACZ;EAED,eAAe,EACb,MAAM,EACJ,QAAQ,EAEN,OAAO,OAAO,SAAS;AACrB,OAAI,CAAC,MAAM,cAAe;AAE1B,SAAM,UAAU;IACd,MAAM;IACN,IAAI,KAAK;IACT,UAAU,KAAK,QAAQ,KAAK,MAAM,MAAM,IAAI,CAAC;IAC7C,WAAW,GAAG,QAAQ,IAAI,QAAQ;IAClC,QAAS,KAAa;IACvB,CAAC;AACF,UAAO,KAAK,4BAA4B,EACtC,OAAO,KAAK,OACb,CAAC;KAEL,EACF,EACF;EAED,OAAO,EACL,OAAO,qBAAqB,OAAO,QAAQ;GACzC,MAAM,EAAE,MAAM,YAAY;GAE1B,MAAM,UAAU,QAAQ,YAAY;GACpC,MAAM,eAAe,QAAQ,SAAS;GACtC,MAAM,OAAO,WAAW;AAExB,OAAI,CAAC,KAAM;AAEX,OAAI,KAAK,SAAS,gBAAgB,EAAE;AAClC,2BAAuB,KAAwB;AAC/C,WAAO,KAAK,gCAAgC;KAC1C,OAAO,KAAK;KACZ,QAAQ,KAAK;KACd,CAAC;AAEF,UAAM,UAAU;KACd,MAAM;KACN,IAAI,KAAK;KACT,UAAU,KAAK,QAAQ,KAAK,MAAM,MAAM,IAAI,CAAC;KAC7C,WAAW,GAAG,QAAQ,IAAI,QAAQ;KAClC,QAAS,KAAa;KACvB,CAAC;AACF,WAAO,KAAK,4BAA4B,EACtC,OAAO,KAAK,OACb,CAAC;;IAEJ,EACH;EAED,UAAU;GACR,cAAc;GAGd,SAAS,EACP,eAAe,EAGd,EACF;GACF;EAED,QAAQ,QAAQ,IAAI;EACpB,SAAS;GACP,WAAW;GACX,IAAI;GAEJ,kBAAkB;IAChB,sBAAsB;KAAE,MAAM;KAAU,UAAU;KAAM,OAAO;KAAO;IACtE,iBAAiB;KAAE,MAAM;KAAU,UAAU;KAAM,OAAO;KAAO;IAClE;GACF;EAED,SAAS;GACP,cAAc,OAAO,EAAE,cAAc;IACnC,MAAM,eAAe;IAErB,IAAIA,UAA0B;IAC9B,IAAIC,kBAA0C;IAC9C,IAAIC,aAAgC;AAEpC,QAAI,aAAa,QAAQ;KACvB,MAAM,WAAW,MAAM,YAAY,aAAa,OAAO;AAEvD,SAAI,SACF,WAAU,aAAa,SAAS;;AAIpC,QAAI,aAAa,sBAAsB;KACrC,MAAM,UAAU,MAAM,oBACpB,aAAa,qBACd;AAED,SAAI,QACF,mBAAkB,qBAAqB,QAAQ;;AAGnD,QAAI,aAAa,iBAAiB;KAChC,MAAM,cAAc,MAAM,eACxB,aAAa,gBACd;AAED,SAAI,YACF,cAAa,gBAAgB,YAAY;;AAc7C,WAAO,gBAFkB,cARuB;KAC9C,SAAS;KACT,MAAM;KACN,cAAc,mBAAmB;KACjC,SAAS,cAAc;KACvB,UAAU;KACX,CAE8D,CAEvB;KACxC;GACF,gBAAgB;IACd,iBAAiB;IACjB,QAAQ,EACN,MAAM,EACJ,iBAAiB,mBAClB,EACF;IACD,sBAAsB,YAAY;AAEhC,SAAI,QAAQ,SAAS,qBACnB,QAAO;AAIT,YAAO;;IAEV,CAAC;GACF,QAAQ;IACN,MAAM,QAAQ,IAAI;IAClB,QAAQ;IACT,CAAC;GACF,WAAW;GACX,UAAU,EACR,eAAe,OAAO,EAAE,OAAO,UAAU;AACvC,WAAO,KAAK,sBAAsB;KAAE;KAAO;KAAK,CAAC;AACjD,UAAM,UAAU;KACd,MAAM;KACN,IAAI;KACJ,UAAU,MAAM,MAAM,IAAI,CAAC;KAC3B,WAAW;KACZ,CAAC;MAEL,CAAC;GACF,IAAI,EACF,0BAA0B,EAAE,EAC7B,CAAC;GACH;EAED,kBAAkB;GAChB,SAAS;GACT,eAAe;GACf,0BAA0B;GAC1B,mBAAmB;GACnB,mBAAmB;GACnB,YAAY;GACZ,mBAAmB,OAAO,EAAE,MAAM,YAAY;AAC5C,WAAO,KAAK,gCAAgC,EAAE,OAAO,KAAK,OAAO,CAAC;AAClE,UAAM,UAAU;KACd,MAAM;KACN,IAAI,KAAK;KACT,UAAU,KAAK,QAAQ,KAAK,MAAM,MAAM,IAAI,CAAC;KAC7C,WAAW,GAAG,QAAQ,IAAI,QAAQ,6BAA6B;KAChE,CAAC;;GAEJ,6BAA6B;GAC9B;EAED,mBAAmB;GACjB,6BAA6B;GAC7B,cAAc;GACd,uBAAuB,OAAO,EAAE,MAAM,UAAU;AAC9C,WAAO,KAAK,8BAA8B,EAAE,OAAO,KAAK,OAAO,CAAC;AAChE,UAAM,UAAU;KACd,MAAM;KACN,IAAI,KAAK;KACT,UAAU,KAAK,QAAQ,KAAK,MAAM,MAAM,IAAI,CAAC;KAC7C,gBAAgB;KACjB,CAAC;;GAEL;EAED,uBAAuB;GACrB,SAAS;GACT,mBAAmB,CAAC,gBAAgB;GACpC,QAAQ,QAAQ,IAAI;GACrB;EACD,cAAc;EACd,SAAS,EACP,eAAe;GACb,MAAM;GACN,YAAY;IACV,UAAU;IACV,QAAQ;IACT;GACF,EACF;EAED,gBAAgB,CACd,QAAQ,IAAI,aACZ,QAAQ,IAAI,QACb;EAED,gBAAgB;GACd,SAAS;GACT,kBAAkB;IAChB;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD;GACF;EACD,iBAAiB;GACf,QAAQ;IACN,UAAU,QAAQ,IAAI;IACtB,cAAc,QAAQ,IAAI;IAC3B;GACD,QAAQ;IACN,UAAU,QAAQ,IAAI;IACtB,cAAc,QAAQ,IAAI;IAC3B;GACD,WAAW;IACT,UAAU,QAAQ,IAAI;IACtB,cAAc,QAAQ,IAAI;IAC3B;GACD,QAAQ;IACN,UAAU,QAAQ,IAAI;IACtB,cAAc,QAAQ,IAAI;IAC3B;GACD,UAAU;IACR,UAAU,QAAQ,IAAI;IACtB,cAAc,QAAQ,IAAI;IAC3B;GACD,WAAW;IACT,UAAU,QAAQ,IAAI;IACtB,cAAc,QAAQ,IAAI;IAC3B;GAYF;EAED,QAAQ,EACN,MAAM,OAAO,SAAS,GAAG,SAAS,OAAO,OAAO,SAAS,GAAG,KAAK,EAClE;EACF,CAAC"}
|
package/dist/esm/utils/cors.mjs
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { logger } from "../logger/index.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/utils/cors.ts
|
|
4
|
-
const whitelist = [process.env.
|
|
4
|
+
const whitelist = [process.env.APP_URL];
|
|
5
5
|
const corsOptions = {
|
|
6
6
|
origin: (origin, callback) => {
|
|
7
|
-
if (!origin)
|
|
7
|
+
if (!origin) {
|
|
8
|
+
callback(null, true);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
8
11
|
if (whitelist.includes(origin)) {
|
|
9
12
|
logger.info("whitelisted origin", origin);
|
|
10
|
-
|
|
13
|
+
callback(null, true);
|
|
14
|
+
return;
|
|
11
15
|
}
|
|
12
16
|
logger.info("non whitelisted origin", origin);
|
|
13
17
|
callback(null, origin);
|
|
@@ -23,8 +27,14 @@ const corsOptions = {
|
|
|
23
27
|
"browsing-topics"
|
|
24
28
|
],
|
|
25
29
|
exposedHeaders: [""],
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
methods: [
|
|
31
|
+
"GET",
|
|
32
|
+
"HEAD",
|
|
33
|
+
"PUT",
|
|
34
|
+
"PATCH",
|
|
35
|
+
"POST",
|
|
36
|
+
"DELETE"
|
|
37
|
+
],
|
|
28
38
|
credentials: true
|
|
29
39
|
};
|
|
30
40
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors.mjs","names":["corsOptions:
|
|
1
|
+
{"version":3,"file":"cors.mjs","names":["corsOptions: FastifyCorsOptions"],"sources":["../../../src/utils/cors.ts"],"sourcesContent":["import type { FastifyCorsOptions } from '@fastify/cors';\nimport { logger } from '@logger';\n\nconst whitelist = [process.env.APP_URL!];\n\nexport const corsOptions: FastifyCorsOptions = {\n origin: (origin, callback) => {\n // Allow requests with no origin (like mobile apps or curl requests)\n if (!origin) {\n callback(null, true);\n return;\n }\n\n if (whitelist.includes(origin)) {\n logger.info('whitelisted origin', origin);\n callback(null, true);\n return;\n }\n\n logger.info('non whitelisted origin', origin);\n // Reflect the request's origin (echo back the origin header)\n callback(null, origin);\n },\n allowedHeaders: [\n 'authorization',\n 'Content-Type',\n 'credentials',\n 'cache-control',\n 'Access-Control-Allow-Origin',\n 'private-state-token-redemption',\n 'private-state-token-issuance',\n 'browsing-topics',\n ],\n exposedHeaders: [''],\n methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],\n credentials: true,\n};\n"],"mappings":";;;AAGA,MAAM,YAAY,CAAC,QAAQ,IAAI,QAAS;AAExC,MAAaA,cAAkC;CAC7C,SAAS,QAAQ,aAAa;AAE5B,MAAI,CAAC,QAAQ;AACX,YAAS,MAAM,KAAK;AACpB;;AAGF,MAAI,UAAU,SAAS,OAAO,EAAE;AAC9B,UAAO,KAAK,sBAAsB,OAAO;AACzC,YAAS,MAAM,KAAK;AACpB;;AAGF,SAAO,KAAK,0BAA0B,OAAO;AAE7C,WAAS,MAAM,OAAO;;CAExB,gBAAgB;EACd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,gBAAgB,CAAC,GAAG;CACpB,SAAS;EAAC;EAAO;EAAQ;EAAO;EAAS;EAAQ;EAAS;CAC1D,aAAa;CACd"}
|
|
@@ -3,7 +3,7 @@ import { HttpStatusCodes } from "../httpStatusCodes.mjs";
|
|
|
3
3
|
import { formatPaginatedResponse, formatResponse } from "../responseData.mjs";
|
|
4
4
|
import { errorData } from "./errorCodes.mjs";
|
|
5
5
|
import { Locales } from "@intlayer/types";
|
|
6
|
-
import { t } from "
|
|
6
|
+
import { t } from "fastify-intlayer";
|
|
7
7
|
|
|
8
8
|
//#region src/utils/errors/ErrorHandler.ts
|
|
9
9
|
var ErrorHandler = class ErrorHandler {
|
|
@@ -27,7 +27,10 @@ var ErrorHandler = class ErrorHandler {
|
|
|
27
27
|
* @param isPaginatedResponse - (Optional) Flag to determine if the response should be paginated.
|
|
28
28
|
*/
|
|
29
29
|
static handleAppErrorResponse(res, error, messageDetails, isPaginatedResponse = false) {
|
|
30
|
-
if (!error.isAppError)
|
|
30
|
+
if (!error.isAppError) {
|
|
31
|
+
ErrorHandler.handleCustomErrorResponse(res, error.errorKey ?? "UNKNOWN_ERROR", "Error", error.message ?? JSON.stringify(error), void 0, error.httpStatusCode ?? HttpStatusCodes.INTERNAL_SERVER_ERROR_500, isPaginatedResponse);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
31
34
|
const isMultilingual = error.isMultilingual ?? false;
|
|
32
35
|
ErrorHandler.handleCustomErrorResponse(res, error.errorKey, isMultilingual ? error.multilingualTitle : error.title, isMultilingual ? error.multilingualMessage : error.message, error.messageDetails ?? messageDetails, error.httpStatusCode, isPaginatedResponse);
|
|
33
36
|
}
|
|
@@ -54,7 +57,8 @@ var ErrorHandler = class ErrorHandler {
|
|
|
54
57
|
},
|
|
55
58
|
status
|
|
56
59
|
});
|
|
57
|
-
res.status(status).json(responseData$1);
|
|
60
|
+
if ("status" in res && "json" in res) res.status(status).json(responseData$1);
|
|
61
|
+
else res.code(status).send(responseData$1);
|
|
58
62
|
return;
|
|
59
63
|
}
|
|
60
64
|
const responseData = formatResponse({
|
|
@@ -66,7 +70,31 @@ var ErrorHandler = class ErrorHandler {
|
|
|
66
70
|
},
|
|
67
71
|
status
|
|
68
72
|
});
|
|
69
|
-
res.status(status).json(responseData);
|
|
73
|
+
if ("status" in res && "json" in res) res.status(status).json(responseData);
|
|
74
|
+
else res.code(status).send(responseData);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Formats a generic error response without sending it (useful for Fastify plugins).
|
|
78
|
+
* @param errorKey - A key representing the specific error.
|
|
79
|
+
* @param errorDetails - Optional error details to include.
|
|
80
|
+
* @param statusCode - Optional HTTP status code.
|
|
81
|
+
* @returns Formatted error response object.
|
|
82
|
+
*/
|
|
83
|
+
static formatGenericErrorResponse(errorKey, errorDetails, statusCode) {
|
|
84
|
+
const error = errorData[errorKey];
|
|
85
|
+
const status = statusCode ?? error.statusCode;
|
|
86
|
+
const errorTitle = t(error.title, Locales.ENGLISH);
|
|
87
|
+
const errorMessage = t(error.message, Locales.ENGLISH);
|
|
88
|
+
logger.error(errorMessage, errorDetails);
|
|
89
|
+
return formatResponse({
|
|
90
|
+
error: {
|
|
91
|
+
code: errorKey,
|
|
92
|
+
title: errorTitle,
|
|
93
|
+
message: errorMessage,
|
|
94
|
+
...errorDetails
|
|
95
|
+
},
|
|
96
|
+
status
|
|
97
|
+
});
|
|
70
98
|
}
|
|
71
99
|
};
|
|
72
100
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorHandler.mjs","names":["responseData"],"sources":["../../../../src/utils/errors/ErrorHandler.ts"],"sourcesContent":["// Import required modules and types from their respective locations.\n\nimport { Locales, type StrictModeLocaleMap } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { formatPaginatedResponse, formatResponse } from '@utils/responseData';\nimport type { Response } from 'express';\nimport { t } from '
|
|
1
|
+
{"version":3,"file":"ErrorHandler.mjs","names":["responseData"],"sources":["../../../../src/utils/errors/ErrorHandler.ts"],"sourcesContent":["// Import required modules and types from their respective locations.\n\nimport { Locales, type StrictModeLocaleMap } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { formatPaginatedResponse, formatResponse } from '@utils/responseData';\nimport type { Response } from 'express';\nimport type { FastifyReply } from 'fastify';\nimport { t } from 'fastify-intlayer';\nimport type { UserAPI } from '@/types/user.types';\nimport { HttpStatusCodes } from '@/utils/httpStatusCodes';\nimport type { AppError } from './ErrorsClass';\nimport { type ErrorCodes, errorData } from './errorCodes';\n\ntype ResponseLike = Response | FastifyReply;\n\n// Define a class named 'ErrorHandler' to encapsulate error handling logic.\nexport class ErrorHandler {\n /**\n * Handles generic error responses by formatting and sending a JSON response.\n * @param res - The response object provided by Express.js.\n * @param errorKey - A key representing the specific error.\n * @param statusCode - (Optional) A specific HTTP status code to use for the response.\n * @param isPaginatedResponse - Flag to determine if the response should be paginated.\n */\n static handleGenericErrorResponse(\n res: ResponseLike,\n errorKey: ErrorCodes,\n errorDetails?: object,\n statusCode?: HttpStatusCodes,\n isPaginatedResponse: boolean = false\n ) {\n const error = errorData[errorKey];\n const status = statusCode ?? error.statusCode; // Use the provided status code or default to the one in errorData.\n\n // Delegate to a more customizable error response handler.\n ErrorHandler.handleCustomErrorResponse(\n res,\n errorKey,\n error.title,\n error.message,\n errorDetails,\n status,\n isPaginatedResponse\n );\n }\n\n /**\n * Handles application-specific error responses by formatting and sending a JSON response.\n * @param res - The response object provided by Express.js.\n * @param error - The error object.\n * @param messageDetails - (Optional) Additional message details to include in the response.\n * @param isPaginatedResponse - (Optional) Flag to determine if the response should be paginated.\n */\n static handleAppErrorResponse(\n res: ResponseLike,\n error: AppError,\n messageDetails?: object,\n isPaginatedResponse: boolean = false\n ) {\n if (!error.isAppError) {\n ErrorHandler.handleCustomErrorResponse(\n res,\n error.errorKey ?? 'UNKNOWN_ERROR',\n 'Error',\n error.message ?? JSON.stringify(error),\n undefined,\n error.httpStatusCode ?? HttpStatusCodes.INTERNAL_SERVER_ERROR_500,\n isPaginatedResponse\n );\n return;\n }\n\n const isMultilingual = error.isMultilingual ?? false;\n // Delegate to a more customizable error response handler.\n ErrorHandler.handleCustomErrorResponse(\n res,\n error.errorKey,\n isMultilingual ? error.multilingualTitle : error.title,\n isMultilingual ? error.multilingualMessage : error.message,\n error.messageDetails ?? messageDetails,\n error.httpStatusCode,\n isPaginatedResponse\n );\n }\n\n /**\n * Handles more customizable error responses with detailed error messages and codes.\n * @param res - The response object.\n * @param errorKey - Error code key used to fetch the corresponding message and default status.\n * @param message - The localized error message object.\n * @param messageDetails - (Optional) Additional message details to include in the response.\n * @param statusCode - (Optional) HTTP status code, defaults to 500 if not specified.\n * @param isPaginatedResponse - Determines if the error should be part of a paginated response.\n */\n static handleCustomErrorResponse<T>(\n res: ResponseLike,\n errorKey: ErrorCodes | string,\n title: StrictModeLocaleMap<string> | string,\n message: StrictModeLocaleMap<string> | string,\n messageDetails?: object,\n statusCode?: HttpStatusCodes,\n isPaginatedResponse: boolean = false\n ) {\n const errorTitle = t(title as StrictModeLocaleMap<string>, Locales.ENGLISH);\n const errorMessage = t(\n message as StrictModeLocaleMap<string>,\n Locales.ENGLISH\n );\n logger.error(errorMessage, messageDetails); // Log the English version of the error message.\n const status = statusCode ?? HttpStatusCodes.INTERNAL_SERVER_ERROR_500; // Default to 500 if no status code is provided.\n\n if (isPaginatedResponse) {\n // Format the response as a paginated error response if requested.\n const responseData = formatPaginatedResponse<T>({\n error: {\n code: errorKey,\n title: errorTitle,\n message: errorMessage,\n },\n status,\n });\n // Support both Express and Fastify\n if ('status' in res && 'json' in res) {\n (res as Response).status(status).json(responseData);\n } else {\n (res as FastifyReply).code(status).send(responseData);\n }\n return;\n }\n\n // Format the response as a standard non-paginated error response.\n const responseData = formatResponse<UserAPI>({\n error: {\n code: errorKey,\n title: errorTitle,\n message: errorMessage,\n ...messageDetails,\n },\n status,\n });\n\n // Support both Express and Fastify\n if ('status' in res && 'json' in res) {\n (res as Response).status(status).json(responseData);\n } else {\n (res as FastifyReply).code(status).send(responseData);\n }\n }\n\n /**\n * Formats a generic error response without sending it (useful for Fastify plugins).\n * @param errorKey - A key representing the specific error.\n * @param errorDetails - Optional error details to include.\n * @param statusCode - Optional HTTP status code.\n * @returns Formatted error response object.\n */\n static formatGenericErrorResponse(\n errorKey: ErrorCodes,\n errorDetails?: object,\n statusCode?: HttpStatusCodes\n ) {\n const error = errorData[errorKey];\n const status = statusCode ?? error.statusCode;\n const errorTitle = t(error.title, Locales.ENGLISH);\n const errorMessage = t(error.message, Locales.ENGLISH);\n logger.error(errorMessage, errorDetails);\n\n return formatResponse<UserAPI>({\n error: {\n code: errorKey,\n title: errorTitle,\n message: errorMessage,\n ...errorDetails,\n },\n status,\n });\n }\n}\n"],"mappings":";;;;;;;;AAgBA,IAAa,eAAb,MAAa,aAAa;;;;;;;;CAQxB,OAAO,2BACL,KACA,UACA,cACA,YACA,sBAA+B,OAC/B;EACA,MAAM,QAAQ,UAAU;EACxB,MAAM,SAAS,cAAc,MAAM;AAGnC,eAAa,0BACX,KACA,UACA,MAAM,OACN,MAAM,SACN,cACA,QACA,oBACD;;;;;;;;;CAUH,OAAO,uBACL,KACA,OACA,gBACA,sBAA+B,OAC/B;AACA,MAAI,CAAC,MAAM,YAAY;AACrB,gBAAa,0BACX,KACA,MAAM,YAAY,iBAClB,SACA,MAAM,WAAW,KAAK,UAAU,MAAM,EACtC,QACA,MAAM,kBAAkB,gBAAgB,2BACxC,oBACD;AACD;;EAGF,MAAM,iBAAiB,MAAM,kBAAkB;AAE/C,eAAa,0BACX,KACA,MAAM,UACN,iBAAiB,MAAM,oBAAoB,MAAM,OACjD,iBAAiB,MAAM,sBAAsB,MAAM,SACnD,MAAM,kBAAkB,gBACxB,MAAM,gBACN,oBACD;;;;;;;;;;;CAYH,OAAO,0BACL,KACA,UACA,OACA,SACA,gBACA,YACA,sBAA+B,OAC/B;EACA,MAAM,aAAa,EAAE,OAAsC,QAAQ,QAAQ;EAC3E,MAAM,eAAe,EACnB,SACA,QAAQ,QACT;AACD,SAAO,MAAM,cAAc,eAAe;EAC1C,MAAM,SAAS,cAAc,gBAAgB;AAE7C,MAAI,qBAAqB;GAEvB,MAAMA,iBAAe,wBAA2B;IAC9C,OAAO;KACL,MAAM;KACN,OAAO;KACP,SAAS;KACV;IACD;IACD,CAAC;AAEF,OAAI,YAAY,OAAO,UAAU,IAC/B,CAAC,IAAiB,OAAO,OAAO,CAAC,KAAKA,eAAa;OAEnD,CAAC,IAAqB,KAAK,OAAO,CAAC,KAAKA,eAAa;AAEvD;;EAIF,MAAM,eAAe,eAAwB;GAC3C,OAAO;IACL,MAAM;IACN,OAAO;IACP,SAAS;IACT,GAAG;IACJ;GACD;GACD,CAAC;AAGF,MAAI,YAAY,OAAO,UAAU,IAC/B,CAAC,IAAiB,OAAO,OAAO,CAAC,KAAK,aAAa;MAEnD,CAAC,IAAqB,KAAK,OAAO,CAAC,KAAK,aAAa;;;;;;;;;CAWzD,OAAO,2BACL,UACA,cACA,YACA;EACA,MAAM,QAAQ,UAAU;EACxB,MAAM,SAAS,cAAc,MAAM;EACnC,MAAM,aAAa,EAAE,MAAM,OAAO,QAAQ,QAAQ;EAClD,MAAM,eAAe,EAAE,MAAM,SAAS,QAAQ,QAAQ;AACtD,SAAO,MAAM,cAAc,aAAa;AAExC,SAAO,eAAwB;GAC7B,OAAO;IACL,MAAM;IACN,OAAO;IACP,SAAS;IACT,GAAG;IACJ;GACD;GACD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorsClass.mjs","names":[],"sources":["../../../../src/utils/errors/ErrorsClass.ts"],"sourcesContent":["import { HttpStatusCodes } from '@utils/httpStatusCodes';\n// @ts-ignore
|
|
1
|
+
{"version":3,"file":"ErrorsClass.mjs","names":[],"sources":["../../../../src/utils/errors/ErrorsClass.ts"],"sourcesContent":["import { HttpStatusCodes } from '@utils/httpStatusCodes';\n// @ts-ignore fastify-intlayer not build yet\nimport { type StrictModeLocaleMap, t } from 'fastify-intlayer';\nimport { type ErrorCodes, errorData } from './errorCodes';\n\n/**\n * Custom error class that extends the native JavaScript Error class.\n * This class supports multilingual error messages and HTTP status codes.\n */\nexport class AppError extends Error {\n public isAppError: boolean = true; // Flag to identify AppError instances.\n public name: string;\n public isMultilingual: boolean = true;\n public errorKey: string;\n public title: string;\n public multilingualTitle: StrictModeLocaleMap<string>;\n public message: string;\n public multilingualMessage: StrictModeLocaleMap<string>;\n public httpStatusCode: HttpStatusCodes;\n public messageDetails?: object;\n\n /**\n * Constructor for the custom error class.\n * @param multilingualMessage - The error message which can be a simple string or a multilingual object.\n * @param httpStatusCode - Optional HTTP status code, defaults to 500 Internal Server Error.\n */\n constructor(\n multilingualTitle: StrictModeLocaleMap<string>,\n multilingualMessage: StrictModeLocaleMap<string>,\n errorKey: string,\n httpStatusCode: HttpStatusCodes = HttpStatusCodes.INTERNAL_SERVER_ERROR_500,\n messageDetails?: object\n ) {\n const title = t(multilingualTitle); // Translate title based on current locale\n const message = t(multilingualMessage); // Translate message based on current locale.\n\n super(message); // Use translated message for the superclass constructor.\n this.title = title;\n this.multilingualTitle = multilingualTitle;\n this.message = message;\n this.multilingualMessage = multilingualMessage; // Store original message format for potential use.\n this.name = 'AppError';\n this.errorKey = errorKey;\n this.httpStatusCode = httpStatusCode; // Set the HTTP status code.\n this.messageDetails = messageDetails; // Store any additional message details.\n\n // Capture the stack trace to exclude the constructor call.\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\nexport class GenericError extends AppError {\n constructor(errorKey: ErrorCodes, messageDetails?: object) {\n const multilingualTitle = errorData[errorKey].title;\n const multilingualMessage = errorData[errorKey].message;\n const httpStatusCode = errorData[errorKey].statusCode;\n\n super(\n multilingualTitle,\n multilingualMessage,\n errorKey,\n httpStatusCode,\n messageDetails\n );\n }\n}\n"],"mappings":";;;;;;;;;AASA,IAAa,WAAb,cAA8B,MAAM;CAClC,AAAO,aAAsB;CAC7B,AAAO;CACP,AAAO,iBAA0B;CACjC,AAAO;CACP,AAAO;CACP,AAAO;CACP,AAAO;CACP,AAAO;CACP,AAAO;CACP,AAAO;;;;;;CAOP,YACE,mBACA,qBACA,UACA,iBAAkC,gBAAgB,2BAClD,gBACA;EACA,MAAM,QAAQ,EAAE,kBAAkB;EAClC,MAAM,UAAU,EAAE,oBAAoB;AAEtC,QAAM,QAAQ;AACd,OAAK,QAAQ;AACb,OAAK,oBAAoB;AACzB,OAAK,UAAU;AACf,OAAK,sBAAsB;AAC3B,OAAK,OAAO;AACZ,OAAK,WAAW;AAChB,OAAK,iBAAiB;AACtB,OAAK,iBAAiB;AAGtB,QAAM,kBAAkB,MAAM,KAAK,YAAY;;;AAInD,IAAa,eAAb,cAAkC,SAAS;CACzC,YAAY,UAAsB,gBAAyB;EACzD,MAAM,oBAAoB,UAAU,UAAU;EAC9C,MAAM,sBAAsB,UAAU,UAAU;EAChD,MAAM,iBAAiB,UAAU,UAAU;AAE3C,QACE,mBACA,qBACA,UACA,gBACA,eACD"}
|