@intlayer/backend 5.7.2-canary.3 → 5.7.3

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 (108) hide show
  1. package/dist/cjs/controllers/ai.controller.cjs.map +1 -1
  2. package/dist/cjs/controllers/dictionary.controller.cjs +39 -20
  3. package/dist/cjs/controllers/dictionary.controller.cjs.map +1 -1
  4. package/dist/cjs/controllers/newsletter.controller.cjs.map +1 -1
  5. package/dist/cjs/controllers/organization.controller.cjs +51 -19
  6. package/dist/cjs/controllers/organization.controller.cjs.map +1 -1
  7. package/dist/cjs/controllers/project.controller.cjs +42 -12
  8. package/dist/cjs/controllers/project.controller.cjs.map +1 -1
  9. package/dist/cjs/controllers/projectAccessKey.controller.cjs +21 -3
  10. package/dist/cjs/controllers/projectAccessKey.controller.cjs.map +1 -1
  11. package/dist/cjs/controllers/search.controller.cjs.map +1 -1
  12. package/dist/cjs/controllers/stripe.controller.cjs.map +1 -1
  13. package/dist/cjs/controllers/tag.controller.cjs +48 -17
  14. package/dist/cjs/controllers/tag.controller.cjs.map +1 -1
  15. package/dist/cjs/controllers/user.controller.cjs +7 -1
  16. package/dist/cjs/controllers/user.controller.cjs.map +1 -1
  17. package/dist/cjs/middlewares/sessionAuth.middleware.cjs.map +1 -1
  18. package/dist/cjs/routes/ai.routes.cjs.map +1 -1
  19. package/dist/cjs/routes/dictionary.routes.cjs.map +1 -1
  20. package/dist/cjs/routes/eventListener.routes.cjs.map +1 -1
  21. package/dist/cjs/routes/newsletter.routes.cjs.map +1 -1
  22. package/dist/cjs/routes/organization.routes.cjs.map +1 -1
  23. package/dist/cjs/routes/project.routes.cjs +3 -12
  24. package/dist/cjs/routes/project.routes.cjs.map +1 -1
  25. package/dist/cjs/routes/search.routes.cjs.map +1 -1
  26. package/dist/cjs/routes/stripe.routes.cjs.map +1 -1
  27. package/dist/cjs/routes/tags.routes.cjs.map +1 -1
  28. package/dist/cjs/routes/user.routes.cjs.map +1 -1
  29. package/dist/cjs/schemas/tag.schema.cjs +5 -0
  30. package/dist/cjs/schemas/tag.schema.cjs.map +1 -1
  31. package/dist/cjs/services/dictionary.service.cjs +0 -8
  32. package/dist/cjs/services/dictionary.service.cjs.map +1 -1
  33. package/dist/cjs/services/organization.service.cjs +0 -9
  34. package/dist/cjs/services/organization.service.cjs.map +1 -1
  35. package/dist/cjs/types/tag.types.cjs.map +1 -1
  36. package/dist/cjs/utils/auth/getAuth.cjs +1 -0
  37. package/dist/cjs/utils/auth/getAuth.cjs.map +1 -1
  38. package/dist/cjs/utils/permissions.cjs +100 -22
  39. package/dist/cjs/utils/permissions.cjs.map +1 -1
  40. package/dist/esm/controllers/ai.controller.mjs.map +1 -1
  41. package/dist/esm/controllers/dictionary.controller.mjs +39 -20
  42. package/dist/esm/controllers/dictionary.controller.mjs.map +1 -1
  43. package/dist/esm/controllers/newsletter.controller.mjs.map +1 -1
  44. package/dist/esm/controllers/organization.controller.mjs +51 -19
  45. package/dist/esm/controllers/organization.controller.mjs.map +1 -1
  46. package/dist/esm/controllers/project.controller.mjs +42 -12
  47. package/dist/esm/controllers/project.controller.mjs.map +1 -1
  48. package/dist/esm/controllers/projectAccessKey.controller.mjs +21 -3
  49. package/dist/esm/controllers/projectAccessKey.controller.mjs.map +1 -1
  50. package/dist/esm/controllers/search.controller.mjs.map +1 -1
  51. package/dist/esm/controllers/stripe.controller.mjs.map +1 -1
  52. package/dist/esm/controllers/tag.controller.mjs +48 -17
  53. package/dist/esm/controllers/tag.controller.mjs.map +1 -1
  54. package/dist/esm/controllers/user.controller.mjs +7 -1
  55. package/dist/esm/controllers/user.controller.mjs.map +1 -1
  56. package/dist/esm/middlewares/sessionAuth.middleware.mjs.map +1 -1
  57. package/dist/esm/routes/ai.routes.mjs.map +1 -1
  58. package/dist/esm/routes/dictionary.routes.mjs.map +1 -1
  59. package/dist/esm/routes/eventListener.routes.mjs.map +1 -1
  60. package/dist/esm/routes/newsletter.routes.mjs.map +1 -1
  61. package/dist/esm/routes/organization.routes.mjs.map +1 -1
  62. package/dist/esm/routes/project.routes.mjs +3 -12
  63. package/dist/esm/routes/project.routes.mjs.map +1 -1
  64. package/dist/esm/routes/search.routes.mjs.map +1 -1
  65. package/dist/esm/routes/stripe.routes.mjs.map +1 -1
  66. package/dist/esm/routes/tags.routes.mjs.map +1 -1
  67. package/dist/esm/routes/user.routes.mjs.map +1 -1
  68. package/dist/esm/schemas/tag.schema.mjs +5 -0
  69. package/dist/esm/schemas/tag.schema.mjs.map +1 -1
  70. package/dist/esm/services/dictionary.service.mjs +0 -7
  71. package/dist/esm/services/dictionary.service.mjs.map +1 -1
  72. package/dist/esm/services/organization.service.mjs +0 -9
  73. package/dist/esm/services/organization.service.mjs.map +1 -1
  74. package/dist/esm/utils/auth/getAuth.mjs +1 -0
  75. package/dist/esm/utils/auth/getAuth.mjs.map +1 -1
  76. package/dist/esm/utils/permissions.mjs +100 -22
  77. package/dist/esm/utils/permissions.mjs.map +1 -1
  78. package/dist/types/controllers/ai.controller.d.ts +10 -9
  79. package/dist/types/controllers/ai.controller.d.ts.map +1 -1
  80. package/dist/types/controllers/dictionary.controller.d.ts +9 -8
  81. package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
  82. package/dist/types/controllers/newsletter.controller.d.ts +5 -4
  83. package/dist/types/controllers/newsletter.controller.d.ts.map +1 -1
  84. package/dist/types/controllers/organization.controller.d.ts +11 -10
  85. package/dist/types/controllers/organization.controller.d.ts.map +1 -1
  86. package/dist/types/controllers/project.controller.d.ts +10 -9
  87. package/dist/types/controllers/project.controller.d.ts.map +1 -1
  88. package/dist/types/controllers/projectAccessKey.controller.d.ts +5 -4
  89. package/dist/types/controllers/projectAccessKey.controller.d.ts.map +1 -1
  90. package/dist/types/controllers/search.controller.d.ts.map +1 -1
  91. package/dist/types/controllers/stripe.controller.d.ts +5 -4
  92. package/dist/types/controllers/stripe.controller.d.ts.map +1 -1
  93. package/dist/types/controllers/tag.controller.d.ts +6 -5
  94. package/dist/types/controllers/tag.controller.d.ts.map +1 -1
  95. package/dist/types/controllers/user.controller.d.ts +9 -8
  96. package/dist/types/controllers/user.controller.d.ts.map +1 -1
  97. package/dist/types/middlewares/sessionAuth.middleware.d.ts +2 -0
  98. package/dist/types/middlewares/sessionAuth.middleware.d.ts.map +1 -1
  99. package/dist/types/schemas/tag.schema.d.ts.map +1 -1
  100. package/dist/types/services/dictionary.service.d.ts +0 -1
  101. package/dist/types/services/dictionary.service.d.ts.map +1 -1
  102. package/dist/types/services/organization.service.d.ts.map +1 -1
  103. package/dist/types/types/tag.types.d.ts +2 -0
  104. package/dist/types/types/tag.types.d.ts.map +1 -1
  105. package/dist/types/utils/auth/getAuth.d.ts.map +1 -1
  106. package/dist/types/utils/permissions.d.ts +67 -26
  107. package/dist/types/utils/permissions.d.ts.map +1 -1
  108. package/package.json +10 -10
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/dictionary.service.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryData,\n DictionaryDocument,\n} from '@/types/dictionary.types';\nimport type { Project } from '@/types/project.types';\nimport { DictionaryModel } from '@models/dictionary.model';\nimport { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport { GenericError } from '@utils/errors';\nimport type { DictionaryFilters } from '@utils/filtersAndPagination/getDictionaryFiltersAndPagination';\nimport { removeObjectKeys } from '@utils/removeObjectKeys';\nimport {\n validateDictionary,\n type DictionaryFields,\n} from '@utils/validation/validateDictionary';\nimport { Types } from 'mongoose';\n\n/**\n * Finds dictionaries based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of dictionaries matching the filters.\n */\nexport const findDictionaries = async (\n filters: DictionaryFilters,\n skip = 0,\n limit = 100\n): Promise<DictionaryDocument[]> => {\n try {\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the filters\n { $match: filters },\n\n // Stage 2: Skip for pagination\n { $skip: skip },\n\n // Stage 3: Limit the number of documents\n { $limit: limit },\n ]);\n\n const formattedResults = dictionaries.map(\n (result) => new DictionaryModel(result)\n );\n\n return formattedResults;\n } catch (error) {\n console.error('Error fetching dictionaries:', error);\n throw error;\n }\n};\n\n/**\n * Finds a dictionary by its ID.\n * @param dictionaryId - The ID of the dictionary to find.\n * @returns The dictionary matching the ID.\n */\n/**\n * Finds a dictionary by its ID and includes the 'availableVersions' field.\n * @param dictionaryId - The ID of the dictionary to find.\n * @returns The dictionary matching the ID with available versions.\n */\nexport const getDictionaryById = async (\n dictionaryId: string | Types.ObjectId\n): Promise<DictionaryDocument> => {\n const id = Types.ObjectId.isValid(dictionaryId as string)\n ? new Types.ObjectId(dictionaryId as string)\n : dictionaryId;\n\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the document by ID\n { $match: { _id: id } },\n\n // Stage 2: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n ]);\n\n if (!dictionaries.length) {\n throw new GenericError('DICTIONARY_NOT_FOUND', { dictionaryId });\n }\n\n return new DictionaryModel(dictionaries[0]);\n};\n\n/**\n * Finds a dictionary by its ID.\n * @param dictionaryKey - The ID of the dictionary to find.\n * @returns The dictionary matching the ID.\n */\nexport const getDictionaryByKey = async (\n dictionaryKey: string,\n projectId: string | Types.ObjectId\n): Promise<DictionaryDocument> => {\n const dictionaries = await getDictionariesByKeys([dictionaryKey], projectId);\n\n return dictionaries[0];\n};\n\nexport const getDictionariesByKeys = async (\n dictionaryKeys: string[],\n projectId: string | Types.ObjectId\n): Promise<DictionaryDocument[]> => {\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the document by key\n { $match: { key: { $in: dictionaryKeys }, projectIds: projectId } },\n\n // Stage 2: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n ]);\n\n if (!dictionaries) {\n throw new GenericError('DICTIONARY_NOT_FOUND', {\n dictionaryKeys,\n projectId,\n });\n }\n\n const formattedResults = dictionaries.map(\n (result) => new DictionaryModel(result)\n );\n\n return formattedResults;\n};\n\nexport const getDictionariesKeys = async (\n projectId: string | Types.ObjectId\n): Promise<string[]> => {\n const dictionaries = await DictionaryModel.find({\n projectIds: projectId,\n }).select('key');\n\n return dictionaries.map((dictionary) => dictionary.key);\n};\n\nexport const getDictionariesByTags = async (\n tags: string[],\n projectId: string | Project['id']\n): Promise<DictionaryDocument[]> => {\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the document by tags\n {\n $match: {\n tags: { $in: tags },\n projectIds: projectId,\n },\n },\n\n // Stage 2: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n ]);\n\n const formattedResults = dictionaries.map(\n (result) => new DictionaryModel(result)\n );\n\n return formattedResults;\n};\n\n/**\n * Counts the total number of dictionaries that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of dictionaries.\n */\nexport const countDictionaries = async (\n filters: DictionaryFilters\n): Promise<number> => {\n const result = await DictionaryModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('DICTIONARY_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new dictionary in the database.\n * @param dictionary - The dictionary data to create.\n * @returns The created dictionary.\n */\nexport const createDictionary = async (\n dictionary: DictionaryData\n): Promise<DictionaryDocument> => {\n const errors = await validateDictionary(dictionary);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n errors,\n });\n }\n\n return await DictionaryModel.create(dictionary);\n};\n\ntype GetExistingDictionaryResult = {\n existingDictionariesKey: string[];\n newDictionariesKey: string[];\n};\n\n/**\n * Gets the existing dictionaries from the provided list of keys.\n * @param dictionariesKeys - List of dictionary keys to check.\n * @param projectId - The ID of the project to check the dictionaries against.\n * @returns The existing dictionaries and the new dictionaries.\n */\nexport const getExistingDictionaryKey = async (\n dictionariesKeys: string[],\n projectId: string | Types.ObjectId\n): Promise<GetExistingDictionaryResult> => {\n // Fetch dictionaries from the database where the key is in the provided list\n const existingDictionaries = await DictionaryModel.find({\n key: { $in: dictionariesKeys },\n projectIds: projectId,\n });\n\n // Map existing dictionaries to a LocalDictionary object\n const existingDictionariesKey: string[] = [];\n const newDictionariesKey: string[] = [];\n\n for (const key of dictionariesKeys) {\n const isDictionaryExist = existingDictionaries.some(\n (dictionary) => dictionary.key === key\n );\n\n if (isDictionaryExist) {\n existingDictionariesKey.push(key);\n } else {\n newDictionariesKey.push(key);\n }\n }\n\n return { existingDictionariesKey, newDictionariesKey };\n};\n\n/**\n * Updates an existing dictionary in the database by its ID.\n * @param dictionaryId - The ID of the dictionary to update.\n * @param dictionary - The updated dictionary data.\n * @returns The updated dictionary.\n */\nexport const updateDictionaryById = async (\n dictionaryId: string | Types.ObjectId,\n dictionary: Partial<Dictionary>\n): Promise<DictionaryDocument> => {\n const dictionaryObject = ensureMongoDocumentToObject(dictionary);\n const dictionaryToUpdate = removeObjectKeys(dictionaryObject, [\n 'id',\n ]) as unknown as Partial<Dictionary>;\n\n const updatedKeys = Object.keys(dictionaryToUpdate) as DictionaryFields;\n const errors = await validateDictionary(dictionaryToUpdate, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n dictionaryId,\n errors,\n });\n }\n\n const result = await DictionaryModel.updateOne(\n { _id: dictionaryId },\n dictionaryToUpdate\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('DICTIONARY_UPDATE_FAILED', { dictionaryId });\n }\n\n const updatedDictionary = await getDictionaryById(dictionaryId);\n\n return updatedDictionary;\n};\n\n/**\n * Updates an existing dictionary in the database by its key.\n * @param dictionaryKey - The ID of the dictionary to update.\n * @param dictionary - The updated dictionary data.\n * @returns The updated dictionary.\n */\nexport const updateDictionaryByKey = async (\n dictionaryKey: string,\n dictionary: Partial<Dictionary>,\n projectId: string | Types.ObjectId\n): Promise<DictionaryDocument> => {\n const dictionaryObject = ensureMongoDocumentToObject(dictionary);\n const dictionaryToUpdate = removeObjectKeys(dictionaryObject, [\n 'id',\n ]) as unknown as Partial<Dictionary>;\n\n const updatedKeys = Object.keys(dictionaryToUpdate) as DictionaryFields;\n const errors = await validateDictionary(dictionaryToUpdate, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n dictionaryKey,\n projectId,\n errors,\n });\n }\n\n const result = await DictionaryModel.updateOne(\n { key: dictionaryKey, projectIds: projectId },\n dictionaryToUpdate\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('DICTIONARY_UPDATE_FAILED', { dictionaryKey });\n }\n\n const updatedDictionary = await getDictionaryByKey(dictionaryKey, projectId);\n\n return updatedDictionary;\n};\n\n/**\n * Deletes a dictionary from the database by its ID.\n * @param dictionaryId - The ID of the dictionary to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteDictionaryById = async (\n dictionaryId: string\n): Promise<DictionaryDocument> => {\n const dictionary = await DictionaryModel.findByIdAndDelete(dictionaryId);\n\n if (!dictionary) {\n throw new GenericError('DICTIONARY_NOT_FOUND', { dictionaryId });\n }\n\n return dictionary;\n};\n\n// Function to extract the numeric part of the version\nconst getVersionNumber = (version: string): number => {\n const match = version.match(/^v(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid version format: ${version}`);\n }\n return parseInt(match[1], 10);\n};\n\nexport const incrementVersion = (dictionary: Dictionary): string => {\n const VERSION_PREFIX = 'v';\n\n const availableVersions = [...(dictionary.content.keys() ?? [])];\n const lastVersion = availableVersions[availableVersions.length - 1];\n\n // Start with the next version number\n let newNumber = getVersionNumber(lastVersion) + 1;\n let newVersion = `${VERSION_PREFIX}${newNumber}`;\n\n // Loop until a unique version is found\n while (availableVersions.includes(newVersion)) {\n newNumber += 1;\n newVersion = `${VERSION_PREFIX}${newNumber}`;\n }\n\n return newVersion;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,wBAAgC;AAChC,yCAA4C;AAC5C,oBAA6B;AAE7B,8BAAiC;AACjC,gCAGO;AACP,sBAAsB;AASf,MAAM,mBAAmB,OAC9B,SACA,OAAO,GACP,QAAQ,QAC0B;AAClC,MAAI;AACF,UAAM,eAAe,MAAM,kCAAgB,UAA8B;AAAA;AAAA,MAEvE,EAAE,QAAQ,QAAQ;AAAA;AAAA,MAGlB,EAAE,OAAO,KAAK;AAAA;AAAA,MAGd,EAAE,QAAQ,MAAM;AAAA,IAClB,CAAC;AAED,UAAM,mBAAmB,aAAa;AAAA,MACpC,CAAC,WAAW,IAAI,kCAAgB,MAAM;AAAA,IACxC;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,MAAM,gCAAgC,KAAK;AACnD,UAAM;AAAA,EACR;AACF;AAYO,MAAM,oBAAoB,OAC/B,iBACgC;AAChC,QAAM,KAAK,sBAAM,SAAS,QAAQ,YAAsB,IACpD,IAAI,sBAAM,SAAS,YAAsB,IACzC;AAEJ,QAAM,eAAe,MAAM,kCAAgB,UAA8B;AAAA;AAAA,IAEvE,EAAE,QAAQ,EAAE,KAAK,GAAG,EAAE;AAAA;AAAA,IAGtB;AAAA,MACE,YAAY;AAAA,QACV,mBAAmB;AAAA,UACjB,MAAM;AAAA,YACJ,OAAO,EAAE,gBAAgB,WAAW;AAAA,YACpC,IAAI;AAAA,YACJ,IAAI;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,MAAI,CAAC,aAAa,QAAQ;AACxB,UAAM,IAAI,2BAAa,wBAAwB,EAAE,aAAa,CAAC;AAAA,EACjE;AAEA,SAAO,IAAI,kCAAgB,aAAa,CAAC,CAAC;AAC5C;AAOO,MAAM,qBAAqB,OAChC,eACA,cACgC;AAChC,QAAM,eAAe,MAAM,sBAAsB,CAAC,aAAa,GAAG,SAAS;AAE3E,SAAO,aAAa,CAAC;AACvB;AAEO,MAAM,wBAAwB,OACnC,gBACA,cACkC;AAClC,QAAM,eAAe,MAAM,kCAAgB,UAA8B;AAAA;AAAA,IAEvE,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,eAAe,GAAG,YAAY,UAAU,EAAE;AAAA;AAAA,IAGlE;AAAA,MACE,YAAY;AAAA,QACV,mBAAmB;AAAA,UACjB,MAAM;AAAA,YACJ,OAAO,EAAE,gBAAgB,WAAW;AAAA,YACpC,IAAI;AAAA,YACJ,IAAI;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI,2BAAa,wBAAwB;AAAA,MAC7C;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,mBAAmB,aAAa;AAAA,IACpC,CAAC,WAAW,IAAI,kCAAgB,MAAM;AAAA,EACxC;AAEA,SAAO;AACT;AAEO,MAAM,sBAAsB,OACjC,cACsB;AACtB,QAAM,eAAe,MAAM,kCAAgB,KAAK;AAAA,IAC9C,YAAY;AAAA,EACd,CAAC,EAAE,OAAO,KAAK;AAEf,SAAO,aAAa,IAAI,CAAC,eAAe,WAAW,GAAG;AACxD;AAEO,MAAM,wBAAwB,OACnC,MACA,cACkC;AAClC,QAAM,eAAe,MAAM,kCAAgB,UAA8B;AAAA;AAAA,IAEvE;AAAA,MACE,QAAQ;AAAA,QACN,MAAM,EAAE,KAAK,KAAK;AAAA,QAClB,YAAY;AAAA,MACd;AAAA,IACF;AAAA;AAAA,IAGA;AAAA,MACE,YAAY;AAAA,QACV,mBAAmB;AAAA,UACjB,MAAM;AAAA,YACJ,OAAO,EAAE,gBAAgB,WAAW;AAAA,YACpC,IAAI;AAAA,YACJ,IAAI;AAAA,UACN;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,mBAAmB,aAAa;AAAA,IACpC,CAAC,WAAW,IAAI,kCAAgB,MAAM;AAAA,EACxC;AAEA,SAAO;AACT;AAOO,MAAM,oBAAoB,OAC/B,YACoB;AACpB,QAAM,SAAS,MAAM,kCAAgB,eAAe,OAAO;AAE3D,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI,2BAAa,2BAA2B,EAAE,QAAQ,CAAC;AAAA,EAC/D;AAEA,SAAO;AACT;AAOO,MAAM,mBAAmB,OAC9B,eACgC;AAChC,QAAM,SAAS,UAAM,8CAAmB,UAAU;AAElD,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,2BAAa,6BAA6B;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,MAAM,kCAAgB,OAAO,UAAU;AAChD;AAaO,MAAM,2BAA2B,OACtC,kBACA,cACyC;AAEzC,QAAM,uBAAuB,MAAM,kCAAgB,KAAK;AAAA,IACtD,KAAK,EAAE,KAAK,iBAAiB;AAAA,IAC7B,YAAY;AAAA,EACd,CAAC;AAGD,QAAM,0BAAoC,CAAC;AAC3C,QAAM,qBAA+B,CAAC;AAEtC,aAAW,OAAO,kBAAkB;AAClC,UAAM,oBAAoB,qBAAqB;AAAA,MAC7C,CAAC,eAAe,WAAW,QAAQ;AAAA,IACrC;AAEA,QAAI,mBAAmB;AACrB,8BAAwB,KAAK,GAAG;AAAA,IAClC,OAAO;AACL,yBAAmB,KAAK,GAAG;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO,EAAE,yBAAyB,mBAAmB;AACvD;AAQO,MAAM,uBAAuB,OAClC,cACA,eACgC;AAChC,QAAM,uBAAmB,gEAA4B,UAAU;AAC/D,QAAM,yBAAqB,0CAAiB,kBAAkB;AAAA,IAC5D;AAAA,EACF,CAAC;AAED,QAAM,cAAc,OAAO,KAAK,kBAAkB;AAClD,QAAM,SAAS,UAAM,8CAAmB,oBAAoB,WAAW;AAEvE,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,2BAAa,6BAA6B;AAAA,MAClD;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,MAAM,kCAAgB;AAAA,IACnC,EAAE,KAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAEA,MAAI,OAAO,iBAAiB,GAAG;AAC7B,UAAM,IAAI,2BAAa,4BAA4B,EAAE,aAAa,CAAC;AAAA,EACrE;AAEA,QAAM,oBAAoB,MAAM,kBAAkB,YAAY;AAE9D,SAAO;AACT;AAQO,MAAM,wBAAwB,OACnC,eACA,YACA,cACgC;AAChC,QAAM,uBAAmB,gEAA4B,UAAU;AAC/D,QAAM,yBAAqB,0CAAiB,kBAAkB;AAAA,IAC5D;AAAA,EACF,CAAC;AAED,QAAM,cAAc,OAAO,KAAK,kBAAkB;AAClD,QAAM,SAAS,UAAM,8CAAmB,oBAAoB,WAAW;AAEvE,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,2BAAa,6BAA6B;AAAA,MAClD;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,MAAM,kCAAgB;AAAA,IACnC,EAAE,KAAK,eAAe,YAAY,UAAU;AAAA,IAC5C;AAAA,EACF;AAEA,MAAI,OAAO,iBAAiB,GAAG;AAC7B,UAAM,IAAI,2BAAa,4BAA4B,EAAE,cAAc,CAAC;AAAA,EACtE;AAEA,QAAM,oBAAoB,MAAM,mBAAmB,eAAe,SAAS;AAE3E,SAAO;AACT;AAOO,MAAM,uBAAuB,OAClC,iBACgC;AAChC,QAAM,aAAa,MAAM,kCAAgB,kBAAkB,YAAY;AAEvE,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,2BAAa,wBAAwB,EAAE,aAAa,CAAC;AAAA,EACjE;AAEA,SAAO;AACT;AAGA,MAAM,mBAAmB,CAAC,YAA4B;AACpD,QAAM,QAAQ,QAAQ,MAAM,UAAU;AACtC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,2BAA2B,OAAO,EAAE;AAAA,EACtD;AACA,SAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAC9B;AAEO,MAAM,mBAAmB,CAAC,eAAmC;AAClE,QAAM,iBAAiB;AAEvB,QAAM,oBAAoB,CAAC,GAAI,WAAW,QAAQ,KAAK,KAAK,CAAC,CAAE;AAC/D,QAAM,cAAc,kBAAkB,kBAAkB,SAAS,CAAC;AAGlE,MAAI,YAAY,iBAAiB,WAAW,IAAI;AAChD,MAAI,aAAa,GAAG,cAAc,GAAG,SAAS;AAG9C,SAAO,kBAAkB,SAAS,UAAU,GAAG;AAC7C,iBAAa;AACb,iBAAa,GAAG,cAAc,GAAG,SAAS;AAAA,EAC5C;AAEA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/services/dictionary.service.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryData,\n DictionaryDocument,\n} from '@/types/dictionary.types';\nimport type { Project } from '@/types/project.types';\nimport { DictionaryModel } from '@models/dictionary.model';\nimport { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport { GenericError } from '@utils/errors';\nimport type { DictionaryFilters } from '@utils/filtersAndPagination/getDictionaryFiltersAndPagination';\nimport { removeObjectKeys } from '@utils/removeObjectKeys';\nimport {\n validateDictionary,\n type DictionaryFields,\n} from '@utils/validation/validateDictionary';\nimport { Types } from 'mongoose';\n\n/**\n * Finds dictionaries based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of dictionaries matching the filters.\n */\nexport const findDictionaries = async (\n filters: DictionaryFilters,\n skip = 0,\n limit = 100\n): Promise<DictionaryDocument[]> => {\n try {\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the filters\n { $match: filters },\n\n // Stage 2: Skip for pagination\n { $skip: skip },\n\n // Stage 3: Limit the number of documents\n { $limit: limit },\n ]);\n\n const formattedResults = dictionaries.map(\n (result) => new DictionaryModel(result)\n );\n\n return formattedResults;\n } catch (error) {\n console.error('Error fetching dictionaries:', error);\n throw error;\n }\n};\n\n/**\n * Finds a dictionary by its ID.\n * @param dictionaryId - The ID of the dictionary to find.\n * @returns The dictionary matching the ID.\n */\n/**\n * Finds a dictionary by its ID and includes the 'availableVersions' field.\n * @param dictionaryId - The ID of the dictionary to find.\n * @returns The dictionary matching the ID with available versions.\n */\nexport const getDictionaryById = async (\n dictionaryId: string | Types.ObjectId\n): Promise<DictionaryDocument> => {\n const id = Types.ObjectId.isValid(dictionaryId as string)\n ? new Types.ObjectId(dictionaryId as string)\n : dictionaryId;\n\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the document by ID\n { $match: { _id: id } },\n\n // Stage 2: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n ]);\n\n if (!dictionaries.length) {\n throw new GenericError('DICTIONARY_NOT_FOUND', { dictionaryId });\n }\n\n return new DictionaryModel(dictionaries[0]);\n};\n\n/**\n * Finds a dictionary by its ID.\n * @param dictionaryKey - The ID of the dictionary to find.\n * @returns The dictionary matching the ID.\n */\nexport const getDictionaryByKey = async (\n dictionaryKey: string,\n projectId: string | Types.ObjectId\n): Promise<DictionaryDocument> => {\n const dictionaries = await getDictionariesByKeys([dictionaryKey], projectId);\n\n return dictionaries[0];\n};\n\nexport const getDictionariesByKeys = async (\n dictionaryKeys: string[],\n projectId: string | Types.ObjectId\n): Promise<DictionaryDocument[]> => {\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the document by key\n { $match: { key: { $in: dictionaryKeys }, projectIds: projectId } },\n\n // Stage 2: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n ]);\n\n if (!dictionaries) {\n throw new GenericError('DICTIONARY_NOT_FOUND', {\n dictionaryKeys,\n projectId,\n });\n }\n\n const formattedResults = dictionaries.map(\n (result) => new DictionaryModel(result)\n );\n\n return formattedResults;\n};\n\nexport const getDictionariesByTags = async (\n tags: string[],\n projectId: string | Project['id']\n): Promise<DictionaryDocument[]> => {\n const dictionaries = await DictionaryModel.aggregate<DictionaryDocument>([\n // Stage 1: Match the document by tags\n {\n $match: {\n tags: { $in: tags },\n projectIds: projectId,\n },\n },\n\n // Stage 2: Add the 'availableVersions' field\n {\n $addFields: {\n availableVersions: {\n $map: {\n input: { $objectToArray: '$content' },\n as: 'version',\n in: '$$version.k',\n },\n },\n },\n },\n ]);\n\n const formattedResults = dictionaries.map(\n (result) => new DictionaryModel(result)\n );\n\n return formattedResults;\n};\n\n/**\n * Counts the total number of dictionaries that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of dictionaries.\n */\nexport const countDictionaries = async (\n filters: DictionaryFilters\n): Promise<number> => {\n const result = await DictionaryModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('DICTIONARY_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new dictionary in the database.\n * @param dictionary - The dictionary data to create.\n * @returns The created dictionary.\n */\nexport const createDictionary = async (\n dictionary: DictionaryData\n): Promise<DictionaryDocument> => {\n const errors = await validateDictionary(dictionary);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n errors,\n });\n }\n\n return await DictionaryModel.create(dictionary);\n};\n\ntype GetExistingDictionaryResult = {\n existingDictionariesKey: string[];\n newDictionariesKey: string[];\n};\n\n/**\n * Gets the existing dictionaries from the provided list of keys.\n * @param dictionariesKeys - List of dictionary keys to check.\n * @param projectId - The ID of the project to check the dictionaries against.\n * @returns The existing dictionaries and the new dictionaries.\n */\nexport const getExistingDictionaryKey = async (\n dictionariesKeys: string[],\n projectId: string | Types.ObjectId\n): Promise<GetExistingDictionaryResult> => {\n // Fetch dictionaries from the database where the key is in the provided list\n const existingDictionaries = await DictionaryModel.find({\n key: { $in: dictionariesKeys },\n projectIds: projectId,\n });\n\n // Map existing dictionaries to a LocalDictionary object\n const existingDictionariesKey: string[] = [];\n const newDictionariesKey: string[] = [];\n\n for (const key of dictionariesKeys) {\n const isDictionaryExist = existingDictionaries.some(\n (dictionary) => dictionary.key === key\n );\n\n if (isDictionaryExist) {\n existingDictionariesKey.push(key);\n } else {\n newDictionariesKey.push(key);\n }\n }\n\n return { existingDictionariesKey, newDictionariesKey };\n};\n\n/**\n * Updates an existing dictionary in the database by its ID.\n * @param dictionaryId - The ID of the dictionary to update.\n * @param dictionary - The updated dictionary data.\n * @returns The updated dictionary.\n */\nexport const updateDictionaryById = async (\n dictionaryId: string | Types.ObjectId,\n dictionary: Partial<Dictionary>\n): Promise<DictionaryDocument> => {\n const dictionaryObject = ensureMongoDocumentToObject(dictionary);\n const dictionaryToUpdate = removeObjectKeys(dictionaryObject, [\n 'id',\n ]) as unknown as Partial<Dictionary>;\n\n const updatedKeys = Object.keys(dictionaryToUpdate) as DictionaryFields;\n const errors = await validateDictionary(dictionaryToUpdate, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n dictionaryId,\n errors,\n });\n }\n\n const result = await DictionaryModel.updateOne(\n { _id: dictionaryId },\n dictionaryToUpdate\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('DICTIONARY_UPDATE_FAILED', { dictionaryId });\n }\n\n const updatedDictionary = await getDictionaryById(dictionaryId);\n\n return updatedDictionary;\n};\n\n/**\n * Updates an existing dictionary in the database by its key.\n * @param dictionaryKey - The ID of the dictionary to update.\n * @param dictionary - The updated dictionary data.\n * @returns The updated dictionary.\n */\nexport const updateDictionaryByKey = async (\n dictionaryKey: string,\n dictionary: Partial<Dictionary>,\n projectId: string | Types.ObjectId\n): Promise<DictionaryDocument> => {\n const dictionaryObject = ensureMongoDocumentToObject(dictionary);\n const dictionaryToUpdate = removeObjectKeys(dictionaryObject, [\n 'id',\n ]) as unknown as Partial<Dictionary>;\n\n const updatedKeys = Object.keys(dictionaryToUpdate) as DictionaryFields;\n const errors = await validateDictionary(dictionaryToUpdate, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('DICTIONARY_INVALID_FIELDS', {\n dictionaryKey,\n projectId,\n errors,\n });\n }\n\n const result = await DictionaryModel.updateOne(\n { key: dictionaryKey, projectIds: projectId },\n dictionaryToUpdate\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('DICTIONARY_UPDATE_FAILED', { dictionaryKey });\n }\n\n const updatedDictionary = await getDictionaryByKey(dictionaryKey, projectId);\n\n return updatedDictionary;\n};\n\n/**\n * Deletes a dictionary from the database by its ID.\n * @param dictionaryId - The ID of the dictionary to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteDictionaryById = async (\n dictionaryId: string\n): Promise<DictionaryDocument> => {\n const dictionary = await DictionaryModel.findByIdAndDelete(dictionaryId);\n\n if (!dictionary) {\n throw new GenericError('DICTIONARY_NOT_FOUND', { dictionaryId });\n }\n\n return dictionary;\n};\n\n// Function to extract the numeric part of the version\nconst getVersionNumber = (version: string): number => {\n const match = version.match(/^v(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid version format: ${version}`);\n }\n return parseInt(match[1], 10);\n};\n\nexport const incrementVersion = (dictionary: Dictionary): string => {\n const VERSION_PREFIX = 'v';\n\n const availableVersions = [...(dictionary.content.keys() ?? [])];\n const lastVersion = availableVersions[availableVersions.length - 1];\n\n // Start with the next version number\n let newNumber = getVersionNumber(lastVersion) + 1;\n let newVersion = `${VERSION_PREFIX}${newNumber}`;\n\n // Loop until a unique version is found\n while (availableVersions.includes(newVersion)) {\n newNumber += 1;\n newVersion = `${VERSION_PREFIX}${newNumber}`;\n }\n\n return newVersion;\n};\n"],"mappings":"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","names":[]}
@@ -51,14 +51,6 @@ const createOrganization = async (organization, userId) => {
51
51
  throw new import_errors.GenericError("ORGANIZATION_INVALID_FIELDS", { errors });
52
52
  }
53
53
  try {
54
- console.log({
55
- organization: {
56
- creatorId: userId,
57
- membersIds: [userId],
58
- adminsIds: [userId],
59
- ...organization
60
- }
61
- });
62
54
  const result = await import_organization.OrganizationModel.create({
63
55
  creatorId: userId,
64
56
  membersIds: [userId],
@@ -67,7 +59,6 @@ const createOrganization = async (organization, userId) => {
67
59
  });
68
60
  return result;
69
61
  } catch (error) {
70
- console.error("Organization creation failed:", error);
71
62
  throw new import_errors.GenericError("ORGANIZATION_CREATION_FAILED", {
72
63
  error: error.message
73
64
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/organization.service.ts"],"sourcesContent":["import type {\n Organization,\n OrganizationCreationData,\n OrganizationDocument,\n} from '@/types/organization.types';\nimport type { Plan, PlanDocument } from '@/types/plan.types';\nimport { OrganizationModel } from '@models/organization.model';\nimport { GenericError } from '@utils/errors';\nimport type { OrganizationFilters } from '@utils/filtersAndPagination/getOrganizationFiltersAndPagination';\nimport {\n type OrganizationFields,\n validateOrganization,\n} from '@utils/validation/validateOrganization';\nimport type { Types } from 'mongoose';\n\n/**\n * Finds organizations based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of organizations matching the filters.\n */\nexport const findOrganizations = async (\n filters: OrganizationFilters,\n skip: number,\n limit: number\n): Promise<OrganizationDocument[]> =>\n await OrganizationModel.find(filters).skip(skip).limit(limit);\n\n/**\n * Finds an organization by its ID.\n * @param organizationId - The ID of the organization to find.\n * @returns The organization matching the ID.\n */\nexport const getOrganizationById = async (\n organizationId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const organization = await OrganizationModel.findById(organizationId);\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND', { organizationId });\n }\n\n return organization;\n};\n\n/**\n * Counts the total number of organizations that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of organizations.\n */\nexport const countOrganizations = async (\n filters: OrganizationFilters\n): Promise<number> => {\n const result = await OrganizationModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('ORGANIZATION_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new organization in the database.\n * @param organization - The organization data to create.\n * @returns The created organization.\n */\nexport const createOrganization = async (\n organization: OrganizationCreationData,\n userId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const errors = validateOrganization(organization, ['name']);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('ORGANIZATION_INVALID_FIELDS', { errors });\n }\n\n try {\n console.log({\n organization: {\n creatorId: userId,\n membersIds: [userId],\n adminsIds: [userId],\n ...organization,\n },\n });\n\n const result = await OrganizationModel.create({\n creatorId: userId,\n membersIds: [userId],\n adminsIds: [userId],\n ...organization,\n });\n\n return result;\n } catch (error) {\n console.error('Organization creation failed:', error);\n throw new GenericError('ORGANIZATION_CREATION_FAILED', {\n error: (error as Error).message,\n });\n }\n};\n\n/**\n * Updates an existing organization in the database by its ID.\n * @param organizationId - The ID of the organization to update.\n * @param organization - The updated organization data.\n * @returns The updated organization.\n */\nexport const updateOrganizationById = async (\n organizationId: string | Types.ObjectId,\n organization: Partial<Organization>\n): Promise<OrganizationDocument> => {\n const updatedKeys = Object.keys(organization) as OrganizationFields;\n const errors = validateOrganization(organization, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('ORGANIZATION_INVALID_FIELDS', {\n organizationId,\n errors,\n });\n }\n\n const result = await OrganizationModel.updateOne(\n { _id: organizationId },\n organization\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('ORGANIZATION_UPDATE_FAILED', { organizationId });\n }\n\n return await getOrganizationById(organizationId);\n};\n\n/**\n * Deletes an organization from the database by its ID.\n * @param organizationId - The ID of the organization to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteOrganizationById = async (\n organizationId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const organization =\n await OrganizationModel.findByIdAndDelete(organizationId);\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND', { organizationId });\n }\n\n return organization;\n};\n\n/**\n * Updates an existing plan in the database by its ID.\n * @param planId - The ID of the plan to update.\n * @param plan - The updated plan data.\n * @returns The updated plan.\n */\nexport const updatePlan = async (\n organization: Organization | OrganizationDocument,\n plan: Partial<Plan>\n): Promise<OrganizationDocument | null> => {\n let prevPlan = organization.plan ?? {};\n\n if (typeof (prevPlan as PlanDocument)?.toObject === 'function') {\n prevPlan = (prevPlan as PlanDocument).toObject();\n }\n\n const updateOrganizationResult = await OrganizationModel.updateOne(\n { _id: organization.id },\n { $set: { plan: { ...prevPlan, ...plan } } },\n { new: true }\n );\n\n if (updateOrganizationResult.matchedCount === 0) {\n throw new GenericError('ORGANIZATION_UPDATE_FAILED', {\n organizationId: organization.id,\n });\n }\n\n const updatedOrganization = await getOrganizationById(organization.id);\n\n return updatedOrganization;\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/services/organization.service.ts"],"sourcesContent":["import type {\n Organization,\n OrganizationCreationData,\n OrganizationDocument,\n} from '@/types/organization.types';\nimport type { Plan, PlanDocument } from '@/types/plan.types';\nimport { OrganizationModel } from '@models/organization.model';\nimport { GenericError } from '@utils/errors';\nimport type { OrganizationFilters } from '@utils/filtersAndPagination/getOrganizationFiltersAndPagination';\nimport {\n type OrganizationFields,\n validateOrganization,\n} from '@utils/validation/validateOrganization';\nimport type { Types } from 'mongoose';\n\n/**\n * Finds organizations based on filters and pagination options.\n * @param filters - MongoDB filter query.\n * @param skip - Number of documents to skip.\n * @param limit - Number of documents to limit.\n * @returns List of organizations matching the filters.\n */\nexport const findOrganizations = async (\n filters: OrganizationFilters,\n skip: number,\n limit: number\n): Promise<OrganizationDocument[]> =>\n await OrganizationModel.find(filters).skip(skip).limit(limit);\n\n/**\n * Finds an organization by its ID.\n * @param organizationId - The ID of the organization to find.\n * @returns The organization matching the ID.\n */\nexport const getOrganizationById = async (\n organizationId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const organization = await OrganizationModel.findById(organizationId);\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND', { organizationId });\n }\n\n return organization;\n};\n\n/**\n * Counts the total number of organizations that match the filters.\n * @param filters - MongoDB filter query.\n * @returns Total number of organizations.\n */\nexport const countOrganizations = async (\n filters: OrganizationFilters\n): Promise<number> => {\n const result = await OrganizationModel.countDocuments(filters);\n\n if (typeof result === 'undefined') {\n throw new GenericError('ORGANIZATION_COUNT_FAILED', { filters });\n }\n\n return result;\n};\n\n/**\n * Creates a new organization in the database.\n * @param organization - The organization data to create.\n * @returns The created organization.\n */\nexport const createOrganization = async (\n organization: OrganizationCreationData,\n userId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const errors = validateOrganization(organization, ['name']);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('ORGANIZATION_INVALID_FIELDS', { errors });\n }\n\n try {\n const result = await OrganizationModel.create({\n creatorId: userId,\n membersIds: [userId],\n adminsIds: [userId],\n ...organization,\n });\n\n return result;\n } catch (error) {\n throw new GenericError('ORGANIZATION_CREATION_FAILED', {\n error: (error as Error).message,\n });\n }\n};\n\n/**\n * Updates an existing organization in the database by its ID.\n * @param organizationId - The ID of the organization to update.\n * @param organization - The updated organization data.\n * @returns The updated organization.\n */\nexport const updateOrganizationById = async (\n organizationId: string | Types.ObjectId,\n organization: Partial<Organization>\n): Promise<OrganizationDocument> => {\n const updatedKeys = Object.keys(organization) as OrganizationFields;\n const errors = validateOrganization(organization, updatedKeys);\n\n if (Object.keys(errors).length > 0) {\n throw new GenericError('ORGANIZATION_INVALID_FIELDS', {\n organizationId,\n errors,\n });\n }\n\n const result = await OrganizationModel.updateOne(\n { _id: organizationId },\n organization\n );\n\n if (result.matchedCount === 0) {\n throw new GenericError('ORGANIZATION_UPDATE_FAILED', { organizationId });\n }\n\n return await getOrganizationById(organizationId);\n};\n\n/**\n * Deletes an organization from the database by its ID.\n * @param organizationId - The ID of the organization to delete.\n * @returns The result of the deletion operation.\n */\nexport const deleteOrganizationById = async (\n organizationId: string | Types.ObjectId\n): Promise<OrganizationDocument> => {\n const organization =\n await OrganizationModel.findByIdAndDelete(organizationId);\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND', { organizationId });\n }\n\n return organization;\n};\n\n/**\n * Updates an existing plan in the database by its ID.\n * @param planId - The ID of the plan to update.\n * @param plan - The updated plan data.\n * @returns The updated plan.\n */\nexport const updatePlan = async (\n organization: Organization | OrganizationDocument,\n plan: Partial<Plan>\n): Promise<OrganizationDocument | null> => {\n let prevPlan = organization.plan ?? {};\n\n if (typeof (prevPlan as PlanDocument)?.toObject === 'function') {\n prevPlan = (prevPlan as PlanDocument).toObject();\n }\n\n const updateOrganizationResult = await OrganizationModel.updateOne(\n { _id: organization.id },\n { $set: { plan: { ...prevPlan, ...plan } } },\n { new: true }\n );\n\n if (updateOrganizationResult.matchedCount === 0) {\n throw new GenericError('ORGANIZATION_UPDATE_FAILED', {\n organizationId: organization.id,\n });\n }\n\n const updatedOrganization = await getOrganizationById(organization.id);\n\n return updatedOrganization;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,0BAAkC;AAClC,oBAA6B;AAE7B,kCAGO;AAUA,MAAM,oBAAoB,OAC/B,SACA,MACA,UAEA,MAAM,sCAAkB,KAAK,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,KAAK;AAOvD,MAAM,sBAAsB,OACjC,mBACkC;AAClC,QAAM,eAAe,MAAM,sCAAkB,SAAS,cAAc;AAEpE,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI,2BAAa,0BAA0B,EAAE,eAAe,CAAC;AAAA,EACrE;AAEA,SAAO;AACT;AAOO,MAAM,qBAAqB,OAChC,YACoB;AACpB,QAAM,SAAS,MAAM,sCAAkB,eAAe,OAAO;AAE7D,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI,2BAAa,6BAA6B,EAAE,QAAQ,CAAC;AAAA,EACjE;AAEA,SAAO;AACT;AAOO,MAAM,qBAAqB,OAChC,cACA,WACkC;AAClC,QAAM,aAAS,kDAAqB,cAAc,CAAC,MAAM,CAAC;AAE1D,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,2BAAa,+BAA+B,EAAE,OAAO,CAAC;AAAA,EAClE;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,sCAAkB,OAAO;AAAA,MAC5C,WAAW;AAAA,MACX,YAAY,CAAC,MAAM;AAAA,MACnB,WAAW,CAAC,MAAM;AAAA,MAClB,GAAG;AAAA,IACL,CAAC;AAED,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI,2BAAa,gCAAgC;AAAA,MACrD,OAAQ,MAAgB;AAAA,IAC1B,CAAC;AAAA,EACH;AACF;AAQO,MAAM,yBAAyB,OACpC,gBACA,iBACkC;AAClC,QAAM,cAAc,OAAO,KAAK,YAAY;AAC5C,QAAM,aAAS,kDAAqB,cAAc,WAAW;AAE7D,MAAI,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAClC,UAAM,IAAI,2BAAa,+BAA+B;AAAA,MACpD;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,MAAM,sCAAkB;AAAA,IACrC,EAAE,KAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,OAAO,iBAAiB,GAAG;AAC7B,UAAM,IAAI,2BAAa,8BAA8B,EAAE,eAAe,CAAC;AAAA,EACzE;AAEA,SAAO,MAAM,oBAAoB,cAAc;AACjD;AAOO,MAAM,yBAAyB,OACpC,mBACkC;AAClC,QAAM,eACJ,MAAM,sCAAkB,kBAAkB,cAAc;AAE1D,MAAI,CAAC,cAAc;AACjB,UAAM,IAAI,2BAAa,0BAA0B,EAAE,eAAe,CAAC;AAAA,EACrE;AAEA,SAAO;AACT;AAQO,MAAM,aAAa,OACxB,cACA,SACyC;AACzC,MAAI,WAAW,aAAa,QAAQ,CAAC;AAErC,MAAI,OAAQ,UAA2B,aAAa,YAAY;AAC9D,eAAY,SAA0B,SAAS;AAAA,EACjD;AAEA,QAAM,2BAA2B,MAAM,sCAAkB;AAAA,IACvD,EAAE,KAAK,aAAa,GAAG;AAAA,IACvB,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,KAAK,EAAE,EAAE;AAAA,IAC3C,EAAE,KAAK,KAAK;AAAA,EACd;AAEA,MAAI,yBAAyB,iBAAiB,GAAG;AAC/C,UAAM,IAAI,2BAAa,8BAA8B;AAAA,MACnD,gBAAgB,aAAa;AAAA,IAC/B,CAAC;AAAA,EACH;AAEA,QAAM,sBAAsB,MAAM,oBAAoB,aAAa,EAAE;AAErE,SAAO;AACT;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/types/tag.types.ts"],"sourcesContent":["import { RenameId } from '@utils/mongoDB/types';\nimport type { Document, Model, ObjectIdToString, Types } from 'mongoose';\nimport type { Organization } from './organization.types';\nimport type { User } from './user.types';\n\nexport type TagCreationData = {\n key: string;\n name?: string;\n description?: string;\n instructions?: string;\n};\n\nexport type TagData = {\n key: string;\n name?: string;\n description?: string;\n instructions?: string;\n creatorId: User['id'];\n organizationId: Organization['id'];\n};\n\nexport type Tag = TagData & {\n id: Types.ObjectId;\n createdAt: number;\n updatedAt: number;\n};\n\nexport type TagAPI = ObjectIdToString<Tag>;\n\nexport type TagSchema = RenameId<Tag>;\nexport type TagModelType = Model<Tag>;\nexport type TagDocument = Document<unknown, {}, Tag> & Tag;\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../../../src/types/tag.types.ts"],"sourcesContent":["import { RenameId } from '@utils/mongoDB/types';\nimport type { Document, Model, ObjectIdToString, Types } from 'mongoose';\nimport type { Organization } from './organization.types';\nimport { Project } from './project.types';\nimport type { User } from './user.types';\n\nexport type TagCreationData = {\n key: string;\n name?: string;\n description?: string;\n instructions?: string;\n};\n\nexport type TagData = {\n key: string;\n name?: string;\n description?: string;\n instructions?: string;\n creatorId: User['id'];\n projectId: Project['id'];\n organizationId: Organization['id'];\n};\n\nexport type Tag = TagData & {\n id: Types.ObjectId;\n createdAt: number;\n updatedAt: number;\n};\n\nexport type TagAPI = ObjectIdToString<Tag>;\n\nexport type TagSchema = RenameId<Tag>;\nexport type TagModelType = Model<Tag>;\nexport type TagDocument = Document<unknown, {}, Tag> & Tag;\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -43,6 +43,7 @@ const formatSession = (session) => {
43
43
  permissions = (0, import_permissions.intersectPermissions)(permissions, session.permissions);
44
44
  }
45
45
  const resultSession = {
46
+ session: session.session,
46
47
  user: (0, import_user3.mapUserToAPI)(session.user),
47
48
  organization: (0, import_organization2.mapOrganizationToAPI)(session.organization),
48
49
  project: (0, import_project2.mapProjectToAPI)(session.project),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/utils/auth/getAuth.ts"],"sourcesContent":["import type { OrganizationAPI } from '@/types/organization.types';\nimport type { ProjectAPI } from '@/types/project.types';\nimport type {\n SessionAPI,\n SessionContext,\n SessionDataApi,\n} from '@/types/session.types';\nimport type { User, UserAPI } from '@/types/user.types';\nimport { sendVerificationUpdate } from '@controllers/user.controller';\nimport { logger } from '@logger';\nimport { sendEmail } from '@services/email.service';\nimport { getOrganizationById } from '@services/organization.service';\nimport { getProjectById } from '@services/project.service';\nimport { getUserById } from '@services/user.service';\nimport { mapOrganizationToAPI } from '@utils/mapper/organization';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport {\n computeEffectivePermission,\n getSessionRoles,\n intersectPermissions,\n} from '@utils/permissions';\nimport { betterAuth, OmitId } from 'better-auth';\nimport { mongodbAdapter } from 'better-auth/adapters/mongodb';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport { customSession } from 'better-auth/plugins';\nimport type { MongoClient } from 'mongodb';\n\nexport type Auth = ReturnType<typeof betterAuth>;\n\nexport const formatSession = (session: SessionContext): OmitId<SessionAPI> => {\n const roles = getSessionRoles(session);\n let permissions = computeEffectivePermission(roles);\n\n // Intersect in the case a Access Token try to override the permissions\n if (session.permissions) {\n permissions = intersectPermissions(permissions, session.permissions);\n }\n\n const resultSession = {\n user: mapUserToAPI(session.user),\n organization: mapOrganizationToAPI(session.organization),\n project: mapProjectToAPI(session.project),\n authType: 'session',\n permissions,\n roles,\n } as OmitId<SessionAPI>;\n\n return resultSession;\n};\n\nexport const getAuth = (dbClient: MongoClient): Auth => {\n if (!dbClient) {\n throw new Error('MongoDB connection not established');\n }\n\n const auth = betterAuth({\n appName: 'Intlayer',\n\n database: mongodbAdapter(dbClient.db()),\n\n /**\n * User model\n */\n user: {\n modelName: 'users',\n },\n\n databaseHooks: {\n user: {\n create: {\n // Runs once, immediately after the INSERT\n after: async (user) => {\n if (!user?.emailVerified) return;\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n },\n },\n },\n },\n\n hooks: {\n after: createAuthMiddleware(async (ctx) => {\n const { path, context } = ctx;\n\n const newUser = context.newSession?.user;\n const existingUser = context.session?.user;\n const user = newUser ?? existingUser;\n\n if (!user) return;\n\n if (['/verify-email'].includes(path)) {\n sendVerificationUpdate(user as unknown as User);\n logger.info('SSE verification update sent', {\n email: user.email,\n userId: user.id,\n });\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n }\n }),\n },\n\n advanced: {\n // 1️⃣ Change or drop the global prefix\n // cookiePrefix: \"intlayer\", // => intlayer.session_token\n cookiePrefix: 'intlayer', // => session_token (no prefix)\n\n // 2️⃣ Override just the session‑token cookie\n cookies: {\n session_token: {\n // name: 'intlayer_session_token', // final name depends on the prefix above\n // attributes: { sameSite: \"lax\", maxAge: 60 * 60 * 24 } // optional\n },\n },\n\n // 3️⃣ (optional) turn off the automatic __Secure‑ prefix in non‑prod\n // useSecureCookies: false,\n },\n\n session: {\n modelName: 'sessions',\n id: 'id',\n\n additionalFields: {\n activeOrganizationId: { type: 'string', nullable: true, input: false },\n activeProjectId: { type: 'string', nullable: true, input: false },\n },\n },\n\n plugins: [\n customSession(async ({ session }) => {\n const typedSession = session as unknown as SessionDataApi;\n\n let userAPI: UserAPI | null = null;\n let organizationAPI: OrganizationAPI | null = null;\n let projectAPI: ProjectAPI | null = null;\n\n if (typedSession.userId) {\n const userData = await getUserById(typedSession.userId);\n\n if (userData) {\n userAPI = mapUserToAPI(userData);\n }\n }\n\n if (typedSession.activeOrganizationId) {\n const orgData = await getOrganizationById(\n typedSession.activeOrganizationId\n );\n\n if (orgData) {\n organizationAPI = mapOrganizationToAPI(orgData);\n }\n }\n if (typedSession.activeProjectId) {\n const projectData = await getProjectById(\n typedSession.activeProjectId\n );\n\n if (projectData) {\n projectAPI = mapProjectToAPI(projectData);\n }\n }\n\n const sessionWithNoPermission: SessionContext = {\n session: typedSession,\n user: userAPI!,\n organization: organizationAPI ?? null,\n project: projectAPI ?? null,\n authType: 'session',\n };\n\n return formatSession(sessionWithNoPermission);\n }),\n ],\n\n emailAndPassword: {\n enabled: true,\n disableSignUp: false,\n requireEmailVerification: true,\n minPasswordLength: 8,\n maxPasswordLength: 128,\n autoSignIn: true,\n sendResetPassword: async ({ user, url }) => {\n logger.info('sending reset password email', { email: user.email });\n await sendEmail({\n type: 'resetPassword',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n resetLink: url,\n });\n },\n resetPasswordTokenExpiresIn: 3600,\n },\n accountLinking: {\n enabled: true, // allow linking in general\n trustedProviders: ['google', 'github'], // optional: auto‑link when Google verifies the e‑mail\n },\n emailVerification: {\n autoSignInAfterVerification: true,\n sendVerificationEmail: async ({ user, url }) => {\n logger.info('sending verification email', { email: user.email });\n await sendEmail({\n type: 'validate',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n validationLink: url,\n });\n },\n },\n\n crossSubDomainCookies: {\n enabled: true,\n additionalCookies: ['session_token'],\n domain: process.env.CLIENT_URL as string,\n },\n cookiePrefix: 'intlayer',\n cookies: {\n session_token: {\n name: 'session_token',\n attributes: {\n httpOnly: true,\n secure: true,\n },\n },\n },\n\n trustedOrigins: [process.env.CLIENT_URL as string],\n\n socialProviders: {\n google: {\n clientId: process.env.GOOGLE_CLIENT_ID as string,\n clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,\n },\n github: {\n clientId: process.env.GITHUB_CLIENT_ID as string,\n clientSecret: process.env.GITHUB_CLIENT_SECRET as string,\n },\n },\n\n logger: {\n log: (level, message, ...args) => logger[level](message, ...args),\n },\n });\n\n return auth;\n};\n"],"mappings":"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","names":["import_user","import_organization","import_project"]}
1
+ {"version":3,"sources":["../../../../src/utils/auth/getAuth.ts"],"sourcesContent":["import type { OrganizationAPI } from '@/types/organization.types';\nimport type { ProjectAPI } from '@/types/project.types';\nimport type {\n SessionAPI,\n SessionContext,\n SessionDataApi,\n} from '@/types/session.types';\nimport type { User, UserAPI } from '@/types/user.types';\nimport { sendVerificationUpdate } from '@controllers/user.controller';\nimport { logger } from '@logger';\nimport { sendEmail } from '@services/email.service';\nimport { getOrganizationById } from '@services/organization.service';\nimport { getProjectById } from '@services/project.service';\nimport { getUserById } from '@services/user.service';\nimport { mapOrganizationToAPI } from '@utils/mapper/organization';\nimport { mapProjectToAPI } from '@utils/mapper/project';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport {\n computeEffectivePermission,\n getSessionRoles,\n intersectPermissions,\n} from '@utils/permissions';\nimport { betterAuth, OmitId } from 'better-auth';\nimport { mongodbAdapter } from 'better-auth/adapters/mongodb';\nimport { createAuthMiddleware } from 'better-auth/api';\nimport { customSession } from 'better-auth/plugins';\nimport type { MongoClient } from 'mongodb';\n\nexport type Auth = ReturnType<typeof betterAuth>;\n\nexport const formatSession = (session: SessionContext): OmitId<SessionAPI> => {\n const roles = getSessionRoles(session);\n let permissions = computeEffectivePermission(roles);\n\n // Intersect in the case a Access Token try to override the permissions\n if (session.permissions) {\n permissions = intersectPermissions(permissions, session.permissions);\n }\n\n const resultSession = {\n session: session.session,\n user: mapUserToAPI(session.user),\n organization: mapOrganizationToAPI(session.organization),\n project: mapProjectToAPI(session.project),\n authType: 'session',\n permissions,\n roles,\n } as OmitId<SessionAPI>;\n\n return resultSession;\n};\n\nexport const getAuth = (dbClient: MongoClient): Auth => {\n if (!dbClient) {\n throw new Error('MongoDB connection not established');\n }\n\n const auth = betterAuth({\n appName: 'Intlayer',\n\n database: mongodbAdapter(dbClient.db()),\n\n /**\n * User model\n */\n user: {\n modelName: 'users',\n },\n\n databaseHooks: {\n user: {\n create: {\n // Runs once, immediately after the INSERT\n after: async (user) => {\n if (!user?.emailVerified) return;\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n },\n },\n },\n },\n\n hooks: {\n after: createAuthMiddleware(async (ctx) => {\n const { path, context } = ctx;\n\n const newUser = context.newSession?.user;\n const existingUser = context.session?.user;\n const user = newUser ?? existingUser;\n\n if (!user) return;\n\n if (['/verify-email'].includes(path)) {\n sendVerificationUpdate(user as unknown as User);\n logger.info('SSE verification update sent', {\n email: user.email,\n userId: user.id,\n });\n\n await sendEmail({\n type: 'welcome',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n loginLink: `${process.env.CLIENT_URL}/auth/login`,\n locale: (user as any).lang,\n });\n logger.info('Welcome e‑mail delivered', {\n email: user.email,\n });\n }\n }),\n },\n\n advanced: {\n // 1️⃣ Change or drop the global prefix\n // cookiePrefix: \"intlayer\", // => intlayer.session_token\n cookiePrefix: 'intlayer', // => session_token (no prefix)\n\n // 2️⃣ Override just the session‑token cookie\n cookies: {\n session_token: {\n // name: 'intlayer_session_token', // final name depends on the prefix above\n // attributes: { sameSite: \"lax\", maxAge: 60 * 60 * 24 } // optional\n },\n },\n\n // 3️⃣ (optional) turn off the automatic __Secure‑ prefix in non‑prod\n // useSecureCookies: false,\n },\n\n session: {\n modelName: 'sessions',\n id: 'id',\n\n additionalFields: {\n activeOrganizationId: { type: 'string', nullable: true, input: false },\n activeProjectId: { type: 'string', nullable: true, input: false },\n },\n },\n\n plugins: [\n customSession(async ({ session }) => {\n const typedSession = session as unknown as SessionDataApi;\n\n let userAPI: UserAPI | null = null;\n let organizationAPI: OrganizationAPI | null = null;\n let projectAPI: ProjectAPI | null = null;\n\n if (typedSession.userId) {\n const userData = await getUserById(typedSession.userId);\n\n if (userData) {\n userAPI = mapUserToAPI(userData);\n }\n }\n\n if (typedSession.activeOrganizationId) {\n const orgData = await getOrganizationById(\n typedSession.activeOrganizationId\n );\n\n if (orgData) {\n organizationAPI = mapOrganizationToAPI(orgData);\n }\n }\n if (typedSession.activeProjectId) {\n const projectData = await getProjectById(\n typedSession.activeProjectId\n );\n\n if (projectData) {\n projectAPI = mapProjectToAPI(projectData);\n }\n }\n\n const sessionWithNoPermission: SessionContext = {\n session: typedSession,\n user: userAPI!,\n organization: organizationAPI ?? null,\n project: projectAPI ?? null,\n authType: 'session',\n };\n\n return formatSession(sessionWithNoPermission);\n }),\n ],\n\n emailAndPassword: {\n enabled: true,\n disableSignUp: false,\n requireEmailVerification: true,\n minPasswordLength: 8,\n maxPasswordLength: 128,\n autoSignIn: true,\n sendResetPassword: async ({ user, url }) => {\n logger.info('sending reset password email', { email: user.email });\n await sendEmail({\n type: 'resetPassword',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n resetLink: url,\n });\n },\n resetPasswordTokenExpiresIn: 3600,\n },\n accountLinking: {\n enabled: true, // allow linking in general\n trustedProviders: ['google', 'github'], // optional: auto‑link when Google verifies the e‑mail\n },\n emailVerification: {\n autoSignInAfterVerification: true,\n sendVerificationEmail: async ({ user, url }) => {\n logger.info('sending verification email', { email: user.email });\n await sendEmail({\n type: 'validate',\n to: user.email,\n username: user.name ?? user.email.split('@')[0],\n validationLink: url,\n });\n },\n },\n\n crossSubDomainCookies: {\n enabled: true,\n additionalCookies: ['session_token'],\n domain: process.env.CLIENT_URL as string,\n },\n cookiePrefix: 'intlayer',\n cookies: {\n session_token: {\n name: 'session_token',\n attributes: {\n httpOnly: true,\n secure: true,\n },\n },\n },\n\n trustedOrigins: [process.env.CLIENT_URL as string],\n\n socialProviders: {\n google: {\n clientId: process.env.GOOGLE_CLIENT_ID as string,\n clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,\n },\n github: {\n clientId: process.env.GITHUB_CLIENT_ID as string,\n clientSecret: process.env.GITHUB_CLIENT_SECRET as string,\n },\n },\n\n logger: {\n log: (level, message, ...args) => logger[level](message, ...args),\n },\n });\n\n return auth;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,kBAAuC;AACvC,oBAAuB;AACvB,mBAA0B;AAC1B,0BAAoC;AACpC,qBAA+B;AAC/B,IAAAA,eAA4B;AAC5B,IAAAC,uBAAqC;AACrC,IAAAC,kBAAgC;AAChC,IAAAF,eAA6B;AAC7B,yBAIO;AACP,yBAAmC;AACnC,qBAA+B;AAC/B,iBAAqC;AACrC,qBAA8B;AAKvB,MAAM,gBAAgB,CAAC,YAAgD;AAC5E,QAAM,YAAQ,oCAAgB,OAAO;AACrC,MAAI,kBAAc,+CAA2B,KAAK;AAGlD,MAAI,QAAQ,aAAa;AACvB,sBAAc,yCAAqB,aAAa,QAAQ,WAAW;AAAA,EACrE;AAEA,QAAM,gBAAgB;AAAA,IACpB,SAAS,QAAQ;AAAA,IACjB,UAAM,2BAAa,QAAQ,IAAI;AAAA,IAC/B,kBAAc,2CAAqB,QAAQ,YAAY;AAAA,IACvD,aAAS,iCAAgB,QAAQ,OAAO;AAAA,IACxC,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AACT;AAEO,MAAM,UAAU,CAAC,aAAgC;AACtD,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAEA,QAAM,WAAO,+BAAW;AAAA,IACtB,SAAS;AAAA,IAET,cAAU,+BAAe,SAAS,GAAG,CAAC;AAAA;AAAA;AAAA;AAAA,IAKtC,MAAM;AAAA,MACJ,WAAW;AAAA,IACb;AAAA,IAEA,eAAe;AAAA,MACb,MAAM;AAAA,QACJ,QAAQ;AAAA;AAAA,UAEN,OAAO,OAAO,SAAS;AACrB,gBAAI,CAAC,MAAM,cAAe;AAE1B,sBAAM,wBAAU;AAAA,cACd,MAAM;AAAA,cACN,IAAI,KAAK;AAAA,cACT,UAAU,KAAK,QAAQ,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,cAC9C,WAAW,GAAG,QAAQ,IAAI,UAAU;AAAA,cACpC,QAAS,KAAa;AAAA,YACxB,CAAC;AACD,iCAAO,KAAK,iCAA4B;AAAA,cACtC,OAAO,KAAK;AAAA,YACd,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,OAAO;AAAA,MACL,WAAO,iCAAqB,OAAO,QAAQ;AACzC,cAAM,EAAE,MAAM,QAAQ,IAAI;AAE1B,cAAM,UAAU,QAAQ,YAAY;AACpC,cAAM,eAAe,QAAQ,SAAS;AACtC,cAAM,OAAO,WAAW;AAExB,YAAI,CAAC,KAAM;AAEX,YAAI,CAAC,eAAe,EAAE,SAAS,IAAI,GAAG;AACpC,kDAAuB,IAAuB;AAC9C,+BAAO,KAAK,gCAAgC;AAAA,YAC1C,OAAO,KAAK;AAAA,YACZ,QAAQ,KAAK;AAAA,UACf,CAAC;AAED,oBAAM,wBAAU;AAAA,YACd,MAAM;AAAA,YACN,IAAI,KAAK;AAAA,YACT,UAAU,KAAK,QAAQ,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,YAC9C,WAAW,GAAG,QAAQ,IAAI,UAAU;AAAA,YACpC,QAAS,KAAa;AAAA,UACxB,CAAC;AACD,+BAAO,KAAK,iCAA4B;AAAA,YACtC,OAAO,KAAK;AAAA,UACd,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IAEA,UAAU;AAAA;AAAA;AAAA,MAGR,cAAc;AAAA;AAAA;AAAA,MAGd,SAAS;AAAA,QACP,eAAe;AAAA;AAAA;AAAA,QAGf;AAAA,MACF;AAAA;AAAA;AAAA,IAIF;AAAA,IAEA,SAAS;AAAA,MACP,WAAW;AAAA,MACX,IAAI;AAAA,MAEJ,kBAAkB;AAAA,QAChB,sBAAsB,EAAE,MAAM,UAAU,UAAU,MAAM,OAAO,MAAM;AAAA,QACrE,iBAAiB,EAAE,MAAM,UAAU,UAAU,MAAM,OAAO,MAAM;AAAA,MAClE;AAAA,IACF;AAAA,IAEA,SAAS;AAAA,UACP,8BAAc,OAAO,EAAE,QAAQ,MAAM;AACnC,cAAM,eAAe;AAErB,YAAI,UAA0B;AAC9B,YAAI,kBAA0C;AAC9C,YAAI,aAAgC;AAEpC,YAAI,aAAa,QAAQ;AACvB,gBAAM,WAAW,UAAM,0BAAY,aAAa,MAAM;AAEtD,cAAI,UAAU;AACZ,0BAAU,2BAAa,QAAQ;AAAA,UACjC;AAAA,QACF;AAEA,YAAI,aAAa,sBAAsB;AACrC,gBAAM,UAAU,UAAM;AAAA,YACpB,aAAa;AAAA,UACf;AAEA,cAAI,SAAS;AACX,kCAAkB,2CAAqB,OAAO;AAAA,UAChD;AAAA,QACF;AACA,YAAI,aAAa,iBAAiB;AAChC,gBAAM,cAAc,UAAM;AAAA,YACxB,aAAa;AAAA,UACf;AAEA,cAAI,aAAa;AACf,6BAAa,iCAAgB,WAAW;AAAA,UAC1C;AAAA,QACF;AAEA,cAAM,0BAA0C;AAAA,UAC9C,SAAS;AAAA,UACT,MAAM;AAAA,UACN,cAAc,mBAAmB;AAAA,UACjC,SAAS,cAAc;AAAA,UACvB,UAAU;AAAA,QACZ;AAEA,eAAO,cAAc,uBAAuB;AAAA,MAC9C,CAAC;AAAA,IACH;AAAA,IAEA,kBAAkB;AAAA,MAChB,SAAS;AAAA,MACT,eAAe;AAAA,MACf,0BAA0B;AAAA,MAC1B,mBAAmB;AAAA,MACnB,mBAAmB;AAAA,MACnB,YAAY;AAAA,MACZ,mBAAmB,OAAO,EAAE,MAAM,IAAI,MAAM;AAC1C,6BAAO,KAAK,gCAAgC,EAAE,OAAO,KAAK,MAAM,CAAC;AACjE,kBAAM,wBAAU;AAAA,UACd,MAAM;AAAA,UACN,IAAI,KAAK;AAAA,UACT,UAAU,KAAK,QAAQ,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,UAC9C,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AAAA,MACA,6BAA6B;AAAA,IAC/B;AAAA,IACA,gBAAgB;AAAA,MACd,SAAS;AAAA;AAAA,MACT,kBAAkB,CAAC,UAAU,QAAQ;AAAA;AAAA,IACvC;AAAA,IACA,mBAAmB;AAAA,MACjB,6BAA6B;AAAA,MAC7B,uBAAuB,OAAO,EAAE,MAAM,IAAI,MAAM;AAC9C,6BAAO,KAAK,8BAA8B,EAAE,OAAO,KAAK,MAAM,CAAC;AAC/D,kBAAM,wBAAU;AAAA,UACd,MAAM;AAAA,UACN,IAAI,KAAK;AAAA,UACT,UAAU,KAAK,QAAQ,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,UAC9C,gBAAgB;AAAA,QAClB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IAEA,uBAAuB;AAAA,MACrB,SAAS;AAAA,MACT,mBAAmB,CAAC,eAAe;AAAA,MACnC,QAAQ,QAAQ,IAAI;AAAA,IACtB;AAAA,IACA,cAAc;AAAA,IACd,SAAS;AAAA,MACP,eAAe;AAAA,QACb,MAAM;AAAA,QACN,YAAY;AAAA,UACV,UAAU;AAAA,UACV,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,IAEA,gBAAgB,CAAC,QAAQ,IAAI,UAAoB;AAAA,IAEjD,iBAAiB;AAAA,MACf,QAAQ;AAAA,QACN,UAAU,QAAQ,IAAI;AAAA,QACtB,cAAc,QAAQ,IAAI;AAAA,MAC5B;AAAA,MACA,QAAQ;AAAA,QACN,UAAU,QAAQ,IAAI;AAAA,QACtB,cAAc,QAAQ,IAAI;AAAA,MAC5B;AAAA,IACF;AAAA,IAEA,QAAQ;AAAA,MACN,KAAK,CAAC,OAAO,YAAY,SAAS,qBAAO,KAAK,EAAE,SAAS,GAAG,IAAI;AAAA,IAClE;AAAA,EACF,CAAC;AAED,SAAO;AACT;","names":["import_user","import_organization","import_project"]}
@@ -43,19 +43,51 @@ const ROLE_POLICY = {
43
43
  "user:write": () => true,
44
44
  "user:admin": () => true
45
45
  },
46
+ user: {
47
+ "user:read": ({
48
+ user,
49
+ targetUserIds
50
+ }) => targetUserIds.map(String).includes(String(user?.id)),
51
+ "user:write": ({
52
+ user,
53
+ targetUserIds
54
+ }) => targetUserIds.map(String).includes(String(user?.id)),
55
+ "user:admin": ({
56
+ user,
57
+ targetUserIds
58
+ }) => targetUserIds.map(String).includes(String(user?.id)),
59
+ "organization:read": ({
60
+ user,
61
+ targetOrganizations
62
+ }) => targetOrganizations.every(
63
+ (organization) => organization?.membersIds?.map(String).includes(String(user?.id))
64
+ )
65
+ },
46
66
  org_admin: {
47
- "organization:read": () => true,
48
- "organization:write": () => true,
49
- "organization:admin": () => true,
50
- "project:read": () => true,
51
- "project:write": () => true,
52
- "project:admin": () => true,
53
- "dictionary:read": () => true,
54
- "dictionary:write": () => true,
55
- "dictionary:admin": () => true,
56
- "tag:read": () => true,
57
- "tag:write": () => true,
58
- "tag:admin": () => true,
67
+ "organization:read": ({
68
+ organization,
69
+ targetOrganizations
70
+ }) => targetOrganizations.every(
71
+ (targetOrg) => String(targetOrg.id) === String(organization?.id)
72
+ ),
73
+ "organization:write": ({
74
+ organization,
75
+ targetOrganizations
76
+ }) => targetOrganizations.every(
77
+ (targetOrg) => String(targetOrg.id) === String(organization?.id)
78
+ ),
79
+ "organization:admin": ({
80
+ organization,
81
+ targetOrganizations
82
+ }) => targetOrganizations.every(
83
+ (targetOrg) => String(targetOrg.id) === String(organization?.id)
84
+ ),
85
+ "project:read": ({ organization, project }) => String(project?.organizationId) === String(organization?.id),
86
+ "project:write": ({ organization, project }) => String(project?.organizationId) === String(organization?.id),
87
+ "project:admin": ({ organization, project }) => String(project?.organizationId) === String(organization?.id),
88
+ "tag:read": ({ organization, project }) => String(project?.organizationId) === String(organization?.id),
89
+ "tag:write": ({ organization, project }) => String(project?.organizationId) === String(organization?.id),
90
+ "tag:admin": ({ organization, project }) => String(project?.organizationId) === String(organization?.id),
59
91
  "user:write": ({
60
92
  organization,
61
93
  targetUserIds
@@ -65,6 +97,12 @@ const ROLE_POLICY = {
65
97
  },
66
98
  org_user: {
67
99
  "organization:read": () => true,
100
+ "project:read": ({
101
+ user,
102
+ targetProjects
103
+ }) => targetProjects?.every(
104
+ (project) => project?.membersIds?.map(String).includes(String(user?.id))
105
+ ),
68
106
  "user:read": ({
69
107
  organization,
70
108
  targetUserIds
@@ -73,23 +111,62 @@ const ROLE_POLICY = {
73
111
  )
74
112
  },
75
113
  project_admin: {
76
- "project:read": () => true,
77
- "project:write": () => true,
78
- "project:admin": () => true,
79
- "tag:read": () => true,
80
- "tag:write": () => true,
81
- "tag:admin": () => true,
114
+ "project:read": ({
115
+ project,
116
+ targetProjectIds
117
+ }) => targetProjectIds?.every(
118
+ (projectId) => String(project?.id) === String(projectId)
119
+ ),
120
+ "project:write": ({
121
+ project,
122
+ targetProjectIds
123
+ }) => targetProjectIds?.every(
124
+ (projectId) => String(project?.id) === String(projectId)
125
+ ),
126
+ "project:admin": ({
127
+ project,
128
+ targetProjectIds
129
+ }) => targetProjectIds?.every(
130
+ (projectId) => String(project?.id) === String(projectId)
131
+ ),
132
+ "tag:read": ({
133
+ project,
134
+ targetTagProjectIds
135
+ }) => targetTagProjectIds.every(
136
+ (projectId) => String(project?.id) === String(projectId)
137
+ ),
138
+ "tag:write": ({
139
+ project,
140
+ targetTagProjectIds
141
+ }) => targetTagProjectIds.every(
142
+ (projectId) => String(project?.id) === String(projectId)
143
+ ),
144
+ "tag:admin": ({
145
+ project,
146
+ targetTagProjectIds
147
+ }) => targetTagProjectIds.every(
148
+ (projectId) => String(project?.id) === String(projectId)
149
+ ),
82
150
  "user:write": ({
83
151
  project,
84
152
  targetUserIds
85
153
  }) => targetUserIds.every(
86
154
  (targetUserId) => project?.membersIds?.map(String).includes(String(targetUserId))
87
- )
155
+ ),
156
+ "dictionary:read": ({ project, user }) => project?.adminsIds?.map(String).includes(String(user?.id)),
157
+ "dictionary:write": ({ project, user }) => project?.adminsIds?.map(String).includes(String(user?.id)),
158
+ "dictionary:admin": ({ project, user }) => project?.adminsIds?.map(String).includes(String(user?.id))
88
159
  },
89
160
  project_user: {
90
- "project:read": ({ user, project }) => project?.membersIds?.map(String).includes(String(user?.id)),
91
- "dictionary:read": ({ user, project }) => project?.membersIds?.map(String).includes(String(user?.id)),
92
- "dictionary:write": ({ user, project }) => project?.membersIds?.map(String).includes(String(user?.id)),
161
+ "project:read": ({ user, organization, project }) => String(organization?.id) === String(project?.organizationId) && organization?.membersIds?.map(String).includes(String(user?.id)) && project?.membersIds?.map(String).includes(String(user?.id)),
162
+ "dictionary:read": ({
163
+ user,
164
+ project,
165
+ targetDictionaries
166
+ }) => project?.membersIds?.map(String).includes(String(user?.id)) && targetDictionaries.every(
167
+ (dictionary) => dictionary?.projectIds?.map(String).includes(String(project?.id))
168
+ ),
169
+ "dictionary:write": ({ user, project }) => project?.adminsIds?.map(String).includes(String(user?.id)),
93
170
  "tag:read": () => true,
94
171
  "tag:write": () => true,
95
172
  "tag:admin": ({ user, organization }) => organization?.adminsIds?.map(String).includes(String(user?.id)),
@@ -115,6 +192,7 @@ const getSessionRoles = ({
115
192
  if (!user) {
116
193
  return roles;
117
194
  }
195
+ roles.push("user");
118
196
  const isUserAdmin = user.role === "admin";
119
197
  if (isUserAdmin) {
120
198
  roles.push("admin");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/permissions.ts"],"sourcesContent":["import { Dictionary, Organization, Project, Tag, User } from '@/export';\nimport { SessionContext } from '@/types/session.types';\n\n/**\n * @description\n * A named grouping of privileges (e.g. `\"org_admin\"`).\n * Users are *granted* one or more Roles.\n */\nexport type Roles =\n | 'admin'\n | 'org_admin'\n | 'org_user'\n | 'project_admin'\n | 'project_user'\n | 'project_reviewer';\n\n/**\n * @description\n * An atomic operation that can be performed on a resource.\n * - **read**: view or list\n * - **write**: create or update\n * - **admin**: delete or change permissions\n */\nexport type Action = 'read' | 'write' | 'admin';\n\n/**\n * @description\n * A first‑class entity in your domain model that you want to protect.\n */\nexport type Resource = {\n organization: Organization;\n project: Project;\n dictionary: Dictionary;\n tag: Tag;\n user: User;\n};\n\n/**\n * @description\n * A literal string combining a Resource and an Action, e.g. `\"project:write\"`.\n * This is the *unit* checked at runtime in your middleware.\n */\nexport type Permission = `${keyof Resource}:${Action}`;\n\ntype CheckPrivilege = (\n args: any\n) => boolean | undefined | Promise<boolean> | Promise<undefined>;\n\ntype RolePolicy = Record<Roles, Partial<Record<Permission, CheckPrivilege>>>;\n\nexport const ROLE_POLICY = {\n admin: {\n 'organization:read': () => true,\n 'organization:write': () => true,\n 'organization:admin': () => true,\n 'project:read': () => true,\n 'project:write': () => true,\n 'project:admin': () => true,\n 'dictionary:read': () => true,\n 'dictionary:write': () => true,\n 'dictionary:admin': () => true,\n 'tag:read': () => true,\n 'tag:write': () => true,\n 'tag:admin': () => true,\n 'user:read': () => true,\n 'user:write': () => true,\n 'user:admin': () => true,\n },\n org_admin: {\n 'organization:read': () => true,\n 'organization:write': () => true,\n 'organization:admin': () => true,\n\n 'project:read': () => true,\n 'project:write': () => true,\n 'project:admin': () => true,\n 'dictionary:read': () => true,\n 'dictionary:write': () => true,\n 'dictionary:admin': () => true,\n 'tag:read': () => true,\n 'tag:write': () => true,\n 'tag:admin': () => true,\n\n 'user:write': ({\n organization,\n targetUserIds,\n }: SessionContext & { targetUserIds: User['id'][] }) =>\n targetUserIds.every((targetUserId) =>\n organization?.membersIds?.map(String).includes(String(targetUserId))\n ),\n },\n org_user: {\n 'organization:read': () => true,\n\n 'user:read': ({\n organization,\n targetUserIds,\n }: SessionContext & { targetUserIds: User['id'][] }) =>\n targetUserIds.every((targetUserId) =>\n organization?.membersIds?.map(String).includes(String(targetUserId))\n ),\n },\n project_admin: {\n 'project:read': () => true,\n 'project:write': () => true,\n 'project:admin': () => true,\n 'tag:read': () => true,\n 'tag:write': () => true,\n 'tag:admin': () => true,\n\n 'user:write': ({\n project,\n targetUserIds,\n }: SessionContext & { targetUserIds: User['id'][] }) =>\n targetUserIds.every((targetUserId) =>\n project?.membersIds?.map(String).includes(String(targetUserId))\n ),\n },\n project_user: {\n 'project:read': ({ user, project }: SessionContext) =>\n project?.membersIds?.map(String).includes(String(user?.id)),\n\n 'dictionary:read': ({ user, project }: SessionContext) =>\n project?.membersIds?.map(String).includes(String(user?.id)),\n 'dictionary:write': ({ user, project }: SessionContext) =>\n project?.membersIds?.map(String).includes(String(user?.id)),\n\n 'tag:read': () => true,\n 'tag:write': () => true,\n 'tag:admin': ({ user, organization }: SessionContext) =>\n organization?.adminsIds?.map(String).includes(String(user?.id)),\n\n 'user:read': ({\n project,\n targetUserIds,\n }: SessionContext & { targetUserIds: User['id'][] }) =>\n targetUserIds.every((targetUserId) =>\n project?.membersIds?.map(String).includes(String(targetUserId))\n ),\n },\n project_reviewer: {\n 'dictionary:read': ({ user, project }: SessionContext) =>\n project?.membersIds?.map(String).includes(String(user?.id)),\n 'dictionary:write': ({ user, project }: SessionContext) =>\n project?.membersIds?.map(String).includes(String(user?.id)),\n\n 'tag:read': () => true,\n },\n} as const satisfies RolePolicy;\n\nexport const getSessionRoles = ({\n user,\n organization,\n project,\n}: SessionContext): Roles[] => {\n const roles: Roles[] = [];\n\n if (!user) {\n return roles;\n }\n\n const isUserAdmin = user.role === 'admin';\n\n if (isUserAdmin) {\n roles.push('admin');\n }\n\n if (!organization) {\n return roles;\n }\n\n const isOrganizationAdmin = organization.adminsIds\n ?.map(String)\n .includes(String(user!.id));\n\n if (isOrganizationAdmin) {\n roles.push('org_admin');\n }\n\n const isOrganizationMember = organization.membersIds\n ?.map(String)\n .includes(String(user!.id));\n\n if (isOrganizationMember) {\n roles.push('org_user');\n }\n\n if (!project) {\n return roles;\n }\n\n const isProjectAdmin = project.adminsIds\n ?.map(String)\n .includes(String(user!.id));\n\n if (isProjectAdmin) {\n roles.push('project_admin');\n }\n\n const isProjectMember = project?.membersIds\n ?.map(String)\n .includes(String(user!.id));\n\n if (isProjectMember) {\n roles.push('project_user');\n }\n\n // const isProjectReviewer =\n // session.project?.reviewersIds?.includes(session.user!.id);\n\n // if (isProjectReviewer) {\n // roles.push('project_reviewer');\n // }\n\n return roles;\n};\n\nexport const computeEffectivePermission = (roles: Roles[]): Permission[] =>\n Array.from(\n new Set(\n roles.flatMap((role) => Object.keys(ROLE_POLICY[role]) as Permission[])\n )\n );\n\n/**\n * Intersect two permission lists\n * @param permissionList1 - The first permission list\n * @param permissionList2 - The second permission list\n * @returns The intersection of the two permission lists (only permissions present in both)\n */\nexport const intersectPermissions = (\n permissions1: Permission[],\n permissions2: Permission[]\n): Permission[] =>\n permissions1.filter((permission) => permissions2.includes(permission));\n\ntype PermissionResult<\n R extends Roles,\n P extends Permission,\n> = (typeof ROLE_POLICY)[R] extends infer RolePerms\n ? RolePerms extends Record<string, (args: any) => any>\n ? P extends keyof RolePerms\n ? RolePerms[P] extends undefined\n ? never\n : RolePerms[P]\n : never\n : never\n : never;\n\nexport const hasPermission = <P extends Permission>(\n roles: Roles[],\n permission: P\n): PermissionResult<Roles, P> => {\n const rolesCheck: any = roles.map(\n (role) =>\n ROLE_POLICY[role]?.[\n permission as keyof (typeof ROLE_POLICY)[typeof role]\n ] ?? (() => false)\n ) as unknown as PermissionResult<Roles, P>[];\n\n return ((args: any) => rolesCheck.some((check: any) => check(args))) as any;\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/utils/permissions.ts"],"sourcesContent":["import { Dictionary, DictionaryAPI } from '@/types/dictionary.types';\nimport { Organization, OrganizationAPI } from '@/types/organization.types';\nimport { Project, ProjectAPI } from '@/types/project.types';\nimport { SessionContext } from '@/types/session.types';\nimport { Tag, TagAPI } from '@/types/tag.types';\nimport { User, UserAPI } from '@/types/user.types';\n\n/**\n * @description\n * A named grouping of privileges (e.g. `\"org_admin\"`).\n * Users are *granted* one or more Roles.\n */\nexport type Roles =\n | 'user'\n | 'admin'\n | 'org_admin'\n | 'org_user'\n | 'project_admin'\n | 'project_user'\n | 'project_reviewer';\n\n/**\n * @description\n * An atomic operation that can be performed on a resource.\n * - **read**: view or list\n * - **write**: create or update\n * - **admin**: delete or change permissions\n */\nexport type Action = 'read' | 'write' | 'admin';\n\n/**\n * @description\n * A first‑class entity in your domain model that you want to protect.\n */\nexport type Resource = {\n organization: Organization;\n project: Project;\n dictionary: Dictionary;\n tag: Tag;\n user: User;\n};\n\n/**\n * @description\n * A literal string combining a Resource and an Action, e.g. `\"project:write\"`.\n * This is the *unit* checked at runtime in your middleware.\n */\nexport type Permission = `${keyof Resource}:${Action}`;\n\ntype CheckPrivilege = (\n args: any\n) => boolean | undefined | Promise<boolean> | Promise<undefined>;\n\ntype RolePolicy = Record<Roles, Partial<Record<Permission, CheckPrivilege>>>;\n\nexport const ROLE_POLICY = {\n admin: {\n 'organization:read': () => true,\n 'organization:write': () => true,\n 'organization:admin': () => true,\n 'project:read': () => true,\n 'project:write': () => true,\n 'project:admin': () => true,\n 'dictionary:read': () => true,\n 'dictionary:write': () => true,\n 'dictionary:admin': () => true,\n 'tag:read': () => true,\n 'tag:write': () => true,\n 'tag:admin': () => true,\n 'user:read': () => true,\n 'user:write': () => true,\n 'user:admin': () => true,\n },\n user: {\n 'user:read': ({\n user,\n targetUserIds,\n }: SessionContext & { targetUserIds: (User | UserAPI)['id'][] }) =>\n targetUserIds.map(String).includes(String(user?.id)),\n 'user:write': ({\n user,\n targetUserIds,\n }: SessionContext & { targetUserIds: (User | UserAPI)['id'][] }) =>\n targetUserIds.map(String).includes(String(user?.id)),\n 'user:admin': ({\n user,\n targetUserIds,\n }: SessionContext & { targetUserIds: (User | UserAPI)['id'][] }) =>\n targetUserIds.map(String).includes(String(user?.id)),\n 'organization:read': ({\n user,\n targetOrganizations,\n }: SessionContext & {\n targetOrganizations: (Organization | OrganizationAPI)[];\n }) =>\n targetOrganizations.every((organization) =>\n organization?.membersIds?.map(String).includes(String(user?.id))\n ),\n },\n org_admin: {\n 'organization:read': ({\n organization,\n targetOrganizations,\n }: SessionContext & {\n targetOrganizations: (Organization | OrganizationAPI)[];\n }) =>\n targetOrganizations.every(\n (targetOrg) => String(targetOrg.id) === String(organization?.id)\n ),\n 'organization:write': ({\n organization,\n targetOrganizations,\n }: SessionContext & {\n targetOrganizations: (Organization | OrganizationAPI)[];\n }) =>\n targetOrganizations.every(\n (targetOrg) => String(targetOrg.id) === String(organization?.id)\n ),\n 'organization:admin': ({\n organization,\n targetOrganizations,\n }: SessionContext & {\n targetOrganizations: (Organization | OrganizationAPI)[];\n }) =>\n targetOrganizations.every(\n (targetOrg) => String(targetOrg.id) === String(organization?.id)\n ),\n\n 'project:read': ({ organization, project }: SessionContext) =>\n String(project?.organizationId) === String(organization?.id),\n 'project:write': ({ organization, project }: SessionContext) =>\n String(project?.organizationId) === String(organization?.id),\n 'project:admin': ({ organization, project }: SessionContext) =>\n String(project?.organizationId) === String(organization?.id),\n\n 'tag:read': ({ organization, project }: SessionContext) =>\n String(project?.organizationId) === String(organization?.id),\n 'tag:write': ({ organization, project }: SessionContext) =>\n String(project?.organizationId) === String(organization?.id),\n 'tag:admin': ({ organization, project }: SessionContext) =>\n String(project?.organizationId) === String(organization?.id),\n\n 'user:write': ({\n organization,\n targetUserIds,\n }: SessionContext & { targetUserIds: (User | UserAPI)['id'][] }) =>\n targetUserIds.every((targetUserId) =>\n organization?.membersIds?.map(String).includes(String(targetUserId))\n ),\n },\n org_user: {\n 'organization:read': () => true,\n\n 'project:read': ({\n user,\n targetProjects,\n }: SessionContext & {\n targetProjects: (Project | ProjectAPI)[];\n }) =>\n targetProjects?.every((project) =>\n project?.membersIds?.map(String).includes(String(user?.id))\n ),\n\n 'user:read': ({\n organization,\n targetUserIds,\n }: SessionContext & { targetUserIds: (User | UserAPI)['id'][] }) =>\n targetUserIds.every((targetUserId) =>\n organization?.membersIds?.map(String).includes(String(targetUserId))\n ),\n },\n project_admin: {\n 'project:read': ({\n project,\n targetProjectIds,\n }: SessionContext & {\n targetProjectIds?: (Project | ProjectAPI)['id'][];\n }) =>\n targetProjectIds?.every(\n (projectId) => String(project?.id) === String(projectId)\n ),\n 'project:write': ({\n project,\n targetProjectIds,\n }: SessionContext & {\n targetProjectIds?: (Project | ProjectAPI)['id'][];\n }) =>\n targetProjectIds?.every(\n (projectId) => String(project?.id) === String(projectId)\n ),\n 'project:admin': ({\n project,\n targetProjectIds,\n }: SessionContext & {\n targetProjectIds?: (Project | ProjectAPI)['id'][];\n }) =>\n targetProjectIds?.every(\n (projectId) => String(project?.id) === String(projectId)\n ),\n\n 'tag:read': ({\n project,\n targetTagProjectIds,\n }: SessionContext & { targetTagProjectIds: (Tag | TagAPI)['id'][] }) =>\n targetTagProjectIds.every(\n (projectId) => String(project?.id) === String(projectId)\n ),\n 'tag:write': ({\n project,\n targetTagProjectIds,\n }: SessionContext & { targetTagProjectIds: (Tag | TagAPI)['id'][] }) =>\n targetTagProjectIds.every(\n (projectId) => String(project?.id) === String(projectId)\n ),\n 'tag:admin': ({\n project,\n targetTagProjectIds,\n }: SessionContext & { targetTagProjectIds: (Tag | TagAPI)['id'][] }) =>\n targetTagProjectIds.every(\n (projectId) => String(project?.id) === String(projectId)\n ),\n\n 'user:write': ({\n project,\n targetUserIds,\n }: SessionContext & { targetUserIds: (User | UserAPI)['id'][] }) =>\n targetUserIds.every((targetUserId) =>\n project?.membersIds?.map(String).includes(String(targetUserId))\n ),\n\n 'dictionary:read': ({ project, user }: SessionContext) =>\n project?.adminsIds?.map(String).includes(String(user?.id)),\n 'dictionary:write': ({ project, user }: SessionContext) =>\n project?.adminsIds?.map(String).includes(String(user?.id)),\n 'dictionary:admin': ({ project, user }: SessionContext) =>\n project?.adminsIds?.map(String).includes(String(user?.id)),\n },\n project_user: {\n 'project:read': ({ user, organization, project }: SessionContext) =>\n String(organization?.id) === String(project?.organizationId) &&\n organization?.membersIds?.map(String).includes(String(user?.id)) &&\n project?.membersIds?.map(String).includes(String(user?.id)),\n\n 'dictionary:read': ({\n user,\n project,\n targetDictionaries,\n }: SessionContext & {\n targetDictionaries: (Dictionary | DictionaryAPI)[];\n }) =>\n project?.membersIds?.map(String).includes(String(user?.id)) &&\n targetDictionaries.every((dictionary) =>\n dictionary?.projectIds?.map(String).includes(String(project?.id))\n ),\n 'dictionary:write': ({ user, project }: SessionContext) =>\n project?.adminsIds?.map(String).includes(String(user?.id)),\n\n 'tag:read': () => true,\n 'tag:write': () => true,\n 'tag:admin': ({ user, organization }: SessionContext) =>\n organization?.adminsIds?.map(String).includes(String(user?.id)),\n\n 'user:read': ({\n project,\n targetUserIds,\n }: SessionContext & { targetUserIds: (User | UserAPI)['id'][] }) =>\n targetUserIds.every((targetUserId) =>\n project?.membersIds?.map(String).includes(String(targetUserId))\n ),\n },\n project_reviewer: {\n 'dictionary:read': ({ user, project }: SessionContext) =>\n project?.membersIds?.map(String).includes(String(user?.id)),\n 'dictionary:write': ({ user, project }: SessionContext) =>\n project?.membersIds?.map(String).includes(String(user?.id)),\n\n 'tag:read': () => true,\n },\n} as const satisfies RolePolicy;\n\nexport const getSessionRoles = ({\n user,\n organization,\n project,\n}: SessionContext): Roles[] => {\n const roles: Roles[] = [];\n\n if (!user) {\n return roles;\n }\n\n roles.push('user');\n\n const isUserAdmin = user.role === 'admin';\n\n if (isUserAdmin) {\n roles.push('admin');\n }\n\n if (!organization) {\n return roles;\n }\n\n const isOrganizationAdmin = organization.adminsIds\n ?.map(String)\n .includes(String(user!.id));\n\n if (isOrganizationAdmin) {\n roles.push('org_admin');\n }\n\n const isOrganizationMember = organization.membersIds\n ?.map(String)\n .includes(String(user!.id));\n\n if (isOrganizationMember) {\n roles.push('org_user');\n }\n\n if (!project) {\n return roles;\n }\n\n const isProjectAdmin = project.adminsIds\n ?.map(String)\n .includes(String(user!.id));\n\n if (isProjectAdmin) {\n roles.push('project_admin');\n }\n\n const isProjectMember = project?.membersIds\n ?.map(String)\n .includes(String(user!.id));\n\n if (isProjectMember) {\n roles.push('project_user');\n }\n\n // const isProjectReviewer =\n // session.project?.reviewersIds?.includes(session.user!.id);\n\n // if (isProjectReviewer) {\n // roles.push('project_reviewer');\n // }\n\n return roles;\n};\n\nexport const computeEffectivePermission = (roles: Roles[]): Permission[] =>\n Array.from(\n new Set(\n roles.flatMap((role) => Object.keys(ROLE_POLICY[role]) as Permission[])\n )\n );\n\n/**\n * Intersect two permission lists\n * @param permissionList1 - The first permission list\n * @param permissionList2 - The second permission list\n * @returns The intersection of the two permission lists (only permissions present in both)\n */\nexport const intersectPermissions = (\n permissions1: Permission[],\n permissions2: Permission[]\n): Permission[] =>\n permissions1.filter((permission) => permissions2.includes(permission));\n\ntype PermissionResult<\n R extends Roles,\n P extends Permission,\n> = (typeof ROLE_POLICY)[R] extends infer RolePerms\n ? RolePerms extends Record<string, (args: any) => any>\n ? P extends keyof RolePerms\n ? RolePerms[P] extends undefined\n ? never\n : RolePerms[P]\n : never\n : never\n : never;\n\nexport const hasPermission = <P extends Permission>(\n roles: Roles[],\n permission: P\n): PermissionResult<Roles, P> => {\n const rolesCheck: any = roles.map(\n (role) =>\n ROLE_POLICY[role]?.[\n permission as keyof (typeof ROLE_POLICY)[typeof role]\n ] ?? (() => false)\n ) as unknown as PermissionResult<Roles, P>[];\n\n return ((args: any) => rolesCheck.some((check: any) => check(args))) as any;\n};\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/controllers/ai.controller.ts"],"sourcesContent":["import { DiscussionModel } from '@/models/discussion.model';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { Tag } from '@/types/tag.types';\nimport { type KeyPath } from '@intlayer/core';\nimport { getDictionariesByTags } from '@services/dictionary.service';\nimport * as tagService from '@services/tag.service';\nimport { getTagsByKeys } from '@services/tag.service';\nimport {\n AIConfig,\n getAIConfig,\n type AIOptions,\n type ChatCompletionRequestMessage,\n} from '@utils/AI/aiSdk';\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 customQueryUtil from '@utils/AI/customQuery';\nimport * as translateJSONUtil from '@utils/AI/translateJSON';\nimport { ErrorHandler, type AppError } from '@utils/errors';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { NextFunction, Request, Response } from 'express';\nimport type { Locales } from 'intlayer';\n\ntype ReplaceAIConfigByOptions<T> = Omit<T, 'aiConfig'> & {\n aiOptions?: AIOptions;\n};\n\nexport type CustomQueryBody =\n ReplaceAIConfigByOptions<customQueryUtil.CustomQueryOptions>;\nexport type CustomQueryResult =\n ResponseData<customQueryUtil.CustomQueryResultData>;\n\nexport const customQuery = async (\n req: Request<AuditContentDeclarationBody>,\n res: Response<CustomQueryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { aiOptions, tagsKeys, ...rest } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: customQueryUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n const auditResponse = await customQueryUtil.customQuery({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'QUERY_FAILED');\n return;\n }\n\n const responseData = formatResponse<customQueryUtil.CustomQueryResultData>({\n data: auditResponse,\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 TranslateJSONBody = Omit<\n ReplaceAIConfigByOptions<translateJSONUtil.TranslateJSONOptions>,\n 'tags'\n> & {\n tagsKeys?: string[];\n};\nexport type TranslateJSONResult =\n ResponseData<translateJSONUtil.TranslateJSONResultData>;\n\nexport const translateJSON = async (\n req: Request<AuditContentDeclarationBody>,\n res: Response<TranslateJSONResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { aiOptions, tagsKeys, ...rest } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: translateJSONUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 translateJSONUtil.translateJSON({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<translateJSONUtil.TranslateJSONResultData>({\n data: auditResponse,\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 AuditContentDeclarationBody = {\n aiOptions?: AIOptions;\n locales: Locales[];\n defaultLocale: Locales;\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 req: Request<AuditContentDeclarationBody>,\n res: Response<AuditContentDeclarationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { fileContent, filePath, aiOptions, locales, defaultLocale, tagsKeys } =\n req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\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 AuditContentDeclarationFieldBody = {\n aiOptions?: AIOptions;\n locales: Locales[];\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n keyPath: KeyPath[];\n};\nexport type AuditContentDeclarationFieldResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationField = async (\n req: Request<AuditContentDeclarationFieldBody>,\n res: Response<AuditContentDeclarationFieldResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { fileContent, aiOptions, locales, tagsKeys, keyPath } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationFieldUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\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 AuditContentDeclarationMetadataBody = {\n aiOptions?: AIOptions;\n fileContent: string;\n};\nexport type AuditContentDeclarationMetadataResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationMetadata = async (\n req: Request<AuditContentDeclarationMetadataBody>,\n res: Response<AuditContentDeclarationMetadataResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization } = res.locals;\n const { fileContent, aiOptions } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationMetadataUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\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 AuditTagBody = {\n aiOptions?: AIOptions;\n tag: Tag;\n};\nexport type AuditTagResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditTag = async (\n req: Request<undefined, undefined, AuditTagBody>,\n res: Response<AuditTagResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { aiOptions, tag } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditTagUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\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 AskDocQuestionBody = {\n messages: ChatCompletionRequestMessage[];\n discutionId: string;\n};\nexport type AskDocQuestionResult =\n ResponseData<askDocQuestionUtil.AskDocQuestionResult>;\n\nexport const askDocQuestion = async (\n req: Request<undefined, undefined, AskDocQuestionBody>,\n res: Response<AskDocQuestionResult>\n) => {\n const { messages, discutionId } = req.body;\n const { user, project, organization } = res.locals;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: {},\n defaultOptions: askDocQuestionUtil.aiDefaultOptions,\n accessType: ['public'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n // 1. Prepare SSE headers and flush them NOW\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'); // disable nginx buffering\n res.flushHeaders?.();\n res.write(': connected\\n\\n'); // initial comment keeps some browsers happy\n res.flush?.();\n\n // 2. Kick off the upstream stream WITHOUT awaiting it\n askDocQuestionUtil\n .askDocQuestion(messages, aiConfig, {\n onMessage: (chunk) => {\n res.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n res.flush?.();\n },\n })\n .then(async (fullResponse) => {\n const lastUserMessageContent = messages.findLast(\n (msg) => msg.role === 'user'\n )?.content;\n const lastUserMessageNbWords = lastUserMessageContent\n ? lastUserMessageContent.split(' ').length\n : 0;\n if (lastUserMessageNbWords > 2) {\n // If the last user message is less than 3 words, don't persist the discussion\n // Example: \"Hello\", \"Hi\", \"Hey\", \"test\", etc.\n\n // 3. Persist discussion while the client already has all chunks\n await DiscussionModel.findOneAndUpdate(\n { discutionId },\n {\n $set: {\n discutionId,\n userId: user?.id,\n projectId: project?.id,\n organizationId: organization?.id,\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp,\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n relatedFiles: fullResponse.relatedFiles,\n timestamp: new Date(),\n },\n ],\n },\n },\n { upsert: true, new: true }\n );\n }\n\n // 4. Tell the client we're done and close the stream\n res.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n res.end();\n })\n .catch((err) => {\n // propagate error as an SSE event so the client knows why it closed\n res.write(\n `event: error\\ndata: ${JSON.stringify({ message: err.message })}\\n\\n`\n );\n res.end();\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 req: Request<undefined, undefined, AutocompleteBody>,\n res: Response<AutocompleteResponse>\n) => {\n try {\n const { text, aiOptions, contextBefore, currentLine, contextAfter } =\n req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: autocompleteUtil.aiDefaultOptions,\n accessType: ['public'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 res.json(responseData);\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/controllers/ai.controller.ts"],"sourcesContent":["import { DiscussionModel } from '@/models/discussion.model';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport type { Tag } from '@/types/tag.types';\nimport { type KeyPath } from '@intlayer/core';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { getDictionariesByTags } from '@services/dictionary.service';\nimport * as tagService from '@services/tag.service';\nimport { getTagsByKeys } from '@services/tag.service';\nimport {\n AIConfig,\n getAIConfig,\n type AIOptions,\n type ChatCompletionRequestMessage,\n} from '@utils/AI/aiSdk';\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 customQueryUtil from '@utils/AI/customQuery';\nimport * as translateJSONUtil from '@utils/AI/translateJSON';\nimport { ErrorHandler, type AppError } from '@utils/errors';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport type { Locales } from 'intlayer';\n\ntype ReplaceAIConfigByOptions<T> = Omit<T, 'aiConfig'> & {\n aiOptions?: AIOptions;\n};\n\nexport type CustomQueryBody =\n ReplaceAIConfigByOptions<customQueryUtil.CustomQueryOptions>;\nexport type CustomQueryResult =\n ResponseData<customQueryUtil.CustomQueryResultData>;\n\nexport const customQuery = async (\n req: Request<AuditContentDeclarationBody>,\n res: ResponseWithSession<CustomQueryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { aiOptions, tagsKeys, ...rest } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: customQueryUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n try {\n const auditResponse = await customQueryUtil.customQuery({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'QUERY_FAILED');\n return;\n }\n\n const responseData = formatResponse<customQueryUtil.CustomQueryResultData>({\n data: auditResponse,\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 TranslateJSONBody = Omit<\n ReplaceAIConfigByOptions<translateJSONUtil.TranslateJSONOptions>,\n 'tags'\n> & {\n tagsKeys?: string[];\n};\nexport type TranslateJSONResult =\n ResponseData<translateJSONUtil.TranslateJSONResultData>;\n\nexport const translateJSON = async (\n req: Request<AuditContentDeclarationBody>,\n res: ResponseWithSession<TranslateJSONResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { aiOptions, tagsKeys, ...rest } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: translateJSONUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 translateJSONUtil.translateJSON({\n ...rest,\n aiConfig,\n applicationContext: aiOptions?.applicationContext,\n tags,\n });\n\n if (!auditResponse) {\n ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<translateJSONUtil.TranslateJSONResultData>({\n data: auditResponse,\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 AuditContentDeclarationBody = {\n aiOptions?: AIOptions;\n locales: Locales[];\n defaultLocale: Locales;\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 req: Request<AuditContentDeclarationBody>,\n res: ResponseWithSession<AuditContentDeclarationResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { fileContent, filePath, aiOptions, locales, defaultLocale, tagsKeys } =\n req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\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 AuditContentDeclarationFieldBody = {\n aiOptions?: AIOptions;\n locales: Locales[];\n fileContent: string;\n filePath?: string;\n tagsKeys?: string[];\n keyPath: KeyPath[];\n};\nexport type AuditContentDeclarationFieldResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationField = async (\n req: Request<AuditContentDeclarationFieldBody>,\n res: ResponseWithSession<AuditContentDeclarationFieldResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { fileContent, aiOptions, locales, tagsKeys, keyPath } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationFieldUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\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 AuditContentDeclarationMetadataBody = {\n aiOptions?: AIOptions;\n fileContent: string;\n};\nexport type AuditContentDeclarationMetadataResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditContentDeclarationMetadata = async (\n req: Request<AuditContentDeclarationMetadataBody>,\n res: ResponseWithSession<AuditContentDeclarationMetadataResult>,\n _next: NextFunction\n): Promise<void> => {\n const { organization } = res.locals;\n const { fileContent, aiOptions } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditContentDeclarationMetadataUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\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 AuditTagBody = {\n aiOptions?: AIOptions;\n tag: Tag;\n};\nexport type AuditTagResult =\n ResponseData<auditContentDeclarationUtil.AuditFileResultData>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const auditTag = async (\n req: Request<undefined, undefined, AuditTagBody>,\n res: ResponseWithSession<AuditTagResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project } = res.locals;\n const { aiOptions, tag } = req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: auditTagUtil.aiDefaultOptions,\n accessType: ['registered_user', 'apiKey'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 ErrorHandler.handleGenericErrorResponse(res, 'AUDIT_FAILED');\n return;\n }\n\n const responseData =\n formatResponse<auditContentDeclarationUtil.AuditFileResultData>({\n data: auditResponse,\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 AskDocQuestionBody = {\n messages: ChatCompletionRequestMessage[];\n discutionId: string;\n};\nexport type AskDocQuestionResult =\n ResponseData<askDocQuestionUtil.AskDocQuestionResult>;\n\nexport const askDocQuestion = async (\n req: Request<undefined, undefined, AskDocQuestionBody>,\n res: ResponseWithSession<AskDocQuestionResult>\n) => {\n const { messages, discutionId } = req.body;\n const { user, project, organization } = res.locals;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: {},\n defaultOptions: askDocQuestionUtil.aiDefaultOptions,\n accessType: ['public'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\n }\n\n // 1. Prepare SSE headers and flush them NOW\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'); // disable nginx buffering\n res.flushHeaders?.();\n res.write(': connected\\n\\n'); // initial comment keeps some browsers happy\n res.flush?.();\n\n // 2. Kick off the upstream stream WITHOUT awaiting it\n askDocQuestionUtil\n .askDocQuestion(messages, aiConfig, {\n onMessage: (chunk) => {\n res.write(`data: ${JSON.stringify({ chunk })}\\n\\n`);\n res.flush?.();\n },\n })\n .then(async (fullResponse) => {\n const lastUserMessageContent = messages.findLast(\n (msg) => msg.role === 'user'\n )?.content;\n const lastUserMessageNbWords = lastUserMessageContent\n ? lastUserMessageContent.split(' ').length\n : 0;\n if (lastUserMessageNbWords > 2) {\n // If the last user message is less than 3 words, don't persist the discussion\n // Example: \"Hello\", \"Hi\", \"Hey\", \"test\", etc.\n\n // 3. Persist discussion while the client already has all chunks\n await DiscussionModel.findOneAndUpdate(\n { discutionId },\n {\n $set: {\n discutionId,\n userId: user?.id,\n projectId: project?.id,\n organizationId: organization?.id,\n messages: [\n ...messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n timestamp: msg.timestamp,\n })),\n {\n role: 'assistant',\n content: fullResponse.response,\n relatedFiles: fullResponse.relatedFiles,\n timestamp: new Date(),\n },\n ],\n },\n },\n { upsert: true, new: true }\n );\n }\n\n // 4. Tell the client we're done and close the stream\n res.write(\n `data: ${JSON.stringify({ done: true, response: fullResponse })}\\n\\n`\n );\n res.end();\n })\n .catch((err) => {\n // propagate error as an SSE event so the client knows why it closed\n res.write(\n `event: error\\ndata: ${JSON.stringify({ message: err.message })}\\n\\n`\n );\n res.end();\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 req: Request<undefined, undefined, AutocompleteBody>,\n res: ResponseWithSession<AutocompleteResponse>\n) => {\n try {\n const { text, aiOptions, contextBefore, currentLine, contextAfter } =\n req.body;\n\n let aiConfig: AIConfig;\n try {\n aiConfig = await getAIConfig(res, {\n userOptions: aiOptions,\n defaultOptions: autocompleteUtil.aiDefaultOptions,\n accessType: ['public'],\n });\n } catch (error) {\n ErrorHandler.handleGenericErrorResponse(res, 'AI_ACCESS_DENIED');\n return;\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 res.json(responseData);\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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","names":[]}