@intlayer/backend 8.10.0 → 8.10.1

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 (179) hide show
  1. package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/dictionary/markdown.json +10948 -8936
  2. package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/interest_of_intlayer.json +1 -14957
  3. package/dist/esm/controllers/ai.controller.mjs +10 -9
  4. package/dist/esm/controllers/ai.controller.mjs.map +1 -1
  5. package/dist/esm/controllers/audit.controller.mjs +1 -1
  6. package/dist/esm/controllers/audit.controller.mjs.map +1 -1
  7. package/dist/esm/controllers/organization.controller.mjs +1 -1
  8. package/dist/esm/controllers/organization.controller.mjs.map +1 -1
  9. package/dist/esm/controllers/project.controller.mjs +2 -2
  10. package/dist/esm/controllers/project.controller.mjs.map +1 -1
  11. package/dist/esm/controllers/showcaseProject.controller.mjs +0 -2
  12. package/dist/esm/controllers/showcaseProject.controller.mjs.map +1 -1
  13. package/dist/esm/controllers/translation.controller.mjs +0 -1
  14. package/dist/esm/controllers/translation.controller.mjs.map +1 -1
  15. package/dist/esm/controllers/user.controller.mjs +0 -6
  16. package/dist/esm/controllers/user.controller.mjs.map +1 -1
  17. package/dist/esm/schemas/account.schema.mjs +3 -2
  18. package/dist/esm/schemas/account.schema.mjs.map +1 -1
  19. package/dist/esm/{models/audit.model.mjs → schemas/audit.schema.mjs} +3 -3
  20. package/dist/esm/schemas/audit.schema.mjs.map +1 -0
  21. package/dist/esm/{models/auditJob.model.mjs → schemas/auditJob.schema.mjs} +3 -3
  22. package/dist/esm/schemas/auditJob.schema.mjs.map +1 -0
  23. package/dist/esm/{models/auditPage.model.mjs → schemas/auditPage.schema.mjs} +3 -3
  24. package/dist/esm/schemas/auditPage.schema.mjs.map +1 -0
  25. package/dist/esm/schemas/cliSessionToken.schema.mjs +3 -2
  26. package/dist/esm/schemas/cliSessionToken.schema.mjs.map +1 -1
  27. package/dist/esm/schemas/dictionary.schema.mjs +3 -2
  28. package/dist/esm/schemas/dictionary.schema.mjs.map +1 -1
  29. package/dist/esm/schemas/discussion.schema.mjs +3 -2
  30. package/dist/esm/schemas/discussion.schema.mjs.map +1 -1
  31. package/dist/esm/schemas/oAuth2.schema.mjs +3 -2
  32. package/dist/esm/schemas/oAuth2.schema.mjs.map +1 -1
  33. package/dist/esm/schemas/organization.schema.mjs +3 -2
  34. package/dist/esm/schemas/organization.schema.mjs.map +1 -1
  35. package/dist/esm/schemas/project.schema.mjs +3 -2
  36. package/dist/esm/schemas/project.schema.mjs.map +1 -1
  37. package/dist/esm/schemas/session.schema.mjs +3 -2
  38. package/dist/esm/schemas/session.schema.mjs.map +1 -1
  39. package/dist/esm/schemas/showcaseProject.schema.mjs +3 -2
  40. package/dist/esm/schemas/showcaseProject.schema.mjs.map +1 -1
  41. package/dist/esm/schemas/tag.schema.mjs +3 -2
  42. package/dist/esm/schemas/tag.schema.mjs.map +1 -1
  43. package/dist/esm/schemas/user.schema.mjs +3 -2
  44. package/dist/esm/schemas/user.schema.mjs.map +1 -1
  45. package/dist/esm/services/audit/recursiveAudit.service.mjs +2 -2
  46. package/dist/esm/services/audit/recursiveAudit.service.mjs.map +1 -1
  47. package/dist/esm/services/bitbucket.service.mjs +1 -1
  48. package/dist/esm/services/bitbucket.service.mjs.map +1 -1
  49. package/dist/esm/services/cliSessionToken.service.mjs +1 -1
  50. package/dist/esm/services/cliSessionToken.service.mjs.map +1 -1
  51. package/dist/esm/services/dictionary.service.mjs +1 -1
  52. package/dist/esm/services/dictionary.service.mjs.map +1 -1
  53. package/dist/esm/services/github.service.mjs +1 -1
  54. package/dist/esm/services/github.service.mjs.map +1 -1
  55. package/dist/esm/services/gitlab.service.mjs +1 -1
  56. package/dist/esm/services/gitlab.service.mjs.map +1 -1
  57. package/dist/esm/services/oAuth2.service.mjs +2 -2
  58. package/dist/esm/services/oAuth2.service.mjs.map +1 -1
  59. package/dist/esm/services/organization.service.mjs +1 -1
  60. package/dist/esm/services/organization.service.mjs.map +1 -1
  61. package/dist/esm/services/project.service.mjs +1 -1
  62. package/dist/esm/services/project.service.mjs.map +1 -1
  63. package/dist/esm/services/projectAccessKey.service.mjs +1 -1
  64. package/dist/esm/services/projectAccessKey.service.mjs.map +1 -1
  65. package/dist/esm/services/showcase/showcaseProject.service.mjs +1 -1
  66. package/dist/esm/services/showcase/showcaseProject.service.mjs.map +1 -1
  67. package/dist/esm/services/tag.service.mjs +1 -1
  68. package/dist/esm/services/tag.service.mjs.map +1 -1
  69. package/dist/esm/services/user.service.mjs +1 -1
  70. package/dist/esm/services/user.service.mjs.map +1 -1
  71. package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/dictionary/markdown.json +10948 -8936
  72. package/dist/esm/utils/AI/askDocQuestion/embeddings/docs/en/interest_of_intlayer.json +1 -14957
  73. package/dist/esm/utils/AI/auditDictionaryField/index.mjs +9 -0
  74. package/dist/esm/utils/AI/auditDictionaryField/index.mjs.map +1 -1
  75. package/dist/esm/utils/AI/getProjectAIOptions.mjs +20 -0
  76. package/dist/esm/utils/AI/getProjectAIOptions.mjs.map +1 -0
  77. package/dist/esm/utils/errors/ErrorHandler.mjs +40 -8
  78. package/dist/esm/utils/errors/ErrorHandler.mjs.map +1 -1
  79. package/dist/esm/utils/mapper/project.mjs +7 -1
  80. package/dist/esm/utils/mapper/project.mjs.map +1 -1
  81. package/dist/esm/utils/mongoDB/connectDB.mjs +12 -12
  82. package/dist/esm/utils/mongoDB/connectDB.mjs.map +1 -1
  83. package/dist/types/controllers/ai.controller.d.ts.map +1 -1
  84. package/dist/types/controllers/project.controller.d.ts.map +1 -1
  85. package/dist/types/controllers/showcaseProject.controller.d.ts.map +1 -1
  86. package/dist/types/controllers/translation.controller.d.ts.map +1 -1
  87. package/dist/types/controllers/user.controller.d.ts.map +1 -1
  88. package/dist/types/schemas/account.schema.d.ts +3 -2
  89. package/dist/types/schemas/account.schema.d.ts.map +1 -1
  90. package/dist/types/schemas/audit.schema.d.ts +64 -0
  91. package/dist/types/schemas/audit.schema.d.ts.map +1 -0
  92. package/dist/types/schemas/auditJob.schema.d.ts +122 -0
  93. package/dist/types/schemas/auditJob.schema.d.ts.map +1 -0
  94. package/dist/types/schemas/auditPage.schema.d.ts +120 -0
  95. package/dist/types/schemas/auditPage.schema.d.ts.map +1 -0
  96. package/dist/types/schemas/cliSessionToken.schema.d.ts +10 -3
  97. package/dist/types/schemas/cliSessionToken.schema.d.ts.map +1 -1
  98. package/dist/types/schemas/dictionary.schema.d.ts +22 -13
  99. package/dist/types/schemas/dictionary.schema.d.ts.map +1 -1
  100. package/dist/types/schemas/discussion.schema.d.ts +19 -14
  101. package/dist/types/schemas/discussion.schema.d.ts.map +1 -1
  102. package/dist/types/schemas/oAuth2.schema.d.ts +13 -3
  103. package/dist/types/schemas/oAuth2.schema.d.ts.map +1 -1
  104. package/dist/types/schemas/organization.schema.d.ts +7 -6
  105. package/dist/types/schemas/organization.schema.d.ts.map +1 -1
  106. package/dist/types/schemas/plans.schema.d.ts +7 -7
  107. package/dist/types/schemas/project.schema.d.ts +7 -6
  108. package/dist/types/schemas/project.schema.d.ts.map +1 -1
  109. package/dist/types/schemas/session.schema.d.ts +11 -10
  110. package/dist/types/schemas/session.schema.d.ts.map +1 -1
  111. package/dist/types/schemas/showcaseProject.schema.d.ts +16 -15
  112. package/dist/types/schemas/showcaseProject.schema.d.ts.map +1 -1
  113. package/dist/types/schemas/tag.schema.d.ts +9 -8
  114. package/dist/types/schemas/tag.schema.d.ts.map +1 -1
  115. package/dist/types/schemas/user.schema.d.ts +8 -7
  116. package/dist/types/schemas/user.schema.d.ts.map +1 -1
  117. package/dist/types/services/audit/recursiveAudit.service.d.ts +2 -2
  118. package/dist/types/types/project.types.d.ts +2 -1
  119. package/dist/types/types/project.types.d.ts.map +1 -1
  120. package/dist/types/utils/AI/getProjectAIOptions.d.ts +15 -0
  121. package/dist/types/utils/AI/getProjectAIOptions.d.ts.map +1 -0
  122. package/dist/types/utils/errors/ErrorHandler.d.ts +4 -4
  123. package/dist/types/utils/errors/ErrorHandler.d.ts.map +1 -1
  124. package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts +3 -3
  125. package/dist/types/utils/mapper/project.d.ts.map +1 -1
  126. package/package.json +5 -5
  127. package/dist/esm/models/account.model.mjs +0 -9
  128. package/dist/esm/models/account.model.mjs.map +0 -1
  129. package/dist/esm/models/audit.model.mjs.map +0 -1
  130. package/dist/esm/models/auditJob.model.mjs.map +0 -1
  131. package/dist/esm/models/auditPage.model.mjs.map +0 -1
  132. package/dist/esm/models/cliSessionToken.model.mjs +0 -9
  133. package/dist/esm/models/cliSessionToken.model.mjs.map +0 -1
  134. package/dist/esm/models/dictionary.model.mjs +0 -9
  135. package/dist/esm/models/dictionary.model.mjs.map +0 -1
  136. package/dist/esm/models/discussion.model.mjs +0 -9
  137. package/dist/esm/models/discussion.model.mjs.map +0 -1
  138. package/dist/esm/models/oAuth2.model.mjs +0 -9
  139. package/dist/esm/models/oAuth2.model.mjs.map +0 -1
  140. package/dist/esm/models/organization.model.mjs +0 -9
  141. package/dist/esm/models/organization.model.mjs.map +0 -1
  142. package/dist/esm/models/project.model.mjs +0 -9
  143. package/dist/esm/models/project.model.mjs.map +0 -1
  144. package/dist/esm/models/session.model.mjs +0 -9
  145. package/dist/esm/models/session.model.mjs.map +0 -1
  146. package/dist/esm/models/showcaseProject.model.mjs +0 -9
  147. package/dist/esm/models/showcaseProject.model.mjs.map +0 -1
  148. package/dist/esm/models/tag.model.mjs +0 -9
  149. package/dist/esm/models/tag.model.mjs.map +0 -1
  150. package/dist/esm/models/user.model.mjs +0 -9
  151. package/dist/esm/models/user.model.mjs.map +0 -1
  152. package/dist/types/models/account.model.d.ts +0 -7
  153. package/dist/types/models/account.model.d.ts.map +0 -1
  154. package/dist/types/models/audit.model.d.ts +0 -18
  155. package/dist/types/models/audit.model.d.ts.map +0 -1
  156. package/dist/types/models/auditJob.model.d.ts +0 -31
  157. package/dist/types/models/auditJob.model.d.ts.map +0 -1
  158. package/dist/types/models/auditPage.model.d.ts +0 -29
  159. package/dist/types/models/auditPage.model.d.ts.map +0 -1
  160. package/dist/types/models/cliSessionToken.model.d.ts +0 -14
  161. package/dist/types/models/cliSessionToken.model.d.ts.map +0 -1
  162. package/dist/types/models/dictionary.model.d.ts +0 -16
  163. package/dist/types/models/dictionary.model.d.ts.map +0 -1
  164. package/dist/types/models/discussion.model.d.ts +0 -12
  165. package/dist/types/models/discussion.model.d.ts.map +0 -1
  166. package/dist/types/models/oAuth2.model.d.ts +0 -18
  167. package/dist/types/models/oAuth2.model.d.ts.map +0 -1
  168. package/dist/types/models/organization.model.d.ts +0 -7
  169. package/dist/types/models/organization.model.d.ts.map +0 -1
  170. package/dist/types/models/project.model.d.ts +0 -7
  171. package/dist/types/models/project.model.d.ts.map +0 -1
  172. package/dist/types/models/session.model.d.ts +0 -7
  173. package/dist/types/models/session.model.d.ts.map +0 -1
  174. package/dist/types/models/showcaseProject.model.d.ts +0 -7
  175. package/dist/types/models/showcaseProject.model.d.ts.map +0 -1
  176. package/dist/types/models/tag.model.d.ts +0 -7
  177. package/dist/types/models/tag.model.d.ts.map +0 -1
  178. package/dist/types/models/user.model.d.ts +0 -7
  179. package/dist/types/models/user.model.d.ts.map +0 -1
