@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":"user.controller.mjs","names":["user: User | undefined","userService.createUser","userService.findUsers","userService.countUsers","userService.getUserById","userService.getUserByEmail","userService.updateUserById","userService.deleteUser","clients: Array<{ id: number; userId: string; res: ResponseWithSession }>"],"sources":["../../../src/controllers/user.controller.ts"],"sourcesContent":["import { logger } from '@logger';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { sendEmail } from '@services/email.service';\nimport * as userService from '@services/user.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport {\n getUserFiltersAndPagination,\n type UserFiltersParam,\n} from '@utils/filtersAndPagination/getUserFiltersAndPagination';\nimport { mapUsersToAPI, mapUserToAPI } from '@utils/mapper/user';\nimport { hasPermission } from '@utils/permissions';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport { t } from 'express-intlayer';\nimport type { User, UserAPI } from '@/types/user.types';\n\nexport type CreateUserBody = { email: string; password?: string };\nexport type CreateUserResult = ResponseData<UserAPI>;\n\n/**\n * Creates a new user.\n */\nexport const createUser = async (\n req: Request<any, any, User>,\n res: ResponseWithSession<CreateUserResult>,\n _next: NextFunction\n): Promise<void> => {\n const user: User | undefined = req.body;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n try {\n const newUser = await userService.createUser(user);\n\n await sendEmail({\n type: 'welcome',\n to: newUser.email,\n username: newUser.name,\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n });\n\n const formattedUser = mapUserToAPI(newUser);\n\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'User created',\n fr: 'Utilisateur créé',\n es: 'Usuario creado',\n }),\n description: t({\n en: 'User created successfully',\n fr: 'Utilisateur créé avec succès',\n es: 'Usuario creado con éxito',\n }),\n data: formattedUser,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type GetUsersParams = FiltersAndPagination<UserFiltersParam>;\nexport type GetUsersResult = PaginatedResponse<UserAPI>;\n\n/**\n * Retrieves a list of users based on filters and pagination.\n */\nexport const getUsers = async (\n req: Request<GetUsersParams>,\n res: ResponseWithSession<GetUsersResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, roles } = res.locals;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getUserFiltersAndPagination(req, res);\n\n try {\n const users = await userService.findUsers(\n filters,\n skip,\n pageSize,\n sortOptions\n );\n\n if (\n !hasPermission(\n roles,\n 'user:read'\n )({\n ...res.locals,\n targetUsers: users,\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const totalItems = await userService.countUsers(filters);\n\n const formattedUsers = mapUsersToAPI(users);\n\n const responseData = formatPaginatedResponse<UserAPI>({\n data: formattedUsers,\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type GetUserByIdParams = { userId: UserAPI['id'] };\nexport type GetUserByIdResult = ResponseData<UserAPI>;\n\nexport const getUserById = async (\n req: Request<GetUserByIdParams>,\n res: ResponseWithSession<GetUserByIdResult>,\n _next: NextFunction\n): Promise<void> => {\n const { userId } = req.params;\n\n try {\n const user = await userService.getUserById(userId);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n const formattedUser = mapUserToAPI(user);\n const responseData = formatResponse<UserAPI>({ data: formattedUser });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type GetUserByEmailParams = { email: string };\nexport type GetUserByEmailResult = ResponseData<UserAPI>;\n\nexport const getUserByEmail = async (\n req: Request<GetUserByEmailParams>,\n res: ResponseWithSession<GetUserByEmailResult>,\n _next: NextFunction\n): Promise<void> => {\n const { email } = req.params;\n const { roles } = res.locals;\n\n try {\n const user = await userService.getUserByEmail(email);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'user:read'\n )({\n ...res.locals,\n targetUsers: [user],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const formattedUser = mapUserToAPI(user);\n const responseData = formatResponse<UserAPI>({ data: formattedUser });\n\n res.json(responseData);\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type UpdateUserBody = Partial<UserAPI>;\nexport type UpdateUserResult = ResponseData<UserAPI>;\n\n/**\n * Updates user information (phone number, date of birth).\n */\nexport const updateUser = async (\n req: Request<any, any, UpdateUserBody | undefined>,\n res: ResponseWithSession<UpdateUserResult>,\n _next: NextFunction\n): Promise<void> => {\n const userData = req.body;\n const { user, roles } = res.locals;\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (typeof userData !== 'object') {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_DATA_NOT_FOUND');\n return;\n }\n\n if (!userData.id) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_INVALID_FIELDS');\n return;\n }\n\n const userDB = await userService.getUserById(userData.id);\n\n if (!userDB) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_FOUND');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'user:write'\n )({\n ...res.locals,\n targetUsers: [userDB],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const updatedUser = await userService.updateUserById(userDB.id, userData);\n\n logger.info(\n `User updated: Name: ${updatedUser.name}, id: ${String(updatedUser.id)}`\n );\n\n const formattedUser = mapUserToAPI(updatedUser);\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'User updated',\n fr: 'Utilisateur mis à jour',\n es: 'Usuario actualizado',\n }),\n description: t({\n en: 'User updated successfully',\n fr: 'Utilisateur mis à jour avec succès',\n es: 'Usuario actualizado con éxito',\n }),\n data: formattedUser,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type DeleteUserParams = { userId: string };\nexport type DeleteUserResult = ResponseData<UserAPI>;\n\n/**\n * Deletes a user based on the provided ID.\n */\nexport const deleteUser = async (\n req: Request<DeleteUserParams>,\n res: ResponseWithSession<DeleteUserResult>,\n _next: NextFunction\n): Promise<void> => {\n const { userId } = req.params;\n const { roles } = res.locals;\n\n try {\n const user = await userService.getUserById(userId);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_FOUND');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'user:admin'\n )({\n ...res.locals,\n targetUsers: [user],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n await userService.deleteUser(userId);\n\n const formattedUser = mapUserToAPI(user);\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'User deleted',\n fr: 'Utilisateur supprimé',\n es: 'Usuario eliminado',\n }),\n description: t({\n en: 'User deleted successfully',\n fr: 'Utilisateur supprimé avec succès',\n es: 'Usuario eliminado con éxito',\n }),\n data: formattedUser,\n });\n\n res.json(responseData);\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nlet clients: Array<{ id: number; userId: string; res: ResponseWithSession }> =\n [];\n\nexport const sendVerificationUpdate = (user: User) => {\n const filteredClients = clients.filter(\n (client) => String(client.userId) === String(user.id)\n );\n\n for (const client of filteredClients) {\n if (user.emailVerified) {\n client.res.write(\n `data: ${JSON.stringify({ userId: user.id, status: 'verified' })}\\n\\n`\n );\n }\n }\n};\n\nexport type VerifyEmailStatusSSEParams = { userId: string };\n\n/**\n * SSE to check the email verification status\n */\nexport const verifyEmailStatusSSE = async (\n req: Request<VerifyEmailStatusSSEParams, any, any>,\n res: ResponseWithSession\n) => {\n // Set headers for SSE\n res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');\n res.setHeader('Cache-Control', 'no-cache, no-transform');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('X-Accel-Buffering', 'no'); // For Nginx buffering\n\n // Send initial data to ensure the connection is open\n res.write(':\\n\\n'); // Comment to keep connection alive\n res.flushHeaders();\n\n const { userId } = req.params; // Get user ID from query parameters\n const clientId = Date.now();\n\n const user = await userService.getUserById(userId);\n\n if (!user) {\n logger.error(`User not found - User ID: ${userId}`);\n res.write(`data: ${JSON.stringify({ userId, status: 'error' })}\\n\\n`);\n res.end();\n return;\n }\n\n // Add client to the list\n const newClient = { id: clientId, userId, res };\n clients.push(newClient);\n\n sendVerificationUpdate(user);\n\n // Remove client on connection close\n req.on('close', () => {\n clients = clients.filter((client) => client.id !== clientId);\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"user.controller.mjs","names":["user: User | undefined","userService.createUser","userService.findUsers","userService.countUsers","userService.getUserById","userService.getUserByEmail","userService.updateUserById","userService.deleteUser","clients: Array<{\n id: number;\n userId: string;\n res: { raw: FastifyReply['raw'] };\n}>"],"sources":["../../../src/controllers/user.controller.ts"],"sourcesContent":["import { logger } from '@logger';\nimport { sendEmail } from '@services/email.service';\nimport * as userService from '@services/user.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport {\n getUserFiltersAndPagination,\n type UserFiltersParam,\n} from '@utils/filtersAndPagination/getUserFiltersAndPagination';\nimport { mapUsersToAPI, mapUserToAPI } from '@utils/mapper/user';\nimport { hasPermission } from '@utils/permissions';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { t } from 'fastify-intlayer';\nimport type { User, UserAPI } from '@/types/user.types';\n\nexport type CreateUserBody = { email: string; password?: string };\nexport type CreateUserResult = ResponseData<UserAPI>;\n\n/**\n * Creates a new user.\n */\nexport const createUser = async (\n request: FastifyRequest<{ Body: User }>,\n reply: FastifyReply\n): Promise<void> => {\n const user: User | undefined = request.body;\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n try {\n const newUser = await userService.createUser(user);\n\n await sendEmail({\n type: 'welcome',\n to: newUser.email,\n username: newUser.name,\n loginLink: `${process.env.APP_URL}/auth/login`,\n });\n\n const formattedUser = mapUserToAPI(newUser);\n\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'User created',\n fr: 'Utilisateur créé',\n es: 'Usuario creado',\n }),\n description: t({\n en: 'User created successfully',\n fr: 'Utilisateur créé avec succès',\n es: 'Usuario creado con éxito',\n }),\n data: formattedUser,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type GetUsersParams = FiltersAndPagination<UserFiltersParam>;\nexport type GetUsersResult = PaginatedResponse<UserAPI>;\n\n/**\n * Retrieves a list of users based on filters and pagination.\n */\nexport const getUsers = async (\n request: FastifyRequest<{ Querystring: GetUsersParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, roles } = request.locals || {};\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getUserFiltersAndPagination(request);\n\n try {\n const users = await userService.findUsers(\n filters,\n skip,\n pageSize,\n sortOptions\n );\n\n if (\n !hasPermission(\n roles || [],\n 'user:read'\n )({\n ...request.locals,\n targetUsers: users,\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const totalItems = await userService.countUsers(filters);\n\n const formattedUsers = mapUsersToAPI(users);\n\n const responseData = formatPaginatedResponse<UserAPI>({\n data: formattedUsers,\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type GetUserByIdParams = { userId: UserAPI['id'] };\nexport type GetUserByIdResult = ResponseData<UserAPI>;\n\nexport const getUserById = async (\n request: FastifyRequest<{ Params: GetUserByIdParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { userId } = request.params;\n\n try {\n const user = await userService.getUserById(userId);\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n const formattedUser = mapUserToAPI(user);\n const responseData = formatResponse<UserAPI>({ data: formattedUser });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type GetUserByEmailParams = { email: string };\nexport type GetUserByEmailResult = ResponseData<UserAPI>;\n\nexport const getUserByEmail = async (\n request: FastifyRequest<{ Params: GetUserByEmailParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { email } = request.params;\n const { roles } = request.locals || {};\n\n try {\n const user = await userService.getUserByEmail(email);\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (\n !hasPermission(\n roles || [],\n 'user:read'\n )({\n ...request.locals,\n targetUsers: [user],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const formattedUser = mapUserToAPI(user);\n const responseData = formatResponse<UserAPI>({ data: formattedUser });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type UpdateUserBody = Partial<UserAPI>;\nexport type UpdateUserResult = ResponseData<UserAPI>;\n\n/**\n * Updates user information (phone number, date of birth).\n */\nexport const updateUser = async (\n request: FastifyRequest<{ Body: UpdateUserBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const userData = request.body;\n const { user, roles } = request.locals || {};\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n if (typeof userData !== 'object') {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'USER_DATA_NOT_FOUND'\n );\n }\n\n if (!userData.id) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'USER_INVALID_FIELDS'\n );\n }\n\n const userDB = await userService.getUserById(userData.id);\n\n if (!userDB) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_FOUND');\n }\n\n if (\n !hasPermission(\n roles || [],\n 'user:write'\n )({\n ...request.locals,\n targetUsers: [userDB],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'PERMISSION_DENIED');\n }\n\n try {\n const updatedUser = await userService.updateUserById(userDB.id, userData);\n\n logger.info(\n `User updated: Name: ${updatedUser.name}, id: ${String(updatedUser.id)}`\n );\n\n const formattedUser = mapUserToAPI(updatedUser);\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'User updated',\n fr: 'Utilisateur mis à jour',\n es: 'Usuario actualizado',\n }),\n description: t({\n en: 'User updated successfully',\n fr: 'Utilisateur mis à jour avec succès',\n es: 'Usuario actualizado con éxito',\n }),\n data: formattedUser,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type DeleteUserParams = { userId: string };\nexport type DeleteUserResult = ResponseData<UserAPI>;\n\n/**\n * Deletes a user based on the provided ID.\n */\nexport const deleteUser = async (\n request: FastifyRequest<{ Params: DeleteUserParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { userId } = request.params;\n const { roles } = request.locals || {};\n\n try {\n const user = await userService.getUserById(userId);\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_FOUND');\n }\n\n if (\n !hasPermission(\n roles || [],\n 'user:admin'\n )({\n ...request.locals,\n targetUsers: [user],\n })\n ) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n await userService.deleteUser(userId);\n\n const formattedUser = mapUserToAPI(user);\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'User deleted',\n fr: 'Utilisateur supprimé',\n es: 'Usuario eliminado',\n }),\n description: t({\n en: 'User deleted successfully',\n fr: 'Utilisateur supprimé avec succès',\n es: 'Usuario eliminado con éxito',\n }),\n data: formattedUser,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nlet clients: Array<{\n id: number;\n userId: string;\n res: { raw: FastifyReply['raw'] };\n}> = [];\n\nexport const sendVerificationUpdate = (user: User) => {\n const filteredClients = clients.filter(\n (client) => String(client.userId) === String(user.id)\n );\n\n for (const client of filteredClients) {\n if (user.emailVerified) {\n client.res.raw.write(\n `data: ${JSON.stringify({ userId: user.id, status: 'verified' })}\\n\\n`\n );\n }\n }\n};\n\nexport type VerifyEmailStatusSSEParams = { userId: string };\n\n/**\n * SSE to check the email verification status\n */\nexport const verifyEmailStatusSSE = async (\n request: FastifyRequest<{ Params: VerifyEmailStatusSSEParams }>,\n reply: FastifyReply\n) => {\n // Set headers for SSE\n reply.raw.setHeader('Content-Type', 'text/event-stream;charset=utf-8');\n reply.raw.setHeader('Cache-Control', 'no-cache, no-transform');\n reply.raw.setHeader('Connection', 'keep-alive');\n reply.raw.setHeader('X-Accel-Buffering', 'no'); // For Nginx buffering\n\n // Send initial data to ensure the connection is open\n reply.raw.write(':\\n\\n'); // Comment to keep connection alive\n reply.raw.flushHeaders?.();\n\n const { userId } = request.params; // Get user ID from params\n const clientId = Date.now();\n\n const user = await userService.getUserById(userId);\n\n if (!user) {\n logger.error(`User not found - User ID: ${userId}`);\n reply.raw.write(`data: ${JSON.stringify({ userId, status: 'error' })}\\n\\n`);\n reply.raw.end();\n return;\n }\n\n // Add client to the list\n const newClient = { id: clientId, userId, res: { raw: reply.raw } };\n clients.push(newClient);\n\n sendVerificationUpdate(user);\n\n // Remove client on connection close\n request.raw.on('close', () => {\n clients = clients.filter((client) => client.id !== clientId);\n });\n};\n"],"mappings":"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"}
@@ -1,11 +1,14 @@
1
1
  import { HttpStatusCodes } from "./utils/httpStatusCodes.mjs";
2
2
  import { formatPaginatedResponse, formatResponse } from "./utils/responseData.mjs";
3
3
  import { getAiRoutes } from "./routes/ai.routes.mjs";
4
+ import { getBitbucketRoutes } from "./routes/bitbucket.routes.mjs";
4
5
  import { getDictionaryRoutes } from "./routes/dictionary.routes.mjs";
6
+ import { getGithubRoutes } from "./routes/github.routes.mjs";
7
+ import { getGitlabRoutes } from "./routes/gitlab.routes.mjs";
5
8
  import { getNewsletterRoutes } from "./routes/newsletter.routes.mjs";
6
9
  import { getOrganizationRoutes } from "./routes/organization.routes.mjs";
7
10
  import { getProjectRoutes } from "./routes/project.routes.mjs";
8
11
  import { getUserRoutes } from "./routes/user.routes.mjs";
9
12
  import { formatSession, getAuth } from "./utils/auth/getAuth.mjs";
10
13
 
11
- export { HttpStatusCodes, formatPaginatedResponse, formatResponse, formatSession, getAiRoutes, getAuth, getDictionaryRoutes, getNewsletterRoutes, getOrganizationRoutes, getProjectRoutes, getUserRoutes };
14
+ export { HttpStatusCodes, formatPaginatedResponse, formatResponse, formatSession, getAiRoutes, getAuth, getBitbucketRoutes, getDictionaryRoutes, getGithubRoutes, getGitlabRoutes, getNewsletterRoutes, getOrganizationRoutes, getProjectRoutes, getUserRoutes };
@@ -1,7 +1,11 @@
1
1
  import { logger } from "./logger/index.mjs";
2
2
  import { ipLimiter } from "./utils/rateLimiter.mjs";
3
3
  import { aiRoute, aiRouter } from "./routes/ai.routes.mjs";
4
+ import { connectDB } from "./utils/mongoDB/connectDB.mjs";
5
+ import { bitbucketRoute, bitbucketRouter } from "./routes/bitbucket.routes.mjs";
4
6
  import { dictionaryRoute, dictionaryRouter } from "./routes/dictionary.routes.mjs";
7
+ import { githubRoute, githubRouter } from "./routes/github.routes.mjs";
8
+ import { gitlabRoute, gitlabRouter } from "./routes/gitlab.routes.mjs";
5
9
  import { newsletterRoute, newsletterRouter } from "./routes/newsletter.routes.mjs";
6
10
  import { organizationRoute, organizationRouter } from "./routes/organization.routes.mjs";
7
11
  import { projectRoute, projectRouter } from "./routes/project.routes.mjs";
@@ -9,31 +13,31 @@ import { userRoute, userRouter } from "./routes/user.routes.mjs";
9
13
  import { getAuth } from "./utils/auth/getAuth.mjs";
10
14
  import { getOAuth2AccessToken } from "./controllers/oAuth2.controller.mjs";
11
15
  import { attachOAuthInstance, oAuth2Middleware } from "./middlewares/oAuth2.middleware.mjs";
12
- import { logAPIRequestURL } from "./middlewares/request.middleware.mjs";
13
16
  import { authMiddleware } from "./middlewares/sessionAuth.middleware.mjs";
14
17
  import { eventListenerRoute, eventListenerRouter } from "./routes/eventListener.routes.mjs";
15
18
  import { searchRoute, searchRouter } from "./routes/search.routes.mjs";
16
19
  import { stripeRoute, stripeRouter } from "./routes/stripe.routes.mjs";
17
20
  import { tagRoute, tagRouter } from "./routes/tags.routes.mjs";
18
21
  import { corsOptions } from "./utils/cors.mjs";
19
- import { connectDB } from "./utils/mongoDB/connectDB.mjs";
20
22
  import { stripeWebhook } from "./webhooks/stripe.webhook.mjs";
21
- import { intlayer, t } from "express-intlayer";
22
- import express from "express";
23
- import { toNodeHandler } from "better-auth/node";
24
- import compression from "compression";
25
- import cookieParser from "cookie-parser";
26
- import cors from "cors";
23
+ import { intlayer, t } from "fastify-intlayer";
24
+ import fastifyRateLimit from "@fastify/rate-limit";
25
+ import fastifyCompress from "@fastify/compress";
26
+ import fastifyCookie from "@fastify/cookie";
27
+ import fastifyCors from "@fastify/cors";
28
+ import fastifyFormbody from "@fastify/formbody";
29
+ import fastifyHelmet from "@fastify/helmet";
27
30
  import dotenv from "dotenv";
28
- import helmet from "helmet";
31
+ import Fastify from "fastify";
29
32
 
30
33
  //#region src/index.ts
31
34
  const startServer = async () => {
32
- const app = express();
33
- app.disable("x-powered-by");
34
- app.use(helmet());
35
- app.set("trust proxy", 1);
36
- const env = app.get("env");
35
+ const app = Fastify({
36
+ disableRequestLogging: true,
37
+ trustProxy: true,
38
+ ignoreTrailingSlash: true
39
+ });
40
+ const env = "development";
37
41
  logger.info(`run as ${env}`);
38
42
  dotenv.config({ path: [
39
43
  `.env.${env}.local`,
@@ -41,42 +45,102 @@ const startServer = async () => {
41
45
  ".env.local",
42
46
  ".env"
43
47
  ] });
44
- app.use(cookieParser());
45
- app.use(intlayer());
46
- app.use(/(.*)/, ipLimiter);
48
+ await app.register(fastifyHelmet, {
49
+ contentSecurityPolicy: false,
50
+ global: true
51
+ });
52
+ await app.register(fastifyCors, corsOptions);
53
+ await app.register(fastifyCompress);
54
+ await app.register(fastifyCookie);
55
+ await app.register(fastifyFormbody);
56
+ await app.register(intlayer);
57
+ await app.register(fastifyRateLimit, ipLimiter);
47
58
  const dbClient = await connectDB();
48
- app.post("/webhook/stripe", express.raw({ type: "application/json" }), stripeWebhook);
49
- app.use(compression());
50
- app.use(express.urlencoded({ extended: true }));
51
- app.use(cors(corsOptions));
52
- app.get("/", (_req, res) => {
53
- res.send(t({
59
+ await app.register(async (stripeScope) => {
60
+ stripeScope.addContentTypeParser("application/json", { parseAs: "buffer" }, (_req, body, done) => {
61
+ done(null, body);
62
+ });
63
+ stripeScope.post("/webhook/stripe", async (request, reply) => {
64
+ const rawBody = request.body;
65
+ await stripeWebhook({
66
+ ...request.raw,
67
+ body: rawBody,
68
+ headers: request.headers
69
+ }, reply);
70
+ });
71
+ });
72
+ app.get("/", async (_request, reply) => {
73
+ return reply.send(t({
54
74
  en: "Ok - locale: en",
55
75
  fr: "Ok - locale: fr",
56
76
  es: "Ok - locale: es"
57
77
  }));
58
78
  });
59
79
  const auth = getAuth(dbClient);
60
- app.all("/api/auth/{*rest}", toNodeHandler(auth));
61
- app.use(/(.*)/, authMiddleware(auth));
62
- app.use(/(.*)/, attachOAuthInstance);
80
+ app.route({
81
+ method: ["GET", "POST"],
82
+ url: "/api/auth/*",
83
+ async handler(request, reply) {
84
+ try {
85
+ const protocol = request.protocol;
86
+ const host = request.headers.host;
87
+ const url = new URL(request.url, `${protocol}://${host}`);
88
+ const headers = new Headers();
89
+ Object.entries(request.headers).forEach(([key, value]) => {
90
+ if (value) headers.append(key, String(value));
91
+ });
92
+ const req = new Request(url.toString(), {
93
+ method: request.method,
94
+ headers,
95
+ ...request.body ? { body: JSON.stringify(request.body) } : {}
96
+ });
97
+ const response = await auth.handler(req);
98
+ reply.status(response.status);
99
+ response.headers.forEach((value, key) => {
100
+ reply.header(key, value);
101
+ });
102
+ const responseBody = response.body ? await response.text() : null;
103
+ return reply.send(responseBody);
104
+ } catch (error) {
105
+ logger.error("Authentication Error:", error);
106
+ return reply.status(500).send({
107
+ error: "Internal authentication error",
108
+ code: "AUTH_FAILURE"
109
+ });
110
+ }
111
+ }
112
+ });
113
+ app.addHook("onRequest", authMiddleware(auth));
114
+ app.addHook("onRequest", attachOAuthInstance);
63
115
  app.post("/oauth2/token", getOAuth2AccessToken);
64
- app.use(/(.*)/, oAuth2Middleware);
65
- app.use(express.json());
66
- if (env === "development") app.use(logAPIRequestURL);
67
- app.use(userRoute, userRouter);
68
- app.use(organizationRoute, organizationRouter);
69
- app.use(projectRoute, projectRouter);
70
- app.use(tagRoute, tagRouter);
71
- app.use(dictionaryRoute, dictionaryRouter);
72
- app.use(stripeRoute, stripeRouter);
73
- app.use(aiRoute, aiRouter);
74
- app.use(eventListenerRoute, eventListenerRouter);
75
- app.use(searchRoute, searchRouter);
76
- app.use(newsletterRoute, newsletterRouter);
77
- app.listen(process.env.PORT, () => {
78
- logger.info(`Listening on port ${process.env.PORT}`);
116
+ app.addHook("preHandler", oAuth2Middleware);
117
+ if (env === "development") app.addHook("onRequest", async (request) => {
118
+ const queryDetails = {
119
+ params: request.params,
120
+ query: request.query,
121
+ body: request.body,
122
+ locals: request.locals
123
+ };
124
+ logger.info(`API Request - ${request.method} - ${request.url} - ${JSON.stringify(queryDetails, null, 2)}`);
125
+ });
126
+ await app.register(userRouter, { prefix: userRoute });
127
+ await app.register(organizationRouter, { prefix: organizationRoute });
128
+ await app.register(projectRouter, { prefix: projectRoute });
129
+ await app.register(tagRouter, { prefix: tagRoute });
130
+ await app.register(dictionaryRouter, { prefix: dictionaryRoute });
131
+ await app.register(stripeRouter, { prefix: stripeRoute });
132
+ await app.register(aiRouter, { prefix: aiRoute });
133
+ await app.register(eventListenerRouter, { prefix: eventListenerRoute });
134
+ await app.register(searchRouter, { prefix: searchRoute });
135
+ await app.register(newsletterRouter, { prefix: newsletterRoute });
136
+ await app.register(githubRouter, { prefix: githubRoute });
137
+ await app.register(gitlabRouter, { prefix: gitlabRoute });
138
+ await app.register(bitbucketRouter, { prefix: bitbucketRoute });
139
+ await app.listen({
140
+ port: Number(process.env.PORT) || 3100,
141
+ host: "0.0.0.0"
79
142
  });
143
+ logger.info(`Listening on port ${process.env.PORT || 3100}`);
80
144
  };
81
145
  startServer();
82
146
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["app: Express"],"sources":["../../src/index.ts"],"sourcesContent":["/// Controllers\nimport { getOAuth2AccessToken } from '@controllers/oAuth2.controller';\n// Middlewares\nimport {\n attachOAuthInstance,\n oAuth2Middleware,\n} from '@middlewares/oAuth2.middleware';\nimport { logAPIRequestURL } from '@middlewares/request.middleware';\nimport { authMiddleware } from '@middlewares/sessionAuth.middleware';\n// Routes\nimport { aiRoute, aiRouter } from '@routes/ai.routes';\nimport { dictionaryRoute, dictionaryRouter } from '@routes/dictionary.routes';\nimport {\n eventListenerRoute,\n eventListenerRouter,\n} from '@routes/eventListener.routes';\nimport { newsletterRoute, newsletterRouter } from '@routes/newsletter.routes';\nimport {\n organizationRoute,\n organizationRouter,\n} from '@routes/organization.routes';\nimport { projectRoute, projectRouter } from '@routes/project.routes';\nimport { searchRoute, searchRouter } from '@routes/search.routes';\nimport { stripeRoute, stripeRouter } from '@routes/stripe.routes';\nimport { tagRoute, tagRouter } from '@routes/tags.routes';\nimport { userRoute, userRouter } from '@routes/user.routes';\n// Utils\nimport { getAuth } from '@utils/auth/getAuth';\nimport { corsOptions } from '@utils/cors';\nimport { connectDB } from '@utils/mongoDB/connectDB';\nimport { ipLimiter } from '@utils/rateLimiter';\n// Webhooks\nimport { stripeWebhook } from '@webhooks/stripe.webhook';\n// Libraries\nimport { toNodeHandler } from 'better-auth/node';\nimport compression from 'compression';\nimport cookieParser from 'cookie-parser';\nimport cors from 'cors';\nimport dotenv from 'dotenv';\nimport express, { type Express } from 'express';\nimport { intlayer, t } from 'express-intlayer';\nimport helmet from 'helmet';\n/// Logger\nimport { logger } from './logger/index';\n\nconst startServer = async () => {\n const app: Express = express();\n\n // Headers security\n app.disable('x-powered-by'); // Disabled to prevent attackers from knowing that the app is running Express\n app.use(helmet());\n app.set('trust proxy', 1);\n\n // Environment variables\n const env = app.get('env');\n\n logger.info(`run as ${env}`);\n\n dotenv.config({\n path: [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env'],\n });\n\n // Parse incoming requests with cookies\n app.use(cookieParser());\n\n // Load internationalization request handler\n app.use(intlayer());\n\n // Rate limiter\n app.use(/(.*)/, ipLimiter);\n\n // Connect to MongoDB\n const dbClient = await connectDB();\n\n // Stripe\n app.post(\n '/webhook/stripe',\n express.raw({ type: 'application/json' }),\n stripeWebhook\n );\n\n // Compress all HTTP responses\n app.use(compression());\n\n // Parse incoming requests with urlencoded payloads\n app.use(express.urlencoded({ extended: true }));\n\n // CORS\n app.use(cors(corsOptions));\n\n // Liveness check\n app.get('/', (_req, res) => {\n res.send(\n t({\n en: 'Ok - locale: en',\n fr: 'Ok - locale: fr',\n es: 'Ok - locale: es',\n })\n );\n });\n\n // Session Auth\n const auth = getAuth(dbClient as any);\n\n app.all('/api/auth/{*rest}', toNodeHandler(auth));\n app.use(/(.*)/, authMiddleware(auth));\n\n // oAuth2 Auth\n app.use(/(.*)/, attachOAuthInstance);\n app.post('/oauth2/token', getOAuth2AccessToken); // Route to get the token\n app.use(/(.*)/, oAuth2Middleware);\n\n // Body parser\n app.use(express.json()); // Should be placed after auth. Attach body to next routes\n\n // debug\n const isDev = env === 'development';\n if (isDev) {\n app.use(logAPIRequestURL);\n }\n\n // Routes\n app.use(userRoute, userRouter);\n app.use(organizationRoute, organizationRouter);\n app.use(projectRoute, projectRouter);\n app.use(tagRoute, tagRouter);\n app.use(dictionaryRoute, dictionaryRouter);\n app.use(stripeRoute, stripeRouter);\n app.use(aiRoute, aiRouter);\n app.use(eventListenerRoute, eventListenerRouter);\n app.use(searchRoute, searchRouter);\n app.use(newsletterRoute, newsletterRouter);\n\n // Server\n app.listen(process.env.PORT, () => {\n logger.info(`Listening on port ${process.env.PORT}`);\n });\n};\n\nstartServer();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,MAAM,cAAc,YAAY;CAC9B,MAAMA,MAAe,SAAS;AAG9B,KAAI,QAAQ,eAAe;AAC3B,KAAI,IAAI,QAAQ,CAAC;AACjB,KAAI,IAAI,eAAe,EAAE;CAGzB,MAAM,MAAM,IAAI,IAAI,MAAM;AAE1B,QAAO,KAAK,UAAU,MAAM;AAE5B,QAAO,OAAO,EACZ,MAAM;EAAC,QAAQ,IAAI;EAAS,QAAQ;EAAO;EAAc;EAAO,EACjE,CAAC;AAGF,KAAI,IAAI,cAAc,CAAC;AAGvB,KAAI,IAAI,UAAU,CAAC;AAGnB,KAAI,IAAI,QAAQ,UAAU;CAG1B,MAAM,WAAW,MAAM,WAAW;AAGlC,KAAI,KACF,mBACA,QAAQ,IAAI,EAAE,MAAM,oBAAoB,CAAC,EACzC,cACD;AAGD,KAAI,IAAI,aAAa,CAAC;AAGtB,KAAI,IAAI,QAAQ,WAAW,EAAE,UAAU,MAAM,CAAC,CAAC;AAG/C,KAAI,IAAI,KAAK,YAAY,CAAC;AAG1B,KAAI,IAAI,MAAM,MAAM,QAAQ;AAC1B,MAAI,KACF,EAAE;GACA,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CAAC,CACH;GACD;CAGF,MAAM,OAAO,QAAQ,SAAgB;AAErC,KAAI,IAAI,qBAAqB,cAAc,KAAK,CAAC;AACjD,KAAI,IAAI,QAAQ,eAAe,KAAK,CAAC;AAGrC,KAAI,IAAI,QAAQ,oBAAoB;AACpC,KAAI,KAAK,iBAAiB,qBAAqB;AAC/C,KAAI,IAAI,QAAQ,iBAAiB;AAGjC,KAAI,IAAI,QAAQ,MAAM,CAAC;AAIvB,KADc,QAAQ,cAEpB,KAAI,IAAI,iBAAiB;AAI3B,KAAI,IAAI,WAAW,WAAW;AAC9B,KAAI,IAAI,mBAAmB,mBAAmB;AAC9C,KAAI,IAAI,cAAc,cAAc;AACpC,KAAI,IAAI,UAAU,UAAU;AAC5B,KAAI,IAAI,iBAAiB,iBAAiB;AAC1C,KAAI,IAAI,aAAa,aAAa;AAClC,KAAI,IAAI,SAAS,SAAS;AAC1B,KAAI,IAAI,oBAAoB,oBAAoB;AAChD,KAAI,IAAI,aAAa,aAAa;AAClC,KAAI,IAAI,iBAAiB,iBAAiB;AAG1C,KAAI,OAAO,QAAQ,IAAI,YAAY;AACjC,SAAO,KAAK,qBAAqB,QAAQ,IAAI,OAAO;GACpD;;AAGJ,aAAa"}
1
+ {"version":3,"file":"index.mjs","names":["app: FastifyInstance"],"sources":["../../src/index.ts"],"sourcesContent":["/// Controllers\nimport { getOAuth2AccessToken } from '@controllers/oAuth2.controller';\nimport fastifyCompress from '@fastify/compress';\nimport fastifyCookie from '@fastify/cookie';\nimport fastifyCors from '@fastify/cors';\nimport fastifyFormbody from '@fastify/formbody';\nimport fastifyHelmet from '@fastify/helmet';\nimport fastifyRateLimit from '@fastify/rate-limit';\n// Middlewares\nimport {\n attachOAuthInstance,\n oAuth2Middleware,\n} from '@middlewares/oAuth2.middleware';\nimport { authMiddleware } from '@middlewares/sessionAuth.middleware';\n// Routes\nimport { aiRoute, aiRouter } from '@routes/ai.routes';\nimport { bitbucketRoute, bitbucketRouter } from '@routes/bitbucket.routes';\nimport { dictionaryRoute, dictionaryRouter } from '@routes/dictionary.routes';\nimport {\n eventListenerRoute,\n eventListenerRouter,\n} from '@routes/eventListener.routes';\nimport { githubRoute, githubRouter } from '@routes/github.routes';\nimport { gitlabRoute, gitlabRouter } from '@routes/gitlab.routes';\nimport { newsletterRoute, newsletterRouter } from '@routes/newsletter.routes';\nimport {\n organizationRoute,\n organizationRouter,\n} from '@routes/organization.routes';\nimport { projectRoute, projectRouter } from '@routes/project.routes';\nimport { searchRoute, searchRouter } from '@routes/search.routes';\nimport { stripeRoute, stripeRouter } from '@routes/stripe.routes';\nimport { tagRoute, tagRouter } from '@routes/tags.routes';\nimport { userRoute, userRouter } from '@routes/user.routes';\n// Utils\nimport { getAuth } from '@utils/auth/getAuth';\nimport { corsOptions } from '@utils/cors';\nimport { connectDB } from '@utils/mongoDB/connectDB';\nimport { ipLimiter } from '@utils/rateLimiter';\n// Webhooks\nimport { stripeWebhook } from '@webhooks/stripe.webhook';\n// Libraries\nimport dotenv from 'dotenv';\nimport Fastify, { type FastifyInstance } from 'fastify';\nimport { intlayer, t } from 'fastify-intlayer';\n/// Logger\nimport { logger } from './logger/index';\n\nconst startServer = async () => {\n const app: FastifyInstance = Fastify({\n disableRequestLogging: true,\n trustProxy: true,\n ignoreTrailingSlash: true,\n });\n\n // Environment variables\n const env = process.env.NODE_ENV || 'development';\n\n logger.info(`run as ${env}`);\n\n dotenv.config({\n path: [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env'],\n });\n\n // Security Headers\n await app.register(fastifyHelmet, {\n contentSecurityPolicy: false,\n global: true,\n });\n\n // CORS\n await app.register(fastifyCors, corsOptions);\n\n // Compression\n await app.register(fastifyCompress);\n\n // Cookie Parser\n await app.register(fastifyCookie);\n\n // Parse application/x-www-form-urlencoded\n await app.register(fastifyFormbody);\n\n // Load internationalization request handler\n await app.register(intlayer);\n\n // Rate limiter\n await app.register(fastifyRateLimit, ipLimiter);\n\n // Connect to MongoDB\n const dbClient = await connectDB();\n\n // Stripe webhook (needs raw body)\n // Register a content type parser for raw body\n await app.register(async (stripeScope) => {\n stripeScope.addContentTypeParser(\n 'application/json',\n { parseAs: 'buffer' },\n (_req, body, done) => {\n done(null, body);\n }\n );\n\n stripeScope.post('/webhook/stripe', async (request, reply) => {\n // For Stripe webhooks, we need the raw body as a Buffer\n // Fastify will parse it as buffer when content-type parser is set\n const rawBody = request.body as Buffer;\n // Create a mock request object for the webhook handler\n const mockReq = {\n ...request.raw,\n body: rawBody,\n headers: request.headers,\n } as any;\n await stripeWebhook(mockReq, reply as any);\n });\n });\n\n // Liveness check\n app.get('/', async (_request, reply) => {\n return reply.send(\n t({\n en: 'Ok - locale: en',\n fr: 'Ok - locale: fr',\n es: 'Ok - locale: es',\n })\n );\n });\n\n // Session Auth\n const auth = getAuth(dbClient as any);\n\n // Better Auth handler - Using Fetch API approach for Fastify compatibility\n app.route({\n method: ['GET', 'POST'],\n url: '/api/auth/*',\n async handler(request, reply) {\n try {\n // This respects the X-Forwarded-Proto header from Coolify\n const protocol = request.protocol;\n const host = request.headers.host;\n\n // Construct request URL using the detected protocol\n const url = new URL(request.url, `${protocol}://${host}`);\n\n const headers = new Headers();\n Object.entries(request.headers).forEach(([key, value]) => {\n if (value) headers.append(key, String(value));\n });\n\n // Create Fetch API-compatible request\n const req = new Request(url.toString(), {\n method: request.method,\n headers,\n ...(request.body ? { body: JSON.stringify(request.body) } : {}),\n });\n\n // Process authentication request\n const response = await auth.handler(req);\n\n // Forward response to client\n reply.status(response.status);\n response.headers.forEach((value, key) => {\n reply.header(key, value);\n });\n\n const responseBody = response.body ? await response.text() : null;\n return reply.send(responseBody);\n } catch (error) {\n logger.error('Authentication Error:', error);\n return reply.status(500).send({\n error: 'Internal authentication error',\n code: 'AUTH_FAILURE',\n });\n }\n },\n });\n\n // Register auth middleware as a hook\n app.addHook('onRequest', authMiddleware(auth));\n\n // // oAuth2 Auth\n app.addHook('onRequest', attachOAuthInstance);\n app.post('/oauth2/token', getOAuth2AccessToken); // Route to get the token\n app.addHook('preHandler', oAuth2Middleware);\n\n // // debug\n const isDev = env === 'development';\n if (isDev) {\n app.addHook('onRequest', async (request) => {\n const queryDetails = {\n params: request.params,\n query: request.query,\n body: request.body,\n locals: request.locals,\n };\n\n logger.info(\n `API Request - ${request.method} - ${request.url} - ${JSON.stringify(queryDetails, null, 2)}`\n );\n });\n }\n\n // Routes\n await app.register(userRouter, { prefix: userRoute });\n await app.register(organizationRouter, { prefix: organizationRoute });\n await app.register(projectRouter, { prefix: projectRoute });\n await app.register(tagRouter, { prefix: tagRoute });\n await app.register(dictionaryRouter, { prefix: dictionaryRoute });\n await app.register(stripeRouter, { prefix: stripeRoute });\n await app.register(aiRouter, { prefix: aiRoute });\n await app.register(eventListenerRouter, { prefix: eventListenerRoute });\n await app.register(searchRouter, { prefix: searchRoute });\n await app.register(newsletterRouter, { prefix: newsletterRoute });\n await app.register(githubRouter, { prefix: githubRoute });\n await app.register(gitlabRouter, { prefix: gitlabRoute });\n await app.register(bitbucketRouter, { prefix: bitbucketRoute });\n\n // Server\n await app.listen({\n port: Number(process.env.PORT) || 3100,\n host: '0.0.0.0',\n });\n logger.info(`Listening on port ${process.env.PORT || 3100}`);\n};\n\nstartServer();\n"],"mappings":"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"}
@@ -11,27 +11,32 @@ const oauth = new OAuth2Server({
11
11
  accessTokenLifetime: 3600,
12
12
  allowBearerTokensInQueryString: true
13
13
  });
14
- const attachOAuthInstance = async (req, _res, next) => {
15
- req.oauth = oauth;
16
- next();
14
+ const attachOAuthInstance = async (request, _reply) => {
15
+ request.oauth = oauth;
17
16
  };
18
- const oAuth2Middleware = async (req, res, next) => {
19
- if (typeof res.locals.authType !== "undefined") return next();
17
+ const oAuth2Middleware = async (request, reply) => {
18
+ if (!request.locals) request.locals = {};
19
+ if (typeof request.locals?.authType !== "undefined") return;
20
20
  try {
21
- if (!Boolean(req.headers.authorization)) return next();
22
- const oauthRequest = new Request(req);
23
- const oauthResponse = new Response(res);
24
- const formattedSession = formatSession(await getOAuth2AccessTokenContext(await validateOAuth2AccessToken((await req.oauth.authenticate(oauthRequest, oauthResponse, authenticateOptions)).accessToken)));
25
- res.locals.authType = "session";
26
- Object.entries(formattedSession).forEach(([key, value]) => {
27
- res.locals[key] = value;
21
+ if (!Boolean(request.headers.authorization)) return;
22
+ const oauthRequest = new Request({
23
+ headers: request.headers,
24
+ method: request.method,
25
+ query: request.query,
26
+ body: request.body
28
27
  });
28
+ const oauthResponse = new Response(reply.raw);
29
+ const formattedSession = formatSession(await getOAuth2AccessTokenContext(await validateOAuth2AccessToken((await request.oauth.authenticate(oauthRequest, oauthResponse, authenticateOptions)).accessToken)));
30
+ const locals = request.locals;
31
+ if (locals) {
32
+ locals.authType = "session";
33
+ for (const [key, value] of Object.entries(formattedSession)) locals[key] = value;
34
+ }
29
35
  logger.info("OAuth2 bearer token authenticated", formattedSession.user.email);
30
36
  } catch (error) {
31
- ErrorHandler.handleAppErrorResponse(res, error);
37
+ ErrorHandler.handleAppErrorResponse(reply, error);
32
38
  return;
33
39
  }
34
- next();
35
40
  };
36
41
 
37
42
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"oAuth2.middleware.mjs","names":["OAuthRequest","OAuthResponse"],"sources":["../../../src/middlewares/oAuth2.middleware.ts"],"sourcesContent":["import { logger } from '@logger';\nimport {\n getOAuth2AccessTokenContext,\n validateOAuth2AccessToken,\n} from '@services/oAuth2.service';\nimport { formatSession } from '@utils/auth/getAuth';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { authenticateOptions, getAuthModel } from '@utils/oAuth2';\nimport type { NextFunction, Request, Response } from 'express';\nimport OAuth2Server, {\n Request as OAuthRequest,\n Response as OAuthResponse,\n} from 'oauth2-server';\n\n// Configuration of the OAuth server\nconst oauth = new OAuth2Server({\n model: getAuthModel(),\n accessTokenLifetime: 60 * 60, // 1 hour\n allowBearerTokensInQueryString: true,\n});\n\nexport type RequestWithOAuth2Information<\n P = any,\n ResBody = any,\n ReqBody = any,\n ReqQuery = qs.ParsedQs,\n> = Request<P, ResBody, ReqBody, ReqQuery> & {\n oauth: OAuth2Server;\n};\n\nexport const attachOAuthInstance = async (\n req: Request,\n _res: Response,\n next: NextFunction\n) => {\n // Attach the instance OAuth to the requests\n (req as RequestWithOAuth2Information).oauth = oauth;\n\n next();\n};\n\n// Middleware to authenticate requests\nexport const oAuth2Middleware = async (\n req: Request,\n res: Response,\n next: NextFunction\n): Promise<void> => {\n if (typeof res.locals.authType !== 'undefined') {\n // Skip if user is already authenticated (ex: session)\n return next();\n }\n\n try {\n const hasToken = Boolean(req.headers.authorization);\n\n if (!hasToken) {\n // If the request does not have a token, skip the oAuth2 authentication\n // Necessary because the oAuth2 library will throw an error if the token is not present\n return next();\n }\n\n // Authenticate the request using OAuth2\n const oauthRequest = new OAuthRequest(req);\n\n const oauthResponse = new OAuthResponse(res);\n\n const oAuthToken = await (\n req as RequestWithOAuth2Information\n ).oauth.authenticate(oauthRequest, oauthResponse, authenticateOptions);\n\n const validatedToken = await validateOAuth2AccessToken(\n oAuthToken.accessToken\n );\n\n const result = await getOAuth2AccessTokenContext(validatedToken);\n\n const formattedSession = formatSession(result);\n res.locals.authType = 'session';\n\n // Attach the session to the response locals\n Object.entries(formattedSession).forEach(([key, value]) => {\n (res.locals as any)[key] = value;\n });\n\n logger.info(\n 'OAuth2 bearer token authenticated',\n formattedSession.user.email\n );\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n\n return;\n }\n\n next();\n};\n"],"mappings":";;;;;;;;AAeA,MAAM,QAAQ,IAAI,aAAa;CAC7B,OAAO,cAAc;CACrB,qBAAqB;CACrB,gCAAgC;CACjC,CAAC;AAWF,MAAa,sBAAsB,OACjC,KACA,MACA,SACG;AAEH,CAAC,IAAqC,QAAQ;AAE9C,OAAM;;AAIR,MAAa,mBAAmB,OAC9B,KACA,KACA,SACkB;AAClB,KAAI,OAAO,IAAI,OAAO,aAAa,YAEjC,QAAO,MAAM;AAGf,KAAI;AAGF,MAAI,CAFa,QAAQ,IAAI,QAAQ,cAAc,CAKjD,QAAO,MAAM;EAIf,MAAM,eAAe,IAAIA,QAAa,IAAI;EAE1C,MAAM,gBAAgB,IAAIC,SAAc,IAAI;EAY5C,MAAM,mBAAmB,cAFV,MAAM,4BAJE,MAAM,2BAJV,MACjB,IACA,MAAM,aAAa,cAAc,eAAe,oBAAoB,EAGzD,YACZ,CAE+D,CAElB;AAC9C,MAAI,OAAO,WAAW;AAGtB,SAAO,QAAQ,iBAAiB,CAAC,SAAS,CAAC,KAAK,WAAW;AACzD,GAAC,IAAI,OAAe,OAAO;IAC3B;AAEF,SAAO,KACL,qCACA,iBAAiB,KAAK,MACvB;UACM,OAAO;AACd,eAAa,uBAAuB,KAAK,MAAkB;AAE3D;;AAGF,OAAM"}
1
+ {"version":3,"file":"oAuth2.middleware.mjs","names":["OAuthRequest","OAuthResponse"],"sources":["../../../src/middlewares/oAuth2.middleware.ts"],"sourcesContent":["import { logger } from '@logger';\nimport {\n getOAuth2AccessTokenContext,\n validateOAuth2AccessToken,\n} from '@services/oAuth2.service';\nimport { formatSession } from '@utils/auth/getAuth';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { authenticateOptions, getAuthModel } from '@utils/oAuth2';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport OAuth2Server, {\n Request as OAuthRequest,\n Response as OAuthResponse,\n} from 'oauth2-server';\n\n// Configuration of the OAuth server\nconst oauth = new OAuth2Server({\n model: getAuthModel(),\n accessTokenLifetime: 60 * 60, // 1 hour\n allowBearerTokensInQueryString: true,\n});\n\n// Extend FastifyRequest to include oauth\ndeclare module 'fastify' {\n interface FastifyRequest {\n oauth?: OAuth2Server;\n }\n}\n\nexport type RequestWithOAuth2Information = FastifyRequest & {\n oauth: OAuth2Server;\n};\n\nexport const attachOAuthInstance = async (\n request: FastifyRequest,\n _reply: FastifyReply\n) => {\n // Attach the instance OAuth to the requests\n (request as RequestWithOAuth2Information).oauth = oauth;\n};\n\n// Middleware to authenticate requests\nexport const oAuth2Middleware = async (\n request: FastifyRequest,\n reply: FastifyReply\n): Promise<void> => {\n if (!request.locals) {\n request.locals = {} as any;\n }\n\n if (typeof request.locals?.authType !== 'undefined') {\n // Skip if user is already authenticated (ex: session)\n return;\n }\n\n try {\n const hasToken = Boolean(request.headers.authorization);\n\n if (!hasToken) {\n // If the request does not have a token, skip the oAuth2 authentication\n // Necessary because the oAuth2 library will throw an error if the token is not present\n return;\n }\n\n // Authenticate the request using OAuth2\n const oauthRequest = new OAuthRequest({\n headers: request.headers,\n method: request.method,\n query: request.query as any,\n body: request.body as any,\n });\n const oauthResponse = new OAuthResponse(reply.raw);\n\n const oAuthToken = await (\n request as RequestWithOAuth2Information\n ).oauth.authenticate(oauthRequest, oauthResponse, authenticateOptions);\n\n const validatedToken = await validateOAuth2AccessToken(\n oAuthToken.accessToken\n );\n\n const result = await getOAuth2AccessTokenContext(validatedToken);\n\n const formattedSession = formatSession(result);\n const locals = request.locals;\n if (locals) {\n locals.authType = 'session';\n\n // Attach the session to the request locals\n for (const [key, value] of Object.entries(formattedSession)) {\n (locals as any)[key] = value;\n }\n }\n\n logger.info(\n 'OAuth2 bearer token authenticated',\n formattedSession.user.email\n );\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(reply as any, error as AppError);\n return;\n }\n};\n"],"mappings":";;;;;;;;AAeA,MAAM,QAAQ,IAAI,aAAa;CAC7B,OAAO,cAAc;CACrB,qBAAqB;CACrB,gCAAgC;CACjC,CAAC;AAaF,MAAa,sBAAsB,OACjC,SACA,WACG;AAEH,CAAC,QAAyC,QAAQ;;AAIpD,MAAa,mBAAmB,OAC9B,SACA,UACkB;AAClB,KAAI,CAAC,QAAQ,OACX,SAAQ,SAAS,EAAE;AAGrB,KAAI,OAAO,QAAQ,QAAQ,aAAa,YAEtC;AAGF,KAAI;AAGF,MAAI,CAFa,QAAQ,QAAQ,QAAQ,cAAc,CAKrD;EAIF,MAAM,eAAe,IAAIA,QAAa;GACpC,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,OAAO,QAAQ;GACf,MAAM,QAAQ;GACf,CAAC;EACF,MAAM,gBAAgB,IAAIC,SAAc,MAAM,IAAI;EAYlD,MAAM,mBAAmB,cAFV,MAAM,4BAJE,MAAM,2BAJV,MACjB,QACA,MAAM,aAAa,cAAc,eAAe,oBAAoB,EAGzD,YACZ,CAE+D,CAElB;EAC9C,MAAM,SAAS,QAAQ;AACvB,MAAI,QAAQ;AACV,UAAO,WAAW;AAGlB,QAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,iBAAiB,CACzD,CAAC,OAAe,OAAO;;AAI3B,SAAO,KACL,qCACA,iBAAiB,KAAK,MACvB;UACM,OAAO;AACd,eAAa,uBAAuB,OAAc,MAAkB;AACpE"}
@@ -2,18 +2,17 @@ import { formatSession } from "../utils/auth/getAuth.mjs";
2
2
  import { fromNodeHeaders } from "better-auth/node";
3
3
 
4
4
  //#region src/middlewares/sessionAuth.middleware.ts
5
- const authMiddleware = (auth) => async (req, res, next) => {
6
- if (typeof res.locals.authType !== "undefined") return next();
7
- const session = await auth.api.getSession({ headers: fromNodeHeaders(req.headers) });
5
+ const authMiddleware = (auth) => async (request, reply) => {
6
+ if (!request.locals) request.locals = {};
7
+ if (typeof request.locals.authType !== "undefined") return;
8
+ const session = await auth.api.getSession({ headers: fromNodeHeaders(request.headers) });
8
9
  if (session) {
9
10
  const formattedSession = formatSession(session);
10
- res.locals.authType = "session";
11
+ request.locals.authType = "session";
11
12
  Object.entries(formattedSession).forEach(([key, value]) => {
12
- res.locals[key] = value;
13
+ request.locals[key] = value;
13
14
  });
14
- return next();
15
15
  }
16
- next();
17
16
  };
18
17
 
19
18
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"sessionAuth.middleware.mjs","names":[],"sources":["../../../src/middlewares/sessionAuth.middleware.ts"],"sourcesContent":["import { type Auth, formatSession } from '@utils/auth/getAuth';\nimport { fromNodeHeaders } from 'better-auth/node';\nimport type { NextFunction, Request, Response } from 'express';\nimport type { Session } from '@/types/session.types';\n\nexport type ResponseWithSession<\n ResBody = any,\n Locals extends Record<string, any> = Record<string, any> & Session,\n> = Response<ResBody, Locals>;\n\nexport const authMiddleware =\n (auth: Auth) => async (req: Request, res: Response, next: NextFunction) => {\n if (typeof res.locals.authType !== 'undefined') {\n // Skip if user is already authenticated (ex: oAuth2)\n return next();\n }\n\n const session = await auth.api.getSession({\n headers: fromNodeHeaders(req.headers),\n });\n\n if (session) {\n const formattedSession = formatSession(session);\n res.locals.authType = 'session';\n\n // Attach the session to the response locals\n Object.entries(formattedSession).forEach(([key, value]) => {\n (res.locals as any)[key] = value;\n });\n\n return next();\n }\n\n next();\n };\n"],"mappings":";;;;AAUA,MAAa,kBACV,SAAe,OAAO,KAAc,KAAe,SAAuB;AACzE,KAAI,OAAO,IAAI,OAAO,aAAa,YAEjC,QAAO,MAAM;CAGf,MAAM,UAAU,MAAM,KAAK,IAAI,WAAW,EACxC,SAAS,gBAAgB,IAAI,QAAQ,EACtC,CAAC;AAEF,KAAI,SAAS;EACX,MAAM,mBAAmB,cAAc,QAAQ;AAC/C,MAAI,OAAO,WAAW;AAGtB,SAAO,QAAQ,iBAAiB,CAAC,SAAS,CAAC,KAAK,WAAW;AACzD,GAAC,IAAI,OAAe,OAAO;IAC3B;AAEF,SAAO,MAAM;;AAGf,OAAM"}
1
+ {"version":3,"file":"sessionAuth.middleware.mjs","names":[],"sources":["../../../src/middlewares/sessionAuth.middleware.ts"],"sourcesContent":["import { type Auth, formatSession } from '@utils/auth/getAuth';\nimport { fromNodeHeaders } from 'better-auth/node';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { Session } from '@/types/session.types';\n\n// Extend FastifyRequest to include locals\ndeclare module 'fastify' {\n interface FastifyRequest {\n locals?: {\n authType?: string;\n [key: string]: any;\n } & Session;\n }\n}\n\nexport type ResponseWithSession<\n ResBody = any,\n Locals extends Record<string, any> = Record<string, any> & Session,\n> = FastifyReply & {\n locals: Locals;\n};\n\nexport const authMiddleware =\n (auth: Auth) => async (request: FastifyRequest, reply: FastifyReply) => {\n if (!request.locals) {\n request.locals = {};\n }\n\n if (typeof request.locals.authType !== 'undefined') {\n // Skip if user is already authenticated (ex: oAuth2)\n return;\n }\n\n const session = await auth.api.getSession({\n headers: fromNodeHeaders(request.headers),\n });\n\n if (session) {\n const formattedSession = formatSession(session);\n request.locals.authType = 'session';\n\n // Attach the session to the request locals\n Object.entries(formattedSession).forEach(([key, value]) => {\n (request.locals as any)[key] = value;\n });\n }\n };\n"],"mappings":";;;;AAsBA,MAAa,kBACV,SAAe,OAAO,SAAyB,UAAwB;AACtE,KAAI,CAAC,QAAQ,OACX,SAAQ,SAAS,EAAE;AAGrB,KAAI,OAAO,QAAQ,OAAO,aAAa,YAErC;CAGF,MAAM,UAAU,MAAM,KAAK,IAAI,WAAW,EACxC,SAAS,gBAAgB,QAAQ,QAAQ,EAC1C,CAAC;AAEF,KAAI,SAAS;EACX,MAAM,mBAAmB,cAAc,QAAQ;AAC/C,UAAQ,OAAO,WAAW;AAG1B,SAAO,QAAQ,iBAAiB,CAAC,SAAS,CAAC,KAAK,WAAW;AACzD,GAAC,QAAQ,OAAe,OAAO;IAC/B"}
@@ -1,9 +1,8 @@
1
1
  import { askDocQuestion, auditContentDeclaration, auditContentDeclarationField, auditContentDeclarationMetadata, auditTag, autocomplete, customQuery, getDiscussions, translateJSON } from "../controllers/ai.controller.mjs";
2
2
  import { unauthenticatedChatBotLimiter } from "../utils/rateLimiter.mjs";
3
- import { Router } from "express";
3
+ import fastifyRateLimit from "@fastify/rate-limit";
4
4
 
5
5
  //#region src/routes/ai.routes.ts
6
- const aiRouter = Router();
7
6
  const aiRoute = "/api/ai";
8
7
  const baseURL = () => `${process.env.BACKEND_URL}${aiRoute}`;
9
8
  const getAiRoutes = () => ({
@@ -53,19 +52,24 @@ const getAiRoutes = () => ({
53
52
  method: "GET"
54
53
  }
55
54
  });
56
- aiRouter.post(getAiRoutes().customQuery.urlModel, customQuery);
57
- aiRouter.post(getAiRoutes().translateJSON.urlModel, translateJSON);
58
- aiRouter.post(getAiRoutes().auditContentDeclaration.urlModel, auditContentDeclaration);
59
- aiRouter.post(getAiRoutes().auditContentDeclarationField.urlModel, auditContentDeclarationField);
60
- aiRouter.post(getAiRoutes().auditContentDeclarationMetadata.urlModel, auditContentDeclarationMetadata);
61
- aiRouter.post(getAiRoutes().auditTag.urlModel, auditTag);
62
- aiRouter.post(getAiRoutes().autocomplete.urlModel, autocomplete);
63
- aiRouter.get(getAiRoutes().getDiscussions.urlModel, getDiscussions);
64
- /**
65
- * This route number of requests is limited for unauthenticated users
66
- */
67
- aiRouter.use(/(.*)/, unauthenticatedChatBotLimiter);
68
- aiRouter.post(getAiRoutes().ask.urlModel, askDocQuestion);
55
+ const aiRouter = async (fastify) => {
56
+ fastify.post(getAiRoutes().customQuery.urlModel, customQuery);
57
+ fastify.post(getAiRoutes().translateJSON.urlModel, translateJSON);
58
+ fastify.post(getAiRoutes().auditContentDeclaration.urlModel, auditContentDeclaration);
59
+ fastify.post(getAiRoutes().auditContentDeclarationField.urlModel, auditContentDeclarationField);
60
+ fastify.post(getAiRoutes().auditContentDeclarationMetadata.urlModel, auditContentDeclarationMetadata);
61
+ fastify.post(getAiRoutes().auditTag.urlModel, auditTag);
62
+ fastify.post(getAiRoutes().autocomplete.urlModel, autocomplete);
63
+ fastify.get(getAiRoutes().getDiscussions.urlModel, getDiscussions);
64
+ /**
65
+ * This route number of requests is limited for unauthenticated users
66
+ */
67
+ await fastify.register(fastifyRateLimit, {
68
+ ...unauthenticatedChatBotLimiter,
69
+ routeId: "ai-ask-rate-limit"
70
+ });
71
+ fastify.post(getAiRoutes().ask.urlModel, askDocQuestion);
72
+ };
69
73
 
70
74
  //#endregion
71
75
  export { aiRoute, aiRouter, getAiRoutes };
@@ -1 +1 @@
1
- {"version":3,"file":"ai.routes.mjs","names":["aiRouter: Router"],"sources":["../../../src/routes/ai.routes.ts"],"sourcesContent":["import {\n askDocQuestion,\n auditContentDeclaration,\n auditContentDeclarationField,\n auditContentDeclarationMetadata,\n auditTag,\n autocomplete,\n customQuery,\n getDiscussions,\n translateJSON,\n} from '@controllers/ai.controller';\nimport { unauthenticatedChatBotLimiter } from '@utils/rateLimiter';\nimport { Router } from 'express';\nimport type { Routes } from '@/types/Routes';\n\nexport const aiRouter: Router = Router();\n\nexport const aiRoute = '/api/ai';\n\nconst baseURL = () => `${process.env.BACKEND_URL}${aiRoute}`;\n\nexport const getAiRoutes = () =>\n ({\n customQuery: {\n urlModel: '/',\n url: `${baseURL()}/`,\n method: 'POST',\n },\n translateJSON: {\n urlModel: '/translate/json',\n url: `${baseURL()}/translate/json`,\n method: 'POST',\n },\n auditContentDeclaration: {\n urlModel: '/audit/dictionary',\n url: `${baseURL()}/audit/dictionary`,\n method: 'POST',\n },\n auditContentDeclarationField: {\n urlModel: '/audit/dictionary/field',\n url: `${baseURL()}/audit/dictionary/field`,\n method: 'POST',\n },\n auditContentDeclarationMetadata: {\n urlModel: '/audit/dictionary/metadata',\n url: `${baseURL()}/audit/dictionary/metadata`,\n method: 'POST',\n },\n auditTag: {\n urlModel: '/audit/tag',\n url: `${baseURL()}/audit/tag`,\n method: 'POST',\n },\n ask: {\n urlModel: '/ask',\n url: `${baseURL()}/ask`,\n method: 'POST',\n },\n autocomplete: {\n urlModel: '/autocomplete',\n url: `${baseURL()}/autocomplete`,\n method: 'POST',\n },\n getDiscussions: {\n urlModel: '/discussions',\n url: `${baseURL()}/discussions`,\n method: 'GET',\n },\n }) satisfies Routes;\n\naiRouter.post(getAiRoutes().customQuery.urlModel, customQuery);\n\naiRouter.post(getAiRoutes().translateJSON.urlModel, translateJSON);\n\naiRouter.post(\n getAiRoutes().auditContentDeclaration.urlModel,\n auditContentDeclaration\n);\naiRouter.post(\n getAiRoutes().auditContentDeclarationField.urlModel,\n auditContentDeclarationField\n);\naiRouter.post(\n getAiRoutes().auditContentDeclarationMetadata.urlModel,\n auditContentDeclarationMetadata\n);\n\naiRouter.post(getAiRoutes().auditTag.urlModel, auditTag);\n\naiRouter.post(getAiRoutes().autocomplete.urlModel, autocomplete);\n\naiRouter.get(getAiRoutes().getDiscussions.urlModel, getDiscussions);\n\n/**\n * This route number of requests is limited for unauthenticated users\n */\naiRouter.use(/(.*)/, unauthenticatedChatBotLimiter);\naiRouter.post(getAiRoutes().ask.urlModel, askDocQuestion);\n"],"mappings":";;;;;AAeA,MAAaA,WAAmB,QAAQ;AAExC,MAAa,UAAU;AAEvB,MAAM,gBAAgB,GAAG,QAAQ,IAAI,cAAc;AAEnD,MAAa,qBACV;CACC,aAAa;EACX,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,eAAe;EACb,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,yBAAyB;EACvB,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,8BAA8B;EAC5B,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,iCAAiC;EAC/B,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,UAAU;EACR,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,KAAK;EACH,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,cAAc;EACZ,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,gBAAgB;EACd,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACF;AAEH,SAAS,KAAK,aAAa,CAAC,YAAY,UAAU,YAAY;AAE9D,SAAS,KAAK,aAAa,CAAC,cAAc,UAAU,cAAc;AAElE,SAAS,KACP,aAAa,CAAC,wBAAwB,UACtC,wBACD;AACD,SAAS,KACP,aAAa,CAAC,6BAA6B,UAC3C,6BACD;AACD,SAAS,KACP,aAAa,CAAC,gCAAgC,UAC9C,gCACD;AAED,SAAS,KAAK,aAAa,CAAC,SAAS,UAAU,SAAS;AAExD,SAAS,KAAK,aAAa,CAAC,aAAa,UAAU,aAAa;AAEhE,SAAS,IAAI,aAAa,CAAC,eAAe,UAAU,eAAe;;;;AAKnE,SAAS,IAAI,QAAQ,8BAA8B;AACnD,SAAS,KAAK,aAAa,CAAC,IAAI,UAAU,eAAe"}
1
+ {"version":3,"file":"ai.routes.mjs","names":[],"sources":["../../../src/routes/ai.routes.ts"],"sourcesContent":["import {\n askDocQuestion,\n auditContentDeclaration,\n auditContentDeclarationField,\n auditContentDeclarationMetadata,\n auditTag,\n autocomplete,\n customQuery,\n getDiscussions,\n translateJSON,\n} from '@controllers/ai.controller';\nimport fastifyRateLimit from '@fastify/rate-limit';\nimport { unauthenticatedChatBotLimiter } from '@utils/rateLimiter';\nimport type { FastifyInstance } from 'fastify';\nimport type { Routes } from '@/types/Routes';\n\nexport const aiRoute = '/api/ai';\n\nconst baseURL = () => `${process.env.BACKEND_URL}${aiRoute}`;\n\nexport const getAiRoutes = () =>\n ({\n customQuery: {\n urlModel: '/',\n url: `${baseURL()}/`,\n method: 'POST',\n },\n translateJSON: {\n urlModel: '/translate/json',\n url: `${baseURL()}/translate/json`,\n method: 'POST',\n },\n auditContentDeclaration: {\n urlModel: '/audit/dictionary',\n url: `${baseURL()}/audit/dictionary`,\n method: 'POST',\n },\n auditContentDeclarationField: {\n urlModel: '/audit/dictionary/field',\n url: `${baseURL()}/audit/dictionary/field`,\n method: 'POST',\n },\n auditContentDeclarationMetadata: {\n urlModel: '/audit/dictionary/metadata',\n url: `${baseURL()}/audit/dictionary/metadata`,\n method: 'POST',\n },\n auditTag: {\n urlModel: '/audit/tag',\n url: `${baseURL()}/audit/tag`,\n method: 'POST',\n },\n ask: {\n urlModel: '/ask',\n url: `${baseURL()}/ask`,\n method: 'POST',\n },\n autocomplete: {\n urlModel: '/autocomplete',\n url: `${baseURL()}/autocomplete`,\n method: 'POST',\n },\n getDiscussions: {\n urlModel: '/discussions',\n url: `${baseURL()}/discussions`,\n method: 'GET',\n },\n }) satisfies Routes;\n\nexport const aiRouter = async (fastify: FastifyInstance) => {\n fastify.post(getAiRoutes().customQuery.urlModel, customQuery);\n fastify.post(getAiRoutes().translateJSON.urlModel, translateJSON);\n fastify.post(\n getAiRoutes().auditContentDeclaration.urlModel,\n auditContentDeclaration\n );\n fastify.post(\n getAiRoutes().auditContentDeclarationField.urlModel,\n auditContentDeclarationField\n );\n fastify.post(\n getAiRoutes().auditContentDeclarationMetadata.urlModel,\n auditContentDeclarationMetadata\n );\n fastify.post(getAiRoutes().auditTag.urlModel, auditTag);\n fastify.post(getAiRoutes().autocomplete.urlModel, autocomplete);\n fastify.get(getAiRoutes().getDiscussions.urlModel, getDiscussions);\n\n /**\n * This route number of requests is limited for unauthenticated users\n */\n await fastify.register(fastifyRateLimit, {\n ...unauthenticatedChatBotLimiter,\n // Apply only to the /ask route\n routeId: 'ai-ask-rate-limit',\n });\n fastify.post(getAiRoutes().ask.urlModel, askDocQuestion);\n};\n"],"mappings":";;;;;AAgBA,MAAa,UAAU;AAEvB,MAAM,gBAAgB,GAAG,QAAQ,IAAI,cAAc;AAEnD,MAAa,qBACV;CACC,aAAa;EACX,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,eAAe;EACb,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,yBAAyB;EACvB,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,8BAA8B;EAC5B,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,iCAAiC;EAC/B,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,UAAU;EACR,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,KAAK;EACH,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,cAAc;EACZ,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,gBAAgB;EACd,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACF;AAEH,MAAa,WAAW,OAAO,YAA6B;AAC1D,SAAQ,KAAK,aAAa,CAAC,YAAY,UAAU,YAAY;AAC7D,SAAQ,KAAK,aAAa,CAAC,cAAc,UAAU,cAAc;AACjE,SAAQ,KACN,aAAa,CAAC,wBAAwB,UACtC,wBACD;AACD,SAAQ,KACN,aAAa,CAAC,6BAA6B,UAC3C,6BACD;AACD,SAAQ,KACN,aAAa,CAAC,gCAAgC,UAC9C,gCACD;AACD,SAAQ,KAAK,aAAa,CAAC,SAAS,UAAU,SAAS;AACvD,SAAQ,KAAK,aAAa,CAAC,aAAa,UAAU,aAAa;AAC/D,SAAQ,IAAI,aAAa,CAAC,eAAe,UAAU,eAAe;;;;AAKlE,OAAM,QAAQ,SAAS,kBAAkB;EACvC,GAAG;EAEH,SAAS;EACV,CAAC;AACF,SAAQ,KAAK,aAAa,CAAC,IAAI,UAAU,eAAe"}
@@ -0,0 +1,43 @@
1
+ import { authCallback, checkConfig, getAuthUrl, getConfigFile, listRepos } from "../controllers/bitbucket.controller.mjs";
2
+
3
+ //#region src/routes/bitbucket.routes.ts
4
+ const bitbucketRoute = "/api/bitbucket";
5
+ const baseURL = () => `${process.env.BACKEND_URL}${bitbucketRoute}`;
6
+ const getBitbucketRoutes = () => ({
7
+ getAuthUrl: {
8
+ urlModel: "/auth-url",
9
+ url: `${baseURL()}/auth-url`,
10
+ method: "GET"
11
+ },
12
+ authCallback: {
13
+ urlModel: "/auth",
14
+ url: `${baseURL()}/auth`,
15
+ method: "POST"
16
+ },
17
+ listRepos: {
18
+ urlModel: "/repos",
19
+ url: `${baseURL()}/repos`,
20
+ method: "GET"
21
+ },
22
+ checkConfig: {
23
+ urlModel: "/check-config",
24
+ url: `${baseURL()}/check-config`,
25
+ method: "POST"
26
+ },
27
+ getConfigFile: {
28
+ urlModel: "/get-config-file",
29
+ url: `${baseURL()}/get-config-file`,
30
+ method: "POST"
31
+ }
32
+ });
33
+ const bitbucketRouter = async (fastify) => {
34
+ fastify.get(getBitbucketRoutes().getAuthUrl.urlModel, getAuthUrl);
35
+ fastify.post(getBitbucketRoutes().authCallback.urlModel, authCallback);
36
+ fastify.get(getBitbucketRoutes().listRepos.urlModel, listRepos);
37
+ fastify.post(getBitbucketRoutes().checkConfig.urlModel, checkConfig);
38
+ fastify.post(getBitbucketRoutes().getConfigFile.urlModel, getConfigFile);
39
+ };
40
+
41
+ //#endregion
42
+ export { bitbucketRoute, bitbucketRouter, getBitbucketRoutes };
43
+ //# sourceMappingURL=bitbucket.routes.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bitbucket.routes.mjs","names":[],"sources":["../../../src/routes/bitbucket.routes.ts"],"sourcesContent":["import {\n authCallback,\n checkConfig,\n getAuthUrl,\n getConfigFile,\n listRepos,\n} from '@controllers/bitbucket.controller';\nimport type { FastifyInstance } from 'fastify';\nimport type { Routes } from '@/types/Routes';\n\nexport const bitbucketRoute = '/api/bitbucket';\n\nconst baseURL = () => `${process.env.BACKEND_URL}${bitbucketRoute}`;\n\nexport const getBitbucketRoutes = () =>\n ({\n getAuthUrl: {\n urlModel: '/auth-url',\n url: `${baseURL()}/auth-url`,\n method: 'GET',\n },\n authCallback: {\n urlModel: '/auth',\n url: `${baseURL()}/auth`,\n method: 'POST',\n },\n listRepos: {\n urlModel: '/repos',\n url: `${baseURL()}/repos`,\n method: 'GET',\n },\n checkConfig: {\n urlModel: '/check-config',\n url: `${baseURL()}/check-config`,\n method: 'POST',\n },\n getConfigFile: {\n urlModel: '/get-config-file',\n url: `${baseURL()}/get-config-file`,\n method: 'POST',\n },\n }) satisfies Routes;\n\nexport const bitbucketRouter = async (fastify: FastifyInstance) => {\n fastify.get(getBitbucketRoutes().getAuthUrl.urlModel, getAuthUrl);\n fastify.post(getBitbucketRoutes().authCallback.urlModel, authCallback);\n fastify.get(getBitbucketRoutes().listRepos.urlModel, listRepos);\n fastify.post(getBitbucketRoutes().checkConfig.urlModel, checkConfig);\n fastify.post(getBitbucketRoutes().getConfigFile.urlModel, getConfigFile);\n};\n"],"mappings":";;;AAUA,MAAa,iBAAiB;AAE9B,MAAM,gBAAgB,GAAG,QAAQ,IAAI,cAAc;AAEnD,MAAa,4BACV;CACC,YAAY;EACV,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,cAAc;EACZ,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,WAAW;EACT,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,aAAa;EACX,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACD,eAAe;EACb,UAAU;EACV,KAAK,GAAG,SAAS,CAAC;EAClB,QAAQ;EACT;CACF;AAEH,MAAa,kBAAkB,OAAO,YAA6B;AACjE,SAAQ,IAAI,oBAAoB,CAAC,WAAW,UAAU,WAAW;AACjE,SAAQ,KAAK,oBAAoB,CAAC,aAAa,UAAU,aAAa;AACtE,SAAQ,IAAI,oBAAoB,CAAC,UAAU,UAAU,UAAU;AAC/D,SAAQ,KAAK,oBAAoB,CAAC,YAAY,UAAU,YAAY;AACpE,SAAQ,KAAK,oBAAoB,CAAC,cAAc,UAAU,cAAc"}
@@ -1,8 +1,6 @@
1
1
  import { addDictionary, deleteDictionary, getDictionaries, getDictionariesKeys, getDictionariesUpdateTimestamp, getDictionaryByKey, pushDictionaries, updateDictionary } from "../controllers/dictionary.controller.mjs";
2
- import { Router } from "express";
3
2
 
4
3
  //#region src/routes/dictionary.routes.ts
5
- const dictionaryRouter = Router();
6
4
  const dictionaryRoute = "/api/dictionary";
7
5
  const baseURL = () => `${process.env.BACKEND_URL}${dictionaryRoute}`;
8
6
  const getDictionaryRoutes = () => ({
@@ -47,14 +45,16 @@ const getDictionaryRoutes = () => ({
47
45
  method: "DELETE"
48
46
  }
49
47
  });
50
- dictionaryRouter.get(getDictionaryRoutes().getDictionaries.urlModel, getDictionaries);
51
- dictionaryRouter.get(getDictionaryRoutes().getDictionariesKeys.urlModel, getDictionariesKeys);
52
- dictionaryRouter.get(getDictionaryRoutes().getDictionariesUpdateTimestamp.urlModel, getDictionariesUpdateTimestamp);
53
- dictionaryRouter.get(getDictionaryRoutes().getDictionary.urlModel, getDictionaryByKey);
54
- dictionaryRouter.post(getDictionaryRoutes().addDictionary.urlModel, addDictionary);
55
- dictionaryRouter.patch(getDictionaryRoutes().pushDictionaries.urlModel, pushDictionaries);
56
- dictionaryRouter.put(getDictionaryRoutes().updateDictionary.urlModel, updateDictionary);
57
- dictionaryRouter.delete(getDictionaryRoutes().deleteDictionary.urlModel, deleteDictionary);
48
+ const dictionaryRouter = async (fastify) => {
49
+ fastify.get(getDictionaryRoutes().getDictionaries.urlModel, getDictionaries);
50
+ fastify.get(getDictionaryRoutes().getDictionariesKeys.urlModel, getDictionariesKeys);
51
+ fastify.get(getDictionaryRoutes().getDictionariesUpdateTimestamp.urlModel, getDictionariesUpdateTimestamp);
52
+ fastify.get(getDictionaryRoutes().getDictionary.urlModel, getDictionaryByKey);
53
+ fastify.post(getDictionaryRoutes().addDictionary.urlModel, addDictionary);
54
+ fastify.patch(getDictionaryRoutes().pushDictionaries.urlModel, pushDictionaries);
55
+ fastify.put(getDictionaryRoutes().updateDictionary.urlModel, updateDictionary);
56
+ fastify.delete(getDictionaryRoutes().deleteDictionary.urlModel, deleteDictionary);
57
+ };
58
58
 
59
59
  //#endregion
60
60
  export { dictionaryRoute, dictionaryRouter, getDictionaryRoutes };