@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.
Files changed (247) hide show
  1. package/README.md +9 -2
  2. package/dist/assets/utils/AI/askDocQuestion/PROMPT.md +1 -1
  3. package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/cli/ci.json +3080 -0
  4. package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/cli/list_projects.json +1 -0
  5. package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_fastify.json +9 -0
  6. package/dist/esm/controllers/ai.controller.mjs +95 -128
  7. package/dist/esm/controllers/ai.controller.mjs.map +1 -1
  8. package/dist/esm/controllers/bitbucket.controller.mjs +77 -0
  9. package/dist/esm/controllers/bitbucket.controller.mjs.map +1 -0
  10. package/dist/esm/controllers/dictionary.controller.mjs +106 -198
  11. package/dist/esm/controllers/dictionary.controller.mjs.map +1 -1
  12. package/dist/esm/controllers/eventListener.controller.mjs +13 -19
  13. package/dist/esm/controllers/eventListener.controller.mjs.map +1 -1
  14. package/dist/esm/controllers/github.controller.mjs +77 -0
  15. package/dist/esm/controllers/github.controller.mjs.map +1 -0
  16. package/dist/esm/controllers/gitlab.controller.mjs +77 -0
  17. package/dist/esm/controllers/gitlab.controller.mjs.map +1 -0
  18. package/dist/esm/controllers/newsletter.controller.mjs +30 -60
  19. package/dist/esm/controllers/newsletter.controller.mjs.map +1 -1
  20. package/dist/esm/controllers/oAuth2.controller.mjs +11 -8
  21. package/dist/esm/controllers/oAuth2.controller.mjs.map +1 -1
  22. package/dist/esm/controllers/organization.controller.mjs +100 -225
  23. package/dist/esm/controllers/organization.controller.mjs.map +1 -1
  24. package/dist/esm/controllers/project.controller.mjs +194 -204
  25. package/dist/esm/controllers/project.controller.mjs.map +1 -1
  26. package/dist/esm/controllers/projectAccessKey.controller.mjs +38 -71
  27. package/dist/esm/controllers/projectAccessKey.controller.mjs.map +1 -1
  28. package/dist/esm/controllers/search.controller.mjs +3 -3
  29. package/dist/esm/controllers/search.controller.mjs.map +1 -1
  30. package/dist/esm/controllers/stripe.controller.mjs +34 -67
  31. package/dist/esm/controllers/stripe.controller.mjs.map +1 -1
  32. package/dist/esm/controllers/tag.controller.mjs +51 -113
  33. package/dist/esm/controllers/tag.controller.mjs.map +1 -1
  34. package/dist/esm/controllers/user.controller.mjs +64 -113
  35. package/dist/esm/controllers/user.controller.mjs.map +1 -1
  36. package/dist/esm/export.mjs +4 -1
  37. package/dist/esm/index.mjs +105 -41
  38. package/dist/esm/index.mjs.map +1 -1
  39. package/dist/esm/middlewares/oAuth2.middleware.mjs +19 -14
  40. package/dist/esm/middlewares/oAuth2.middleware.mjs.map +1 -1
  41. package/dist/esm/middlewares/sessionAuth.middleware.mjs +6 -7
  42. package/dist/esm/middlewares/sessionAuth.middleware.mjs.map +1 -1
  43. package/dist/esm/routes/ai.routes.mjs +19 -15
  44. package/dist/esm/routes/ai.routes.mjs.map +1 -1
  45. package/dist/esm/routes/bitbucket.routes.mjs +43 -0
  46. package/dist/esm/routes/bitbucket.routes.mjs.map +1 -0
  47. package/dist/esm/routes/dictionary.routes.mjs +10 -10
  48. package/dist/esm/routes/dictionary.routes.mjs.map +1 -1
  49. package/dist/esm/routes/eventListener.routes.mjs +3 -3
  50. package/dist/esm/routes/eventListener.routes.mjs.map +1 -1
  51. package/dist/esm/routes/github.routes.mjs +43 -0
  52. package/dist/esm/routes/github.routes.mjs.map +1 -0
  53. package/dist/esm/routes/gitlab.routes.mjs +43 -0
  54. package/dist/esm/routes/gitlab.routes.mjs.map +1 -0
  55. package/dist/esm/routes/newsletter.routes.mjs +5 -5
  56. package/dist/esm/routes/newsletter.routes.mjs.map +1 -1
  57. package/dist/esm/routes/organization.routes.mjs +11 -11
  58. package/dist/esm/routes/organization.routes.mjs.map +1 -1
  59. package/dist/esm/routes/project.routes.mjs +38 -14
  60. package/dist/esm/routes/project.routes.mjs.map +1 -1
  61. package/dist/esm/routes/search.routes.mjs +3 -3
  62. package/dist/esm/routes/search.routes.mjs.map +1 -1
  63. package/dist/esm/routes/stripe.routes.mjs +5 -5
  64. package/dist/esm/routes/stripe.routes.mjs.map +1 -1
  65. package/dist/esm/routes/tags.routes.mjs +6 -6
  66. package/dist/esm/routes/tags.routes.mjs.map +1 -1
  67. package/dist/esm/routes/user.routes.mjs +9 -9
  68. package/dist/esm/routes/user.routes.mjs.map +1 -1
  69. package/dist/esm/schemas/project.schema.mjs +70 -1
  70. package/dist/esm/schemas/project.schema.mjs.map +1 -1
  71. package/dist/esm/services/bitbucket.service.mjs +173 -0
  72. package/dist/esm/services/bitbucket.service.mjs.map +1 -0
  73. package/dist/esm/services/ci.service.mjs +134 -0
  74. package/dist/esm/services/ci.service.mjs.map +1 -0
  75. package/dist/esm/services/email.service.mjs +1 -1
  76. package/dist/esm/services/email.service.mjs.map +1 -1
  77. package/dist/esm/services/github.service.mjs +218 -0
  78. package/dist/esm/services/github.service.mjs.map +1 -0
  79. package/dist/esm/services/gitlab.service.mjs +217 -0
  80. package/dist/esm/services/gitlab.service.mjs.map +1 -0
  81. package/dist/esm/services/oAuth2.service.mjs +1 -1
  82. package/dist/esm/services/subscription.service.mjs +1 -1
  83. package/dist/esm/services/subscription.service.mjs.map +1 -1
  84. package/dist/esm/services/webhook.service.mjs +164 -0
  85. package/dist/esm/services/webhook.service.mjs.map +1 -0
  86. package/dist/esm/utils/auth/getAuth.mjs +28 -16
  87. package/dist/esm/utils/auth/getAuth.mjs.map +1 -1
  88. package/dist/esm/utils/cors.mjs +15 -5
  89. package/dist/esm/utils/cors.mjs.map +1 -1
  90. package/dist/esm/utils/errors/ErrorHandler.mjs +32 -4
  91. package/dist/esm/utils/errors/ErrorHandler.mjs.map +1 -1
  92. package/dist/esm/utils/errors/ErrorsClass.mjs +1 -1
  93. package/dist/esm/utils/errors/ErrorsClass.mjs.map +1 -1
  94. package/dist/esm/utils/errors/errorCodes.mjs +234 -0
  95. package/dist/esm/utils/errors/errorCodes.mjs.map +1 -1
  96. package/dist/esm/utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs +3 -2
  97. package/dist/esm/utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs.map +1 -1
  98. package/dist/esm/utils/filtersAndPagination/getDiscussionFiltersAndPagination.mjs +1 -1
  99. package/dist/esm/utils/filtersAndPagination/getDiscussionFiltersAndPagination.mjs.map +1 -1
  100. package/dist/esm/utils/filtersAndPagination/getFiltersAndPaginationFromBody.mjs +1 -1
  101. package/dist/esm/utils/filtersAndPagination/getFiltersAndPaginationFromBody.mjs.map +1 -1
  102. package/dist/esm/utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs +3 -2
  103. package/dist/esm/utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs.map +1 -1
  104. package/dist/esm/utils/filtersAndPagination/getProjectFiltersAndPagination.mjs +3 -2
  105. package/dist/esm/utils/filtersAndPagination/getProjectFiltersAndPagination.mjs.map +1 -1
  106. package/dist/esm/utils/filtersAndPagination/getTagFiltersAndPagination.mjs +3 -2
  107. package/dist/esm/utils/filtersAndPagination/getTagFiltersAndPagination.mjs.map +1 -1
  108. package/dist/esm/utils/filtersAndPagination/getUserFiltersAndPagination.mjs +3 -2
  109. package/dist/esm/utils/filtersAndPagination/getUserFiltersAndPagination.mjs.map +1 -1
  110. package/dist/esm/utils/mapper/project.mjs +28 -1
  111. package/dist/esm/utils/mapper/project.mjs.map +1 -1
  112. package/dist/esm/utils/mongoDB/connectDB.mjs +1 -1
  113. package/dist/esm/utils/rateLimiter.mjs +40 -30
  114. package/dist/esm/utils/rateLimiter.mjs.map +1 -1
  115. package/dist/esm/webhooks/stripe.webhook.mjs +2 -2
  116. package/dist/esm/webhooks/stripe.webhook.mjs.map +1 -1
  117. package/dist/types/controllers/ai.controller.d.ts +29 -12
  118. package/dist/types/controllers/ai.controller.d.ts.map +1 -1
  119. package/dist/types/controllers/bitbucket.controller.d.ts +62 -0
  120. package/dist/types/controllers/bitbucket.controller.d.ts.map +1 -0
  121. package/dist/types/controllers/dictionary.controller.d.ts +23 -13
  122. package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
  123. package/dist/types/controllers/eventListener.controller.d.ts +4 -2
  124. package/dist/types/controllers/eventListener.controller.d.ts.map +1 -1
  125. package/dist/types/controllers/github.controller.d.ts +63 -0
  126. package/dist/types/controllers/github.controller.d.ts.map +1 -0
  127. package/dist/types/controllers/gitlab.controller.d.ts +67 -0
  128. package/dist/types/controllers/gitlab.controller.d.ts.map +1 -0
  129. package/dist/types/controllers/newsletter.controller.d.ts +8 -7
  130. package/dist/types/controllers/newsletter.controller.d.ts.map +1 -1
  131. package/dist/types/controllers/oAuth2.controller.d.ts +4 -2
  132. package/dist/types/controllers/oAuth2.controller.d.ts.map +1 -1
  133. package/dist/types/controllers/organization.controller.d.ts +28 -12
  134. package/dist/types/controllers/organization.controller.d.ts.map +1 -1
  135. package/dist/types/controllers/project.controller.d.ts +60 -17
  136. package/dist/types/controllers/project.controller.d.ts.map +1 -1
  137. package/dist/types/controllers/projectAccessKey.controller.d.ts +10 -5
  138. package/dist/types/controllers/projectAccessKey.controller.d.ts.map +1 -1
  139. package/dist/types/controllers/search.controller.d.ts +4 -2
  140. package/dist/types/controllers/search.controller.d.ts.map +1 -1
  141. package/dist/types/controllers/stripe.controller.d.ts +11 -12
  142. package/dist/types/controllers/stripe.controller.d.ts.map +1 -1
  143. package/dist/types/controllers/tag.controller.d.ts +14 -9
  144. package/dist/types/controllers/tag.controller.d.ts.map +1 -1
  145. package/dist/types/controllers/user.controller.d.ts +22 -9
  146. package/dist/types/controllers/user.controller.d.ts.map +1 -1
  147. package/dist/types/emails/InviteUserEmail.d.ts +4 -4
  148. package/dist/types/emails/MagicLinkEmail.d.ts +4 -4
  149. package/dist/types/emails/OAuthTokenCreatedEmail.d.ts +4 -4
  150. package/dist/types/emails/OAuthTokenCreatedEmail.d.ts.map +1 -1
  151. package/dist/types/emails/PasswordChangeConfirmation.d.ts +4 -4
  152. package/dist/types/emails/ResetUserPassword.d.ts +4 -4
  153. package/dist/types/emails/ResetUserPassword.d.ts.map +1 -1
  154. package/dist/types/emails/SubscriptionPaymentCancellation.d.ts +4 -4
  155. package/dist/types/emails/SubscriptionPaymentError.d.ts +4 -4
  156. package/dist/types/emails/SubscriptionPaymentSuccess.d.ts +4 -4
  157. package/dist/types/emails/ValidateUserEmail.d.ts +4 -4
  158. package/dist/types/emails/Welcome.d.ts +4 -4
  159. package/dist/types/export.d.ts +11 -5
  160. package/dist/types/middlewares/oAuth2.middleware.d.ts +9 -4
  161. package/dist/types/middlewares/oAuth2.middleware.d.ts.map +1 -1
  162. package/dist/types/middlewares/sessionAuth.middleware.d.ts +13 -3
  163. package/dist/types/middlewares/sessionAuth.middleware.d.ts.map +1 -1
  164. package/dist/types/models/discussion.model.d.ts +3 -3
  165. package/dist/types/models/oAuth2.model.d.ts +3 -3
  166. package/dist/types/routes/ai.routes.d.ts +2 -2
  167. package/dist/types/routes/ai.routes.d.ts.map +1 -1
  168. package/dist/types/routes/bitbucket.routes.d.ts +35 -0
  169. package/dist/types/routes/bitbucket.routes.d.ts.map +1 -0
  170. package/dist/types/routes/dictionary.routes.d.ts +2 -2
  171. package/dist/types/routes/dictionary.routes.d.ts.map +1 -1
  172. package/dist/types/routes/eventListener.routes.d.ts +2 -2
  173. package/dist/types/routes/eventListener.routes.d.ts.map +1 -1
  174. package/dist/types/routes/github.routes.d.ts +35 -0
  175. package/dist/types/routes/github.routes.d.ts.map +1 -0
  176. package/dist/types/routes/gitlab.routes.d.ts +35 -0
  177. package/dist/types/routes/gitlab.routes.d.ts.map +1 -0
  178. package/dist/types/routes/newsletter.routes.d.ts +2 -2
  179. package/dist/types/routes/newsletter.routes.d.ts.map +1 -1
  180. package/dist/types/routes/organization.routes.d.ts +2 -2
  181. package/dist/types/routes/organization.routes.d.ts.map +1 -1
  182. package/dist/types/routes/project.routes.d.ts +22 -2
  183. package/dist/types/routes/project.routes.d.ts.map +1 -1
  184. package/dist/types/routes/search.routes.d.ts +2 -2
  185. package/dist/types/routes/search.routes.d.ts.map +1 -1
  186. package/dist/types/routes/stripe.routes.d.ts +2 -2
  187. package/dist/types/routes/stripe.routes.d.ts.map +1 -1
  188. package/dist/types/routes/tags.routes.d.ts +2 -2
  189. package/dist/types/routes/tags.routes.d.ts.map +1 -1
  190. package/dist/types/routes/user.routes.d.ts +2 -2
  191. package/dist/types/routes/user.routes.d.ts.map +1 -1
  192. package/dist/types/schemas/dictionary.schema.d.ts +6 -6
  193. package/dist/types/schemas/discussion.schema.d.ts +6 -6
  194. package/dist/types/schemas/oAuth2.schema.d.ts +5 -5
  195. package/dist/types/schemas/oAuth2.schema.d.ts.map +1 -1
  196. package/dist/types/schemas/plans.schema.d.ts +6 -6
  197. package/dist/types/schemas/project.schema.d.ts +6 -6
  198. package/dist/types/schemas/project.schema.d.ts.map +1 -1
  199. package/dist/types/schemas/session.schema.d.ts +6 -6
  200. package/dist/types/schemas/tag.schema.d.ts +6 -6
  201. package/dist/types/schemas/user.schema.d.ts +6 -6
  202. package/dist/types/schemas/user.schema.d.ts.map +1 -1
  203. package/dist/types/services/bitbucket.service.d.ts +71 -0
  204. package/dist/types/services/bitbucket.service.d.ts.map +1 -0
  205. package/dist/types/services/ci.service.d.ts +27 -0
  206. package/dist/types/services/ci.service.d.ts.map +1 -0
  207. package/dist/types/services/github.service.d.ts +40 -0
  208. package/dist/types/services/github.service.d.ts.map +1 -0
  209. package/dist/types/services/gitlab.service.d.ts +58 -0
  210. package/dist/types/services/gitlab.service.d.ts.map +1 -0
  211. package/dist/types/services/webhook.service.d.ts +19 -0
  212. package/dist/types/services/webhook.service.d.ts.map +1 -0
  213. package/dist/types/types/project.types.d.ts +46 -5
  214. package/dist/types/types/project.types.d.ts.map +1 -1
  215. package/dist/types/types/session.types.d.ts +1 -1
  216. package/dist/types/types/user.types.d.ts +1 -1
  217. package/dist/types/utils/AI/auditTag/index.d.ts +1 -1
  218. package/dist/types/utils/auth/getAuth.d.ts.map +1 -1
  219. package/dist/types/utils/cors.d.ts +2 -2
  220. package/dist/types/utils/errors/ErrorHandler.d.ts +31 -3
  221. package/dist/types/utils/errors/ErrorHandler.d.ts.map +1 -1
  222. package/dist/types/utils/errors/ErrorsClass.d.ts +1 -1
  223. package/dist/types/utils/errors/errorCodes.d.ts +234 -0
  224. package/dist/types/utils/errors/errorCodes.d.ts.map +1 -1
  225. package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts +8 -4
  226. package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts.map +1 -1
  227. package/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts +6 -3
  228. package/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts.map +1 -1
  229. package/dist/types/utils/filtersAndPagination/getFiltersAndPaginationFromBody.d.ts +6 -2
  230. package/dist/types/utils/filtersAndPagination/getFiltersAndPaginationFromBody.d.ts.map +1 -1
  231. package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts +8 -4
  232. package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts.map +1 -1
  233. package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts +6 -2
  234. package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts.map +1 -1
  235. package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts +8 -4
  236. package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts.map +1 -1
  237. package/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts +6 -2
  238. package/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts.map +1 -1
  239. package/dist/types/utils/mapper/project.d.ts.map +1 -1
  240. package/dist/types/utils/permissions.d.ts +1 -1
  241. package/dist/types/utils/rateLimiter.d.ts +4 -2
  242. package/dist/types/utils/rateLimiter.d.ts.map +1 -1
  243. package/package.json +24 -28
  244. package/dist/esm/middlewares/request.middleware.mjs +0 -17
  245. package/dist/esm/middlewares/request.middleware.mjs.map +0 -1
  246. package/dist/types/middlewares/request.middleware.d.ts +0 -7
  247. package/dist/types/middlewares/request.middleware.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"project.schema.mjs","names":[],"sources":["../../../src/schemas/project.schema.ts"],"sourcesContent":["import { Locales } from '@intlayer/types';\nimport type { RenameId } from '@utils/mongoDB/types';\nimport {\n MEMBERS_MIN_LENGTH,\n NAME_MAX_LENGTH,\n NAME_MIN_LENGTH,\n} from '@utils/validation/validateProject';\nimport { Schema } from 'mongoose';\nimport type {\n OAuth2Access,\n Project,\n ProjectSchema,\n} from '@/types/project.types';\n\n// Define the oAuth2Access subdocument schema with timestamps\nconst oAuth2AccessSchema = new Schema<RenameId<OAuth2Access>>(\n {\n clientId: { type: String, required: true },\n clientSecret: { type: String, required: true },\n userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },\n name: { type: String, required: true },\n expiresAt: { type: Date },\n accessToken: { type: [String], required: true, default: [] },\n grants: { type: [String], required: true, default: [] },\n },\n {\n timestamps: true,\n }\n);\n\nconst projectConfigSchema = new Schema<Project['configuration']>(\n {\n internationalization: {\n locales: {\n type: [String],\n enum: Object.values(Locales.ALL_LOCALES),\n required: true,\n },\n defaultLocale: {\n type: String,\n enum: Object.values(Locales.ALL_LOCALES),\n },\n },\n editor: {\n applicationURL: {\n type: String,\n },\n cmsURL: {\n type: String,\n },\n },\n },\n {\n _id: false, // Prevents the generation of an _id field for this subdocument\n }\n);\n\nexport const projectSchema = new Schema<ProjectSchema>(\n {\n organizationId: {\n type: Schema.Types.ObjectId,\n ref: 'Organization',\n required: true,\n },\n name: {\n type: String,\n required: true,\n minlength: NAME_MIN_LENGTH,\n maxlength: NAME_MAX_LENGTH,\n },\n configuration: projectConfigSchema,\n oAuth2Access: [oAuth2AccessSchema],\n membersIds: {\n type: [Schema.Types.ObjectId],\n ref: 'User',\n required: true,\n minlength: MEMBERS_MIN_LENGTH,\n },\n adminsIds: {\n type: [Schema.Types.ObjectId],\n ref: 'User',\n required: true,\n minlength: MEMBERS_MIN_LENGTH,\n },\n creatorId: {\n type: Schema.Types.ObjectId,\n ref: 'User',\n required: true,\n },\n },\n {\n timestamps: true,\n\n toJSON: {\n virtuals: true, // keep the automatic `id` getter\n versionKey: false, // drop __v\n transform(_doc, ret: any) {\n const { _id, ...rest } = ret;\n return {\n ...rest,\n id: _id.toString(),\n };\n },\n },\n toObject: {\n virtuals: true,\n transform(_doc, ret: any) {\n const { _id, ...rest } = ret;\n return {\n ...rest,\n id: _id,\n };\n },\n },\n }\n);\n"],"mappings":";;;;;AAeA,MAAM,qBAAqB,IAAI,OAC7B;CACE,UAAU;EAAE,MAAM;EAAQ,UAAU;EAAM;CAC1C,cAAc;EAAE,MAAM;EAAQ,UAAU;EAAM;CAC9C,QAAQ;EAAE,MAAM,OAAO,MAAM;EAAU,KAAK;EAAQ,UAAU;EAAM;CACpE,MAAM;EAAE,MAAM;EAAQ,UAAU;EAAM;CACtC,WAAW,EAAE,MAAM,MAAM;CACzB,aAAa;EAAE,MAAM,CAAC,OAAO;EAAE,UAAU;EAAM,SAAS,EAAE;EAAE;CAC5D,QAAQ;EAAE,MAAM,CAAC,OAAO;EAAE,UAAU;EAAM,SAAS,EAAE;EAAE;CACxD,EACD,EACE,YAAY,MACb,CACF;AAED,MAAM,sBAAsB,IAAI,OAC9B;CACE,sBAAsB;EACpB,SAAS;GACP,MAAM,CAAC,OAAO;GACd,MAAM,OAAO,OAAO,QAAQ,YAAY;GACxC,UAAU;GACX;EACD,eAAe;GACb,MAAM;GACN,MAAM,OAAO,OAAO,QAAQ,YAAY;GACzC;EACF;CACD,QAAQ;EACN,gBAAgB,EACd,MAAM,QACP;EACD,QAAQ,EACN,MAAM,QACP;EACF;CACF,EACD,EACE,KAAK,OACN,CACF;AAED,MAAa,gBAAgB,IAAI,OAC/B;CACE,gBAAgB;EACd,MAAM,OAAO,MAAM;EACnB,KAAK;EACL,UAAU;EACX;CACD,MAAM;EACJ,MAAM;EACN,UAAU;EACV,WAAW;EACX,WAAW;EACZ;CACD,eAAe;CACf,cAAc,CAAC,mBAAmB;CAClC,YAAY;EACV,MAAM,CAAC,OAAO,MAAM,SAAS;EAC7B,KAAK;EACL,UAAU;EACV,WAAW;EACZ;CACD,WAAW;EACT,MAAM,CAAC,OAAO,MAAM,SAAS;EAC7B,KAAK;EACL,UAAU;EACV,WAAW;EACZ;CACD,WAAW;EACT,MAAM,OAAO,MAAM;EACnB,KAAK;EACL,UAAU;EACX;CACF,EACD;CACE,YAAY;CAEZ,QAAQ;EACN,UAAU;EACV,YAAY;EACZ,UAAU,MAAM,KAAU;GACxB,MAAM,EAAE,KAAK,GAAG,SAAS;AACzB,UAAO;IACL,GAAG;IACH,IAAI,IAAI,UAAU;IACnB;;EAEJ;CACD,UAAU;EACR,UAAU;EACV,UAAU,MAAM,KAAU;GACxB,MAAM,EAAE,KAAK,GAAG,SAAS;AACzB,UAAO;IACL,GAAG;IACH,IAAI;IACL;;EAEJ;CACF,CACF"}
