@intlayer/backend 7.5.9 → 7.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/dist/assets/utils/AI/askDocQuestion/PROMPT.md +1 -1
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/cli/ci.json +3080 -0
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/cli/list_projects.json +1 -0
- package/dist/assets/utils/AI/askDocQuestion/embeddings/docs/en/intlayer_with_fastify.json +9 -0
- package/dist/esm/controllers/ai.controller.mjs +95 -128
- package/dist/esm/controllers/ai.controller.mjs.map +1 -1
- package/dist/esm/controllers/bitbucket.controller.mjs +77 -0
- package/dist/esm/controllers/bitbucket.controller.mjs.map +1 -0
- package/dist/esm/controllers/dictionary.controller.mjs +106 -198
- package/dist/esm/controllers/dictionary.controller.mjs.map +1 -1
- package/dist/esm/controllers/eventListener.controller.mjs +13 -19
- package/dist/esm/controllers/eventListener.controller.mjs.map +1 -1
- package/dist/esm/controllers/github.controller.mjs +77 -0
- package/dist/esm/controllers/github.controller.mjs.map +1 -0
- package/dist/esm/controllers/gitlab.controller.mjs +77 -0
- package/dist/esm/controllers/gitlab.controller.mjs.map +1 -0
- package/dist/esm/controllers/newsletter.controller.mjs +30 -60
- package/dist/esm/controllers/newsletter.controller.mjs.map +1 -1
- package/dist/esm/controllers/oAuth2.controller.mjs +11 -8
- package/dist/esm/controllers/oAuth2.controller.mjs.map +1 -1
- package/dist/esm/controllers/organization.controller.mjs +100 -225
- package/dist/esm/controllers/organization.controller.mjs.map +1 -1
- package/dist/esm/controllers/project.controller.mjs +194 -204
- package/dist/esm/controllers/project.controller.mjs.map +1 -1
- package/dist/esm/controllers/projectAccessKey.controller.mjs +38 -71
- package/dist/esm/controllers/projectAccessKey.controller.mjs.map +1 -1
- package/dist/esm/controllers/search.controller.mjs +3 -3
- package/dist/esm/controllers/search.controller.mjs.map +1 -1
- package/dist/esm/controllers/stripe.controller.mjs +34 -67
- package/dist/esm/controllers/stripe.controller.mjs.map +1 -1
- package/dist/esm/controllers/tag.controller.mjs +51 -113
- package/dist/esm/controllers/tag.controller.mjs.map +1 -1
- package/dist/esm/controllers/user.controller.mjs +64 -113
- package/dist/esm/controllers/user.controller.mjs.map +1 -1
- package/dist/esm/export.mjs +4 -1
- package/dist/esm/index.mjs +105 -41
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/middlewares/oAuth2.middleware.mjs +19 -14
- package/dist/esm/middlewares/oAuth2.middleware.mjs.map +1 -1
- package/dist/esm/middlewares/sessionAuth.middleware.mjs +6 -7
- package/dist/esm/middlewares/sessionAuth.middleware.mjs.map +1 -1
- package/dist/esm/routes/ai.routes.mjs +19 -15
- package/dist/esm/routes/ai.routes.mjs.map +1 -1
- package/dist/esm/routes/bitbucket.routes.mjs +43 -0
- package/dist/esm/routes/bitbucket.routes.mjs.map +1 -0
- package/dist/esm/routes/dictionary.routes.mjs +10 -10
- package/dist/esm/routes/dictionary.routes.mjs.map +1 -1
- package/dist/esm/routes/eventListener.routes.mjs +3 -3
- package/dist/esm/routes/eventListener.routes.mjs.map +1 -1
- package/dist/esm/routes/github.routes.mjs +43 -0
- package/dist/esm/routes/github.routes.mjs.map +1 -0
- package/dist/esm/routes/gitlab.routes.mjs +43 -0
- package/dist/esm/routes/gitlab.routes.mjs.map +1 -0
- package/dist/esm/routes/newsletter.routes.mjs +5 -5
- package/dist/esm/routes/newsletter.routes.mjs.map +1 -1
- package/dist/esm/routes/organization.routes.mjs +11 -11
- package/dist/esm/routes/organization.routes.mjs.map +1 -1
- package/dist/esm/routes/project.routes.mjs +38 -14
- package/dist/esm/routes/project.routes.mjs.map +1 -1
- package/dist/esm/routes/search.routes.mjs +3 -3
- package/dist/esm/routes/search.routes.mjs.map +1 -1
- package/dist/esm/routes/stripe.routes.mjs +5 -5
- package/dist/esm/routes/stripe.routes.mjs.map +1 -1
- package/dist/esm/routes/tags.routes.mjs +6 -6
- package/dist/esm/routes/tags.routes.mjs.map +1 -1
- package/dist/esm/routes/user.routes.mjs +9 -9
- package/dist/esm/routes/user.routes.mjs.map +1 -1
- package/dist/esm/schemas/project.schema.mjs +70 -1
- package/dist/esm/schemas/project.schema.mjs.map +1 -1
- package/dist/esm/services/bitbucket.service.mjs +173 -0
- package/dist/esm/services/bitbucket.service.mjs.map +1 -0
- package/dist/esm/services/ci.service.mjs +134 -0
- package/dist/esm/services/ci.service.mjs.map +1 -0
- package/dist/esm/services/email.service.mjs +1 -1
- package/dist/esm/services/email.service.mjs.map +1 -1
- package/dist/esm/services/github.service.mjs +218 -0
- package/dist/esm/services/github.service.mjs.map +1 -0
- package/dist/esm/services/gitlab.service.mjs +217 -0
- package/dist/esm/services/gitlab.service.mjs.map +1 -0
- package/dist/esm/services/oAuth2.service.mjs +1 -1
- package/dist/esm/services/subscription.service.mjs +1 -1
- package/dist/esm/services/subscription.service.mjs.map +1 -1
- package/dist/esm/services/webhook.service.mjs +164 -0
- package/dist/esm/services/webhook.service.mjs.map +1 -0
- package/dist/esm/utils/auth/getAuth.mjs +28 -16
- package/dist/esm/utils/auth/getAuth.mjs.map +1 -1
- package/dist/esm/utils/cors.mjs +15 -5
- package/dist/esm/utils/cors.mjs.map +1 -1
- package/dist/esm/utils/errors/ErrorHandler.mjs +32 -4
- package/dist/esm/utils/errors/ErrorHandler.mjs.map +1 -1
- package/dist/esm/utils/errors/ErrorsClass.mjs +1 -1
- package/dist/esm/utils/errors/ErrorsClass.mjs.map +1 -1
- package/dist/esm/utils/errors/errorCodes.mjs +234 -0
- package/dist/esm/utils/errors/errorCodes.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getDiscussionFiltersAndPagination.mjs +1 -1
- package/dist/esm/utils/filtersAndPagination/getDiscussionFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getFiltersAndPaginationFromBody.mjs +1 -1
- package/dist/esm/utils/filtersAndPagination/getFiltersAndPaginationFromBody.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getProjectFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getProjectFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getTagFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getTagFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/filtersAndPagination/getUserFiltersAndPagination.mjs +3 -2
- package/dist/esm/utils/filtersAndPagination/getUserFiltersAndPagination.mjs.map +1 -1
- package/dist/esm/utils/mapper/project.mjs +28 -1
- package/dist/esm/utils/mapper/project.mjs.map +1 -1
- package/dist/esm/utils/mongoDB/connectDB.mjs +1 -1
- package/dist/esm/utils/rateLimiter.mjs +40 -30
- package/dist/esm/utils/rateLimiter.mjs.map +1 -1
- package/dist/esm/webhooks/stripe.webhook.mjs +2 -2
- package/dist/esm/webhooks/stripe.webhook.mjs.map +1 -1
- package/dist/types/controllers/ai.controller.d.ts +29 -12
- package/dist/types/controllers/ai.controller.d.ts.map +1 -1
- package/dist/types/controllers/bitbucket.controller.d.ts +62 -0
- package/dist/types/controllers/bitbucket.controller.d.ts.map +1 -0
- package/dist/types/controllers/dictionary.controller.d.ts +23 -13
- package/dist/types/controllers/dictionary.controller.d.ts.map +1 -1
- package/dist/types/controllers/eventListener.controller.d.ts +4 -2
- package/dist/types/controllers/eventListener.controller.d.ts.map +1 -1
- package/dist/types/controllers/github.controller.d.ts +63 -0
- package/dist/types/controllers/github.controller.d.ts.map +1 -0
- package/dist/types/controllers/gitlab.controller.d.ts +67 -0
- package/dist/types/controllers/gitlab.controller.d.ts.map +1 -0
- package/dist/types/controllers/newsletter.controller.d.ts +8 -7
- package/dist/types/controllers/newsletter.controller.d.ts.map +1 -1
- package/dist/types/controllers/oAuth2.controller.d.ts +4 -2
- package/dist/types/controllers/oAuth2.controller.d.ts.map +1 -1
- package/dist/types/controllers/organization.controller.d.ts +28 -12
- package/dist/types/controllers/organization.controller.d.ts.map +1 -1
- package/dist/types/controllers/project.controller.d.ts +60 -17
- package/dist/types/controllers/project.controller.d.ts.map +1 -1
- package/dist/types/controllers/projectAccessKey.controller.d.ts +10 -5
- package/dist/types/controllers/projectAccessKey.controller.d.ts.map +1 -1
- package/dist/types/controllers/search.controller.d.ts +4 -2
- package/dist/types/controllers/search.controller.d.ts.map +1 -1
- package/dist/types/controllers/stripe.controller.d.ts +11 -12
- package/dist/types/controllers/stripe.controller.d.ts.map +1 -1
- package/dist/types/controllers/tag.controller.d.ts +14 -9
- package/dist/types/controllers/tag.controller.d.ts.map +1 -1
- package/dist/types/controllers/user.controller.d.ts +22 -9
- package/dist/types/controllers/user.controller.d.ts.map +1 -1
- package/dist/types/emails/InviteUserEmail.d.ts +4 -4
- package/dist/types/emails/MagicLinkEmail.d.ts +4 -4
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts +4 -4
- package/dist/types/emails/OAuthTokenCreatedEmail.d.ts.map +1 -1
- package/dist/types/emails/PasswordChangeConfirmation.d.ts +4 -4
- package/dist/types/emails/ResetUserPassword.d.ts +4 -4
- package/dist/types/emails/ResetUserPassword.d.ts.map +1 -1
- package/dist/types/emails/SubscriptionPaymentCancellation.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentError.d.ts +4 -4
- package/dist/types/emails/SubscriptionPaymentSuccess.d.ts +4 -4
- package/dist/types/emails/ValidateUserEmail.d.ts +4 -4
- package/dist/types/emails/Welcome.d.ts +4 -4
- package/dist/types/export.d.ts +11 -5
- package/dist/types/middlewares/oAuth2.middleware.d.ts +9 -4
- package/dist/types/middlewares/oAuth2.middleware.d.ts.map +1 -1
- package/dist/types/middlewares/sessionAuth.middleware.d.ts +13 -3
- package/dist/types/middlewares/sessionAuth.middleware.d.ts.map +1 -1
- package/dist/types/models/discussion.model.d.ts +3 -3
- package/dist/types/models/oAuth2.model.d.ts +3 -3
- package/dist/types/routes/ai.routes.d.ts +2 -2
- package/dist/types/routes/ai.routes.d.ts.map +1 -1
- package/dist/types/routes/bitbucket.routes.d.ts +35 -0
- package/dist/types/routes/bitbucket.routes.d.ts.map +1 -0
- package/dist/types/routes/dictionary.routes.d.ts +2 -2
- package/dist/types/routes/dictionary.routes.d.ts.map +1 -1
- package/dist/types/routes/eventListener.routes.d.ts +2 -2
- package/dist/types/routes/eventListener.routes.d.ts.map +1 -1
- package/dist/types/routes/github.routes.d.ts +35 -0
- package/dist/types/routes/github.routes.d.ts.map +1 -0
- package/dist/types/routes/gitlab.routes.d.ts +35 -0
- package/dist/types/routes/gitlab.routes.d.ts.map +1 -0
- package/dist/types/routes/newsletter.routes.d.ts +2 -2
- package/dist/types/routes/newsletter.routes.d.ts.map +1 -1
- package/dist/types/routes/organization.routes.d.ts +2 -2
- package/dist/types/routes/organization.routes.d.ts.map +1 -1
- package/dist/types/routes/project.routes.d.ts +22 -2
- package/dist/types/routes/project.routes.d.ts.map +1 -1
- package/dist/types/routes/search.routes.d.ts +2 -2
- package/dist/types/routes/search.routes.d.ts.map +1 -1
- package/dist/types/routes/stripe.routes.d.ts +2 -2
- package/dist/types/routes/stripe.routes.d.ts.map +1 -1
- package/dist/types/routes/tags.routes.d.ts +2 -2
- package/dist/types/routes/tags.routes.d.ts.map +1 -1
- package/dist/types/routes/user.routes.d.ts +2 -2
- package/dist/types/routes/user.routes.d.ts.map +1 -1
- package/dist/types/schemas/dictionary.schema.d.ts +6 -6
- package/dist/types/schemas/discussion.schema.d.ts +6 -6
- package/dist/types/schemas/oAuth2.schema.d.ts +5 -5
- package/dist/types/schemas/oAuth2.schema.d.ts.map +1 -1
- package/dist/types/schemas/plans.schema.d.ts +6 -6
- package/dist/types/schemas/project.schema.d.ts +6 -6
- package/dist/types/schemas/project.schema.d.ts.map +1 -1
- package/dist/types/schemas/session.schema.d.ts +6 -6
- package/dist/types/schemas/tag.schema.d.ts +6 -6
- package/dist/types/schemas/user.schema.d.ts +6 -6
- package/dist/types/schemas/user.schema.d.ts.map +1 -1
- package/dist/types/services/bitbucket.service.d.ts +71 -0
- package/dist/types/services/bitbucket.service.d.ts.map +1 -0
- package/dist/types/services/ci.service.d.ts +27 -0
- package/dist/types/services/ci.service.d.ts.map +1 -0
- package/dist/types/services/github.service.d.ts +40 -0
- package/dist/types/services/github.service.d.ts.map +1 -0
- package/dist/types/services/gitlab.service.d.ts +58 -0
- package/dist/types/services/gitlab.service.d.ts.map +1 -0
- package/dist/types/services/webhook.service.d.ts +19 -0
- package/dist/types/services/webhook.service.d.ts.map +1 -0
- package/dist/types/types/project.types.d.ts +46 -5
- package/dist/types/types/project.types.d.ts.map +1 -1
- package/dist/types/types/session.types.d.ts +1 -1
- package/dist/types/types/user.types.d.ts +1 -1
- package/dist/types/utils/AI/auditTag/index.d.ts +1 -1
- package/dist/types/utils/auth/getAuth.d.ts.map +1 -1
- package/dist/types/utils/cors.d.ts +2 -2
- package/dist/types/utils/errors/ErrorHandler.d.ts +31 -3
- package/dist/types/utils/errors/ErrorHandler.d.ts.map +1 -1
- package/dist/types/utils/errors/ErrorsClass.d.ts +1 -1
- package/dist/types/utils/errors/errorCodes.d.ts +234 -0
- package/dist/types/utils/errors/errorCodes.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts +8 -4
- package/dist/types/utils/filtersAndPagination/getDictionaryFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts +6 -3
- package/dist/types/utils/filtersAndPagination/getDiscussionFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getFiltersAndPaginationFromBody.d.ts +6 -2
- package/dist/types/utils/filtersAndPagination/getFiltersAndPaginationFromBody.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts +8 -4
- package/dist/types/utils/filtersAndPagination/getOrganizationFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts +6 -2
- package/dist/types/utils/filtersAndPagination/getProjectFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts +8 -4
- package/dist/types/utils/filtersAndPagination/getTagFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts +6 -2
- package/dist/types/utils/filtersAndPagination/getUserFiltersAndPagination.d.ts.map +1 -1
- package/dist/types/utils/mapper/project.d.ts.map +1 -1
- package/dist/types/utils/permissions.d.ts +1 -1
- package/dist/types/utils/rateLimiter.d.ts +4 -2
- package/dist/types/utils/rateLimiter.d.ts.map +1 -1
- package/package.json +24 -28
- package/dist/esm/middlewares/request.middleware.mjs +0 -17
- package/dist/esm/middlewares/request.middleware.mjs.map +0 -1
- package/dist/types/middlewares/request.middleware.d.ts +0 -7
- package/dist/types/middlewares/request.middleware.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getDictionaryFiltersAndPagination.mjs","names":["filters: DictionaryFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getDictionaryFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type DictionaryFiltersParams = {\n ids?: string | string[];\n projectId?: string;\n projectIds?: string[];\n organizationId?: string;\n organizationIds?: string[];\n userId?: string;\n userIds?: string[];\n creatorId?: string;\n creatorIds?: string[];\n title?: string;\n description?: string;\n key?: string;\n keys?: string[];\n tags?: string | string[];\n version?: string;\n search?: string;\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users, if true, will fetch all users without filtering by organization\n */\n fetchAll?: 'true' | 'false';\n};\nexport type DictionaryFilters = RootFilterQuery<Dictionary>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express request object.\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getDictionaryFiltersAndPagination = (\n req
|
|
1
|
+
{"version":3,"file":"getDictionaryFiltersAndPagination.mjs","names":["filters: DictionaryFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getDictionaryFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { FastifyRequest } from 'fastify';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Dictionary } from '@/types/dictionary.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type DictionaryFiltersParams = {\n ids?: string | string[];\n projectId?: string;\n projectIds?: string[];\n organizationId?: string;\n organizationIds?: string[];\n userId?: string;\n userIds?: string[];\n creatorId?: string;\n creatorIds?: string[];\n title?: string;\n description?: string;\n key?: string;\n keys?: string[];\n tags?: string | string[];\n version?: string;\n search?: string;\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users, if true, will fetch all users without filtering by organization\n */\n fetchAll?: 'true' | 'false';\n};\nexport type DictionaryFilters = RootFilterQuery<Dictionary>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express or Fastify request object.\n * @param res - Express or Fastify response object (optional, for Express compatibility).\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getDictionaryFiltersAndPagination = (\n req:\n | Request<FiltersAndPagination<DictionaryFiltersParams>>\n | FastifyRequest<{\n Querystring: FiltersAndPagination<DictionaryFiltersParams>;\n }>,\n res?: ResponseWithSession\n) => {\n const { filters: filtersRequest, ...pagination } =\n getFiltersAndPaginationFromBody<DictionaryFiltersParams>(req as any);\n // Support both Express (res.locals) and Fastify (req.locals)\n const locals = (res as any)?.locals || (req as FastifyRequest).locals || {};\n const { roles, project } = locals;\n\n let filters: DictionaryFilters = {};\n let sortOptions: Record<string, 1 | -1> = { updatedAt: -1 };\n\n const {\n key,\n search,\n keys,\n tags,\n ids,\n projectId,\n projectIds,\n userId,\n userIds,\n creatorId,\n creatorIds,\n title,\n description,\n version,\n sortBy,\n sortOrder,\n fetchAll,\n } = filtersRequest ?? {};\n\n if (ids) {\n filters = { ...filters, _id: { $in: ensureArrayQueryFilter(ids) } };\n }\n\n if (projectId) {\n filters = { ...filters, projectIds: projectId };\n }\n\n if (projectIds) {\n filters = {\n ...filters,\n projectIds: { $in: ensureArrayQueryFilter(projectIds) },\n };\n }\n\n if (!(roles.includes('admin') && fetchAll === 'true')) {\n filters = { ...filters, projectIds: { $in: project?.id } };\n }\n\n if (userId) {\n filters = { ...filters, creatorId: userId };\n }\n\n if (userIds) {\n filters = {\n ...filters,\n creatorId: { $in: ensureArrayQueryFilter(userIds) },\n };\n }\n\n if (creatorId) {\n filters = { ...filters, creatorId: creatorId };\n }\n\n if (creatorIds) {\n filters = {\n ...filters,\n creatorId: { $in: ensureArrayQueryFilter(creatorIds) },\n };\n }\n\n if (title) {\n filters = { ...filters, title: new RegExp(title, 'i') };\n }\n\n if (description) {\n filters = { ...filters, description: new RegExp(description, 'i') };\n }\n\n if (key) {\n filters = { ...filters, key: new RegExp(key, 'i') };\n }\n\n if (search) {\n const searchRegex = new RegExp(search, 'i');\n filters = {\n ...filters,\n $or: [\n { key: searchRegex },\n { title: searchRegex },\n { description: searchRegex },\n { tags: { $in: [searchRegex] } },\n ],\n };\n }\n\n if (keys) {\n filters = { ...filters, key: { $in: ensureArrayQueryFilter(keys) } };\n }\n\n if (tags) {\n filters = { ...filters, tags: { $in: ensureArrayQueryFilter(tags) } };\n }\n\n if (version) {\n filters = { ...filters, content: { [version]: `$content.${version}` } };\n }\n\n if (sortBy && sortOrder && (sortOrder === 'asc' || sortOrder === 'desc')) {\n sortOptions = { [sortBy]: sortOrder === 'asc' ? 1 : -1 };\n }\n\n return { filters, sortOptions, ...pagination };\n};\n"],"mappings":";;;;;;;;;;AA2CA,MAAa,qCACX,KAKA,QACG;CACH,MAAM,EAAE,SAAS,gBAAgB,GAAG,eAClC,gCAAyD,IAAW;CAGtE,MAAM,EAAE,OAAO,YADC,KAAa,UAAW,IAAuB,UAAU,EAAE;CAG3E,IAAIA,UAA6B,EAAE;CACnC,IAAIC,cAAsC,EAAE,WAAW,IAAI;CAE3D,MAAM,EACJ,KACA,QACA,MACA,MACA,KACA,WACA,YACA,QACA,SACA,WACA,YACA,OACA,aACA,SACA,QACA,WACA,aACE,kBAAkB,EAAE;AAExB,KAAI,IACF,WAAU;EAAE,GAAG;EAAS,KAAK,EAAE,KAAK,uBAAuB,IAAI,EAAE;EAAE;AAGrE,KAAI,UACF,WAAU;EAAE,GAAG;EAAS,YAAY;EAAW;AAGjD,KAAI,WACF,WAAU;EACR,GAAG;EACH,YAAY,EAAE,KAAK,uBAAuB,WAAW,EAAE;EACxD;AAGH,KAAI,EAAE,MAAM,SAAS,QAAQ,IAAI,aAAa,QAC5C,WAAU;EAAE,GAAG;EAAS,YAAY,EAAE,KAAK,SAAS,IAAI;EAAE;AAG5D,KAAI,OACF,WAAU;EAAE,GAAG;EAAS,WAAW;EAAQ;AAG7C,KAAI,QACF,WAAU;EACR,GAAG;EACH,WAAW,EAAE,KAAK,uBAAuB,QAAQ,EAAE;EACpD;AAGH,KAAI,UACF,WAAU;EAAE,GAAG;EAAoB;EAAW;AAGhD,KAAI,WACF,WAAU;EACR,GAAG;EACH,WAAW,EAAE,KAAK,uBAAuB,WAAW,EAAE;EACvD;AAGH,KAAI,MACF,WAAU;EAAE,GAAG;EAAS,OAAO,IAAI,OAAO,OAAO,IAAI;EAAE;AAGzD,KAAI,YACF,WAAU;EAAE,GAAG;EAAS,aAAa,IAAI,OAAO,aAAa,IAAI;EAAE;AAGrE,KAAI,IACF,WAAU;EAAE,GAAG;EAAS,KAAK,IAAI,OAAO,KAAK,IAAI;EAAE;AAGrD,KAAI,QAAQ;EACV,MAAM,cAAc,IAAI,OAAO,QAAQ,IAAI;AAC3C,YAAU;GACR,GAAG;GACH,KAAK;IACH,EAAE,KAAK,aAAa;IACpB,EAAE,OAAO,aAAa;IACtB,EAAE,aAAa,aAAa;IAC5B,EAAE,MAAM,EAAE,KAAK,CAAC,YAAY,EAAE,EAAE;IACjC;GACF;;AAGH,KAAI,KACF,WAAU;EAAE,GAAG;EAAS,KAAK,EAAE,KAAK,uBAAuB,KAAK,EAAE;EAAE;AAGtE,KAAI,KACF,WAAU;EAAE,GAAG;EAAS,MAAM,EAAE,KAAK,uBAAuB,KAAK,EAAE;EAAE;AAGvE,KAAI,QACF,WAAU;EAAE,GAAG;EAAS,SAAS,GAAG,UAAU,YAAY,WAAW;EAAE;AAGzE,KAAI,UAAU,cAAc,cAAc,SAAS,cAAc,QAC/D,eAAc,GAAG,SAAS,cAAc,QAAQ,IAAI,IAAI;AAG1D,QAAO;EAAE;EAAS;EAAa,GAAG;EAAY"}
|
|
@@ -8,7 +8,7 @@ import { getFiltersAndPaginationFromBody } from "./getFiltersAndPaginationFromBo
|
|
|
8
8
|
*/
|
|
9
9
|
const getDiscussionFiltersAndPagination = (req, res) => {
|
|
10
10
|
const { filters: filtersRequest, ...pagination } = getFiltersAndPaginationFromBody(req);
|
|
11
|
-
const { roles, user } = res.locals;
|
|
11
|
+
const { roles, user } = res?.locals || req.locals || {};
|
|
12
12
|
let filters = {};
|
|
13
13
|
let sortOptions = { updatedAt: -1 };
|
|
14
14
|
const { ids, userId, userIds, discussionId, search, isArchived, sortBy, sortOrder, fetchAll } = filtersRequest ?? {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getDiscussionFiltersAndPagination.mjs","names":["filters: DiscussionFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getDiscussionFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Discussion } from '@/types/discussion.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type DiscussionFiltersParams = {\n ids?: string | string[];\n userId?: string;\n userIds?: string[];\n discussionId?: string;\n search?: string;\n isArchived?: 'true' | 'false';\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users only: if true, will not restrict to current user\n */\n fetchAll?: 'true' | 'false';\n};\nexport type DiscussionFilters = RootFilterQuery<Discussion>;\n\n/**\n * Extracts filters and pagination information for discussions.\n * Enforces that non-admin users can only see their own discussions.\n */\nexport const getDiscussionFiltersAndPagination = (\n req
|
|
1
|
+
{"version":3,"file":"getDiscussionFiltersAndPagination.mjs","names":["filters: DiscussionFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getDiscussionFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { FastifyRequest } from 'fastify';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Discussion } from '@/types/discussion.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type DiscussionFiltersParams = {\n ids?: string | string[];\n userId?: string;\n userIds?: string[];\n discussionId?: string;\n search?: string;\n isArchived?: 'true' | 'false';\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users only: if true, will not restrict to current user\n */\n fetchAll?: 'true' | 'false';\n};\nexport type DiscussionFilters = RootFilterQuery<Discussion>;\n\n/**\n * Extracts filters and pagination information for discussions.\n * Enforces that non-admin users can only see their own discussions.\n */\nexport const getDiscussionFiltersAndPagination = (\n req:\n | Request<FiltersAndPagination<DiscussionFiltersParams>>\n | FastifyRequest<{\n Querystring: FiltersAndPagination<DiscussionFiltersParams>;\n }>,\n res?: ResponseWithSession\n) => {\n const { filters: filtersRequest, ...pagination } =\n getFiltersAndPaginationFromBody<DiscussionFiltersParams>(req as any);\n // Support both Express (res.locals) and Fastify (req.locals)\n const locals = (res as any)?.locals || (req as FastifyRequest).locals || {};\n const { roles, user } = locals;\n\n let filters: DiscussionFilters = {};\n let sortOptions: Record<string, 1 | -1> = { updatedAt: -1 };\n\n const {\n ids,\n userId,\n userIds,\n discussionId,\n search,\n isArchived,\n sortBy,\n sortOrder,\n fetchAll,\n } = filtersRequest ?? {};\n\n if (ids) {\n filters = { ...filters, _id: { $in: ensureArrayQueryFilter(ids) } };\n }\n\n if (discussionId) {\n filters = { ...filters, discussionId };\n }\n\n if (typeof isArchived !== 'undefined') {\n filters = { ...filters, isArchived: isArchived === 'true' };\n }\n\n if (userId) {\n filters = { ...filters, userId: userId };\n }\n\n if (userIds) {\n filters = { ...filters, userId: { $in: ensureArrayQueryFilter(userIds) } };\n }\n\n if (search) {\n const searchRegex = new RegExp(search, 'i');\n filters = {\n ...filters,\n $or: [\n { discussionId: searchRegex },\n { title: searchRegex },\n // search within messages content\n { 'messages.content': searchRegex },\n ],\n } as DiscussionFilters;\n }\n\n if (sortBy && sortOrder && (sortOrder === 'asc' || sortOrder === 'desc')) {\n sortOptions = { [sortBy]: sortOrder === 'asc' ? 1 : -1 };\n }\n\n // Enforce user scope for non-admins\n const isAdmin = roles.includes('admin');\n if (!isAdmin && user?.id) {\n filters = { ...filters, userId: user.id };\n } else if (isAdmin && fetchAll !== 'true' && user?.id) {\n // by default, even admins see their own discussions unless fetchAll=true\n filters = { ...filters, userId: user.id };\n }\n\n return { filters, sortOptions, ...pagination };\n};\n"],"mappings":";;;;;;;;AA+BA,MAAa,qCACX,KAKA,QACG;CACH,MAAM,EAAE,SAAS,gBAAgB,GAAG,eAClC,gCAAyD,IAAW;CAGtE,MAAM,EAAE,OAAO,SADC,KAAa,UAAW,IAAuB,UAAU,EAAE;CAG3E,IAAIA,UAA6B,EAAE;CACnC,IAAIC,cAAsC,EAAE,WAAW,IAAI;CAE3D,MAAM,EACJ,KACA,QACA,SACA,cACA,QACA,YACA,QACA,WACA,aACE,kBAAkB,EAAE;AAExB,KAAI,IACF,WAAU;EAAE,GAAG;EAAS,KAAK,EAAE,KAAK,uBAAuB,IAAI,EAAE;EAAE;AAGrE,KAAI,aACF,WAAU;EAAE,GAAG;EAAS;EAAc;AAGxC,KAAI,OAAO,eAAe,YACxB,WAAU;EAAE,GAAG;EAAS,YAAY,eAAe;EAAQ;AAG7D,KAAI,OACF,WAAU;EAAE,GAAG;EAAiB;EAAQ;AAG1C,KAAI,QACF,WAAU;EAAE,GAAG;EAAS,QAAQ,EAAE,KAAK,uBAAuB,QAAQ,EAAE;EAAE;AAG5E,KAAI,QAAQ;EACV,MAAM,cAAc,IAAI,OAAO,QAAQ,IAAI;AAC3C,YAAU;GACR,GAAG;GACH,KAAK;IACH,EAAE,cAAc,aAAa;IAC7B,EAAE,OAAO,aAAa;IAEtB,EAAE,oBAAoB,aAAa;IACpC;GACF;;AAGH,KAAI,UAAU,cAAc,cAAc,SAAS,cAAc,QAC/D,eAAc,GAAG,SAAS,cAAc,QAAQ,IAAI,IAAI;CAI1D,MAAM,UAAU,MAAM,SAAS,QAAQ;AACvC,KAAI,CAAC,WAAW,MAAM,GACpB,WAAU;EAAE,GAAG;EAAS,QAAQ,KAAK;EAAI;UAChC,WAAW,aAAa,UAAU,MAAM,GAEjD,WAAU;EAAE,GAAG;EAAS,QAAQ,KAAK;EAAI;AAG3C,QAAO;EAAE;EAAS;EAAa,GAAG;EAAY"}
|
|
@@ -3,7 +3,7 @@ const DEFAULT_PAGE_SIZE = 1e3;
|
|
|
3
3
|
const DEFAULT_PAGE = 1;
|
|
4
4
|
/**
|
|
5
5
|
* Extracts filters and pagination information from the request body.
|
|
6
|
-
* @param req - Express request object.
|
|
6
|
+
* @param req - Express or Fastify request object.
|
|
7
7
|
* @returns Object containing filters, page, pageSize, and getNumberOfPages functions.
|
|
8
8
|
*/
|
|
9
9
|
const getFiltersAndPaginationFromBody = (req) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFiltersAndPaginationFromBody.mjs","names":[],"sources":["../../../../src/utils/filtersAndPagination/getFiltersAndPaginationFromBody.ts"],"sourcesContent":["import type { Request } from 'express';\nimport type { ObjectId } from 'mongoose';\n\nconst DEFAULT_PAGE_SIZE = 1000;\nconst DEFAULT_PAGE = 1;\n\ntype Filters = Record<string, string | string[] | ObjectId | ObjectId[]>;\n\nexport type FiltersAndPagination<T extends Filters> =\n | ({\n page?: string | number;\n pageSize?: string | number;\n } & T)\n | undefined;\n\nexport type FiltersAndPaginationResult<T extends Filters> = {\n filters: T;\n page: number;\n skip: number;\n pageSize: number;\n getNumberOfPages: (totalItems: number) => number;\n};\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express request object.\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getFiltersAndPaginationFromBody = <T extends Filters>(\n req:
|
|
1
|
+
{"version":3,"file":"getFiltersAndPaginationFromBody.mjs","names":[],"sources":["../../../../src/utils/filtersAndPagination/getFiltersAndPaginationFromBody.ts"],"sourcesContent":["import type { Request } from 'express';\nimport type { FastifyRequest } from 'fastify';\nimport type { ObjectId } from 'mongoose';\n\nconst DEFAULT_PAGE_SIZE = 1000;\nconst DEFAULT_PAGE = 1;\n\ntype Filters = Record<string, string | string[] | ObjectId | ObjectId[]>;\n\nexport type FiltersAndPagination<T extends Filters> =\n | ({\n page?: string | number;\n pageSize?: string | number;\n } & T)\n | undefined;\n\nexport type FiltersAndPaginationResult<T extends Filters> = {\n filters: T;\n page: number;\n skip: number;\n pageSize: number;\n getNumberOfPages: (totalItems: number) => number;\n};\n\ntype RequestLike<T extends Filters> =\n | Request<FiltersAndPagination<T>>\n | FastifyRequest<{ Querystring: FiltersAndPagination<T> }>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express or Fastify request object.\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getFiltersAndPaginationFromBody = <T extends Filters>(\n req: RequestLike<T>\n): FiltersAndPaginationResult<T> => {\n let filters = {} as T;\n let pageSize = DEFAULT_PAGE_SIZE;\n let page = DEFAULT_PAGE;\n\n const query = req.query as unknown as FiltersAndPagination<T>;\n\n if (typeof query === 'object') {\n const {\n pageSize: pageSizeRequest,\n page: pageRequest,\n ...filtersRequest\n } = query;\n\n if (typeof pageSizeRequest === 'string') {\n pageSize = parseInt(pageSizeRequest, 10);\n } else if (typeof pageSizeRequest === 'number') {\n pageSize = pageSizeRequest;\n }\n\n if (typeof pageRequest === 'string') {\n page = parseInt(pageRequest, 10);\n } else if (typeof pageRequest === 'number') {\n page = pageRequest;\n }\n\n if (filtersRequest && Object.keys(filtersRequest).length > 0) {\n filters = filtersRequest as T;\n }\n }\n\n const skip = (page - 1) * pageSize;\n\n const getNumberOfPages = (totalItems: number) =>\n Math.ceil(totalItems / pageSize);\n\n return {\n filters,\n skip,\n page,\n pageSize,\n getNumberOfPages,\n };\n};\n"],"mappings":";AAIA,MAAM,oBAAoB;AAC1B,MAAM,eAAe;;;;;;AA4BrB,MAAa,mCACX,QACkC;CAClC,IAAI,UAAU,EAAE;CAChB,IAAI,WAAW;CACf,IAAI,OAAO;CAEX,MAAM,QAAQ,IAAI;AAElB,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,EACJ,UAAU,iBACV,MAAM,aACN,GAAG,mBACD;AAEJ,MAAI,OAAO,oBAAoB,SAC7B,YAAW,SAAS,iBAAiB,GAAG;WAC/B,OAAO,oBAAoB,SACpC,YAAW;AAGb,MAAI,OAAO,gBAAgB,SACzB,QAAO,SAAS,aAAa,GAAG;WACvB,OAAO,gBAAgB,SAChC,QAAO;AAGT,MAAI,kBAAkB,OAAO,KAAK,eAAe,CAAC,SAAS,EACzD,WAAU;;CAId,MAAM,QAAQ,OAAO,KAAK;CAE1B,MAAM,oBAAoB,eACxB,KAAK,KAAK,aAAa,SAAS;AAElC,QAAO;EACL;EACA;EACA;EACA;EACA;EACD"}
|
|
@@ -4,12 +4,13 @@ import { getFiltersAndPaginationFromBody } from "./getFiltersAndPaginationFromBo
|
|
|
4
4
|
//#region src/utils/filtersAndPagination/getOrganizationFiltersAndPagination.ts
|
|
5
5
|
/**
|
|
6
6
|
* Extracts filters and pagination information from the request body.
|
|
7
|
-
* @param req - Express request object.
|
|
7
|
+
* @param req - Express or Fastify request object.
|
|
8
|
+
* @param res - Express or Fastify response object (optional, for Express compatibility).
|
|
8
9
|
* @returns Object containing filters, page, pageSize, and getNumberOfPages functions.
|
|
9
10
|
*/
|
|
10
11
|
const getOrganizationFiltersAndPagination = (req, res) => {
|
|
11
12
|
const { filters: filtersRequest, ...pagination } = getFiltersAndPaginationFromBody(req);
|
|
12
|
-
const { roles, user } = res.locals;
|
|
13
|
+
const { roles, user } = res?.locals || req.locals || {};
|
|
13
14
|
let filters = {};
|
|
14
15
|
let sortOptions = { updatedAt: -1 };
|
|
15
16
|
const { name, search, ids, membersIds, fetchAll, sortBy, sortOrder } = filtersRequest ?? {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getOrganizationFiltersAndPagination.mjs","names":["filters: OrganizationFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getOrganizationFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Organization } from '@/types/organization.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type OrganizationFiltersParams = {\n /**\n * Comma separated list of ids\n *\n * ```\n * GET /organizations?ids=5f8d9f1d8a1e4f0e8c0c,5f8d9f1d8a1e4f0e8d1\n * -> ids: \"5f8d9f1d8a1e4f0e8c0c,5f8d9f1d8a1e4f0e8d1\"\n * ```\n */\n ids?: string | string[];\n name?: string;\n search?: string;\n membersIds?: string[];\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users, if true, will fetch all organizations without filtering by members\n */\n fetchAll?: 'true' | 'false';\n};\nexport type OrganizationFilters = RootFilterQuery<Organization>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express request object.\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getOrganizationFiltersAndPagination = (\n req
|
|
1
|
+
{"version":3,"file":"getOrganizationFiltersAndPagination.mjs","names":["filters: OrganizationFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getOrganizationFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { FastifyRequest } from 'fastify';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Organization } from '@/types/organization.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type OrganizationFiltersParams = {\n /**\n * Comma separated list of ids\n *\n * ```\n * GET /organizations?ids=5f8d9f1d8a1e4f0e8c0c,5f8d9f1d8a1e4f0e8d1\n * -> ids: \"5f8d9f1d8a1e4f0e8c0c,5f8d9f1d8a1e4f0e8d1\"\n * ```\n */\n ids?: string | string[];\n name?: string;\n search?: string;\n membersIds?: string[];\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users, if true, will fetch all organizations without filtering by members\n */\n fetchAll?: 'true' | 'false';\n};\nexport type OrganizationFilters = RootFilterQuery<Organization>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express or Fastify request object.\n * @param res - Express or Fastify response object (optional, for Express compatibility).\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getOrganizationFiltersAndPagination = (\n req:\n | Request<FiltersAndPagination<OrganizationFiltersParams>>\n | FastifyRequest<{\n Querystring: FiltersAndPagination<OrganizationFiltersParams>;\n }>,\n res?: ResponseWithSession\n) => {\n const { filters: filtersRequest, ...pagination } =\n getFiltersAndPaginationFromBody<OrganizationFiltersParams>(req as any);\n // Support both Express (res.locals) and Fastify (req.locals)\n const locals = (res as any)?.locals || (req as FastifyRequest).locals || {};\n const { roles, user } = locals;\n\n let filters: OrganizationFilters = {};\n let sortOptions: Record<string, 1 | -1> = { updatedAt: -1 };\n\n const { name, search, ids, membersIds, fetchAll, sortBy, sortOrder } =\n filtersRequest ?? {};\n\n if (ids) {\n filters = { ...filters, _id: { $in: ensureArrayQueryFilter(ids) } };\n }\n\n // Non-admins can only see organizations they are members of\n if (!(roles.includes('admin') && fetchAll === 'true')) {\n filters = { ...filters, membersIds: { $in: [user?.id] } };\n }\n\n if (name) {\n filters = { ...filters, name: new RegExp(name, 'i') };\n }\n\n if (search) {\n const searchRegex = new RegExp(search, 'i');\n filters = { ...filters, $or: [{ name: searchRegex }] };\n }\n\n if (membersIds) {\n filters = {\n ...filters,\n membersIds: { $in: ensureArrayQueryFilter(membersIds) },\n };\n }\n\n if (sortBy && sortOrder && (sortOrder === 'asc' || sortOrder === 'desc')) {\n sortOptions = { [sortBy]: sortOrder === 'asc' ? 1 : -1 };\n }\n\n return { filters, sortOptions, ...pagination };\n};\n"],"mappings":";;;;;;;;;;AAuCA,MAAa,uCACX,KAKA,QACG;CACH,MAAM,EAAE,SAAS,gBAAgB,GAAG,eAClC,gCAA2D,IAAW;CAGxE,MAAM,EAAE,OAAO,SADC,KAAa,UAAW,IAAuB,UAAU,EAAE;CAG3E,IAAIA,UAA+B,EAAE;CACrC,IAAIC,cAAsC,EAAE,WAAW,IAAI;CAE3D,MAAM,EAAE,MAAM,QAAQ,KAAK,YAAY,UAAU,QAAQ,cACvD,kBAAkB,EAAE;AAEtB,KAAI,IACF,WAAU;EAAE,GAAG;EAAS,KAAK,EAAE,KAAK,uBAAuB,IAAI,EAAE;EAAE;AAIrE,KAAI,EAAE,MAAM,SAAS,QAAQ,IAAI,aAAa,QAC5C,WAAU;EAAE,GAAG;EAAS,YAAY,EAAE,KAAK,CAAC,MAAM,GAAG,EAAE;EAAE;AAG3D,KAAI,KACF,WAAU;EAAE,GAAG;EAAS,MAAM,IAAI,OAAO,MAAM,IAAI;EAAE;AAGvD,KAAI,QAAQ;EACV,MAAM,cAAc,IAAI,OAAO,QAAQ,IAAI;AAC3C,YAAU;GAAE,GAAG;GAAS,KAAK,CAAC,EAAE,MAAM,aAAa,CAAC;GAAE;;AAGxD,KAAI,WACF,WAAU;EACR,GAAG;EACH,YAAY,EAAE,KAAK,uBAAuB,WAAW,EAAE;EACxD;AAGH,KAAI,UAAU,cAAc,cAAc,SAAS,cAAc,QAC/D,eAAc,GAAG,SAAS,cAAc,QAAQ,IAAI,IAAI;AAG1D,QAAO;EAAE;EAAS;EAAa,GAAG;EAAY"}
|
|
@@ -4,12 +4,13 @@ import { getFiltersAndPaginationFromBody } from "./getFiltersAndPaginationFromBo
|
|
|
4
4
|
//#region src/utils/filtersAndPagination/getProjectFiltersAndPagination.ts
|
|
5
5
|
/**
|
|
6
6
|
* Extracts filters and pagination information from the request body.
|
|
7
|
-
* @param req - Express request object.
|
|
7
|
+
* @param req - Express or Fastify request object.
|
|
8
|
+
* @param res - Express or Fastify response object (optional, for Express compatibility).
|
|
8
9
|
* @returns Object containing filters, page, pageSize, and getNumberOfPages functions.
|
|
9
10
|
*/
|
|
10
11
|
const getProjectFiltersAndPagination = (req, res) => {
|
|
11
12
|
const { filters: filtersRequest, ...pagination } = getFiltersAndPaginationFromBody(req);
|
|
12
|
-
const { roles, organization } = res.locals;
|
|
13
|
+
const { roles, organization } = res?.locals || req.locals || {};
|
|
13
14
|
let filters = {};
|
|
14
15
|
let sortOptions = { updatedAt: -1 };
|
|
15
16
|
const { name, search, ids, organizationId, membersIds, sortBy, sortOrder, fetchAll } = filtersRequest ?? {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getProjectFiltersAndPagination.mjs","names":["filters: ProjectFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getProjectFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Project } from '@/types/project.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type ProjectFiltersParams = {\n ids?: string | string[];\n name?: string;\n search?: string;\n organizationId?: string;\n membersIds?: string[];\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users, if true, will fetch all projects without filtering by organization\n */\n fetchAll?: 'true' | 'false';\n};\nexport type ProjectFilters = RootFilterQuery<Project>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express request object.\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getProjectFiltersAndPagination = (\n req
|
|
1
|
+
{"version":3,"file":"getProjectFiltersAndPagination.mjs","names":["filters: ProjectFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getProjectFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { FastifyRequest } from 'fastify';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Project } from '@/types/project.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type ProjectFiltersParams = {\n ids?: string | string[];\n name?: string;\n search?: string;\n organizationId?: string;\n membersIds?: string[];\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users, if true, will fetch all projects without filtering by organization\n */\n fetchAll?: 'true' | 'false';\n};\nexport type ProjectFilters = RootFilterQuery<Project>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express or Fastify request object.\n * @param res - Express or Fastify response object (optional, for Express compatibility).\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getProjectFiltersAndPagination = (\n req:\n | Request<FiltersAndPagination<ProjectFiltersParams>>\n | FastifyRequest<{\n Querystring: FiltersAndPagination<ProjectFiltersParams>;\n }>,\n res?: ResponseWithSession\n) => {\n const { filters: filtersRequest, ...pagination } =\n getFiltersAndPaginationFromBody<ProjectFiltersParams>(req as any);\n // Support both Express (res.locals) and Fastify (req.locals)\n const locals = (res as any)?.locals || (req as FastifyRequest).locals || {};\n const { roles, organization } = locals;\n\n let filters: ProjectFilters = {};\n let sortOptions: Record<string, 1 | -1> = { updatedAt: -1 };\n\n const {\n name,\n search,\n ids,\n organizationId,\n membersIds,\n sortBy,\n sortOrder,\n fetchAll,\n } = filtersRequest ?? {};\n\n if (ids) {\n filters = { ...filters, _id: { $in: ensureArrayQueryFilter(ids) } };\n }\n\n if (name) {\n filters = { ...filters, name: new RegExp(name, 'i') };\n }\n\n if (search) {\n const searchRegex = new RegExp(search, 'i');\n filters = {\n ...filters,\n $or: [{ name: searchRegex }],\n };\n }\n\n if (organizationId) {\n filters = { ...filters, organizationId };\n }\n\n if (membersIds) {\n filters = {\n ...filters,\n membersIds: { $in: ensureArrayQueryFilter(membersIds) },\n };\n }\n\n if (sortBy && sortOrder && (sortOrder === 'asc' || sortOrder === 'desc')) {\n sortOptions = { [sortBy]: sortOrder === 'asc' ? 1 : -1 };\n }\n\n if (!(roles.includes('admin') && fetchAll === 'true')) {\n filters = { ...filters, organizationId: organization?.id };\n }\n\n return { filters, sortOptions, ...pagination };\n};\n"],"mappings":";;;;;;;;;;AAgCA,MAAa,kCACX,KAKA,QACG;CACH,MAAM,EAAE,SAAS,gBAAgB,GAAG,eAClC,gCAAsD,IAAW;CAGnE,MAAM,EAAE,OAAO,iBADC,KAAa,UAAW,IAAuB,UAAU,EAAE;CAG3E,IAAIA,UAA0B,EAAE;CAChC,IAAIC,cAAsC,EAAE,WAAW,IAAI;CAE3D,MAAM,EACJ,MACA,QACA,KACA,gBACA,YACA,QACA,WACA,aACE,kBAAkB,EAAE;AAExB,KAAI,IACF,WAAU;EAAE,GAAG;EAAS,KAAK,EAAE,KAAK,uBAAuB,IAAI,EAAE;EAAE;AAGrE,KAAI,KACF,WAAU;EAAE,GAAG;EAAS,MAAM,IAAI,OAAO,MAAM,IAAI;EAAE;AAGvD,KAAI,QAAQ;EACV,MAAM,cAAc,IAAI,OAAO,QAAQ,IAAI;AAC3C,YAAU;GACR,GAAG;GACH,KAAK,CAAC,EAAE,MAAM,aAAa,CAAC;GAC7B;;AAGH,KAAI,eACF,WAAU;EAAE,GAAG;EAAS;EAAgB;AAG1C,KAAI,WACF,WAAU;EACR,GAAG;EACH,YAAY,EAAE,KAAK,uBAAuB,WAAW,EAAE;EACxD;AAGH,KAAI,UAAU,cAAc,cAAc,SAAS,cAAc,QAC/D,eAAc,GAAG,SAAS,cAAc,QAAQ,IAAI,IAAI;AAG1D,KAAI,EAAE,MAAM,SAAS,QAAQ,IAAI,aAAa,QAC5C,WAAU;EAAE,GAAG;EAAS,gBAAgB,cAAc;EAAI;AAG5D,QAAO;EAAE;EAAS;EAAa,GAAG;EAAY"}
|
|
@@ -4,12 +4,13 @@ import { getFiltersAndPaginationFromBody } from "./getFiltersAndPaginationFromBo
|
|
|
4
4
|
//#region src/utils/filtersAndPagination/getTagFiltersAndPagination.ts
|
|
5
5
|
/**
|
|
6
6
|
* Extracts filters and pagination information from the request body.
|
|
7
|
-
* @param req - Express request object.
|
|
7
|
+
* @param req - Express or Fastify request object.
|
|
8
|
+
* @param res - Express or Fastify response object (optional, for Express compatibility).
|
|
8
9
|
* @returns Object containing filters, page, pageSize, and getNumberOfPages functions.
|
|
9
10
|
*/
|
|
10
11
|
const getTagFiltersAndPagination = (req, res) => {
|
|
11
12
|
const { filters: filtersRequest, ...pagination } = getFiltersAndPaginationFromBody(req);
|
|
12
|
-
const { roles, organization } = res.locals;
|
|
13
|
+
const { roles, organization } = res?.locals || req.locals || {};
|
|
13
14
|
let filters = {};
|
|
14
15
|
const sortOptions = { updatedAt: -1 };
|
|
15
16
|
const { name, search, ids, keys, organizationId, fetchAll } = filtersRequest ?? {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getTagFiltersAndPagination.mjs","names":["filters: TagFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getTagFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Tag } from '@/types/tag.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type TagFiltersParams = {\n ids?: string | string[];\n keys?: string | string[];\n name?: string;\n search?: string;\n organizationId?: string;\n /**\n * For admin users, if true, will fetch all tags without filtering by organization\n */\n fetchAll?: 'true' | 'false';\n};\nexport type TagFilters = RootFilterQuery<Tag>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express request object.\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getTagFiltersAndPagination = (\n req
|
|
1
|
+
{"version":3,"file":"getTagFiltersAndPagination.mjs","names":["filters: TagFilters","sortOptions: Record<string, 1 | -1>"],"sources":["../../../../src/utils/filtersAndPagination/getTagFiltersAndPagination.ts"],"sourcesContent":["import type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { FastifyRequest } from 'fastify';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { Tag } from '@/types/tag.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type TagFiltersParams = {\n ids?: string | string[];\n keys?: string | string[];\n name?: string;\n search?: string;\n organizationId?: string;\n /**\n * For admin users, if true, will fetch all tags without filtering by organization\n */\n fetchAll?: 'true' | 'false';\n};\nexport type TagFilters = RootFilterQuery<Tag>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express or Fastify request object.\n * @param res - Express or Fastify response object (optional, for Express compatibility).\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getTagFiltersAndPagination = (\n req:\n | Request<FiltersAndPagination<TagFiltersParams>>\n | FastifyRequest<{ Querystring: FiltersAndPagination<TagFiltersParams> }>,\n res?: ResponseWithSession\n) => {\n const { filters: filtersRequest, ...pagination } =\n getFiltersAndPaginationFromBody<TagFiltersParams>(req as any);\n // Support both Express (res.locals) and Fastify (req.locals)\n const locals = (res as any)?.locals || (req as FastifyRequest).locals || {};\n const { roles, organization } = locals;\n\n let filters: TagFilters = {};\n const sortOptions: Record<string, 1 | -1> = { updatedAt: -1 };\n\n const { name, search, ids, keys, organizationId, fetchAll } =\n filtersRequest ?? {};\n\n if (ids) {\n filters = { ...filters, _id: { $in: ensureArrayQueryFilter(ids) } };\n }\n\n if (keys) {\n filters = { ...filters, key: { $in: ensureArrayQueryFilter(keys) } };\n }\n\n if (name) {\n filters = { ...filters, name: new RegExp(name, 'i') };\n }\n\n if (search) {\n const searchRegex = new RegExp(search, 'i');\n filters = {\n ...filters,\n $or: [\n { name: searchRegex },\n { key: searchRegex },\n { description: searchRegex },\n { instructions: searchRegex },\n ],\n };\n }\n\n if (organizationId) {\n filters = { ...filters, organizationId };\n }\n\n if (!(roles.includes('admin') && fetchAll === 'true')) {\n filters = { ...filters, organizationId: organization?.id };\n }\n\n return { filters, sortOptions, ...pagination };\n};\n"],"mappings":";;;;;;;;;;AA8BA,MAAa,8BACX,KAGA,QACG;CACH,MAAM,EAAE,SAAS,gBAAgB,GAAG,eAClC,gCAAkD,IAAW;CAG/D,MAAM,EAAE,OAAO,iBADC,KAAa,UAAW,IAAuB,UAAU,EAAE;CAG3E,IAAIA,UAAsB,EAAE;CAC5B,MAAMC,cAAsC,EAAE,WAAW,IAAI;CAE7D,MAAM,EAAE,MAAM,QAAQ,KAAK,MAAM,gBAAgB,aAC/C,kBAAkB,EAAE;AAEtB,KAAI,IACF,WAAU;EAAE,GAAG;EAAS,KAAK,EAAE,KAAK,uBAAuB,IAAI,EAAE;EAAE;AAGrE,KAAI,KACF,WAAU;EAAE,GAAG;EAAS,KAAK,EAAE,KAAK,uBAAuB,KAAK,EAAE;EAAE;AAGtE,KAAI,KACF,WAAU;EAAE,GAAG;EAAS,MAAM,IAAI,OAAO,MAAM,IAAI;EAAE;AAGvD,KAAI,QAAQ;EACV,MAAM,cAAc,IAAI,OAAO,QAAQ,IAAI;AAC3C,YAAU;GACR,GAAG;GACH,KAAK;IACH,EAAE,MAAM,aAAa;IACrB,EAAE,KAAK,aAAa;IACpB,EAAE,aAAa,aAAa;IAC5B,EAAE,cAAc,aAAa;IAC9B;GACF;;AAGH,KAAI,eACF,WAAU;EAAE,GAAG;EAAS;EAAgB;AAG1C,KAAI,EAAE,MAAM,SAAS,QAAQ,IAAI,aAAa,QAC5C,WAAU;EAAE,GAAG;EAAS,gBAAgB,cAAc;EAAI;AAG5D,QAAO;EAAE;EAAS;EAAa,GAAG;EAAY"}
|
|
@@ -4,12 +4,13 @@ import { getFiltersAndPaginationFromBody } from "./getFiltersAndPaginationFromBo
|
|
|
4
4
|
//#region src/utils/filtersAndPagination/getUserFiltersAndPagination.ts
|
|
5
5
|
/**
|
|
6
6
|
* Extracts filters and pagination information from the request body.
|
|
7
|
-
* @param req - Express request object.
|
|
7
|
+
* @param req - Express or Fastify request object.
|
|
8
|
+
* @param res - Express or Fastify response object (optional, for Express compatibility).
|
|
8
9
|
* @returns Object containing filters, page, pageSize, and getNumberOfPages functions.
|
|
9
10
|
*/
|
|
10
11
|
const getUserFiltersAndPagination = (req, res) => {
|
|
11
12
|
const { filters: filtersRequest, ...pagination } = getFiltersAndPaginationFromBody(req);
|
|
12
|
-
const { roles, organization } = res.locals;
|
|
13
|
+
const { roles, organization } = res?.locals || req.locals || {};
|
|
13
14
|
let filters = {};
|
|
14
15
|
let sortOptions = { updatedAt: -1 };
|
|
15
16
|
const { firstName, lastName, email, emailVerified, role, search, sortBy, sortOrder, ids, fetchAll } = filtersRequest ?? {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getUserFiltersAndPagination.mjs","names":["sortOptions: Record<string, 1 | -1>","secureMembersIds: string[]"],"sources":["../../../../src/utils/filtersAndPagination/getUserFiltersAndPagination.ts"],"sourcesContent":["import type { GetUsersResult } from '@controllers/user.controller';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { User } from '@/types/user.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type UserFiltersParam = {\n ids?: string | string[];\n firstName?: string;\n lastName?: string;\n email?: string;\n emailVerified?: string;\n role?: string;\n search?: string;\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users, if true, will fetch all users without filtering by organization\n */\n fetchAll?: 'true' | 'false';\n};\nexport type UserFilters = RootFilterQuery<User>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express request object.\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getUserFiltersAndPagination = (\n req
|
|
1
|
+
{"version":3,"file":"getUserFiltersAndPagination.mjs","names":["sortOptions: Record<string, 1 | -1>","secureMembersIds: string[]"],"sources":["../../../../src/utils/filtersAndPagination/getUserFiltersAndPagination.ts"],"sourcesContent":["import type { GetUsersResult } from '@controllers/user.controller';\nimport type { ResponseWithSession } from '@middlewares/sessionAuth.middleware';\nimport { ensureArrayQueryFilter } from '@utils/ensureArrayQueryFilter';\nimport type { Request } from 'express';\nimport type { FastifyRequest } from 'fastify';\nimport type { RootFilterQuery } from 'mongoose';\nimport type { User } from '@/types/user.types';\nimport {\n type FiltersAndPagination,\n getFiltersAndPaginationFromBody,\n} from './getFiltersAndPaginationFromBody';\n\nexport type UserFiltersParam = {\n ids?: string | string[];\n firstName?: string;\n lastName?: string;\n email?: string;\n emailVerified?: string;\n role?: string;\n search?: string;\n sortBy?: string;\n sortOrder?: string;\n /**\n * For admin users, if true, will fetch all users without filtering by organization\n */\n fetchAll?: 'true' | 'false';\n};\nexport type UserFilters = RootFilterQuery<User>;\n\n/**\n * Extracts filters and pagination information from the request body.\n * @param req - Express or Fastify request object.\n * @param res - Express or Fastify response object (optional, for Express compatibility).\n * @returns Object containing filters, page, pageSize, and getNumberOfPages functions.\n */\nexport const getUserFiltersAndPagination = (\n req:\n | Request<FiltersAndPagination<UserFiltersParam>>\n | FastifyRequest<{ Querystring: FiltersAndPagination<UserFiltersParam> }>,\n res?: ResponseWithSession<GetUsersResult>\n) => {\n const { filters: filtersRequest, ...pagination } =\n getFiltersAndPaginationFromBody<UserFiltersParam>(req as any);\n // Support both Express (res.locals) and Fastify (req.locals)\n const locals = (res as any)?.locals || (req as FastifyRequest).locals || {};\n const { roles, organization } = locals;\n\n let filters = {};\n let sortOptions: Record<string, 1 | -1> = { updatedAt: -1 };\n\n const {\n firstName,\n lastName,\n email,\n emailVerified,\n role,\n search,\n sortBy,\n sortOrder,\n ids,\n fetchAll,\n } = filtersRequest ?? {};\n\n if (ids) {\n filters = { ...filters, _id: { $in: ensureArrayQueryFilter(ids) } };\n\n if (!(roles.includes('admin') && fetchAll === 'true')) {\n const secureMembersIds: string[] =\n ensureArrayQueryFilter(ids)?.filter((id) =>\n organization?.membersIds?.map(String).includes(id)\n ) ?? [];\n\n filters = {\n ...filters,\n _id: {\n $in: secureMembersIds,\n },\n };\n }\n }\n\n if (firstName) {\n filters = { ...filters, firstName: new RegExp(firstName, 'i') };\n }\n\n if (lastName) {\n filters = { ...filters, lastName: new RegExp(lastName, 'i') };\n }\n\n if (email) {\n filters = { ...filters, email: new RegExp(email, 'i') };\n }\n\n if (emailVerified !== undefined) {\n const isVerified = emailVerified === 'true';\n filters = { ...filters, emailVerified: isVerified };\n }\n\n if (role) {\n filters = { ...filters, role };\n }\n\n if (search) {\n const searchRegex = new RegExp(search, 'i');\n filters = {\n ...filters,\n $or: [\n { firstName: searchRegex },\n { lastName: searchRegex },\n { email: searchRegex },\n { name: searchRegex },\n ],\n };\n }\n\n if (sortBy && sortOrder && (sortOrder === 'asc' || sortOrder === 'desc')) {\n sortOptions = { [sortBy]: sortOrder === 'asc' ? 1 : -1 };\n }\n\n return { filters, sortOptions, ...pagination };\n};\n"],"mappings":";;;;;;;;;;AAmCA,MAAa,+BACX,KAGA,QACG;CACH,MAAM,EAAE,SAAS,gBAAgB,GAAG,eAClC,gCAAkD,IAAW;CAG/D,MAAM,EAAE,OAAO,iBADC,KAAa,UAAW,IAAuB,UAAU,EAAE;CAG3E,IAAI,UAAU,EAAE;CAChB,IAAIA,cAAsC,EAAE,WAAW,IAAI;CAE3D,MAAM,EACJ,WACA,UACA,OACA,eACA,MACA,QACA,QACA,WACA,KACA,aACE,kBAAkB,EAAE;AAExB,KAAI,KAAK;AACP,YAAU;GAAE,GAAG;GAAS,KAAK,EAAE,KAAK,uBAAuB,IAAI,EAAE;GAAE;AAEnE,MAAI,EAAE,MAAM,SAAS,QAAQ,IAAI,aAAa,SAAS;GACrD,MAAMC,mBACJ,uBAAuB,IAAI,EAAE,QAAQ,OACnC,cAAc,YAAY,IAAI,OAAO,CAAC,SAAS,GAAG,CACnD,IAAI,EAAE;AAET,aAAU;IACR,GAAG;IACH,KAAK,EACH,KAAK,kBACN;IACF;;;AAIL,KAAI,UACF,WAAU;EAAE,GAAG;EAAS,WAAW,IAAI,OAAO,WAAW,IAAI;EAAE;AAGjE,KAAI,SACF,WAAU;EAAE,GAAG;EAAS,UAAU,IAAI,OAAO,UAAU,IAAI;EAAE;AAG/D,KAAI,MACF,WAAU;EAAE,GAAG;EAAS,OAAO,IAAI,OAAO,OAAO,IAAI;EAAE;AAGzD,KAAI,kBAAkB,QAAW;EAC/B,MAAM,aAAa,kBAAkB;AACrC,YAAU;GAAE,GAAG;GAAS,eAAe;GAAY;;AAGrD,KAAI,KACF,WAAU;EAAE,GAAG;EAAS;EAAM;AAGhC,KAAI,QAAQ;EACV,MAAM,cAAc,IAAI,OAAO,QAAQ,IAAI;AAC3C,YAAU;GACR,GAAG;GACH,KAAK;IACH,EAAE,WAAW,aAAa;IAC1B,EAAE,UAAU,aAAa;IACzB,EAAE,OAAO,aAAa;IACtB,EAAE,MAAM,aAAa;IACtB;GACF;;AAGH,KAAI,UAAU,cAAc,cAAc,SAAS,cAAc,QAC/D,eAAc,GAAG,SAAS,cAAc,QAAQ,IAAI,IAAI;AAG1D,QAAO;EAAE;EAAS;EAAa,GAAG;EAAY"}
|
|
@@ -2,6 +2,31 @@ import { ensureMongoDocumentToObject } from "../ensureMongoDocumentToObject.mjs"
|
|
|
2
2
|
|
|
3
3
|
//#region src/utils/mapper/project.ts
|
|
4
4
|
/**
|
|
5
|
+
* Sanitizes the AI configuration by removing the API key and adding a flag.
|
|
6
|
+
* @param aiConfig - The AI configuration to sanitize.
|
|
7
|
+
* @returns The sanitized AI configuration.
|
|
8
|
+
*/
|
|
9
|
+
const sanitizeAIConfig = (aiConfig) => {
|
|
10
|
+
if (!aiConfig) return aiConfig;
|
|
11
|
+
const { apiKey, ...rest } = aiConfig;
|
|
12
|
+
return {
|
|
13
|
+
...rest,
|
|
14
|
+
apiKeyConfigured: !!apiKey
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Sanitizes the project configuration by removing sensitive data.
|
|
19
|
+
* @param configuration - The project configuration to sanitize.
|
|
20
|
+
* @returns The sanitized project configuration.
|
|
21
|
+
*/
|
|
22
|
+
const sanitizeProjectConfiguration = (configuration) => {
|
|
23
|
+
if (!configuration) return configuration;
|
|
24
|
+
return {
|
|
25
|
+
...configuration,
|
|
26
|
+
ai: sanitizeAIConfig(configuration.ai)
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
5
30
|
* Maps a project to an API response.
|
|
6
31
|
* @param project - The project to map.
|
|
7
32
|
* @param - Whether the user is an admin of the project.
|
|
@@ -9,7 +34,9 @@ import { ensureMongoDocumentToObject } from "../ensureMongoDocumentToObject.mjs"
|
|
|
9
34
|
*/
|
|
10
35
|
const mapProjectToAPI = (project) => {
|
|
11
36
|
if (!project) return null;
|
|
12
|
-
|
|
37
|
+
const projectObject = ensureMongoDocumentToObject(project);
|
|
38
|
+
if (projectObject.configuration) projectObject.configuration = sanitizeProjectConfiguration(projectObject.configuration);
|
|
39
|
+
return projectObject;
|
|
13
40
|
};
|
|
14
41
|
/**
|
|
15
42
|
* Formats an array of projects for API response. Removes sensitive information.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.mjs","names":[],"sources":["../../../../src/utils/mapper/project.ts"],"sourcesContent":["import { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport type { Project, ProjectAPI } from '@/types/project.types';\n\n/**\n * Maps a project to an API response.\n * @param project - The project to map.\n * @param - Whether the user is an admin of the project.\n * @returns The project mapped to an API response.\n */\nexport const mapProjectToAPI = <T extends Project | ProjectAPI | null>(\n project?: T\n): T extends null ? null : ProjectAPI => {\n if (!project) {\n return null as any;\n }\n\n const projectObject = ensureMongoDocumentToObject(project);\n\n return projectObject as any;\n};\n\n/**\n * Formats an array of projects for API response. Removes sensitive information.\n * @param projects - The array of project objects to format.\n * @param user - The user object.\n * @param - Whether the user is an admin of the project.\n * @returns The formatted array of user objects.\n */\nexport const mapProjectsToAPI = (\n projects: (Project | ProjectAPI)[]\n): ProjectAPI[] =>\n projects.map(mapProjectToAPI).filter(Boolean) as ProjectAPI[];\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"project.mjs","names":[],"sources":["../../../../src/utils/mapper/project.ts"],"sourcesContent":["import { ensureMongoDocumentToObject } from '@utils/ensureMongoDocumentToObject';\nimport type { Project, ProjectAPI } from '@/types/project.types';\n\n/**\n * Sanitizes the AI configuration by removing the API key and adding a flag.\n * @param aiConfig - The AI configuration to sanitize.\n * @returns The sanitized AI configuration.\n */\nconst sanitizeAIConfig = (aiConfig?: any) => {\n if (!aiConfig) {\n return aiConfig;\n }\n\n const { apiKey, ...rest } = aiConfig;\n return {\n ...rest,\n apiKeyConfigured: !!apiKey,\n };\n};\n\n/**\n * Sanitizes the project configuration by removing sensitive data.\n * @param configuration - The project configuration to sanitize.\n * @returns The sanitized project configuration.\n */\nconst sanitizeProjectConfiguration = (\n configuration?: Project['configuration']\n) => {\n if (!configuration) {\n return configuration;\n }\n\n return {\n ...configuration,\n ai: sanitizeAIConfig(configuration.ai),\n };\n};\n\n/**\n * Maps a project to an API response.\n * @param project - The project to map.\n * @param - Whether the user is an admin of the project.\n * @returns The project mapped to an API response.\n */\nexport const mapProjectToAPI = <T extends Project | ProjectAPI | null>(\n project?: T\n): T extends null ? null : ProjectAPI => {\n if (!project) {\n return null as any;\n }\n\n const projectObject = ensureMongoDocumentToObject(project);\n\n // Sanitize configuration to remove sensitive API key\n if (projectObject.configuration) {\n projectObject.configuration = sanitizeProjectConfiguration(\n projectObject.configuration\n ) as any;\n }\n\n return projectObject as any;\n};\n\n/**\n * Formats an array of projects for API response. Removes sensitive information.\n * @param projects - The array of project objects to format.\n * @param user - The user object.\n * @param - Whether the user is an admin of the project.\n * @returns The formatted array of user objects.\n */\nexport const mapProjectsToAPI = (\n projects: (Project | ProjectAPI)[]\n): ProjectAPI[] =>\n projects.map(mapProjectToAPI).filter(Boolean) as ProjectAPI[];\n"],"mappings":";;;;;;;;AAQA,MAAM,oBAAoB,aAAmB;AAC3C,KAAI,CAAC,SACH,QAAO;CAGT,MAAM,EAAE,QAAQ,GAAG,SAAS;AAC5B,QAAO;EACL,GAAG;EACH,kBAAkB,CAAC,CAAC;EACrB;;;;;;;AAQH,MAAM,gCACJ,kBACG;AACH,KAAI,CAAC,cACH,QAAO;AAGT,QAAO;EACL,GAAG;EACH,IAAI,iBAAiB,cAAc,GAAG;EACvC;;;;;;;;AASH,MAAa,mBACX,YACuC;AACvC,KAAI,CAAC,QACH,QAAO;CAGT,MAAM,gBAAgB,4BAA4B,QAAQ;AAG1D,KAAI,cAAc,cAChB,eAAc,gBAAgB,6BAC5B,cAAc,cACf;AAGH,QAAO;;;;;;;;;AAUT,MAAa,oBACX,aAEA,SAAS,IAAI,gBAAgB,CAAC,OAAO,QAAQ"}
|
|
@@ -3,8 +3,8 @@ import { logger } from "../../logger/index.mjs";
|
|
|
3
3
|
import { OrganizationModel } from "../../models/organization.model.mjs";
|
|
4
4
|
import { ProjectModel } from "../../models/project.model.mjs";
|
|
5
5
|
import { TagModel } from "../../models/tag.model.mjs";
|
|
6
|
-
import { UserModel } from "../../models/user.model.mjs";
|
|
7
6
|
import { OAuth2AccessTokenModel } from "../../models/oAuth2.model.mjs";
|
|
7
|
+
import { UserModel } from "../../models/user.model.mjs";
|
|
8
8
|
import { connect } from "mongoose";
|
|
9
9
|
|
|
10
10
|
//#region src/utils/mongoDB/connectDB.ts
|
|
@@ -1,44 +1,54 @@
|
|
|
1
1
|
import { ErrorHandler } from "./errors/ErrorHandler.mjs";
|
|
2
|
-
import rateLimit, { ipKeyGenerator } from "express-rate-limit";
|
|
3
2
|
|
|
4
3
|
//#region src/utils/rateLimiter.ts
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
const normalizeIP = (ip) => {
|
|
5
|
+
if (!ip) return "unknown";
|
|
6
|
+
if (ip.startsWith("::ffff:")) return ip.substring(7);
|
|
7
|
+
return ip;
|
|
8
|
+
};
|
|
9
|
+
const ipLimiter = {
|
|
10
|
+
max: 500,
|
|
11
|
+
timeWindow: 60 * 1e3,
|
|
12
|
+
enableDraftSpec: true,
|
|
13
|
+
keyGenerator: (request) => {
|
|
14
|
+
const ip = request.ip ?? request.socket?.remoteAddress ?? request.headers["x-forwarded-for"] ?? "unknown";
|
|
15
|
+
return normalizeIP(typeof ip === "string" ? ip : ip[0]);
|
|
12
16
|
},
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
ErrorHandler.
|
|
16
|
-
limit: `${
|
|
17
|
-
retryAfter
|
|
18
|
-
remaining
|
|
17
|
+
errorResponseBuilder: (_request, context) => {
|
|
18
|
+
const retryAfter = Math.ceil(context.ttl / 1e3);
|
|
19
|
+
const errorResponse = ErrorHandler.formatGenericErrorResponse("RATE_LIMIT_EXCEEDED", {
|
|
20
|
+
limit: `${context.max} per minute`,
|
|
21
|
+
retryAfter
|
|
19
22
|
});
|
|
23
|
+
return {
|
|
24
|
+
statusCode: errorResponse.status,
|
|
25
|
+
...errorResponse
|
|
26
|
+
};
|
|
20
27
|
}
|
|
21
28
|
};
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
keyGenerator: (
|
|
30
|
-
|
|
29
|
+
const unauthenticatedChatBotLimiter = {
|
|
30
|
+
max: 3,
|
|
31
|
+
timeWindow: 3600 * 1e3,
|
|
32
|
+
enableDraftSpec: true,
|
|
33
|
+
allowList: (request) => {
|
|
34
|
+
return Boolean(request.locals?.user);
|
|
35
|
+
},
|
|
36
|
+
keyGenerator: (request) => {
|
|
37
|
+
const ip = request.ip ?? request.socket?.remoteAddress ?? request.headers["x-forwarded-for"] ?? "unknown";
|
|
38
|
+
return normalizeIP(typeof ip === "string" ? ip : ip[0]);
|
|
31
39
|
},
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
ErrorHandler.
|
|
35
|
-
limit: `${
|
|
36
|
-
retryAfter
|
|
37
|
-
remaining
|
|
40
|
+
errorResponseBuilder: (_request, context) => {
|
|
41
|
+
const retryAfter = Math.ceil(context.ttl / 1e3);
|
|
42
|
+
const errorResponse = ErrorHandler.formatGenericErrorResponse("RATE_LIMIT_EXCEEDED_UNAUTHENTICATED", {
|
|
43
|
+
limit: `${context.max} per hour`,
|
|
44
|
+
retryAfter
|
|
38
45
|
});
|
|
46
|
+
return {
|
|
47
|
+
statusCode: errorResponse.status,
|
|
48
|
+
...errorResponse
|
|
49
|
+
};
|
|
39
50
|
}
|
|
40
51
|
};
|
|
41
|
-
const unauthenticatedChatBotLimiter = rateLimit(unauthenticatedChatBotLimiterOptions);
|
|
42
52
|
|
|
43
53
|
//#endregion
|
|
44
54
|
export { ipLimiter, unauthenticatedChatBotLimiter };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimiter.mjs","names":["
|
|
1
|
+
{"version":3,"file":"rateLimiter.mjs","names":["ipLimiter: RateLimitOptions","unauthenticatedChatBotLimiter: RateLimitOptions"],"sources":["../../../src/utils/rateLimiter.ts"],"sourcesContent":["import type { RateLimitOptions } from '@fastify/rate-limit';\nimport type { FastifyRequest } from 'fastify';\nimport { ErrorHandler } from './errors';\n\n// Helper function to normalize IP addresses (similar to express-rate-limit's ipKeyGenerator)\nconst normalizeIP = (ip: string | undefined): string => {\n if (!ip) return 'unknown';\n // Normalize IPv6 mapped IPv4 addresses\n if (ip.startsWith('::ffff:')) {\n return ip.substring(7);\n }\n return ip;\n};\n\n// -------------------------------------------------------------\n// Create the rate-limiter instances once at module load-time so\n// that the hit counters are shared across every incoming request.\n// -------------------------------------------------------------\n\nexport const ipLimiter: RateLimitOptions = {\n max: 500, // 500 requests\n timeWindow: 60 * 1000, // 1-minute window\n enableDraftSpec: true,\n // Use a custom key generator that handles proxy headers securely\n keyGenerator: (request: FastifyRequest) => {\n const ip =\n request.ip ??\n request.socket?.remoteAddress ??\n request.headers['x-forwarded-for'] ??\n 'unknown';\n return normalizeIP(typeof ip === 'string' ? ip : ip[0]);\n },\n errorResponseBuilder: (_request: FastifyRequest, context) => {\n // context.ttl is already the remaining time in milliseconds\n const retryAfter = Math.ceil(context.ttl / 1000);\n const errorResponse = ErrorHandler.formatGenericErrorResponse(\n 'RATE_LIMIT_EXCEEDED',\n {\n limit: `${context.max} per minute`,\n retryAfter,\n }\n );\n return {\n statusCode: errorResponse.status,\n ...errorResponse,\n };\n },\n};\n\nexport const unauthenticatedChatBotLimiter: RateLimitOptions = {\n max: 3, // 3 requests\n timeWindow: 60 * 60 * 1000, // 1-hour window\n enableDraftSpec: true,\n // Skip rate limiting if user is authenticated (allowList returns true to skip)\n allowList: (request: FastifyRequest) => {\n return Boolean((request as any).locals?.user);\n },\n // Use a custom key generator that handles proxy headers securely\n keyGenerator: (request: FastifyRequest) => {\n const ip =\n request.ip ??\n request.socket?.remoteAddress ??\n request.headers['x-forwarded-for'] ??\n 'unknown';\n return normalizeIP(typeof ip === 'string' ? ip : ip[0]);\n },\n errorResponseBuilder: (_request: FastifyRequest, context) => {\n // context.ttl is already the remaining time in milliseconds\n const retryAfter = Math.ceil(context.ttl / 1000);\n const errorResponse = ErrorHandler.formatGenericErrorResponse(\n 'RATE_LIMIT_EXCEEDED_UNAUTHENTICATED',\n {\n limit: `${context.max} per hour`,\n retryAfter,\n }\n );\n return {\n statusCode: errorResponse.status,\n ...errorResponse,\n };\n },\n};\n"],"mappings":";;;AAKA,MAAM,eAAe,OAAmC;AACtD,KAAI,CAAC,GAAI,QAAO;AAEhB,KAAI,GAAG,WAAW,UAAU,CAC1B,QAAO,GAAG,UAAU,EAAE;AAExB,QAAO;;AAQT,MAAaA,YAA8B;CACzC,KAAK;CACL,YAAY,KAAK;CACjB,iBAAiB;CAEjB,eAAe,YAA4B;EACzC,MAAM,KACJ,QAAQ,MACR,QAAQ,QAAQ,iBAChB,QAAQ,QAAQ,sBAChB;AACF,SAAO,YAAY,OAAO,OAAO,WAAW,KAAK,GAAG,GAAG;;CAEzD,uBAAuB,UAA0B,YAAY;EAE3D,MAAM,aAAa,KAAK,KAAK,QAAQ,MAAM,IAAK;EAChD,MAAM,gBAAgB,aAAa,2BACjC,uBACA;GACE,OAAO,GAAG,QAAQ,IAAI;GACtB;GACD,CACF;AACD,SAAO;GACL,YAAY,cAAc;GAC1B,GAAG;GACJ;;CAEJ;AAED,MAAaC,gCAAkD;CAC7D,KAAK;CACL,YAAY,OAAU;CACtB,iBAAiB;CAEjB,YAAY,YAA4B;AACtC,SAAO,QAAS,QAAgB,QAAQ,KAAK;;CAG/C,eAAe,YAA4B;EACzC,MAAM,KACJ,QAAQ,MACR,QAAQ,QAAQ,iBAChB,QAAQ,QAAQ,sBAChB;AACF,SAAO,YAAY,OAAO,OAAO,WAAW,KAAK,GAAG,GAAG;;CAEzD,uBAAuB,UAA0B,YAAY;EAE3D,MAAM,aAAa,KAAK,KAAK,QAAQ,MAAM,IAAK;EAChD,MAAM,gBAAgB,aAAa,2BACjC,uCACA;GACE,OAAO,GAAG,QAAQ,IAAI;GACtB;GACD,CACF;AACD,SAAO;GACL,YAAY,cAAc;GAC1B,GAAG;GACJ;;CAEJ"}
|
|
@@ -44,7 +44,7 @@ const stripeWebhook = async (req, res) => {
|
|
|
44
44
|
to: user.email,
|
|
45
45
|
email: user.email,
|
|
46
46
|
subscriptionStartDate: (/* @__PURE__ */ new Date()).toLocaleDateString(),
|
|
47
|
-
manageSubscriptionLink: `${process.env.
|
|
47
|
+
manageSubscriptionLink: `${process.env.APP_URL}/organization`,
|
|
48
48
|
username: user.name,
|
|
49
49
|
organizationName: organization.name,
|
|
50
50
|
planName: organization.plan?.type ?? "Unknown"
|
|
@@ -54,7 +54,7 @@ const stripeWebhook = async (req, res) => {
|
|
|
54
54
|
to: user.email,
|
|
55
55
|
email: user.email,
|
|
56
56
|
cancellationDate: (/* @__PURE__ */ new Date(subscription.current_period_end * 1e3)).toLocaleDateString(),
|
|
57
|
-
reactivateLink: `${process.env.
|
|
57
|
+
reactivateLink: `${process.env.APP_URL}/pricing`,
|
|
58
58
|
username: user.name,
|
|
59
59
|
organizationName: organization.name,
|
|
60
60
|
planName: organization.plan?.type ?? "Unknown"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stripe.webhook.mjs","names":["event: Stripe.Event","emailService.sendEmail"],"sources":["../../../src/webhooks/stripe.webhook.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types';\nimport { logger } from '@logger';\nimport * as emailService from '@services/email.service';\nimport { getOrganizationById } from '@services/organization.service';\nimport {\n addOrUpdateSubscription,\n cancelSubscription,\n changeSubscriptionStatus,\n} from '@services/subscription.service';\nimport { getUserById } from '@services/user.service';\nimport { GenericError } from '@utils/errors';\nimport type { Request, Response } from 'express';\nimport { Stripe } from 'stripe';\nimport type { Plan } from '@/types/plan.types';\n\ntype SubscriptionMetadata = {\n locale: Locale; // Localization setting (e.g., 'en', 'fr', 'es')\n userId: string; // ID of the user associated with the subscription\n organizationId: string; // ID of the organization associated with the subscription\n};\n\n/**\n * Stripe webhook handler for processing subscription and invoice events.\n * @param req - Express request object.\n * @param res - Express response object.\n */\nexport const stripeWebhook = async (req: Request, res: Response) => {\n // Initialize the Stripe client with the secret key\n const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);\n\n const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET!; // Webhook secret for verifying event signatures\n const sig = req.headers['stripe-signature']!; // Retrieve the signature from the webhook request headers\n\n let event: Stripe.Event;\n\n // Verify the webhook signature to ensure the request is authentic\n try {\n event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);\n } catch (err) {\n // Respond with a 400 status code if the signature verification fails\n res.status(400).send(`Webhook Error: ${(err as Error).message}`);\n return;\n }\n\n // Utility function to extract metadata from a Stripe customer\n const extractMetadata = async (customerId: string) => {\n const customer = await stripe.customers.retrieve(customerId); // Retrieve customer details from Stripe\n return (customer as Stripe.Customer).metadata as SubscriptionMetadata; // Return the metadata object\n };\n\n // Handles subscription-related events (creation, update, deletion)\n const handleSubscriptionEvent = async (\n subscription: Stripe.Subscription,\n statusOverride?: Plan['status'] // Optionally override the subscription status\n ) => {\n const { id: subscriptionId, customer } = subscription;\n const priceId = subscription.items.data[0]?.price?.id; // Extract the price ID from subscription items\n\n if (!customer) {\n throw new GenericError('STRIPE_SUBSCRIPTION_NO_CUSTOMER');\n }\n\n const customerId = customer as string;\n const { locale, userId, organizationId } =\n await extractMetadata(customerId); // Extract metadata from the customer\n\n // Set localization in response locals if available\n if (locale) {\n res.locals.locales = locale;\n }\n\n const organization = await getOrganizationById(organizationId); // Fetch organization details by ID\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND');\n }\n\n const user = await getUserById(userId);\n\n if (!user) {\n throw new GenericError('USER_NOT_FOUND');\n }\n\n const status = statusOverride ?? subscription.status; // Use the provided status override or the subscription's status\n\n // Update or create a subscription record in the database\n await addOrUpdateSubscription(\n subscriptionId,\n priceId!,\n customerId,\n userId,\n organization,\n status\n );\n\n if (status === 'active') {\n await emailService.sendEmail({\n type: 'subscriptionPaymentSuccess',\n to: user.email,\n email: user.email,\n subscriptionStartDate: new Date().toLocaleDateString(),\n manageSubscriptionLink: `${process.env.CLIENT_URL}/dashboard/organization`,\n username: user.name,\n organizationName: organization.name,\n planName: organization.plan?.type ?? 'Unknown',\n });\n }\n if (status === 'canceled') {\n await emailService.sendEmail({\n type: 'subscriptionPaymentCancellation',\n to: user.email,\n email: user.email,\n cancellationDate: new Date(\n (subscription as any).current_period_end * 1000\n ).toLocaleDateString(),\n reactivateLink: `${process.env.CLIENT_URL}/pricing`,\n username: user.name,\n organizationName: organization.name,\n planName: organization.plan?.type ?? 'Unknown',\n });\n }\n };\n\n // Handles invoice-related events (payment success or failure)\n const handleInvoiceEvent = async (\n invoice: Stripe.Invoice,\n status: 'active' | 'incomplete'\n ) => {\n const subscriptionId =\n typeof (invoice as any).subscription === 'string'\n ? (invoice as any).subscription\n : (invoice as any).subscription?.id; // Extract the subscription ID from the invoice\n if (!subscriptionId) {\n logger.warn('Subscription ID is undefined in invoice.');\n return;\n }\n\n // Retrieve the subscription details from Stripe\n const subscription = await stripe.subscriptions.retrieve(subscriptionId);\n const organization = await getOrganizationById(\n subscription.metadata.organizationId\n );\n\n // Prevent duplicate subscriptions by canceling conflicting subscriptions\n if (\n organization.plan?.subscriptionId &&\n organization.plan.subscriptionId !== subscriptionId\n ) {\n await stripe.subscriptions.cancel(subscriptionId);\n }\n\n const customerId = invoice.customer as string;\n const { locale, userId, organizationId } =\n await extractMetadata(customerId);\n\n // Set localization in response locals if available\n if (locale) {\n res.locals.locales = locale;\n }\n\n // Update the subscription status in the database\n await changeSubscriptionStatus(\n subscriptionId,\n status,\n userId,\n organizationId\n );\n };\n\n try {\n // Log the event type for debugging and monitoring\n logger.info(`Triggered event type ${event.type}`);\n\n // Handle specific event types\n switch (event.type) {\n case 'customer.subscription.created': {\n logger.info(`Handled event type ${event.type}`);\n // Process a new subscription creation event\n await handleSubscriptionEvent(event.data.object as Stripe.Subscription);\n break;\n }\n case 'customer.subscription.updated': {\n logger.info(`Handled event type ${event.type}`);\n // Process a subscription update event\n await handleSubscriptionEvent(event.data.object as Stripe.Subscription);\n break;\n }\n case 'customer.subscription.deleted': {\n logger.info(`Handled event type ${event.type}`);\n const subscription = event.data\n .object as unknown as Stripe.Subscription;\n const customerId = subscription.customer as string;\n const { locale, organizationId } = await extractMetadata(customerId);\n\n // Set localization in response locals if available\n if (locale) {\n res.locals.locales = locale;\n }\n\n // Handle subscription deletion by canceling it in the database\n await cancelSubscription(subscription.id, organizationId);\n break;\n }\n case 'invoice.payment_succeeded': {\n logger.info(`Handled event type ${event.type}`);\n // Handle successful invoice payment\n await handleInvoiceEvent(event.data.object as Stripe.Invoice, 'active');\n break;\n }\n case 'invoice.payment_failed': {\n logger.info(`Handled event type ${event.type}`);\n // Handle failed invoice payment\n await handleInvoiceEvent(\n event.data.object as Stripe.Invoice,\n 'incomplete'\n );\n break;\n }\n default:\n // Log unhandled event types for visibility\n logger.info(`Unhandled event type ${event.type}`);\n }\n\n // Respond to Stripe to confirm the event was processed successfully\n res.send();\n } catch (error) {\n // Log errors for debugging and respond with a 500 status code\n logger.error(\n `Error handling event ${event.type}: ${(error as Error).message}`\n );\n res.status(500).send('Server Error');\n }\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"stripe.webhook.mjs","names":["event: Stripe.Event","emailService.sendEmail"],"sources":["../../../src/webhooks/stripe.webhook.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types';\nimport { logger } from '@logger';\nimport * as emailService from '@services/email.service';\nimport { getOrganizationById } from '@services/organization.service';\nimport {\n addOrUpdateSubscription,\n cancelSubscription,\n changeSubscriptionStatus,\n} from '@services/subscription.service';\nimport { getUserById } from '@services/user.service';\nimport { GenericError } from '@utils/errors';\nimport type { Request, Response } from 'express';\nimport { Stripe } from 'stripe';\nimport type { Plan } from '@/types/plan.types';\n\ntype SubscriptionMetadata = {\n locale: Locale; // Localization setting (e.g., 'en', 'fr', 'es')\n userId: string; // ID of the user associated with the subscription\n organizationId: string; // ID of the organization associated with the subscription\n};\n\n/**\n * Stripe webhook handler for processing subscription and invoice events.\n * @param req - Express request object.\n * @param res - Express response object.\n */\nexport const stripeWebhook = async (req: Request, res: Response) => {\n // Initialize the Stripe client with the secret key\n const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);\n\n const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET!; // Webhook secret for verifying event signatures\n const sig = req.headers['stripe-signature']!; // Retrieve the signature from the webhook request headers\n\n let event: Stripe.Event;\n\n // Verify the webhook signature to ensure the request is authentic\n try {\n event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);\n } catch (err) {\n // Respond with a 400 status code if the signature verification fails\n res.status(400).send(`Webhook Error: ${(err as Error).message}`);\n return;\n }\n\n // Utility function to extract metadata from a Stripe customer\n const extractMetadata = async (customerId: string) => {\n const customer = await stripe.customers.retrieve(customerId); // Retrieve customer details from Stripe\n return (customer as Stripe.Customer).metadata as SubscriptionMetadata; // Return the metadata object\n };\n\n // Handles subscription-related events (creation, update, deletion)\n const handleSubscriptionEvent = async (\n subscription: Stripe.Subscription,\n statusOverride?: Plan['status'] // Optionally override the subscription status\n ) => {\n const { id: subscriptionId, customer } = subscription;\n const priceId = subscription.items.data[0]?.price?.id; // Extract the price ID from subscription items\n\n if (!customer) {\n throw new GenericError('STRIPE_SUBSCRIPTION_NO_CUSTOMER');\n }\n\n const customerId = customer as string;\n const { locale, userId, organizationId } =\n await extractMetadata(customerId); // Extract metadata from the customer\n\n // Set localization in response locals if available\n if (locale) {\n res.locals.locales = locale;\n }\n\n const organization = await getOrganizationById(organizationId); // Fetch organization details by ID\n\n if (!organization) {\n throw new GenericError('ORGANIZATION_NOT_FOUND');\n }\n\n const user = await getUserById(userId);\n\n if (!user) {\n throw new GenericError('USER_NOT_FOUND');\n }\n\n const status = statusOverride ?? subscription.status; // Use the provided status override or the subscription's status\n\n // Update or create a subscription record in the database\n await addOrUpdateSubscription(\n subscriptionId,\n priceId!,\n customerId,\n userId,\n organization,\n status\n );\n\n if (status === 'active') {\n await emailService.sendEmail({\n type: 'subscriptionPaymentSuccess',\n to: user.email,\n email: user.email,\n subscriptionStartDate: new Date().toLocaleDateString(),\n manageSubscriptionLink: `${process.env.APP_URL}/organization`,\n username: user.name,\n organizationName: organization.name,\n planName: organization.plan?.type ?? 'Unknown',\n });\n }\n if (status === 'canceled') {\n await emailService.sendEmail({\n type: 'subscriptionPaymentCancellation',\n to: user.email,\n email: user.email,\n cancellationDate: new Date(\n (subscription as any).current_period_end * 1000\n ).toLocaleDateString(),\n reactivateLink: `${process.env.APP_URL}/pricing`,\n username: user.name,\n organizationName: organization.name,\n planName: organization.plan?.type ?? 'Unknown',\n });\n }\n };\n\n // Handles invoice-related events (payment success or failure)\n const handleInvoiceEvent = async (\n invoice: Stripe.Invoice,\n status: 'active' | 'incomplete'\n ) => {\n const subscriptionId =\n typeof (invoice as any).subscription === 'string'\n ? (invoice as any).subscription\n : (invoice as any).subscription?.id; // Extract the subscription ID from the invoice\n if (!subscriptionId) {\n logger.warn('Subscription ID is undefined in invoice.');\n return;\n }\n\n // Retrieve the subscription details from Stripe\n const subscription = await stripe.subscriptions.retrieve(subscriptionId);\n const organization = await getOrganizationById(\n subscription.metadata.organizationId\n );\n\n // Prevent duplicate subscriptions by canceling conflicting subscriptions\n if (\n organization.plan?.subscriptionId &&\n organization.plan.subscriptionId !== subscriptionId\n ) {\n await stripe.subscriptions.cancel(subscriptionId);\n }\n\n const customerId = invoice.customer as string;\n const { locale, userId, organizationId } =\n await extractMetadata(customerId);\n\n // Set localization in response locals if available\n if (locale) {\n res.locals.locales = locale;\n }\n\n // Update the subscription status in the database\n await changeSubscriptionStatus(\n subscriptionId,\n status,\n userId,\n organizationId\n );\n };\n\n try {\n // Log the event type for debugging and monitoring\n logger.info(`Triggered event type ${event.type}`);\n\n // Handle specific event types\n switch (event.type) {\n case 'customer.subscription.created': {\n logger.info(`Handled event type ${event.type}`);\n // Process a new subscription creation event\n await handleSubscriptionEvent(event.data.object as Stripe.Subscription);\n break;\n }\n case 'customer.subscription.updated': {\n logger.info(`Handled event type ${event.type}`);\n // Process a subscription update event\n await handleSubscriptionEvent(event.data.object as Stripe.Subscription);\n break;\n }\n case 'customer.subscription.deleted': {\n logger.info(`Handled event type ${event.type}`);\n const subscription = event.data\n .object as unknown as Stripe.Subscription;\n const customerId = subscription.customer as string;\n const { locale, organizationId } = await extractMetadata(customerId);\n\n // Set localization in response locals if available\n if (locale) {\n res.locals.locales = locale;\n }\n\n // Handle subscription deletion by canceling it in the database\n await cancelSubscription(subscription.id, organizationId);\n break;\n }\n case 'invoice.payment_succeeded': {\n logger.info(`Handled event type ${event.type}`);\n // Handle successful invoice payment\n await handleInvoiceEvent(event.data.object as Stripe.Invoice, 'active');\n break;\n }\n case 'invoice.payment_failed': {\n logger.info(`Handled event type ${event.type}`);\n // Handle failed invoice payment\n await handleInvoiceEvent(\n event.data.object as Stripe.Invoice,\n 'incomplete'\n );\n break;\n }\n default:\n // Log unhandled event types for visibility\n logger.info(`Unhandled event type ${event.type}`);\n }\n\n // Respond to Stripe to confirm the event was processed successfully\n res.send();\n } catch (error) {\n // Log errors for debugging and respond with a 500 status code\n logger.error(\n `Error handling event ${event.type}: ${(error as Error).message}`\n );\n res.status(500).send('Server Error');\n }\n};\n"],"mappings":"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"}
|