@@ -1,4 +1,5 @@
1
1
  import { logger } from "../logger/index.mjs";
2
+ import { DiscussionModel } from "../schemas/discussion.schema.mjs";
2
3
  import { formatPaginatedResponse, formatResponse } from "../utils/responseData.mjs";
3
4
  import { ErrorHandler } from "../utils/errors/ErrorHandler.mjs";
4
5
  import { getDictionariesByTags } from "../services/dictionary.service.mjs";
@@ -12,16 +13,16 @@ import { aiDefaultOptions as aiDefaultOptions$4, autocomplete as autocomplete$1
12
13
  import { chat as chat$1 } from "../utils/AI/chat/index.mjs";
13
14
  import { createSessionTools } from "../utils/AI/chat/sessionTools.mjs";
14
15
  import { aiDefaultOptions as aiDefaultOptions$5, customQuery as customQuery$2 } from "../utils/AI/customQuery/index.mjs";
16
+ import { getProjectAIOptions } from "../utils/AI/getProjectAIOptions.mjs";
15
17
  import { aiDefaultOptions as aiDefaultOptions$6, translateJSON as translateJSON$2 } from "../utils/AI/translateJSON/index.mjs";
16
18
  import { getDiscussionFiltersAndPagination } from "../utils/filtersAndPagination/getDiscussionFiltersAndPagination.mjs";
17
- import { DiscussionModel } from "../models/discussion.model.mjs";
18
19
  import { getAIConfig } from "@intlayer/ai";
19
20
 
20
21
  //#region src/controllers/ai.controller.ts
21
22
  const customQuery = async (request, reply) => {
22
23
  const { aiOptions, tagsKeys, ...rest } = request.body;
23
24
  const { user, project } = request.session || {};
24
- const projectAIOptions = project?.configuration?.ai ? project.configuration.ai : void 0;
25
+ const projectAIOptions = await getProjectAIOptions(project);
25
26
  let aiConfig;
26
27
  try {
27
28
  aiConfig = await getAIConfig({
@@ -48,7 +49,7 @@ const customQuery = async (request, reply) => {
48
49
  const translateJSON = async (request, reply) => {
49
50
  const { project, user } = request.session || {};
50
51
  const { aiOptions, tagsKeys, ...rest } = request.body;
51
- const projectAIOptions = project?.configuration?.ai ? project.configuration.ai : void 0;
52
+ const projectAIOptions = await getProjectAIOptions(project);
52
53
  let aiConfig;
53
54
  try {
54
55
  aiConfig = await getAIConfig({
@@ -82,7 +83,7 @@ const translateJSON = async (request, reply) => {
82
83
  const auditContentDeclaration = async (request, reply) => {
83
84
  const { project, user } = request.session || {};
84
85
  const { fileContent, filePath, aiOptions, locales, defaultLocale, tagsKeys } = request.body;
85
- const projectAIOptions = project?.configuration?.ai ? project.configuration.ai : void 0;
86
+ const projectAIOptions = await getProjectAIOptions(project);
86
87
  let aiConfig;
87
88
  try {
88
89
  aiConfig = await getAIConfig({
@@ -119,7 +120,7 @@ const auditContentDeclaration = async (request, reply) => {
119
120
  const auditContentDeclarationField = async (request, reply) => {
120
121
  const { project, user } = request.session || {};
121
122
  const { fileContent, aiOptions, locales, tagsKeys, keyPath } = request.body;
122
- const projectAIOptions = project?.configuration?.ai ? project.configuration.ai : void 0;
123
+ const projectAIOptions = await getProjectAIOptions(project);
123
124
  let aiConfig;
124
125
  try {
125
126
  aiConfig = await getAIConfig({
@@ -186,7 +187,7 @@ const auditContentDeclarationMetadata = async (request, reply) => {
186
187
  const auditTag = async (request, reply) => {
187
188
  const { project, user } = request.session || {};
188
189
  const { aiOptions, tag } = request.body;
189
- const projectAIOptions = project?.configuration?.ai ? project.configuration.ai : void 0;
190
+ const projectAIOptions = await getProjectAIOptions(project);
190
191
  let aiConfig;
191
192
  try {
192
193
  aiConfig = await getAIConfig({
@@ -220,7 +221,7 @@ const askDocQuestion = async (request, reply) => {
220
221
  reply.hijack();
221
222
  const headers = reply.getHeaders();
222
223
  for (const [key, value] of Object.entries(headers)) if (value !== void 0) reply.raw.setHeader(key, value);
223
- const projectAIOptions = project?.configuration?.ai ? project.configuration.ai : void 0;
224
+ const projectAIOptions = await getProjectAIOptions(project);
224
225
  let aiConfig;
225
226
  try {
226
227
  try {
@@ -304,7 +305,7 @@ const chat = async (request, reply) => {
304
305
  reply.hijack();
305
306
  const headers = reply.getHeaders();
306
307
  for (const [key, value] of Object.entries(headers)) if (value !== void 0) reply.raw.setHeader(key, value);
307
- const projectAIOptions = project?.configuration?.ai ? project.configuration.ai : void 0;
308
+ const projectAIOptions = await getProjectAIOptions(project);
308
309
  try {
309
310
  let aiConfig;
310
311
  try {
@@ -396,7 +397,7 @@ const chat = async (request, reply) => {
396
397
  };
397
398
  const autocomplete = async (request, reply) => {
398
399
  const { user, project } = request.session || {};
399
- const projectAIOptions = project?.configuration?.ai ? project.configuration.ai : void 0;
400
+ const projectAIOptions = await getProjectAIOptions(project);
400
401
  try {
401
402
  const { text, aiOptions, contextBefore, currentLine, contextAfter } = request.body;
402
403
  let aiConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"ai.controller.mjs","names":["customQueryUtil.aiDefaultOptions","customQueryUtil.customQuery","translateJSONUtil.aiDefaultOptions","translateJSONUtil.translateJSON","auditContentDeclarationUtil.aiDefaultOptions","auditContentDeclarationUtil.auditDictionary","auditContentDeclarationFieldUtil.aiDefaultOptions","auditContentDeclarationFieldUtil.auditDictionaryField","auditContentDeclarationMetadataUtil.aiDefaultOptions","tagService.findTags","auditContentDeclarationMetadataUtil.auditDictionaryMetadata","auditTagUtil.aiDefaultOptions","auditTagUtil.auditTag","askDocQuestionUtil.askDocQuestion","chatUtil.chat","autocompleteUtil.aiDefaultOptions","autocompleteUtil.autocomplete"],"sources":["../../../src/controllers/ai.controller.ts"],"sourcesContent":["import {\n type AIConfig,\n type AIOptions,\n type ChatCompletionRequestMessage,\n getAIConfig,\n} from '@intlayer/ai';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport { logger } from '@logger';\nimport { getDictionariesByTags } from '@services/dictionary.service';\nimport * as tagService from '@services/tag.service';\nimport { getTagsByKeys } from '@services/tag.service';\nimport * as askDocQuestionUtil from '@utils/AI/askDocQuestion/askDocQuestion';\nimport * as auditContentDeclarationUtil from '@utils/AI/auditDictionary';\nimport * as auditContentDeclarationFieldUtil from '@utils/AI/auditDictionaryField';\nimport * as auditContentDeclarationMetadataUtil from '@utils/AI/auditDictionaryMetadata';\nimport * as auditTagUtil from '@utils/AI/auditTag';\nimport * as autocompleteUtil from '@utils/AI/autocomplete';\nimport * as chatUtil from '@utils/AI/chat';\nimport { createSessionTools } from '@utils/AI/chat/sessionTools';\nimport * as customQueryUtil from '@utils/AI/customQuery';\nimport * as translateJSONUtil from '@utils/AI/translateJSON';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n type DiscussionFiltersParams,\n getDiscussionFiltersAndPagination,\n} from '@utils/filtersAndPagination/getDiscussionFiltersAndPagination';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport { DiscussionModel } from '@/models/discussion.model';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { DiscussionAPI } from '@/types/discussion.types';\nimport type { Tag, TagAPI } from '@/types/tag.types';\n\nexport type {\n AIConfig,\n AIOptions,\n AIProvider,\n ChatCompletionRequestMessage,\n} from '@intlayer/ai';\n\ntype ReplaceAIConfigByOptions<T> = Omit<T, 'aiConfig'> & {\n aiOptions?: AIOptions;\n};\n\nexport type CustomQueryBody =\n ReplaceAIConfigByOptions<customQueryUtil.CustomQueryOptions> & {\n tagsKeys?: string[];\n applicationContext?: string;\n };\nexport type CustomQueryResult =\n ResponseData<customQueryUtil.CustomQueryResultData>;\n\nexport const customQuery = async (\n request: FastifyRequest<{ Body: CustomQueryBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { aiOptions, tagsKeys, ...rest } = request.body;\n const { user, project } = request.session || {};\n\n const projectAIOptions = project?.configuration?.ai\n ? (project.configuration.ai as AIOptions)\n : undefined;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: customQueryUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n const auditResponse = await customQueryUtil.customQuery({\n ...rest,\n aiConfig,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'QUERY_FAILED');\n }\n\n const responseData = formatResponse<customQueryUtil.CustomQueryResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type TranslateJSONBody = Omit<\n ReplaceAIConfigByOptions<translateJSONUtil.TranslateJSONOptions<JSON>>,\n 'tags'\n> & {\n tagsKeys?: string[];\n};\nexport type TranslateJSONResult = ResponseData<\n translateJSONUtil.TranslateJSONResultData<JSON>\n>;\n\nexport const translateJSON = async (\n request: FastifyRequest<{ Body: TranslateJSONBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { aiOptions, tagsKeys, ...rest } = request.body;\n\n const projectAIOptions = project?.configuration?.ai\n ? (project.configuration.ai as AIOptions)\n : undefined;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: translateJSONUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId && tagsKeys) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse = await translateJSONUtil.translateJSON<any>({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData = formatResponse<\n translateJSONUtil.TranslateJSONResultData<any>\n >({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n defaultLocale: Locale;\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n};\nexport type AuditContentDeclarationResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclaration = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { fileContent, filePath, aiOptions, locales, defaultLocale, tagsKeys } =\n request.body;\n\n const projectAIOptions = project?.configuration?.ai\n ? (project.configuration.ai as AIOptions)\n : undefined;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditContentDeclarationUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys ?? [], project.organizationId);\n }\n\n const auditResponse = await auditContentDeclarationUtil.auditDictionary({\n fileContent,\n filePath,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n defaultLocale,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationFieldBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n keyPath: KeyPath[];\n};\nexport type AuditContentDeclarationFieldResult =\n ResponseData<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationField = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationFieldBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { fileContent, aiOptions, locales, tagsKeys, keyPath } = request.body;\n\n const projectAIOptions = project?.configuration?.ai\n ? (project.configuration.ai as AIOptions)\n : undefined;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditContentDeclarationFieldUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys ?? [], project.organizationId);\n }\n\n const auditResponse =\n await auditContentDeclarationFieldUtil.auditDictionaryField({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n tags,\n keyPath,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>(\n {\n data: auditResponse,\n }\n );\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationMetadataBody = {\n aiOptions?: AIOptions;\n fileContent: string;\n};\n\nexport type AuditContentDeclarationMetadataResult =\n ResponseData<auditContentDeclarationMetadataUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationMetadata = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationMetadataBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, user } = request.session || {};\n const { fileContent, aiOptions } = request.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationMetadataUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n const tags: Tag[] = await tagService.findTags(\n {\n organizationId: organization?.id,\n },\n 0,\n 1000\n );\n\n const auditResponse =\n await auditContentDeclarationMetadataUtil.auditDictionaryMetadata({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationMetadataUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditTagBody = {\n aiOptions?: AIOptions;\n tag: TagAPI;\n};\nexport type AuditTagResult = ResponseData<auditTagUtil.TranslateJSONResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditTag = async (\n request: FastifyRequest<{ Body: AuditTagBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { aiOptions, tag } = request.body;\n\n const projectAIOptions = project?.configuration?.ai\n ? (project.configuration.ai as AIOptions)\n : undefined;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditTagUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let dictionaries: Dictionary[] = [];\n if (project?.organizationId) {\n dictionaries = await getDictionariesByTags([tag.key], project.id);\n }\n\n const auditResponse = await auditTagUtil.auditTag({\n aiConfig,\n dictionaries,\n tag,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData = formatResponse<auditTagUtil.TranslateJSONResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AskDocQuestionBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type AskDocQuestionResult =\n ResponseData<askDocQuestionUtil.AskDocQuestionResult>;\n\nexport const askDocQuestion = async (\n request: FastifyRequest<{ Body: AskDocQuestionBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { messages = [], discussionId } = request.body;\n const { user, project, organization } = request.session || {};\n\n // Hijack response\n reply.hijack();\n\n // Copy all Fastify-managed headers (including CORS) to the raw response\n // immediately after hijacking, before any early returns.\n const headers = reply.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n reply.raw.setHeader(key, value);\n }\n }\n\n const projectAIOptions = project?.configuration?.ai\n ? (project.configuration.ai as AIOptions)\n : undefined;\n\n let aiConfig: AIConfig;\n\n // Wrap EVERYTHING in a main try/catch block\n try {\n // Auth Check\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: {},\n projectOptions: projectAIOptions,\n accessType: ['public'],\n },\n !!user\n );\n } catch (error) {\n console.error(error);\n\n // Manually handle this specific error case\n const errorPayload = {\n code: 'AI_ACCESS_DENIED',\n title: 'Access Denied',\n message: 'Unable to configure AI access.',\n };\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n\n reply.raw.end();\n return;\n }\n\n // Set Stream Headers & Flush\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');\n\n if (reply.raw.flushHeaders) {\n reply.raw.flushHeaders();\n }\n\n reply.raw.write(': connected\\n\\n');\n\n // Execute AI Logic (Awaited properly)\n // This is where 'generateEmbedding' or 'streamText' will throw\n const fullResponse = await askDocQuestionUtil.askDocQuestion(\n messages,\n aiConfig,\n {\n onMessage: (chunk) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n }\n },\n }\n );\n\n // Persist Discussion (Only on success)\n const reversedMessages = [...messages].reverse();\n const lastUserMessageContent = reversedMessages.find(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords =\n typeof lastUserMessageContent === 'string'\n ? lastUserMessageContent.split(' ').length\n : 0;\n\n if (lastUserMessageNbWords >= 2 || messages.length >= 2) {\n const updatePayload: any = {\n discussionId,\n type: 'doc',\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp ?? new Date(),\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n relatedFiles: fullResponse.relatedFiles,\n timestamp: new Date(),\n },\n ],\n };\n\n if (user?.id) updatePayload.userId = user.id;\n if (project?.id) updatePayload.projectId = project.id;\n if (organization?.id) updatePayload.organizationId = organization.id;\n\n await DiscussionModel.findOneAndUpdate(\n { discussionId: String(discussionId) },\n { $set: updatePayload },\n { upsert: true, returnDocument: 'after' }\n );\n }\n\n // Send Completion Event\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n reply.raw.end();\n }\n } catch (err) {\n // -------------------------------------------------------------------------\n // CENTRALIZED ERROR CATCHER\n // -------------------------------------------------------------------------\n const errorMessage = err instanceof Error ? err.message : String(err);\n const errorStack = err instanceof Error ? err.stack : undefined;\n\n // Log the full error to your backend console\n logger.error('AI Stream Error Caught:', {\n message: errorMessage,\n stack: errorStack,\n });\n\n // Determine if it's an Auth error (common with OpenAI 401)\n const isAuthError =\n errorMessage.includes('401') ||\n errorMessage.includes('Incorrect API key');\n\n // Format error for Frontend\n const errorPayload = {\n code: isAuthError ? 'AI_AUTH_ERROR' : 'AI_STREAM_ERROR',\n title: isAuthError ? 'AI Configuration Error' : 'Generation Failed',\n message: errorMessage,\n };\n\n // Send error event to client\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n }\n }\n};\n\nexport type ChatBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type ChatResult = ResponseData<chatUtil.ChatResultData>;\n\nexport const chat = async (\n request: FastifyRequest<{ Body: ChatBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { messages = [], discussionId } = request.body;\n const { user, project, organization, roles } = request.session || {};\n\n reply.hijack();\n\n const headers = reply.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n reply.raw.setHeader(key, value);\n }\n }\n\n const projectAIOptions = project?.configuration?.ai\n ? (project.configuration.ai as AIOptions)\n : undefined;\n\n try {\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: {},\n projectOptions: projectAIOptions,\n accessType: ['registered_user'],\n },\n !!user\n );\n } catch (error) {\n console.error(error);\n\n const errorPayload = {\n code: 'AI_ACCESS_DENIED',\n title: 'Access Denied',\n message: 'Unable to configure AI access.',\n };\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n return;\n }\n\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');\n\n if (reply.raw.flushHeaders) {\n reply.raw.flushHeaders();\n }\n\n reply.raw.write(': connected\\n\\n');\n\n const sessionTools = createSessionTools({\n projectId: project?.id ? String(project.id) : undefined,\n organizationId: organization?.id ? String(organization.id) : undefined,\n userId: user?.id ? String(user.id) : undefined,\n roles: roles || [],\n session: request.session,\n onAction: (action) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ action })}\\n\\n`);\n }\n },\n });\n\n const fullResponse = await chatUtil.chat(messages, aiConfig, {\n tools: sessionTools,\n onMessage: (chunk) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n }\n },\n });\n\n const reversedMessages = [...messages].reverse();\n const lastUserMessageContent = reversedMessages.find(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords =\n typeof lastUserMessageContent === 'string'\n ? lastUserMessageContent.split(' ').length\n : 0;\n\n if (lastUserMessageNbWords >= 2 || messages.length >= 2) {\n const updatePayload: any = {\n discussionId,\n type: 'dashboard',\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp ?? new Date(),\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n timestamp: new Date(),\n },\n ],\n };\n\n if (user?.id) updatePayload.userId = user.id;\n if (project?.id) updatePayload.projectId = project.id;\n if (organization?.id) updatePayload.organizationId = organization.id;\n\n await DiscussionModel.findOneAndUpdate(\n { discussionId: String(discussionId) },\n { $set: updatePayload },\n { upsert: true, returnDocument: 'after' }\n );\n }\n\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n reply.raw.end();\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const errorStack = err instanceof Error ? err.stack : undefined;\n\n logger.error('AI Chat Stream Error:', {\n message: errorMessage,\n stack: errorStack,\n });\n\n const isAuthError =\n errorMessage.includes('401') ||\n errorMessage.includes('Incorrect API key');\n\n const errorPayload = {\n code: isAuthError ? 'AI_AUTH_ERROR' : 'AI_STREAM_ERROR',\n title: isAuthError ? 'AI Configuration Error' : 'Generation Failed',\n message: errorMessage,\n };\n\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n }\n }\n};\n\nexport type AutocompleteBody = {\n text: string;\n aiOptions?: AIOptions;\n contextBefore?: string;\n currentLine?: string;\n contextAfter?: string;\n};\n\nexport type AutocompleteResponse = ResponseData<{\n autocompletion: string;\n}>;\n\nexport const autocomplete = async (\n request: FastifyRequest<{ Body: AutocompleteBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project } = request.session || {};\n\n const projectAIOptions = project?.configuration?.ai\n ? (project.configuration.ai as AIOptions)\n : undefined;\n\n try {\n const { text, aiOptions, contextBefore, currentLine, contextAfter } =\n request.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: autocompleteUtil.aiDefaultOptions,\n accessType: ['public'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n const response = (await autocompleteUtil.autocomplete({\n text,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n contextBefore,\n currentLine,\n contextAfter,\n })) ?? {\n autocompletion: '',\n tokenUsed: 0,\n };\n\n const responseData =\n formatResponse<autocompleteUtil.AutocompleteFileResultData>({\n data: response,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type GetDiscussionsParams =\n | ({\n page?: string | number;\n pageSize?: string | number;\n includeMessages?: 'true' | 'false';\n } & DiscussionFiltersParams)\n | undefined;\n\nexport type GetDiscussionsResult = PaginatedResponse<DiscussionAPI>;\n\n/**\n * Retrieves a list of discussions with filters and pagination.\n * Only the owner or admins can access. By default, users only see their own.\n */\nexport const getDiscussions = async (\n request: FastifyRequest<{ Querystring: GetDiscussionsParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, roles } = request.session || {};\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getDiscussionFiltersAndPagination(request);\n const includeMessagesParam = (request.query as any)?.includeMessages as\n | 'true'\n | 'false'\n | undefined;\n const includeMessages = includeMessagesParam !== 'false';\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n try {\n const projection = includeMessages ? {} : { messages: 0 };\n const discussions = await DiscussionModel.find(filters, projection)\n .sort(sortOptions)\n .skip(skip)\n .limit(pageSize)\n .lean();\n\n // Compute number of messages for each discussion\n const numberOfMessagesById: Record<string, number> = {};\n if (!includeMessages && discussions.length > 0) {\n const ids = discussions.map((d: any) => d._id);\n const counts = await DiscussionModel.aggregate([\n { $match: { _id: { $in: ids } } },\n {\n $project: {\n numberOfMessages: { $size: { $ifNull: ['$messages', []] } },\n },\n },\n ]);\n for (const c of counts as any[]) {\n numberOfMessagesById[String(c._id)] = c.numberOfMessages ?? 0;\n }\n }\n\n // Permission: allow admin, or the owner for all returned entries\n const allOwnedByUser = discussions.every(\n (d) => String(d.userId) === String(user.id)\n );\n const isAllowed = roles?.includes('admin') || allOwnedByUser;\n\n if (!isAllowed) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const totalItems = await DiscussionModel.countDocuments(filters);\n\n const responseData = formatPaginatedResponse({\n data: discussions.map((d: any) => ({\n ...d,\n id: String(d._id ?? d.id),\n numberOfMessages: includeMessages\n ? Array.isArray(d.messages)\n ? d.messages.length\n : 0\n : (numberOfMessagesById[String(d._id ?? d.id)] ?? 0),\n })),\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n return reply.send(responseData as any);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ai.controller.mjs","names":["customQueryUtil.aiDefaultOptions","customQueryUtil.customQuery","translateJSONUtil.aiDefaultOptions","translateJSONUtil.translateJSON","auditContentDeclarationUtil.aiDefaultOptions","auditContentDeclarationUtil.auditDictionary","auditContentDeclarationFieldUtil.aiDefaultOptions","auditContentDeclarationFieldUtil.auditDictionaryField","auditContentDeclarationMetadataUtil.aiDefaultOptions","tagService.findTags","auditContentDeclarationMetadataUtil.auditDictionaryMetadata","auditTagUtil.aiDefaultOptions","auditTagUtil.auditTag","askDocQuestionUtil.askDocQuestion","chatUtil.chat","autocompleteUtil.aiDefaultOptions","autocompleteUtil.autocomplete"],"sources":["../../../src/controllers/ai.controller.ts"],"sourcesContent":["import {\n type AIConfig,\n type AIOptions,\n type ChatCompletionRequestMessage,\n getAIConfig,\n} from '@intlayer/ai';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { KeyPath } from '@intlayer/types/keyPath';\nimport { logger } from '@logger';\nimport { DiscussionModel } from '@schemas/discussion.schema';\nimport { getDictionariesByTags } from '@services/dictionary.service';\nimport * as tagService from '@services/tag.service';\nimport { getTagsByKeys } from '@services/tag.service';\nimport * as askDocQuestionUtil from '@utils/AI/askDocQuestion/askDocQuestion';\nimport * as auditContentDeclarationUtil from '@utils/AI/auditDictionary';\nimport * as auditContentDeclarationFieldUtil from '@utils/AI/auditDictionaryField';\nimport * as auditContentDeclarationMetadataUtil from '@utils/AI/auditDictionaryMetadata';\nimport * as auditTagUtil from '@utils/AI/auditTag';\nimport * as autocompleteUtil from '@utils/AI/autocomplete';\nimport * as chatUtil from '@utils/AI/chat';\nimport { createSessionTools } from '@utils/AI/chat/sessionTools';\nimport * as customQueryUtil from '@utils/AI/customQuery';\nimport { getProjectAIOptions } from '@utils/AI/getProjectAIOptions';\nimport * as translateJSONUtil from '@utils/AI/translateJSON';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n type DiscussionFiltersParams,\n getDiscussionFiltersAndPagination,\n} from '@utils/filtersAndPagination/getDiscussionFiltersAndPagination';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { DiscussionAPI } from '@/types/discussion.types';\nimport type { Tag, TagAPI } from '@/types/tag.types';\n\nexport type {\n AIConfig,\n AIOptions,\n AIProvider,\n ChatCompletionRequestMessage,\n} from '@intlayer/ai';\n\ntype ReplaceAIConfigByOptions<T> = Omit<T, 'aiConfig'> & {\n aiOptions?: AIOptions;\n};\n\nexport type CustomQueryBody =\n ReplaceAIConfigByOptions<customQueryUtil.CustomQueryOptions> & {\n tagsKeys?: string[];\n applicationContext?: string;\n };\nexport type CustomQueryResult =\n ResponseData<customQueryUtil.CustomQueryResultData>;\n\nexport const customQuery = async (\n request: FastifyRequest<{ Body: CustomQueryBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { aiOptions, tagsKeys, ...rest } = request.body;\n const { user, project } = request.session || {};\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: customQueryUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n const auditResponse = await customQueryUtil.customQuery({\n ...rest,\n aiConfig,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'QUERY_FAILED');\n }\n\n const responseData = formatResponse<customQueryUtil.CustomQueryResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type TranslateJSONBody = Omit<\n ReplaceAIConfigByOptions<translateJSONUtil.TranslateJSONOptions<JSON>>,\n 'tags'\n> & {\n tagsKeys?: string[];\n};\nexport type TranslateJSONResult = ResponseData<\n translateJSONUtil.TranslateJSONResultData<JSON>\n>;\n\nexport const translateJSON = async (\n request: FastifyRequest<{ Body: TranslateJSONBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { aiOptions, tagsKeys, ...rest } = request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: translateJSONUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId && tagsKeys) {\n tags = await getTagsByKeys(tagsKeys, project.organizationId);\n }\n\n const auditResponse = await translateJSONUtil.translateJSON<any>({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData = formatResponse<\n translateJSONUtil.TranslateJSONResultData<any>\n >({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n defaultLocale: Locale;\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n};\nexport type AuditContentDeclarationResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclaration = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { fileContent, filePath, aiOptions, locales, defaultLocale, tagsKeys } =\n request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditContentDeclarationUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys ?? [], project.organizationId);\n }\n\n const auditResponse = await auditContentDeclarationUtil.auditDictionary({\n fileContent,\n filePath,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n defaultLocale,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationFieldBody = {\n aiOptions?: AIOptions;\n locales: Locale[];\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n keyPath: KeyPath[];\n};\nexport type AuditContentDeclarationFieldResult =\n ResponseData<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationField = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationFieldBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { fileContent, aiOptions, locales, tagsKeys, keyPath } = request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditContentDeclarationFieldUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let tags: Tag[] = [];\n\n if (project?.organizationId) {\n tags = await getTagsByKeys(tagsKeys ?? [], project.organizationId);\n }\n\n const auditResponse =\n await auditContentDeclarationFieldUtil.auditDictionaryField({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n locales,\n tags,\n keyPath,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationFieldUtil.AuditDictionaryFieldResultData>(\n {\n data: auditResponse,\n }\n );\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditContentDeclarationMetadataBody = {\n aiOptions?: AIOptions;\n fileContent: string;\n};\n\nexport type AuditContentDeclarationMetadataResult =\n ResponseData<auditContentDeclarationMetadataUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationMetadata = async (\n request: FastifyRequest<{ Body: AuditContentDeclarationMetadataBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { organization, user } = request.session || {};\n const { fileContent, aiOptions } = request.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationMetadataUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n const tags: Tag[] = await tagService.findTags(\n {\n organizationId: organization?.id,\n },\n 0,\n 1000\n );\n\n const auditResponse =\n await auditContentDeclarationMetadataUtil.auditDictionaryMetadata({\n fileContent,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData =\n formatResponse<auditContentDeclarationMetadataUtil.AuditFileResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AuditTagBody = {\n aiOptions?: AIOptions;\n tag: TagAPI;\n};\nexport type AuditTagResult = ResponseData<auditTagUtil.TranslateJSONResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditTag = async (\n request: FastifyRequest<{ Body: AuditTagBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { project, user } = request.session || {};\n const { aiOptions, tag } = request.body;\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: auditTagUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n try {\n let dictionaries: Dictionary[] = [];\n if (project?.organizationId) {\n dictionaries = await getDictionariesByTags([tag.key], project.id);\n }\n\n const auditResponse = await auditTagUtil.auditTag({\n aiConfig,\n dictionaries,\n tag,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AUDIT_FAILED');\n }\n\n const responseData = formatResponse<auditTagUtil.TranslateJSONResultData>({\n data: auditResponse,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type AskDocQuestionBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type AskDocQuestionResult =\n ResponseData<askDocQuestionUtil.AskDocQuestionResult>;\n\nexport const askDocQuestion = async (\n request: FastifyRequest<{ Body: AskDocQuestionBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { messages = [], discussionId } = request.body;\n const { user, project, organization } = request.session || {};\n\n // Hijack response\n reply.hijack();\n\n // Copy all Fastify-managed headers (including CORS) to the raw response\n // immediately after hijacking, before any early returns.\n const headers = reply.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n reply.raw.setHeader(key, value);\n }\n }\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n let aiConfig: AIConfig;\n\n // Wrap EVERYTHING in a main try/catch block\n try {\n // Auth Check\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: {},\n projectOptions: projectAIOptions,\n accessType: ['public'],\n },\n !!user\n );\n } catch (error) {\n console.error(error);\n\n // Manually handle this specific error case\n const errorPayload = {\n code: 'AI_ACCESS_DENIED',\n title: 'Access Denied',\n message: 'Unable to configure AI access.',\n };\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n\n reply.raw.end();\n return;\n }\n\n // Set Stream Headers & Flush\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');\n\n if (reply.raw.flushHeaders) {\n reply.raw.flushHeaders();\n }\n\n reply.raw.write(': connected\\n\\n');\n\n // Execute AI Logic (Awaited properly)\n // This is where 'generateEmbedding' or 'streamText' will throw\n const fullResponse = await askDocQuestionUtil.askDocQuestion(\n messages,\n aiConfig,\n {\n onMessage: (chunk) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n }\n },\n }\n );\n\n // Persist Discussion (Only on success)\n const reversedMessages = [...messages].reverse();\n const lastUserMessageContent = reversedMessages.find(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords =\n typeof lastUserMessageContent === 'string'\n ? lastUserMessageContent.split(' ').length\n : 0;\n\n if (lastUserMessageNbWords >= 2 || messages.length >= 2) {\n const updatePayload: any = {\n discussionId,\n type: 'doc',\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp ?? new Date(),\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n relatedFiles: fullResponse.relatedFiles,\n timestamp: new Date(),\n },\n ],\n };\n\n if (user?.id) updatePayload.userId = user.id;\n if (project?.id) updatePayload.projectId = project.id;\n if (organization?.id) updatePayload.organizationId = organization.id;\n\n await DiscussionModel.findOneAndUpdate(\n { discussionId: String(discussionId) },\n { $set: updatePayload },\n { upsert: true, returnDocument: 'after' }\n );\n }\n\n // Send Completion Event\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n reply.raw.end();\n }\n } catch (err) {\n // -------------------------------------------------------------------------\n // CENTRALIZED ERROR CATCHER\n // -------------------------------------------------------------------------\n const errorMessage = err instanceof Error ? err.message : String(err);\n const errorStack = err instanceof Error ? err.stack : undefined;\n\n // Log the full error to your backend console\n logger.error('AI Stream Error Caught:', {\n message: errorMessage,\n stack: errorStack,\n });\n\n // Determine if it's an Auth error (common with OpenAI 401)\n const isAuthError =\n errorMessage.includes('401') ||\n errorMessage.includes('Incorrect API key');\n\n // Format error for Frontend\n const errorPayload = {\n code: isAuthError ? 'AI_AUTH_ERROR' : 'AI_STREAM_ERROR',\n title: isAuthError ? 'AI Configuration Error' : 'Generation Failed',\n message: errorMessage,\n };\n\n // Send error event to client\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n }\n }\n};\n\nexport type ChatBody = {\n messages: ChatCompletionRequestMessage[];\n discussionId: string;\n};\nexport type ChatResult = ResponseData<chatUtil.ChatResultData>;\n\nexport const chat = async (\n request: FastifyRequest<{ Body: ChatBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { messages = [], discussionId } = request.body;\n const { user, project, organization, roles } = request.session || {};\n\n reply.hijack();\n\n const headers = reply.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n reply.raw.setHeader(key, value);\n }\n }\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n try {\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: {},\n projectOptions: projectAIOptions,\n accessType: ['registered_user'],\n },\n !!user\n );\n } catch (error) {\n console.error(error);\n\n const errorPayload = {\n code: 'AI_ACCESS_DENIED',\n title: 'Access Denied',\n message: 'Unable to configure AI access.',\n };\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n return;\n }\n\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');\n\n if (reply.raw.flushHeaders) {\n reply.raw.flushHeaders();\n }\n\n reply.raw.write(': connected\\n\\n');\n\n const sessionTools = createSessionTools({\n projectId: project?.id ? String(project.id) : undefined,\n organizationId: organization?.id ? String(organization.id) : undefined,\n userId: user?.id ? String(user.id) : undefined,\n roles: roles || [],\n session: request.session,\n onAction: (action) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ action })}\\n\\n`);\n }\n },\n });\n\n const fullResponse = await chatUtil.chat(messages, aiConfig, {\n tools: sessionTools,\n onMessage: (chunk) => {\n if (!reply.raw.writableEnded) {\n reply.raw.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n }\n },\n });\n\n const reversedMessages = [...messages].reverse();\n const lastUserMessageContent = reversedMessages.find(\n (message) => message.role === 'user'\n )?.content;\n const lastUserMessageNbWords =\n typeof lastUserMessageContent === 'string'\n ? lastUserMessageContent.split(' ').length\n : 0;\n\n if (lastUserMessageNbWords >= 2 || messages.length >= 2) {\n const updatePayload: any = {\n discussionId,\n type: 'dashboard',\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp ?? new Date(),\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n timestamp: new Date(),\n },\n ],\n };\n\n if (user?.id) updatePayload.userId = user.id;\n if (project?.id) updatePayload.projectId = project.id;\n if (organization?.id) updatePayload.organizationId = organization.id;\n\n await DiscussionModel.findOneAndUpdate(\n { discussionId: String(discussionId) },\n { $set: updatePayload },\n { upsert: true, returnDocument: 'after' }\n );\n }\n\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n reply.raw.end();\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const errorStack = err instanceof Error ? err.stack : undefined;\n\n logger.error('AI Chat Stream Error:', {\n message: errorMessage,\n stack: errorStack,\n });\n\n const isAuthError =\n errorMessage.includes('401') ||\n errorMessage.includes('Incorrect API key');\n\n const errorPayload = {\n code: isAuthError ? 'AI_AUTH_ERROR' : 'AI_STREAM_ERROR',\n title: isAuthError ? 'AI Configuration Error' : 'Generation Failed',\n message: errorMessage,\n };\n\n if (!reply.raw.writableEnded) {\n reply.raw.write(\n `event: error\\ndata: ${JSON.stringify(errorPayload)}\\n\\n`\n );\n reply.raw.end();\n }\n }\n};\n\nexport type AutocompleteBody = {\n text: string;\n aiOptions?: AIOptions;\n contextBefore?: string;\n currentLine?: string;\n contextAfter?: string;\n};\n\nexport type AutocompleteResponse = ResponseData<{\n autocompletion: string;\n}>;\n\nexport const autocomplete = async (\n request: FastifyRequest<{ Body: AutocompleteBody }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, project } = request.session || {};\n\n const projectAIOptions = await getProjectAIOptions(project);\n\n try {\n const { text, aiOptions, contextBefore, currentLine, contextAfter } =\n request.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(\n {\n userOptions: aiOptions,\n projectOptions: projectAIOptions,\n defaultOptions: autocompleteUtil.aiDefaultOptions,\n accessType: ['public'],\n },\n !!user\n );\n } catch (_error) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'AI_ACCESS_DENIED');\n }\n\n const response = (await autocompleteUtil.autocomplete({\n text,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n contextBefore,\n currentLine,\n contextAfter,\n })) ?? {\n autocompletion: '',\n tokenUsed: 0,\n };\n\n const responseData =\n formatResponse<autocompleteUtil.AutocompleteFileResultData>({\n data: response,\n });\n\n return reply.send(responseData);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n\nexport type GetDiscussionsParams =\n | ({\n page?: string | number;\n pageSize?: string | number;\n includeMessages?: 'true' | 'false';\n } & DiscussionFiltersParams)\n | undefined;\n\nexport type GetDiscussionsResult = PaginatedResponse<DiscussionAPI>;\n\n/**\n * Retrieves a list of discussions with filters and pagination.\n * Only the owner or admins can access. By default, users only see their own.\n */\nexport const getDiscussions = async (\n request: FastifyRequest<{ Querystring: GetDiscussionsParams }>,\n reply: FastifyReply\n): Promise<void> => {\n const { user, roles } = request.session || {};\n const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } =\n getDiscussionFiltersAndPagination(request);\n const includeMessagesParam = (request.query as any)?.includeMessages as\n | 'true'\n | 'false'\n | undefined;\n const includeMessages = includeMessagesParam !== 'false';\n\n if (!user) {\n return ErrorHandler.handleGenericErrorResponse(reply, 'USER_NOT_DEFINED');\n }\n\n try {\n const projection = includeMessages ? {} : { messages: 0 };\n const discussions = await DiscussionModel.find(filters, projection)\n .sort(sortOptions)\n .skip(skip)\n .limit(pageSize)\n .lean();\n\n // Compute number of messages for each discussion\n const numberOfMessagesById: Record<string, number> = {};\n if (!includeMessages && discussions.length > 0) {\n const ids = discussions.map((d: any) => d._id);\n const counts = await DiscussionModel.aggregate([\n { $match: { _id: { $in: ids } } },\n {\n $project: {\n numberOfMessages: { $size: { $ifNull: ['$messages', []] } },\n },\n },\n ]);\n for (const c of counts as any[]) {\n numberOfMessagesById[String(c._id)] = c.numberOfMessages ?? 0;\n }\n }\n\n // Permission: allow admin, or the owner for all returned entries\n const allOwnedByUser = discussions.every(\n (d) => String(d.userId) === String(user.id)\n );\n const isAllowed = roles?.includes('admin') || allOwnedByUser;\n\n if (!isAllowed) {\n return ErrorHandler.handleGenericErrorResponse(\n reply,\n 'PERMISSION_DENIED'\n );\n }\n\n const totalItems = await DiscussionModel.countDocuments(filters);\n\n const responseData = formatPaginatedResponse({\n data: discussions.map((d: any) => ({\n ...d,\n id: String(d._id ?? d.id),\n numberOfMessages: includeMessages\n ? Array.isArray(d.messages)\n ? d.messages.length\n : 0\n : (numberOfMessagesById[String(d._id ?? d.id)] ?? 0),\n })),\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n return reply.send(responseData as any);\n } catch (error) {\n return ErrorHandler.handleAppErrorResponse(reply, error as AppError);\n }\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA2DA,MAAa,cAAc,OACzB,SACA,UACkB;CAClB,MAAM,EAAE,WAAW,UAAU,GAAG,SAAS,QAAQ;CACjD,MAAM,EAAE,MAAM,YAAY,QAAQ,WAAW,CAAC;CAE9C,MAAM,mBAAmB,MAAM,oBAAoB,OAAO;CAE1D,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,YACf;GACE,aAAa;GACb,gBAAgB;GAChB,gBAAgBA;GAChB,YAAY,CAAC,mBAAmB,QAAQ;EAC1C,GACA,CAAC,CAAC,IACJ;CACF,SAAS,QAAQ;EACf,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAC1E;CAEA,IAAI;EACF,MAAM,gBAAgB,MAAMC,cAA4B;GACtD,GAAG;GACH;EACF,CAAC;EAED,IAAI,CAAC,eACH,OAAO,aAAa,2BAA2B,OAAO,cAAc;EAGtE,MAAM,eAAe,eAAsD,EACzE,MAAM,cACR,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;AAYA,MAAa,gBAAgB,OAC3B,SACA,UACkB;CAClB,MAAM,EAAE,SAAS,SAAS,QAAQ,WAAW,CAAC;CAC9C,MAAM,EAAE,WAAW,UAAU,GAAG,SAAS,QAAQ;CAEjD,MAAM,mBAAmB,MAAM,oBAAoB,OAAO;CAE1D,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,YACf;GACE,aAAa;GACb,gBAAgB;GAChB,gBAAgBC;GAChB,YAAY,CAAC,mBAAmB,QAAQ;EAC1C,GACA,CAAC,CAAC,IACJ;CACF,SAAS,QAAQ;EACf,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAC1E;CAEA,IAAI;EACF,IAAI,OAAc,CAAC;EAEnB,IAAI,SAAS,kBAAkB,UAC7B,OAAO,MAAM,cAAc,UAAU,QAAQ,cAAc;EAG7D,MAAM,gBAAgB,MAAMC,gBAAqC;GAC/D,GAAG;GACH;GACA,oBAAoB,WAAW;GAC/B;EACF,CAAC;EAED,IAAI,CAAC,eACH,OAAO,aAAa,2BAA2B,OAAO,cAAc;EAGtE,MAAM,eAAe,eAEnB,EACA,MAAM,cACR,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAgBA,MAAa,0BAA0B,OACrC,SACA,UACkB;CAClB,MAAM,EAAE,SAAS,SAAS,QAAQ,WAAW,CAAC;CAC9C,MAAM,EAAE,aAAa,UAAU,WAAW,SAAS,eAAe,aAChE,QAAQ;CAEV,MAAM,mBAAmB,MAAM,oBAAoB,OAAO;CAE1D,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,YACf;GACE,aAAa;GACb,gBAAgB;GAChB,gBAAgBC;GAChB,YAAY,CAAC,mBAAmB,QAAQ;EAC1C,GACA,CAAC,CAAC,IACJ;CACF,SAAS,QAAQ;EACf,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAC1E;CAEA,IAAI;EACF,IAAI,OAAc,CAAC;EAEnB,IAAI,SAAS,gBACX,OAAO,MAAM,cAAc,YAAY,CAAC,GAAG,QAAQ,cAAc;EAGnE,MAAM,gBAAgB,MAAMC,gBAA4C;GACtE;GACA;GACA;GACA,oBAAoB,WAAW;GAC/B;GACA;GACA;EACF,CAAC;EAED,IAAI,CAAC,eACH,OAAO,aAAa,2BAA2B,OAAO,cAAc;EAGtE,MAAM,eACJ,eAAgE,EAC9D,MAAM,cACR,CAAC;EAEH,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAgBA,MAAa,+BAA+B,OAC1C,SACA,UACkB;CAClB,MAAM,EAAE,SAAS,SAAS,QAAQ,WAAW,CAAC;CAC9C,MAAM,EAAE,aAAa,WAAW,SAAS,UAAU,YAAY,QAAQ;CAEvE,MAAM,mBAAmB,MAAM,oBAAoB,OAAO;CAE1D,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,YACf;GACE,aAAa;GACb,gBAAgB;GAChB,gBAAgBC;GAChB,YAAY,CAAC,mBAAmB,QAAQ;EAC1C,GACA,CAAC,CAAC,IACJ;CACF,SAAS,QAAQ;EACf,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAC1E;CAEA,IAAI;EACF,IAAI,OAAc,CAAC;EAEnB,IAAI,SAAS,gBACX,OAAO,MAAM,cAAc,YAAY,CAAC,GAAG,QAAQ,cAAc;EAGnE,MAAM,gBACJ,MAAMC,qBAAsD;GAC1D;GACA;GACA,oBAAoB,WAAW;GAC/B;GACA;GACA;EACF,CAAC;EAEH,IAAI,CAAC,eACH,OAAO,aAAa,2BAA2B,OAAO,cAAc;EAGtE,MAAM,eACJ,eACE,EACE,MAAM,cACR,CACF;EAEF,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAaA,MAAa,kCAAkC,OAC7C,SACA,UACkB;CAClB,MAAM,EAAE,cAAc,SAAS,QAAQ,WAAW,CAAC;CACnD,MAAM,EAAE,aAAa,cAAc,QAAQ;CAE3C,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,YACf;GACE,aAAa;GACb,gBAAgBC;GAChB,YAAY,CAAC,mBAAmB,QAAQ;EAC1C,GACA,CAAC,CAAC,IACJ;CACF,SAAS,QAAQ;EACf,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAC1E;CAEA,IAAI;EACF,MAAM,OAAc,MAAMC,SACxB,EACE,gBAAgB,cAAc,GAChC,GACA,GACA,GACF;EAEA,MAAM,gBACJ,MAAMC,0BAA4D;GAChE;GACA;GACA,oBAAoB,WAAW;GAC/B;EACF,CAAC;EAEH,IAAI,CAAC,eACH,OAAO,aAAa,2BAA2B,OAAO,cAAc;EAGtE,MAAM,eACJ,eAAwE,EACtE,MAAM,cACR,CAAC;EAEH,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;AAWA,MAAa,WAAW,OACtB,SACA,UACkB;CAClB,MAAM,EAAE,SAAS,SAAS,QAAQ,WAAW,CAAC;CAC9C,MAAM,EAAE,WAAW,QAAQ,QAAQ;CAEnC,MAAM,mBAAmB,MAAM,oBAAoB,OAAO;CAE1D,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,YACf;GACE,aAAa;GACb,gBAAgB;GAChB,gBAAgBC;GAChB,YAAY,CAAC,mBAAmB,QAAQ;EAC1C,GACA,CAAC,CAAC,IACJ;CACF,SAAS,QAAQ;EACf,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAC1E;CAEA,IAAI;EACF,IAAI,eAA6B,CAAC;EAClC,IAAI,SAAS,gBACX,eAAe,MAAM,sBAAsB,CAAC,IAAI,GAAG,GAAG,QAAQ,EAAE;EAGlE,MAAM,gBAAgB,MAAMC,WAAsB;GAChD;GACA;GACA;GACA,oBAAoB,WAAW;EACjC,CAAC;EAED,IAAI,CAAC,eACH,OAAO,aAAa,2BAA2B,OAAO,cAAc;EAGtE,MAAM,eAAe,eAAqD,EACxE,MAAM,cACR,CAAC;EAED,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;AASA,MAAa,iBAAiB,OAC5B,SACA,UACkB;CAClB,MAAM,EAAE,WAAW,CAAC,GAAG,iBAAiB,QAAQ;CAChD,MAAM,EAAE,MAAM,SAAS,iBAAiB,QAAQ,WAAW,CAAC;CAG5D,MAAM,OAAO;CAIb,MAAM,UAAU,MAAM,WAAW;CACjC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAC/C,IAAI,UAAU,QACZ,MAAM,IAAI,UAAU,KAAK,KAAK;CAIlC,MAAM,mBAAmB,MAAM,oBAAoB,OAAO;CAE1D,IAAI;CAGJ,IAAI;EAEF,IAAI;GACF,WAAW,MAAM,YACf;IACE,aAAa,CAAC;IACd,gBAAgB;IAChB,YAAY,CAAC,QAAQ;GACvB,GACA,CAAC,CAAC,IACJ;EACF,SAAS,OAAO;GACd,QAAQ,MAAM,KAAK;GAQnB,MAAM,IAAI,MACR,uBAAuB,KAAK,UAAU;IALtC,MAAM;IACN,OAAO;IACP,SAAS;GAGwC,CAAC,EAAE,KACtD;GAEA,MAAM,IAAI,IAAI;GACd;EACF;EAGA,MAAM,IAAI,UAAU,gBAAgB,kCAAkC;EACtE,MAAM,IAAI,UAAU,iBAAiB,wBAAwB;EAC7D,MAAM,IAAI,UAAU,cAAc,YAAY;EAC9C,MAAM,IAAI,UAAU,qBAAqB,IAAI;EAE7C,IAAI,MAAM,IAAI,cACZ,MAAM,IAAI,aAAa;EAGzB,MAAM,IAAI,MAAM,iBAAiB;EAIjC,MAAM,eAAe,MAAMC,iBACzB,UACA,UACA,EACE,YAAY,UAAU;GACpB,IAAI,CAAC,MAAM,IAAI,eACb,MAAM,IAAI,MAAM,SAAS,KAAK,UAAU,EAAE,MAAM,CAAC,EAAE,KAAK;EAE5D,EACF,CACF;EAIA,MAAM,yBADmB,CAAC,GAAG,QAAQ,EAAE,QACO,EAAE,MAC7C,YAAY,QAAQ,SAAS,MAChC,GAAG;EAMH,KAJE,OAAO,2BAA2B,WAC9B,uBAAuB,MAAM,GAAG,EAAE,SAClC,MAEwB,KAAK,SAAS,UAAU,GAAG;GACvD,MAAM,gBAAqB;IACzB;IACA,MAAM;IACN,UAAU,CACR,GAAG,SAAS,KAAK,SAAS;KACxB,MAAM,IAAI;KACV,SAAS,IAAI;KACb,WAAW,IAAI,6BAAa,IAAI,KAAK;IACvC,EAAE,GACF;KACE,MAAM;KACN,SAAS,aAAa;KACtB,cAAc,aAAa;KAC3B,2BAAW,IAAI,KAAK;IACtB,CACF;GACF;GAEA,IAAI,MAAM,IAAI,cAAc,SAAS,KAAK;GAC1C,IAAI,SAAS,IAAI,cAAc,YAAY,QAAQ;GACnD,IAAI,cAAc,IAAI,cAAc,iBAAiB,aAAa;GAElE,MAAM,gBAAgB,iBACpB,EAAE,cAAc,OAAO,YAAY,EAAE,GACrC,EAAE,MAAM,cAAc,GACtB;IAAE,QAAQ;IAAM,gBAAgB;GAAQ,CAC1C;EACF;EAGA,IAAI,CAAC,MAAM,IAAI,eAAe;GAC5B,MAAM,IAAI,MACR,SAAS,KAAK,UAAU;IAAE,MAAM;IAAM,UAAU;GAAa,CAAC,EAAE,KAClE;GACA,MAAM,IAAI,IAAI;EAChB;CACF,SAAS,KAAK;EAIZ,MAAM,eAAe,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;EACpE,MAAM,aAAa,eAAe,QAAQ,IAAI,QAAQ;EAGtD,OAAO,MAAM,2BAA2B;GACtC,SAAS;GACT,OAAO;EACT,CAAC;EAGD,MAAM,cACJ,aAAa,SAAS,KAAK,KAC3B,aAAa,SAAS,mBAAmB;EAG3C,MAAM,eAAe;GACnB,MAAM,cAAc,kBAAkB;GACtC,OAAO,cAAc,2BAA2B;GAChD,SAAS;EACX;EAGA,IAAI,CAAC,MAAM,IAAI,eAAe;GAC5B,MAAM,IAAI,MACR,uBAAuB,KAAK,UAAU,YAAY,EAAE,KACtD;GACA,MAAM,IAAI,IAAI;EAChB;CACF;AACF;AAQA,MAAa,OAAO,OAClB,SACA,UACkB;CAClB,MAAM,EAAE,WAAW,CAAC,GAAG,iBAAiB,QAAQ;CAChD,MAAM,EAAE,MAAM,SAAS,cAAc,UAAU,QAAQ,WAAW,CAAC;CAEnE,MAAM,OAAO;CAEb,MAAM,UAAU,MAAM,WAAW;CACjC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAC/C,IAAI,UAAU,QACZ,MAAM,IAAI,UAAU,KAAK,KAAK;CAIlC,MAAM,mBAAmB,MAAM,oBAAoB,OAAO;CAE1D,IAAI;EACF,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,YACf;IACE,aAAa,CAAC;IACd,gBAAgB;IAChB,YAAY,CAAC,iBAAiB;GAChC,GACA,CAAC,CAAC,IACJ;EACF,SAAS,OAAO;GACd,QAAQ,MAAM,KAAK;GAOnB,MAAM,IAAI,MACR,uBAAuB,KAAK,UAAU;IALtC,MAAM;IACN,OAAO;IACP,SAAS;GAGwC,CAAC,EAAE,KACtD;GACA,MAAM,IAAI,IAAI;GACd;EACF;EAEA,MAAM,IAAI,UAAU,gBAAgB,kCAAkC;EACtE,MAAM,IAAI,UAAU,iBAAiB,wBAAwB;EAC7D,MAAM,IAAI,UAAU,cAAc,YAAY;EAC9C,MAAM,IAAI,UAAU,qBAAqB,IAAI;EAE7C,IAAI,MAAM,IAAI,cACZ,MAAM,IAAI,aAAa;EAGzB,MAAM,IAAI,MAAM,iBAAiB;EAEjC,MAAM,eAAe,mBAAmB;GACtC,WAAW,SAAS,KAAK,OAAO,QAAQ,EAAE,IAAI;GAC9C,gBAAgB,cAAc,KAAK,OAAO,aAAa,EAAE,IAAI;GAC7D,QAAQ,MAAM,KAAK,OAAO,KAAK,EAAE,IAAI;GACrC,OAAO,SAAS,CAAC;GACjB,SAAS,QAAQ;GACjB,WAAW,WAAW;IACpB,IAAI,CAAC,MAAM,IAAI,eACb,MAAM,IAAI,MAAM,SAAS,KAAK,UAAU,EAAE,OAAO,CAAC,EAAE,KAAK;GAE7D;EACF,CAAC;EAED,MAAM,eAAe,MAAMC,OAAc,UAAU,UAAU;GAC3D,OAAO;GACP,YAAY,UAAU;IACpB,IAAI,CAAC,MAAM,IAAI,eACb,MAAM,IAAI,MAAM,SAAS,KAAK,UAAU,EAAE,MAAM,CAAC,EAAE,KAAK;GAE5D;EACF,CAAC;EAGD,MAAM,yBADmB,CAAC,GAAG,QAAQ,EAAE,QACO,EAAE,MAC7C,YAAY,QAAQ,SAAS,MAChC,GAAG;EAMH,KAJE,OAAO,2BAA2B,WAC9B,uBAAuB,MAAM,GAAG,EAAE,SAClC,MAEwB,KAAK,SAAS,UAAU,GAAG;GACvD,MAAM,gBAAqB;IACzB;IACA,MAAM;IACN,UAAU,CACR,GAAG,SAAS,KAAK,SAAS;KACxB,MAAM,IAAI;KACV,SAAS,IAAI;KACb,WAAW,IAAI,6BAAa,IAAI,KAAK;IACvC,EAAE,GACF;KACE,MAAM;KACN,SAAS,aAAa;KACtB,2BAAW,IAAI,KAAK;IACtB,CACF;GACF;GAEA,IAAI,MAAM,IAAI,cAAc,SAAS,KAAK;GAC1C,IAAI,SAAS,IAAI,cAAc,YAAY,QAAQ;GACnD,IAAI,cAAc,IAAI,cAAc,iBAAiB,aAAa;GAElE,MAAM,gBAAgB,iBACpB,EAAE,cAAc,OAAO,YAAY,EAAE,GACrC,EAAE,MAAM,cAAc,GACtB;IAAE,QAAQ;IAAM,gBAAgB;GAAQ,CAC1C;EACF;EAEA,IAAI,CAAC,MAAM,IAAI,eAAe;GAC5B,MAAM,IAAI,MACR,SAAS,KAAK,UAAU;IAAE,MAAM;IAAM,UAAU;GAAa,CAAC,EAAE,KAClE;GACA,MAAM,IAAI,IAAI;EAChB;CACF,SAAS,KAAK;EACZ,MAAM,eAAe,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;EACpE,MAAM,aAAa,eAAe,QAAQ,IAAI,QAAQ;EAEtD,OAAO,MAAM,yBAAyB;GACpC,SAAS;GACT,OAAO;EACT,CAAC;EAED,MAAM,cACJ,aAAa,SAAS,KAAK,KAC3B,aAAa,SAAS,mBAAmB;EAE3C,MAAM,eAAe;GACnB,MAAM,cAAc,kBAAkB;GACtC,OAAO,cAAc,2BAA2B;GAChD,SAAS;EACX;EAEA,IAAI,CAAC,MAAM,IAAI,eAAe;GAC5B,MAAM,IAAI,MACR,uBAAuB,KAAK,UAAU,YAAY,EAAE,KACtD;GACA,MAAM,IAAI,IAAI;EAChB;CACF;AACF;AAcA,MAAa,eAAe,OAC1B,SACA,UACkB;CAClB,MAAM,EAAE,MAAM,YAAY,QAAQ,WAAW,CAAC;CAE9C,MAAM,mBAAmB,MAAM,oBAAoB,OAAO;CAE1D,IAAI;EACF,MAAM,EAAE,MAAM,WAAW,eAAe,aAAa,iBACnD,QAAQ;EAEV,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,YACf;IACE,aAAa;IACb,gBAAgB;IAChB,gBAAgBC;IAChB,YAAY,CAAC,QAAQ;GACvB,GACA,CAAC,CAAC,IACJ;EACF,SAAS,QAAQ;GACf,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;EAC1E;EAcA,MAAM,eACJ,eAA4D,EAC1D,MAdc,MAAMC,eAA8B;GACpD;GACA;GACA,oBAAoB,WAAW;GAC/B;GACA;GACA;EACF,CAAC,KAAM;GACL,gBAAgB;GAChB,WAAW;EACb,EAKE,CAAC;EAEH,OAAO,MAAM,KAAK,YAAY;CAChC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF;;;;;AAgBA,MAAa,iBAAiB,OAC5B,SACA,UACkB;CAClB,MAAM,EAAE,MAAM,UAAU,QAAQ,WAAW,CAAC;CAC5C,MAAM,EAAE,SAAS,aAAa,UAAU,MAAM,MAAM,qBAClD,kCAAkC,OAAO;CAK3C,MAAM,kBAJwB,QAAQ,OAAe,oBAIJ;CAEjD,IAAI,CAAC,MACH,OAAO,aAAa,2BAA2B,OAAO,kBAAkB;CAG1E,IAAI;EACF,MAAM,aAAa,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE;EACxD,MAAM,cAAc,MAAM,gBAAgB,KAAK,SAAS,UAAU,EAC/D,KAAK,WAAW,EAChB,KAAK,IAAI,EACT,MAAM,QAAQ,EACd,KAAK;EAGR,MAAM,uBAA+C,CAAC;EACtD,IAAI,CAAC,mBAAmB,YAAY,SAAS,GAAG;GAC9C,MAAM,MAAM,YAAY,KAAK,MAAW,EAAE,GAAG;GAC7C,MAAM,SAAS,MAAM,gBAAgB,UAAU,CAC7C,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,EAAE,GAChC,EACE,UAAU,EACR,kBAAkB,EAAE,OAAO,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC,EAAE,EAAE,EAC5D,EACF,CACF,CAAC;GACD,KAAK,MAAM,KAAK,QACd,qBAAqB,OAAO,EAAE,GAAG,KAAK,EAAE,oBAAoB;EAEhE;EAGA,MAAM,iBAAiB,YAAY,OAChC,MAAM,OAAO,EAAE,MAAM,MAAM,OAAO,KAAK,EAAE,CAC5C;EAGA,IAAI,EAFc,OAAO,SAAS,OAAO,KAAK,iBAG5C,OAAO,aAAa,2BAClB,OACA,mBACF;EAGF,MAAM,aAAa,MAAM,gBAAgB,eAAe,OAAO;EAE/D,MAAM,eAAe,wBAAwB;GAC3C,MAAM,YAAY,KAAK,OAAY;IACjC,GAAG;IACH,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE;IACxB,kBAAkB,kBACd,MAAM,QAAQ,EAAE,QAAQ,IACtB,EAAE,SAAS,SACX,IACD,qBAAqB,OAAO,EAAE,OAAO,EAAE,EAAE,MAAM;GACtD,EAAE;GACF;GACA;GACA,YAAY,iBAAiB,UAAU;GACvC;EACF,CAAC;EAED,OAAO,MAAM,KAAK,YAAmB;CACvC,SAAS,OAAO;EACd,OAAO,aAAa,uBAAuB,OAAO,KAAiB;CACrE;AACF"}
@@ -1,5 +1,5 @@
1
1
  import { logger } from "../logger/index.mjs";
2
- import { AuditModel } from "../models/audit.model.mjs";
2
+ import { AuditModel } from "../schemas/audit.schema.mjs";
3
3
  import { mutateScore } from "../services/audit/analysis/calculateScore.mjs";
4
4
  import { runSingleAudit } from "../services/audit/seoAudit.service.mjs";
5
5
  import { lookup } from "node:dns/promises";
@@ -1 +1 @@
1
- {"version":3,"file":"audit.controller.mjs","names":["dnsLookup"],"sources":["../../../src/controllers/audit.controller.ts"],"sourcesContent":["import { lookup as dnsLookup } from 'node:dns/promises';\nimport net from 'node:net';\nimport { logger } from '@logger';\nimport { AuditModel } from '@models/audit.model';\nimport {\n mutateScore,\n type Score,\n} from '@services/audit/analysis/calculateScore';\nimport { runSingleAudit } from '@services/audit/seoAudit.service';\nimport type { AuditEvent } from '@services/audit/types';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\n/**\n * Returns true if the given IP address is private, loopback, or link-local.\n * Covers IPv4 and IPv6.\n */\nconst isPrivateOrReservedIp = (ip: string): boolean => {\n if (net.isIPv4(ip)) {\n const parts = ip.split('.').map(Number);\n const [a, b] = parts;\n // Loopback: 127.0.0.0/8\n if (a === 127) return true;\n // Any address starting with 0\n if (a === 0) return true;\n // Private: 10.0.0.0/8\n if (a === 10) return true;\n // Private: 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) return true;\n // Private: 192.168.0.0/16\n if (a === 192 && b === 168) return true;\n // Link-local: 169.254.0.0/16\n if (a === 169 && b === 254) return true;\n return false;\n }\n\n if (net.isIPv6(ip)) {\n const normalized = ip.toLowerCase();\n // Loopback: ::1\n if (normalized === '::1') return true;\n // Link-local: fe80::/10\n if (\n normalized.startsWith('fe80:') ||\n normalized.startsWith('fe8') ||\n normalized.startsWith('fe9') ||\n normalized.startsWith('fea') ||\n normalized.startsWith('feb')\n )\n return true;\n // IPv4-mapped: ::ffff:x.x.x.x — check the embedded IPv4\n const ipv4MappedMatch = normalized.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/);\n if (ipv4MappedMatch) return isPrivateOrReservedIp(ipv4MappedMatch[1]);\n return false;\n }\n\n // Unknown format — block it\n return true;\n};\n\nconst sendSSE = (res: FastifyReply, data: AuditEvent) => {\n res.raw.write(`data: ${JSON.stringify(data)}\\n\\n`);\n};\n\n/**\n * GET /api/scan?url=<targetUrl>\n * Streams audit results as Server-Sent Events.\n */\nexport const auditGetHandler = async (\n req: FastifyRequest,\n res: FastifyReply\n) => {\n res.hijack();\n\n const headers = res.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n res.raw.setHeader(key, value as string | number | readonly string[]);\n }\n }\n\n res.raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n res.raw.setHeader('Cache-Control', 'no-cache, no-transform');\n res.raw.setHeader('Connection', 'keep-alive');\n res.raw.setHeader('X-Accel-Buffering', 'no');\n\n if ((res.raw as any).flushHeaders) {\n (res.raw as any).flushHeaders();\n }\n\n res.raw.write(': connected\\n\\n');\n\n const { url: targetUrl } = req.query as { url?: string };\n\n let score: Score = { score: 0, totalScore: 0 };\n let currentProgress = 0;\n\n if (!targetUrl) {\n sendSSE(res, { status: 'error', globalError: 'Missing URL parameter' });\n res.raw.end();\n return;\n }\n\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(targetUrl);\n } catch {\n sendSSE(res, { status: 'error', globalError: 'Invalid URL format' });\n res.raw.end();\n return;\n }\n\n // Only allow http: and https: — block file://, data:, ftp://, etc.\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n sendSSE(res, {\n status: 'error',\n globalError: 'Only http and https URLs are allowed',\n });\n res.raw.end();\n return;\n }\n\n // Resolve the hostname and block private / loopback / link-local addresses (SSRF prevention)\n try {\n const { address } = await dnsLookup(parsedUrl.hostname);\n if (isPrivateOrReservedIp(address)) {\n sendSSE(res, {\n status: 'error',\n globalError: 'URL resolves to a private or reserved address',\n });\n res.raw.end();\n return;\n }\n } catch {\n sendSSE(res, {\n status: 'error',\n globalError: 'Could not resolve hostname',\n });\n res.raw.end();\n return;\n }\n\n let aborted = false;\n\n req.raw.on('close', () => {\n logger.info('Client connection closed');\n aborted = true;\n });\n\n try {\n await runSingleAudit(targetUrl, (event) => {\n score = mutateScore(score, event);\n\n if (event.progress !== undefined) {\n currentProgress = event.progress;\n }\n\n if (!aborted) {\n sendSSE(res, {\n ...event,\n progress: currentProgress,\n score: Math.round(\n score.totalScore > 0 ? (score.score / score.totalScore) * 100 : 0\n ),\n });\n }\n });\n\n try {\n const url = new URL(targetUrl);\n const domain = url.hostname;\n const finalScore = Math.round(\n score.totalScore > 0 ? (score.score / score.totalScore) * 100 : 0\n );\n\n const audit = new AuditModel({ domain, score: finalScore });\n await audit.save();\n logger.info(\n `Audit saved for domain: ${domain} with score: ${finalScore}`\n );\n } catch (dbError) {\n logger.error('Failed to save audit to database:', dbError);\n }\n\n res.raw.end();\n } catch (error) {\n logger.error('Audit GET error:', error);\n if (!aborted) {\n sendSSE(res, {\n globalError:\n error instanceof Error ? error.message : 'Internal server error',\n });\n res.raw.end();\n }\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"audit.controller.mjs","names":["dnsLookup"],"sources":["../../../src/controllers/audit.controller.ts"],"sourcesContent":["import { lookup as dnsLookup } from 'node:dns/promises';\nimport net from 'node:net';\nimport { logger } from '@logger';\nimport { AuditModel } from '@schemas/audit.schema';\nimport {\n mutateScore,\n type Score,\n} from '@services/audit/analysis/calculateScore';\nimport { runSingleAudit } from '@services/audit/seoAudit.service';\nimport type { AuditEvent } from '@services/audit/types';\nimport type { FastifyReply, FastifyRequest } from 'fastify';\n\n/**\n * Returns true if the given IP address is private, loopback, or link-local.\n * Covers IPv4 and IPv6.\n */\nconst isPrivateOrReservedIp = (ip: string): boolean => {\n if (net.isIPv4(ip)) {\n const parts = ip.split('.').map(Number);\n const [a, b] = parts;\n // Loopback: 127.0.0.0/8\n if (a === 127) return true;\n // Any address starting with 0\n if (a === 0) return true;\n // Private: 10.0.0.0/8\n if (a === 10) return true;\n // Private: 172.16.0.0/12\n if (a === 172 && b >= 16 && b <= 31) return true;\n // Private: 192.168.0.0/16\n if (a === 192 && b === 168) return true;\n // Link-local: 169.254.0.0/16\n if (a === 169 && b === 254) return true;\n return false;\n }\n\n if (net.isIPv6(ip)) {\n const normalized = ip.toLowerCase();\n // Loopback: ::1\n if (normalized === '::1') return true;\n // Link-local: fe80::/10\n if (\n normalized.startsWith('fe80:') ||\n normalized.startsWith('fe8') ||\n normalized.startsWith('fe9') ||\n normalized.startsWith('fea') ||\n normalized.startsWith('feb')\n )\n return true;\n // IPv4-mapped: ::ffff:x.x.x.x — check the embedded IPv4\n const ipv4MappedMatch = normalized.match(/^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)$/);\n if (ipv4MappedMatch) return isPrivateOrReservedIp(ipv4MappedMatch[1]);\n return false;\n }\n\n // Unknown format — block it\n return true;\n};\n\nconst sendSSE = (res: FastifyReply, data: AuditEvent) => {\n res.raw.write(`data: ${JSON.stringify(data)}\\n\\n`);\n};\n\n/**\n * GET /api/scan?url=<targetUrl>\n * Streams audit results as Server-Sent Events.\n */\nexport const auditGetHandler = async (\n req: FastifyRequest,\n res: FastifyReply\n) => {\n res.hijack();\n\n const headers = res.getHeaders();\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n res.raw.setHeader(key, value as string | number | readonly string[]);\n }\n }\n\n res.raw.setHeader('Content-Type', 'text/event-stream; charset=utf-8');\n res.raw.setHeader('Cache-Control', 'no-cache, no-transform');\n res.raw.setHeader('Connection', 'keep-alive');\n res.raw.setHeader('X-Accel-Buffering', 'no');\n\n if ((res.raw as any).flushHeaders) {\n (res.raw as any).flushHeaders();\n }\n\n res.raw.write(': connected\\n\\n');\n\n const { url: targetUrl } = req.query as { url?: string };\n\n let score: Score = { score: 0, totalScore: 0 };\n let currentProgress = 0;\n\n if (!targetUrl) {\n sendSSE(res, { status: 'error', globalError: 'Missing URL parameter' });\n res.raw.end();\n return;\n }\n\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(targetUrl);\n } catch {\n sendSSE(res, { status: 'error', globalError: 'Invalid URL format' });\n res.raw.end();\n return;\n }\n\n // Only allow http: and https: — block file://, data:, ftp://, etc.\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n sendSSE(res, {\n status: 'error',\n globalError: 'Only http and https URLs are allowed',\n });\n res.raw.end();\n return;\n }\n\n // Resolve the hostname and block private / loopback / link-local addresses (SSRF prevention)\n try {\n const { address } = await dnsLookup(parsedUrl.hostname);\n if (isPrivateOrReservedIp(address)) {\n sendSSE(res, {\n status: 'error',\n globalError: 'URL resolves to a private or reserved address',\n });\n res.raw.end();\n return;\n }\n } catch {\n sendSSE(res, {\n status: 'error',\n globalError: 'Could not resolve hostname',\n });\n res.raw.end();\n return;\n }\n\n let aborted = false;\n\n req.raw.on('close', () => {\n logger.info('Client connection closed');\n aborted = true;\n });\n\n try {\n await runSingleAudit(targetUrl, (event) => {\n score = mutateScore(score, event);\n\n if (event.progress !== undefined) {\n currentProgress = event.progress;\n }\n\n if (!aborted) {\n sendSSE(res, {\n ...event,\n progress: currentProgress,\n score: Math.round(\n score.totalScore > 0 ? (score.score / score.totalScore) * 100 : 0\n ),\n });\n }\n });\n\n try {\n const url = new URL(targetUrl);\n const domain = url.hostname;\n const finalScore = Math.round(\n score.totalScore > 0 ? (score.score / score.totalScore) * 100 : 0\n );\n\n const audit = new AuditModel({ domain, score: finalScore });\n await audit.save();\n logger.info(\n `Audit saved for domain: ${domain} with score: ${finalScore}`\n );\n } catch (dbError) {\n logger.error('Failed to save audit to database:', dbError);\n }\n\n res.raw.end();\n } catch (error) {\n logger.error('Audit GET error:', error);\n if (!aborted) {\n sendSSE(res, {\n globalError:\n error instanceof Error ? error.message : 'Internal server error',\n });\n res.raw.end();\n }\n }\n};\n"],"mappings":"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"}
@@ -5,7 +5,7 @@ import { countOrganizations, createOrganization, deleteOrganizationById, findOrg
5
5
  import { findProjects } from "../services/project.service.mjs";
6
6
  import { createUser, getUserByEmail, getUsersByIds, updateUserById } from "../services/user.service.mjs";
7
7
  import { hasPermission } from "../utils/permissions.mjs";
8
- import { SessionModel } from "../models/session.model.mjs";
8
+ import { SessionModel } from "../schemas/session.schema.mjs";
9
9
  import { sendEmail } from "../services/email.service.mjs";
10
10
  import { getOrganizationFiltersAndPagination } from "../utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs";
11
11
  import { mapOrganizationToAPI, mapOrganizationsToAPI } from "../utils/mapper/organization.mjs";