From 2fb0c597032d9517138954af4c73155883a8b30e Mon Sep 17 00:00:00 2001 From: bohdanshcherba <73034554+bohdanshcherba@users.noreply.github.com> Date: Mon, 28 Mar 2022 22:56:21 +0300 Subject: [PATCH 1/4] bws-139: + added validation for uuid --- backend/src/api/slc/slc.api.ts | 17 ++++++++++++++++- backend/src/validation-schemas/uuid/uuid.ts | 1 + .../validation-schemas/validation-schemas.ts | 1 + shared/src/common/enums/enums.ts | 2 ++ .../uuid/uuid-validation-massage.ts | 6 ++++++ .../uuid/uuid-validation-rule.enum.ts | 6 ++++++ .../src/common/enums/validation/uuid/uuid.ts | 2 ++ .../src/common/enums/validation/validation.ts | 1 + .../validation/uuid/uuid.validation-shema.ts | 19 +++++++++++++++++++ shared/src/validation/validation-schemas.ts | 1 + 10 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 backend/src/validation-schemas/uuid/uuid.ts create mode 100644 shared/src/common/enums/validation/uuid/uuid-validation-massage.ts create mode 100644 shared/src/common/enums/validation/uuid/uuid-validation-rule.enum.ts create mode 100644 shared/src/common/enums/validation/uuid/uuid.ts create mode 100644 shared/src/validation/uuid/uuid.validation-shema.ts diff --git a/backend/src/api/slc/slc.api.ts b/backend/src/api/slc/slc.api.ts index 83a265784..bcef12093 100644 --- a/backend/src/api/slc/slc.api.ts +++ b/backend/src/api/slc/slc.api.ts @@ -4,7 +4,10 @@ import { slcFunction as slcFunctionServ, token as tokenServ, } from '~/services/services'; -import { slcFunctionCreate as slcFunctionCreateValidationSchema } from '~/validation-schemas/validation-schemas'; +import { + slcFunctionCreate as slcFunctionCreateValidationSchema, + UUID as UUIDValidationSchema, +} from '~/validation-schemas/validation-schemas'; import { HttpCode, HttpMethod, @@ -124,6 +127,18 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { method: HttpMethod.GET, url: `${SLCApiPath.SLC_FUNCTIONS}${SLCFunctionApiPath.$ID}`, preHandler: checkHasPermissionsHook(Permission.MANAGE_SLC), + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: SLCFunctionLoadParamsDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Params: SLCFunctionLoadParamsDto; diff --git a/backend/src/validation-schemas/uuid/uuid.ts b/backend/src/validation-schemas/uuid/uuid.ts new file mode 100644 index 000000000..b5b17fd42 --- /dev/null +++ b/backend/src/validation-schemas/uuid/uuid.ts @@ -0,0 +1 @@ +export { UUID } from 'bws-shared/validation/validation-schemas'; diff --git a/backend/src/validation-schemas/validation-schemas.ts b/backend/src/validation-schemas/validation-schemas.ts index 42d6e9a56..11a889aa3 100644 --- a/backend/src/validation-schemas/validation-schemas.ts +++ b/backend/src/validation-schemas/validation-schemas.ts @@ -12,3 +12,4 @@ export { bsSpaceCreate } from './bs-space/bs-space'; export { scInstanceCreate, scInstanceUpdate } from './sc-instance/sc-instance'; export { slcFunctionCreate } from './slc-function/slc-function'; export { eamTenantUpdate } from './eam-tenant-update/eam-tenant-update'; +export { UUID } from './uuid/uuid'; diff --git a/shared/src/common/enums/enums.ts b/shared/src/common/enums/enums.ts index 9e494d600..45642bdd3 100644 --- a/shared/src/common/enums/enums.ts +++ b/shared/src/common/enums/enums.ts @@ -33,6 +33,8 @@ export { SCInstanceValidationMessage, SLCFunctionValidationMessage, SLCFunctionValidationRule, + UUIDValidationRule, + UUIDValidationMessage, } from './validation/validation'; export { Permission } from './permissions/permissions'; export { UserRole } from './roles/roles'; diff --git a/shared/src/common/enums/validation/uuid/uuid-validation-massage.ts b/shared/src/common/enums/validation/uuid/uuid-validation-massage.ts new file mode 100644 index 000000000..c8c8972b6 --- /dev/null +++ b/shared/src/common/enums/validation/uuid/uuid-validation-massage.ts @@ -0,0 +1,6 @@ +const UUIDValidationMessage = { + UUID_PATTERN: 'uuid is invalid', + UUID_REQUIRE: 'uuid is required', +} as const; + +export { UUIDValidationMessage }; diff --git a/shared/src/common/enums/validation/uuid/uuid-validation-rule.enum.ts b/shared/src/common/enums/validation/uuid/uuid-validation-rule.enum.ts new file mode 100644 index 000000000..f2188df5c --- /dev/null +++ b/shared/src/common/enums/validation/uuid/uuid-validation-rule.enum.ts @@ -0,0 +1,6 @@ +const UUIDValidationRule = { + UUID_PATTERN: + /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/, +} as const; + +export { UUIDValidationRule }; diff --git a/shared/src/common/enums/validation/uuid/uuid.ts b/shared/src/common/enums/validation/uuid/uuid.ts new file mode 100644 index 000000000..9ddadc117 --- /dev/null +++ b/shared/src/common/enums/validation/uuid/uuid.ts @@ -0,0 +1,2 @@ +export { UUIDValidationMessage } from './uuid-validation-massage'; +export { UUIDValidationRule } from './uuid-validation-rule.enum'; diff --git a/shared/src/common/enums/validation/validation.ts b/shared/src/common/enums/validation/validation.ts index f66548351..bf63ebe4a 100644 --- a/shared/src/common/enums/validation/validation.ts +++ b/shared/src/common/enums/validation/validation.ts @@ -26,3 +26,4 @@ export { SLCFunctionValidationMessage, SLCFunctionValidationRule, } from './slc-function/slc-function'; +export { UUIDValidationMessage, UUIDValidationRule } from './uuid/uuid'; diff --git a/shared/src/validation/uuid/uuid.validation-shema.ts b/shared/src/validation/uuid/uuid.validation-shema.ts new file mode 100644 index 000000000..584166f99 --- /dev/null +++ b/shared/src/validation/uuid/uuid.validation-shema.ts @@ -0,0 +1,19 @@ +import * as Joi from 'joi'; +import { getNameOf } from '~/helpers/helpers'; +import { SLCFunctionLoadParamsDto } from '~/common/types/types'; +import { + UUIDValidationRule, + UUIDValidationMessage, +} from '~/common/enums/enums'; + +const UUID = Joi.object({ + [getNameOf('id')]: Joi.string() + .required() + .regex(UUIDValidationRule.UUID_PATTERN) + .messages({ + 'string.empty': UUIDValidationMessage.UUID_REQUIRE, + 'string.pattern.base': UUIDValidationMessage.UUID_PATTERN, + }), +}); + +export { UUID }; diff --git a/shared/src/validation/validation-schemas.ts b/shared/src/validation/validation-schemas.ts index 63111729b..d0957d0d0 100644 --- a/shared/src/validation/validation-schemas.ts +++ b/shared/src/validation/validation-schemas.ts @@ -13,3 +13,4 @@ export { bsSpaceCreate } from './bs-space/bs-space'; export { scInstanceCreate, scInstanceUpdate } from './sc-instance/sc-instance'; export { slcFunctionCreate } from './slc-function/slc-function'; export { eamTenantUpdate } from './eam-tenant-update/eam-tenant-update.validation-schema'; +export { UUID } from './uuid/uuid.validation-shema'; From 7223640f5cb2dc2b8e26cd6dc1a04e8e9c3dd6ec Mon Sep 17 00:00:00 2001 From: bohdanshcherba <73034554+bohdanshcherba@users.noreply.github.com> Date: Tue, 29 Mar 2022 17:38:45 +0300 Subject: [PATCH 2/4] bws-139: + added validation for uuid --- backend/src/api/bs/bs.api.ts | 17 ++++++++++++++- backend/src/api/eam/eam.api.ts | 38 ++++++++++++++++++++++++++++++++++ backend/src/api/sc/sc.api.ts | 26 +++++++++++++++++++++++ backend/src/api/slc/slc.api.ts | 36 ++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) diff --git a/backend/src/api/bs/bs.api.ts b/backend/src/api/bs/bs.api.ts index 3d4db48a4..71a89d309 100644 --- a/backend/src/api/bs/bs.api.ts +++ b/backend/src/api/bs/bs.api.ts @@ -24,7 +24,10 @@ import { BSObjectGetRequestParamsDto, } from '~/common/types/types'; import { FastifyRouteSchemaDef } from 'fastify/types/schema'; -import { bsSpaceCreate as bsSpaceCreateValidationSchema } from '~/validation-schemas/validation-schemas'; +import { + bsSpaceCreate as bsSpaceCreateValidationSchema, + UUID as UUIDValidationSchema, +} from '~/validation-schemas/validation-schemas'; import { upload as uploadHook, checkHasPermissions as checkHasPermissionsHook, @@ -98,6 +101,18 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { method: HttpMethod.DELETE, url: `${BSApiPath.SPACES}${SpacesApiPath.$ID}`, preHandler: checkHasPermissionsHook(Permission.MANAGE_BS), + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: BSSpaceDeleteParamsDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Params: BSSpaceDeleteParamsDto }>, rep: FastifyReply, diff --git a/backend/src/api/eam/eam.api.ts b/backend/src/api/eam/eam.api.ts index 4de3645c4..678b98789 100644 --- a/backend/src/api/eam/eam.api.ts +++ b/backend/src/api/eam/eam.api.ts @@ -29,6 +29,7 @@ import { eamGroupCreate as groupCreateValidationSchema, eamWorkerCreateBackend as workerValidationSchema, eamGroupUpdate as groupUpdateValidationSchema, + UUID as UUIDValidationSchema, } from '~/validation-schemas/validation-schemas'; import { checkHasPermissions as checkHasPermissionsHook } from '~/hooks/hooks'; import { EAMError } from '~/exceptions/exceptions'; @@ -111,6 +112,7 @@ const initEamApi: FastifyPluginAsync = async (fastify, opts) => { url: `${EAMApiPath.GROUPS}${GroupsApiPath.$ID}`, schema: { body: groupUpdateValidationSchema, + params: UUIDValidationSchema, }, validatorCompiler({ schema, @@ -138,6 +140,18 @@ const initEamApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.GET, url: `${EAMApiPath.GROUPS}${GroupsApiPath.$ID}`, + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: EamGroupGetByIdRequestDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Querystring: EamGroupGetByIdRequestDto; @@ -191,6 +205,18 @@ const initEamApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.DELETE, url: `${EAMApiPath.GROUPS}${GroupsApiPath.$ID}`, + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: EAMGroupDeleteParamsDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Params: EAMGroupDeleteParamsDto }>, rep, @@ -206,6 +232,18 @@ const initEamApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.DELETE, url: `${EAMApiPath.WORKERS}${WorkersApiPath.$ID}`, + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: EAMWorkerDeleteRequestDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Params: EAMWorkerDeleteRequestDto }>, rep, diff --git a/backend/src/api/sc/sc.api.ts b/backend/src/api/sc/sc.api.ts index 9f9ae7279..de601cee6 100644 --- a/backend/src/api/sc/sc.api.ts +++ b/backend/src/api/sc/sc.api.ts @@ -24,6 +24,7 @@ import { FastifyRouteSchemaDef } from 'fastify/types/schema'; import { scInstanceCreate as scInstanceCreateValidationSchema, scInstanceUpdate as scInstanceUpdateValidationSchema, + UUID as UUIDValidationSchema, } from '~/validation-schemas/validation-schemas'; import { checkHasPermissions as checkHasPermissionsHook } from '~/hooks/hooks'; @@ -56,6 +57,18 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { method: HttpMethod.GET, url: `${SCApiPath.SSH_KEYS}${SshKeysApiPath.$ID}`, preHandler: checkHasPermissionsHook(Permission.MANAGE_SC), + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: SCSshKeyGetByIdParamsDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Params: SCSshKeyGetByIdParamsDto; @@ -89,6 +102,18 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { method: HttpMethod.DELETE, url: `${SCApiPath.INSTANCES}${InstancesApiPath.$ID}`, preHandler: checkHasPermissionsHook(Permission.MANAGE_SC), + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: SCInstanceDeleteParamsDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Params: SCInstanceDeleteParamsDto; @@ -106,6 +131,7 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { preHandler: checkHasPermissionsHook(Permission.MANAGE_SC), schema: { body: scInstanceUpdateValidationSchema, + params: UUIDValidationSchema, }, validatorCompiler({ schema, diff --git a/backend/src/api/slc/slc.api.ts b/backend/src/api/slc/slc.api.ts index bcef12093..a2aa22671 100644 --- a/backend/src/api/slc/slc.api.ts +++ b/backend/src/api/slc/slc.api.ts @@ -77,6 +77,18 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { method: HttpMethod.POST, url: `${SLCApiPath.SLC_FUNCTIONS}${SLCFunctionApiPath.$ID}`, preHandler: checkHasPermissionsHook(Permission.MANAGE_SLC), + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: SLCFunctionLoadParamsDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Params: SLCFunctionRunParamsDto; @@ -159,6 +171,18 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { method: HttpMethod.DELETE, url: `${SLCApiPath.SLC_FUNCTIONS}${SLCFunctionApiPath.$ID}`, preHandler: checkHasPermissionsHook(Permission.MANAGE_SLC), + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: SLCFunctionLoadParamsDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Params: SLCFunctionDeleteParamsDto }>, rep: FastifyReply, @@ -185,6 +209,18 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { method: HttpMethod.PUT, url: `${SLCApiPath.SLC_FUNCTIONS}${SLCFunctionApiPath.$ID}`, preHandler: checkHasPermissionsHook(Permission.MANAGE_SLC), + schema: { + params: UUIDValidationSchema, + }, + validatorCompiler({ + schema, + }: FastifyRouteSchemaDef) { + return ( + data: SLCFunctionLoadParamsDto, + ): ReturnType => { + return schema.validate(data); + }; + }, async handler( req: FastifyRequest<{ Params: SLCFunctionUpdateParamsDto; From 5c32a5f47fba4e016345328f5eb57bd20b61af8f Mon Sep 17 00:00:00 2001 From: bohdanshcherba <73034554+bohdanshcherba@users.noreply.github.com> Date: Tue, 29 Mar 2022 19:42:33 +0300 Subject: [PATCH 3/4] bws-139: + some fix --- shared/src/validation/uuid/uuid.validation-shema.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/shared/src/validation/uuid/uuid.validation-shema.ts b/shared/src/validation/uuid/uuid.validation-shema.ts index 584166f99..7b19f7838 100644 --- a/shared/src/validation/uuid/uuid.validation-shema.ts +++ b/shared/src/validation/uuid/uuid.validation-shema.ts @@ -1,13 +1,12 @@ import * as Joi from 'joi'; -import { getNameOf } from '~/helpers/helpers'; -import { SLCFunctionLoadParamsDto } from '~/common/types/types'; + import { UUIDValidationRule, UUIDValidationMessage, } from '~/common/enums/enums'; const UUID = Joi.object({ - [getNameOf('id')]: Joi.string() + ['id']: Joi.string() .required() .regex(UUIDValidationRule.UUID_PATTERN) .messages({ From a68dd653a280a52c2f1ccad35b0fb20509879696 Mon Sep 17 00:00:00 2001 From: bohdanshcherba <73034554+bohdanshcherba@users.noreply.github.com> Date: Thu, 31 Mar 2022 15:51:20 +0300 Subject: [PATCH 4/4] bws-139: * resolved conflict --- backend/src/api/api.ts | 1 - backend/src/api/auth/auth.api.ts | 16 +-- backend/src/api/bs/bs.api.ts | 63 +++++------ backend/src/api/eam/eam.api.ts | 25 ++-- backend/src/api/sc/sc.api.ts | 31 +++-- backend/src/api/slc/slc.api.ts | 68 +++++------ backend/src/api/tenants/tenants.api.ts | 6 +- .../enums/exception/exception-message.enum.ts | 19 +--- .../repositories/worker/worker.repository.ts | 2 +- backend/src/exceptions/bs-error/bs-error.ts | 2 +- backend/src/exceptions/eam-error/eam-error.ts | 2 +- .../check-has-permission.hook.ts | 6 +- .../check-has-role/check-has-role.hook.ts | 31 +++++ backend/src/hooks/hooks.ts | 1 + backend/src/index.d.ts | 2 +- .../authorization/authorization.plugin.ts | 2 +- .../services/bs-object/bs-object.service.ts | 16 +-- .../src/services/instance/instance.service.ts | 12 +- .../src/services/key-pair/key-pair.service.ts | 4 +- backend/src/services/services.ts | 2 - .../slc-function/slc-function.service.ts | 21 +--- backend/src/services/space/space.service.ts | 9 +- backend/src/services/worker/worker.service.ts | 23 +--- .../common/enums/entity/entity-type.enum.ts | 10 ++ frontend/src/common/enums/entity/entity.ts | 1 + frontend/src/common/enums/enums.ts | 1 + .../types/app/use-pagination-hook.type.ts | 1 + frontend/src/components/bs-space/bs-space.tsx | 86 +++++--------- .../objects-table/objects-table.tsx | 53 ++++++++- .../objects-table/styles.module.scss | 36 ++++++ .../components/bs-space/styles.module.scss | 37 ------ frontend/src/components/bs/bs.tsx | 61 +++++----- .../components/spaces-table/spaces-table.tsx | 51 ++++++--- .../spaces-table/styles.module.scss | 10 ++ frontend/src/components/bs/styles.module.scss | 11 -- frontend/src/components/common/common.ts | 1 + .../confirm-delete-popup.tsx | 44 +++++++ .../confirm-delete-popup/styles.module.scss | 28 +++++ .../workers-table/workers-table.tsx | 45 +++++++- .../eam-configurate-group.tsx | 4 +- .../eam-configurate-worker.tsx | 36 +++++- .../components/groups-table/groups-table.tsx | 32 +++++- .../groups-table/styles.module.scss | 11 ++ .../workers-table/styles.module.scss | 11 ++ .../workers-table/workers-table.tsx | 31 ++++- frontend/src/components/eam/eam.tsx | 107 +++++++----------- .../src/components/eam/styles.module.scss | 12 -- .../instances-table/instance-table.tsx | 35 ++++-- .../instances-table/styles.module.scss | 10 ++ frontend/src/components/sc/sc.tsx | 73 ++++++------ frontend/src/components/sc/styles.module.scss | 11 -- .../functions-table/functions-table.tsx | 31 ++++- .../helpers/get-columns.helper.ts | 2 +- .../functions-table/styles.module.scss | 10 ++ frontend/src/components/slc/slc.tsx | 64 +++++------ .../src/components/slc/styles.module.scss | 11 -- .../use-pagination/use-pagination.hook.ts | 25 +++- frontend/src/store/bs/reducer.ts | 2 + .../store/eam-group-configurate/reducer.ts | 4 + .../store/eam-worker-configurate/reducer.ts | 3 + frontend/src/store/eam/reducer.ts | 3 +- frontend/src/store/sc/reducer.ts | 2 + frontend/src/store/slc/reducer.ts | 2 + .../src/common/enums/http/http-code.enum.ts | 1 + 64 files changed, 797 insertions(+), 576 deletions(-) create mode 100644 backend/src/hooks/check-has-role/check-has-role.hook.ts create mode 100644 frontend/src/common/enums/entity/entity-type.enum.ts create mode 100644 frontend/src/common/enums/entity/entity.ts create mode 100644 frontend/src/components/bs-space/components/objects-table/styles.module.scss create mode 100644 frontend/src/components/bs/components/spaces-table/styles.module.scss create mode 100644 frontend/src/components/common/confirm-delete-popup/confirm-delete-popup.tsx create mode 100644 frontend/src/components/common/confirm-delete-popup/styles.module.scss create mode 100644 frontend/src/components/eam/components/groups-table/styles.module.scss create mode 100644 frontend/src/components/eam/components/workers-table/styles.module.scss create mode 100644 frontend/src/components/sc/components/instances-table/styles.module.scss create mode 100644 frontend/src/components/slc/components/functions-table/styles.module.scss diff --git a/backend/src/api/api.ts b/backend/src/api/api.ts index 82e98fda4..8f97efd4e 100644 --- a/backend/src/api/api.ts +++ b/backend/src/api/api.ts @@ -76,7 +76,6 @@ const initApi: FastifyPluginAsync = async (fastify) => { fastify.register(initSLCApi, { services: { slcFunction, - token, }, prefix: ApiPath.SLC, }); diff --git a/backend/src/api/auth/auth.api.ts b/backend/src/api/auth/auth.api.ts index cd55b98a9..51830c7d5 100644 --- a/backend/src/api/auth/auth.api.ts +++ b/backend/src/api/auth/auth.api.ts @@ -6,18 +6,12 @@ import { eamMasterSignIn as masterSignInValidationSchema, eamWorkerSignIn as workerSignInValidationSchema, } from '~/validation-schemas/validation-schemas'; -import { - HttpCode, - HttpMethod, - AuthApiPath, - ExceptionMessage, -} from '~/common/enums/enums'; +import { HttpCode, HttpMethod, AuthApiPath } from '~/common/enums/enums'; import { EAMMasterSignUpRequestDto, EAMMasterSignInRequestDto, EAMWorkerSignInRequestDto, } from '~/common/types/types'; -import { InvalidCredentialsError } from '~/exceptions/exceptions'; type Options = { services: { master: typeof masterServ; auth: typeof authServ }; @@ -31,13 +25,7 @@ const initAuthApi: FastifyPluginAsync = async (fastify, opts) => { url: AuthApiPath.ROOT, async handler(req, rep) { const [, token] = req.headers?.authorization?.split(' ') ?? []; - - const user = await authService.getCurrentUser(token).catch(() => { - throw new InvalidCredentialsError({ - status: HttpCode.BAD_REQUEST, - message: ExceptionMessage.INVALID_TOKEN, - }); - }); + const user = await authService.getCurrentUser(token); return rep.send(user).status(HttpCode.OK); }, diff --git a/backend/src/api/bs/bs.api.ts b/backend/src/api/bs/bs.api.ts index 049dfa4a3..913f5afe6 100644 --- a/backend/src/api/bs/bs.api.ts +++ b/backend/src/api/bs/bs.api.ts @@ -11,7 +11,6 @@ import { SpacesApiPath, Permission, UserRole, - ExceptionMessage, FormDataCommonKey, } from '~/common/enums/enums'; import { @@ -20,9 +19,9 @@ import { BSSpaceGetRequestParamsDto, BSObjectDownloadParamsDto, BSObjectUploadParamsDto, - TokenPayload, BSObjectGetRequestParamsDto, BSObjectDeleteParamsDto, + TokenPayload, } from '~/common/types/types'; import { FastifyRouteSchemaDef } from 'fastify/types/schema'; import { @@ -32,8 +31,8 @@ import { import { upload as uploadHook, checkHasPermissions as checkHasPermissionsHook, + checkHasRole as checkHasRoleHook, } from '~/hooks/hooks'; -import { BsError } from '~/exceptions/exceptions'; type Options = { services: { @@ -53,7 +52,10 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.POST, url: BSApiPath.SPACES, - preHandler: checkHasPermissionsHook(Permission.MANAGE_BS), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_BS), + checkHasRoleHook(UserRole.WORKER), + ], schema: { body: bsSpaceCreateValidationSchema, }, @@ -74,7 +76,7 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { .send( await spaceService.create({ name: req.body.name, - token: req.user?.token as string, + token: req.userData?.token as string, }), ) .status(HttpCode.CREATED); @@ -91,7 +93,7 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { ) { const spaces = await spaceService.getSpacesByTenant({ query: req.query, - token: req.user?.token as string, + token: req.userData?.token as string, }); return rep.send(spaces).status(HttpCode.OK); @@ -101,7 +103,10 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.DELETE, url: `${BSApiPath.SPACES}${SpacesApiPath.$ID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_BS), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_BS), + checkHasRoleHook(UserRole.WORKER), + ], schema: { params: UUIDValidationSchema, }, @@ -114,22 +119,12 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { return schema.validate(data); }; }, + async handler( req: FastifyRequest<{ Params: BSSpaceDeleteParamsDto }>, rep: FastifyReply, ) { - const { id } = req.params; - const token = req.user?.token as string; - const { userRole } = tokenService.decode(token); - - if (userRole !== UserRole.WORKER) { - throw new BsError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_SPACE_DELETE, - }); - } - - await spaceService.delete(id); + await spaceService.delete(req.params.id); return rep.send(true).status(HttpCode.OK); }, @@ -144,6 +139,7 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { preHandler: [ uploadHook.single(FormDataCommonKey.FILE), checkHasPermissionsHook(Permission.MANAGE_BS), + checkHasRoleHook(UserRole.WORKER), ], async onError(req, rep, err) { if (err) { @@ -152,12 +148,12 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { }, async handler( req: FastifyRequest<{ Params: BSObjectUploadParamsDto }>, - rep, + rep: FastifyReply, ) { const { id } = req.params; await bsObjectService.upload({ - token: req.user?.token as string, + token: req.userData?.token as string, file: req.file, id, }); @@ -171,12 +167,15 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { }>({ method: HttpMethod.GET, url: `${BSApiPath.SPACES}${SpacesApiPath.$SPACEID_OBJECTS_$OBJECTID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_BS), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_BS), + checkHasRoleHook(UserRole.WORKER), + ], async handler(req, rep) { const { spaceId, objectId } = req.params; const object = await bsObjectService.download({ - token: req.user?.token as string, + token: req.userData?.token as string, spaceId, objectId, }); @@ -200,7 +199,7 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { spaceId: req.params.id, from: req.query.from, count: req.query.count, - token: req.user?.token as string, + token: req.userData?.token as string, }); return rep.send(objects).status(HttpCode.OK); @@ -210,21 +209,17 @@ const initBsApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.DELETE, url: `${BSApiPath.SPACES}${SpacesApiPath.$SPACEID_OBJECTS_$OBJECTID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_BS), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_BS), + checkHasRoleHook(UserRole.WORKER), + ], async handler( req: FastifyRequest<{ Params: BSObjectDeleteParamsDto }>, rep: FastifyReply, ) { const { spaceId, objectId } = req.params; - const token = req.user?.token as string; - const { userRole, tenantId } = tokenService.decode(token); - - if (userRole !== UserRole.WORKER) { - throw new BsError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_OBJECT_DELETE, - }); - } + const token = req.userData?.token as string; + const { tenantId } = tokenService.decode(token); await bsObjectService.deleteObject(spaceId, objectId, tenantId); diff --git a/backend/src/api/eam/eam.api.ts b/backend/src/api/eam/eam.api.ts index 030650c69..ba6b59d23 100644 --- a/backend/src/api/eam/eam.api.ts +++ b/backend/src/api/eam/eam.api.ts @@ -31,8 +31,10 @@ import { eamGroupUpdate as groupUpdateValidationSchema, UUID as UUIDValidationSchema, } from '~/validation-schemas/validation-schemas'; -import { checkHasPermissions as checkHasPermissionsHook } from '~/hooks/hooks'; -import { EAMError } from '~/exceptions/exceptions'; +import { + checkHasPermissions as checkHasPermissionsHook, + checkHasRole as checkHasRoleHook, +} from '~/hooks/hooks'; type Options = { services: { @@ -71,7 +73,7 @@ const initEamApi: FastifyPluginAsync = async (fastify, opts) => { req: FastifyRequest<{ Body: EAMWorkerCreateRequestDto }>, rep: FastifyReply, ) { - const token = req.user?.token as string; + const token = req.userData?.token as string; const { tenantId } = tokenService.decode(token); return rep @@ -240,7 +242,10 @@ const initEamApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.DELETE, url: `${EAMApiPath.WORKERS}${WorkersApiPath.$ID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_EAM), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_EAM), + checkHasRoleHook(UserRole.MASTER), + ], schema: { params: UUIDValidationSchema, }, @@ -253,20 +258,12 @@ const initEamApi: FastifyPluginAsync = async (fastify, opts) => { return schema.validate(data); }; }, + async handler( req: FastifyRequest<{ Params: EAMWorkerDeleteRequestDto }>, rep: FastifyReply, ) { - const { id } = req.params; - const { userRole } = tokenService.decode( - req.user?.token as string, - ); - - if (userRole !== UserRole.MASTER) { - throw new EAMError(); - } - - await workerService.deleteWorker(id); + await workerService.delete(req.params.id); return rep.send(true).status(HttpCode.OK); }, diff --git a/backend/src/api/sc/sc.api.ts b/backend/src/api/sc/sc.api.ts index de601cee6..9620c6a5b 100644 --- a/backend/src/api/sc/sc.api.ts +++ b/backend/src/api/sc/sc.api.ts @@ -11,6 +11,7 @@ import { InstancesApiPath, SshKeysApiPath, Permission, + UserRole, } from '~/common/enums/enums'; import { SCInstanceCreateRequestDto, @@ -26,7 +27,10 @@ import { scInstanceUpdate as scInstanceUpdateValidationSchema, UUID as UUIDValidationSchema, } from '~/validation-schemas/validation-schemas'; -import { checkHasPermissions as checkHasPermissionsHook } from '~/hooks/hooks'; +import { + checkHasPermissions as checkHasPermissionsHook, + checkHasRole as checkHasRoleHook, +} from '~/hooks/hooks'; type Options = { services: { @@ -56,7 +60,10 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.GET, url: `${SCApiPath.SSH_KEYS}${SshKeysApiPath.$ID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_SC), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_SC), + checkHasRoleHook(UserRole.WORKER), + ], schema: { params: UUIDValidationSchema, }, @@ -69,6 +76,7 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { return schema.validate(data); }; }, + async handler( req: FastifyRequest<{ Params: SCSshKeyGetByIdParamsDto; @@ -92,7 +100,7 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { ) { const instances = await instanceService.getByTenantId({ requestParams: req.query, - token: req.user?.token as string, + token: req.userData?.token as string, }); return rep.send(instances).status(HttpCode.OK); }, @@ -101,7 +109,10 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.DELETE, url: `${SCApiPath.INSTANCES}${InstancesApiPath.$ID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_SC), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_SC), + checkHasRoleHook(UserRole.WORKER), + ], schema: { params: UUIDValidationSchema, }, @@ -128,7 +139,10 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.PUT, url: `${SCApiPath.INSTANCES}${InstancesApiPath.$ID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_SC), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_SC), + checkHasRoleHook(UserRole.WORKER), + ], schema: { body: scInstanceUpdateValidationSchema, params: UUIDValidationSchema, @@ -157,7 +171,10 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.POST, url: SCApiPath.ROOT, - preHandler: checkHasPermissionsHook(Permission.MANAGE_SC), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_SC), + checkHasRoleHook(UserRole.WORKER), + ], schema: { body: scInstanceCreateValidationSchema, }, @@ -176,7 +193,7 @@ const initScApi: FastifyPluginAsync = async (fastify, opts) => { ) { const instance = await instanceService.create({ instanceCredentials: req.body, - token: req.user?.token as string, + token: req.userData?.token as string, }); return rep.send(instance).status(HttpCode.CREATED); }, diff --git a/backend/src/api/slc/slc.api.ts b/backend/src/api/slc/slc.api.ts index a2aa22671..f08737f9e 100644 --- a/backend/src/api/slc/slc.api.ts +++ b/backend/src/api/slc/slc.api.ts @@ -1,9 +1,6 @@ import { FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify'; import { FastifyRouteSchemaDef } from 'fastify/types/schema'; -import { - slcFunction as slcFunctionServ, - token as tokenServ, -} from '~/services/services'; +import { slcFunction as slcFunctionServ } from '~/services/services'; import { slcFunctionCreate as slcFunctionCreateValidationSchema, UUID as UUIDValidationSchema, @@ -15,7 +12,6 @@ import { SLCFunctionApiPath, UserRole, Permission, - ExceptionMessage, } from '~/common/enums/enums'; import { SLCFunctionCreateRequestDto, @@ -26,26 +22,28 @@ import { SLCFunctionLoadParamsDto, SLCFunctionRunParamsDto, SLCFunctionRunRequestDto, - TokenPayload, } from '~/common/types/types'; -import { SLCError } from '~/exceptions/exceptions'; -import { checkHasPermissions as checkHasPermissionsHook } from '~/hooks/hooks'; +import { + checkHasPermissions as checkHasPermissionsHook, + checkHasRole as checkHasRoleHook, +} from '~/hooks/hooks'; type Options = { services: { slcFunction: typeof slcFunctionServ; - token: typeof tokenServ; }; }; const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { - const { slcFunction: slcFunctionService, token: tokenService } = - opts.services; + const { slcFunction: slcFunctionService } = opts.services; fastify.route({ method: HttpMethod.POST, url: SLCApiPath.SLC_FUNCTIONS, - preHandler: checkHasPermissionsHook(Permission.MANAGE_SLC), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_SLC), + checkHasRoleHook(UserRole.WORKER), + ], schema: { body: slcFunctionCreateValidationSchema, }, @@ -66,7 +64,7 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { .send( await slcFunctionService.create({ name: req.body.name, - token: req.user?.token as string, + token: req.userData?.token as string, }), ) .status(HttpCode.CREATED); @@ -76,7 +74,10 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.POST, url: `${SLCApiPath.SLC_FUNCTIONS}${SLCFunctionApiPath.$ID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_SLC), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_SLC), + checkHasRoleHook(UserRole.WORKER), + ], schema: { params: UUIDValidationSchema, }, @@ -89,6 +90,7 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { return schema.validate(data); }; }, + async handler( req: FastifyRequest<{ Params: SLCFunctionRunParamsDto; @@ -96,17 +98,6 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { }>, rep: FastifyReply, ) { - const { userRole } = tokenService.decode( - req.user?.token as string, - ); - - if (userRole !== UserRole.WORKER) { - throw new SLCError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_FUNCTION_RUN, - }); - } - return rep .send( await slcFunctionService.runById({ @@ -128,7 +119,7 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { ) { const slcFunctions = await slcFunctionService.getSLCFunctionsByTenant({ query: req.query, - token: req.user?.token as string, + token: req.userData?.token as string, }); return rep.send(slcFunctions).status(HttpCode.OK); @@ -170,7 +161,10 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.DELETE, url: `${SLCApiPath.SLC_FUNCTIONS}${SLCFunctionApiPath.$ID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_SLC), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_SLC), + checkHasRoleHook(UserRole.WORKER), + ], schema: { params: UUIDValidationSchema, }, @@ -187,19 +181,7 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { req: FastifyRequest<{ Params: SLCFunctionDeleteParamsDto }>, rep: FastifyReply, ) { - const { id } = req.params; - const { userRole } = tokenService.decode( - req.user?.token as string, - ); - - if (userRole !== UserRole.WORKER) { - throw new SLCError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_FUNCTION_DELETE, - }); - } - - await slcFunctionService.delete(id); + await slcFunctionService.delete(req.params.id); return rep.send(true).status(HttpCode.OK); }, @@ -208,7 +190,10 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { fastify.route({ method: HttpMethod.PUT, url: `${SLCApiPath.SLC_FUNCTIONS}${SLCFunctionApiPath.$ID}`, - preHandler: checkHasPermissionsHook(Permission.MANAGE_SLC), + preHandler: [ + checkHasPermissionsHook(Permission.MANAGE_SLC), + checkHasRoleHook(UserRole.WORKER), + ], schema: { params: UUIDValidationSchema, }, @@ -233,7 +218,6 @@ const initSLCApi: FastifyPluginAsync = async (fastify, opts) => { await slcFunctionService.updateById({ id: req.params.id, sourceCode: req.body.sourceCode, - token: req.user?.token as string, }), ) .status(HttpCode.OK); diff --git a/backend/src/api/tenants/tenants.api.ts b/backend/src/api/tenants/tenants.api.ts index bcd6e4336..74216465b 100644 --- a/backend/src/api/tenants/tenants.api.ts +++ b/backend/src/api/tenants/tenants.api.ts @@ -1,4 +1,4 @@ -import { FastifyPluginAsync, FastifyRequest } from 'fastify'; +import { FastifyPluginAsync, FastifyReply, FastifyRequest } from 'fastify'; import { EAMTenantByIdRequestParamsDto, EAMTenantCreateRequestDto, @@ -23,7 +23,7 @@ const initTenantsApi: FastifyPluginAsync = async (fastify, opts) => { url: TenantsApiPath.$ID, async handler( req: FastifyRequest<{ Params: EAMTenantByIdRequestParamsDto }>, - rep, + rep: FastifyReply, ) { const tenant = await tenantService.getTenantById(req.params.id); return rep.send(tenant).status(HttpCode.OK); @@ -50,7 +50,7 @@ const initTenantsApi: FastifyPluginAsync = async (fastify, opts) => { Params: EAMTenantUpdateRequestDto; Body: EAMTenantUpdateRequestDto; }>, - rep, + rep: FastifyReply, ) { const tenant = await tenantService.updateTenantById({ id: req.params.id, diff --git a/backend/src/common/enums/exception/exception-message.enum.ts b/backend/src/common/enums/exception/exception-message.enum.ts index b64ad5bf8..feed1b8f1 100644 --- a/backend/src/common/enums/exception/exception-message.enum.ts +++ b/backend/src/common/enums/exception/exception-message.enum.ts @@ -1,4 +1,8 @@ const ExceptionMessage = { + USER_ROLE: 'This type of user not able to do this', + USER_ROLE_NOT_ADD: 'User type not specified for access', + USER_EXISTS: 'Account with this email already exists.', + UNAUTHORIZED_USER: 'Unauthorized user.', INCORRECT_CREDENTIALS: 'Your authentication information is incorrect. Please try again.', INCORRECT_EMAIL: @@ -10,25 +14,11 @@ const ExceptionMessage = { 'Account with that worker name does not exist. Try again or create a new account.', INVALID_TOKEN: 'Token is invalid.', PASSWORDS_NOT_MATCH: 'Passwords do not match.', - USER_EXISTS: 'Account with this email already exists.', - UNAUTHORIZED_USER: 'Unauthorized user.', WORKER_NAME_EXISTS: 'Worker with this name is exist', - WORKER_DELETE_WORKER: 'Worker is not able to delete another worker', WORKER_NOT_FOUND: 'Worker not Found', MASTER_NOT_FOUND: 'Master not Found', - MASTER_FUNCTION_CREATE: 'Master is not able to create function.', - MASTER_FUNCTION_DELETE: 'Master is not able to delete function.', - MASTER_FUNCTION_UPDATE: 'Master is not able to edit function.', - MASTER_FUNCTION_RUN: 'Master is not able to run function.', - MASTER_SPACE_CREATE: 'Master is not able to create space.', - MASTER_SPACE_DELETE: 'Master is not able to delete space', - MASTER_OBJECT_DELETE: 'Master is not able to delete object', - MASTER_OBJECT_UPLOAD: 'Master is not able to upload object', - MASTER_OBJECT_DOWNLOAD: 'Master is not able to download object', SPACE_NOT_FOUND: 'Space not found.', OBJECT_NOT_FOUND: 'Object not found, reload page and try again', - MASTER_INSTANCE_CREATE: 'Master is not able to create instance', - MASTER_DELETE: 'Master can not be deleted', FUNCTION_NAME_EXISTS: 'Function with this name already exists.', FUNCTION_NOT_CREATED: 'Something went wrong and function don`t created.', FUNCTION_NOT_FOUND: 'Function not found.', @@ -38,6 +28,7 @@ const ExceptionMessage = { GROUP_NOT_EMPTY: 'The group with workers can not be deleted', GROUP_NOT_FOUND: 'Group not found', GROUP_EXISTS: 'Group with this name already exists', + KEY_PAIR_NOT_FOUND: 'Key pair not found', INSTANCE_NOT_FOUND: 'Instance not found', SSH_KEY_NOT_FOUND: 'Ssh key not found', NOTHING_TO_UPDATE: 'Nothing to update', diff --git a/backend/src/data/repositories/worker/worker.repository.ts b/backend/src/data/repositories/worker/worker.repository.ts index 2f58e75b2..7bdbacd3f 100644 --- a/backend/src/data/repositories/worker/worker.repository.ts +++ b/backend/src/data/repositories/worker/worker.repository.ts @@ -68,7 +68,7 @@ class Worker { .resultSize(); } - public async deleteWorker(workerId: string): Promise { + public async delete(workerId: string): Promise { await this.#UsersGroupsModel.query().where({ 'userId': workerId }).delete(); return this.#WorkerModel.query().deleteById(workerId); } diff --git a/backend/src/exceptions/bs-error/bs-error.ts b/backend/src/exceptions/bs-error/bs-error.ts index ebce73558..5fcafbf7c 100644 --- a/backend/src/exceptions/bs-error/bs-error.ts +++ b/backend/src/exceptions/bs-error/bs-error.ts @@ -5,7 +5,7 @@ type Constructor = { message?: string; }; -const DEFAULT_MESSAGE = ExceptionMessage.MASTER_SPACE_CREATE; +const DEFAULT_MESSAGE = ExceptionMessage.SPACE_NOT_FOUND; class BsError extends Error { status: HttpCode; diff --git a/backend/src/exceptions/eam-error/eam-error.ts b/backend/src/exceptions/eam-error/eam-error.ts index 90105e7fe..4c364f24e 100644 --- a/backend/src/exceptions/eam-error/eam-error.ts +++ b/backend/src/exceptions/eam-error/eam-error.ts @@ -5,7 +5,7 @@ type Constructor = { message?: string; }; -const DEFAULT_MESSAGE = ExceptionMessage.WORKER_DELETE_WORKER; +const DEFAULT_MESSAGE = ExceptionMessage.WORKER_NOT_FOUND; class EAMError extends Error { status: HttpCode; diff --git a/backend/src/hooks/check-has-permission/check-has-permission.hook.ts b/backend/src/hooks/check-has-permission/check-has-permission.hook.ts index a4110c763..77dce3d58 100644 --- a/backend/src/hooks/check-has-permission/check-has-permission.hook.ts +++ b/backend/src/hooks/check-has-permission/check-has-permission.hook.ts @@ -1,4 +1,4 @@ -import { FastifyRequest } from 'fastify'; +import { FastifyRequest, FastifyReply } from 'fastify'; import { HttpCode, ExceptionMessage, Permission } from '~/common/enums/enums'; import { EAMWorkerSignInResponseDto, @@ -9,8 +9,8 @@ import { checkHasPermission } from '~/helpers/helpers'; const checkHasPermissions = (...permissions: Permission[]) => - async (req: T): Promise => { - const { user: userData } = req; + async (req: FastifyRequest, _rep: FastifyReply): Promise => { + const { userData } = req; const hasUser = Boolean(userData); diff --git a/backend/src/hooks/check-has-role/check-has-role.hook.ts b/backend/src/hooks/check-has-role/check-has-role.hook.ts new file mode 100644 index 000000000..9eb4fc2db --- /dev/null +++ b/backend/src/hooks/check-has-role/check-has-role.hook.ts @@ -0,0 +1,31 @@ +import { FastifyRequest, FastifyReply } from 'fastify'; +import { ExceptionMessage, HttpCode, UserRole } from '~/common/enums/enums'; +import { TokenPayload } from '~/common/types/types'; +import { HttpError } from '~/exceptions/exceptions'; +import { token as tokenService } from '~/services/services'; + +const checkHasRole = + (...roles: UserRole[]) => + async (req: FastifyRequest, _rep: FastifyReply): Promise => { + const token = req.userData?.token as string; + const { userRole } = tokenService.decode(token); + const hasRole = Boolean(roles.length); + + if (!hasRole) { + throw new HttpError({ + status: HttpCode.LOCKED, + message: ExceptionMessage.USER_ROLE_NOT_ADD, + }); + } + + const hasCorrectRole = roles.some((it) => it === userRole); + + if (!hasCorrectRole) { + throw new HttpError({ + status: HttpCode.DENIED, + message: ExceptionMessage.USER_ROLE, + }); + } + }; + +export { checkHasRole }; diff --git a/backend/src/hooks/hooks.ts b/backend/src/hooks/hooks.ts index adc5224c0..8b2006ef7 100644 --- a/backend/src/hooks/hooks.ts +++ b/backend/src/hooks/hooks.ts @@ -1,2 +1,3 @@ export { upload } from './upload/upload.hook'; export { checkHasPermissions } from './check-has-permission/check-has-permission.hook'; +export { checkHasRole } from './check-has-role/check-has-role.hook'; diff --git a/backend/src/index.d.ts b/backend/src/index.d.ts index 978024766..8b69d2891 100644 --- a/backend/src/index.d.ts +++ b/backend/src/index.d.ts @@ -7,6 +7,6 @@ import { declare module 'fastify' { interface FastifyRequest { file?: File; - user?: EAMMasterSignInResponseDto | EAMWorkerSignInResponseDto; + userData?: EAMMasterSignInResponseDto | EAMWorkerSignInResponseDto; } } diff --git a/backend/src/plugins/authorization/authorization.plugin.ts b/backend/src/plugins/authorization/authorization.plugin.ts index 08a2f96ae..c54aba999 100644 --- a/backend/src/plugins/authorization/authorization.plugin.ts +++ b/backend/src/plugins/authorization/authorization.plugin.ts @@ -30,7 +30,7 @@ const authorization: FastifyPluginAsync = fp(async (fastify, opts) => { }); } - request.user = await auth.getCurrentUser(token); + request.userData = await auth.getCurrentUser(token); }); }); diff --git a/backend/src/services/bs-object/bs-object.service.ts b/backend/src/services/bs-object/bs-object.service.ts index 39532caed..a11f153e3 100644 --- a/backend/src/services/bs-object/bs-object.service.ts +++ b/backend/src/services/bs-object/bs-object.service.ts @@ -7,7 +7,7 @@ import { } from '~/services/services'; import { BSObject as BSObjectEntity } from './bs-object.entity'; import { HttpCode } from '~/common/enums/http/http'; -import { ExceptionMessage, UserRole } from '~/common/enums/enums'; +import { ExceptionMessage } from '~/common/enums/enums'; import { BsError } from '~/exceptions/exceptions'; import { UploadPayload, @@ -52,13 +52,6 @@ class BSObject { }: UploadPayload): Promise { const user: TokenPayload = await this.#tokenService.decode(token); - if (user.userRole !== UserRole.WORKER) { - throw new BsError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_OBJECT_UPLOAD, - }); - } - const space = await this.#spaceService.getSpaceById(id); const worker = await this.#workerService.getUserById(space.createdBy); @@ -105,13 +98,6 @@ class BSObject { }): Promise { const user: TokenPayload = await this.#tokenService.decode(token); - if (user.userRole !== UserRole.WORKER) { - throw new BsError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_OBJECT_DOWNLOAD, - }); - } - const space = await this.#spaceService.getSpaceById(spaceId); const worker = await this.#workerService.getUserById(space.createdBy); diff --git a/backend/src/services/instance/instance.service.ts b/backend/src/services/instance/instance.service.ts index d42746263..de1e1bbb2 100644 --- a/backend/src/services/instance/instance.service.ts +++ b/backend/src/services/instance/instance.service.ts @@ -16,7 +16,6 @@ import { } from '~/services/services'; import { InstanceDefaultParam, - UserRole, HttpCode, ExceptionMessage, InstanceState, @@ -101,14 +100,9 @@ class Instance { token: string; }): Promise { const { name, operationSystemId, userData } = instanceCredentials; - const { userId, userRole, tenantId }: TokenPayload = - await this.#tokenService.decode(token); - if (userRole !== UserRole.WORKER) { - throw new SCError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_INSTANCE_CREATE, - }); - } + const { userId, tenantId }: TokenPayload = await this.#tokenService.decode( + token, + ); const keyPairId = await this.#keyPairService.create(); const operationSystem = diff --git a/backend/src/services/key-pair/key-pair.service.ts b/backend/src/services/key-pair/key-pair.service.ts index 98d8d3743..e765f6b0b 100644 --- a/backend/src/services/key-pair/key-pair.service.ts +++ b/backend/src/services/key-pair/key-pair.service.ts @@ -24,8 +24,8 @@ class KeyPair { const keyPairData = await this.#keyPairRepository.getById(id); if (!keyPairData) { throw new SCError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_INSTANCE_CREATE, + status: HttpCode.NOT_FOUND, + message: ExceptionMessage.KEY_PAIR_NOT_FOUND, }); } diff --git a/backend/src/services/services.ts b/backend/src/services/services.ts index a48f2d69a..ec1f30038 100644 --- a/backend/src/services/services.ts +++ b/backend/src/services/services.ts @@ -110,8 +110,6 @@ const worker = new Worker({ instanceRepository, encryptService: encrypt, tokenService: token, - masterService: master, - tenantService: tenant, instanceService: instance, spaceService: space, slcFunctionService: slcFunction, diff --git a/backend/src/services/slc-function/slc-function.service.ts b/backend/src/services/slc-function/slc-function.service.ts index 52a856931..b10c58583 100644 --- a/backend/src/services/slc-function/slc-function.service.ts +++ b/backend/src/services/slc-function/slc-function.service.ts @@ -14,7 +14,6 @@ import { SLCError } from '~/exceptions/exceptions'; import { HttpCode, ExceptionMessage, - UserRole, LambdaDefaultParam, } from '~/common/enums/enums'; import { token as tokenServ, lambda as lambdaServ } from '~/services/services'; @@ -47,16 +46,9 @@ class SLCFunction { name: string; token: string; }): Promise { - const { userId: createdBy, userRole } = + const { userId: createdBy } = this.#tokenService.decode(token); - if (userRole !== UserRole.WORKER) { - throw new SLCError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_FUNCTION_CREATE, - }); - } - const slcFunctionByName = await this.#slcFunctionRepository.getByName(name); if (slcFunctionByName) { @@ -131,21 +123,10 @@ class SLCFunction { public async updateById({ id, sourceCode, - token, }: { id: string; sourceCode: string; - token: string; }): Promise { - const { userRole } = this.#tokenService.decode(token); - - if (userRole !== UserRole.WORKER) { - throw new SLCError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_FUNCTION_UPDATE, - }); - } - const slcFunction = await this.#slcFunctionRepository.getById(id); if (!slcFunction) { diff --git a/backend/src/services/space/space.service.ts b/backend/src/services/space/space.service.ts index ed2a51162..8e9638f27 100644 --- a/backend/src/services/space/space.service.ts +++ b/backend/src/services/space/space.service.ts @@ -6,7 +6,7 @@ import { BSSpaceGetResponseDto, TokenPayload, } from '~/common/types/types'; -import { ExceptionMessage, HttpCode, UserRole } from '~/common/enums/enums'; +import { ExceptionMessage, HttpCode } from '~/common/enums/enums'; import { s3 as s3Serv, token as tokenServ } from '~/services/services'; import { BsError } from '~/exceptions/exceptions'; @@ -58,13 +58,6 @@ class Space { }): Promise { const user: TokenPayload = await this.#tokenService.decode(token); - if (user.userRole !== UserRole.WORKER) { - throw new BsError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_SPACE_CREATE, - }); - } - await this.#s3Service.creteBucket(name); const space = SpaceEntity.createNew({ name, createdBy: user.userId }); diff --git a/backend/src/services/worker/worker.service.ts b/backend/src/services/worker/worker.service.ts index 4e18903ee..9dd35cbec 100644 --- a/backend/src/services/worker/worker.service.ts +++ b/backend/src/services/worker/worker.service.ts @@ -18,9 +18,7 @@ import { HttpCode } from '~/common/enums/http/http'; import { ExceptionMessage, UserRole } from '~/common/enums/enums'; import { encrypt as encryptServ, - master as masterServ, token as tokenServ, - tenant as tenantServ, instance as instanceServ, space as spaceServ, slcFunction as slcFunctionServ, @@ -35,8 +33,6 @@ type Constructor = { instanceRepository: typeof instanceRep; encryptService: typeof encryptServ; tokenService: typeof tokenServ; - masterService: typeof masterServ; - tenantService: typeof tenantServ; instanceService: typeof instanceServ; spaceService: typeof spaceServ; slcFunctionService: typeof slcFunctionServ; @@ -50,8 +46,6 @@ class Worker { #instanceRepository: typeof instanceRep; #encryptService: typeof encryptServ; #tokenService: typeof tokenServ; - #masterService: typeof masterServ; - #tenantService: typeof tenantServ; #instanceService: typeof instanceServ; #spaceService: typeof spaceServ; #slcFunctionService: typeof slcFunctionServ; @@ -64,8 +58,6 @@ class Worker { instanceRepository, encryptService, tokenService, - masterService, - tenantService, instanceService, spaceService, slcFunctionService, @@ -77,8 +69,6 @@ class Worker { this.#instanceRepository = instanceRepository; this.#encryptService = encryptService; this.#tokenService = tokenService; - this.#masterService = masterService; - this.#tenantService = tenantService; this.#instanceService = instanceService; this.#spaceService = spaceService; this.#slcFunctionService = slcFunctionService; @@ -201,7 +191,7 @@ class Worker { return { items: workers, countItems }; } - public async deleteWorker(id: string): Promise { + public async delete(id: string): Promise { const worker = await this.#workerRepository.getById(id); if (!worker) { @@ -211,15 +201,6 @@ class Worker { }); } - const master = await this.#masterService.getMasterById(id); - - if (master) { - throw new EAMError({ - status: HttpCode.DENIED, - message: ExceptionMessage.MASTER_DELETE, - }); - } - const instances = await this.#instanceRepository.getByWorkerId(id); const spaces = await this.#spaceRepository.getByWorkerId(id); const slcFunctions = await this.#slcFunctionRepository.getByWorkerId(id); @@ -240,7 +221,7 @@ class Worker { }), ); - await this.#workerRepository.deleteWorker(id); + await this.#workerRepository.delete(id); } } diff --git a/frontend/src/common/enums/entity/entity-type.enum.ts b/frontend/src/common/enums/entity/entity-type.enum.ts new file mode 100644 index 000000000..e57579f9a --- /dev/null +++ b/frontend/src/common/enums/entity/entity-type.enum.ts @@ -0,0 +1,10 @@ +enum EntityType { + INSTANCE = 'instance', + FUNCTION = 'function', + OBJECT = 'object', + SPACE = 'space', + GROUP = 'group', + WORKER = 'worker', +} + +export { EntityType }; diff --git a/frontend/src/common/enums/entity/entity.ts b/frontend/src/common/enums/entity/entity.ts new file mode 100644 index 000000000..87957a4d6 --- /dev/null +++ b/frontend/src/common/enums/entity/entity.ts @@ -0,0 +1 @@ +export { EntityType } from './entity-type.enum'; diff --git a/frontend/src/common/enums/enums.ts b/frontend/src/common/enums/enums.ts index 8314748d4..ac2343b7a 100644 --- a/frontend/src/common/enums/enums.ts +++ b/frontend/src/common/enums/enums.ts @@ -57,3 +57,4 @@ export { Pagination } from './pagination/pagination'; export { InstanceState } from './instance-states/instance-states'; export { KeydownKey } from './event/event'; export { FormDataCommonKey } from './form-data/form-data'; +export { EntityType } from './entity/entity'; diff --git a/frontend/src/common/types/app/use-pagination-hook.type.ts b/frontend/src/common/types/app/use-pagination-hook.type.ts index 02a9635fd..ce8c0078a 100644 --- a/frontend/src/common/types/app/use-pagination-hook.type.ts +++ b/frontend/src/common/types/app/use-pagination-hook.type.ts @@ -4,6 +4,7 @@ type UsePaginationtemsHook = { allPage: number; currentPage: number; countItems: number; + onReload: () => void; }; export { type UsePaginationtemsHook }; diff --git a/frontend/src/components/bs-space/bs-space.tsx b/frontend/src/components/bs-space/bs-space.tsx index a599fb70f..7eb9e9732 100644 --- a/frontend/src/components/bs-space/bs-space.tsx +++ b/frontend/src/components/bs-space/bs-space.tsx @@ -1,6 +1,6 @@ -import { IconName } from 'common/enums/enums'; -import { IconButton } from 'components/common/common'; -import { useAppDispatch, useEffect, useParams } from 'hooks/hooks'; +import { EntityType } from 'common/enums/enums'; +import { ConfirmDeletePopup } from 'components/common/common'; +import { useAppDispatch, useEffect, useParams, useState } from 'hooks/hooks'; import { ObjectsTable } from './components/components'; import React, { FC } from 'react'; import styles from './styles.module.scss'; @@ -8,6 +8,7 @@ import { BSSpace as BSSpaceActions } from 'store/actions'; const BSSpace: FC = () => { const dispatch = useAppDispatch(); + const [currentObjectId, setCurrentObjectId] = useState(null); const { id } = useParams(); @@ -23,18 +24,6 @@ const BSSpace: FC = () => { ); }, [dispatch]); - const handleObjectsReload = (): void => { - dispatch( - BSSpaceActions.loadObjects({ - filter: { - from: 0, - count: 5, - }, - id: id as string, - }), - ); - }; - const handleObjectDownload = (objectId: string): void => { dispatch( BSSpaceActions.downloadObject({ @@ -44,59 +33,42 @@ const BSSpace: FC = () => { ); }; - const handleObjectUpload = (evt: React.FormEvent): void => { - const [file] = evt.currentTarget.files ?? []; - const hasFiles = Boolean(file); - - if (!hasFiles) { - return; - } + const handleObjectDelete = (id: string): void => setCurrentObjectId(id); - dispatch( - BSSpaceActions.uploadObject({ id: id as string, file: file as File }), - ); - }; + const handleCancelDelete = (): void => setCurrentObjectId(null); - const handleObjectDelete = (objectId: string): void => { + const handleConfirmDelete = (): void => { dispatch( BSSpaceActions.deleteObject({ spaceId: id as string, - objectId, + objectId: currentObjectId as string, }), ); + setCurrentObjectId(null); }; return ( -
-

- BS -
- Binary Storage -

-
- -
- - -
-
+ <> +
+

+ BS -
+ Binary Storage +

+
+ +
-
+ + ); }; diff --git a/frontend/src/components/bs-space/components/objects-table/objects-table.tsx b/frontend/src/components/bs-space/components/objects-table/objects-table.tsx index eedee46fb..6f9eb0e19 100644 --- a/frontend/src/components/bs-space/components/objects-table/objects-table.tsx +++ b/frontend/src/components/bs-space/components/objects-table/objects-table.tsx @@ -4,11 +4,13 @@ import { useMemo, useAppDispatch, usePagination, + useParams, } from 'hooks/hooks'; -import { Table } from 'components/common/common'; +import { Table, IconButton } from 'components/common/common'; import { getRows, getColumns } from './helpers/helpers'; -import { DataStatus, Pagination } from 'common/enums/enums'; +import { DataStatus, Pagination, IconName } from 'common/enums/enums'; import { BSSpace as BSSpaceActions } from 'store/actions'; +import styles from './styles.module.scss'; type Props = { spaceId: string; @@ -23,7 +25,6 @@ type Props = { }; const ObjectsTable: FC = ({ - children, onObjectDelete, onObjectDownload, spaceId, @@ -56,6 +57,21 @@ const ObjectsTable: FC = ({ from: Pagination.INITIAL_FROM_COUNT, }); + const { id } = useParams(); + + const handleObjectsReload = (): void => { + dispatch( + BSSpaceActions.loadObjects({ + filter: { + from: 0, + count: 5, + }, + id: id as string, + }), + ); + objectsPagination.onReload(); + }; + const data = useMemo( () => getRows({ objects, onObjectDownload, onObjectDelete }), [objects], @@ -63,6 +79,21 @@ const ObjectsTable: FC = ({ const columns = useMemo(() => getColumns(), []); + const handleObjectUpload = (evt: React.FormEvent): void => { + const [file] = evt.currentTarget.files ?? []; + const hasFiles = Boolean(file); + + if (!hasFiles) { + return; + } + + dispatch( + BSSpaceActions.uploadObject({ id: id as string, file: file as File }), + ); + + (evt.target as HTMLInputElement).value = ''; + }; + return ( = ({ isLoading={isLoading} pagination={objectsPagination} > - {children} +
+ + +
); }; diff --git a/frontend/src/components/bs-space/components/objects-table/styles.module.scss b/frontend/src/components/bs-space/components/objects-table/styles.module.scss new file mode 100644 index 000000000..2655fe8f3 --- /dev/null +++ b/frontend/src/components/bs-space/components/objects-table/styles.module.scss @@ -0,0 +1,36 @@ +.buttonsBlock { + display: flex; + align-items: center; + gap: 14px; + width: 250px; +} + +.hideDefaultInput { + position: absolute; + width: 0.1px; + height: 0.1px; + overflow: hidden; + opacity: 0; +} + +.fileInput { + width: 100%; + min-height: 45px; + padding-top: 8px; + padding-bottom: 8px; + color: var(--color-white); + font-weight: 800; + font-size: 16px; + text-align: center; + text-decoration: none; + background-color: var(--color-orange); + border: 2px solid var(--color-orange); + border-radius: 50px; + cursor: pointer; + transition-duration: 0.4s; +} + +.fileInput:hover { + color: var(--color-orange); + background-color: transparent; +} diff --git a/frontend/src/components/bs-space/styles.module.scss b/frontend/src/components/bs-space/styles.module.scss index 717809262..2583bb90d 100644 --- a/frontend/src/components/bs-space/styles.module.scss +++ b/frontend/src/components/bs-space/styles.module.scss @@ -36,40 +36,3 @@ .addSpaceBtn { max-width: 200px; } - -.buttonsBlock { - display: flex; - align-items: center; - gap: 14px; - width: 250px; -} - -.hideDefaultInput { - position: absolute; - width: 0.1px; - height: 0.1px; - overflow: hidden; - opacity: 0; -} - -.fileInput { - width: 100%; - min-height: 45px; - padding-top: 8px; - padding-bottom: 8px; - color: var(--color-white); - font-weight: 800; - font-size: 16px; - text-align: center; - text-decoration: none; - background-color: var(--color-orange); - border: 2px solid var(--color-orange); - border-radius: 50px; - cursor: pointer; - transition-duration: 0.4s; -} - -.fileInput:hover { - color: var(--color-orange); - background-color: transparent; -} diff --git a/frontend/src/components/bs/bs.tsx b/frontend/src/components/bs/bs.tsx index 9085516f1..805491154 100644 --- a/frontend/src/components/bs/bs.tsx +++ b/frontend/src/components/bs/bs.tsx @@ -1,6 +1,6 @@ -import { AppRoute, IconName } from 'common/enums/enums'; -import { Button, IconButton } from 'components/common/common'; -import { useAppDispatch, useEffect } from 'hooks/hooks'; +import { EntityType } from 'common/enums/enums'; +import { ConfirmDeletePopup } from 'components/common/common'; +import { useAppDispatch, useEffect, useState } from 'hooks/hooks'; import { SpacesTable } from './components/components'; import { FC } from 'react'; import styles from './styles.module.scss'; @@ -8,6 +8,7 @@ import { bs as bsActions } from 'store/actions'; const BS: FC = () => { const dispatch = useAppDispatch(); + const [currentSpaceId, setCurrentSpaceId] = useState(null); useEffect(() => { dispatch( @@ -18,43 +19,33 @@ const BS: FC = () => { ); }, [dispatch]); - const handleSpaceDelete = (id: string): void => { - dispatch(bsActions.deleteSpace(id)); - }; + const handleSpaceDelete = (id: string): void => setCurrentSpaceId(id); - const handleSpacesReload = (): void => { - dispatch( - bsActions.loadSpaces({ - from: 0, - count: 5, - }), - ); + const handleCancelDelete = (): void => setCurrentSpaceId(null); + + const handleConfirmDelete = (): void => { + dispatch(bsActions.deleteSpace(currentSpaceId as string)); + setCurrentSpaceId(null); }; return ( -
-

- BS -
- Binary Storage -

-
- -
- -
-
+ <> +
+

+ BS -
+ Binary Storage +

+
+ +
-
+ + ); }; diff --git a/frontend/src/components/bs/components/spaces-table/spaces-table.tsx b/frontend/src/components/bs/components/spaces-table/spaces-table.tsx index ad9392be8..7f29ba376 100644 --- a/frontend/src/components/bs/components/spaces-table/spaces-table.tsx +++ b/frontend/src/components/bs/components/spaces-table/spaces-table.tsx @@ -5,10 +5,11 @@ import { useAppDispatch, usePagination, } from 'hooks/hooks'; -import { Table } from 'components/common/common'; +import { Table, Button, IconButton } from 'components/common/common'; import { getRows, getColumns } from './helpers/helpers'; -import { DataStatus, Pagination } from 'common/enums/enums'; +import { DataStatus, Pagination, AppRoute, IconName } from 'common/enums/enums'; import { bs as bsActions } from 'store/actions'; +import styles from './styles.module.scss'; type Props = { onSpaceDelete: (id: string) => void; @@ -20,7 +21,7 @@ type Props = { }; }; -const SpacesTable: FC = ({ children, onSpaceDelete }) => { +const SpacesTable: FC = ({ onSpaceDelete }) => { const dispatch = useAppDispatch(); const { spaces, dataStatus, countItems } = useAppSelector(({ bs }) => ({ @@ -46,21 +47,45 @@ const SpacesTable: FC = ({ children, onSpaceDelete }) => { from: Pagination.INITIAL_FROM_COUNT, }); + const handleSpacesReload = (): void => { + dispatch( + bsActions.loadSpaces({ + from: 0, + count: 5, + }), + ); + spacePagination.onReload(); + }; + const data = useMemo(() => getRows({ spaces, onSpaceDelete }), [spaces]); const columns = useMemo(() => getColumns(), []); return ( - - {children} -
+ <> + +
+ +
+
+ ); }; diff --git a/frontend/src/components/bs/components/spaces-table/styles.module.scss b/frontend/src/components/bs/components/spaces-table/styles.module.scss new file mode 100644 index 000000000..376f25822 --- /dev/null +++ b/frontend/src/components/bs/components/spaces-table/styles.module.scss @@ -0,0 +1,10 @@ +.addSpaceBtn { + max-width: 200px; +} + +.buttonsBlock { + display: flex; + align-items: center; + gap: 14px; + width: 250px; +} diff --git a/frontend/src/components/bs/styles.module.scss b/frontend/src/components/bs/styles.module.scss index 40a7cee69..8c8849eee 100644 --- a/frontend/src/components/bs/styles.module.scss +++ b/frontend/src/components/bs/styles.module.scss @@ -32,14 +32,3 @@ .tableWrapper { margin: 0 0 50px; } - -.addSpaceBtn { - max-width: 200px; -} - -.buttonsBlock { - display: flex; - align-items: center; - gap: 14px; - width: 250px; -} diff --git a/frontend/src/components/common/common.ts b/frontend/src/components/common/common.ts index e7a2dfe8a..bf760350f 100644 --- a/frontend/src/components/common/common.ts +++ b/frontend/src/components/common/common.ts @@ -17,3 +17,4 @@ export { IconButton } from './icon-button/icon-button'; export { Editor } from './editor/editor'; export { Chip } from './chip/chip'; export { Modal } from './modal/modal'; +export { ConfirmDeletePopup } from './confirm-delete-popup/confirm-delete-popup'; diff --git a/frontend/src/components/common/confirm-delete-popup/confirm-delete-popup.tsx b/frontend/src/components/common/confirm-delete-popup/confirm-delete-popup.tsx new file mode 100644 index 000000000..4148344f2 --- /dev/null +++ b/frontend/src/components/common/confirm-delete-popup/confirm-delete-popup.tsx @@ -0,0 +1,44 @@ +import { FC } from 'react'; +import { Button, Modal } from 'components/common/common'; +import { ButtonStyle, EntityType } from 'common/enums/enums'; +import styles from './styles.module.scss'; + +interface Props { + isOpen: boolean; + onClose: () => void; + onConfirm: () => void; + entityType: EntityType; +} + +const ConfirmDeletePopup: FC = ({ + isOpen, + onClose, + onConfirm, + entityType, +}) => { + return ( + +
+

+ {`Are you sure you want to delete this ${entityType}?`} +

+
+
+
+
+ ); +}; + +export { ConfirmDeletePopup }; diff --git a/frontend/src/components/common/confirm-delete-popup/styles.module.scss b/frontend/src/components/common/confirm-delete-popup/styles.module.scss new file mode 100644 index 000000000..c0758aae5 --- /dev/null +++ b/frontend/src/components/common/confirm-delete-popup/styles.module.scss @@ -0,0 +1,28 @@ +.wrapper { + display: flex; + flex-direction: column; + min-width: 700px; + max-width: 80%; + max-height: 80%; + padding: 40px 50px 20px; + overflow: auto; + background: var(--color-boltgun); + border-radius: 15px; +} + +.title { + margin: 0 0 15px; + color: var(--color-text-light); + font-size: 20px; +} + +.buttons { + display: flex; + justify-content: flex-end; + gap: 10px; + margin: 10px 0; +} + +.button { + max-width: 100px; +} diff --git a/frontend/src/components/eam-configurate-group/components/workers-table/workers-table.tsx b/frontend/src/components/eam-configurate-group/components/workers-table/workers-table.tsx index a9a2b2011..28fd05c33 100644 --- a/frontend/src/components/eam-configurate-group/components/workers-table/workers-table.tsx +++ b/frontend/src/components/eam-configurate-group/components/workers-table/workers-table.tsx @@ -1,14 +1,27 @@ import React, { FC } from 'react'; -import { useAppSelector, useMemo } from 'hooks/hooks'; +import { + useAppSelector, + useMemo, + useAppDispatch, + usePagination, +} from 'hooks/hooks'; import { Table } from 'components/common/table/table'; import { getRows, getColumns } from './helpers/helpers'; import styles from './styles.module.scss'; +import { Pagination } from 'common/enums/enums'; +import { EAMGroupConfigurate as EAMGroupConfigurateActions } from 'store/actions'; type Props = { handleAddWorkerId: (id: string) => void; handleRemoveWorkerId: (id: string) => void; handleIsCheckedId: (id: string) => boolean; selectedWorkers: string[]; + pagination?: { + perPage: number; + countItems: number; + handleLoad: (from: number) => void; + from: number; + }; hasGroup: boolean; }; @@ -19,9 +32,32 @@ const WorkersTable: FC = ({ handleIsCheckedId, hasGroup, }) => { - const { workers } = useAppSelector(({ EAMGroupConfigurate }) => ({ - workers: EAMGroupConfigurate.workers, - })); + const dispatch = useAppDispatch(); + + const { workers, tenantId, countItems } = useAppSelector( + ({ app, EAMGroupConfigurate }) => ({ + workers: EAMGroupConfigurate.workers, + tenantId: app.tenant?.id, + countItems: EAMGroupConfigurate.workersCountItems, + }), + ); + + const handleLoad = (from: number, count: number): void => { + dispatch( + EAMGroupConfigurateActions.loadWorkers({ + tenantId: tenantId as string, + from: from, + count: count, + }), + ); + }; + + const workerPagination = usePagination({ + perPageCount: Pagination.PER_PAGE, + countItems, + onLoad: handleLoad, + from: Pagination.INITIAL_FROM_COUNT, + }); const data = useMemo( () => @@ -48,6 +84,7 @@ const WorkersTable: FC = ({ columns={columns} data={data} placeholder="No workers to display" + pagination={workerPagination} />
); diff --git a/frontend/src/components/eam-configurate-group/eam-configurate-group.tsx b/frontend/src/components/eam-configurate-group/eam-configurate-group.tsx index ce819dcc7..3b8d65671 100644 --- a/frontend/src/components/eam-configurate-group/eam-configurate-group.tsx +++ b/frontend/src/components/eam-configurate-group/eam-configurate-group.tsx @@ -52,7 +52,7 @@ const EAMConfigurateGroup: FC = () => { dispatch( EAMGroupConfigurateActions.loadWorkers({ from: 0, - count: 10, + count: 5, tenantId: tenantId as string, }), ); @@ -109,7 +109,7 @@ const EAMConfigurateGroup: FC = () => {
  • - {hasGroup ? 'Edit' : 'Name'} group name + {hasGroup ? 'Edit group name' : 'Name the group'}

    { +type Props = { + pagination?: { + perPage: number; + countItems: number; + handleLoad: (from: number) => void; + from: number; + }; +}; + +const EAMConfigurateWorker: FC = () => { const dispatch = useAppDispatch(); const selectedGroups = useSelectedItems([]); - const { tenantId, groups, csvColumns } = useAppSelector( + const { tenantId, groups, csvColumns, countItems } = useAppSelector( ({ app, EAMWorkerConfigurate }) => ({ tenantId: app.tenant?.id, groups: EAMWorkerConfigurate.groups, csvColumns: EAMWorkerConfigurate.csvColumns, + countItems: EAMWorkerConfigurate.countItems, }), ); @@ -38,7 +50,7 @@ const EAMConfigurateWorker: FC = () => { EAMWorkerConfigurateActions.getGroups({ tenantId: tenantId as string, from: 0, - count: 100, + count: 5, }), ); } @@ -75,6 +87,23 @@ const EAMConfigurateWorker: FC = () => { const hasCsvColumns = Boolean(csvColumns.length); + const handleLoad = (from: number, count: number): void => { + dispatch( + EAMWorkerConfigurateActions.getGroups({ + tenantId: tenantId as string, + from: from, + count: count, + }), + ); + }; + + const groupPagination = usePagination({ + perPageCount: Pagination.PER_PAGE, + countItems, + onLoad: handleLoad, + from: Pagination.INITIAL_FROM_COUNT, + }); + const columns = getColumns( selectedGroups.handleAdd, selectedGroups.handleRemove, @@ -113,6 +142,7 @@ const EAMConfigurateWorker: FC = () => { title="Groups" columns={columns} data={data} + pagination={groupPagination} placeholder="No groups to display" />
  • diff --git a/frontend/src/components/eam/components/groups-table/groups-table.tsx b/frontend/src/components/eam/components/groups-table/groups-table.tsx index a775427b8..28c2366bd 100644 --- a/frontend/src/components/eam/components/groups-table/groups-table.tsx +++ b/frontend/src/components/eam/components/groups-table/groups-table.tsx @@ -5,10 +5,11 @@ import { useAppDispatch, usePagination, } from 'hooks/hooks'; -import { Table } from 'components/common/common'; +import { Table, Button, IconButton } from 'components/common/common'; import { getRows, getColumns } from './helpers/helpers'; -import { DataStatus, Pagination } from 'common/enums/enums'; +import { DataStatus, Pagination, AppRoute, IconName } from 'common/enums/enums'; import { eam as eamActions } from 'store/actions'; +import styles from './styles.module.scss'; type Props = { onGroupDelete: (id: string) => void; @@ -20,7 +21,7 @@ type Props = { }; }; -const GroupsTable: FC = ({ children, onGroupDelete }) => { +const GroupsTable: FC = ({ onGroupDelete }) => { const dispatch = useAppDispatch(); const { groups, groupsDataStatus, countItems, tenantId } = useAppSelector( @@ -51,6 +52,17 @@ const GroupsTable: FC = ({ children, onGroupDelete }) => { from: Pagination.INITIAL_FROM_COUNT, }); + const handleGroupsReload = (): void => { + dispatch( + eamActions.loadGroups({ + tenantId: tenantId as string, + from: 0, + count: 5, + }), + ); + groupPagination.onReload(); + }; + const data = useMemo(() => getRows({ groups, onGroupDelete }), [groups]); const columns = useMemo(() => getColumns(), []); @@ -65,7 +77,19 @@ const GroupsTable: FC = ({ children, onGroupDelete }) => { pagination={groupPagination} isLoading={isLoading} > - {children} +
    + +
    ); }; diff --git a/frontend/src/components/eam/components/groups-table/styles.module.scss b/frontend/src/components/eam/components/groups-table/styles.module.scss new file mode 100644 index 000000000..a0f8caf10 --- /dev/null +++ b/frontend/src/components/eam/components/groups-table/styles.module.scss @@ -0,0 +1,11 @@ +.addWorkerBtn, +.addGroupBtn { + max-width: 200px; +} + +.buttonsBlock { + display: flex; + align-items: center; + gap: 14px; + width: 250px; +} diff --git a/frontend/src/components/eam/components/workers-table/styles.module.scss b/frontend/src/components/eam/components/workers-table/styles.module.scss new file mode 100644 index 000000000..a0f8caf10 --- /dev/null +++ b/frontend/src/components/eam/components/workers-table/styles.module.scss @@ -0,0 +1,11 @@ +.addWorkerBtn, +.addGroupBtn { + max-width: 200px; +} + +.buttonsBlock { + display: flex; + align-items: center; + gap: 14px; + width: 250px; +} diff --git a/frontend/src/components/eam/components/workers-table/workers-table.tsx b/frontend/src/components/eam/components/workers-table/workers-table.tsx index 04328c187..f847abb7f 100644 --- a/frontend/src/components/eam/components/workers-table/workers-table.tsx +++ b/frontend/src/components/eam/components/workers-table/workers-table.tsx @@ -8,7 +8,9 @@ import { import { Table } from 'components/common/table/table'; import { getRows, getColumns } from './helpers/helpers'; import { eam as eamActions } from 'store/actions'; -import { DataStatus, Pagination } from 'common/enums/enums'; +import { DataStatus, Pagination, IconName, AppRoute } from 'common/enums/enums'; +import { Button, IconButton } from 'components/common/common'; +import styles from './styles.module.scss'; type Props = { onWorkerDelete: (id: string) => void; @@ -20,7 +22,7 @@ type Props = { }; }; -const WorkersTable: FC = ({ children, onWorkerDelete }) => { +const WorkersTable: FC = ({ onWorkerDelete }) => { const dispatch = useAppDispatch(); const { workers, countItems, tenantId, workersDataStatus } = useAppSelector( @@ -51,6 +53,17 @@ const WorkersTable: FC = ({ children, onWorkerDelete }) => { from: Pagination.INITIAL_FROM_COUNT, }); + const handleWorkersReload = (): void => { + dispatch( + eamActions.loadWorkers({ + tenantId: tenantId as string, + from: 0, + count: 5, + }), + ); + workersPagination.onReload(); + }; + const data = useMemo(() => getRows({ workers, onWorkerDelete }), [workers]); const columns = useMemo(() => getColumns(), []); @@ -64,7 +77,19 @@ const WorkersTable: FC = ({ children, onWorkerDelete }) => { pagination={workersPagination} isLoading={isLoading} > - {children} +
    + +
    ); }; diff --git a/frontend/src/components/eam/eam.tsx b/frontend/src/components/eam/eam.tsx index 479bc2783..c52ba621a 100644 --- a/frontend/src/components/eam/eam.tsx +++ b/frontend/src/components/eam/eam.tsx @@ -1,13 +1,20 @@ import { FC } from 'react'; -import { Button, IconButton } from 'components/common/common'; -import { AppRoute, IconName } from 'common/enums/enums'; -import { useAppDispatch, useAppSelector, useEffect } from 'hooks/hooks'; +import { ConfirmDeletePopup } from 'components/common/common'; +import { EntityType } from 'common/enums/enums'; +import { + useAppDispatch, + useAppSelector, + useEffect, + useState, +} from 'hooks/hooks'; import { eam as eamActions } from 'store/actions'; -import { GroupsTable, WorkersTable, Tenant } from './components/components'; +import { GroupsTable, Tenant, WorkersTable } from './components/components'; import styles from './styles.module.scss'; const EAM: FC = () => { const dispatch = useAppDispatch(); + const [currentGroupId, setCurrentGroupId] = useState(null); + const [currentWorkerId, setCurrentWorkerId] = useState(null); const { tenantId } = useAppSelector(({ app }) => ({ tenantId: app.tenant?.id, @@ -35,73 +42,43 @@ const EAM: FC = () => { ); }, [dispatch, tenantId]); - const handleGroupDelete = (id: string): void => { - dispatch(eamActions.deleteGroup(id)); - }; + const handleGroupDelete = (id: string): void => setCurrentGroupId(id); - const handleWorkersDelete = (id: string): void => { - dispatch(eamActions.deleteWorker(id)); - }; + const handleWorkersDelete = (id: string): void => setCurrentWorkerId(id); - const handleWorkersReload = (): void => { - dispatch( - eamActions.loadWorkers({ - tenantId: tenantId as string, - from: 0, - count: 5, - }), - ); - }; - const handleGroupsReload = (): void => { - dispatch( - eamActions.loadGroups({ - tenantId: tenantId as string, - from: 0, - count: 5, - }), - ); + const handleCancelDelete = (): void => + currentGroupId ? setCurrentGroupId(null) : setCurrentWorkerId(null); + + const handleConfirmDelete = (): void => { + if (currentGroupId) { + dispatch(eamActions.deleteGroup(currentGroupId as string)); + setCurrentGroupId(null); + } else { + dispatch(eamActions.deleteWorker(currentWorkerId as string)); + setCurrentWorkerId(null); + } }; return ( -
    -

    - EAM -
    - Entity Access Management -

    - -
    - -
    - -
    -
    -
    - -
    - -
    + + ); }; diff --git a/frontend/src/components/eam/styles.module.scss b/frontend/src/components/eam/styles.module.scss index d028902be..9901f882f 100644 --- a/frontend/src/components/eam/styles.module.scss +++ b/frontend/src/components/eam/styles.module.scss @@ -32,15 +32,3 @@ .tableWrapper { margin: 0 0 50px; } - -.addWorkerBtn, -.addGroupBtn { - max-width: 200px; -} - -.buttonsBlock { - display: flex; - align-items: center; - gap: 14px; - width: 250px; -} diff --git a/frontend/src/components/sc/components/instances-table/instance-table.tsx b/frontend/src/components/sc/components/instances-table/instance-table.tsx index fb5af04d5..4a4c45669 100644 --- a/frontend/src/components/sc/components/instances-table/instance-table.tsx +++ b/frontend/src/components/sc/components/instances-table/instance-table.tsx @@ -5,10 +5,11 @@ import { useAppDispatch, usePagination, } from 'hooks/hooks'; -import { Table } from 'components/common/common'; +import { Table, Button, IconButton } from 'components/common/common'; import { getRows, getColumns } from './helpers/helpers'; -import { DataStatus, Pagination } from 'common/enums/enums'; +import { DataStatus, Pagination, AppRoute, IconName } from 'common/enums/enums'; import { sc as scActions } from 'store/actions'; +import styles from './styles.module.scss'; type Props = { onInstanceDelete: (id: string) => void; @@ -21,11 +22,7 @@ type Props = { }; }; -const InstancesTable: FC = ({ - children, - onInstanceDelete, - onKeyClick, -}) => { +const InstancesTable: FC = ({ onInstanceDelete, onKeyClick }) => { const dispatch = useAppDispatch(); const { instances, dataStatus, countItems } = useAppSelector(({ sc }) => ({ instances: sc.instances, @@ -50,6 +47,16 @@ const InstancesTable: FC = ({ from: Pagination.INITIAL_FROM_COUNT, }); + const handleReload = (): void => { + dispatch( + scActions.loadInstances({ + from: 0, + count: 5, + }), + ); + spacePagination.onReload(); + }; + const data = useMemo( () => getRows({ instances, onInstanceDelete, onKeyClick }), [instances, onInstanceDelete], @@ -66,7 +73,19 @@ const InstancesTable: FC = ({ pagination={spacePagination} isLoading={isLoading} > - {children} +
    + +
    ); }; diff --git a/frontend/src/components/sc/components/instances-table/styles.module.scss b/frontend/src/components/sc/components/instances-table/styles.module.scss new file mode 100644 index 000000000..9a6cb8ef1 --- /dev/null +++ b/frontend/src/components/sc/components/instances-table/styles.module.scss @@ -0,0 +1,10 @@ +.addInstanceBtn { + max-width: 200px; +} + +.buttonsBlock { + display: flex; + align-items: center; + gap: 14px; + width: 250px; +} diff --git a/frontend/src/components/sc/sc.tsx b/frontend/src/components/sc/sc.tsx index 7c71c816f..be2d3f1b5 100644 --- a/frontend/src/components/sc/sc.tsx +++ b/frontend/src/components/sc/sc.tsx @@ -1,13 +1,16 @@ import { FC } from 'react'; -import { AppRoute, IconName } from 'common/enums/enums'; -import { useAppDispatch, useEffect } from 'hooks/hooks'; -import { sc as scActions } from 'store/actions'; +import { EntityType } from 'common/enums/enums'; +import { useAppDispatch, useEffect, useState } from 'hooks/hooks'; import { InstancesTable } from './components/components'; -import { Button, IconButton } from 'components/common/common'; +import { ConfirmDeletePopup } from 'components/common/common'; +import { sc as scActions } from 'store/actions'; import styles from './styles.module.scss'; const SC: FC = () => { const dispatch = useAppDispatch(); + const [currentInstanceId, setCurrentInstanceId] = useState( + null, + ); useEffect(() => { dispatch( @@ -18,50 +21,40 @@ const SC: FC = () => { ); }, [dispatch]); - const handleDeleteInstance = (id: string): void => { - dispatch(scActions.deleteInstance(id)); + const handleDeleteInstance = (id: string): void => setCurrentInstanceId(id); + + const handleCancelDelete = (): void => setCurrentInstanceId(null); + + const handleConfirmDelete = (): void => { + dispatch(scActions.deleteInstance(currentInstanceId as string)); + setCurrentInstanceId(null); }; const handleCopyKey = (keyId: string): void => { dispatch(scActions.loadSshKey(keyId)); }; - const handleReload = (): void => { - dispatch( - scActions.loadInstances({ - from: 0, - count: 5, - }), - ); - }; - return ( -
    -

    - SC -
    - Server Computing -

    -
    - -
    - -
    -
    + <> +
    +

    + SC -
    + Server Computing +

    +
    + +
    -
    + + ); }; diff --git a/frontend/src/components/sc/styles.module.scss b/frontend/src/components/sc/styles.module.scss index 80906505c..9c7e1a226 100644 --- a/frontend/src/components/sc/styles.module.scss +++ b/frontend/src/components/sc/styles.module.scss @@ -28,14 +28,3 @@ background-size: 40px; border-radius: 100%; } - -.addInstanceBtn { - max-width: 200px; -} - -.buttonsBlock { - display: flex; - align-items: center; - gap: 14px; - width: 250px; -} diff --git a/frontend/src/components/slc/components/functions-table/functions-table.tsx b/frontend/src/components/slc/components/functions-table/functions-table.tsx index 65939a83b..72d0087d8 100644 --- a/frontend/src/components/slc/components/functions-table/functions-table.tsx +++ b/frontend/src/components/slc/components/functions-table/functions-table.tsx @@ -5,10 +5,11 @@ import { useAppDispatch, usePagination, } from 'hooks/hooks'; -import { Table } from 'components/common/common'; +import { Table, Button, IconButton } from 'components/common/common'; import { getRows, getColumns } from './helpers/helpers'; -import { DataStatus, Pagination } from 'common/enums/enums'; +import { DataStatus, Pagination, AppRoute, IconName } from 'common/enums/enums'; import { slc as slcActions } from 'store/actions'; +import styles from './styles.module.scss'; type Props = { onFunctionDelete: (id: string) => void; @@ -20,7 +21,7 @@ type Props = { }; }; -const FunctionsTable: FC = ({ children, onFunctionDelete }) => { +const FunctionsTable: FC = ({ onFunctionDelete }) => { const { functions, dataStatus, countItems } = useAppSelector(({ slc }) => ({ functions: slc.functions, dataStatus: slc.dataStatus, @@ -45,6 +46,16 @@ const FunctionsTable: FC = ({ children, onFunctionDelete }) => { from: Pagination.INITIAL_FROM_COUNT, }); + const handleFunctionReload = (): void => { + dispatch( + slcActions.loadFunctions({ + from: 0, + count: 5, + }), + ); + functionPagination.onReload(); + }; + const data = useMemo( () => getRows({ slcFunctions: functions, onFunctionDelete }), [functions], @@ -61,7 +72,19 @@ const FunctionsTable: FC = ({ children, onFunctionDelete }) => { pagination={functionPagination} isLoading={isLoading} > - {children} +
    + +
    ); }; diff --git a/frontend/src/components/slc/components/functions-table/helpers/get-columns.helper.ts b/frontend/src/components/slc/components/functions-table/helpers/get-columns.helper.ts index dbb82cb6b..4621fed92 100644 --- a/frontend/src/components/slc/components/functions-table/helpers/get-columns.helper.ts +++ b/frontend/src/components/slc/components/functions-table/helpers/get-columns.helper.ts @@ -42,7 +42,7 @@ const getColumns = (): Column[] => { Header: FunctionsTableHeader.ACTIONS, accessor: FunctionsTableAccessor.ACTIONS, minWidth: 100, - width: 100, + width: 150, disableSortBy: true, }, ]; diff --git a/frontend/src/components/slc/components/functions-table/styles.module.scss b/frontend/src/components/slc/components/functions-table/styles.module.scss new file mode 100644 index 000000000..b63b31e99 --- /dev/null +++ b/frontend/src/components/slc/components/functions-table/styles.module.scss @@ -0,0 +1,10 @@ +.addFunctionBtn { + max-width: 200px; +} + +.buttonsBlock { + display: flex; + align-items: center; + gap: 14px; + width: 250px; +} diff --git a/frontend/src/components/slc/slc.tsx b/frontend/src/components/slc/slc.tsx index 7e381042e..a18a7fefe 100644 --- a/frontend/src/components/slc/slc.tsx +++ b/frontend/src/components/slc/slc.tsx @@ -1,13 +1,16 @@ import { FC } from 'react'; -import { AppRoute, IconName } from 'common/enums/enums'; -import { Button, IconButton } from 'components/common/common'; +import { EntityType } from 'common/enums/enums'; +import { ConfirmDeletePopup } from 'components/common/common'; import styles from './styles.module.scss'; import { FunctionsTable } from './components/components'; -import { useAppDispatch, useEffect } from 'hooks/hooks'; +import { useAppDispatch, useEffect, useState } from 'hooks/hooks'; import { slc as slcActions } from 'store/actions'; const SLC: FC = () => { const dispatch = useAppDispatch(); + const [currentFunctionId, setCurrentFunctionId] = useState( + null, + ); useEffect(() => { dispatch( @@ -18,42 +21,33 @@ const SLC: FC = () => { ); }, [dispatch]); - const handleFunctionDelete = (id: string): void => { - dispatch(slcActions.deleteFunction(id)); - }; - const handleFunctionReload = (): void => { - dispatch( - slcActions.loadFunctions({ - from: 0, - count: 5, - }), - ); + const handleFunctionDelete = (id: string): void => setCurrentFunctionId(id); + + const handleCancelDelete = (): void => setCurrentFunctionId(null); + + const handleConfirmDelete = (): void => { + dispatch(slcActions.deleteFunction(currentFunctionId as string)); + setCurrentFunctionId(null); }; return ( -
    -

    - SLC -
    - ServerLess Computing -

    -
    - -
    - -
    -
    + <> +
    +

    + SLC -
    + ServerLess Computing +

    +
    + +
    -
    + + ); }; diff --git a/frontend/src/components/slc/styles.module.scss b/frontend/src/components/slc/styles.module.scss index 09b2a7299..c8016fe2e 100644 --- a/frontend/src/components/slc/styles.module.scss +++ b/frontend/src/components/slc/styles.module.scss @@ -32,14 +32,3 @@ .tableWrapper { margin: 0 0 50px; } - -.addFunctionBtn { - max-width: 200px; -} - -.buttonsBlock { - display: flex; - align-items: center; - gap: 14px; - width: 250px; -} diff --git a/frontend/src/hooks/use-pagination/use-pagination.hook.ts b/frontend/src/hooks/use-pagination/use-pagination.hook.ts index aa83ef4d3..ada321f3a 100644 --- a/frontend/src/hooks/use-pagination/use-pagination.hook.ts +++ b/frontend/src/hooks/use-pagination/use-pagination.hook.ts @@ -1,6 +1,6 @@ import { UsePaginationtemsHook } from 'common/types/types'; import { Pagination } from 'common/enums/enums'; -import { useState } from 'hooks/hooks'; +import { useState, useEffect } from 'hooks/hooks'; type UsePaginationArgs = { perPageCount: number; @@ -40,12 +40,35 @@ const usePagination = ( } }; + const handleReload = (): void => { + setFrom(pagination.from ?? Pagination.INITIAL_FROM_COUNT); + setCurrentPage(pagination.currentPage ?? Pagination.INITIAL_CURRENT_PAGE); + }; + + useEffect(() => { + if (allPage > 0 && allPage < currentPage) { + setCurrentPage(currentPage - Pagination.INCREMENT); + pagination.onLoad( + from - pagination.perPageCount, + pagination.perPageCount, + ); + } + }, [ + allPage, + currentPage, + setCurrentPage, + pagination.onLoad, + from, + pagination.perPageCount, + ]); + return { onBackPage: handleBackPage, onNextPage: handleNextPage, allPage, currentPage, countItems: pagination.countItems, + onReload: handleReload, }; }; diff --git a/frontend/src/store/bs/reducer.ts b/frontend/src/store/bs/reducer.ts index ab042fb95..0d7929f77 100644 --- a/frontend/src/store/bs/reducer.ts +++ b/frontend/src/store/bs/reducer.ts @@ -2,6 +2,7 @@ import { BSSpaceGetResponseItemDto } from 'common/types/types'; import { DataStatus } from 'common/enums/app/data-status.enum'; import { createReducer } from '@reduxjs/toolkit'; import { deleteSpace, loadSpaces } from './actions'; +import { Pagination } from 'common/enums/enums'; import { logOut } from 'store/auth/actions'; type State = { @@ -34,6 +35,7 @@ const reducer = createReducer(initialState, (builder) => { builder.addCase(deleteSpace.fulfilled, (state, action) => { state.dataStatus = DataStatus.FULFILLED; state.spaces = state.spaces.filter((space) => space.id !== action.payload); + state.countItems = state.countItems - Pagination.INCREMENT; }); builder.addCase(deleteSpace.rejected, (state) => { state.dataStatus = DataStatus.REJECTED; diff --git a/frontend/src/store/eam-group-configurate/reducer.ts b/frontend/src/store/eam-group-configurate/reducer.ts index aeda73454..2330037ba 100644 --- a/frontend/src/store/eam-group-configurate/reducer.ts +++ b/frontend/src/store/eam-group-configurate/reducer.ts @@ -12,12 +12,14 @@ import { EAMPermissionGetAllItemResponseDto, EamGroupGetByIdResponseDto, } from 'common/types/types'; + type State = { dataStatus: DataStatus; permissionsDateStatus: DataStatus; workers: EAMWorkerGetAllItemResponseDto[]; permissions: EAMPermissionGetAllItemResponseDto[]; group: EamGroupGetByIdResponseDto | null; + workersCountItems: number; }; const initialState: State = { @@ -26,6 +28,7 @@ const initialState: State = { workers: [], permissions: [], group: null, + workersCountItems: 0, }; const reducer = createReducer(initialState, (builder) => { @@ -38,6 +41,7 @@ const reducer = createReducer(initialState, (builder) => { builder.addCase(loadWorkers.fulfilled, (state, action) => { state.dataStatus = DataStatus.FULFILLED; state.workers = action.payload.items; + state.workersCountItems = action.payload.countItems; }); builder.addCase(getPermission.fulfilled, (state, action) => { state.permissionsDateStatus = DataStatus.FULFILLED; diff --git a/frontend/src/store/eam-worker-configurate/reducer.ts b/frontend/src/store/eam-worker-configurate/reducer.ts index c70ac3a5b..c943805c0 100644 --- a/frontend/src/store/eam-worker-configurate/reducer.ts +++ b/frontend/src/store/eam-worker-configurate/reducer.ts @@ -7,12 +7,14 @@ type State = { dataStatus: DataStatus; groups: EAMGroupGetByTenantResponseItemDto[]; csvColumns: string[][]; + countItems: number; }; const initialState: State = { dataStatus: DataStatus.IDLE, groups: [], csvColumns: [], + countItems: 0, }; const reducer = createReducer(initialState, (builder) => { @@ -27,6 +29,7 @@ const reducer = createReducer(initialState, (builder) => { builder.addCase(getGroups.fulfilled, (state, action) => { state.dataStatus = DataStatus.FULFILLED; state.groups = action.payload.items; + state.countItems = action.payload.countItems; }); builder.addCase(cleanupCSV, (state) => { diff --git a/frontend/src/store/eam/reducer.ts b/frontend/src/store/eam/reducer.ts index 1c7de77f5..666921d6a 100644 --- a/frontend/src/store/eam/reducer.ts +++ b/frontend/src/store/eam/reducer.ts @@ -1,5 +1,5 @@ import { createReducer } from '@reduxjs/toolkit'; -import { DataStatus } from 'common/enums/enums'; +import { DataStatus, Pagination } from 'common/enums/enums'; import { deleteGroup, loadWorkers, loadGroups, deleteWorker } from './actions'; import { logOut } from 'store/auth/actions'; import { @@ -56,6 +56,7 @@ const reducer = createReducer(initialState, (builder) => { builder.addCase(deleteGroup.fulfilled, (state, action) => { state.dataStatus = DataStatus.FULFILLED; state.groups = state.groups.filter((group) => group.id !== action.payload); + state.groupsCountAll = state.groupsCountAll - Pagination.INCREMENT; }); builder.addCase(deleteGroup.rejected, (state) => { state.dataStatus = DataStatus.REJECTED; diff --git a/frontend/src/store/sc/reducer.ts b/frontend/src/store/sc/reducer.ts index 5a61b21bc..81b9b6d6c 100644 --- a/frontend/src/store/sc/reducer.ts +++ b/frontend/src/store/sc/reducer.ts @@ -2,6 +2,7 @@ import { SCInstanceGetByTenantResponseItemDto } from 'common/types/types'; import { DataStatus } from 'common/enums/app/data-status.enum'; import { createReducer } from '@reduxjs/toolkit'; import { deleteInstance, loadInstances } from './actions'; +import { Pagination } from 'common/enums/enums'; import { logOut } from 'store/auth/actions'; type State = { @@ -36,6 +37,7 @@ const reducer = createReducer(initialState, (builder) => { state.instances = state.instances.filter( (item) => item.id !== action.payload, ); + state.countItems = state.countItems - Pagination.INCREMENT; }); builder.addCase(deleteInstance.rejected, (state) => { state.dataStatus = DataStatus.REJECTED; diff --git a/frontend/src/store/slc/reducer.ts b/frontend/src/store/slc/reducer.ts index 47610d444..5ef80944f 100644 --- a/frontend/src/store/slc/reducer.ts +++ b/frontend/src/store/slc/reducer.ts @@ -2,6 +2,7 @@ import { SLCFunctionGetResponseItemDto } from 'common/types/types'; import { DataStatus } from 'common/enums/app/data-status.enum'; import { createReducer } from '@reduxjs/toolkit'; import { loadFunctions, deleteFunction } from './actions'; +import { Pagination } from 'common/enums/enums'; import { logOut } from 'store/auth/actions'; type State = { @@ -36,6 +37,7 @@ const reducer = createReducer(initialState, (builder) => { state.functions = state.functions.filter( (slcFunction) => slcFunction.id !== action.payload, ); + state.countItems = state.countItems - Pagination.INCREMENT; }); builder.addCase(deleteFunction.rejected, (state) => { state.dataStatus = DataStatus.REJECTED; diff --git a/shared/src/common/enums/http/http-code.enum.ts b/shared/src/common/enums/http/http-code.enum.ts index 6951ec60c..7e8a31e32 100644 --- a/shared/src/common/enums/http/http-code.enum.ts +++ b/shared/src/common/enums/http/http-code.enum.ts @@ -7,6 +7,7 @@ enum HttpCode { NOT_FOUND = 404, CONFLICT = 409, UNPROCESSABLE_ENTITY = 422, + LOCKED = 423, INTERNAL_SERVER_ERROR = 500, }