@intlayer/backend 5.7.3 → 5.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/controllers/dictionary.controller.cjs.map +1 -1
- package/dist/cjs/controllers/newsletter.controller.cjs +12 -13
- package/dist/cjs/controllers/newsletter.controller.cjs.map +1 -1
- package/dist/cjs/controllers/organization.controller.cjs +28 -41
- package/dist/cjs/controllers/organization.controller.cjs.map +1 -1
- package/dist/cjs/controllers/project.controller.cjs +7 -1
- package/dist/cjs/controllers/project.controller.cjs.map +1 -1
- package/dist/cjs/controllers/projectAccessKey.controller.cjs +9 -3
- package/dist/cjs/controllers/projectAccessKey.controller.cjs.map +1 -1
- package/dist/cjs/controllers/stripe.controller.cjs +15 -3
- package/dist/cjs/controllers/stripe.controller.cjs.map +1 -1
- package/dist/cjs/controllers/tag.controller.cjs +4 -6
- package/dist/cjs/controllers/tag.controller.cjs.map +1 -1
- package/dist/cjs/controllers/user.controller.cjs +19 -14
- package/dist/cjs/controllers/user.controller.cjs.map +1 -1
- package/dist/cjs/emails/OAuthTokenCreatedEmail.cjs +5 -7
- package/dist/cjs/emails/OAuthTokenCreatedEmail.cjs.map +1 -1
- package/dist/cjs/schemas/dictionary.schema.cjs +1 -1
- package/dist/cjs/schemas/dictionary.schema.cjs.map +1 -1
- package/dist/cjs/schemas/discussion.schema.cjs +1 -1
- package/dist/cjs/schemas/discussion.schema.cjs.map +1 -1
- package/dist/cjs/schemas/oAuth2.schema.cjs +1 -1
- package/dist/cjs/schemas/oAuth2.schema.cjs.map +1 -1
- package/dist/cjs/schemas/organization.schema.cjs +1 -1
- package/dist/cjs/schemas/organization.schema.cjs.map +1 -1
- package/dist/cjs/schemas/plans.schema.cjs +1 -1
- package/dist/cjs/schemas/plans.schema.cjs.map +1 -1
- package/dist/cjs/schemas/project.schema.cjs +1 -1
- package/dist/cjs/schemas/project.schema.cjs.map +1 -1
- package/dist/cjs/schemas/session.schema.cjs +1 -1
- package/dist/cjs/schemas/session.schema.cjs.map +1 -1
- package/dist/cjs/schemas/tag.schema.cjs +1 -1
- package/dist/cjs/schemas/tag.schema.cjs.map +1 -1
- package/dist/cjs/schemas/user.schema.cjs +1 -1
- package/dist/cjs/schemas/user.schema.cjs.map +1 -1
- package/dist/cjs/types/organization.types.cjs.map +1 -1
- package/dist/cjs/types/project.types.cjs.map +1 -1
- package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs +3 -0
- package/dist/cjs/utils/AI/askDocQuestion/askDocQuestion.cjs.map +1 -1
- package/dist/cjs/utils/auth/getAuth.cjs +8 -5
- package/dist/cjs/utils/auth/getAuth.cjs.map +1 -1
- package/dist/cjs/utils/errors/errorCodes.cjs +13 -0
- package/dist/cjs/utils/errors/errorCodes.cjs.map +1 -1
- package/dist/cjs/utils/mapper/project.cjs +1 -2
- package/dist/cjs/utils/mapper/project.cjs.map +1 -1
- package/dist/cjs/utils/mapper/session.cjs +37 -0
- package/dist/cjs/utils/mapper/session.cjs.map +1 -0
- package/dist/cjs/utils/permissions.cjs +51 -34
- package/dist/cjs/utils/permissions.cjs.map +1 -1
- package/dist/cjs/utils/validation/validateArray.cjs +5 -1
- package/dist/cjs/utils/validation/validateArray.cjs.map +1 -1
- package/dist/esm/controllers/dictionary.controller.mjs.map +1 -1
- package/dist/esm/controllers/newsletter.controller.mjs +12 -13
- package/dist/esm/controllers/newsletter.controller.mjs.map +1 -1
- package/dist/esm/controllers/organization.controller.mjs +28 -41
- package/dist/esm/controllers/organization.controller.mjs.map +1 -1
- package/dist/esm/controllers/project.controller.mjs +7 -1
- package/dist/esm/controllers/project.controller.mjs.map +1 -1
- package/dist/esm/controllers/projectAccessKey.controller.mjs +10 -4
- package/dist/esm/controllers/projectAccessKey.controller.mjs.map +1 -1
- package/dist/esm/controllers/stripe.controller.mjs +15 -3
- package/dist/esm/controllers/stripe.controller.mjs.map +1 -1
- package/dist/esm/controllers/tag.controller.mjs +4 -6
- package/dist/esm/controllers/tag.controller.mjs.map +1 -1
- package/dist/esm/controllers/user.controller.mjs +19 -14
- package/dist/esm/controllers/user.controller.mjs.map +1 -1
- package/dist/esm/emails/OAuthTokenCreatedEmail.mjs +5 -7
- package/dist/esm/emails/OAuthTokenCreatedEmail.mjs.map +1 -1
- package/dist/esm/schemas/dictionary.schema.mjs +1 -1
- package/dist/esm/schemas/dictionary.schema.mjs.map +1 -1
- package/dist/esm/schemas/discussion.schema.mjs +1 -1
- package/dist/esm/schemas/discussion.schema.mjs.map +1 -1
- package/dist/esm/schemas/oAuth2.schema.mjs +1 -1
- package/dist/esm/schemas/oAuth2.schema.mjs.map +1 -1
- package/dist/esm/schemas/organization.schema.mjs +1 -1
- package/dist/esm/schemas/organization.schema.mjs.map +1 -1
- package/dist/esm/schemas/plans.schema.mjs +1 -1
- package/dist/esm/schemas/plans.schema.mjs.map +1 -1
- package/dist/esm/schemas/project.schema.mjs +1 -1
- package/dist/esm/schemas/project.schema.mjs.map +1 -1
- package/dist/esm/schemas/session.schema.mjs +1 -1
- package/dist/esm/schemas/session.schema.mjs.map +1 -1
- package/dist/esm/schemas/tag.schema.mjs +1 -1
- package/dist/esm/schemas/tag.schema.mjs.map +1 -1
- package/dist/esm/schemas/user.schema.mjs +1 -1
- package/dist/esm/schemas/user.schema.mjs.map +1 -1
- package/dist/esm/utils/AI/askDocQuestion/askDocQuestion.mjs +3 -0
- package/dist/esm/utils/AI/askDocQuestion/askDocQuestion.mjs.map +1 -1
- package/dist/esm/utils/auth/getAuth.mjs +8 -5
- package/dist/esm/utils/auth/getAuth.mjs.map +1 -1
- package/dist/esm/utils/errors/errorCodes.mjs +13 -0
- package/dist/esm/utils/errors/errorCodes.mjs.map +1 -1
- package/dist/esm/utils/mapper/project.mjs +1 -2
- package/dist/esm/utils/mapper/project.mjs.map +1 -1
- package/dist/esm/utils/mapper/session.mjs +13 -0
- package/dist/esm/utils/mapper/session.mjs.map +1 -0
- package/dist/esm/utils/permissions.mjs +51 -34
- package/dist/esm/utils/permissions.mjs.map +1 -1
- package/dist/esm/utils/validation/validateArray.mjs +5 -1
- package/dist/esm/utils/validation/validateArray.mjs.map +1 -1
- package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
- package/dist/types/controllers/newsletter.controller.d.ts.map +1 -1
- package/dist/types/controllers/organization.controller.d.ts +3 -5
- package/dist/types/controllers/organization.controller.d.ts.map +1 -1
- package/dist/types/controllers/project.controller.d.ts.map +1 -1
- package/dist/types/controllers/projectAccessKey.controller.d.ts.map +1 -1
- package/dist/types/controllers/stripe.controller.d.ts +4 -1
- package/dist/types/controllers/stripe.controller.d.ts.map +1 -1
- package/dist/types/controllers/tag.controller.d.ts.map +1 -1
- package/dist/types/controllers/user.controller.d.ts.map +1 -1
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts.map +1 -1
- package/dist/types/types/organization.types.d.ts +1 -3
- package/dist/types/types/organization.types.d.ts.map +1 -1
- package/dist/types/types/project.types.d.ts +1 -3
- package/dist/types/types/project.types.d.ts.map +1 -1
- package/dist/types/utils/AI/askDocQuestion/askDocQuestion.d.ts.map +1 -1
- package/dist/types/utils/auth/getAuth.d.ts +2 -2
- package/dist/types/utils/auth/getAuth.d.ts.map +1 -1
- package/dist/types/utils/errors/errorCodes.d.ts +13 -0
- package/dist/types/utils/errors/errorCodes.d.ts.map +1 -1
- package/dist/types/utils/mapper/project.d.ts.map +1 -1
- package/dist/types/utils/mapper/session.d.ts +4 -0
- package/dist/types/utils/mapper/session.d.ts.map +1 -0
- package/dist/types/utils/permissions.d.ts +32 -28
- package/dist/types/utils/permissions.d.ts.map +1 -1
- package/dist/types/utils/validation/validateArray.d.ts.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
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
|
+
{"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 * 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 * 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 * 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 * 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 targetUsers,\n }: SessionContext & { targetUsers: (User | UserAPI)[] }) =>\n targetUsers.every(\n (targetUser) =>\n String(targetUser.id) === String(user?.id) &&\n targetUser.email === user?.email\n ),\n 'user:write': ({\n user,\n targetUsers,\n }: SessionContext & { targetUsers: (User | UserAPI)[] }) =>\n targetUsers.every(\n (targetUser) =>\n String(targetUser.id) === String(user?.id) &&\n targetUser.email === user?.email\n ),\n 'user:admin': ({\n user,\n targetUsers,\n }: SessionContext & { targetUsers: (User | UserAPI)[] }) =>\n targetUsers.every(\n (targetUser) =>\n String(targetUser.id) === String(user?.id) &&\n targetUser.email === user?.email\n ),\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 targetUsers,\n }: SessionContext & { targetUsers: (User | UserAPI)[] }) =>\n targetUsers.every((targetUser) =>\n organization?.membersIds?.map(String).includes(String(targetUser?.id))\n ),\n },\n org_user: {\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\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 targetUsers,\n }: SessionContext & { targetUsers: (User | UserAPI)[] }) =>\n targetUsers.every((targetUser) =>\n organization?.membersIds?.map(String).includes(String(targetUser?.id))\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 targetTags,\n }: SessionContext & { targetTags: (Tag | TagAPI)[] }) =>\n targetTags.every((tag) => String(project?.id) === String(tag?.projectId)),\n 'tag:write': ({\n project,\n targetTags,\n }: SessionContext & { targetTags: (Tag | TagAPI)[] }) =>\n targetTags.every((tag) => String(project?.id) === String(tag?.projectId)),\n 'tag:admin': ({\n project,\n targetTags,\n }: SessionContext & { targetTags: (Tag | TagAPI)[] }) =>\n targetTags.every((tag) => String(project?.id) === String(tag?.projectId)),\n\n 'user:write': ({\n project,\n targetUsers,\n }: SessionContext & { targetUsers: (User | UserAPI)[] }) =>\n targetUsers.every((targetUser) =>\n project?.membersIds?.map(String).includes(String(targetUser?.id))\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': ({\n project,\n targetTags,\n user,\n }: SessionContext & {\n targetTags: (Tag | TagAPI)[];\n }) =>\n project?.membersIds?.map(String).includes(String(user?.id)) &&\n targetTags.every((tag) => String(project?.id) === String(tag?.projectId)),\n 'tag:write': ({\n project,\n targetTags,\n user,\n }: SessionContext & {\n targetTags: (Tag | TagAPI)[];\n }) =>\n project?.membersIds?.map(String).includes(String(user?.id)) &&\n targetTags.every((tag) => String(project?.id) === String(tag?.projectId)),\n 'tag:admin': ({\n user,\n project,\n targetTags,\n }: SessionContext & {\n targetTags: (Tag | TagAPI)[];\n }) =>\n project?.adminsIds?.map(String).includes(String(user?.id)) &&\n targetTags.every((tag) => String(project?.id) === String(tag?.projectId)),\n\n 'user:read': ({\n project,\n targetUsers,\n }: SessionContext & { targetUsers: (User | UserAPI)[] }) =>\n targetUsers.every((targetUser) =>\n project?.membersIds?.map(String).includes(String(targetUser?.id))\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":[]}
|
|
@@ -30,7 +30,11 @@ const validateArray = (value, entityName = "Array", elementType, elementValidato
|
|
|
30
30
|
} else if (maxLength && value.length > maxLength) {
|
|
31
31
|
errors.push(`${entityName} must be at most ${maxLength} items long.`);
|
|
32
32
|
} else if (elementType) {
|
|
33
|
-
const isValid = value.
|
|
33
|
+
const isValid = value.every((element) => {
|
|
34
|
+
console.log("element", element, typeof element, elementType);
|
|
35
|
+
return typeof element === elementType;
|
|
36
|
+
});
|
|
37
|
+
console.log("isValid", isValid);
|
|
34
38
|
if (!isValid) {
|
|
35
39
|
errors.push(`${entityName} must contain only ${elementType} elements.`);
|
|
36
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/utils/validation/validateArray.ts"],"sourcesContent":["/* eslint-disable sonarjs/cognitive-complexity */\ntype ElementType = 'string' | 'number' | 'boolean' | 'object' | 'array';\ntype ElementValidator<T> = (value: T) => boolean | string[];\n\n/**\n * Validates a email field.\n * @param value The value to validate.\n * @param entityName The name of the entity being validated.\n * @param minLength The minimum length of the string.\n * @param maxLength The maximum length of the string.\n * @returns An array of validation errors.\n */\nexport const validateArray = <T = unknown>(\n value: T[],\n entityName = 'Array',\n elementType?: ElementType,\n elementValidator?: ElementValidator<T>,\n minLength = 0,\n maxLength = undefined\n): string[] => {\n const errors: string[] = [];\n\n if (!Array.isArray(value)) {\n errors.push(`${entityName} must be an array.`);\n } else if (minLength && value.length < minLength) {\n errors.push(`${entityName} must be at least ${minLength} items long.`);\n } else if (maxLength && value.length > maxLength) {\n errors.push(`${entityName} must be at most ${maxLength} items long.`);\n } else if (elementType) {\n const isValid = value.
|
|
1
|
+
{"version":3,"sources":["../../../../src/utils/validation/validateArray.ts"],"sourcesContent":["/* eslint-disable sonarjs/cognitive-complexity */\ntype ElementType = 'string' | 'number' | 'boolean' | 'object' | 'array';\ntype ElementValidator<T> = (value: T) => boolean | string[];\n\n/**\n * Validates a email field.\n * @param value The value to validate.\n * @param entityName The name of the entity being validated.\n * @param minLength The minimum length of the string.\n * @param maxLength The maximum length of the string.\n * @returns An array of validation errors.\n */\nexport const validateArray = <T = unknown>(\n value: T[],\n entityName = 'Array',\n elementType?: ElementType,\n elementValidator?: ElementValidator<T>,\n minLength = 0,\n maxLength = undefined\n): string[] => {\n const errors: string[] = [];\n\n if (!Array.isArray(value)) {\n errors.push(`${entityName} must be an array.`);\n } else if (minLength && value.length < minLength) {\n errors.push(`${entityName} must be at least ${minLength} items long.`);\n } else if (maxLength && value.length > maxLength) {\n errors.push(`${entityName} must be at most ${maxLength} items long.`);\n } else if (elementType) {\n const isValid = value.every((element) => {\n console.log('element', element, typeof element, elementType);\n return typeof element === elementType;\n });\n\n console.log('isValid', isValid);\n\n if (!isValid) {\n errors.push(`${entityName} must contain only ${elementType} elements.`);\n }\n } else if (elementValidator) {\n const isValidOrErrorList = value.every(elementValidator);\n\n if (Array.isArray(isValidOrErrorList)) {\n const isValid = isValidOrErrorList.length > 0;\n\n if (!isValid) {\n errors.push(...isValidOrErrorList);\n }\n } else {\n const isValid = isValidOrErrorList;\n\n if (!isValid) {\n errors.push(`${entityName} must contain valid elements.`);\n }\n }\n }\n\n return errors;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAYO,MAAM,gBAAgB,CAC3B,OACA,aAAa,SACb,aACA,kBACA,YAAY,GACZ,YAAY,WACC;AACb,QAAM,SAAmB,CAAC;AAE1B,MAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,WAAO,KAAK,GAAG,UAAU,oBAAoB;AAAA,EAC/C,WAAW,aAAa,MAAM,SAAS,WAAW;AAChD,WAAO,KAAK,GAAG,UAAU,qBAAqB,SAAS,cAAc;AAAA,EACvE,WAAW,aAAa,MAAM,SAAS,WAAW;AAChD,WAAO,KAAK,GAAG,UAAU,oBAAoB,SAAS,cAAc;AAAA,EACtE,WAAW,aAAa;AACtB,UAAM,UAAU,MAAM,MAAM,CAAC,YAAY;AACvC,cAAQ,IAAI,WAAW,SAAS,OAAO,SAAS,WAAW;AAC3D,aAAO,OAAO,YAAY;AAAA,IAC5B,CAAC;AAED,YAAQ,IAAI,WAAW,OAAO;AAE9B,QAAI,CAAC,SAAS;AACZ,aAAO,KAAK,GAAG,UAAU,sBAAsB,WAAW,YAAY;AAAA,IACxE;AAAA,EACF,WAAW,kBAAkB;AAC3B,UAAM,qBAAqB,MAAM,MAAM,gBAAgB;AAEvD,QAAI,MAAM,QAAQ,kBAAkB,GAAG;AACrC,YAAM,UAAU,mBAAmB,SAAS;AAE5C,UAAI,CAAC,SAAS;AACZ,eAAO,KAAK,GAAG,kBAAkB;AAAA,MACnC;AAAA,IACF,OAAO;AACL,YAAM,UAAU;AAEhB,UAAI,CAAC,SAAS;AACZ,eAAO,KAAK,GAAG,UAAU,+BAA+B;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/controllers/dictionary.controller.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryAPI,\n DictionaryCreationData,\n DictionaryData,\n VersionedContent,\n} from '@/types/dictionary.types';\nimport * as eventListener from '@controllers/eventListener.controller';\nimport type {\n ContentNode,\n Dictionary as LocalDictionary,\n} from '@intlayer/core';\nimport { logger } from '@logger';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport * as dictionaryService from '@services/dictionary.service';\nimport { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n type DictionaryFiltersParams,\n getDictionaryFiltersAndPagination,\n} from '@utils/filtersAndPagination/getDictionaryFiltersAndPagination';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport { mapDictionaryToAPI } from '@utils/mapper/dictionary';\nimport { hasPermission } from '@utils/permissions';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport { t } from 'express-intlayer';\n\nexport type GetDictionariesParams =\n FiltersAndPagination<DictionaryFiltersParams>;\nexport type GetDictionariesResult = PaginatedResponse<DictionaryAPI>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const getDictionaries = async (\n req: Request<GetDictionariesParams>,\n res: ResponseWithSession<GetDictionariesResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, project, roles } = res.locals;\n const { filters, pageSize, skip, page, getNumberOfPages } =\n getDictionaryFiltersAndPagination(req);\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n try {\n const dictionaries = await dictionaryService.findDictionaries(\n {\n ...filters,\n projectIds: project.id,\n },\n skip,\n pageSize\n );\n\n if (\n !hasPermission(\n roles,\n 'dictionary:read'\n )({\n ...res.locals,\n targetDictionaries: dictionaries,\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const totalItems = await dictionaryService.countDictionaries(filters);\n\n const dictionariesAPI = dictionaries.map((el) =>\n mapDictionaryToAPI(el, project.id)\n );\n\n const responseData = formatPaginatedResponse<DictionaryAPI>({\n data: dictionariesAPI,\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type GetDictionariesKeysResult = ResponseData<string[]>;\n\n/**\n * Retrieves a list of dictionaries keys based on filters and pagination.\n */\nexport const getDictionariesKeys = async (\n _req: Request,\n res: ResponseWithSession<GetDictionariesKeysResult>,\n _next: NextFunction\n) => {\n const { project, roles } = res.locals;\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n try {\n const dictionaries = await dictionaryService.findDictionaries({\n projectIds: project.id,\n });\n\n if (\n !hasPermission(\n roles,\n 'dictionary:read'\n )({\n ...res.locals,\n targetDictionaries: dictionaries,\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const dictionariesKeys = dictionaries.map((dictionary) => dictionary.key);\n\n const responseData = formatResponse<string[]>({\n data: dictionariesKeys,\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 GetDictionaryParams = { dictionaryKey: string };\nexport type GetDictionaryQuery = { version?: string };\nexport type GetDictionaryResult = ResponseData<DictionaryAPI>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const getDictionaryByKey = async (\n req: Request<GetDictionaryParams, any, any, GetDictionaryQuery>,\n res: ResponseWithSession<GetDictionaryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project, user, roles } = res.locals;\n const { dictionaryKey } = req.params;\n const version = req.query.version;\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n try {\n const dictionary = await dictionaryService.getDictionaryByKey(\n dictionaryKey,\n project.id\n );\n\n if (\n !hasPermission(\n roles,\n 'dictionary:read'\n )({\n ...res.locals,\n targetDictionaries: [dictionary],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n if (!dictionary.projectIds.map(String).includes(String(project.id))) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'DICTIONARY_PROJECT_MISMATCH'\n );\n return;\n }\n\n const apiResult = mapDictionaryToAPI(dictionary, project.id, version);\n\n const responseData = formatResponse<DictionaryAPI>({\n data: apiResult,\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 AddDictionaryBody = { dictionary: DictionaryCreationData };\nexport type AddDictionaryResult = ResponseData<DictionaryAPI>;\n\n/**\n * Adds a new dictionary to the database.\n */\nexport const addDictionary = async (\n req: Request<any, any, AddDictionaryBody>,\n res: ResponseWithSession<AddDictionaryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project, user, roles } = res.locals;\n const dictionaryData = req.body.dictionary;\n\n if (!dictionaryData) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_DATA_NOT_FOUND');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!dictionaryData.projectIds?.includes(String(project.id))) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_PROJECT_MISMATCH');\n return;\n }\n\n const dictionary: DictionaryData = {\n key: dictionaryData.key,\n title: dictionaryData.title,\n description: dictionaryData.description,\n content: new Map([\n ['v1', { content: dictionaryData.content ?? ({} as ContentNode) }],\n ]),\n creatorId: user.id,\n filePath: {\n [String(project.id)]: dictionaryData.filePath ?? '',\n },\n projectIds: dictionaryData.projectIds ?? [String(project.id)],\n };\n\n if (!hasPermission(roles, 'dictionary:write')(res.locals)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const newDictionary = await dictionaryService.createDictionary(dictionary);\n\n const apiResult = mapDictionaryToAPI(newDictionary, project.id);\n\n const responseData = formatResponse<DictionaryAPI>({\n message: t({\n en: 'Dictionary created successfully',\n fr: 'Dictionnaire créé avec succès',\n es: 'Diccionario creado con éxito',\n }),\n description: t({\n en: 'Your dictionary has been created successfully',\n fr: 'Votre dictionnaire a été créé avec succès',\n es: 'Su diccionario ha sido creado con éxito',\n }),\n data: apiResult,\n });\n\n res.json(responseData);\n\n eventListener.sendDictionaryUpdate([\n {\n dictionary: mapDictionaryToAPI(newDictionary, project.id),\n status: 'ADDED',\n },\n ]);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type PushDictionariesBody = {\n dictionaries: LocalDictionary[];\n};\ntype PushDictionariesResultData = {\n newDictionaries: string[];\n updatedDictionaries: string[];\n error: { dictionaryId: string; message: string }[];\n};\nexport type PushDictionariesResult = ResponseData<PushDictionariesResultData>;\n\n/**\n * Check each dictionaries, add the new ones and update the existing ones.\n * @param req - Express request object.\n * @param res - Express response object.\n * @returns Response containing the created dictionary.\n */\nexport const pushDictionaries = async (\n req: Request<any, any, PushDictionariesBody>,\n res: ResponseWithSession<PushDictionariesResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project, user, roles } = res.locals;\n const dictionaryData = req.body.dictionaries;\n const dictionariesKeys = dictionaryData.map((dictionary) => dictionary.key);\n\n if (\n typeof dictionaryData === 'object' &&\n Array.isArray(dictionaryData) &&\n dictionaryData.length === 0\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARIES_NOT_PROVIDED');\n return;\n } else if (!dictionaryData) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_DATA_NOT_FOUND');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'dictionary:write')(res.locals)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const { existingDictionariesKey, newDictionariesKey } =\n await dictionaryService.getExistingDictionaryKey(\n dictionariesKeys,\n project.id\n );\n\n const existingDictionaries = dictionaryData.filter((dictionary) =>\n existingDictionariesKey.includes(dictionary.key)\n );\n const newDictionaries = dictionaryData.filter((dictionary) =>\n newDictionariesKey.includes(dictionary.key)\n );\n\n const newDictionariesResult: DictionaryAPI[] = [];\n const updatedDictionariesResult: DictionaryAPI[] = [];\n const errorResult: PushDictionariesResultData['error'] = [];\n\n for (const dictionaryDataEl of newDictionaries) {\n const dictionary: DictionaryData = {\n title: dictionaryDataEl.title,\n description: dictionaryDataEl.description,\n projectIds: [String(project.id)],\n creatorId: user.id,\n content: new Map([\n ['v1', { content: dictionaryDataEl.content ?? ({} as ContentNode) }],\n ]),\n filePath: {\n [String(project.id)]: dictionaryDataEl.filePath ?? '',\n },\n key: dictionaryDataEl.key,\n };\n\n try {\n const newDictionary =\n await dictionaryService.createDictionary(dictionary);\n newDictionariesResult.push(\n mapDictionaryToAPI(newDictionary, project.id)\n );\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n }\n\n if (existingDictionariesKey.length >= 0) {\n const existingDictionariesDB =\n await dictionaryService.getDictionariesByKeys(\n existingDictionariesKey,\n project.id\n );\n\n for (const dictionaryDataEl of existingDictionaries) {\n const existingDictionaryDB = existingDictionariesDB.find(\n (dictionaryDB) => dictionaryDB.key === dictionaryDataEl.key\n )!;\n\n const versionList = [...(existingDictionaryDB.content.keys() ?? [])];\n const lastVersion = versionList[versionList.length - 1];\n\n const lastContent =\n (existingDictionaryDB.content.get(lastVersion)\n ?.content as DictionaryAPI['content']) ?? null;\n\n const isSameContent =\n JSON.stringify(lastContent) ===\n JSON.stringify(dictionaryDataEl.content);\n\n let newContent: VersionedContent = existingDictionaryDB.content;\n\n if (!isSameContent) {\n const newContentVersion =\n dictionaryService.incrementVersion(existingDictionaryDB);\n\n existingDictionaryDB.content.set(newContentVersion, {\n content: dictionaryDataEl.content ?? ({} as ContentNode),\n });\n\n newContent = existingDictionaryDB.content;\n }\n\n const dictionary: DictionaryData = {\n ...ensureMongoDocumentToObject(existingDictionaryDB),\n ...dictionaryDataEl,\n content: newContent,\n projectIds: [String(project.id)],\n creatorId: user.id,\n filePath: {\n [String(project.id)]: dictionaryDataEl.filePath ?? '',\n },\n key: dictionaryDataEl.key,\n };\n\n try {\n const updatedDictionary =\n await dictionaryService.updateDictionaryByKey(\n dictionaryDataEl.key,\n dictionary,\n project.id\n );\n updatedDictionariesResult.push(\n mapDictionaryToAPI(updatedDictionary, project.id)\n );\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n }\n }\n\n const result: PushDictionariesResultData = {\n newDictionaries: newDictionariesResult.map(\n (dictionary) => dictionary.key\n ),\n updatedDictionaries: updatedDictionariesResult.map(\n (dictionary) => dictionary.key\n ),\n error: errorResult,\n };\n\n const responseData = formatResponse<PushDictionariesResultData>({\n message: t({\n en: 'Dictionaries updated successfully',\n fr: 'Dictionnaires mis à jour avec succès',\n es: 'Diccionarios actualizados con éxito',\n }),\n description: t({\n en: 'Your dictionaries have been updated successfully',\n fr: 'Vos dictionnaires ont été mis à jour avec succès',\n es: 'Sus diccionarios han sido actualizados con éxito',\n }),\n data: result,\n });\n\n eventListener.sendDictionaryUpdate([\n ...newDictionariesResult.map(\n (dictionary) =>\n ({\n dictionary,\n status: 'ADDED',\n }) as eventListener.SendDictionaryUpdateArg\n ),\n ...updatedDictionariesResult.map(\n (dictionary) =>\n ({\n dictionary,\n status: 'UPDATED',\n }) as eventListener.SendDictionaryUpdateArg\n ),\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 UpdateDictionaryParam = { dictionaryId: string };\nexport type UpdateDictionaryBody = Partial<Dictionary>;\nexport type UpdateDictionaryResult = ResponseData<DictionaryAPI>;\n\n/**\n * Updates an existing dictionary in the database.\n */\nexport const updateDictionary = async (\n req: Request<UpdateDictionaryParam, any, UpdateDictionaryBody>,\n res: ResponseWithSession<UpdateDictionaryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { dictionaryId } = req.params;\n const { project, roles } = res.locals;\n const dictionaryData = req.body;\n\n if (!dictionaryData) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_DATA_NOT_FOUND');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!dictionaryData.projectIds?.includes(String(project.id))) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_PROJECT_MISMATCH');\n return;\n }\n\n if (typeof dictionaryId === 'undefined') {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_ID_NOT_FOUND');\n return;\n }\n\n if (!hasPermission(roles, 'dictionary:write')(res.locals)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const updatedDictionary = await dictionaryService.updateDictionaryById(\n dictionaryId,\n dictionaryData\n );\n\n const apiResult = mapDictionaryToAPI(updatedDictionary, project.id);\n\n const responseData = formatResponse<DictionaryAPI>({\n message: t({\n en: 'Dictionary updated successfully',\n fr: 'Dictionnaire mis à jour avec succès',\n es: 'Diccionario actualizado con éxito',\n }),\n description: t({\n en: 'Your dictionary has been updated successfully',\n fr: 'Votre dictionnaire a été mis à jour avec succès',\n es: 'Su diccionario ha sido actualizado con éxito',\n }),\n data: apiResult,\n });\n\n eventListener.sendDictionaryUpdate([\n {\n dictionary: apiResult,\n status: 'UPDATED',\n },\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 DeleteDictionaryParam = { dictionaryId: string };\nexport type DeleteDictionaryResult = ResponseData<DictionaryAPI>;\n\n/**\n * Deletes a dictionary from the database by its ID.\n */\nexport const deleteDictionary = async (\n req: Request<DeleteDictionaryParam>,\n res: ResponseWithSession<DeleteDictionaryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project, roles } = res.locals;\n const { dictionaryId } = req.params as Partial<DeleteDictionaryParam>;\n\n if (!dictionaryId) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_ID_NOT_FOUND');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'dictionary:admin')(res.locals)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const dictionaryToDelete =\n await dictionaryService.getDictionaryById(dictionaryId);\n\n if (!dictionaryToDelete.projectIds.includes(project.id)) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'DICTIONARY_PROJECT_MISMATCH'\n );\n return;\n }\n\n const deletedDictionary =\n await dictionaryService.deleteDictionaryById(dictionaryId);\n\n if (!deletedDictionary) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_NOT_FOUND', {\n dictionaryId,\n });\n return;\n }\n\n logger.info(`Dictionary deleted: ${String(deletedDictionary.id)}`);\n\n const apiResult = mapDictionaryToAPI(deletedDictionary, project.id);\n\n const responseData = formatResponse<DictionaryAPI>({\n message: t({\n en: 'Dictionary deleted successfully',\n fr: 'Dictionnaire supprimé avec succès',\n es: 'Diccionario eliminado con éxito',\n }),\n description: t({\n en: 'Your dictionary has been deleted successfully',\n fr: 'Votre dictionnaire a été supprimé avec succès',\n es: 'Su diccionario ha sido eliminado con éxito',\n }),\n data: apiResult,\n });\n\n res.json(responseData);\n\n eventListener.sendDictionaryUpdate([\n {\n dictionary: apiResult,\n status: 'DELETED',\n },\n ]);\n\n return;\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/dictionary.controller.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryAPI,\n DictionaryCreationData,\n DictionaryData,\n VersionedContent,\n} from '@/types/dictionary.types';\nimport * as eventListener from '@controllers/eventListener.controller';\nimport type {\n ContentNode,\n Dictionary as LocalDictionary,\n} from '@intlayer/core';\nimport { logger } from '@logger';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport * as dictionaryService from '@services/dictionary.service';\nimport { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport {\n type DictionaryFiltersParams,\n getDictionaryFiltersAndPagination,\n} from '@utils/filtersAndPagination/getDictionaryFiltersAndPagination';\nimport type { FiltersAndPagination } from '@utils/filtersAndPagination/getFiltersAndPaginationFromBody';\nimport { mapDictionaryToAPI } from '@utils/mapper/dictionary';\nimport { hasPermission } from '@utils/permissions';\nimport {\n formatPaginatedResponse,\n formatResponse,\n type PaginatedResponse,\n type ResponseData,\n} from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport { t } from 'express-intlayer';\n\nexport type GetDictionariesParams =\n FiltersAndPagination<DictionaryFiltersParams>;\nexport type GetDictionariesResult = PaginatedResponse<DictionaryAPI>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const getDictionaries = async (\n req: Request<GetDictionariesParams>,\n res: ResponseWithSession<GetDictionariesResult>,\n _next: NextFunction\n): Promise<void> => {\n const { user, project, roles } = res.locals;\n const { filters, pageSize, skip, page, getNumberOfPages } =\n getDictionaryFiltersAndPagination(req);\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n try {\n const dictionaries = await dictionaryService.findDictionaries(\n {\n ...filters,\n projectIds: project.id,\n },\n skip,\n pageSize\n );\n\n if (\n !hasPermission(\n roles,\n 'dictionary:read'\n )({\n ...res.locals,\n targetDictionaries: dictionaries,\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const totalItems = await dictionaryService.countDictionaries(filters);\n\n const dictionariesAPI = dictionaries.map((el) =>\n mapDictionaryToAPI(el, project.id)\n );\n\n const responseData = formatPaginatedResponse<DictionaryAPI>({\n data: dictionariesAPI,\n page,\n pageSize,\n totalPages: getNumberOfPages(totalItems),\n totalItems,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type GetDictionariesKeysResult = ResponseData<string[]>;\n\n/**\n * Retrieves a list of dictionaries keys based on filters and pagination.\n */\nexport const getDictionariesKeys = async (\n _req: Request,\n res: ResponseWithSession<GetDictionariesKeysResult>,\n _next: NextFunction\n) => {\n const { project, roles } = res.locals;\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n try {\n const dictionaries = await dictionaryService.findDictionaries({\n projectIds: project.id,\n });\n\n if (\n !hasPermission(\n roles,\n 'dictionary:read'\n )({\n ...res.locals,\n targetDictionaries: dictionaries,\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const dictionariesKeys = dictionaries.map((dictionary) => dictionary.key);\n\n const responseData = formatResponse<string[]>({\n data: dictionariesKeys,\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 GetDictionaryParams = { dictionaryKey: string };\nexport type GetDictionaryQuery = { version?: string };\nexport type GetDictionaryResult = ResponseData<DictionaryAPI>;\n\n/**\n * Retrieves a list of dictionaries based on filters and pagination.\n */\nexport const getDictionaryByKey = async (\n req: Request<GetDictionaryParams, any, any, GetDictionaryQuery>,\n res: ResponseWithSession<GetDictionaryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project, user, roles } = res.locals;\n const { dictionaryKey } = req.params;\n const version = req.query.version;\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n try {\n const dictionary = await dictionaryService.getDictionaryByKey(\n dictionaryKey,\n project.id\n );\n\n if (\n !hasPermission(\n roles,\n 'dictionary:read'\n )({\n ...res.locals,\n targetDictionaries: [dictionary],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n if (!dictionary.projectIds.map(String).includes(String(project.id))) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'DICTIONARY_PROJECT_MISMATCH'\n );\n return;\n }\n\n const apiResult = mapDictionaryToAPI(dictionary, project.id, version);\n\n const responseData = formatResponse<DictionaryAPI>({\n data: apiResult,\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 AddDictionaryBody = { dictionary: DictionaryCreationData };\nexport type AddDictionaryResult = ResponseData<DictionaryAPI>;\n\n/**\n * Adds a new dictionary to the database.\n */\nexport const addDictionary = async (\n req: Request<any, any, AddDictionaryBody>,\n res: ResponseWithSession<AddDictionaryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project, user, roles } = res.locals;\n const dictionaryData = req.body.dictionary;\n\n if (!dictionaryData) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_DATA_NOT_FOUND');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!dictionaryData.projectIds?.includes(String(project.id))) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_PROJECT_MISMATCH');\n return;\n }\n\n const dictionary: DictionaryData = {\n key: dictionaryData.key,\n title: dictionaryData.title,\n description: dictionaryData.description,\n content: new Map([\n ['v1', { content: dictionaryData.content ?? ({} as ContentNode) }],\n ]),\n creatorId: user.id,\n filePath: {\n [String(project.id)]: dictionaryData.filePath ?? '',\n },\n projectIds: dictionaryData.projectIds ?? [String(project.id)],\n };\n\n if (!hasPermission(roles, 'dictionary:write')(res.locals)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const newDictionary = await dictionaryService.createDictionary(dictionary);\n\n const apiResult = mapDictionaryToAPI(newDictionary, project.id);\n\n const responseData = formatResponse<DictionaryAPI>({\n message: t({\n en: 'Dictionary created successfully',\n fr: 'Dictionnaire créé avec succès',\n es: 'Diccionario creado con éxito',\n }),\n description: t({\n en: 'Your dictionary has been created successfully',\n fr: 'Votre dictionnaire a été créé avec succès',\n es: 'Su diccionario ha sido creado con éxito',\n }),\n data: apiResult,\n });\n\n res.json(responseData);\n\n eventListener.sendDictionaryUpdate([\n {\n dictionary: mapDictionaryToAPI(newDictionary, project.id),\n status: 'ADDED',\n },\n ]);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type PushDictionariesBody = {\n dictionaries: LocalDictionary[];\n};\ntype PushDictionariesResultData = {\n newDictionaries: string[];\n updatedDictionaries: string[];\n error: { dictionaryId: string; message: string }[];\n};\nexport type PushDictionariesResult = ResponseData<PushDictionariesResultData>;\n\n/**\n * Check each dictionaries, add the new ones and update the existing ones.\n * @param req - Express request object.\n * @param res - Express response object.\n * @returns Response containing the created dictionary.\n */\nexport const pushDictionaries = async (\n req: Request<any, any, PushDictionariesBody>,\n res: ResponseWithSession<PushDictionariesResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project, user, roles } = res.locals;\n const dictionaryData = req.body.dictionaries;\n const dictionariesKeys = dictionaryData.map((dictionary) => dictionary.key);\n\n if (\n typeof dictionaryData === 'object' &&\n Array.isArray(dictionaryData) &&\n dictionaryData.length === 0\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARIES_NOT_PROVIDED');\n return;\n } else if (!dictionaryData) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_DATA_NOT_FOUND');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'dictionary:write')(res.locals)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const { existingDictionariesKey, newDictionariesKey } =\n await dictionaryService.getExistingDictionaryKey(\n dictionariesKeys,\n project.id\n );\n\n const existingDictionaries = dictionaryData.filter((dictionary) =>\n existingDictionariesKey.includes(dictionary.key)\n );\n const newDictionaries = dictionaryData.filter((dictionary) =>\n newDictionariesKey.includes(dictionary.key)\n );\n\n const newDictionariesResult: DictionaryAPI[] = [];\n const updatedDictionariesResult: DictionaryAPI[] = [];\n const errorResult: PushDictionariesResultData['error'] = [];\n\n for (const dictionaryDataEl of newDictionaries) {\n const dictionary: DictionaryData = {\n title: dictionaryDataEl.title,\n description: dictionaryDataEl.description,\n projectIds: [String(project.id)],\n creatorId: user.id,\n content: new Map([\n ['v1', { content: dictionaryDataEl.content ?? ({} as ContentNode) }],\n ]),\n filePath: {\n [String(project.id)]: dictionaryDataEl.filePath ?? '',\n },\n key: dictionaryDataEl.key,\n };\n\n try {\n const newDictionary =\n await dictionaryService.createDictionary(dictionary);\n newDictionariesResult.push(\n mapDictionaryToAPI(newDictionary, project.id)\n );\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n }\n\n if (existingDictionariesKey.length >= 0) {\n const existingDictionariesDB =\n await dictionaryService.getDictionariesByKeys(\n existingDictionariesKey,\n project.id\n );\n\n for (const dictionaryDataEl of existingDictionaries) {\n const existingDictionaryDB = existingDictionariesDB.find(\n (dictionaryDB) => dictionaryDB.key === dictionaryDataEl.key\n )!;\n\n const versionList = [...(existingDictionaryDB.content.keys() ?? [])];\n const lastVersion = versionList[versionList.length - 1];\n\n const lastContent =\n (existingDictionaryDB.content.get(lastVersion)\n ?.content as DictionaryAPI['content']) ?? null;\n\n const isSameContent =\n JSON.stringify(lastContent) ===\n JSON.stringify(dictionaryDataEl.content);\n\n let newContent: VersionedContent = existingDictionaryDB.content;\n\n if (!isSameContent) {\n const newContentVersion =\n dictionaryService.incrementVersion(existingDictionaryDB);\n\n existingDictionaryDB.content.set(newContentVersion, {\n content: dictionaryDataEl.content ?? ({} as ContentNode),\n });\n\n newContent = existingDictionaryDB.content;\n }\n\n const dictionary: DictionaryData = {\n ...ensureMongoDocumentToObject(existingDictionaryDB),\n ...dictionaryDataEl,\n content: newContent,\n projectIds: [String(project.id)],\n creatorId: user.id,\n filePath: {\n [String(project.id)]: dictionaryDataEl.filePath ?? '',\n },\n key: dictionaryDataEl.key,\n };\n\n try {\n const updatedDictionary =\n await dictionaryService.updateDictionaryByKey(\n dictionaryDataEl.key,\n dictionary,\n project.id\n );\n updatedDictionariesResult.push(\n mapDictionaryToAPI(updatedDictionary, project.id)\n );\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n }\n }\n\n const result: PushDictionariesResultData = {\n newDictionaries: newDictionariesResult.map(\n (dictionary) => dictionary.key\n ),\n updatedDictionaries: updatedDictionariesResult.map(\n (dictionary) => dictionary.key\n ),\n error: errorResult,\n };\n\n const responseData = formatResponse<PushDictionariesResultData>({\n message: t({\n en: 'Dictionaries updated successfully',\n fr: 'Dictionnaires mis à jour avec succès',\n es: 'Diccionarios actualizados con éxito',\n }),\n description: t({\n en: 'Your dictionaries have been updated successfully',\n fr: 'Vos dictionnaires ont été mis à jour avec succès',\n es: 'Sus diccionarios han sido actualizados con éxito',\n }),\n data: result,\n });\n\n eventListener.sendDictionaryUpdate([\n ...newDictionariesResult.map(\n (dictionary) =>\n ({\n dictionary,\n status: 'ADDED',\n }) as eventListener.SendDictionaryUpdateArg\n ),\n ...updatedDictionariesResult.map(\n (dictionary) =>\n ({\n dictionary,\n status: 'UPDATED',\n }) as eventListener.SendDictionaryUpdateArg\n ),\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 UpdateDictionaryParam = { dictionaryId: string };\nexport type UpdateDictionaryBody = Partial<Dictionary>;\nexport type UpdateDictionaryResult = ResponseData<DictionaryAPI>;\n\n/**\n * Updates an existing dictionary in the database.\n */\nexport const updateDictionary = async (\n req: Request<UpdateDictionaryParam, any, UpdateDictionaryBody>,\n res: ResponseWithSession<UpdateDictionaryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { dictionaryId } = req.params;\n const { project, roles } = res.locals;\n const dictionaryData = req.body;\n\n if (!dictionaryData) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_DATA_NOT_FOUND');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!dictionaryData.projectIds?.includes(String(project.id))) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_PROJECT_MISMATCH');\n return;\n }\n\n if (typeof dictionaryId === 'undefined') {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_ID_NOT_FOUND');\n return;\n }\n\n if (!hasPermission(roles, 'dictionary:write')(res.locals)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const updatedDictionary = await dictionaryService.updateDictionaryById(\n dictionaryId,\n dictionaryData\n );\n\n const apiResult = mapDictionaryToAPI(updatedDictionary, project.id);\n\n const responseData = formatResponse<DictionaryAPI>({\n message: t({\n en: 'Dictionary updated successfully',\n fr: 'Dictionnaire mis à jour avec succès',\n es: 'Diccionario actualizado con éxito',\n }),\n description: t({\n en: 'Your dictionary has been updated successfully',\n fr: 'Votre dictionnaire a été mis à jour avec succès',\n es: 'Su diccionario ha sido actualizado con éxito',\n }),\n data: apiResult,\n });\n\n eventListener.sendDictionaryUpdate([\n {\n dictionary: apiResult,\n status: 'UPDATED',\n },\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 DeleteDictionaryParam = { dictionaryId: string };\nexport type DeleteDictionaryResult = ResponseData<DictionaryAPI>;\n\n/**\n * Deletes a dictionary from the database by its ID.\n */\nexport const deleteDictionary = async (\n req: Request<DeleteDictionaryParam>,\n res: ResponseWithSession<DeleteDictionaryResult>,\n _next: NextFunction\n): Promise<void> => {\n const { project, roles } = res.locals;\n const { dictionaryId } = req.params as Partial<DeleteDictionaryParam>;\n\n if (!dictionaryId) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_ID_NOT_FOUND');\n return;\n }\n\n if (!project) {\n ErrorHandler.handleGenericErrorResponse(res, 'PROJECT_NOT_DEFINED');\n return;\n }\n\n if (!hasPermission(roles, 'dictionary:admin')(res.locals)) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n const dictionaryToDelete =\n await dictionaryService.getDictionaryById(dictionaryId);\n\n if (!dictionaryToDelete.projectIds.includes(project.id)) {\n ErrorHandler.handleGenericErrorResponse(\n res,\n 'DICTIONARY_PROJECT_MISMATCH'\n );\n return;\n }\n\n const deletedDictionary =\n await dictionaryService.deleteDictionaryById(dictionaryId);\n\n if (!deletedDictionary) {\n ErrorHandler.handleGenericErrorResponse(res, 'DICTIONARY_NOT_FOUND', {\n dictionaryId,\n });\n return;\n }\n\n logger.info(`Dictionary deleted: ${String(deletedDictionary.id)}`);\n\n const apiResult = mapDictionaryToAPI(deletedDictionary, project.id);\n\n const responseData = formatResponse<DictionaryAPI>({\n message: t({\n en: 'Dictionary deleted successfully',\n fr: 'Dictionnaire supprimé avec succès',\n es: 'Diccionario eliminado con éxito',\n }),\n description: t({\n en: 'Your dictionary has been deleted successfully',\n fr: 'Votre dictionnaire a été supprimé avec succès',\n es: 'Su diccionario ha sido eliminado con éxito',\n }),\n data: apiResult,\n });\n\n res.json(responseData);\n\n eventListener.sendDictionaryUpdate([\n {\n dictionary: apiResult,\n status: 'DELETED',\n },\n ]);\n\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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","names":[]}
|
|
@@ -5,7 +5,6 @@ import { mapUserToAPI } from "./../utils/mapper/user.mjs";
|
|
|
5
5
|
import { hasPermission } from "./../utils/permissions.mjs";
|
|
6
6
|
import { formatResponse } from "./../utils/responseData.mjs";
|
|
7
7
|
import { t } from "express-intlayer";
|
|
8
|
-
import { ObjectId } from "mongodb";
|
|
9
8
|
const subscribeToNewsletter = async (req, res, _next) => {
|
|
10
9
|
const { roles } = res.locals;
|
|
11
10
|
const { email, emailList } = req.body;
|
|
@@ -31,7 +30,7 @@ const subscribeToNewsletter = async (req, res, _next) => {
|
|
|
31
30
|
"user:write"
|
|
32
31
|
)({
|
|
33
32
|
...res.locals,
|
|
34
|
-
|
|
33
|
+
targetUsers: [user]
|
|
35
34
|
})) {
|
|
36
35
|
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
37
36
|
return;
|
|
@@ -69,22 +68,22 @@ const unsubscribeFromNewsletter = async (req, res, _next) => {
|
|
|
69
68
|
ErrorHandler.handleGenericErrorResponse(res, "USER_DATA_NOT_FOUND");
|
|
70
69
|
return;
|
|
71
70
|
}
|
|
72
|
-
if (!hasPermission(
|
|
73
|
-
roles,
|
|
74
|
-
"user:write"
|
|
75
|
-
)({
|
|
76
|
-
...res.locals,
|
|
77
|
-
targetUserIds: [new ObjectId(userId)]
|
|
78
|
-
})) {
|
|
79
|
-
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
71
|
try {
|
|
83
72
|
const user = await userService.getUserById(userId);
|
|
84
73
|
if (!user) {
|
|
85
74
|
ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_FOUND");
|
|
86
75
|
return;
|
|
87
76
|
}
|
|
77
|
+
if (!hasPermission(
|
|
78
|
+
roles,
|
|
79
|
+
"user:write"
|
|
80
|
+
)({
|
|
81
|
+
...res.locals,
|
|
82
|
+
targetUsers: [user]
|
|
83
|
+
})) {
|
|
84
|
+
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
88
87
|
const emailLists = Array.isArray(emailList) ? emailList : [emailList];
|
|
89
88
|
const emailsListObject = Object.fromEntries(
|
|
90
89
|
emailLists.map((list) => [list, false])
|
|
@@ -132,7 +131,7 @@ const getNewsletterStatus = async (_req, res, _next) => {
|
|
|
132
131
|
"user:read"
|
|
133
132
|
)({
|
|
134
133
|
...res.locals,
|
|
135
|
-
|
|
134
|
+
targetUsers: [user]
|
|
136
135
|
})) {
|
|
137
136
|
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
138
137
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/controllers/newsletter.controller.ts"],"sourcesContent":["import type { EmailsList, UserAPI } from '@/types/user.types';\nimport { logger } from '@logger';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport * as userService from '@services/user.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport { hasPermission } from '@utils/permissions';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport { t } from 'express-intlayer';\nimport { ObjectId } from 'mongodb';\n\nexport type NewsletterSubscriptionBody = {\n email: string;\n emailList: EmailsList | EmailsList[];\n};\nexport type NewsletterSubscriptionResult = ResponseData<UserAPI>;\n\n/**\n * Subscribes a user to the newsletter.\n * If the user doesn't exist, creates a new user.\n * If the user exists, updates their newsletter subscription to true.\n */\nexport const subscribeToNewsletter = async (\n req: Request<any, any, NewsletterSubscriptionBody>,\n res: ResponseWithSession<NewsletterSubscriptionResult>,\n _next: NextFunction\n): Promise<void> => {\n const { roles } = res.locals;\n const { email, emailList } = req.body;\n\n if (!email) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_DATA_NOT_FOUND');\n return;\n }\n\n const emailLists = Array.isArray(emailList) ? emailList : [emailList];\n\n // Create new user with newsletter subscription enabled\n const emailsListObject = Object.fromEntries(\n emailLists.map((list) => [list, true])\n ) as Record<EmailsList, boolean>;\n\n try {\n // Check if user exists\n let user = await userService.getUserByEmail(email);\n\n if (!user) {\n user = await userService.createUser({\n email,\n emailsList: emailsListObject,\n });\n\n logger.info(`New user created and subscribed to newsletter: ${email}`);\n } else {\n if (\n !hasPermission(\n roles,\n 'user:write'\n )({\n ...res.locals,\n targetUserIds: [user.id],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n // Update existing user's newsletter subscription\n user = await userService.updateUserById(user.id, {\n emailsList: { ...user.emailsList, ...emailsListObject },\n });\n\n logger.info(`User subscribed to newsletter: ${email}`);\n }\n\n const formattedUser = mapUserToAPI(user);\n\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'Successfully subscribed to newsletter',\n fr: 'Abonnement à la newsletter réussi',\n es: 'Suscripción al boletín exitosa',\n }),\n description: t({\n en: 'You have been successfully subscribed to our newsletter',\n fr: 'Vous avez été abonné avec succès à notre newsletter',\n es: 'Te has suscrito exitosamente a nuestro boletín',\n }),\n data: formattedUser,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type NewsletterUnsubscriptionBody = {\n userId: string;\n emailList: EmailsList | EmailsList[];\n};\n\n/**\n * Unsubscribes a user from the newsletter.\n * Only works if the user exists.\n */\nexport const unsubscribeFromNewsletter = async (\n req: Request<any, any, NewsletterUnsubscriptionBody>,\n res: ResponseWithSession<NewsletterSubscriptionResult>,\n _next: NextFunction\n): Promise<void> => {\n const { userId, emailList } = req.body;\n const { roles } = res.locals;\n\n if (!userId) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_DATA_NOT_FOUND');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'user:write'\n )({\n ...res.locals,\n targetUserIds: [new ObjectId(userId)],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n try {\n // Check if user exists\n const user = await userService.getUserById(userId);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_FOUND');\n return;\n }\n\n const emailLists = Array.isArray(emailList) ? emailList : [emailList];\n\n // Create new user with newsletter subscription enabled\n const emailsListObject = Object.fromEntries(\n emailLists.map((list) => [list, false])\n ) as Record<EmailsList, boolean>;\n\n // Update user's newsletter subscription to false\n const updatedUser = await userService.updateUserById(user.id, {\n emailsList: { ...user.emailsList, ...emailsListObject },\n });\n\n logger.info(`User unsubscribed from newsletter: ${updatedUser.email}`);\n\n const formattedUser = mapUserToAPI(updatedUser);\n\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'Successfully unsubscribed from newsletter',\n fr: 'Désabonnement de la newsletter réussi',\n es: 'Cancelación de suscripción al boletín exitosa',\n }),\n description: t({\n en: 'You have been successfully unsubscribed from our newsletter',\n fr: 'Vous avez été désabonné avec succès de notre newsletter',\n es: 'Te has desuscrito exitosamente de nuestro boletín',\n }),\n data: formattedUser,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\n/**\n * Gets the newsletter subscription status for a user.\n */\nexport const getNewsletterStatus = async (\n _req: Request<{ email: string }>,\n res: ResponseWithSession<NewsletterSubscriptionResult>,\n _next: NextFunction\n): Promise<void> => {\n const email = res.locals.user?.email;\n const { roles } = res.locals;\n\n if (!email) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_DATA_NOT_FOUND');\n return;\n }\n\n try {\n const user = await userService.getUserByEmail(email);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_FOUND');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'user:read'\n )({\n ...res.locals,\n targetUserIds: [user.id],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const formattedUser = mapUserToAPI(user);\n\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'Newsletter subscription status retrieved',\n fr: \"Statut d'abonnement à la newsletter récupéré\",\n es: 'Estado de suscripción al boletín obtenido',\n }),\n data: formattedUser,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/controllers/newsletter.controller.ts"],"sourcesContent":["import type { EmailsList, UserAPI } from '@/types/user.types';\nimport { logger } from '@logger';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport * as userService from '@services/user.service';\nimport { type AppError, ErrorHandler } from '@utils/errors';\nimport { mapUserToAPI } from '@utils/mapper/user';\nimport { hasPermission } from '@utils/permissions';\nimport { formatResponse, type ResponseData } from '@utils/responseData';\nimport type { NextFunction, Request } from 'express';\nimport { t } from 'express-intlayer';\n\nexport type NewsletterSubscriptionBody = {\n email: string;\n emailList: EmailsList | EmailsList[];\n};\nexport type NewsletterSubscriptionResult = ResponseData<UserAPI>;\n\n/**\n * Subscribes a user to the newsletter.\n * If the user doesn't exist, creates a new user.\n * If the user exists, updates their newsletter subscription to true.\n */\nexport const subscribeToNewsletter = async (\n req: Request<any, any, NewsletterSubscriptionBody>,\n res: ResponseWithSession<NewsletterSubscriptionResult>,\n _next: NextFunction\n): Promise<void> => {\n const { roles } = res.locals;\n const { email, emailList } = req.body;\n\n if (!email) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_DATA_NOT_FOUND');\n return;\n }\n\n const emailLists = Array.isArray(emailList) ? emailList : [emailList];\n\n // Create new user with newsletter subscription enabled\n const emailsListObject = Object.fromEntries(\n emailLists.map((list) => [list, true])\n ) as Record<EmailsList, boolean>;\n\n try {\n // Check if user exists\n let user = await userService.getUserByEmail(email);\n\n if (!user) {\n user = await userService.createUser({\n email,\n emailsList: emailsListObject,\n });\n\n logger.info(`New user created and subscribed to newsletter: ${email}`);\n } else {\n if (\n !hasPermission(\n roles,\n 'user:write'\n )({\n ...res.locals,\n targetUsers: [user],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n // Update existing user's newsletter subscription\n user = await userService.updateUserById(user.id, {\n emailsList: { ...user.emailsList, ...emailsListObject },\n });\n\n logger.info(`User subscribed to newsletter: ${email}`);\n }\n\n const formattedUser = mapUserToAPI(user);\n\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'Successfully subscribed to newsletter',\n fr: 'Abonnement à la newsletter réussi',\n es: 'Suscripción al boletín exitosa',\n }),\n description: t({\n en: 'You have been successfully subscribed to our newsletter',\n fr: 'Vous avez été abonné avec succès à notre newsletter',\n es: 'Te has suscrito exitosamente a nuestro boletín',\n }),\n data: formattedUser,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\nexport type NewsletterUnsubscriptionBody = {\n userId: string;\n emailList: EmailsList | EmailsList[];\n};\n\n/**\n * Unsubscribes a user from the newsletter.\n * Only works if the user exists.\n */\nexport const unsubscribeFromNewsletter = async (\n req: Request<any, any, NewsletterUnsubscriptionBody>,\n res: ResponseWithSession<NewsletterSubscriptionResult>,\n _next: NextFunction\n): Promise<void> => {\n const { userId, emailList } = req.body;\n const { roles } = res.locals;\n\n if (!userId) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_DATA_NOT_FOUND');\n return;\n }\n\n try {\n // Check if user exists\n const user = await userService.getUserById(userId);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_FOUND');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'user:write'\n )({\n ...res.locals,\n targetUsers: [user],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const emailLists = Array.isArray(emailList) ? emailList : [emailList];\n\n // Create new user with newsletter subscription enabled\n const emailsListObject = Object.fromEntries(\n emailLists.map((list) => [list, false])\n ) as Record<EmailsList, boolean>;\n\n // Update user's newsletter subscription to false\n const updatedUser = await userService.updateUserById(user.id, {\n emailsList: { ...user.emailsList, ...emailsListObject },\n });\n\n logger.info(`User unsubscribed from newsletter: ${updatedUser.email}`);\n\n const formattedUser = mapUserToAPI(updatedUser);\n\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'Successfully unsubscribed from newsletter',\n fr: 'Désabonnement de la newsletter réussi',\n es: 'Cancelación de suscripción al boletín exitosa',\n }),\n description: t({\n en: 'You have been successfully unsubscribed from our newsletter',\n fr: 'Vous avez été désabonné avec succès de notre newsletter',\n es: 'Te has desuscrito exitosamente de nuestro boletín',\n }),\n data: formattedUser,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n\n/**\n * Gets the newsletter subscription status for a user.\n */\nexport const getNewsletterStatus = async (\n _req: Request<{ email: string }>,\n res: ResponseWithSession<NewsletterSubscriptionResult>,\n _next: NextFunction\n): Promise<void> => {\n const email = res.locals.user?.email;\n const { roles } = res.locals;\n\n if (!email) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_DATA_NOT_FOUND');\n return;\n }\n\n try {\n const user = await userService.getUserByEmail(email);\n\n if (!user) {\n ErrorHandler.handleGenericErrorResponse(res, 'USER_NOT_FOUND');\n return;\n }\n\n if (\n !hasPermission(\n roles,\n 'user:read'\n )({\n ...res.locals,\n targetUsers: [user],\n })\n ) {\n ErrorHandler.handleGenericErrorResponse(res, 'PERMISSION_DENIED');\n return;\n }\n\n const formattedUser = mapUserToAPI(user);\n\n const responseData = formatResponse<UserAPI>({\n message: t({\n en: 'Newsletter subscription status retrieved',\n fr: \"Statut d'abonnement à la newsletter récupéré\",\n es: 'Estado de suscripción al boletín obtenido',\n }),\n data: formattedUser,\n });\n\n res.json(responseData);\n return;\n } catch (error) {\n ErrorHandler.handleAppErrorResponse(res, error as AppError);\n return;\n }\n};\n"],"mappings":"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","names":[]}
|
|
@@ -184,12 +184,6 @@ const addOrganizationMember = async (req, res, _next) => {
|
|
|
184
184
|
ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
|
|
185
185
|
return;
|
|
186
186
|
}
|
|
187
|
-
if (!organization.plan) {
|
|
188
|
-
ErrorHandler.handleGenericErrorResponse(res, "PLAN_NOT_FOUND", {
|
|
189
|
-
organizationId: organization.id
|
|
190
|
-
});
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
187
|
if (!hasPermission(
|
|
194
188
|
roles,
|
|
195
189
|
"organization:admin"
|
|
@@ -226,7 +220,7 @@ const addOrganizationMember = async (req, res, _next) => {
|
|
|
226
220
|
invitedByUsername: user.name,
|
|
227
221
|
invitedByEmail: user.email,
|
|
228
222
|
organizationName: organization.name,
|
|
229
|
-
inviteLink: `${process.env.
|
|
223
|
+
inviteLink: `${process.env.CLIENT_URL}/auth/login?email=${newMember.email}`,
|
|
230
224
|
inviteFromIp: req.ip ?? "",
|
|
231
225
|
inviteFromLocation: req.hostname
|
|
232
226
|
});
|
|
@@ -256,11 +250,25 @@ const addOrganizationMember = async (req, res, _next) => {
|
|
|
256
250
|
};
|
|
257
251
|
const updateOrganizationMembers = async (req, res, _next) => {
|
|
258
252
|
const { organization, roles } = res.locals;
|
|
259
|
-
const { membersIds } = req.body;
|
|
253
|
+
const { membersIds, adminsIds } = req.body;
|
|
260
254
|
if (!organization) {
|
|
261
255
|
ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
|
|
262
256
|
return;
|
|
263
257
|
}
|
|
258
|
+
if (!hasPermission(
|
|
259
|
+
roles,
|
|
260
|
+
"organization:admin"
|
|
261
|
+
)({
|
|
262
|
+
...res.locals,
|
|
263
|
+
targetOrganizations: [organization]
|
|
264
|
+
})) {
|
|
265
|
+
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
if (!membersIds) {
|
|
269
|
+
ErrorHandler.handleGenericErrorResponse(res, "INVALID_REQUEST_BODY");
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
264
272
|
if (membersIds?.length === 0) {
|
|
265
273
|
ErrorHandler.handleGenericErrorResponse(
|
|
266
274
|
res,
|
|
@@ -268,49 +276,28 @@ const updateOrganizationMembers = async (req, res, _next) => {
|
|
|
268
276
|
);
|
|
269
277
|
return;
|
|
270
278
|
}
|
|
271
|
-
if (
|
|
279
|
+
if (adminsIds?.filter((id) => membersIds?.includes(id)).length === 0) {
|
|
272
280
|
ErrorHandler.handleGenericErrorResponse(
|
|
273
281
|
res,
|
|
274
282
|
"ORGANIZATION_MUST_HAVE_ADMIN"
|
|
275
283
|
);
|
|
276
284
|
return;
|
|
277
285
|
}
|
|
278
|
-
if (!hasPermission(
|
|
279
|
-
roles,
|
|
280
|
-
"organization:admin"
|
|
281
|
-
)({
|
|
282
|
-
...res.locals,
|
|
283
|
-
targetOrganizations: [organization]
|
|
284
|
-
})) {
|
|
285
|
-
ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
286
|
try {
|
|
289
|
-
|
|
290
|
-
if (
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return {
|
|
299
|
-
user,
|
|
300
|
-
isAdmin
|
|
301
|
-
};
|
|
302
|
-
});
|
|
303
|
-
existingUsers = userMap;
|
|
304
|
-
}
|
|
287
|
+
const existingUsers = await userService.getUsersByIds(membersIds);
|
|
288
|
+
if (!existingUsers) {
|
|
289
|
+
ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_FOUND");
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const existingAdmins = await userService.getUsersByIds(adminsIds);
|
|
293
|
+
if (!existingAdmins) {
|
|
294
|
+
ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_FOUND");
|
|
295
|
+
return;
|
|
305
296
|
}
|
|
306
|
-
const formattedMembers = existingUsers.map(
|
|
307
|
-
(user) => user.user.id
|
|
308
|
-
);
|
|
309
|
-
const formattedAdmin = existingUsers.filter((el) => el.isAdmin).map((user) => user.user.id);
|
|
310
297
|
const updatedOrganization = await organizationService.updateOrganizationById(organization.id, {
|
|
311
298
|
...organization,
|
|
312
|
-
membersIds:
|
|
313
|
-
adminsIds:
|
|
299
|
+
membersIds: existingUsers.map((user) => user.id),
|
|
300
|
+
adminsIds: existingAdmins.map((user) => user.id)
|
|
314
301
|
});
|
|
315
302
|
const responseData = formatResponse({
|
|
316
303
|
message: t({
|