1
+ {"version":3,"file":"project.schema.mjs","names":[],"sources":["../../../src/schemas/project.schema.ts"],"sourcesContent":["import { AiProviders, Locales } from '@intlayer/types';\nimport type { RenameId } from '@utils/mongoDB/types';\nimport {\n MEMBERS_MIN_LENGTH,\n NAME_MAX_LENGTH,\n NAME_MIN_LENGTH,\n} from '@utils/validation/validateProject';\nimport { Schema } from 'mongoose';\nimport type {\n OAuth2Access,\n Project,\n ProjectSchema,\n} from '@/types/project.types';\n\n// Define the oAuth2Access subdocument schema with timestamps\nconst oAuth2AccessSchema = new Schema<RenameId<OAuth2Access>>(\n {\n clientId: { type: String, required: true },\n clientSecret: { type: String, required: true },\n userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },\n name: { type: String, required: true },\n expiresAt: { type: Date },\n accessToken: { type: [String], required: true, default: [] },\n grants: { type: [String], required: true, default: [] },\n },\n {\n timestamps: true,\n }\n);\n\n// Schema for generic webhooks (Vercel, Netlify, Custom, etc.)\nconst webhookSchema = new Schema(\n {\n name: { type: String, required: true },\n url: { type: String, required: true },\n enabled: { type: Boolean, default: true },\n secret: { type: String }, // Optional signature secret\n },\n { _id: true }\n);\n\nconst projectConfigSchema = new Schema<Project['configuration']>(\n {\n internationalization: {\n locales: {\n type: [String],\n enum: Object.values(Locales.ALL_LOCALES),\n required: true,\n },\n defaultLocale: {\n type: String,\n enum: Object.values(Locales.ALL_LOCALES),\n },\n },\n editor: {\n applicationURL: {\n type: String,\n },\n cmsURL: {\n type: String,\n },\n },\n ai: {\n provider: {\n type: String,\n enum: Object.values(AiProviders),\n },\n model: {\n type: String,\n },\n apiKey: {\n type: String,\n },\n applicationContext: {\n type: String,\n },\n baseURL: {\n type: String,\n },\n },\n },\n {\n _id: false, // Prevents the generation of an _id field for this subdocument\n }\n);\n\n// Schema for webhooks/CI configuration (top-level project property, not part of configuration)\nconst webhooksConfigSchema = new Schema<Project['webhooks']>(\n {\n autoTriggerBuilds: { type: Boolean, default: false }, // Master toggle\n webhooks: [webhookSchema], // Generic hooks (Vercel, Netlify, Custom)\n },\n {\n _id: false,\n }\n);\n\nconst repositorySchema = new Schema<Project['repository']>(\n {\n provider: {\n type: String,\n enum: ['github', 'gitlab', 'bitbucket'],\n required: true,\n },\n owner: { type: String, required: true },\n repository: { type: String, required: true },\n branch: { type: String, default: 'main' },\n url: { type: String, required: true },\n configFilePath: { type: String, required: true },\n // GitHub specific\n installationId: { type: Number },\n // GitLab specific\n projectId: { type: Number },\n instanceUrl: { type: String },\n // Bitbucket specific\n workspace: { type: String },\n },\n {\n _id: false,\n }\n);\n\nexport const projectSchema = new Schema<ProjectSchema>(\n {\n organizationId: {\n type: Schema.Types.ObjectId,\n ref: 'Organization',\n required: true,\n },\n name: {\n type: String,\n required: true,\n minlength: NAME_MIN_LENGTH,\n maxlength: NAME_MAX_LENGTH,\n },\n configuration: projectConfigSchema,\n oAuth2Access: [oAuth2AccessSchema],\n repository: repositorySchema,\n webhooks: webhooksConfigSchema,\n membersIds: {\n type: [Schema.Types.ObjectId],\n ref: 'User',\n required: true,\n minlength: MEMBERS_MIN_LENGTH,\n },\n adminsIds: {\n type: [Schema.Types.ObjectId],\n ref: 'User',\n required: true,\n minlength: MEMBERS_MIN_LENGTH,\n },\n creatorId: {\n type: Schema.Types.ObjectId,\n ref: 'User',\n required: true,\n },\n },\n {\n timestamps: true,\n\n toJSON: {\n virtuals: true, // keep the automatic `id` getter\n versionKey: false, // drop __v\n transform(_doc, ret: any) {\n const { _id, ...rest } = ret;\n return {\n ...rest,\n id: _id.toString(),\n };\n },\n },\n toObject: {\n virtuals: true,\n transform(_doc, ret: any) {\n const { _id, ...rest } = ret;\n return {\n ...rest,\n id: _id,\n };\n },\n },\n }\n);\n"],"mappings":"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"}
@@ -0,0 +1,173 @@
1
+ import { logger } from "../logger/index.mjs";
2
+ import { getDBClient } from "../utils/mongoDB/connectDB.mjs";
3
+ import { configurationFilesCandidates } from "@intlayer/config";
4
+ import { ObjectId } from "mongodb";
5
+
6
+ //#region src/services/bitbucket.service.ts
7
+ const BITBUCKET_API_URL = "https://api.bitbucket.org/2.0";
8
+ const BITBUCKET_AUTH_URL = "https://bitbucket.org/site/oauth2";
9
+ /**
10
+ * Get Bitbucket (Atlassian) authorization URL for OAuth flow
11
+ */
12
+ const getAuthorizationUrl = (redirectUri) => {
13
+ const clientId = process.env.ATLASSIAN_CLIENT_ID;
14
+ if (!clientId) throw new Error("Bitbucket/Atlassian Client ID is not configured");
15
+ return `${BITBUCKET_AUTH_URL}/authorize?${new URLSearchParams({
16
+ client_id: clientId,
17
+ response_type: "code",
18
+ state: "bitbucket_oauth"
19
+ }).toString()}`;
20
+ };
21
+ /**
22
+ * Exchange Bitbucket authorization code for access token
23
+ */
24
+ const exchangeCodeForToken = async (code) => {
25
+ const clientId = process.env.ATLASSIAN_CLIENT_ID;
26
+ const clientSecret = process.env.ATLASSIAN_CLIENT_SECRET;
27
+ if (!clientId || !clientSecret) throw new Error("Bitbucket OAuth credentials are not configured");
28
+ try {
29
+ const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
30
+ const response = await fetch(`${BITBUCKET_AUTH_URL}/access_token`, {
31
+ method: "POST",
32
+ headers: {
33
+ "Content-Type": "application/x-www-form-urlencoded",
34
+ Authorization: `Basic ${credentials}`
35
+ },
36
+ body: new URLSearchParams({
37
+ grant_type: "authorization_code",
38
+ code
39
+ })
40
+ });
41
+ if (!response.ok) throw new Error(`Bitbucket token exchange failed: ${response.statusText}`);
42
+ const data = await response.json();
43
+ if (data.error) throw new Error(`Bitbucket token error: ${data.error_description}`);
44
+ return data.access_token;
45
+ } catch (error) {
46
+ logger.error("Error exchanging Bitbucket code for token:", error);
47
+ throw error;
48
+ }
49
+ };
50
+ /**
51
+ * Get user's Bitbucket repositories
52
+ */
53
+ const getUserRepositories = async (accessToken) => {
54
+ try {
55
+ const userResponse = await fetch(`${BITBUCKET_API_URL}/user`, { headers: {
56
+ Authorization: `Bearer ${accessToken}`,
57
+ Accept: "application/json"
58
+ } });
59
+ if (!userResponse.ok) throw new Error(`Failed to fetch Bitbucket user: ${userResponse.statusText}`);
60
+ const reposResponse = await fetch(`${BITBUCKET_API_URL}/repositories?role=member&sort=-updated_on&pagelen=100`, { headers: {
61
+ Authorization: `Bearer ${accessToken}`,
62
+ Accept: "application/json"
63
+ } });
64
+ if (!reposResponse.ok) throw new Error(`Failed to fetch Bitbucket repositories: ${reposResponse.statusText}`);
65
+ return (await reposResponse.json()).values || [];
66
+ } catch (error) {
67
+ logger.error("Error fetching Bitbucket repositories:", error);
68
+ throw error;
69
+ }
70
+ };
71
+ /**
72
+ * Check if valid intlayer configuration files exist in a Bitbucket repository (Recursively).
73
+ * Returns an array of file paths found.
74
+ */
75
+ const checkIntlayerConfig = async (accessToken, workspace, repoSlug, branch = "main") => {
76
+ try {
77
+ const response = await fetch(`${BITBUCKET_API_URL}/repositories/${workspace}/${repoSlug}/src/${encodeURIComponent(branch)}/?max_depth=10&pagelen=10000`, { headers: {
78
+ Authorization: `Bearer ${accessToken}`,
79
+ Accept: "application/json"
80
+ } });
81
+ if (!response.ok) {
82
+ if (response.status === 404) return [];
83
+ throw new Error(`Failed to fetch repository tree: ${response.statusText}`);
84
+ }
85
+ return ((await response.json()).values || []).filter((item) => {
86
+ if (item.type !== "commit_file") return false;
87
+ return configurationFilesCandidates.some((candidate) => item.path.endsWith(candidate));
88
+ }).map((item) => item.path);
89
+ } catch (error) {
90
+ if (error.status === 404) return [];
91
+ logger.error("Error checking intlayer configuration on Bitbucket:", error);
92
+ return [];
93
+ }
94
+ };
95
+ /**
96
+ * Get repository file contents from Bitbucket and decode it
97
+ */
98
+ const getRepositoryFileContents = async (accessToken, workspace, repoSlug, path, branch = "main") => {
99
+ try {
100
+ const response = await fetch(`${BITBUCKET_API_URL}/repositories/${workspace}/${repoSlug}/src/${encodeURIComponent(branch)}/${encodeURIComponent(path)}`, { headers: {
101
+ Authorization: `Bearer ${accessToken}`,
102
+ Accept: "application/json"
103
+ } });
104
+ if (!response.ok) {
105
+ if (response.status === 404) return null;
106
+ throw new Error(`Failed to fetch file contents: ${response.statusText}`);
107
+ }
108
+ return await response.text();
109
+ } catch (error) {
110
+ if (error.status === 404) return null;
111
+ logger.error("Error fetching Bitbucket file contents:", error);
112
+ throw error;
113
+ }
114
+ };
115
+ /**
116
+ * Get Bitbucket access token from user's linked account (Atlassian)
117
+ */
118
+ const getBitbucketTokenFromUser = async (userId) => {
119
+ try {
120
+ const db = getDBClient().db();
121
+ let account = await db.collection("account").findOne({
122
+ userId,
123
+ providerId: "atlassian"
124
+ });
125
+ if (!account && ObjectId.isValid(userId)) account = await db.collection("account").findOne({
126
+ userId: new ObjectId(userId),
127
+ providerId: "atlassian"
128
+ });
129
+ if (!account) account = await db.collection("accounts").findOne({
130
+ userId,
131
+ providerId: "atlassian"
132
+ });
133
+ if (!account && ObjectId.isValid(userId)) account = await db.collection("accounts").findOne({
134
+ userId: new ObjectId(userId),
135
+ providerId: "atlassian"
136
+ });
137
+ if (!account) return null;
138
+ return account.accessToken || account.access_token || null;
139
+ } catch (error) {
140
+ logger.error("Error retrieving Bitbucket token from DB:", error);
141
+ return null;
142
+ }
143
+ };
144
+ /**
145
+ * Check if a Bitbucket pipeline file exists
146
+ */
147
+ const checkPipelineFileExists = async (accessToken, workspace, repoSlug, filename = "bitbucket-pipelines.yml", branch = "main") => {
148
+ try {
149
+ const response = await fetch(`${BITBUCKET_API_URL}/repositories/${workspace}/${repoSlug}/src/${encodeURIComponent(branch)}/${encodeURIComponent(filename)}`, { headers: {
150
+ Authorization: `Bearer ${accessToken}`,
151
+ Accept: "application/json"
152
+ } });
153
+ if (response.status === 404) return false;
154
+ if (!response.ok) throw new Error(`Failed to check file existence: ${response.statusText}`);
155
+ return true;
156
+ } catch (error) {
157
+ if (error.status === 404) return false;
158
+ logger.error("Error checking pipeline file existence:", error);
159
+ throw error;
160
+ }
161
+ };
162
+ /**
163
+ * Create or update a Bitbucket pipeline file
164
+ * Note: Bitbucket API doesn't support direct file creation via API v2.0
165
+ * This function returns false for allowAutoPush, requiring manual installation
166
+ */
167
+ const createPipelineFile = async (accessToken, workspace, repoSlug, filename = "bitbucket-pipelines.yml", content, branch = "main", message = "Add Intlayer CI pipeline") => {
168
+ throw new Error("Bitbucket API does not support automatic file creation. Please manually add the pipeline file to your repository.");
169
+ };
170
+
171
+ //#endregion
172
+ export { checkIntlayerConfig, checkPipelineFileExists, createPipelineFile, exchangeCodeForToken, getAuthorizationUrl, getBitbucketTokenFromUser, getRepositoryFileContents, getUserRepositories };
173
+ //# sourceMappingURL=bitbucket.service.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitbucket.service.mjs","names":["error: any"],"sources":["../../../src/services/bitbucket.service.ts"],"sourcesContent":["import { configurationFilesCandidates } from '@intlayer/config';\nimport { logger } from '@logger';\nimport { getDBClient } from '@utils/mongoDB/connectDB';\nimport { ObjectId } from 'mongodb';\n\nconst BITBUCKET_API_URL = 'https://api.bitbucket.org/2.0';\nconst BITBUCKET_AUTH_URL = 'https://bitbucket.org/site/oauth2';\n\nexport type BitbucketRepository = {\n uuid: string;\n name: string;\n full_name: string;\n slug: string;\n mainbranch?: {\n name: string;\n type: string;\n };\n links: {\n html: {\n href: string;\n };\n };\n workspace: {\n slug: string;\n name: string;\n uuid: string;\n };\n owner: {\n display_name: string;\n username?: string;\n uuid: string;\n };\n updated_on: string;\n is_private: boolean;\n};\n\nexport type BitbucketTreeItem = {\n path: string;\n type: 'commit_file' | 'commit_directory';\n size?: number;\n};\n\n/**\n * Get Bitbucket (Atlassian) authorization URL for OAuth flow\n */\nexport const getAuthorizationUrl = (redirectUri: string): string => {\n const clientId = process.env.ATLASSIAN_CLIENT_ID;\n\n if (!clientId) {\n throw new Error('Bitbucket/Atlassian Client ID is not configured');\n }\n\n const params = new URLSearchParams({\n client_id: clientId,\n response_type: 'code',\n state: 'bitbucket_oauth',\n });\n\n return `${BITBUCKET_AUTH_URL}/authorize?${params.toString()}`;\n};\n\n/**\n * Exchange Bitbucket authorization code for access token\n */\nexport const exchangeCodeForToken = async (code: string): Promise<string> => {\n const clientId = process.env.ATLASSIAN_CLIENT_ID;\n const clientSecret = process.env.ATLASSIAN_CLIENT_SECRET;\n\n if (!clientId || !clientSecret) {\n throw new Error('Bitbucket OAuth credentials are not configured');\n }\n\n try {\n const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString(\n 'base64'\n );\n\n const response = await fetch(`${BITBUCKET_AUTH_URL}/access_token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Authorization: `Basic ${credentials}`,\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `Bitbucket token exchange failed: ${response.statusText}`\n );\n }\n\n const data = await response.json();\n\n if (data.error) {\n throw new Error(`Bitbucket token error: ${data.error_description}`);\n }\n\n return data.access_token;\n } catch (error) {\n logger.error('Error exchanging Bitbucket code for token:', error);\n throw error;\n }\n};\n\n/**\n * Get user's Bitbucket repositories\n */\nexport const getUserRepositories = async (\n accessToken: string\n): Promise<BitbucketRepository[]> => {\n try {\n // First, get the current user to find their workspaces\n const userResponse = await fetch(`${BITBUCKET_API_URL}/user`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n });\n\n if (!userResponse.ok) {\n throw new Error(\n `Failed to fetch Bitbucket user: ${userResponse.statusText}`\n );\n }\n\n // Get repositories the user has access to\n const reposResponse = await fetch(\n `${BITBUCKET_API_URL}/repositories?role=member&sort=-updated_on&pagelen=100`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!reposResponse.ok) {\n throw new Error(\n `Failed to fetch Bitbucket repositories: ${reposResponse.statusText}`\n );\n }\n\n const data = await reposResponse.json();\n return data.values || [];\n } catch (error) {\n logger.error('Error fetching Bitbucket repositories:', error);\n throw error;\n }\n};\n\n/**\n * Check if valid intlayer configuration files exist in a Bitbucket repository (Recursively).\n * Returns an array of file paths found.\n */\nexport const checkIntlayerConfig = async (\n accessToken: string,\n workspace: string,\n repoSlug: string,\n branch: string = 'main'\n): Promise<string[]> => {\n try {\n // Use Bitbucket's src API to list files recursively\n const response = await fetch(\n `${BITBUCKET_API_URL}/repositories/${workspace}/${repoSlug}/src/${encodeURIComponent(branch)}/?max_depth=10&pagelen=10000`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n if (response.status === 404) return [];\n throw new Error(\n `Failed to fetch repository tree: ${response.statusText}`\n );\n }\n\n const data = await response.json();\n const items: BitbucketTreeItem[] = data.values || [];\n\n // Filter files that match the configuration candidates\n const foundFiles = items\n .filter((item) => {\n if (item.type !== 'commit_file') return false;\n return (configurationFilesCandidates as readonly string[]).some(\n (candidate) => item.path.endsWith(candidate)\n );\n })\n .map((item) => item.path);\n\n return foundFiles;\n } catch (error: any) {\n if (error.status === 404) return [];\n logger.error('Error checking intlayer configuration on Bitbucket:', error);\n return [];\n }\n};\n\n/**\n * Get repository file contents from Bitbucket and decode it\n */\nexport const getRepositoryFileContents = async (\n accessToken: string,\n workspace: string,\n repoSlug: string,\n path: string,\n branch: string = 'main'\n): Promise<string | null> => {\n try {\n const response = await fetch(\n `${BITBUCKET_API_URL}/repositories/${workspace}/${repoSlug}/src/${encodeURIComponent(branch)}/${encodeURIComponent(path)}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (!response.ok) {\n if (response.status === 404) return null;\n throw new Error(`Failed to fetch file contents: ${response.statusText}`);\n }\n\n const content = await response.text();\n return content;\n } catch (error: any) {\n if (error.status === 404) return null;\n logger.error('Error fetching Bitbucket file contents:', error);\n throw error;\n }\n};\n\n/**\n * Get Bitbucket access token from user's linked account (Atlassian)\n */\nexport const getBitbucketTokenFromUser = async (\n userId: string\n): Promise<string | null> => {\n try {\n const client = getDBClient();\n const db = client.db();\n\n // Try with 'atlassian' provider ID (as it's linked through Better Auth's atlassian provider)\n let account = await db.collection('account').findOne({\n userId: userId,\n providerId: 'atlassian',\n });\n\n if (!account && ObjectId.isValid(userId)) {\n account = await db.collection('account').findOne({\n userId: new ObjectId(userId),\n providerId: 'atlassian',\n });\n }\n\n if (!account) {\n account = await db.collection('accounts').findOne({\n userId: userId,\n providerId: 'atlassian',\n });\n }\n\n if (!account && ObjectId.isValid(userId)) {\n account = await db.collection('accounts').findOne({\n userId: new ObjectId(userId),\n providerId: 'atlassian',\n });\n }\n\n if (!account) {\n return null;\n }\n\n const accessToken = account.accessToken || account.access_token;\n\n return accessToken || null;\n } catch (error) {\n logger.error('Error retrieving Bitbucket token from DB:', error);\n return null;\n }\n};\n\n/**\n * Check if a Bitbucket pipeline file exists\n */\nexport const checkPipelineFileExists = async (\n accessToken: string,\n workspace: string,\n repoSlug: string,\n filename: string = 'bitbucket-pipelines.yml',\n branch: string = 'main'\n): Promise<boolean> => {\n try {\n const response = await fetch(\n `${BITBUCKET_API_URL}/repositories/${workspace}/${repoSlug}/src/${encodeURIComponent(branch)}/${encodeURIComponent(filename)}`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n Accept: 'application/json',\n },\n }\n );\n\n if (response.status === 404) return false;\n if (!response.ok) {\n throw new Error(`Failed to check file existence: ${response.statusText}`);\n }\n\n return true;\n } catch (error: any) {\n if (error.status === 404) return false;\n logger.error('Error checking pipeline file existence:', error);\n throw error;\n }\n};\n\n/**\n * Create or update a Bitbucket pipeline file\n * Note: Bitbucket API doesn't support direct file creation via API v2.0\n * This function returns false for allowAutoPush, requiring manual installation\n */\nexport const createPipelineFile = async (\n accessToken: string,\n workspace: string,\n repoSlug: string,\n filename: string = 'bitbucket-pipelines.yml',\n content: string,\n branch: string = 'main',\n message: string = 'Add Intlayer CI pipeline'\n): Promise<void> => {\n // Bitbucket API v2.0 doesn't support direct file creation/update\n // Users need to manually add the file or use the web interface\n // We'll throw an error indicating manual installation is required\n throw new Error(\n 'Bitbucket API does not support automatic file creation. Please manually add the pipeline file to your repository.'\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ import { logger } from "../logger/index.mjs";
2
+ import { checkPipelineFileExists } from "./bitbucket.service.mjs";
3
+ import { checkWorkflowFileExists, createWorkflowFile } from "./github.service.mjs";
4
+ import { checkPipelineFileExists as checkPipelineFileExists$1, createPipelineFile } from "./gitlab.service.mjs";
5
+
6
+ //#region src/services/ci.service.ts
7
+ const GITHUB_WORKFLOW_FILENAME = ".github/workflows/intlayer-cms.yml";
8
+ const GITLAB_PIPELINE_FILENAME = ".gitlab-ci.yml";
9
+ const BITBUCKET_PIPELINE_FILENAME = "bitbucket-pipelines.yml";
10
+ const GITHUB_TEMPLATE = `name: Intlayer CMS Update
11
+ on:
12
+ repository_dispatch:
13
+ types: [intlayer_cms_update]
14
+ jobs:
15
+ build:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - run: npm ci
20
+ - run: npm run build
21
+ `;
22
+ const GITLAB_TEMPLATE = `stages:
23
+ - build
24
+
25
+ intlayer_cms_update:
26
+ stage: build
27
+ only:
28
+ - triggers
29
+ script:
30
+ - npm ci
31
+ - npm run build
32
+ `;
33
+ const BITBUCKET_TEMPLATE = `pipelines:
34
+ custom:
35
+ intlayer-cms-update:
36
+ - step:
37
+ name: Build
38
+ script:
39
+ - npm ci
40
+ - npm run build
41
+ `;
42
+ /**
43
+ * Get access token from project's OAuth2 access
44
+ */
45
+ const getAccessToken = (project) => {
46
+ return (project.oAuth2Access?.[0])?.accessToken?.[0] || null;
47
+ };
48
+ /**
49
+ * Get CI configuration status for a project
50
+ */
51
+ const getCIStatus = async (project) => {
52
+ const { repository } = project;
53
+ if (!repository) throw new Error("Project is not connected to a repository.");
54
+ const accessToken = getAccessToken(project);
55
+ if (!accessToken) throw new Error("No valid OAuth2 access token found.");
56
+ const branch = repository.branch || "main";
57
+ try {
58
+ switch (repository.provider) {
59
+ case "github": {
60
+ const { owner, repository: repoName } = repository;
61
+ const filename = GITHUB_WORKFLOW_FILENAME;
62
+ return {
63
+ exists: await checkWorkflowFileExists(accessToken, owner, repoName, filename, branch),
64
+ content: GITHUB_TEMPLATE,
65
+ path: filename,
66
+ fileUrl: `https://github.com/${owner}/${repoName}/blob/${branch}/${filename}`,
67
+ allowAutoPush: true
68
+ };
69
+ }
70
+ case "gitlab": {
71
+ const { projectId, instanceUrl } = repository;
72
+ if (!projectId) throw new Error("GitLab project ID is required.");
73
+ const filename = GITLAB_PIPELINE_FILENAME;
74
+ return {
75
+ exists: await checkPipelineFileExists$1(accessToken, projectId, filename, branch, instanceUrl),
76
+ content: GITLAB_TEMPLATE,
77
+ path: filename,
78
+ fileUrl: `${instanceUrl || "https://gitlab.com"}/${repository.owner}/${repository.repository}/-/blob/${branch}/${filename}`,
79
+ allowAutoPush: true
80
+ };
81
+ }
82
+ case "bitbucket": {
83
+ const { workspace, repository: repoSlug } = repository;
84
+ const filename = BITBUCKET_PIPELINE_FILENAME;
85
+ return {
86
+ exists: await checkPipelineFileExists(accessToken, workspace, repoSlug, filename, branch),
87
+ content: BITBUCKET_TEMPLATE,
88
+ path: filename,
89
+ fileUrl: `https://bitbucket.org/${workspace}/${repoSlug}/src/${branch}/${filename}`,
90
+ allowAutoPush: false
91
+ };
92
+ }
93
+ default: throw new Error(`Unsupported repository provider: ${repository.provider}`);
94
+ }
95
+ } catch (error) {
96
+ logger.error("Error getting CI status:", error);
97
+ throw error;
98
+ }
99
+ };
100
+ /**
101
+ * Install CI configuration file in the repository
102
+ */
103
+ const installCI = async (project) => {
104
+ const { repository } = project;
105
+ if (!repository) throw new Error("Project is not connected to a repository.");
106
+ const accessToken = getAccessToken(project);
107
+ if (!accessToken) throw new Error("No valid OAuth2 access token found.");
108
+ const branch = repository.branch || "main";
109
+ try {
110
+ switch (repository.provider) {
111
+ case "github": {
112
+ const { owner, repository: repoName } = repository;
113
+ await createWorkflowFile(accessToken, owner, repoName, GITHUB_WORKFLOW_FILENAME, GITHUB_TEMPLATE, branch, "Add Intlayer CMS workflow");
114
+ break;
115
+ }
116
+ case "gitlab": {
117
+ const { projectId, instanceUrl } = repository;
118
+ if (!projectId) throw new Error("GitLab project ID is required.");
119
+ await createPipelineFile(accessToken, projectId, GITLAB_PIPELINE_FILENAME, GITLAB_TEMPLATE, branch, instanceUrl, "Add Intlayer CMS pipeline");
120
+ break;
121
+ }
122
+ case "bitbucket": throw new Error("Bitbucket does not support automatic CI file installation. Please add the file manually.");
123
+ default: throw new Error(`Unsupported repository provider: ${repository.provider}`);
124
+ }
125
+ logger.info(`Successfully installed CI configuration for project ${project.id}`);
126
+ } catch (error) {
127
+ logger.error("Error installing CI configuration:", error);
128
+ throw error;
129
+ }
130
+ };
131
+
132
+ //#endregion
133
+ export { BITBUCKET_PIPELINE_FILENAME, BITBUCKET_TEMPLATE, GITHUB_TEMPLATE, GITHUB_WORKFLOW_FILENAME, GITLAB_PIPELINE_FILENAME, GITLAB_TEMPLATE, getCIStatus, installCI };
134
+ //# sourceMappingURL=ci.service.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci.service.mjs","names":["githubService.checkWorkflowFileExists","gitlabService.checkPipelineFileExists","bitbucketService.checkPipelineFileExists","githubService.createWorkflowFile","gitlabService.createPipelineFile"],"sources":["../../../src/services/ci.service.ts"],"sourcesContent":["import { logger } from '@logger';\nimport type { Project } from '@/types/project.types';\nimport * as bitbucketService from './bitbucket.service';\nimport * as githubService from './github.service';\nimport * as gitlabService from './gitlab.service';\n\nexport const GITHUB_WORKFLOW_FILENAME = '.github/workflows/intlayer-cms.yml';\nexport const GITLAB_PIPELINE_FILENAME = '.gitlab-ci.yml';\nexport const BITBUCKET_PIPELINE_FILENAME = 'bitbucket-pipelines.yml';\n\nexport const GITHUB_TEMPLATE = `name: Intlayer CMS Update\non:\n repository_dispatch:\n types: [intlayer_cms_update]\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - run: npm ci\n - run: npm run build\n`;\n\nexport const GITLAB_TEMPLATE = `stages:\n - build\n\nintlayer_cms_update:\n stage: build\n only:\n - triggers\n script:\n - npm ci\n - npm run build\n`;\n\nexport const BITBUCKET_TEMPLATE = `pipelines:\n custom:\n intlayer-cms-update:\n - step:\n name: Build\n script:\n - npm ci\n - npm run build\n`;\n\nexport type CIStatus = {\n exists: boolean;\n content: string;\n path: string;\n fileUrl?: string;\n allowAutoPush: boolean;\n};\n\n/**\n * Get access token from project's OAuth2 access\n */\nconst getAccessToken = (project: Project): string | null => {\n const tokenData = project.oAuth2Access?.[0];\n const accessToken = tokenData?.accessToken?.[0];\n return accessToken || null;\n};\n\n/**\n * Get CI configuration status for a project\n */\nexport const getCIStatus = async (project: Project): Promise<CIStatus> => {\n const { repository } = project;\n\n if (!repository) {\n throw new Error('Project is not connected to a repository.');\n }\n\n const accessToken = getAccessToken(project);\n\n if (!accessToken) {\n throw new Error('No valid OAuth2 access token found.');\n }\n\n const branch = repository.branch || 'main';\n\n try {\n switch (repository.provider) {\n case 'github': {\n const { owner, repository: repoName } = repository;\n const filename = GITHUB_WORKFLOW_FILENAME;\n const exists = await githubService.checkWorkflowFileExists(\n accessToken,\n owner,\n repoName,\n filename,\n branch\n );\n\n const content = GITHUB_TEMPLATE;\n const fileUrl = `https://github.com/${owner}/${repoName}/blob/${branch}/${filename}`;\n\n return {\n exists,\n content,\n path: filename,\n fileUrl,\n allowAutoPush: true, // GitHub allows file creation via API\n };\n }\n\n case 'gitlab': {\n const { projectId, instanceUrl } = repository;\n if (!projectId) {\n throw new Error('GitLab project ID is required.');\n }\n\n const filename = GITLAB_PIPELINE_FILENAME;\n const exists = await gitlabService.checkPipelineFileExists(\n accessToken,\n projectId,\n filename,\n branch,\n instanceUrl\n );\n\n const content = GITLAB_TEMPLATE;\n const baseUrl = instanceUrl || 'https://gitlab.com';\n const fileUrl = `${baseUrl}/${repository.owner}/${repository.repository}/-/blob/${branch}/${filename}`;\n\n return {\n exists,\n content,\n path: filename,\n fileUrl,\n allowAutoPush: true, // GitLab allows file creation via API\n };\n }\n\n case 'bitbucket': {\n const { workspace, repository: repoSlug } = repository;\n const filename = BITBUCKET_PIPELINE_FILENAME;\n const exists = await bitbucketService.checkPipelineFileExists(\n accessToken,\n workspace,\n repoSlug,\n filename,\n branch\n );\n\n const content = BITBUCKET_TEMPLATE;\n const fileUrl = `https://bitbucket.org/${workspace}/${repoSlug}/src/${branch}/${filename}`;\n\n return {\n exists,\n content,\n path: filename,\n fileUrl,\n allowAutoPush: false, // Bitbucket API doesn't support automatic file creation\n };\n }\n\n default:\n throw new Error(\n `Unsupported repository provider: ${repository.provider}`\n );\n }\n } catch (error) {\n logger.error('Error getting CI status:', error);\n throw error;\n }\n};\n\n/**\n * Install CI configuration file in the repository\n */\nexport const installCI = async (project: Project): Promise<void> => {\n const { repository } = project;\n\n if (!repository) {\n throw new Error('Project is not connected to a repository.');\n }\n\n const accessToken = getAccessToken(project);\n\n if (!accessToken) {\n throw new Error('No valid OAuth2 access token found.');\n }\n\n const branch = repository.branch || 'main';\n\n try {\n switch (repository.provider) {\n case 'github': {\n const { owner, repository: repoName } = repository;\n await githubService.createWorkflowFile(\n accessToken,\n owner,\n repoName,\n GITHUB_WORKFLOW_FILENAME,\n GITHUB_TEMPLATE,\n branch,\n 'Add Intlayer CMS workflow'\n );\n break;\n }\n\n case 'gitlab': {\n const { projectId, instanceUrl } = repository;\n if (!projectId) {\n throw new Error('GitLab project ID is required.');\n }\n\n await gitlabService.createPipelineFile(\n accessToken,\n projectId,\n GITLAB_PIPELINE_FILENAME,\n GITLAB_TEMPLATE,\n branch,\n instanceUrl,\n 'Add Intlayer CMS pipeline'\n );\n break;\n }\n\n case 'bitbucket': {\n // Bitbucket API doesn't support automatic file creation\n // This should not be called if allowAutoPush is false\n throw new Error(\n 'Bitbucket does not support automatic CI file installation. Please add the file manually.'\n );\n }\n\n default:\n throw new Error(\n `Unsupported repository provider: ${repository.provider}`\n );\n }\n\n logger.info(\n `Successfully installed CI configuration for project ${project.id}`\n );\n } catch (error) {\n logger.error('Error installing CI configuration:', error);\n throw error;\n }\n};\n"],"mappings":";;;;;;AAMA,MAAa,2BAA2B;AACxC,MAAa,2BAA2B;AACxC,MAAa,8BAA8B;AAE3C,MAAa,kBAAkB;;;;;;;;;;;;AAa/B,MAAa,kBAAkB;;;;;;;;;;;AAY/B,MAAa,qBAAqB;;;;;;;;;;;;AAqBlC,MAAM,kBAAkB,YAAoC;AAG1D,SAFkB,QAAQ,eAAe,KACV,cAAc,MACvB;;;;;AAMxB,MAAa,cAAc,OAAO,YAAwC;CACxE,MAAM,EAAE,eAAe;AAEvB,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,4CAA4C;CAG9D,MAAM,cAAc,eAAe,QAAQ;AAE3C,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,sCAAsC;CAGxD,MAAM,SAAS,WAAW,UAAU;AAEpC,KAAI;AACF,UAAQ,WAAW,UAAnB;GACE,KAAK,UAAU;IACb,MAAM,EAAE,OAAO,YAAY,aAAa;IACxC,MAAM,WAAW;AAYjB,WAAO;KACL,QAZa,MAAMA,wBACnB,aACA,OACA,UACA,UACA,OACD;KAOC,SALc;KAMd,MAAM;KACN,SANc,sBAAsB,MAAM,GAAG,SAAS,QAAQ,OAAO,GAAG;KAOxE,eAAe;KAChB;;GAGH,KAAK,UAAU;IACb,MAAM,EAAE,WAAW,gBAAgB;AACnC,QAAI,CAAC,UACH,OAAM,IAAI,MAAM,iCAAiC;IAGnD,MAAM,WAAW;AAajB,WAAO;KACL,QAba,MAAMC,0BACnB,aACA,WACA,UACA,QACA,YACD;KAQC,SANc;KAOd,MAAM;KACN,SANc,GADA,eAAe,qBACJ,GAAG,WAAW,MAAM,GAAG,WAAW,WAAW,UAAU,OAAO,GAAG;KAO1F,eAAe;KAChB;;GAGH,KAAK,aAAa;IAChB,MAAM,EAAE,WAAW,YAAY,aAAa;IAC5C,MAAM,WAAW;AAYjB,WAAO;KACL,QAZa,MAAMC,wBACnB,aACA,WACA,UACA,UACA,OACD;KAOC,SALc;KAMd,MAAM;KACN,SANc,yBAAyB,UAAU,GAAG,SAAS,OAAO,OAAO,GAAG;KAO9E,eAAe;KAChB;;GAGH,QACE,OAAM,IAAI,MACR,oCAAoC,WAAW,WAChD;;UAEE,OAAO;AACd,SAAO,MAAM,4BAA4B,MAAM;AAC/C,QAAM;;;;;;AAOV,MAAa,YAAY,OAAO,YAAoC;CAClE,MAAM,EAAE,eAAe;AAEvB,KAAI,CAAC,WACH,OAAM,IAAI,MAAM,4CAA4C;CAG9D,MAAM,cAAc,eAAe,QAAQ;AAE3C,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,sCAAsC;CAGxD,MAAM,SAAS,WAAW,UAAU;AAEpC,KAAI;AACF,UAAQ,WAAW,UAAnB;GACE,KAAK,UAAU;IACb,MAAM,EAAE,OAAO,YAAY,aAAa;AACxC,UAAMC,mBACJ,aACA,OACA,UACA,0BACA,iBACA,QACA,4BACD;AACD;;GAGF,KAAK,UAAU;IACb,MAAM,EAAE,WAAW,gBAAgB;AACnC,QAAI,CAAC,UACH,OAAM,IAAI,MAAM,iCAAiC;AAGnD,UAAMC,mBACJ,aACA,WACA,0BACA,iBACA,QACA,aACA,4BACD;AACD;;GAGF,KAAK,YAGH,OAAM,IAAI,MACR,2FACD;GAGH,QACE,OAAM,IAAI,MACR,oCAAoC,WAAW,WAChD;;AAGL,SAAO,KACL,uDAAuD,QAAQ,KAChE;UACM,OAAO;AACd,SAAO,MAAM,sCAAsC,MAAM;AACzD,QAAM"}
@@ -9,7 +9,7 @@ import { SubscriptionPaymentErrorEN, SubscriptionPaymentErrorES, SubscriptionPay
9
9
  import { SubscriptionPaymentSuccessEN, SubscriptionPaymentSuccessES, SubscriptionPaymentSuccessFR } from "../emails/SubscriptionPaymentSuccess.mjs";
10
10
  import { ValidateUserEmailEN, ValidateUserEmailES, ValidateUserEmailFR } from "../emails/ValidateUserEmail.mjs";
11
11
  import { WelcomeEmailEN, WelcomeEmailES, WelcomeEmailFR } from "../emails/Welcome.mjs";
12
- import { t } from "express-intlayer";
12
+ import { t } from "fastify-intlayer";
13
13
  import { jsx } from "react/jsx-runtime";
14
14
  import { Resend } from "resend";
15
15
 
@@ -1 +1 @@
1
- {"version":3,"file":"email.service.mjs","names":[],"sources":["../../../src/services/email.service.tsx"],"sourcesContent":["import {\n InviteUserEmailEN,\n InviteUserEmailES,\n InviteUserEmailFR,\n} from '@emails/InviteUserEmail';\nimport {\n MagicLinkEmailEN,\n MagicLinkEmailES,\n MagicLinkEmailFR,\n} from '@emails/MagicLinkEmail';\nimport {\n OAuthTokenCreatedEmailEN,\n OAuthTokenCreatedEmailES,\n OAuthTokenCreatedEmailFR,\n} from '@emails/OAuthTokenCreatedEmail';\nimport {\n PasswordChangeConfirmationEmailEN,\n PasswordChangeConfirmationEmailES,\n PasswordChangeConfirmationEmailFR,\n} from '@emails/PasswordChangeConfirmation';\nimport {\n ResetPasswordEmailEN,\n ResetPasswordEmailES,\n ResetPasswordEmailFR,\n} from '@emails/ResetUserPassword';\n\nimport {\n SubscriptionPaymentCancellationEN,\n SubscriptionPaymentCancellationES,\n SubscriptionPaymentCancellationFR,\n} from '@emails/SubscriptionPaymentCancellation';\nimport {\n SubscriptionPaymentErrorEN,\n SubscriptionPaymentErrorES,\n SubscriptionPaymentErrorFR,\n} from '@emails/SubscriptionPaymentError';\nimport {\n SubscriptionPaymentSuccessEN,\n SubscriptionPaymentSuccessES,\n SubscriptionPaymentSuccessFR,\n} from '@emails/SubscriptionPaymentSuccess';\nimport {\n ValidateUserEmailEN,\n ValidateUserEmailES,\n ValidateUserEmailFR,\n} from '@emails/ValidateUserEmail';\nimport {\n WelcomeEmailEN,\n WelcomeEmailES,\n WelcomeEmailFR,\n} from '@emails/Welcome';\nimport type { Locale } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { t } from 'express-intlayer';\nimport type { ComponentProps, JSX } from 'react';\nimport { Resend } from 'resend';\n\ntype EmailComponentsType = (...props: any) => JSX.Element;\ntype EmailComponents = {\n [key: string]: {\n template: EmailComponentsType;\n subject: string;\n };\n};\n\nconst getEmailComponents = (locale?: Locale) =>\n ({\n invite: {\n template: t(\n {\n en: InviteUserEmailEN,\n fr: InviteUserEmailFR,\n es: InviteUserEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'You have been invited to join Intlayer',\n fr: 'Vous êtes invité à rejoindre Intlayer',\n es: 'Has sido invitado a unirte a Intlayer',\n },\n locale\n ),\n },\n validate: {\n template: t(\n {\n en: ValidateUserEmailEN,\n fr: ValidateUserEmailFR,\n es: ValidateUserEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Validate your email for Intlayer',\n fr: 'Validez votre email pour Intlayer',\n es: 'Valida tu correo electrónico para Intlayer',\n },\n locale\n ),\n },\n resetPassword: {\n template: t(\n {\n en: ResetPasswordEmailEN,\n fr: ResetPasswordEmailFR,\n es: ResetPasswordEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Reset your password for Intlayer',\n fr: 'Réinitialisez votre mot de passe pour Intlayer',\n es: 'Restablece tu contraseña para Intlayer',\n },\n locale\n ),\n },\n welcome: {\n template: t(\n {\n en: WelcomeEmailEN,\n fr: WelcomeEmailFR,\n es: WelcomeEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Welcome to Intlayer!',\n fr: 'Bienvenue chez Intlayer!',\n es: '¡Bienvenido a Intlayer!',\n },\n locale\n ),\n },\n magicLink: {\n template: t(\n {\n en: MagicLinkEmailEN,\n fr: MagicLinkEmailFR,\n es: MagicLinkEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Sign in to Intlayer',\n fr: 'Connectez-vous à Intlayer',\n es: 'Inicia sesión en Intlayer',\n },\n locale\n ),\n },\n passwordChangeConfirmation: {\n template: t(\n {\n en: PasswordChangeConfirmationEmailEN,\n fr: PasswordChangeConfirmationEmailFR,\n es: PasswordChangeConfirmationEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your Intlayer password has been changed',\n fr: 'Votre mot de passe Intlayer a été modifié',\n es: 'Tu contraseña de Intlayer ha sido cambiada',\n },\n locale\n ),\n },\n subscriptionPaymentSuccess: {\n template: t({\n en: SubscriptionPaymentSuccessEN,\n fr: SubscriptionPaymentSuccessFR,\n es: SubscriptionPaymentSuccessES,\n }),\n subject: t({\n en: 'Your payment for Intlayer subscription is confirmed',\n fr: \"Votre paiement pour l'abonnement Intlayer est confirmé\",\n es: 'Tu pago por la suscripción de Intlayer ha sido confirmado',\n }),\n },\n subscriptionPaymentCancellation: {\n template: t({\n en: SubscriptionPaymentCancellationEN,\n fr: SubscriptionPaymentCancellationFR,\n es: SubscriptionPaymentCancellationES,\n }),\n subject: t({\n en: 'Your Intlayer subscription has been canceled',\n fr: 'Votre abonnement Intlayer a été annulé',\n es: 'Tu suscripción de Intlayer ha sido cancelada',\n }),\n },\n subscriptionPaymentError: {\n template: t({\n en: SubscriptionPaymentErrorEN,\n fr: SubscriptionPaymentErrorFR,\n es: SubscriptionPaymentErrorES,\n }),\n subject: t({\n en: 'There was an issue with your Intlayer subscription payment',\n fr: \"Un problème est survenu avec votre paiement pour l'abonnement Intlayer\",\n es: 'Hubo un problema con el pago de tu suscripción de Intlayer',\n }),\n },\n oAuthTokenCreated: {\n template: t({\n en: OAuthTokenCreatedEmailEN,\n fr: OAuthTokenCreatedEmailFR,\n es: OAuthTokenCreatedEmailES,\n }),\n subject: t({\n en: 'A third-party OAuth application has been added to your Intlayer account',\n fr: 'Une application OAuth tierce a été ajoutée à votre compte Intlayer',\n es: 'Una aplicación OAuth de terceros ha sido añadida a tu cuenta de Intlayer',\n }),\n },\n }) satisfies EmailComponents;\n\ntype EmailType = keyof ReturnType<typeof getEmailComponents>;\n\nexport type SendEmailProps<T extends EmailType> = {\n type: T;\n to: string;\n subject?: string;\n locale?: Locale;\n} & ComponentProps<ReturnType<typeof getEmailComponents>[T]['template']>;\n\nexport const sendEmail = async <T extends EmailType>({\n type,\n to,\n subject,\n locale,\n ...props\n}: SendEmailProps<T>) => {\n const resend = new Resend(process.env.RESEND_API_KEY);\n\n const emailComponents = getEmailComponents(locale);\n\n const { template, subject: baseSubject } = emailComponents[type];\n\n type EmailComponentType = (typeof emailComponents)[T]['template'];\n\n const EmailComponent: EmailComponentType = template;\n\n const react = <EmailComponent {...(props as any)} />;\n\n await resend.emails\n .send({\n from: 'Intlayer <no-reply@intlayer.org>',\n to,\n subject: subject ?? baseSubject,\n react,\n })\n .catch((err) => logger.error(err));\n\n logger.info(`Email sent ${type} to ${to}`);\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAiEA,MAAM,sBAAsB,YACzB;CACC,QAAQ;EACN,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,UAAU;EACR,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,eAAe;EACb,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,SAAS;EACP,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,WAAW;EACT,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,4BAA4B;EAC1B,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,4BAA4B;EAC1B,UAAU,EAAE;GACV,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACF,SAAS,EAAE;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACH;CACD,iCAAiC;EAC/B,UAAU,EAAE;GACV,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACF,SAAS,EAAE;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACH;CACD,0BAA0B;EACxB,UAAU,EAAE;GACV,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACF,SAAS,EAAE;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACH;CACD,mBAAmB;EACjB,UAAU,EAAE;GACV,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACF,SAAS,EAAE;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACH;CACF;AAWH,MAAa,YAAY,OAA4B,EACnD,MACA,IACA,SACA,QACA,GAAG,YACoB;CACvB,MAAM,SAAS,IAAI,OAAO,QAAQ,IAAI,eAAe;CAIrD,MAAM,EAAE,UAAU,SAAS,gBAFH,mBAAmB,OAAO,CAES;CAM3D,MAAM,QAAQ,oBAF6B,YAEb,GAAK,QAAiB;AAEpD,OAAM,OAAO,OACV,KAAK;EACJ,MAAM;EACN;EACA,SAAS,WAAW;EACpB;EACD,CAAC,CACD,OAAO,QAAQ,OAAO,MAAM,IAAI,CAAC;AAEpC,QAAO,KAAK,cAAc,KAAK,MAAM,KAAK"}
1
+ {"version":3,"file":"email.service.mjs","names":[],"sources":["../../../src/services/email.service.tsx"],"sourcesContent":["import {\n InviteUserEmailEN,\n InviteUserEmailES,\n InviteUserEmailFR,\n} from '@emails/InviteUserEmail';\nimport {\n MagicLinkEmailEN,\n MagicLinkEmailES,\n MagicLinkEmailFR,\n} from '@emails/MagicLinkEmail';\nimport {\n OAuthTokenCreatedEmailEN,\n OAuthTokenCreatedEmailES,\n OAuthTokenCreatedEmailFR,\n} from '@emails/OAuthTokenCreatedEmail';\nimport {\n PasswordChangeConfirmationEmailEN,\n PasswordChangeConfirmationEmailES,\n PasswordChangeConfirmationEmailFR,\n} from '@emails/PasswordChangeConfirmation';\nimport {\n ResetPasswordEmailEN,\n ResetPasswordEmailES,\n ResetPasswordEmailFR,\n} from '@emails/ResetUserPassword';\n\nimport {\n SubscriptionPaymentCancellationEN,\n SubscriptionPaymentCancellationES,\n SubscriptionPaymentCancellationFR,\n} from '@emails/SubscriptionPaymentCancellation';\nimport {\n SubscriptionPaymentErrorEN,\n SubscriptionPaymentErrorES,\n SubscriptionPaymentErrorFR,\n} from '@emails/SubscriptionPaymentError';\nimport {\n SubscriptionPaymentSuccessEN,\n SubscriptionPaymentSuccessES,\n SubscriptionPaymentSuccessFR,\n} from '@emails/SubscriptionPaymentSuccess';\nimport {\n ValidateUserEmailEN,\n ValidateUserEmailES,\n ValidateUserEmailFR,\n} from '@emails/ValidateUserEmail';\nimport {\n WelcomeEmailEN,\n WelcomeEmailES,\n WelcomeEmailFR,\n} from '@emails/Welcome';\nimport type { Locale } from '@intlayer/types';\nimport { logger } from '@logger';\nimport { t } from 'fastify-intlayer';\nimport type { ComponentProps, JSX } from 'react';\nimport { Resend } from 'resend';\n\ntype EmailComponentsType = (...props: any) => JSX.Element;\ntype EmailComponents = {\n [key: string]: {\n template: EmailComponentsType;\n subject: string;\n };\n};\n\nconst getEmailComponents = (locale?: Locale) =>\n ({\n invite: {\n template: t(\n {\n en: InviteUserEmailEN,\n fr: InviteUserEmailFR,\n es: InviteUserEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'You have been invited to join Intlayer',\n fr: 'Vous êtes invité à rejoindre Intlayer',\n es: 'Has sido invitado a unirte a Intlayer',\n },\n locale\n ),\n },\n validate: {\n template: t(\n {\n en: ValidateUserEmailEN,\n fr: ValidateUserEmailFR,\n es: ValidateUserEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Validate your email for Intlayer',\n fr: 'Validez votre email pour Intlayer',\n es: 'Valida tu correo electrónico para Intlayer',\n },\n locale\n ),\n },\n resetPassword: {\n template: t(\n {\n en: ResetPasswordEmailEN,\n fr: ResetPasswordEmailFR,\n es: ResetPasswordEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Reset your password for Intlayer',\n fr: 'Réinitialisez votre mot de passe pour Intlayer',\n es: 'Restablece tu contraseña para Intlayer',\n },\n locale\n ),\n },\n welcome: {\n template: t(\n {\n en: WelcomeEmailEN,\n fr: WelcomeEmailFR,\n es: WelcomeEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Welcome to Intlayer!',\n fr: 'Bienvenue chez Intlayer!',\n es: '¡Bienvenido a Intlayer!',\n },\n locale\n ),\n },\n magicLink: {\n template: t(\n {\n en: MagicLinkEmailEN,\n fr: MagicLinkEmailFR,\n es: MagicLinkEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Sign in to Intlayer',\n fr: 'Connectez-vous à Intlayer',\n es: 'Inicia sesión en Intlayer',\n },\n locale\n ),\n },\n passwordChangeConfirmation: {\n template: t(\n {\n en: PasswordChangeConfirmationEmailEN,\n fr: PasswordChangeConfirmationEmailFR,\n es: PasswordChangeConfirmationEmailES,\n },\n locale\n ),\n subject: t(\n {\n en: 'Your Intlayer password has been changed',\n fr: 'Votre mot de passe Intlayer a été modifié',\n es: 'Tu contraseña de Intlayer ha sido cambiada',\n },\n locale\n ),\n },\n subscriptionPaymentSuccess: {\n template: t({\n en: SubscriptionPaymentSuccessEN,\n fr: SubscriptionPaymentSuccessFR,\n es: SubscriptionPaymentSuccessES,\n }),\n subject: t({\n en: 'Your payment for Intlayer subscription is confirmed',\n fr: \"Votre paiement pour l'abonnement Intlayer est confirmé\",\n es: 'Tu pago por la suscripción de Intlayer ha sido confirmado',\n }),\n },\n subscriptionPaymentCancellation: {\n template: t({\n en: SubscriptionPaymentCancellationEN,\n fr: SubscriptionPaymentCancellationFR,\n es: SubscriptionPaymentCancellationES,\n }),\n subject: t({\n en: 'Your Intlayer subscription has been canceled',\n fr: 'Votre abonnement Intlayer a été annulé',\n es: 'Tu suscripción de Intlayer ha sido cancelada',\n }),\n },\n subscriptionPaymentError: {\n template: t({\n en: SubscriptionPaymentErrorEN,\n fr: SubscriptionPaymentErrorFR,\n es: SubscriptionPaymentErrorES,\n }),\n subject: t({\n en: 'There was an issue with your Intlayer subscription payment',\n fr: \"Un problème est survenu avec votre paiement pour l'abonnement Intlayer\",\n es: 'Hubo un problema con el pago de tu suscripción de Intlayer',\n }),\n },\n oAuthTokenCreated: {\n template: t({\n en: OAuthTokenCreatedEmailEN,\n fr: OAuthTokenCreatedEmailFR,\n es: OAuthTokenCreatedEmailES,\n }),\n subject: t({\n en: 'A third-party OAuth application has been added to your Intlayer account',\n fr: 'Une application OAuth tierce a été ajoutée à votre compte Intlayer',\n es: 'Una aplicación OAuth de terceros ha sido añadida a tu cuenta de Intlayer',\n }),\n },\n }) satisfies EmailComponents;\n\ntype EmailType = keyof ReturnType<typeof getEmailComponents>;\n\nexport type SendEmailProps<T extends EmailType> = {\n type: T;\n to: string;\n subject?: string;\n locale?: Locale;\n} & ComponentProps<ReturnType<typeof getEmailComponents>[T]['template']>;\n\nexport const sendEmail = async <T extends EmailType>({\n type,\n to,\n subject,\n locale,\n ...props\n}: SendEmailProps<T>) => {\n const resend = new Resend(process.env.RESEND_API_KEY);\n\n const emailComponents = getEmailComponents(locale);\n\n const { template, subject: baseSubject } = emailComponents[type];\n\n type EmailComponentType = (typeof emailComponents)[T]['template'];\n\n const EmailComponent: EmailComponentType = template;\n\n const react = <EmailComponent {...(props as any)} />;\n\n await resend.emails\n .send({\n from: 'Intlayer <no-reply@intlayer.org>',\n to,\n subject: subject ?? baseSubject,\n react,\n })\n .catch((err) => logger.error(err));\n\n logger.info(`Email sent ${type} to ${to}`);\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAiEA,MAAM,sBAAsB,YACzB;CACC,QAAQ;EACN,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,UAAU;EACR,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,eAAe;EACb,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,SAAS;EACP,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,WAAW;EACT,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,4BAA4B;EAC1B,UAAU,EACR;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACD,SAAS,EACP;GACE,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EACD,OACD;EACF;CACD,4BAA4B;EAC1B,UAAU,EAAE;GACV,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACF,SAAS,EAAE;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACH;CACD,iCAAiC;EAC/B,UAAU,EAAE;GACV,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACF,SAAS,EAAE;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACH;CACD,0BAA0B;EACxB,UAAU,EAAE;GACV,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACF,SAAS,EAAE;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACH;CACD,mBAAmB;EACjB,UAAU,EAAE;GACV,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACF,SAAS,EAAE;GACT,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC;EACH;CACF;AAWH,MAAa,YAAY,OAA4B,EACnD,MACA,IACA,SACA,QACA,GAAG,YACoB;CACvB,MAAM,SAAS,IAAI,OAAO,QAAQ,IAAI,eAAe;CAIrD,MAAM,EAAE,UAAU,SAAS,gBAFH,mBAAmB,OAAO,CAES;CAM3D,MAAM,QAAQ,oBAF6B,YAEb,GAAK,QAAiB;AAEpD,OAAM,OAAO,OACV,KAAK;EACJ,MAAM;EACN;EACA,SAAS,WAAW;EACpB;EACD,CAAC,CACD,OAAO,QAAQ,OAAO,MAAM,IAAI,CAAC;AAEpC,QAAO,KAAK,cAAc,KAAK,MAAM,KAAK"}