From bcb632687761daff4a0d933daada28816712fa34 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Mon, 19 Jan 2026 02:58:10 +0700 Subject: [PATCH] feat: add menu & action module, update fixing, etc --- app/database/entity/menu_actions.entity.go | 27 + .../entity/user_level_menu_accesses.entity.go | 24 + .../user_level_menu_action_accesses.entity.go | 25 + app/database/index.database.go | 3 + .../menu_action_access.middleware.go | 169 ++ .../mapper/master_menus.mapper.go | 1 + .../repository/master_menus.repository.go | 6 + .../request/master_menus.request.go | 24 +- .../response/master_menus.response.go | 1 + .../service/master_menus.service.go | 19 +- .../mapper/master_modules.mapper.go | 1 + .../request/master_modules.request.go | 24 +- .../response/master_modules.response.go | 1 + .../menu_actions/controller/controller.go | 16 + .../controller/menu_actions.controller.go | 260 +++ .../mapper/menu_actions.mapper.go | 26 + .../menu_actions/menu_actions.module.go | 56 + .../repository/menu_actions.repository.go | 140 ++ .../request/menu_actions.request.go | 73 + .../response/menu_actions.response.go | 18 + .../service/menu_actions.service.go | 158 ++ .../repository/menu_modules.repository.go | 23 +- .../request/menu_modules.request.go | 12 +- .../controller/controller.go | 16 + .../user_level_menu_accesses.controller.go | 328 +++ .../mapper/user_level_menu_accesses.mapper.go | 22 + .../user_level_menu_accesses.repository.go | 161 ++ .../user_level_menu_accesses.request.go | 64 + .../user_level_menu_accesses.response.go | 14 + .../user_level_menu_accesses.service.go | 151 ++ .../user_level_menu_accesses.module.go | 58 + .../controller/controller.go | 16 + ...r_level_menu_action_accesses.controller.go | 379 ++++ .../user_level_menu_action_accesses.mapper.go | 23 + ...r_level_menu_action_accesses.repository.go | 178 ++ ...user_level_menu_action_accesses.request.go | 72 + ...ser_level_menu_action_accesses.response.go | 15 + ...user_level_menu_action_accesses.service.go | 166 ++ .../user_level_menu_action_accesses.module.go | 59 + .../user_level_module_accesses.repository.go | 23 +- .../user_level_module_accesses.request.go | 10 +- app/router/api.go | 27 +- docs/PLAN_MENU_ACTION_ACCESS_SYSTEM.md | 311 +++ .../005_add_menu_action_access_system.sql | 195 ++ docs/swagger/docs.go | 1794 ++++++++++++++++- docs/swagger/swagger.json | 1794 ++++++++++++++++- docs/swagger/swagger.yaml | 1149 ++++++++++- main.go | 6 + 48 files changed, 8086 insertions(+), 52 deletions(-) create mode 100644 app/database/entity/menu_actions.entity.go create mode 100644 app/database/entity/user_level_menu_accesses.entity.go create mode 100644 app/database/entity/user_level_menu_action_accesses.entity.go create mode 100644 app/middleware/menu_action_access.middleware.go create mode 100644 app/module/menu_actions/controller/controller.go create mode 100644 app/module/menu_actions/controller/menu_actions.controller.go create mode 100644 app/module/menu_actions/mapper/menu_actions.mapper.go create mode 100644 app/module/menu_actions/menu_actions.module.go create mode 100644 app/module/menu_actions/repository/menu_actions.repository.go create mode 100644 app/module/menu_actions/request/menu_actions.request.go create mode 100644 app/module/menu_actions/response/menu_actions.response.go create mode 100644 app/module/menu_actions/service/menu_actions.service.go create mode 100644 app/module/user_level_menu_accesses/controller/controller.go create mode 100644 app/module/user_level_menu_accesses/controller/user_level_menu_accesses.controller.go create mode 100644 app/module/user_level_menu_accesses/mapper/user_level_menu_accesses.mapper.go create mode 100644 app/module/user_level_menu_accesses/repository/user_level_menu_accesses.repository.go create mode 100644 app/module/user_level_menu_accesses/request/user_level_menu_accesses.request.go create mode 100644 app/module/user_level_menu_accesses/response/user_level_menu_accesses.response.go create mode 100644 app/module/user_level_menu_accesses/service/user_level_menu_accesses.service.go create mode 100644 app/module/user_level_menu_accesses/user_level_menu_accesses.module.go create mode 100644 app/module/user_level_menu_action_accesses/controller/controller.go create mode 100644 app/module/user_level_menu_action_accesses/controller/user_level_menu_action_accesses.controller.go create mode 100644 app/module/user_level_menu_action_accesses/mapper/user_level_menu_action_accesses.mapper.go create mode 100644 app/module/user_level_menu_action_accesses/repository/user_level_menu_action_accesses.repository.go create mode 100644 app/module/user_level_menu_action_accesses/request/user_level_menu_action_accesses.request.go create mode 100644 app/module/user_level_menu_action_accesses/response/user_level_menu_action_accesses.response.go create mode 100644 app/module/user_level_menu_action_accesses/service/user_level_menu_action_accesses.service.go create mode 100644 app/module/user_level_menu_action_accesses/user_level_menu_action_accesses.module.go create mode 100644 docs/PLAN_MENU_ACTION_ACCESS_SYSTEM.md create mode 100644 docs/migrations/005_add_menu_action_access_system.sql diff --git a/app/database/entity/menu_actions.entity.go b/app/database/entity/menu_actions.entity.go new file mode 100644 index 0000000..ccdbc3b --- /dev/null +++ b/app/database/entity/menu_actions.entity.go @@ -0,0 +1,27 @@ +package entity + +import ( + "github.com/google/uuid" + "time" +) + +// MenuActions menyimpan actions yang tersedia di setiap menu +// Contoh: Menu "Content Management" punya actions: view, create, edit, delete, approve, export +type MenuActions struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + MenuId uint `json:"menu_id" gorm:"type:int4;not null"` + ActionCode string `json:"action_code" gorm:"type:varchar(50);not null"` // 'view', 'create', 'edit', 'delete', 'approve', 'export' + ActionName string `json:"action_name" gorm:"type:varchar(255);not null"` // 'View Content', 'Create Content', etc. + Description *string `json:"description" gorm:"type:text"` + PathUrl *string `json:"path_url" gorm:"type:varchar(255)"` // Optional: untuk routing frontend + HttpMethod *string `json:"http_method" gorm:"type:varchar(10)"` // Optional: 'GET', 'POST', 'PUT', 'DELETE' + Position *int `json:"position" gorm:"type:int4"` + ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + + // Relations + Menu *MasterMenus `json:"menu,omitempty" gorm:"foreignKey:MenuId"` +} + diff --git a/app/database/entity/user_level_menu_accesses.entity.go b/app/database/entity/user_level_menu_accesses.entity.go new file mode 100644 index 0000000..780fe0a --- /dev/null +++ b/app/database/entity/user_level_menu_accesses.entity.go @@ -0,0 +1,24 @@ +package entity + +import ( + "github.com/google/uuid" + "time" +) + +// UserLevelMenuAccesses mengatur akses user_level ke menu tertentu +// Contoh: UserLevel "Admin Pusat" bisa akses semua menu, "Editor" hanya bisa akses "Content Management" +type UserLevelMenuAccesses struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserLevelId uint `json:"user_level_id" gorm:"type:int4;not null"` + MenuId uint `json:"menu_id" gorm:"type:int4;not null"` + CanAccess bool `json:"can_access" gorm:"type:bool;default:true"` // Apakah boleh akses menu ini + ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + + // Relations + UserLevel *UserLevels `json:"user_level,omitempty" gorm:"foreignKey:UserLevelId"` + Menu *MasterMenus `json:"menu,omitempty" gorm:"foreignKey:MenuId"` +} + diff --git a/app/database/entity/user_level_menu_action_accesses.entity.go b/app/database/entity/user_level_menu_action_accesses.entity.go new file mode 100644 index 0000000..4d5c246 --- /dev/null +++ b/app/database/entity/user_level_menu_action_accesses.entity.go @@ -0,0 +1,25 @@ +package entity + +import ( + "github.com/google/uuid" + "time" +) + +// UserLevelMenuActionAccesses mengatur akses user_level ke action tertentu di dalam menu +// Contoh: UserLevel "Creator" di menu "Content Management" hanya bisa create dan edit, tidak bisa delete +type UserLevelMenuActionAccesses struct { + ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` + UserLevelId uint `json:"user_level_id" gorm:"type:int4;not null"` + MenuId uint `json:"menu_id" gorm:"type:int4;not null"` + ActionCode string `json:"action_code" gorm:"type:varchar(50);not null"` // 'view', 'create', 'edit', 'delete', etc. + CanAccess bool `json:"can_access" gorm:"type:bool;default:true"` // Apakah boleh melakukan action ini + ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` + IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` + CreatedAt time.Time `json:"created_at" gorm:"default:now()"` + UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` + + // Relations + UserLevel *UserLevels `json:"user_level,omitempty" gorm:"foreignKey:UserLevelId"` + Menu *MasterMenus `json:"menu,omitempty" gorm:"foreignKey:MenuId"` +} + diff --git a/app/database/index.database.go b/app/database/index.database.go index f5600b0..e5243f1 100644 --- a/app/database/index.database.go +++ b/app/database/index.database.go @@ -127,6 +127,9 @@ func Models() []interface{} { entity.MagazineFiles{}, entity.MasterMenus{}, entity.MasterModules{}, + entity.MenuActions{}, // New: Menu actions (view, create, edit, delete, etc) + entity.UserLevelMenuAccesses{}, // New: User level menu access control + entity.UserLevelMenuActionAccesses{}, // New: User level menu action access control entity.MasterStatuses{}, entity.MasterApprovalStatuses{}, entity.Provinces{}, diff --git a/app/middleware/menu_action_access.middleware.go b/app/middleware/menu_action_access.middleware.go new file mode 100644 index 0000000..1e1db08 --- /dev/null +++ b/app/middleware/menu_action_access.middleware.go @@ -0,0 +1,169 @@ +package middleware + +import ( + "netidhub-saas-be/app/database" + "netidhub-saas-be/app/database/entity" + + "github.com/gofiber/fiber/v2" +) + +type MenuActionAccessMiddleware struct { + DB *database.Database +} + +func NewMenuActionAccessMiddleware(db *database.Database) *MenuActionAccessMiddleware { + return &MenuActionAccessMiddleware{ + DB: db, + } +} + +// CheckMenuAccess middleware untuk validasi akses user_level ke menu tertentu +func (m *MenuActionAccessMiddleware) CheckMenuAccess(menuId uint) fiber.Handler { + return func(c *fiber.Ctx) error { + // Get user from context + userCtx := c.Locals("user") + if userCtx == nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "code": 401, + "messages": []string{"User tidak terautentikasi"}, + }) + } + + user, ok := userCtx.(*entity.Users) + if !ok || user == nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "code": 401, + "messages": []string{"User tidak valid"}, + }) + } + + // Get user role untuk mendapatkan user_level_id + var userRole entity.UserRoles + if err := m.DB.DB.Where("id = ?", user.UserRoleId).First(&userRole).Error; err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "code": 500, + "messages": []string{"Error mendapatkan user role"}, + "error": err.Error(), + }) + } + + userLevelId := userRole.UserLevelId + + // Check akses user_level ke menu + var access entity.UserLevelMenuAccesses + err := m.DB.DB.Where( + "user_level_id = ? AND menu_id = ? AND is_active = ? AND can_access = ?", + userLevelId, + menuId, + true, + true, + ).First(&access).Error + + if err != nil { + // Jika tidak ada record, berarti tidak ada akses + return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ + "success": false, + "code": 403, + "messages": []string{"Anda tidak memiliki akses ke menu ini"}, + "user_level_id": userLevelId, + "menu_id": menuId, + }) + } + + // Set menu ke context + c.Locals("menu_id", menuId) + c.Locals("user_level_id", userLevelId) + + return c.Next() + } +} + +// CheckMenuActionAccess middleware untuk validasi akses user_level ke action tertentu di dalam menu +func (m *MenuActionAccessMiddleware) CheckMenuActionAccess(menuId uint, actionCode string) fiber.Handler { + return func(c *fiber.Ctx) error { + // Get user from context + userCtx := c.Locals("user") + if userCtx == nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "code": 401, + "messages": []string{"User tidak terautentikasi"}, + }) + } + + user, ok := userCtx.(*entity.Users) + if !ok || user == nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "code": 401, + "messages": []string{"User tidak valid"}, + }) + } + + // Get user role untuk mendapatkan user_level_id + var userRole entity.UserRoles + if err := m.DB.DB.Where("id = ?", user.UserRoleId).First(&userRole).Error; err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "code": 500, + "messages": []string{"Error mendapatkan user role"}, + "error": err.Error(), + }) + } + + userLevelId := userRole.UserLevelId + + // First, check if user has access to the menu + var menuAccess entity.UserLevelMenuAccesses + err := m.DB.DB.Where( + "user_level_id = ? AND menu_id = ? AND is_active = ? AND can_access = ?", + userLevelId, + menuId, + true, + true, + ).First(&menuAccess).Error + + if err != nil { + return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ + "success": false, + "code": 403, + "messages": []string{"Anda tidak memiliki akses ke menu ini"}, + "user_level_id": userLevelId, + "menu_id": menuId, + }) + } + + // Then, check if user has access to the specific action + var actionAccess entity.UserLevelMenuActionAccesses + err = m.DB.DB.Where( + "user_level_id = ? AND menu_id = ? AND action_code = ? AND is_active = ? AND can_access = ?", + userLevelId, + menuId, + actionCode, + true, + true, + ).First(&actionAccess).Error + + if err != nil { + return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ + "success": false, + "code": 403, + "messages": []string{"Anda tidak memiliki akses untuk melakukan action ini"}, + "user_level_id": userLevelId, + "menu_id": menuId, + "action_code": actionCode, + }) + } + + // Set to context + c.Locals("menu_id", menuId) + c.Locals("action_code", actionCode) + c.Locals("user_level_id", userLevelId) + + return c.Next() + } +} + diff --git a/app/module/master_menus/mapper/master_menus.mapper.go b/app/module/master_menus/mapper/master_menus.mapper.go index ad34d80..a6d04bb 100644 --- a/app/module/master_menus/mapper/master_menus.mapper.go +++ b/app/module/master_menus/mapper/master_menus.mapper.go @@ -13,6 +13,7 @@ func MasterMenusResponseMapper(masterMenusReq *entity.MasterMenus) (masterMenusR Description: masterMenusReq.Description, ModuleId: masterMenusReq.ModuleId, ParentMenuId: masterMenusReq.ParentMenuId, + Group: masterMenusReq.Group, Icon: masterMenusReq.Icon, Position: masterMenusReq.Position, StatusId: masterMenusReq.StatusId, diff --git a/app/module/master_menus/repository/master_menus.repository.go b/app/module/master_menus/repository/master_menus.repository.go index 97002a4..9234813 100644 --- a/app/module/master_menus/repository/master_menus.repository.go +++ b/app/module/master_menus/repository/master_menus.repository.go @@ -7,6 +7,8 @@ import ( "netidhub-saas-be/app/module/master_menus/request" "netidhub-saas-be/utils/paginator" "strings" + + "gorm.io/gorm" ) type masterMenusRepository struct { @@ -87,6 +89,10 @@ func (_i *masterMenusRepository) FindOne(id uint) (masterMenus *entity.MasterMen func (_i *masterMenusRepository) FindLastMenuPosition() (position *int, err error) { var masterMenus *entity.MasterMenus if err := _i.DB.DB.Where("position IS NOT NULL").Order(fmt.Sprintf("%s %s", "position", "DESC")).First(&masterMenus).Error; err != nil { + // If no record found, return nil without error (it's expected when no menus exist yet) + if err == gorm.ErrRecordNotFound { + return nil, nil + } return nil, err } diff --git a/app/module/master_menus/request/master_menus.request.go b/app/module/master_menus/request/master_menus.request.go index a50338f..8c76399 100644 --- a/app/module/master_menus/request/master_menus.request.go +++ b/app/module/master_menus/request/master_menus.request.go @@ -23,18 +23,22 @@ type MasterMenusQueryRequest struct { type MasterMenusCreateRequest struct { Name string `json:"name" validate:"required"` Description string `json:"description" validate:"required"` - ModuleId int `json:"moduleId" validate:"required"` + ModuleId *int `json:"moduleId,omitempty"` Group string `json:"group" validate:"required"` StatusId int `json:"statusId" validate:"required"` - ParentMenuId *int `json:"parentMenuId"` - Icon *string `json:"icon"` + ParentMenuId *int `json:"parentMenuId,omitempty"` + Icon *string `json:"icon,omitempty"` } func (req MasterMenusCreateRequest) ToEntity() *entity.MasterMenus { + moduleId := 0 + if req.ModuleId != nil { + moduleId = *req.ModuleId + } return &entity.MasterMenus{ Name: req.Name, Description: req.Description, - ModuleId: req.ModuleId, + ModuleId: moduleId, ParentMenuId: req.ParentMenuId, Icon: req.Icon, Group: req.Group, @@ -46,20 +50,24 @@ type MasterMenusUpdateRequest struct { ID uint `json:"id" validate:"required"` Name string `json:"name" validate:"required"` Description string `json:"description" validate:"required"` - ModuleId int `json:"moduleId" validate:"required"` + ModuleId *int `json:"moduleId,omitempty"` Group string `json:"group" validate:"required"` StatusId int `json:"statusId" validate:"required"` - ParentMenuId *int `json:"parentMenuId"` - Icon *string `json:"icon"` + ParentMenuId *int `json:"parentMenuId,omitempty"` + Icon *string `json:"icon,omitempty"` UpdatedAt time.Time `json:"updatedAt"` } func (req MasterMenusUpdateRequest) ToEntity() *entity.MasterMenus { + moduleId := 0 + if req.ModuleId != nil { + moduleId = *req.ModuleId + } return &entity.MasterMenus{ ID: req.ID, Name: req.Name, Description: req.Description, - ModuleId: req.ModuleId, + ModuleId: moduleId, ParentMenuId: req.ParentMenuId, Icon: req.Icon, Group: req.Group, diff --git a/app/module/master_menus/response/master_menus.response.go b/app/module/master_menus/response/master_menus.response.go index 3f814df..fe78613 100644 --- a/app/module/master_menus/response/master_menus.response.go +++ b/app/module/master_menus/response/master_menus.response.go @@ -8,6 +8,7 @@ type MasterMenusResponse struct { Description string `json:"description"` ModuleId int `json:"module_id"` ParentMenuId *int `json:"parent_menu_id"` + Group string `json:"group"` Icon *string `json:"icon"` Position *int `json:"position"` StatusId int `json:"status_id"` diff --git a/app/module/master_menus/service/master_menus.service.go b/app/module/master_menus/service/master_menus.service.go index 3542447..bd4ae74 100644 --- a/app/module/master_menus/service/master_menus.service.go +++ b/app/module/master_menus/service/master_menus.service.go @@ -60,14 +60,19 @@ func (_i *masterMenusService) Save(req request.MasterMenusCreateRequest) (err er _i.Log.Info().Interface("data", req).Msg("") newReq := req.ToEntity() - var latestPosition, _ = _i.Repo.FindLastMenuPosition() + latestPosition, err := _i.Repo.FindLastMenuPosition() if err != nil { - return err - } - *latestPosition = *latestPosition + 1 - - if latestPosition != nil { - newReq.Position = latestPosition + // If no menu with position exists, start with position 1 + position := 1 + newReq.Position = &position + } else if latestPosition != nil { + // Increment the latest position + newPosition := *latestPosition + 1 + newReq.Position = &newPosition + } else { + // Fallback: set position to 1 + position := 1 + newReq.Position = &position } return _i.Repo.Create(newReq) diff --git a/app/module/master_modules/mapper/master_modules.mapper.go b/app/module/master_modules/mapper/master_modules.mapper.go index f316bce..70d3df8 100644 --- a/app/module/master_modules/mapper/master_modules.mapper.go +++ b/app/module/master_modules/mapper/master_modules.mapper.go @@ -12,6 +12,7 @@ func MasterModulesResponseMapper(masterModulesReq *entity.MasterModules) (master Name: masterModulesReq.Name, Description: masterModulesReq.Description, PathUrl: masterModulesReq.PathUrl, + ActionType: masterModulesReq.ActionType, StatusId: masterModulesReq.StatusId, IsActive: masterModulesReq.IsActive, CreatedAt: masterModulesReq.CreatedAt, diff --git a/app/module/master_modules/request/master_modules.request.go b/app/module/master_modules/request/master_modules.request.go index d774c5a..21f32c9 100644 --- a/app/module/master_modules/request/master_modules.request.go +++ b/app/module/master_modules/request/master_modules.request.go @@ -19,10 +19,12 @@ type MasterModulesQueryRequest struct { } type MasterModulesCreateRequest struct { - Name string `json:"name" validate:"required"` - Description string `json:"description" validate:"required"` - PathUrl string `json:"pathUrl" validate:"required"` - StatusId int `json:"statusId" validate:"required"` + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"required"` + PathUrl string `json:"pathUrl" validate:"required"` + ActionType *string `json:"actionType"` + StatusId int `json:"statusId" validate:"required"` + MenuIds []int `json:"menuIds"` // Optional: untuk langsung assign ke menu saat create } func (req MasterModulesCreateRequest) ToEntity() *entity.MasterModules { @@ -30,16 +32,19 @@ func (req MasterModulesCreateRequest) ToEntity() *entity.MasterModules { Name: req.Name, Description: req.Description, PathUrl: req.PathUrl, + ActionType: req.ActionType, StatusId: req.StatusId, } } type MasterModulesUpdateRequest struct { - ID uint `json:"id" validate:"required"` - Name string `json:"name" validate:"required"` - Description string `json:"description" validate:"required"` - PathUrl string `json:"pathUrl" validate:"required"` - StatusId int `json:"statusId" validate:"required"` + ID uint `json:"id" validate:"required"` + Name string `json:"name" validate:"required"` + Description string `json:"description" validate:"required"` + PathUrl string `json:"pathUrl" validate:"required"` + ActionType *string `json:"actionType"` + StatusId int `json:"statusId" validate:"required"` + MenuIds []int `json:"menuIds"` // Optional: untuk update relasi dengan menu } func (req MasterModulesUpdateRequest) ToEntity() *entity.MasterModules { @@ -48,6 +53,7 @@ func (req MasterModulesUpdateRequest) ToEntity() *entity.MasterModules { Name: req.Name, Description: req.Description, PathUrl: req.PathUrl, + ActionType: req.ActionType, StatusId: req.StatusId, UpdatedAt: time.Now(), } diff --git a/app/module/master_modules/response/master_modules.response.go b/app/module/master_modules/response/master_modules.response.go index 9a80bf0..5ec79e2 100644 --- a/app/module/master_modules/response/master_modules.response.go +++ b/app/module/master_modules/response/master_modules.response.go @@ -7,6 +7,7 @@ type MasterModulesResponse struct { Name string `json:"name"` Description string `json:"description"` PathUrl string `json:"path_url"` + ActionType *string `json:"action_type"` StatusId int `json:"status_id"` IsActive *bool `json:"is_active"` CreatedAt time.Time `json:"created_at"` diff --git a/app/module/menu_actions/controller/controller.go b/app/module/menu_actions/controller/controller.go new file mode 100644 index 0000000..0ff0644 --- /dev/null +++ b/app/module/menu_actions/controller/controller.go @@ -0,0 +1,16 @@ +package controller + +import ( + "netidhub-saas-be/app/module/menu_actions/service" +) + +type Controller struct { + MenuActions MenuActionsController +} + +func NewController(menuActionsService service.MenuActionsService) *Controller { + return &Controller{ + MenuActions: NewMenuActionsController(menuActionsService), + } +} + diff --git a/app/module/menu_actions/controller/menu_actions.controller.go b/app/module/menu_actions/controller/menu_actions.controller.go new file mode 100644 index 0000000..13ed166 --- /dev/null +++ b/app/module/menu_actions/controller/menu_actions.controller.go @@ -0,0 +1,260 @@ +package controller + +import ( + "netidhub-saas-be/app/module/menu_actions/request" + "netidhub-saas-be/app/module/menu_actions/service" + "netidhub-saas-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "netidhub-saas-be/utils/response" + utilVal "netidhub-saas-be/utils/validator" +) + +type menuActionsController struct { + menuActionsService service.MenuActionsService +} + +type MenuActionsController interface { + All(c *fiber.Ctx) error + GetByMenuId(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + SaveBatch(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewMenuActionsController(menuActionsService service.MenuActionsService) MenuActionsController { + return &menuActionsController{ + menuActionsService: menuActionsService, + } +} + +// All MenuActions +// @Summary Get all MenuActions +// @Description API for getting all MenuActions +// @Tags MenuActions +// @Security Bearer +// @Param menu_id query int false "Menu ID" +// @Param action_code query string false "Action Code" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /menu-actions [get] +func (_i *menuActionsController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + req := request.MenuActionsQueryRequest{ + Pagination: paginate, + } + + if menuId := c.Query("menu_id"); menuId != "" { + id, _ := strconv.ParseUint(menuId, 10, 0) + menuIdUint := uint(id) + req.MenuId = &menuIdUint + } + + if actionCode := c.Query("action_code"); actionCode != "" { + req.ActionCode = &actionCode + } + + menuActionsData, paging, err := _i.menuActionsService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MenuActions list successfully retrieved"}, + Data: menuActionsData, + Meta: paging, + }) +} + +// GetByMenuId get MenuActions by Menu ID +// @Summary Get MenuActions by Menu ID +// @Description API for getting MenuActions by Menu ID +// @Tags MenuActions +// @Security Bearer +// @Param menu_id path int true "Menu ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /menu-actions/menu/{menu_id} [get] +func (_i *menuActionsController) GetByMenuId(c *fiber.Ctx) error { + menuId, err := strconv.ParseUint(c.Params("menu_id"), 10, 0) + if err != nil { + return err + } + + menuActionsData, err := _i.menuActionsService.GetByMenuId(uint(menuId)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MenuActions by menu successfully retrieved"}, + Data: menuActionsData, + }) +} + +// Show get one MenuAction +// @Summary Get one MenuAction +// @Description API for getting one MenuAction +// @Tags MenuActions +// @Security Bearer +// @Param id path int true "MenuAction ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /menu-actions/{id} [get] +func (_i *menuActionsController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + menuActionData, err := _i.menuActionsService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MenuAction successfully retrieved"}, + Data: menuActionData, + }) +} + +// Save create MenuAction +// @Summary Create MenuAction +// @Description API for create MenuAction +// @Tags MenuActions +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param payload body request.MenuActionsCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /menu-actions [post] +func (_i *menuActionsController) Save(c *fiber.Ctx) error { + req := new(request.MenuActionsCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.menuActionsService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MenuAction successfully created"}, + }) +} + +// SaveBatch create MenuActions batch +// @Summary Create MenuActions batch +// @Description API for create MenuActions batch +// @Tags MenuActions +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param payload body request.MenuActionsBatchCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /menu-actions/batch [post] +func (_i *menuActionsController) SaveBatch(c *fiber.Ctx) error { + req := new(request.MenuActionsBatchCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.menuActionsService.SaveBatch(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MenuActions batch successfully created"}, + }) +} + +// Update MenuAction +// @Summary Update MenuAction +// @Description API for update MenuAction +// @Tags MenuActions +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param id path int true "MenuAction ID" +// @Param payload body request.MenuActionsUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /menu-actions/{id} [put] +func (_i *menuActionsController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.MenuActionsUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.menuActionsService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MenuAction successfully updated"}, + }) +} + +// Delete MenuAction +// @Summary Delete MenuAction +// @Description API for delete MenuAction +// @Tags MenuActions +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param id path int true "MenuAction ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /menu-actions/{id} [delete] +func (_i *menuActionsController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.menuActionsService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"MenuAction successfully deleted"}, + }) +} + diff --git a/app/module/menu_actions/mapper/menu_actions.mapper.go b/app/module/menu_actions/mapper/menu_actions.mapper.go new file mode 100644 index 0000000..ffc5a95 --- /dev/null +++ b/app/module/menu_actions/mapper/menu_actions.mapper.go @@ -0,0 +1,26 @@ +package mapper + +import ( + "netidhub-saas-be/app/database/entity" + res "netidhub-saas-be/app/module/menu_actions/response" +) + +func MenuActionsResponseMapper(menuActionReq *entity.MenuActions) (menuActionRes *res.MenuActionsResponse) { + if menuActionReq != nil { + menuActionRes = &res.MenuActionsResponse{ + ID: menuActionReq.ID, + MenuId: menuActionReq.MenuId, + ActionCode: menuActionReq.ActionCode, + ActionName: menuActionReq.ActionName, + Description: menuActionReq.Description, + PathUrl: menuActionReq.PathUrl, + HttpMethod: menuActionReq.HttpMethod, + Position: menuActionReq.Position, + IsActive: menuActionReq.IsActive, + CreatedAt: menuActionReq.CreatedAt, + UpdatedAt: menuActionReq.UpdatedAt, + } + } + return menuActionRes +} + diff --git a/app/module/menu_actions/menu_actions.module.go b/app/module/menu_actions/menu_actions.module.go new file mode 100644 index 0000000..88698db --- /dev/null +++ b/app/module/menu_actions/menu_actions.module.go @@ -0,0 +1,56 @@ +package menu_actions + +import ( + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" + "netidhub-saas-be/app/module/menu_actions/controller" + "netidhub-saas-be/app/module/menu_actions/repository" + "netidhub-saas-be/app/module/menu_actions/service" +) + +// struct of MenuActionsRouter +type MenuActionsRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of MenuActions module +var NewMenuActionsModule = fx.Options( + // register repository of MenuActions module + fx.Provide(repository.NewMenuActionsRepository), + + // register service of MenuActions module + fx.Provide(service.NewMenuActionsService), + + // register controller of MenuActions module + fx.Provide(controller.NewController), + + // register router of MenuActions module + fx.Provide(NewMenuActionsRouter), +) + +// init MenuActionsRouter +func NewMenuActionsRouter(fiber *fiber.App, controller *controller.Controller) *MenuActionsRouter { + return &MenuActionsRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of MenuActions module +func (_i *MenuActionsRouter) RegisterMenuActionsRoutes() { + // define controllers + menuActionsController := _i.Controller.MenuActions + + // define routes + _i.App.Route("/menu-actions", func(router fiber.Router) { + router.Get("/", menuActionsController.All) + router.Get("/:id", menuActionsController.Show) + router.Get("/menu/:menu_id", menuActionsController.GetByMenuId) + router.Post("/", menuActionsController.Save) + router.Post("/batch", menuActionsController.SaveBatch) + router.Put("/:id", menuActionsController.Update) + router.Delete("/:id", menuActionsController.Delete) + }) +} + diff --git a/app/module/menu_actions/repository/menu_actions.repository.go b/app/module/menu_actions/repository/menu_actions.repository.go new file mode 100644 index 0000000..22d6441 --- /dev/null +++ b/app/module/menu_actions/repository/menu_actions.repository.go @@ -0,0 +1,140 @@ +package repository + +import ( + "fmt" + "netidhub-saas-be/app/database" + "netidhub-saas-be/app/database/entity" + "netidhub-saas-be/app/module/menu_actions/request" + "netidhub-saas-be/utils/paginator" +) + +type menuActionsRepository struct { + DB *database.Database +} + +// MenuActionsRepository define interface of IMenuActionsRepository +type MenuActionsRepository interface { + GetAll(req request.MenuActionsQueryRequest) (menuActions []*entity.MenuActions, paging paginator.Pagination, err error) + GetByMenuId(menuId uint) (menuActions []*entity.MenuActions, err error) + GetByActionCode(menuId uint, actionCode string) (menuAction *entity.MenuActions, err error) + FindOne(id uint) (menuAction *entity.MenuActions, err error) + Create(menuAction *entity.MenuActions) (err error) + CreateBatch(menuActions []*entity.MenuActions) (err error) + Update(id uint, menuAction *entity.MenuActions) (err error) + Delete(id uint) (err error) + DeleteByMenuId(menuId uint) (err error) +} + +func NewMenuActionsRepository(db *database.Database) MenuActionsRepository { + return &menuActionsRepository{ + DB: db, + } +} + +// implement interface of IMenuActionsRepository +func (_i *menuActionsRepository) GetAll(req request.MenuActionsQueryRequest) (menuActions []*entity.MenuActions, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.MenuActions{}) + query = query.Where("is_active = ?", true) + + if req.MenuId != nil { + query = query.Where("menu_id = ?", req.MenuId) + } + if req.ActionCode != nil && *req.ActionCode != "" { + query = query.Where("action_code = ?", req.ActionCode) + } + if req.ClientId != nil { + query = query.Where("client_id = ?", req.ClientId) + } + + // Preload relations + query = query.Preload("Menu") + + query.Count(&count) + + // Apply sorting + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } else { + query.Order("position ASC") + } + + // Apply pagination (manual calculation like articles) + page := req.Pagination.Page + limit := req.Pagination.Limit + if page <= 0 { + page = 1 + } + if limit <= 0 { + limit = 10 + } + + offset := (page - 1) * limit + err = query.Offset(offset).Limit(limit).Find(&menuActions).Error + if err != nil { + return + } + + // Create pagination response + paging = paginator.Pagination{ + Page: page, + Limit: limit, + Count: count, + TotalPage: int((count + int64(limit) - 1) / int64(limit)), + } + + return +} + +func (_i *menuActionsRepository) GetByMenuId(menuId uint) (menuActions []*entity.MenuActions, err error) { + query := _i.DB.DB.Model(&entity.MenuActions{}) + query = query.Where("menu_id = ? AND is_active = ?", menuId, true) + query = query.Preload("Menu") + query = query.Order("position ASC") + err = query.Find(&menuActions).Error + return +} + +func (_i *menuActionsRepository) GetByActionCode(menuId uint, actionCode string) (menuAction *entity.MenuActions, err error) { + query := _i.DB.DB.Model(&entity.MenuActions{}) + query = query.Where("menu_id = ? AND action_code = ? AND is_active = ?", menuId, actionCode, true) + query = query.Preload("Menu") + err = query.First(&menuAction).Error + return +} + +func (_i *menuActionsRepository) FindOne(id uint) (menuAction *entity.MenuActions, err error) { + query := _i.DB.DB.Preload("Menu") + if err := query.First(&menuAction, id).Error; err != nil { + return nil, err + } + return menuAction, nil +} + +func (_i *menuActionsRepository) Create(menuAction *entity.MenuActions) (err error) { + return _i.DB.DB.Create(menuAction).Error +} + +func (_i *menuActionsRepository) CreateBatch(menuActions []*entity.MenuActions) (err error) { + return _i.DB.DB.Create(&menuActions).Error +} + +func (_i *menuActionsRepository) Update(id uint, menuAction *entity.MenuActions) (err error) { + return _i.DB.DB.Model(&entity.MenuActions{}). + Where(&entity.MenuActions{ID: id}). + Updates(menuAction).Error +} + +func (_i *menuActionsRepository) Delete(id uint) (err error) { + return _i.DB.DB.Delete(&entity.MenuActions{}, id).Error +} + +func (_i *menuActionsRepository) DeleteByMenuId(menuId uint) (err error) { + return _i.DB.DB.Where("menu_id = ?", menuId).Delete(&entity.MenuActions{}).Error +} + diff --git a/app/module/menu_actions/request/menu_actions.request.go b/app/module/menu_actions/request/menu_actions.request.go new file mode 100644 index 0000000..2d63ddc --- /dev/null +++ b/app/module/menu_actions/request/menu_actions.request.go @@ -0,0 +1,73 @@ +package request + +import ( + "netidhub-saas-be/app/database/entity" + "netidhub-saas-be/utils/paginator" + "github.com/google/uuid" +) + +type MenuActionsQueryRequest struct { + MenuId *uint `query:"menu_id"` + ActionCode *string `query:"action_code"` + ClientId *uuid.UUID `query:"client_id"` + Pagination *paginator.Pagination `query:"pagination"` +} + +type MenuActionsCreateRequest struct { + MenuId uint `json:"menuId" validate:"required"` + ActionCode string `json:"actionCode" validate:"required"` + ActionName string `json:"actionName" validate:"required"` + Description *string `json:"description"` + PathUrl *string `json:"pathUrl"` + HttpMethod *string `json:"httpMethod"` + Position *int `json:"position"` + ClientId *uuid.UUID `json:"clientId"` + IsActive *bool `json:"isActive"` +} + +func (req MenuActionsCreateRequest) ToEntity() *entity.MenuActions { + return &entity.MenuActions{ + MenuId: req.MenuId, + ActionCode: req.ActionCode, + ActionName: req.ActionName, + Description: req.Description, + PathUrl: req.PathUrl, + HttpMethod: req.HttpMethod, + Position: req.Position, + ClientId: req.ClientId, + IsActive: req.IsActive, + } +} + +type MenuActionsUpdateRequest struct { + MenuId uint `json:"menuId"` + ActionCode string `json:"actionCode"` + ActionName string `json:"actionName" validate:"required"` + Description *string `json:"description"` + PathUrl *string `json:"pathUrl"` + HttpMethod *string `json:"httpMethod"` + Position *int `json:"position"` + ClientId *uuid.UUID `json:"clientId"` + IsActive *bool `json:"isActive"` +} + +func (req MenuActionsUpdateRequest) ToEntity() *entity.MenuActions { + return &entity.MenuActions{ + MenuId: req.MenuId, + ActionCode: req.ActionCode, + ActionName: req.ActionName, + Description: req.Description, + PathUrl: req.PathUrl, + HttpMethod: req.HttpMethod, + Position: req.Position, + ClientId: req.ClientId, + IsActive: req.IsActive, + } +} + +type MenuActionsBatchCreateRequest struct { + MenuId uint `json:"menuId" validate:"required"` + ActionCodes []string `json:"actionCodes" validate:"required,min=1"` + ClientId *uuid.UUID `json:"clientId"` +} + diff --git a/app/module/menu_actions/response/menu_actions.response.go b/app/module/menu_actions/response/menu_actions.response.go new file mode 100644 index 0000000..d39230e --- /dev/null +++ b/app/module/menu_actions/response/menu_actions.response.go @@ -0,0 +1,18 @@ +package response + +import "time" + +type MenuActionsResponse struct { + ID uint `json:"id"` + MenuId uint `json:"menuId"` + ActionCode string `json:"actionCode"` + ActionName string `json:"actionName"` + Description *string `json:"description"` + PathUrl *string `json:"pathUrl"` + HttpMethod *string `json:"httpMethod"` + Position *int `json:"position"` + IsActive *bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + diff --git a/app/module/menu_actions/service/menu_actions.service.go b/app/module/menu_actions/service/menu_actions.service.go new file mode 100644 index 0000000..21371a6 --- /dev/null +++ b/app/module/menu_actions/service/menu_actions.service.go @@ -0,0 +1,158 @@ +package service + +import ( + "github.com/rs/zerolog" + "netidhub-saas-be/app/module/menu_actions/mapper" + "netidhub-saas-be/app/module/menu_actions/repository" + "netidhub-saas-be/app/module/menu_actions/request" + "netidhub-saas-be/app/module/menu_actions/response" + "netidhub-saas-be/app/database/entity" + "netidhub-saas-be/utils/paginator" +) + +// MenuActionsService +type menuActionsService struct { + Repo repository.MenuActionsRepository + Log zerolog.Logger +} + +// MenuActionsService define interface of IMenuActionsService +type MenuActionsService interface { + All(req request.MenuActionsQueryRequest) (menuActions []*response.MenuActionsResponse, paging paginator.Pagination, err error) + GetByMenuId(menuId uint) (menuActions []*response.MenuActionsResponse, err error) + Show(id uint) (menuAction *response.MenuActionsResponse, err error) + Save(req request.MenuActionsCreateRequest) (err error) + SaveBatch(req request.MenuActionsBatchCreateRequest) (err error) + Update(id uint, req request.MenuActionsUpdateRequest) (err error) + Delete(id uint) error +} + +// NewMenuActionsService init MenuActionsService +func NewMenuActionsService(repo repository.MenuActionsRepository, log zerolog.Logger) MenuActionsService { + return &menuActionsService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of MenuActionsService +func (_i *menuActionsService) All(req request.MenuActionsQueryRequest) (menuActions []*response.MenuActionsResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + menuActions = append(menuActions, mapper.MenuActionsResponseMapper(result)) + } + + return +} + +func (_i *menuActionsService) GetByMenuId(menuId uint) (menuActions []*response.MenuActionsResponse, err error) { + results, err := _i.Repo.GetByMenuId(menuId) + if err != nil { + return nil, err + } + + for _, result := range results { + menuActions = append(menuActions, mapper.MenuActionsResponseMapper(result)) + } + + return +} + +func (_i *menuActionsService) Show(id uint) (menuAction *response.MenuActionsResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.MenuActionsResponseMapper(result), nil +} + +func (_i *menuActionsService) Save(req request.MenuActionsCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Creating menu action") + + isActive := true + if req.IsActive != nil { + isActive = *req.IsActive + } + + menuAction := req.ToEntity() + menuAction.IsActive = &isActive + + return _i.Repo.Create(menuAction) +} + +func (_i *menuActionsService) SaveBatch(req request.MenuActionsBatchCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Creating menu actions batch") + + isActive := true + var menuActions []*entity.MenuActions + + // Standard action names mapping + actionNameMap := map[string]string{ + "view": "View", + "create": "Create", + "edit": "Edit", + "update": "Update", + "delete": "Delete", + "approve": "Approve", + "reject": "Reject", + "export": "Export", + "import": "Import", + "print": "Print", + } + + for idx, actionCode := range req.ActionCodes { + position := idx + 1 + actionName := actionNameMap[actionCode] + if actionName == "" { + // Capitalize first letter if not in map + if len(actionCode) > 0 { + actionName = string(actionCode[0]-32) + actionCode[1:] + } else { + actionName = actionCode + } + } + + menuAction := &entity.MenuActions{ + MenuId: req.MenuId, + ActionCode: actionCode, + ActionName: actionName, + Position: &position, + ClientId: req.ClientId, + IsActive: &isActive, + } + menuActions = append(menuActions, menuAction) + } + + return _i.Repo.CreateBatch(menuActions) +} + +func (_i *menuActionsService) Update(id uint, req request.MenuActionsUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Updating menu action") + + isActive := true + if req.IsActive != nil { + isActive = *req.IsActive + } + + menuAction := req.ToEntity() + menuAction.IsActive = &isActive + + return _i.Repo.Update(id, menuAction) +} + +func (_i *menuActionsService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} + diff --git a/app/module/menu_modules/repository/menu_modules.repository.go b/app/module/menu_modules/repository/menu_modules.repository.go index bfec3a8..0dfe09c 100644 --- a/app/module/menu_modules/repository/menu_modules.repository.go +++ b/app/module/menu_modules/repository/menu_modules.repository.go @@ -54,6 +54,7 @@ func (_i *menuModulesRepository) GetAll(req request.MenuModulesQueryRequest) (me query.Count(&count) + // Apply sorting if req.Pagination.SortBy != "" { direction := "ASC" if req.Pagination.Sort == "desc" { @@ -64,15 +65,29 @@ func (_i *menuModulesRepository) GetAll(req request.MenuModulesQueryRequest) (me query.Order("position ASC") } - req.Pagination.Count = count - req.Pagination = paginator.Paging(req.Pagination) + // Apply pagination (manual calculation like articles) + page := req.Pagination.Page + limit := req.Pagination.Limit + if page <= 0 { + page = 1 + } + if limit <= 0 { + limit = 10 + } - err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&menuModules).Error + offset := (page - 1) * limit + err = query.Offset(offset).Limit(limit).Find(&menuModules).Error if err != nil { return } - paging = *req.Pagination + // Create pagination response + paging = paginator.Pagination{ + Page: page, + Limit: limit, + Count: count, + TotalPage: int((count + int64(limit) - 1) / int64(limit)), + } return } diff --git a/app/module/menu_modules/request/menu_modules.request.go b/app/module/menu_modules/request/menu_modules.request.go index 74988b6..5e44995 100644 --- a/app/module/menu_modules/request/menu_modules.request.go +++ b/app/module/menu_modules/request/menu_modules.request.go @@ -1,15 +1,16 @@ package request import ( - "github.com/google/uuid" "netidhub-saas-be/utils/paginator" + + "github.com/google/uuid" ) type MenuModulesQueryRequest struct { - MenuId *uint `query:"menu_id"` - ModuleId *uint `query:"module_id"` - ClientId *uuid.UUID `query:"client_id"` - Pagination *paginator.Query `query:"pagination"` + MenuId *uint `query:"menu_id"` + ModuleId *uint `query:"module_id"` + ClientId *uuid.UUID `query:"client_id"` + Pagination *paginator.Pagination `query:"pagination"` } type MenuModulesCreateRequest struct { @@ -33,4 +34,3 @@ type MenuModulesUpdateRequest struct { ClientId *uuid.UUID `json:"client_id"` IsActive *bool `json:"is_active"` } - diff --git a/app/module/user_level_menu_accesses/controller/controller.go b/app/module/user_level_menu_accesses/controller/controller.go new file mode 100644 index 0000000..3612019 --- /dev/null +++ b/app/module/user_level_menu_accesses/controller/controller.go @@ -0,0 +1,16 @@ +package controller + +import ( + "netidhub-saas-be/app/module/user_level_menu_accesses/service" +) + +type Controller struct { + UserLevelMenuAccesses UserLevelMenuAccessesController +} + +func NewController(userLevelMenuAccessesService service.UserLevelMenuAccessesService) *Controller { + return &Controller{ + UserLevelMenuAccesses: NewUserLevelMenuAccessesController(userLevelMenuAccessesService), + } +} + diff --git a/app/module/user_level_menu_accesses/controller/user_level_menu_accesses.controller.go b/app/module/user_level_menu_accesses/controller/user_level_menu_accesses.controller.go new file mode 100644 index 0000000..3253949 --- /dev/null +++ b/app/module/user_level_menu_accesses/controller/user_level_menu_accesses.controller.go @@ -0,0 +1,328 @@ +package controller + +import ( + "netidhub-saas-be/app/module/user_level_menu_accesses/request" + "netidhub-saas-be/app/module/user_level_menu_accesses/service" + "netidhub-saas-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "netidhub-saas-be/utils/response" + utilVal "netidhub-saas-be/utils/validator" +) + +type userLevelMenuAccessesController struct { + userLevelMenuAccessesService service.UserLevelMenuAccessesService +} + +type UserLevelMenuAccessesController interface { + All(c *fiber.Ctx) error + GetByUserLevelId(c *fiber.Ctx) error + GetByMenuId(c *fiber.Ctx) error + CheckAccess(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + SaveBatch(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewUserLevelMenuAccessesController(userLevelMenuAccessesService service.UserLevelMenuAccessesService) UserLevelMenuAccessesController { + return &userLevelMenuAccessesController{ + userLevelMenuAccessesService: userLevelMenuAccessesService, + } +} + +// All UserLevelMenuAccesses +// @Summary Get all UserLevelMenuAccesses +// @Description API for getting all UserLevelMenuAccesses +// @Tags UserLevelMenuAccesses +// @Security Bearer +// @Param user_level_id query int false "User Level ID" +// @Param menu_id query int false "Menu ID" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-accesses [get] +func (_i *userLevelMenuAccessesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + req := request.UserLevelMenuAccessesQueryRequest{ + Pagination: paginate, + } + + if userLevelId := c.Query("user_level_id"); userLevelId != "" { + id, _ := strconv.ParseUint(userLevelId, 10, 0) + userLevelIdUint := uint(id) + req.UserLevelId = &userLevelIdUint + } + + if menuId := c.Query("menu_id"); menuId != "" { + id, _ := strconv.ParseUint(menuId, 10, 0) + menuIdUint := uint(id) + req.MenuId = &menuIdUint + } + + accessesData, paging, err := _i.userLevelMenuAccessesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuAccesses list successfully retrieved"}, + Data: accessesData, + Meta: paging, + }) +} + +// GetByUserLevelId get UserLevelMenuAccesses by User Level ID +// @Summary Get UserLevelMenuAccesses by User Level ID +// @Description API for getting UserLevelMenuAccesses by User Level ID +// @Tags UserLevelMenuAccesses +// @Security Bearer +// @Param user_level_id path int true "User Level ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-accesses/user-level/{user_level_id} [get] +func (_i *userLevelMenuAccessesController) GetByUserLevelId(c *fiber.Ctx) error { + userLevelId, err := strconv.ParseUint(c.Params("user_level_id"), 10, 0) + if err != nil { + return err + } + + accessesData, err := _i.userLevelMenuAccessesService.GetByUserLevelId(uint(userLevelId)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuAccesses by user level successfully retrieved"}, + Data: accessesData, + }) +} + +// GetByMenuId get UserLevelMenuAccesses by Menu ID +// @Summary Get UserLevelMenuAccesses by Menu ID +// @Description API for getting UserLevelMenuAccesses by Menu ID +// @Tags UserLevelMenuAccesses +// @Security Bearer +// @Param menu_id path int true "Menu ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-accesses/menu/{menu_id} [get] +func (_i *userLevelMenuAccessesController) GetByMenuId(c *fiber.Ctx) error { + menuId, err := strconv.ParseUint(c.Params("menu_id"), 10, 0) + if err != nil { + return err + } + + accessesData, err := _i.userLevelMenuAccessesService.GetByMenuId(uint(menuId)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuAccesses by menu successfully retrieved"}, + Data: accessesData, + }) +} + +// CheckAccess check if user level has access to menu +// @Summary Check User Level Menu Access +// @Description API for checking if user level has access to menu +// @Tags UserLevelMenuAccesses +// @Security Bearer +// @Param user_level_id path int true "User Level ID" +// @Param menu_id path int true "Menu ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-accesses/check/{user_level_id}/{menu_id} [get] +func (_i *userLevelMenuAccessesController) CheckAccess(c *fiber.Ctx) error { + userLevelId, err := strconv.ParseUint(c.Params("user_level_id"), 10, 0) + if err != nil { + return err + } + + menuId, err := strconv.ParseUint(c.Params("menu_id"), 10, 0) + if err != nil { + return err + } + + hasAccess, err := _i.userLevelMenuAccessesService.CheckAccess(uint(userLevelId), uint(menuId)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Access check completed"}, + Data: fiber.Map{"has_access": hasAccess}, + }) +} + +// Show get one UserLevelMenuAccess +// @Summary Get one UserLevelMenuAccess +// @Description API for getting one UserLevelMenuAccess +// @Tags UserLevelMenuAccesses +// @Security Bearer +// @Param id path int true "UserLevelMenuAccess ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-accesses/{id} [get] +func (_i *userLevelMenuAccessesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + accessData, err := _i.userLevelMenuAccessesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuAccess successfully retrieved"}, + Data: accessData, + }) +} + +// Save create UserLevelMenuAccess +// @Summary Create UserLevelMenuAccess +// @Description API for create UserLevelMenuAccess +// @Tags UserLevelMenuAccesses +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param payload body request.UserLevelMenuAccessesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-accesses [post] +func (_i *userLevelMenuAccessesController) Save(c *fiber.Ctx) error { + req := new(request.UserLevelMenuAccessesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.userLevelMenuAccessesService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuAccess successfully created"}, + }) +} + +// SaveBatch create UserLevelMenuAccesses batch +// @Summary Create UserLevelMenuAccesses batch +// @Description API for create UserLevelMenuAccesses batch +// @Tags UserLevelMenuAccesses +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param payload body request.UserLevelMenuAccessesBatchCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-accesses/batch [post] +func (_i *userLevelMenuAccessesController) SaveBatch(c *fiber.Ctx) error { + req := new(request.UserLevelMenuAccessesBatchCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.userLevelMenuAccessesService.SaveBatch(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuAccesses batch successfully created"}, + }) +} + +// Update UserLevelMenuAccess +// @Summary Update UserLevelMenuAccess +// @Description API for update UserLevelMenuAccess +// @Tags UserLevelMenuAccesses +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param id path int true "UserLevelMenuAccess ID" +// @Param payload body request.UserLevelMenuAccessesUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-accesses/{id} [put] +func (_i *userLevelMenuAccessesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.UserLevelMenuAccessesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.userLevelMenuAccessesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuAccess successfully updated"}, + }) +} + +// Delete UserLevelMenuAccess +// @Summary Delete UserLevelMenuAccess +// @Description API for delete UserLevelMenuAccess +// @Tags UserLevelMenuAccesses +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param id path int true "UserLevelMenuAccess ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-accesses/{id} [delete] +func (_i *userLevelMenuAccessesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.userLevelMenuAccessesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuAccess successfully deleted"}, + }) +} + diff --git a/app/module/user_level_menu_accesses/mapper/user_level_menu_accesses.mapper.go b/app/module/user_level_menu_accesses/mapper/user_level_menu_accesses.mapper.go new file mode 100644 index 0000000..0b1747a --- /dev/null +++ b/app/module/user_level_menu_accesses/mapper/user_level_menu_accesses.mapper.go @@ -0,0 +1,22 @@ +package mapper + +import ( + "netidhub-saas-be/app/database/entity" + res "netidhub-saas-be/app/module/user_level_menu_accesses/response" +) + +func UserLevelMenuAccessesResponseMapper(accessReq *entity.UserLevelMenuAccesses) (accessRes *res.UserLevelMenuAccessesResponse) { + if accessReq != nil { + accessRes = &res.UserLevelMenuAccessesResponse{ + ID: accessReq.ID, + UserLevelId: accessReq.UserLevelId, + MenuId: accessReq.MenuId, + CanAccess: accessReq.CanAccess, + IsActive: accessReq.IsActive, + CreatedAt: accessReq.CreatedAt, + UpdatedAt: accessReq.UpdatedAt, + } + } + return accessRes +} + diff --git a/app/module/user_level_menu_accesses/repository/user_level_menu_accesses.repository.go b/app/module/user_level_menu_accesses/repository/user_level_menu_accesses.repository.go new file mode 100644 index 0000000..b86f5d6 --- /dev/null +++ b/app/module/user_level_menu_accesses/repository/user_level_menu_accesses.repository.go @@ -0,0 +1,161 @@ +package repository + +import ( + "fmt" + "netidhub-saas-be/app/database" + "netidhub-saas-be/app/database/entity" + "netidhub-saas-be/app/module/user_level_menu_accesses/request" + "netidhub-saas-be/utils/paginator" + "gorm.io/gorm" +) + +type userLevelMenuAccessesRepository struct { + DB *database.Database +} + +// UserLevelMenuAccessesRepository define interface of IUserLevelMenuAccessesRepository +type UserLevelMenuAccessesRepository interface { + GetAll(req request.UserLevelMenuAccessesQueryRequest) (accesses []*entity.UserLevelMenuAccesses, paging paginator.Pagination, err error) + GetByUserLevelId(userLevelId uint) (accesses []*entity.UserLevelMenuAccesses, err error) + GetByMenuId(menuId uint) (accesses []*entity.UserLevelMenuAccesses, err error) + CheckAccess(userLevelId uint, menuId uint) (hasAccess bool, err error) + FindOne(id uint) (access *entity.UserLevelMenuAccesses, err error) + Create(access *entity.UserLevelMenuAccesses) (err error) + CreateBatch(accesses []*entity.UserLevelMenuAccesses) (err error) + Update(id uint, access *entity.UserLevelMenuAccesses) (err error) + Delete(id uint) (err error) + DeleteByUserLevelId(userLevelId uint) (err error) + DeleteByMenuId(menuId uint) (err error) +} + +func NewUserLevelMenuAccessesRepository(db *database.Database) UserLevelMenuAccessesRepository { + return &userLevelMenuAccessesRepository{ + DB: db, + } +} + +// implement interface of IUserLevelMenuAccessesRepository +func (_i *userLevelMenuAccessesRepository) GetAll(req request.UserLevelMenuAccessesQueryRequest) (accesses []*entity.UserLevelMenuAccesses, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.UserLevelMenuAccesses{}) + query = query.Where("is_active = ?", true) + + if req.UserLevelId != nil { + query = query.Where("user_level_id = ?", req.UserLevelId) + } + if req.MenuId != nil { + query = query.Where("menu_id = ?", req.MenuId) + } + if req.ClientId != nil { + query = query.Where("client_id = ?", req.ClientId) + } + if req.CanAccess != nil { + query = query.Where("can_access = ?", req.CanAccess) + } + + // Preload relations + query = query.Preload("UserLevel").Preload("Menu") + + query.Count(&count) + + // Apply sorting + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + // Apply pagination (manual calculation like articles) + page := req.Pagination.Page + limit := req.Pagination.Limit + if page <= 0 { + page = 1 + } + if limit <= 0 { + limit = 10 + } + + offset := (page - 1) * limit + err = query.Offset(offset).Limit(limit).Find(&accesses).Error + if err != nil { + return + } + + // Create pagination response + paging = paginator.Pagination{ + Page: page, + Limit: limit, + Count: count, + TotalPage: int((count + int64(limit) - 1) / int64(limit)), + } + + return +} + +func (_i *userLevelMenuAccessesRepository) GetByUserLevelId(userLevelId uint) (accesses []*entity.UserLevelMenuAccesses, err error) { + query := _i.DB.DB.Model(&entity.UserLevelMenuAccesses{}) + query = query.Where("user_level_id = ? AND is_active = ?", userLevelId, true) + query = query.Preload("Menu") + err = query.Find(&accesses).Error + return +} + +func (_i *userLevelMenuAccessesRepository) GetByMenuId(menuId uint) (accesses []*entity.UserLevelMenuAccesses, err error) { + query := _i.DB.DB.Model(&entity.UserLevelMenuAccesses{}) + query = query.Where("menu_id = ? AND is_active = ?", menuId, true) + query = query.Preload("UserLevel") + err = query.Find(&accesses).Error + return +} + +func (_i *userLevelMenuAccessesRepository) CheckAccess(userLevelId uint, menuId uint) (hasAccess bool, err error) { + var access entity.UserLevelMenuAccesses + query := _i.DB.DB.Model(&entity.UserLevelMenuAccesses{}) + query = query.Where("user_level_id = ? AND menu_id = ? AND is_active = ? AND can_access = ?", userLevelId, menuId, true, true) + err = query.First(&access).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return false, nil + } + return false, err + } + return true, nil +} + +func (_i *userLevelMenuAccessesRepository) FindOne(id uint) (access *entity.UserLevelMenuAccesses, err error) { + query := _i.DB.DB.Preload("UserLevel").Preload("Menu") + if err := query.First(&access, id).Error; err != nil { + return nil, err + } + return access, nil +} + +func (_i *userLevelMenuAccessesRepository) Create(access *entity.UserLevelMenuAccesses) (err error) { + return _i.DB.DB.Create(access).Error +} + +func (_i *userLevelMenuAccessesRepository) CreateBatch(accesses []*entity.UserLevelMenuAccesses) (err error) { + return _i.DB.DB.Create(&accesses).Error +} + +func (_i *userLevelMenuAccessesRepository) Update(id uint, access *entity.UserLevelMenuAccesses) (err error) { + return _i.DB.DB.Model(&entity.UserLevelMenuAccesses{}). + Where(&entity.UserLevelMenuAccesses{ID: id}). + Updates(access).Error +} + +func (_i *userLevelMenuAccessesRepository) Delete(id uint) (err error) { + return _i.DB.DB.Delete(&entity.UserLevelMenuAccesses{}, id).Error +} + +func (_i *userLevelMenuAccessesRepository) DeleteByUserLevelId(userLevelId uint) (err error) { + return _i.DB.DB.Where("user_level_id = ?", userLevelId).Delete(&entity.UserLevelMenuAccesses{}).Error +} + +func (_i *userLevelMenuAccessesRepository) DeleteByMenuId(menuId uint) (err error) { + return _i.DB.DB.Where("menu_id = ?", menuId).Delete(&entity.UserLevelMenuAccesses{}).Error +} + diff --git a/app/module/user_level_menu_accesses/request/user_level_menu_accesses.request.go b/app/module/user_level_menu_accesses/request/user_level_menu_accesses.request.go new file mode 100644 index 0000000..a95b4af --- /dev/null +++ b/app/module/user_level_menu_accesses/request/user_level_menu_accesses.request.go @@ -0,0 +1,64 @@ +package request + +import ( + "netidhub-saas-be/app/database/entity" + "netidhub-saas-be/utils/paginator" + "github.com/google/uuid" +) + +type UserLevelMenuAccessesQueryRequest struct { + UserLevelId *uint `query:"user_level_id"` + MenuId *uint `query:"menu_id"` + ClientId *uuid.UUID `query:"client_id"` + CanAccess *bool `query:"can_access"` + Pagination *paginator.Pagination `query:"pagination"` +} + +type UserLevelMenuAccessesCreateRequest struct { + UserLevelId uint `json:"userLevelId" validate:"required"` + MenuId uint `json:"menuId" validate:"required"` + CanAccess bool `json:"canAccess"` + ClientId *uuid.UUID `json:"clientId"` + IsActive *bool `json:"isActive"` +} + +func (req UserLevelMenuAccessesCreateRequest) ToEntity() *entity.UserLevelMenuAccesses { + return &entity.UserLevelMenuAccesses{ + UserLevelId: req.UserLevelId, + MenuId: req.MenuId, + CanAccess: req.CanAccess, + ClientId: req.ClientId, + IsActive: req.IsActive, + } +} + +type UserLevelMenuAccessesUpdateRequest struct { + UserLevelId *uint `json:"userLevelId"` + MenuId *uint `json:"menuId"` + CanAccess *bool `json:"canAccess"` + ClientId *uuid.UUID `json:"clientId"` + IsActive *bool `json:"isActive"` +} + +func (req UserLevelMenuAccessesUpdateRequest) ToEntity() *entity.UserLevelMenuAccesses { + access := &entity.UserLevelMenuAccesses{} + if req.UserLevelId != nil { + access.UserLevelId = *req.UserLevelId + } + if req.MenuId != nil { + access.MenuId = *req.MenuId + } + if req.CanAccess != nil { + access.CanAccess = *req.CanAccess + } + access.ClientId = req.ClientId + access.IsActive = req.IsActive + return access +} + +type UserLevelMenuAccessesBatchCreateRequest struct { + UserLevelId uint `json:"userLevelId" validate:"required"` + MenuIds []uint `json:"menuIds" validate:"required,min=1"` + ClientId *uuid.UUID `json:"clientId"` +} + diff --git a/app/module/user_level_menu_accesses/response/user_level_menu_accesses.response.go b/app/module/user_level_menu_accesses/response/user_level_menu_accesses.response.go new file mode 100644 index 0000000..4de27b6 --- /dev/null +++ b/app/module/user_level_menu_accesses/response/user_level_menu_accesses.response.go @@ -0,0 +1,14 @@ +package response + +import "time" + +type UserLevelMenuAccessesResponse struct { + ID uint `json:"id"` + UserLevelId uint `json:"userLevelId"` + MenuId uint `json:"menuId"` + CanAccess bool `json:"canAccess"` + IsActive *bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + diff --git a/app/module/user_level_menu_accesses/service/user_level_menu_accesses.service.go b/app/module/user_level_menu_accesses/service/user_level_menu_accesses.service.go new file mode 100644 index 0000000..adf3388 --- /dev/null +++ b/app/module/user_level_menu_accesses/service/user_level_menu_accesses.service.go @@ -0,0 +1,151 @@ +package service + +import ( + "github.com/rs/zerolog" + "netidhub-saas-be/app/database/entity" + "netidhub-saas-be/app/module/user_level_menu_accesses/mapper" + "netidhub-saas-be/app/module/user_level_menu_accesses/repository" + "netidhub-saas-be/app/module/user_level_menu_accesses/request" + "netidhub-saas-be/app/module/user_level_menu_accesses/response" + "netidhub-saas-be/utils/paginator" +) + +// UserLevelMenuAccessesService +type userLevelMenuAccessesService struct { + Repo repository.UserLevelMenuAccessesRepository + Log zerolog.Logger +} + +// UserLevelMenuAccessesService define interface of IUserLevelMenuAccessesService +type UserLevelMenuAccessesService interface { + All(req request.UserLevelMenuAccessesQueryRequest) (accesses []*response.UserLevelMenuAccessesResponse, paging paginator.Pagination, err error) + GetByUserLevelId(userLevelId uint) (accesses []*response.UserLevelMenuAccessesResponse, err error) + GetByMenuId(menuId uint) (accesses []*response.UserLevelMenuAccessesResponse, err error) + CheckAccess(userLevelId uint, menuId uint) (hasAccess bool, err error) + Show(id uint) (access *response.UserLevelMenuAccessesResponse, err error) + Save(req request.UserLevelMenuAccessesCreateRequest) (err error) + SaveBatch(req request.UserLevelMenuAccessesBatchCreateRequest) (err error) + Update(id uint, req request.UserLevelMenuAccessesUpdateRequest) (err error) + Delete(id uint) error +} + +// NewUserLevelMenuAccessesService init UserLevelMenuAccessesService +func NewUserLevelMenuAccessesService(repo repository.UserLevelMenuAccessesRepository, log zerolog.Logger) UserLevelMenuAccessesService { + return &userLevelMenuAccessesService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of UserLevelMenuAccessesService +func (_i *userLevelMenuAccessesService) All(req request.UserLevelMenuAccessesQueryRequest) (accesses []*response.UserLevelMenuAccessesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + accesses = append(accesses, mapper.UserLevelMenuAccessesResponseMapper(result)) + } + + return +} + +func (_i *userLevelMenuAccessesService) GetByUserLevelId(userLevelId uint) (accesses []*response.UserLevelMenuAccessesResponse, err error) { + results, err := _i.Repo.GetByUserLevelId(userLevelId) + if err != nil { + return nil, err + } + + for _, result := range results { + accesses = append(accesses, mapper.UserLevelMenuAccessesResponseMapper(result)) + } + + return +} + +func (_i *userLevelMenuAccessesService) GetByMenuId(menuId uint) (accesses []*response.UserLevelMenuAccessesResponse, err error) { + results, err := _i.Repo.GetByMenuId(menuId) + if err != nil { + return nil, err + } + + for _, result := range results { + accesses = append(accesses, mapper.UserLevelMenuAccessesResponseMapper(result)) + } + + return +} + +func (_i *userLevelMenuAccessesService) CheckAccess(userLevelId uint, menuId uint) (hasAccess bool, err error) { + return _i.Repo.CheckAccess(userLevelId, menuId) +} + +func (_i *userLevelMenuAccessesService) Show(id uint) (access *response.UserLevelMenuAccessesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.UserLevelMenuAccessesResponseMapper(result), nil +} + +func (_i *userLevelMenuAccessesService) Save(req request.UserLevelMenuAccessesCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Creating user level menu access") + + isActive := true + if req.IsActive != nil { + isActive = *req.IsActive + } + + access := req.ToEntity() + access.IsActive = &isActive + + return _i.Repo.Create(access) +} + +func (_i *userLevelMenuAccessesService) SaveBatch(req request.UserLevelMenuAccessesBatchCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Creating user level menu accesses batch") + + isActive := true + var accesses []*entity.UserLevelMenuAccesses + + for _, menuId := range req.MenuIds { + access := &entity.UserLevelMenuAccesses{ + UserLevelId: req.UserLevelId, + MenuId: menuId, + CanAccess: true, + ClientId: req.ClientId, + IsActive: &isActive, + } + accesses = append(accesses, access) + } + + return _i.Repo.CreateBatch(accesses) +} + +func (_i *userLevelMenuAccessesService) Update(id uint, req request.UserLevelMenuAccessesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Updating user level menu access") + + isActive := true + if req.IsActive != nil { + isActive = *req.IsActive + } + + access := req.ToEntity() + access.IsActive = &isActive + + return _i.Repo.Update(id, access) +} + +func (_i *userLevelMenuAccessesService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} + diff --git a/app/module/user_level_menu_accesses/user_level_menu_accesses.module.go b/app/module/user_level_menu_accesses/user_level_menu_accesses.module.go new file mode 100644 index 0000000..9963d5d --- /dev/null +++ b/app/module/user_level_menu_accesses/user_level_menu_accesses.module.go @@ -0,0 +1,58 @@ +package user_level_menu_accesses + +import ( + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" + "netidhub-saas-be/app/module/user_level_menu_accesses/controller" + "netidhub-saas-be/app/module/user_level_menu_accesses/repository" + "netidhub-saas-be/app/module/user_level_menu_accesses/service" +) + +// struct of UserLevelMenuAccessesRouter +type UserLevelMenuAccessesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of UserLevelMenuAccesses module +var NewUserLevelMenuAccessesModule = fx.Options( + // register repository of UserLevelMenuAccesses module + fx.Provide(repository.NewUserLevelMenuAccessesRepository), + + // register service of UserLevelMenuAccesses module + fx.Provide(service.NewUserLevelMenuAccessesService), + + // register controller of UserLevelMenuAccesses module + fx.Provide(controller.NewController), + + // register router of UserLevelMenuAccesses module + fx.Provide(NewUserLevelMenuAccessesRouter), +) + +// init UserLevelMenuAccessesRouter +func NewUserLevelMenuAccessesRouter(fiber *fiber.App, controller *controller.Controller) *UserLevelMenuAccessesRouter { + return &UserLevelMenuAccessesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of UserLevelMenuAccesses module +func (_i *UserLevelMenuAccessesRouter) RegisterUserLevelMenuAccessesRoutes() { + // define controllers + userLevelMenuAccessesController := _i.Controller.UserLevelMenuAccesses + + // define routes + _i.App.Route("/user-level-menu-accesses", func(router fiber.Router) { + router.Get("/", userLevelMenuAccessesController.All) + router.Get("/:id", userLevelMenuAccessesController.Show) + router.Get("/user-level/:user_level_id", userLevelMenuAccessesController.GetByUserLevelId) + router.Get("/menu/:menu_id", userLevelMenuAccessesController.GetByMenuId) + router.Get("/check/:user_level_id/:menu_id", userLevelMenuAccessesController.CheckAccess) + router.Post("/", userLevelMenuAccessesController.Save) + router.Post("/batch", userLevelMenuAccessesController.SaveBatch) + router.Put("/:id", userLevelMenuAccessesController.Update) + router.Delete("/:id", userLevelMenuAccessesController.Delete) + }) +} + diff --git a/app/module/user_level_menu_action_accesses/controller/controller.go b/app/module/user_level_menu_action_accesses/controller/controller.go new file mode 100644 index 0000000..f3f1295 --- /dev/null +++ b/app/module/user_level_menu_action_accesses/controller/controller.go @@ -0,0 +1,16 @@ +package controller + +import ( + "netidhub-saas-be/app/module/user_level_menu_action_accesses/service" +) + +type Controller struct { + UserLevelMenuActionAccesses UserLevelMenuActionAccessesController +} + +func NewController(userLevelMenuActionAccessesService service.UserLevelMenuActionAccessesService) *Controller { + return &Controller{ + UserLevelMenuActionAccesses: NewUserLevelMenuActionAccessesController(userLevelMenuActionAccessesService), + } +} + diff --git a/app/module/user_level_menu_action_accesses/controller/user_level_menu_action_accesses.controller.go b/app/module/user_level_menu_action_accesses/controller/user_level_menu_action_accesses.controller.go new file mode 100644 index 0000000..0572d30 --- /dev/null +++ b/app/module/user_level_menu_action_accesses/controller/user_level_menu_action_accesses.controller.go @@ -0,0 +1,379 @@ +package controller + +import ( + "netidhub-saas-be/app/module/user_level_menu_action_accesses/request" + "netidhub-saas-be/app/module/user_level_menu_action_accesses/service" + "netidhub-saas-be/utils/paginator" + "strconv" + + "github.com/gofiber/fiber/v2" + + utilRes "netidhub-saas-be/utils/response" + utilVal "netidhub-saas-be/utils/validator" +) + +type userLevelMenuActionAccessesController struct { + userLevelMenuActionAccessesService service.UserLevelMenuActionAccessesService +} + +type UserLevelMenuActionAccessesController interface { + All(c *fiber.Ctx) error + GetByUserLevelId(c *fiber.Ctx) error + GetByUserLevelIdAndMenuId(c *fiber.Ctx) error + GetByMenuId(c *fiber.Ctx) error + CheckAccess(c *fiber.Ctx) error + Show(c *fiber.Ctx) error + Save(c *fiber.Ctx) error + SaveBatch(c *fiber.Ctx) error + Update(c *fiber.Ctx) error + Delete(c *fiber.Ctx) error +} + +func NewUserLevelMenuActionAccessesController(userLevelMenuActionAccessesService service.UserLevelMenuActionAccessesService) UserLevelMenuActionAccessesController { + return &userLevelMenuActionAccessesController{ + userLevelMenuActionAccessesService: userLevelMenuActionAccessesService, + } +} + +// All UserLevelMenuActionAccesses +// @Summary Get all UserLevelMenuActionAccesses +// @Description API for getting all UserLevelMenuActionAccesses +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param user_level_id query int false "User Level ID" +// @Param menu_id query int false "Menu ID" +// @Param action_code query string false "Action Code" +// @Param req query paginator.Pagination false "pagination parameters" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses [get] +func (_i *userLevelMenuActionAccessesController) All(c *fiber.Ctx) error { + paginate, err := paginator.Paginate(c) + if err != nil { + return err + } + + req := request.UserLevelMenuActionAccessesQueryRequest{ + Pagination: paginate, + } + + if userLevelId := c.Query("user_level_id"); userLevelId != "" { + id, _ := strconv.ParseUint(userLevelId, 10, 0) + userLevelIdUint := uint(id) + req.UserLevelId = &userLevelIdUint + } + + if menuId := c.Query("menu_id"); menuId != "" { + id, _ := strconv.ParseUint(menuId, 10, 0) + menuIdUint := uint(id) + req.MenuId = &menuIdUint + } + + if actionCode := c.Query("action_code"); actionCode != "" { + req.ActionCode = &actionCode + } + + accessesData, paging, err := _i.userLevelMenuActionAccessesService.All(req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuActionAccesses list successfully retrieved"}, + Data: accessesData, + Meta: paging, + }) +} + +// GetByUserLevelId get UserLevelMenuActionAccesses by User Level ID +// @Summary Get UserLevelMenuActionAccesses by User Level ID +// @Description API for getting UserLevelMenuActionAccesses by User Level ID +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param user_level_id path int true "User Level ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses/user-level/{user_level_id} [get] +func (_i *userLevelMenuActionAccessesController) GetByUserLevelId(c *fiber.Ctx) error { + userLevelId, err := strconv.ParseUint(c.Params("user_level_id"), 10, 0) + if err != nil { + return err + } + + accessesData, err := _i.userLevelMenuActionAccessesService.GetByUserLevelId(uint(userLevelId)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuActionAccesses by user level successfully retrieved"}, + Data: accessesData, + }) +} + +// GetByUserLevelIdAndMenuId get UserLevelMenuActionAccesses by User Level ID and Menu ID +// @Summary Get UserLevelMenuActionAccesses by User Level ID and Menu ID +// @Description API for getting UserLevelMenuActionAccesses by User Level ID and Menu ID +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param user_level_id path int true "User Level ID" +// @Param menu_id path int true "Menu ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses/user-level/{user_level_id}/menu/{menu_id} [get] +func (_i *userLevelMenuActionAccessesController) GetByUserLevelIdAndMenuId(c *fiber.Ctx) error { + userLevelId, err := strconv.ParseUint(c.Params("user_level_id"), 10, 0) + if err != nil { + return err + } + + menuId, err := strconv.ParseUint(c.Params("menu_id"), 10, 0) + if err != nil { + return err + } + + accessesData, err := _i.userLevelMenuActionAccessesService.GetByUserLevelIdAndMenuId(uint(userLevelId), uint(menuId)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuActionAccesses by user level and menu successfully retrieved"}, + Data: accessesData, + }) +} + +// GetByMenuId get UserLevelMenuActionAccesses by Menu ID +// @Summary Get UserLevelMenuActionAccesses by Menu ID +// @Description API for getting UserLevelMenuActionAccesses by Menu ID +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param menu_id path int true "Menu ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses/menu/{menu_id} [get] +func (_i *userLevelMenuActionAccessesController) GetByMenuId(c *fiber.Ctx) error { + menuId, err := strconv.ParseUint(c.Params("menu_id"), 10, 0) + if err != nil { + return err + } + + accessesData, err := _i.userLevelMenuActionAccessesService.GetByMenuId(uint(menuId)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuActionAccesses by menu successfully retrieved"}, + Data: accessesData, + }) +} + +// CheckAccess check if user level has access to action in menu +// @Summary Check User Level Menu Action Access +// @Description API for checking if user level has access to action in menu +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param user_level_id path int true "User Level ID" +// @Param menu_id path int true "Menu ID" +// @Param action_code path string true "Action Code" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses/check/{user_level_id}/{menu_id}/{action_code} [get] +func (_i *userLevelMenuActionAccessesController) CheckAccess(c *fiber.Ctx) error { + userLevelId, err := strconv.ParseUint(c.Params("user_level_id"), 10, 0) + if err != nil { + return err + } + + menuId, err := strconv.ParseUint(c.Params("menu_id"), 10, 0) + if err != nil { + return err + } + + actionCode := c.Params("action_code") + if actionCode == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "code": 400, + "messages": []string{"Action code is required"}, + }) + } + + hasAccess, err := _i.userLevelMenuActionAccessesService.CheckAccess(uint(userLevelId), uint(menuId), actionCode) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"Access check completed"}, + Data: fiber.Map{"has_access": hasAccess}, + }) +} + +// Show get one UserLevelMenuActionAccess +// @Summary Get one UserLevelMenuActionAccess +// @Description API for getting one UserLevelMenuActionAccess +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param id path int true "UserLevelMenuActionAccess ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses/{id} [get] +func (_i *userLevelMenuActionAccessesController) Show(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + accessData, err := _i.userLevelMenuActionAccessesService.Show(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuActionAccess successfully retrieved"}, + Data: accessData, + }) +} + +// Save create UserLevelMenuActionAccess +// @Summary Create UserLevelMenuActionAccess +// @Description API for create UserLevelMenuActionAccess +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param payload body request.UserLevelMenuActionAccessesCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses [post] +func (_i *userLevelMenuActionAccessesController) Save(c *fiber.Ctx) error { + req := new(request.UserLevelMenuActionAccessesCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.userLevelMenuActionAccessesService.Save(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuActionAccess successfully created"}, + }) +} + +// SaveBatch create UserLevelMenuActionAccesses batch +// @Summary Create UserLevelMenuActionAccesses batch +// @Description API for create UserLevelMenuActionAccesses batch +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param payload body request.UserLevelMenuActionAccessesBatchCreateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses/batch [post] +func (_i *userLevelMenuActionAccessesController) SaveBatch(c *fiber.Ctx) error { + req := new(request.UserLevelMenuActionAccessesBatchCreateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err := _i.userLevelMenuActionAccessesService.SaveBatch(*req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuActionAccesses batch successfully created"}, + }) +} + +// Update UserLevelMenuActionAccess +// @Summary Update UserLevelMenuActionAccess +// @Description API for update UserLevelMenuActionAccess +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param id path int true "UserLevelMenuActionAccess ID" +// @Param payload body request.UserLevelMenuActionAccessesUpdateRequest true "Required payload" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses/{id} [put] +func (_i *userLevelMenuActionAccessesController) Update(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + req := new(request.UserLevelMenuActionAccessesUpdateRequest) + if err := utilVal.ParseAndValidate(c, req); err != nil { + return err + } + + err = _i.userLevelMenuActionAccessesService.Update(uint(id), *req) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuActionAccess successfully updated"}, + }) +} + +// Delete UserLevelMenuActionAccess +// @Summary Delete UserLevelMenuActionAccess +// @Description API for delete UserLevelMenuActionAccess +// @Tags UserLevelMenuActionAccesses +// @Security Bearer +// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" +// @Param id path int true "UserLevelMenuActionAccess ID" +// @Success 200 {object} response.Response +// @Failure 400 {object} response.BadRequestError +// @Failure 401 {object} response.UnauthorizedError +// @Failure 500 {object} response.InternalServerError +// @Router /user-level-menu-action-accesses/{id} [delete] +func (_i *userLevelMenuActionAccessesController) Delete(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 0) + if err != nil { + return err + } + + err = _i.userLevelMenuActionAccessesService.Delete(uint(id)) + if err != nil { + return err + } + + return utilRes.Resp(c, utilRes.Response{ + Success: true, + Messages: utilRes.Messages{"UserLevelMenuActionAccess successfully deleted"}, + }) +} + diff --git a/app/module/user_level_menu_action_accesses/mapper/user_level_menu_action_accesses.mapper.go b/app/module/user_level_menu_action_accesses/mapper/user_level_menu_action_accesses.mapper.go new file mode 100644 index 0000000..498918a --- /dev/null +++ b/app/module/user_level_menu_action_accesses/mapper/user_level_menu_action_accesses.mapper.go @@ -0,0 +1,23 @@ +package mapper + +import ( + "netidhub-saas-be/app/database/entity" + res "netidhub-saas-be/app/module/user_level_menu_action_accesses/response" +) + +func UserLevelMenuActionAccessesResponseMapper(accessReq *entity.UserLevelMenuActionAccesses) (accessRes *res.UserLevelMenuActionAccessesResponse) { + if accessReq != nil { + accessRes = &res.UserLevelMenuActionAccessesResponse{ + ID: accessReq.ID, + UserLevelId: accessReq.UserLevelId, + MenuId: accessReq.MenuId, + ActionCode: accessReq.ActionCode, + CanAccess: accessReq.CanAccess, + IsActive: accessReq.IsActive, + CreatedAt: accessReq.CreatedAt, + UpdatedAt: accessReq.UpdatedAt, + } + } + return accessRes +} + diff --git a/app/module/user_level_menu_action_accesses/repository/user_level_menu_action_accesses.repository.go b/app/module/user_level_menu_action_accesses/repository/user_level_menu_action_accesses.repository.go new file mode 100644 index 0000000..4e13312 --- /dev/null +++ b/app/module/user_level_menu_action_accesses/repository/user_level_menu_action_accesses.repository.go @@ -0,0 +1,178 @@ +package repository + +import ( + "fmt" + "netidhub-saas-be/app/database" + "netidhub-saas-be/app/database/entity" + "netidhub-saas-be/app/module/user_level_menu_action_accesses/request" + "netidhub-saas-be/utils/paginator" + "gorm.io/gorm" +) + +type userLevelMenuActionAccessesRepository struct { + DB *database.Database +} + +// UserLevelMenuActionAccessesRepository define interface of IUserLevelMenuActionAccessesRepository +type UserLevelMenuActionAccessesRepository interface { + GetAll(req request.UserLevelMenuActionAccessesQueryRequest) (accesses []*entity.UserLevelMenuActionAccesses, paging paginator.Pagination, err error) + GetByUserLevelId(userLevelId uint) (accesses []*entity.UserLevelMenuActionAccesses, err error) + GetByUserLevelIdAndMenuId(userLevelId uint, menuId uint) (accesses []*entity.UserLevelMenuActionAccesses, err error) + GetByMenuId(menuId uint) (accesses []*entity.UserLevelMenuActionAccesses, err error) + CheckAccess(userLevelId uint, menuId uint, actionCode string) (hasAccess bool, err error) + FindOne(id uint) (access *entity.UserLevelMenuActionAccesses, err error) + Create(access *entity.UserLevelMenuActionAccesses) (err error) + CreateBatch(accesses []*entity.UserLevelMenuActionAccesses) (err error) + Update(id uint, access *entity.UserLevelMenuActionAccesses) (err error) + Delete(id uint) (err error) + DeleteByUserLevelId(userLevelId uint) (err error) + DeleteByMenuId(menuId uint) (err error) + DeleteByUserLevelIdAndMenuId(userLevelId uint, menuId uint) (err error) +} + +func NewUserLevelMenuActionAccessesRepository(db *database.Database) UserLevelMenuActionAccessesRepository { + return &userLevelMenuActionAccessesRepository{ + DB: db, + } +} + +// implement interface of IUserLevelMenuActionAccessesRepository +func (_i *userLevelMenuActionAccessesRepository) GetAll(req request.UserLevelMenuActionAccessesQueryRequest) (accesses []*entity.UserLevelMenuActionAccesses, paging paginator.Pagination, err error) { + var count int64 + + query := _i.DB.DB.Model(&entity.UserLevelMenuActionAccesses{}) + query = query.Where("is_active = ?", true) + + if req.UserLevelId != nil { + query = query.Where("user_level_id = ?", req.UserLevelId) + } + if req.MenuId != nil { + query = query.Where("menu_id = ?", req.MenuId) + } + if req.ActionCode != nil && *req.ActionCode != "" { + query = query.Where("action_code = ?", req.ActionCode) + } + if req.ClientId != nil { + query = query.Where("client_id = ?", req.ClientId) + } + if req.CanAccess != nil { + query = query.Where("can_access = ?", req.CanAccess) + } + + // Preload relations + query = query.Preload("UserLevel").Preload("Menu") + + query.Count(&count) + + // Apply sorting + if req.Pagination.SortBy != "" { + direction := "ASC" + if req.Pagination.Sort == "desc" { + direction = "DESC" + } + query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) + } + + // Apply pagination (manual calculation like articles) + page := req.Pagination.Page + limit := req.Pagination.Limit + if page <= 0 { + page = 1 + } + if limit <= 0 { + limit = 10 + } + + offset := (page - 1) * limit + err = query.Offset(offset).Limit(limit).Find(&accesses).Error + if err != nil { + return + } + + // Create pagination response + paging = paginator.Pagination{ + Page: page, + Limit: limit, + Count: count, + TotalPage: int((count + int64(limit) - 1) / int64(limit)), + } + + return +} + +func (_i *userLevelMenuActionAccessesRepository) GetByUserLevelId(userLevelId uint) (accesses []*entity.UserLevelMenuActionAccesses, err error) { + query := _i.DB.DB.Model(&entity.UserLevelMenuActionAccesses{}) + query = query.Where("user_level_id = ? AND is_active = ?", userLevelId, true) + query = query.Preload("Menu") + err = query.Find(&accesses).Error + return +} + +func (_i *userLevelMenuActionAccessesRepository) GetByUserLevelIdAndMenuId(userLevelId uint, menuId uint) (accesses []*entity.UserLevelMenuActionAccesses, err error) { + query := _i.DB.DB.Model(&entity.UserLevelMenuActionAccesses{}) + query = query.Where("user_level_id = ? AND menu_id = ? AND is_active = ?", userLevelId, menuId, true) + query = query.Preload("Menu") + err = query.Find(&accesses).Error + return +} + +func (_i *userLevelMenuActionAccessesRepository) GetByMenuId(menuId uint) (accesses []*entity.UserLevelMenuActionAccesses, err error) { + query := _i.DB.DB.Model(&entity.UserLevelMenuActionAccesses{}) + query = query.Where("menu_id = ? AND is_active = ?", menuId, true) + query = query.Preload("UserLevel") + err = query.Find(&accesses).Error + return +} + +func (_i *userLevelMenuActionAccessesRepository) CheckAccess(userLevelId uint, menuId uint, actionCode string) (hasAccess bool, err error) { + var access entity.UserLevelMenuActionAccesses + query := _i.DB.DB.Model(&entity.UserLevelMenuActionAccesses{}) + query = query.Where("user_level_id = ? AND menu_id = ? AND action_code = ? AND is_active = ? AND can_access = ?", userLevelId, menuId, actionCode, true, true) + err = query.First(&access).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + return false, nil + } + return false, err + } + return true, nil +} + +func (_i *userLevelMenuActionAccessesRepository) FindOne(id uint) (access *entity.UserLevelMenuActionAccesses, err error) { + query := _i.DB.DB.Preload("UserLevel").Preload("Menu") + if err := query.First(&access, id).Error; err != nil { + return nil, err + } + return access, nil +} + +func (_i *userLevelMenuActionAccessesRepository) Create(access *entity.UserLevelMenuActionAccesses) (err error) { + return _i.DB.DB.Create(access).Error +} + +func (_i *userLevelMenuActionAccessesRepository) CreateBatch(accesses []*entity.UserLevelMenuActionAccesses) (err error) { + return _i.DB.DB.Create(&accesses).Error +} + +func (_i *userLevelMenuActionAccessesRepository) Update(id uint, access *entity.UserLevelMenuActionAccesses) (err error) { + return _i.DB.DB.Model(&entity.UserLevelMenuActionAccesses{}). + Where(&entity.UserLevelMenuActionAccesses{ID: id}). + Updates(access).Error +} + +func (_i *userLevelMenuActionAccessesRepository) Delete(id uint) (err error) { + return _i.DB.DB.Delete(&entity.UserLevelMenuActionAccesses{}, id).Error +} + +func (_i *userLevelMenuActionAccessesRepository) DeleteByUserLevelId(userLevelId uint) (err error) { + return _i.DB.DB.Where("user_level_id = ?", userLevelId).Delete(&entity.UserLevelMenuActionAccesses{}).Error +} + +func (_i *userLevelMenuActionAccessesRepository) DeleteByMenuId(menuId uint) (err error) { + return _i.DB.DB.Where("menu_id = ?", menuId).Delete(&entity.UserLevelMenuActionAccesses{}).Error +} + +func (_i *userLevelMenuActionAccessesRepository) DeleteByUserLevelIdAndMenuId(userLevelId uint, menuId uint) (err error) { + return _i.DB.DB.Where("user_level_id = ? AND menu_id = ?", userLevelId, menuId).Delete(&entity.UserLevelMenuActionAccesses{}).Error +} + diff --git a/app/module/user_level_menu_action_accesses/request/user_level_menu_action_accesses.request.go b/app/module/user_level_menu_action_accesses/request/user_level_menu_action_accesses.request.go new file mode 100644 index 0000000..315d0a3 --- /dev/null +++ b/app/module/user_level_menu_action_accesses/request/user_level_menu_action_accesses.request.go @@ -0,0 +1,72 @@ +package request + +import ( + "netidhub-saas-be/app/database/entity" + "netidhub-saas-be/utils/paginator" + "github.com/google/uuid" +) + +type UserLevelMenuActionAccessesQueryRequest struct { + UserLevelId *uint `query:"user_level_id"` + MenuId *uint `query:"menu_id"` + ActionCode *string `query:"action_code"` + ClientId *uuid.UUID `query:"client_id"` + CanAccess *bool `query:"can_access"` + Pagination *paginator.Pagination `query:"pagination"` +} + +type UserLevelMenuActionAccessesCreateRequest struct { + UserLevelId uint `json:"userLevelId" validate:"required"` + MenuId uint `json:"menuId" validate:"required"` + ActionCode string `json:"actionCode" validate:"required"` + CanAccess bool `json:"canAccess"` + ClientId *uuid.UUID `json:"clientId"` + IsActive *bool `json:"isActive"` +} + +func (req UserLevelMenuActionAccessesCreateRequest) ToEntity() *entity.UserLevelMenuActionAccesses { + return &entity.UserLevelMenuActionAccesses{ + UserLevelId: req.UserLevelId, + MenuId: req.MenuId, + ActionCode: req.ActionCode, + CanAccess: req.CanAccess, + ClientId: req.ClientId, + IsActive: req.IsActive, + } +} + +type UserLevelMenuActionAccessesUpdateRequest struct { + UserLevelId *uint `json:"userLevelId"` + MenuId *uint `json:"menuId"` + ActionCode *string `json:"actionCode"` + CanAccess *bool `json:"canAccess"` + ClientId *uuid.UUID `json:"clientId"` + IsActive *bool `json:"isActive"` +} + +func (req UserLevelMenuActionAccessesUpdateRequest) ToEntity() *entity.UserLevelMenuActionAccesses { + access := &entity.UserLevelMenuActionAccesses{} + if req.UserLevelId != nil { + access.UserLevelId = *req.UserLevelId + } + if req.MenuId != nil { + access.MenuId = *req.MenuId + } + if req.ActionCode != nil { + access.ActionCode = *req.ActionCode + } + if req.CanAccess != nil { + access.CanAccess = *req.CanAccess + } + access.ClientId = req.ClientId + access.IsActive = req.IsActive + return access +} + +type UserLevelMenuActionAccessesBatchCreateRequest struct { + UserLevelId uint `json:"userLevelId" validate:"required"` + MenuId uint `json:"menuId" validate:"required"` + ActionCodes []string `json:"actionCodes" validate:"required,min=1"` + ClientId *uuid.UUID `json:"clientId"` +} + diff --git a/app/module/user_level_menu_action_accesses/response/user_level_menu_action_accesses.response.go b/app/module/user_level_menu_action_accesses/response/user_level_menu_action_accesses.response.go new file mode 100644 index 0000000..39e8bf0 --- /dev/null +++ b/app/module/user_level_menu_action_accesses/response/user_level_menu_action_accesses.response.go @@ -0,0 +1,15 @@ +package response + +import "time" + +type UserLevelMenuActionAccessesResponse struct { + ID uint `json:"id"` + UserLevelId uint `json:"userLevelId"` + MenuId uint `json:"menuId"` + ActionCode string `json:"actionCode"` + CanAccess bool `json:"canAccess"` + IsActive *bool `json:"isActive"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + diff --git a/app/module/user_level_menu_action_accesses/service/user_level_menu_action_accesses.service.go b/app/module/user_level_menu_action_accesses/service/user_level_menu_action_accesses.service.go new file mode 100644 index 0000000..d0f1748 --- /dev/null +++ b/app/module/user_level_menu_action_accesses/service/user_level_menu_action_accesses.service.go @@ -0,0 +1,166 @@ +package service + +import ( + "github.com/rs/zerolog" + "netidhub-saas-be/app/database/entity" + "netidhub-saas-be/app/module/user_level_menu_action_accesses/mapper" + "netidhub-saas-be/app/module/user_level_menu_action_accesses/repository" + "netidhub-saas-be/app/module/user_level_menu_action_accesses/request" + "netidhub-saas-be/app/module/user_level_menu_action_accesses/response" + "netidhub-saas-be/utils/paginator" +) + +// UserLevelMenuActionAccessesService +type userLevelMenuActionAccessesService struct { + Repo repository.UserLevelMenuActionAccessesRepository + Log zerolog.Logger +} + +// UserLevelMenuActionAccessesService define interface of IUserLevelMenuActionAccessesService +type UserLevelMenuActionAccessesService interface { + All(req request.UserLevelMenuActionAccessesQueryRequest) (accesses []*response.UserLevelMenuActionAccessesResponse, paging paginator.Pagination, err error) + GetByUserLevelId(userLevelId uint) (accesses []*response.UserLevelMenuActionAccessesResponse, err error) + GetByUserLevelIdAndMenuId(userLevelId uint, menuId uint) (accesses []*response.UserLevelMenuActionAccessesResponse, err error) + GetByMenuId(menuId uint) (accesses []*response.UserLevelMenuActionAccessesResponse, err error) + CheckAccess(userLevelId uint, menuId uint, actionCode string) (hasAccess bool, err error) + Show(id uint) (access *response.UserLevelMenuActionAccessesResponse, err error) + Save(req request.UserLevelMenuActionAccessesCreateRequest) (err error) + SaveBatch(req request.UserLevelMenuActionAccessesBatchCreateRequest) (err error) + Update(id uint, req request.UserLevelMenuActionAccessesUpdateRequest) (err error) + Delete(id uint) error +} + +// NewUserLevelMenuActionAccessesService init UserLevelMenuActionAccessesService +func NewUserLevelMenuActionAccessesService(repo repository.UserLevelMenuActionAccessesRepository, log zerolog.Logger) UserLevelMenuActionAccessesService { + return &userLevelMenuActionAccessesService{ + Repo: repo, + Log: log, + } +} + +// All implement interface of UserLevelMenuActionAccessesService +func (_i *userLevelMenuActionAccessesService) All(req request.UserLevelMenuActionAccessesQueryRequest) (accesses []*response.UserLevelMenuActionAccessesResponse, paging paginator.Pagination, err error) { + results, paging, err := _i.Repo.GetAll(req) + if err != nil { + return + } + + for _, result := range results { + accesses = append(accesses, mapper.UserLevelMenuActionAccessesResponseMapper(result)) + } + + return +} + +func (_i *userLevelMenuActionAccessesService) GetByUserLevelId(userLevelId uint) (accesses []*response.UserLevelMenuActionAccessesResponse, err error) { + results, err := _i.Repo.GetByUserLevelId(userLevelId) + if err != nil { + return nil, err + } + + for _, result := range results { + accesses = append(accesses, mapper.UserLevelMenuActionAccessesResponseMapper(result)) + } + + return +} + +func (_i *userLevelMenuActionAccessesService) GetByUserLevelIdAndMenuId(userLevelId uint, menuId uint) (accesses []*response.UserLevelMenuActionAccessesResponse, err error) { + results, err := _i.Repo.GetByUserLevelIdAndMenuId(userLevelId, menuId) + if err != nil { + return nil, err + } + + for _, result := range results { + accesses = append(accesses, mapper.UserLevelMenuActionAccessesResponseMapper(result)) + } + + return +} + +func (_i *userLevelMenuActionAccessesService) GetByMenuId(menuId uint) (accesses []*response.UserLevelMenuActionAccessesResponse, err error) { + results, err := _i.Repo.GetByMenuId(menuId) + if err != nil { + return nil, err + } + + for _, result := range results { + accesses = append(accesses, mapper.UserLevelMenuActionAccessesResponseMapper(result)) + } + + return +} + +func (_i *userLevelMenuActionAccessesService) CheckAccess(userLevelId uint, menuId uint, actionCode string) (hasAccess bool, err error) { + return _i.Repo.CheckAccess(userLevelId, menuId, actionCode) +} + +func (_i *userLevelMenuActionAccessesService) Show(id uint) (access *response.UserLevelMenuActionAccessesResponse, err error) { + result, err := _i.Repo.FindOne(id) + if err != nil { + return nil, err + } + + return mapper.UserLevelMenuActionAccessesResponseMapper(result), nil +} + +func (_i *userLevelMenuActionAccessesService) Save(req request.UserLevelMenuActionAccessesCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Creating user level menu action access") + + isActive := true + if req.IsActive != nil { + isActive = *req.IsActive + } + + access := req.ToEntity() + access.IsActive = &isActive + + return _i.Repo.Create(access) +} + +func (_i *userLevelMenuActionAccessesService) SaveBatch(req request.UserLevelMenuActionAccessesBatchCreateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Creating user level menu action accesses batch") + + isActive := true + var accesses []*entity.UserLevelMenuActionAccesses + + for _, actionCode := range req.ActionCodes { + access := &entity.UserLevelMenuActionAccesses{ + UserLevelId: req.UserLevelId, + MenuId: req.MenuId, + ActionCode: actionCode, + CanAccess: true, + ClientId: req.ClientId, + IsActive: &isActive, + } + accesses = append(accesses, access) + } + + return _i.Repo.CreateBatch(accesses) +} + +func (_i *userLevelMenuActionAccessesService) Update(id uint, req request.UserLevelMenuActionAccessesUpdateRequest) (err error) { + _i.Log.Info().Interface("data", req).Msg("Updating user level menu action access") + + isActive := true + if req.IsActive != nil { + isActive = *req.IsActive + } + + access := req.ToEntity() + access.IsActive = &isActive + + return _i.Repo.Update(id, access) +} + +func (_i *userLevelMenuActionAccessesService) Delete(id uint) error { + result, err := _i.Repo.FindOne(id) + if err != nil { + return err + } + + isActive := false + result.IsActive = &isActive + return _i.Repo.Update(id, result) +} + diff --git a/app/module/user_level_menu_action_accesses/user_level_menu_action_accesses.module.go b/app/module/user_level_menu_action_accesses/user_level_menu_action_accesses.module.go new file mode 100644 index 0000000..0c44376 --- /dev/null +++ b/app/module/user_level_menu_action_accesses/user_level_menu_action_accesses.module.go @@ -0,0 +1,59 @@ +package user_level_menu_action_accesses + +import ( + "github.com/gofiber/fiber/v2" + "go.uber.org/fx" + "netidhub-saas-be/app/module/user_level_menu_action_accesses/controller" + "netidhub-saas-be/app/module/user_level_menu_action_accesses/repository" + "netidhub-saas-be/app/module/user_level_menu_action_accesses/service" +) + +// struct of UserLevelMenuActionAccessesRouter +type UserLevelMenuActionAccessesRouter struct { + App fiber.Router + Controller *controller.Controller +} + +// register bulky of UserLevelMenuActionAccesses module +var NewUserLevelMenuActionAccessesModule = fx.Options( + // register repository of UserLevelMenuActionAccesses module + fx.Provide(repository.NewUserLevelMenuActionAccessesRepository), + + // register service of UserLevelMenuActionAccesses module + fx.Provide(service.NewUserLevelMenuActionAccessesService), + + // register controller of UserLevelMenuActionAccesses module + fx.Provide(controller.NewController), + + // register router of UserLevelMenuActionAccesses module + fx.Provide(NewUserLevelMenuActionAccessesRouter), +) + +// init UserLevelMenuActionAccessesRouter +func NewUserLevelMenuActionAccessesRouter(fiber *fiber.App, controller *controller.Controller) *UserLevelMenuActionAccessesRouter { + return &UserLevelMenuActionAccessesRouter{ + App: fiber, + Controller: controller, + } +} + +// register routes of UserLevelMenuActionAccesses module +func (_i *UserLevelMenuActionAccessesRouter) RegisterUserLevelMenuActionAccessesRoutes() { + // define controllers + userLevelMenuActionAccessesController := _i.Controller.UserLevelMenuActionAccesses + + // define routes + _i.App.Route("/user-level-menu-action-accesses", func(router fiber.Router) { + router.Get("/", userLevelMenuActionAccessesController.All) + router.Get("/:id", userLevelMenuActionAccessesController.Show) + router.Get("/user-level/:user_level_id", userLevelMenuActionAccessesController.GetByUserLevelId) + router.Get("/user-level/:user_level_id/menu/:menu_id", userLevelMenuActionAccessesController.GetByUserLevelIdAndMenuId) + router.Get("/menu/:menu_id", userLevelMenuActionAccessesController.GetByMenuId) + router.Get("/check/:user_level_id/:menu_id/:action_code", userLevelMenuActionAccessesController.CheckAccess) + router.Post("/", userLevelMenuActionAccessesController.Save) + router.Post("/batch", userLevelMenuActionAccessesController.SaveBatch) + router.Put("/:id", userLevelMenuActionAccessesController.Update) + router.Delete("/:id", userLevelMenuActionAccessesController.Delete) + }) +} + diff --git a/app/module/user_level_module_accesses/repository/user_level_module_accesses.repository.go b/app/module/user_level_module_accesses/repository/user_level_module_accesses.repository.go index a29a065..31e79ee 100644 --- a/app/module/user_level_module_accesses/repository/user_level_module_accesses.repository.go +++ b/app/module/user_level_module_accesses/repository/user_level_module_accesses.repository.go @@ -58,6 +58,7 @@ func (_i *userLevelModuleAccessesRepository) GetAll(req request.UserLevelModuleA query.Count(&count) + // Apply sorting if req.Pagination.SortBy != "" { direction := "ASC" if req.Pagination.Sort == "desc" { @@ -66,15 +67,29 @@ func (_i *userLevelModuleAccessesRepository) GetAll(req request.UserLevelModuleA query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction)) } - req.Pagination.Count = count - req.Pagination = paginator.Paging(req.Pagination) + // Apply pagination (manual calculation like articles) + page := req.Pagination.Page + limit := req.Pagination.Limit + if page <= 0 { + page = 1 + } + if limit <= 0 { + limit = 10 + } - err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&accesses).Error + offset := (page - 1) * limit + err = query.Offset(offset).Limit(limit).Find(&accesses).Error if err != nil { return } - paging = *req.Pagination + // Create pagination response + paging = paginator.Pagination{ + Page: page, + Limit: limit, + Count: count, + TotalPage: int((count + int64(limit) - 1) / int64(limit)), + } return } diff --git a/app/module/user_level_module_accesses/request/user_level_module_accesses.request.go b/app/module/user_level_module_accesses/request/user_level_module_accesses.request.go index d9af3c2..7d9f065 100644 --- a/app/module/user_level_module_accesses/request/user_level_module_accesses.request.go +++ b/app/module/user_level_module_accesses/request/user_level_module_accesses.request.go @@ -6,11 +6,11 @@ import ( ) type UserLevelModuleAccessesQueryRequest struct { - UserLevelId *uint `query:"user_level_id"` - ModuleId *uint `query:"module_id"` - CanAccess *bool `query:"can_access"` - ClientId *uuid.UUID `query:"client_id"` - Pagination *paginator.Query `query:"pagination"` + UserLevelId *uint `query:"user_level_id"` + ModuleId *uint `query:"module_id"` + CanAccess *bool `query:"can_access"` + ClientId *uuid.UUID `query:"client_id"` + Pagination *paginator.Pagination `query:"pagination"` } type UserLevelModuleAccessesCreateRequest struct { diff --git a/app/router/api.go b/app/router/api.go index 7b82684..365cea5 100644 --- a/app/router/api.go +++ b/app/router/api.go @@ -25,6 +25,9 @@ import ( "netidhub-saas-be/app/module/magazines" "netidhub-saas-be/app/module/master_menus" "netidhub-saas-be/app/module/master_modules" + "netidhub-saas-be/app/module/menu_actions" + "netidhub-saas-be/app/module/user_level_menu_accesses" + "netidhub-saas-be/app/module/user_level_menu_action_accesses" "netidhub-saas-be/app/module/provinces" "netidhub-saas-be/app/module/schedules" "netidhub-saas-be/app/module/subscription" @@ -65,9 +68,12 @@ type Router struct { FeedbacksRouter *feedbacks.FeedbacksRouter MagazineFilesRouter *magazine_files.MagazineFilesRouter MagazinesRouter *magazines.MagazinesRouter - MasterMenusRouter *master_menus.MasterMenusRouter - MasterModulesRouter *master_modules.MasterModulesRouter - ProvincesRouter *provinces.ProvincesRouter + MasterMenusRouter *master_menus.MasterMenusRouter + MasterModulesRouter *master_modules.MasterModulesRouter + MenuActionsRouter *menu_actions.MenuActionsRouter + UserLevelMenuAccessesRouter *user_level_menu_accesses.UserLevelMenuAccessesRouter + UserLevelMenuActionAccessesRouter *user_level_menu_action_accesses.UserLevelMenuActionAccessesRouter + ProvincesRouter *provinces.ProvincesRouter SchedulesRouter *schedules.SchedulesRouter SubscriptionRouter *subscription.SubscriptionRouter UserLevelsRouter *user_levels.UserLevelsRouter @@ -104,6 +110,9 @@ func NewRouter( magazinesRouter *magazines.MagazinesRouter, masterMenuRouter *master_menus.MasterMenusRouter, masterModuleRouter *master_modules.MasterModulesRouter, + menuActionsRouter *menu_actions.MenuActionsRouter, + userLevelMenuAccessesRouter *user_level_menu_accesses.UserLevelMenuAccessesRouter, + userLevelMenuActionAccessesRouter *user_level_menu_action_accesses.UserLevelMenuActionAccessesRouter, provincesRouter *provinces.ProvincesRouter, schedulesRouter *schedules.SchedulesRouter, subscriptionRouter *subscription.SubscriptionRouter, @@ -137,9 +146,12 @@ func NewRouter( FeedbacksRouter: feedbacksRouter, MagazineFilesRouter: magazineFilesRouter, MagazinesRouter: magazinesRouter, - MasterMenusRouter: masterMenuRouter, - MasterModulesRouter: masterModuleRouter, - ProvincesRouter: provincesRouter, + MasterMenusRouter: masterMenuRouter, + MasterModulesRouter: masterModuleRouter, + MenuActionsRouter: menuActionsRouter, + UserLevelMenuAccessesRouter: userLevelMenuAccessesRouter, + UserLevelMenuActionAccessesRouter: userLevelMenuActionAccessesRouter, + ProvincesRouter: provincesRouter, SchedulesRouter: schedulesRouter, SubscriptionRouter: subscriptionRouter, UserLevelsRouter: userLevelsRouter, @@ -184,6 +196,9 @@ func (r *Router) Register() { r.MagazineFilesRouter.RegisterMagazineFilesRoutes() r.MasterMenusRouter.RegisterMasterMenusRoutes() r.MasterModulesRouter.RegisterMasterModulesRoutes() + r.MenuActionsRouter.RegisterMenuActionsRoutes() + r.UserLevelMenuAccessesRouter.RegisterUserLevelMenuAccessesRoutes() + r.UserLevelMenuActionAccessesRouter.RegisterUserLevelMenuActionAccessesRoutes() r.ProvincesRouter.RegisterProvincesRoutes() r.SchedulesRouter.RegisterSchedulesRoutes() r.SubscriptionRouter.RegisterSubscriptionRoutes() diff --git a/docs/PLAN_MENU_ACTION_ACCESS_SYSTEM.md b/docs/PLAN_MENU_ACTION_ACCESS_SYSTEM.md new file mode 100644 index 0000000..de42288 --- /dev/null +++ b/docs/PLAN_MENU_ACTION_ACCESS_SYSTEM.md @@ -0,0 +1,311 @@ +# 📋 Plan: Menu & Action Access Control System + +## 🎯 Tujuan + +Membangun sistem akses kontrol yang memungkinkan: +1. **1 Menu memiliki banyak Actions** - Satu menu dapat memiliki berbagai action (view table, create, edit, delete, approve, export, dll) +2. **User Level dapat di-assign Menu** - Setiap user level dapat di-assign menu apa saja yang bisa diakses +3. **User Level dapat di-assign Actions per Menu** - Setiap user level dapat di-assign action apa saja yang bisa dilakukan di setiap menu (contoh: Creator hanya bisa create dan edit, Approver bisa delete) + +## 🔍 Analisis Kebutuhan + +### Use Case 1: Menu "Content Management" +- **Actions yang tersedia:** + - `view` - Melihat daftar content + - `create` - Membuat content baru + - `edit` - Mengedit content yang ada + - `delete` - Menghapus content + - `approve` - Approve content + - `export` - Export data content + +### Use Case 2: User Level "Creator" +- **Menu yang bisa diakses:** Content Management +- **Actions yang bisa dilakukan:** + - ✅ `view` - Bisa melihat daftar + - ✅ `create` - Bisa membuat baru + - ✅ `edit` - Bisa mengedit + - ❌ `delete` - Tidak bisa menghapus + - ❌ `approve` - Tidak bisa approve + - ❌ `export` - Tidak bisa export + +### Use Case 3: User Level "Approver" +- **Menu yang bisa diakses:** Content Management +- **Actions yang bisa dilakukan:** + - ✅ `view` - Bisa melihat daftar + - ❌ `create` - Tidak bisa membuat baru + - ❌ `edit` - Tidak bisa mengedit + - ✅ `delete` - Bisa menghapus + - ✅ `approve` - Bisa approve + - ✅ `export` - Bisa export + +## 🏗️ Arsitektur Sistem + +### Opsi 1: Menu-Action Based (Recommended) ⭐ + +**Konsep:** +- Menu memiliki Actions langsung (bukan melalui Module) +- User Level memiliki akses ke Menu +- User Level memiliki akses ke Actions di dalam Menu + +**Struktur Database:** + +#### 1. `master_menus` (Existing - Enhanced) +```sql +-- Sudah ada, tidak perlu perubahan +-- Menu seperti: "Content Management", "User Management", "Settings" +``` + +#### 2. `menu_actions` (New) +```sql +CREATE TABLE menu_actions ( + id SERIAL PRIMARY KEY, + menu_id INT NOT NULL, + action_code VARCHAR(50) NOT NULL, -- 'view', 'create', 'edit', 'delete', 'approve', 'export' + action_name VARCHAR(255) NOT NULL, -- 'View Content', 'Create Content', etc. + description TEXT NULL, + path_url VARCHAR(255) NULL, -- Optional: untuk routing frontend + http_method VARCHAR(10) NULL, -- Optional: 'GET', 'POST', 'PUT', 'DELETE' + position INT NULL, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + FOREIGN KEY (menu_id) REFERENCES master_menus(id) ON DELETE CASCADE, + UNIQUE(menu_id, action_code) +); + +CREATE INDEX idx_menu_actions_menu_id ON menu_actions(menu_id); +CREATE INDEX idx_menu_actions_action_code ON menu_actions(action_code); +``` + +#### 3. `user_level_menu_accesses` (New) +```sql +CREATE TABLE user_level_menu_accesses ( + id SERIAL PRIMARY KEY, + user_level_id INT NOT NULL, + menu_id INT NOT NULL, + can_access BOOLEAN DEFAULT TRUE, + client_id UUID NULL, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + FOREIGN KEY (user_level_id) REFERENCES user_levels(id) ON DELETE CASCADE, + FOREIGN KEY (menu_id) REFERENCES master_menus(id) ON DELETE CASCADE, + UNIQUE(user_level_id, menu_id, client_id) +); + +CREATE INDEX idx_user_level_menu_accesses_user_level_id ON user_level_menu_accesses(user_level_id); +CREATE INDEX idx_user_level_menu_accesses_menu_id ON user_level_menu_accesses(menu_id); +``` + +#### 4. `user_level_menu_action_accesses` (New) +```sql +CREATE TABLE user_level_menu_action_accesses ( + id SERIAL PRIMARY KEY, + user_level_id INT NOT NULL, + menu_id INT NOT NULL, + action_code VARCHAR(50) NOT NULL, + can_access BOOLEAN DEFAULT TRUE, + client_id UUID NULL, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + FOREIGN KEY (user_level_id) REFERENCES user_levels(id) ON DELETE CASCADE, + FOREIGN KEY (menu_id) REFERENCES master_menus(id) ON DELETE CASCADE, + FOREIGN KEY (menu_id, action_code) REFERENCES menu_actions(menu_id, action_code) ON DELETE CASCADE, + UNIQUE(user_level_id, menu_id, action_code, client_id) +); + +CREATE INDEX idx_user_level_menu_action_accesses_user_level_id ON user_level_menu_action_accesses(user_level_id); +CREATE INDEX idx_user_level_menu_action_accesses_menu_id ON user_level_menu_action_accesses(menu_id); +CREATE INDEX idx_user_level_menu_action_accesses_action_code ON user_level_menu_action_accesses(action_code); +``` + +**Relasi:** +``` +master_menus (1) ─────< (N) menu_actions + │ │ + │ │ + │ (1) │ (N) + │ │ + ∨ ∨ +user_level_menu_accesses user_level_menu_action_accesses + │ │ + │ (N) │ (N) + │ │ + ∨ ∨ +user_levels (1) ──────────────┘ +``` + +**Keuntungan:** +- ✅ Lebih sederhana dan intuitif +- ✅ Langsung ke point: Menu → Actions +- ✅ Mudah di-manage: assign menu, lalu assign actions +- ✅ Tidak perlu konsep "Module" yang membingungkan +- ✅ Frontend lebih mudah: cek menu access, lalu cek action access + +**Kekurangan:** +- ❌ Perlu migration dari sistem Module yang ada +- ❌ Perlu refactor middleware dan service yang sudah ada + +--- + +### Opsi 2: Keep Module, Enhance Structure + +**Konsep:** +- Tetap menggunakan Module, tapi Module sekarang lebih spesifik ke Menu +- Module = Action di dalam Menu +- User Level memiliki akses ke Menu +- User Level memiliki akses ke Module (Action) di dalam Menu + +**Struktur Database:** + +#### 1. `master_menus` (Existing) +```sql +-- Tidak berubah +``` + +#### 2. `master_modules` (Existing - Enhanced) +```sql +-- Tetap ada, tapi sekarang lebih spesifik: +-- Module sekarang = Action di dalam Menu +-- action_type menjadi lebih penting +``` + +#### 3. `menu_modules` (Existing - Enhanced) +```sql +-- Tetap ada, relasi Menu dengan Module (Action) +-- Tapi sekarang lebih strict: 1 module hanya untuk 1 menu +``` + +#### 4. `user_level_menu_accesses` (New) +```sql +-- Sama seperti Opsi 1 +``` + +#### 5. `user_level_module_accesses` (Existing) +```sql +-- Tetap ada, tapi sekarang lebih spesifik: +-- Module = Action di dalam Menu +``` + +**Keuntungan:** +- ✅ Tidak perlu migration besar-besaran +- ✅ Bisa reuse struktur yang sudah ada +- ✅ Lebih fleksibel (module bisa digunakan oleh banyak menu) + +**Kekurangan:** +- ❌ Konsep "Module" masih membingungkan +- ❌ Lebih kompleks: Menu → Module → Action +- ❌ Perlu mapping yang lebih kompleks + +--- + +## 🎯 Rekomendasi: Opsi 1 (Menu-Action Based) + +**Alasan:** +1. **Lebih intuitif** - Langsung ke point: Menu punya Actions +2. **Lebih mudah di-manage** - Admin langsung assign menu dan actions +3. **Lebih mudah di-frontend** - Cek menu access, lalu cek action access +4. **Lebih scalable** - Mudah ditambah menu baru dengan actions baru +5. **Lebih maintainable** - Struktur lebih jelas dan mudah dipahami + +## 📝 Implementation Plan + +### Phase 1: Database Schema +1. ✅ Buat tabel `menu_actions` +2. ✅ Buat tabel `user_level_menu_accesses` +3. ✅ Buat tabel `user_level_menu_action_accesses` +4. ✅ Buat migration script +5. ✅ Migrate data dari `master_modules` dan `menu_modules` ke `menu_actions` (jika perlu) + +### Phase 2: Backend Entities & Repositories +1. ✅ Buat entity `MenuActions` +2. ✅ Buat entity `UserLevelMenuAccesses` +3. ✅ Buat entity `UserLevelMenuActionAccesses` +4. ✅ Buat repository untuk masing-masing entity +5. ✅ Buat service untuk masing-masing entity + +### Phase 3: Backend API Endpoints +1. ✅ CRUD API untuk `menu_actions` +2. ✅ CRUD API untuk `user_level_menu_accesses` +3. ✅ CRUD API untuk `user_level_menu_action_accesses` +4. ✅ API untuk get menu actions by menu_id +5. ✅ API untuk get user level menu accesses +6. ✅ API untuk get user level menu action accesses + +### Phase 4: Backend Middleware +1. ✅ Buat middleware `CheckMenuAccess` - cek apakah user level bisa akses menu +2. ✅ Buat middleware `CheckMenuActionAccess` - cek apakah user level bisa akses action di menu +3. ✅ Update existing middleware untuk menggunakan struktur baru + +### Phase 5: Frontend Services +1. ✅ Buat service untuk `menu_actions` +2. ✅ Buat service untuk `user_level_menu_accesses` +3. ✅ Buat service untuk `user_level_menu_action_accesses` +4. ✅ Update existing services + +### Phase 6: Frontend UI - Menu Management +1. ✅ Update halaman Menu Management untuk menampilkan Actions +2. ✅ Tambah form untuk manage Actions di setiap Menu +3. ✅ Tambah UI untuk assign Actions ke Menu + +### Phase 7: Frontend UI - User Level Management +1. ✅ Update halaman User Level Management +2. ✅ Tambah tab "Menu Access" untuk assign menu ke user level +3. ✅ Tambah tab "Action Access" untuk assign actions per menu ke user level +4. ✅ Update form User Level untuk include menu dan action access + +### Phase 8: Frontend UI - Permission Check +1. ✅ Buat hook `useMenuAccess` untuk cek menu access +2. ✅ Buat hook `useMenuActionAccess` untuk cek action access +3. ✅ Update components untuk menggunakan hooks +4. ✅ Hide/show UI elements berdasarkan permission + +### Phase 9: Testing & Documentation +1. ✅ Test semua API endpoints +2. ✅ Test middleware +3. ✅ Test frontend UI +4. ✅ Update documentation + +## 🔄 Migration Strategy + +### Option A: Clean Slate (Recommended) +- Buat struktur baru dari awal +- Data lama tetap ada tapi tidak digunakan +- Migrate data secara bertahap jika perlu + +### Option B: Gradual Migration +- Keep struktur lama dan baru berjalan bersamaan +- Migrate data dari lama ke baru +- Deprecate struktur lama setelah semua ter-migrate + +## 📊 Comparison: Current vs Proposed + +### Current System +``` +Menu → Module (via menu_modules) → User Level Access (via user_level_module_accesses) +``` +- Module adalah entitas terpisah +- Module bisa digunakan oleh banyak menu +- Akses diberikan ke Module, bukan ke Menu + Action + +### Proposed System +``` +Menu → Actions (via menu_actions) → User Level Menu Access → User Level Action Access +``` +- Actions langsung di dalam Menu +- Actions spesifik untuk Menu tertentu +- Akses diberikan ke Menu dan Actions secara terpisah + +## ✅ Next Steps + +1. **Review & Approval** - Review plan ini dengan tim +2. **Decision** - Pilih antara Opsi 1 atau Opsi 2 +3. **Database Design** - Finalize database schema +4. **Implementation** - Mulai implementasi sesuai phase yang sudah direncanakan + +## 📚 Reference + +- Current system: `docs/MENU_MODULE_ACCESS_SYSTEM.md` +- Implementation summary: `docs/IMPLEMENTATION_SUMMARY_MENU_MODULE_ACCESS.md` + diff --git a/docs/migrations/005_add_menu_action_access_system.sql b/docs/migrations/005_add_menu_action_access_system.sql new file mode 100644 index 0000000..3f5ff90 --- /dev/null +++ b/docs/migrations/005_add_menu_action_access_system.sql @@ -0,0 +1,195 @@ +-- Migration: Add Menu Action Access System +-- Description: Create menu_actions, user_level_menu_accesses, and user_level_menu_action_accesses tables +-- Date: 2026-01-16 + +-- ============================================================================ +-- Step 1: Create menu_actions table +-- ============================================================================ +CREATE TABLE IF NOT EXISTS menu_actions ( + id SERIAL PRIMARY KEY, + menu_id INT NOT NULL, + action_code VARCHAR(50) NOT NULL, + action_name VARCHAR(255) NOT NULL, + description TEXT NULL, + path_url VARCHAR(255) NULL, + http_method VARCHAR(10) NULL, + position INT NULL, + client_id UUID NULL, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + FOREIGN KEY (menu_id) REFERENCES master_menus(id) ON DELETE CASCADE, + UNIQUE(menu_id, action_code, COALESCE(client_id, '00000000-0000-0000-0000-000000000000'::UUID)) +); + +-- Add indexes for performance +CREATE INDEX idx_menu_actions_menu_id ON menu_actions(menu_id); +CREATE INDEX idx_menu_actions_action_code ON menu_actions(action_code); +CREATE INDEX idx_menu_actions_client_id ON menu_actions(client_id) WHERE client_id IS NOT NULL; +CREATE INDEX idx_menu_actions_is_active ON menu_actions(is_active); + +-- Add comments +COMMENT ON TABLE menu_actions IS 'Actions yang tersedia di setiap menu (view, create, edit, delete, approve, export, etc)'; +COMMENT ON COLUMN menu_actions.menu_id IS 'Foreign key ke master_menus'; +COMMENT ON COLUMN menu_actions.action_code IS 'Kode action: view, create, edit, delete, approve, export, etc'; +COMMENT ON COLUMN menu_actions.action_name IS 'Nama action yang ditampilkan ke user'; +COMMENT ON COLUMN menu_actions.path_url IS 'Optional: URL path untuk routing frontend'; +COMMENT ON COLUMN menu_actions.http_method IS 'Optional: HTTP method (GET, POST, PUT, DELETE)'; + +-- ============================================================================ +-- Step 2: Create user_level_menu_accesses table +-- ============================================================================ +CREATE TABLE IF NOT EXISTS user_level_menu_accesses ( + id SERIAL PRIMARY KEY, + user_level_id INT NOT NULL, + menu_id INT NOT NULL, + can_access BOOLEAN DEFAULT TRUE, + client_id UUID NULL, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + FOREIGN KEY (user_level_id) REFERENCES user_levels(id) ON DELETE CASCADE, + FOREIGN KEY (menu_id) REFERENCES master_menus(id) ON DELETE CASCADE, + UNIQUE(user_level_id, menu_id, COALESCE(client_id, '00000000-0000-0000-0000-000000000000'::UUID)) +); + +-- Add indexes for performance +CREATE INDEX idx_user_level_menu_accesses_user_level_id ON user_level_menu_accesses(user_level_id); +CREATE INDEX idx_user_level_menu_accesses_menu_id ON user_level_menu_accesses(menu_id); +CREATE INDEX idx_user_level_menu_accesses_client_id ON user_level_menu_accesses(client_id) WHERE client_id IS NOT NULL; +CREATE INDEX idx_user_level_menu_accesses_is_active ON user_level_menu_accesses(is_active); + +-- Add comments +COMMENT ON TABLE user_level_menu_accesses IS 'Mengatur akses user_level ke menu tertentu'; +COMMENT ON COLUMN user_level_menu_accesses.user_level_id IS 'Foreign key ke user_levels'; +COMMENT ON COLUMN user_level_menu_accesses.menu_id IS 'Foreign key ke master_menus'; +COMMENT ON COLUMN user_level_menu_accesses.can_access IS 'Apakah user level boleh mengakses menu ini'; + +-- ============================================================================ +-- Step 3: Create user_level_menu_action_accesses table +-- ============================================================================ +CREATE TABLE IF NOT EXISTS user_level_menu_action_accesses ( + id SERIAL PRIMARY KEY, + user_level_id INT NOT NULL, + menu_id INT NOT NULL, + action_code VARCHAR(50) NOT NULL, + can_access BOOLEAN DEFAULT TRUE, + client_id UUID NULL, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + FOREIGN KEY (user_level_id) REFERENCES user_levels(id) ON DELETE CASCADE, + FOREIGN KEY (menu_id) REFERENCES master_menus(id) ON DELETE CASCADE, + UNIQUE(user_level_id, menu_id, action_code, COALESCE(client_id, '00000000-0000-0000-0000-000000000000'::UUID)) +); + +-- Add indexes for performance +CREATE INDEX idx_user_level_menu_action_accesses_user_level_id ON user_level_menu_action_accesses(user_level_id); +CREATE INDEX idx_user_level_menu_action_accesses_menu_id ON user_level_menu_action_accesses(menu_id); +CREATE INDEX idx_user_level_menu_action_accesses_action_code ON user_level_menu_action_accesses(action_code); +CREATE INDEX idx_user_level_menu_action_accesses_client_id ON user_level_menu_action_accesses(client_id) WHERE client_id IS NOT NULL; +CREATE INDEX idx_user_level_menu_action_accesses_is_active ON user_level_menu_action_accesses(is_active); + +-- Add comments +COMMENT ON TABLE user_level_menu_action_accesses IS 'Mengatur akses user_level ke action tertentu di dalam menu'; +COMMENT ON COLUMN user_level_menu_action_accesses.user_level_id IS 'Foreign key ke user_levels'; +COMMENT ON COLUMN user_level_menu_action_accesses.menu_id IS 'Foreign key ke master_menus'; +COMMENT ON COLUMN user_level_menu_action_accesses.action_code IS 'Kode action (harus sesuai dengan menu_actions.action_code)'; +COMMENT ON COLUMN user_level_menu_action_accesses.can_access IS 'Apakah user level boleh melakukan action ini'; + +-- ============================================================================ +-- Step 4: Create trigger for updated_at +-- ============================================================================ +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ language 'plpgsql'; + +-- Apply trigger to menu_actions +DROP TRIGGER IF EXISTS update_menu_actions_updated_at ON menu_actions; +CREATE TRIGGER update_menu_actions_updated_at + BEFORE UPDATE ON menu_actions + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +-- Apply trigger to user_level_menu_accesses +DROP TRIGGER IF EXISTS update_user_level_menu_accesses_updated_at ON user_level_menu_accesses; +CREATE TRIGGER update_user_level_menu_accesses_updated_at + BEFORE UPDATE ON user_level_menu_accesses + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +-- Apply trigger to user_level_menu_action_accesses +DROP TRIGGER IF EXISTS update_user_level_menu_action_accesses_updated_at ON user_level_menu_action_accesses; +CREATE TRIGGER update_user_level_menu_action_accesses_updated_at + BEFORE UPDATE ON user_level_menu_action_accesses + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================================================ +-- Verification +-- ============================================================================ +DO $$ +DECLARE + v_menu_actions_exists BOOLEAN; + v_user_level_menu_accesses_exists BOOLEAN; + v_user_level_menu_action_accesses_exists BOOLEAN; +BEGIN + -- Check menu_actions table + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'menu_actions' + ) INTO v_menu_actions_exists; + + -- Check user_level_menu_accesses table + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'user_level_menu_accesses' + ) INTO v_user_level_menu_accesses_exists; + + -- Check user_level_menu_action_accesses table + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'user_level_menu_action_accesses' + ) INTO v_user_level_menu_action_accesses_exists; + + -- Report results + RAISE NOTICE '========================================'; + RAISE NOTICE 'Migration Verification Results:'; + RAISE NOTICE '========================================'; + + IF v_menu_actions_exists THEN + RAISE NOTICE '✓ Table menu_actions created successfully'; + ELSE + RAISE WARNING '✗ Table menu_actions NOT created'; + END IF; + + IF v_user_level_menu_accesses_exists THEN + RAISE NOTICE '✓ Table user_level_menu_accesses created successfully'; + ELSE + RAISE WARNING '✗ Table user_level_menu_accesses NOT created'; + END IF; + + IF v_user_level_menu_action_accesses_exists THEN + RAISE NOTICE '✓ Table user_level_menu_action_accesses created successfully'; + ELSE + RAISE WARNING '✗ Table user_level_menu_action_accesses NOT created'; + END IF; + + RAISE NOTICE '========================================'; + + IF v_menu_actions_exists AND v_user_level_menu_accesses_exists AND v_user_level_menu_action_accesses_exists THEN + RAISE NOTICE 'Migration completed successfully! ✓'; + ELSE + RAISE WARNING 'Migration completed with errors! Please check above.'; + END IF; + + RAISE NOTICE '========================================'; +END $$; + diff --git a/docs/swagger/docs.go b/docs/swagger/docs.go index b870e14..e21a067 100644 --- a/docs/swagger/docs.go +++ b/docs/swagger/docs.go @@ -13499,6 +13499,425 @@ const docTemplate = `{ } } }, + "/menu-actions": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MenuActions", + "tags": [ + "MenuActions" + ], + "summary": "Get all MenuActions", + "parameters": [ + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "query" + }, + { + "type": "string", + "description": "Action Code", + "name": "action_code", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MenuAction", + "tags": [ + "MenuActions" + ], + "summary": "Create MenuAction", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MenuActionsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/menu-actions/batch": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MenuActions batch", + "tags": [ + "MenuActions" + ], + "summary": "Create MenuActions batch", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MenuActionsBatchCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/menu-actions/menu/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting MenuActions by Menu ID", + "tags": [ + "MenuActions" + ], + "summary": "Get MenuActions by Menu ID", + "parameters": [ + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/menu-actions/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MenuAction", + "tags": [ + "MenuActions" + ], + "summary": "Get one MenuAction", + "parameters": [ + { + "type": "integer", + "description": "MenuAction ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MenuAction", + "tags": [ + "MenuActions" + ], + "summary": "Update MenuAction", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "MenuAction ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MenuActionsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MenuAction", + "tags": [ + "MenuActions" + ], + "summary": "Delete MenuAction", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "MenuAction ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/menu-modules": { "get": { "security": [ @@ -14955,6 +15374,1123 @@ const docTemplate = `{ } } }, + "/user-level-menu-accesses": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserLevelMenuAccesses", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Get all UserLevelMenuAccesses", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "query" + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevelMenuAccess", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Create UserLevelMenuAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuAccessesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/batch": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevelMenuAccesses batch", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Create UserLevelMenuAccesses batch", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuAccessesBatchCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/check/{user_level_id}/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for checking if user level has access to menu", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Check User Level Menu Access", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/menu/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuAccesses by Menu ID", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Get UserLevelMenuAccesses by Menu ID", + "parameters": [ + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/user-level/{user_level_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuAccesses by User Level ID", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Get UserLevelMenuAccesses by User Level ID", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserLevelMenuAccess", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Get one UserLevelMenuAccess", + "parameters": [ + { + "type": "integer", + "description": "UserLevelMenuAccess ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserLevelMenuAccess", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Update UserLevelMenuAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "UserLevelMenuAccess ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuAccessesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserLevelMenuAccess", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Delete UserLevelMenuAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "UserLevelMenuAccess ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserLevelMenuActionAccesses", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get all UserLevelMenuActionAccesses", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "query" + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "query" + }, + { + "type": "string", + "description": "Action Code", + "name": "action_code", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevelMenuActionAccess", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Create UserLevelMenuActionAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuActionAccessesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/batch": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevelMenuActionAccesses batch", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Create UserLevelMenuActionAccesses batch", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuActionAccessesBatchCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/check/{user_level_id}/{menu_id}/{action_code}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for checking if user level has access to action in menu", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Check User Level Menu Action Access", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Action Code", + "name": "action_code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/menu/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuActionAccesses by Menu ID", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get UserLevelMenuActionAccesses by Menu ID", + "parameters": [ + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/user-level/{user_level_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuActionAccesses by User Level ID", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get UserLevelMenuActionAccesses by User Level ID", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/user-level/{user_level_id}/menu/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuActionAccesses by User Level ID and Menu ID", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get UserLevelMenuActionAccesses by User Level ID and Menu ID", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserLevelMenuActionAccess", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get one UserLevelMenuActionAccess", + "parameters": [ + { + "type": "integer", + "description": "UserLevelMenuActionAccess ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserLevelMenuActionAccess", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Update UserLevelMenuActionAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "UserLevelMenuActionAccess ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuActionAccessesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserLevelMenuActionAccess", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Delete UserLevelMenuActionAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "UserLevelMenuActionAccess ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/user-level-module-accesses": { "get": { "security": [ @@ -19389,7 +20925,6 @@ const docTemplate = `{ "required": [ "description", "group", - "moduleId", "name", "statusId" ], @@ -19426,9 +20961,19 @@ const docTemplate = `{ "statusId" ], "properties": { + "actionType": { + "type": "string" + }, "description": { "type": "string" }, + "menuIds": { + "description": "Optional: untuk langsung assign ke menu saat create", + "type": "array", + "items": { + "type": "integer" + } + }, "name": { "type": "string" }, @@ -19450,12 +20995,22 @@ const docTemplate = `{ "statusId" ], "properties": { + "actionType": { + "type": "string" + }, "description": { "type": "string" }, "id": { "type": "integer" }, + "menuIds": { + "description": "Optional: untuk update relasi dengan menu", + "type": "array", + "items": { + "type": "integer" + } + }, "name": { "type": "string" }, @@ -19467,6 +21022,100 @@ const docTemplate = `{ } } }, + "request.MenuActionsBatchCreateRequest": { + "type": "object", + "required": [ + "actionCodes", + "menuId" + ], + "properties": { + "actionCodes": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "clientId": { + "type": "string" + }, + "menuId": { + "type": "integer" + } + } + }, + "request.MenuActionsCreateRequest": { + "type": "object", + "required": [ + "actionCode", + "actionName", + "menuId" + ], + "properties": { + "actionCode": { + "type": "string" + }, + "actionName": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "description": { + "type": "string" + }, + "httpMethod": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "pathUrl": { + "type": "string" + }, + "position": { + "type": "integer" + } + } + }, + "request.MenuActionsUpdateRequest": { + "type": "object", + "required": [ + "actionName" + ], + "properties": { + "actionCode": { + "type": "string" + }, + "actionName": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "description": { + "type": "string" + }, + "httpMethod": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "pathUrl": { + "type": "string" + }, + "position": { + "type": "integer" + } + } + }, "request.MenuModulesBatchCreateRequest": { "type": "object", "required": [ @@ -19895,6 +21544,149 @@ const docTemplate = `{ } } }, + "request.UserLevelMenuAccessesBatchCreateRequest": { + "type": "object", + "required": [ + "menuIds", + "userLevelId" + ], + "properties": { + "clientId": { + "type": "string" + }, + "menuIds": { + "type": "array", + "minItems": 1, + "items": { + "type": "integer" + } + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuAccessesCreateRequest": { + "type": "object", + "required": [ + "menuId", + "userLevelId" + ], + "properties": { + "canAccess": { + "type": "boolean" + }, + "clientId": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuAccessesUpdateRequest": { + "type": "object", + "properties": { + "canAccess": { + "type": "boolean" + }, + "clientId": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuActionAccessesBatchCreateRequest": { + "type": "object", + "required": [ + "actionCodes", + "menuId", + "userLevelId" + ], + "properties": { + "actionCodes": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "clientId": { + "type": "string" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuActionAccessesCreateRequest": { + "type": "object", + "required": [ + "actionCode", + "menuId", + "userLevelId" + ], + "properties": { + "actionCode": { + "type": "string" + }, + "canAccess": { + "type": "boolean" + }, + "clientId": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuActionAccessesUpdateRequest": { + "type": "object", + "properties": { + "actionCode": { + "type": "string" + }, + "canAccess": { + "type": "boolean" + }, + "clientId": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, "request.UserLevelModuleAccessesBatchCreateRequest": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 32596a2..57b400f 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -13488,6 +13488,425 @@ } } }, + "/menu-actions": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all MenuActions", + "tags": [ + "MenuActions" + ], + "summary": "Get all MenuActions", + "parameters": [ + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "query" + }, + { + "type": "string", + "description": "Action Code", + "name": "action_code", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MenuAction", + "tags": [ + "MenuActions" + ], + "summary": "Create MenuAction", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MenuActionsCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/menu-actions/batch": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create MenuActions batch", + "tags": [ + "MenuActions" + ], + "summary": "Create MenuActions batch", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MenuActionsBatchCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/menu-actions/menu/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting MenuActions by Menu ID", + "tags": [ + "MenuActions" + ], + "summary": "Get MenuActions by Menu ID", + "parameters": [ + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/menu-actions/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one MenuAction", + "tags": [ + "MenuActions" + ], + "summary": "Get one MenuAction", + "parameters": [ + { + "type": "integer", + "description": "MenuAction ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update MenuAction", + "tags": [ + "MenuActions" + ], + "summary": "Update MenuAction", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "MenuAction ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.MenuActionsUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete MenuAction", + "tags": [ + "MenuActions" + ], + "summary": "Delete MenuAction", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "MenuAction ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/menu-modules": { "get": { "security": [ @@ -14944,6 +15363,1123 @@ } } }, + "/user-level-menu-accesses": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserLevelMenuAccesses", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Get all UserLevelMenuAccesses", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "query" + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevelMenuAccess", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Create UserLevelMenuAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuAccessesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/batch": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevelMenuAccesses batch", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Create UserLevelMenuAccesses batch", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuAccessesBatchCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/check/{user_level_id}/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for checking if user level has access to menu", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Check User Level Menu Access", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/menu/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuAccesses by Menu ID", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Get UserLevelMenuAccesses by Menu ID", + "parameters": [ + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/user-level/{user_level_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuAccesses by User Level ID", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Get UserLevelMenuAccesses by User Level ID", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-accesses/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserLevelMenuAccess", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Get one UserLevelMenuAccess", + "parameters": [ + { + "type": "integer", + "description": "UserLevelMenuAccess ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserLevelMenuAccess", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Update UserLevelMenuAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "UserLevelMenuAccess ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuAccessesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserLevelMenuAccess", + "tags": [ + "UserLevelMenuAccesses" + ], + "summary": "Delete UserLevelMenuAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "UserLevelMenuAccess ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting all UserLevelMenuActionAccesses", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get all UserLevelMenuActionAccesses", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "query" + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "query" + }, + { + "type": "string", + "description": "Action Code", + "name": "action_code", + "in": "query" + }, + { + "type": "integer", + "name": "count", + "in": "query" + }, + { + "type": "integer", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "name": "nextPage", + "in": "query" + }, + { + "type": "integer", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "name": "previousPage", + "in": "query" + }, + { + "type": "string", + "name": "sort", + "in": "query" + }, + { + "type": "string", + "name": "sortBy", + "in": "query" + }, + { + "type": "integer", + "name": "totalPage", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevelMenuActionAccess", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Create UserLevelMenuActionAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuActionAccessesCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/batch": { + "post": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for create UserLevelMenuActionAccesses batch", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Create UserLevelMenuActionAccesses batch", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuActionAccessesBatchCreateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/check/{user_level_id}/{menu_id}/{action_code}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for checking if user level has access to action in menu", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Check User Level Menu Action Access", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Action Code", + "name": "action_code", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/menu/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuActionAccesses by Menu ID", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get UserLevelMenuActionAccesses by Menu ID", + "parameters": [ + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/user-level/{user_level_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuActionAccesses by User Level ID", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get UserLevelMenuActionAccesses by User Level ID", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/user-level/{user_level_id}/menu/{menu_id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting UserLevelMenuActionAccesses by User Level ID and Menu ID", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get UserLevelMenuActionAccesses by User Level ID and Menu ID", + "parameters": [ + { + "type": "integer", + "description": "User Level ID", + "name": "user_level_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Menu ID", + "name": "menu_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, + "/user-level-menu-action-accesses/{id}": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for getting one UserLevelMenuActionAccess", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Get one UserLevelMenuActionAccess", + "parameters": [ + { + "type": "integer", + "description": "UserLevelMenuActionAccess ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for update UserLevelMenuActionAccess", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Update UserLevelMenuActionAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "UserLevelMenuActionAccess ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Required payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.UserLevelMenuActionAccessesUpdateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "Bearer": [] + } + ], + "description": "API for delete UserLevelMenuActionAccess", + "tags": [ + "UserLevelMenuActionAccesses" + ], + "summary": "Delete UserLevelMenuActionAccess", + "parameters": [ + { + "type": "string", + "description": "Insert the X-Csrf-Token", + "name": "X-Csrf-Token", + "in": "header" + }, + { + "type": "integer", + "description": "UserLevelMenuActionAccess ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.BadRequestError" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/response.UnauthorizedError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.InternalServerError" + } + } + } + } + }, "/user-level-module-accesses": { "get": { "security": [ @@ -19378,7 +20914,6 @@ "required": [ "description", "group", - "moduleId", "name", "statusId" ], @@ -19415,9 +20950,19 @@ "statusId" ], "properties": { + "actionType": { + "type": "string" + }, "description": { "type": "string" }, + "menuIds": { + "description": "Optional: untuk langsung assign ke menu saat create", + "type": "array", + "items": { + "type": "integer" + } + }, "name": { "type": "string" }, @@ -19439,12 +20984,22 @@ "statusId" ], "properties": { + "actionType": { + "type": "string" + }, "description": { "type": "string" }, "id": { "type": "integer" }, + "menuIds": { + "description": "Optional: untuk update relasi dengan menu", + "type": "array", + "items": { + "type": "integer" + } + }, "name": { "type": "string" }, @@ -19456,6 +21011,100 @@ } } }, + "request.MenuActionsBatchCreateRequest": { + "type": "object", + "required": [ + "actionCodes", + "menuId" + ], + "properties": { + "actionCodes": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "clientId": { + "type": "string" + }, + "menuId": { + "type": "integer" + } + } + }, + "request.MenuActionsCreateRequest": { + "type": "object", + "required": [ + "actionCode", + "actionName", + "menuId" + ], + "properties": { + "actionCode": { + "type": "string" + }, + "actionName": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "description": { + "type": "string" + }, + "httpMethod": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "pathUrl": { + "type": "string" + }, + "position": { + "type": "integer" + } + } + }, + "request.MenuActionsUpdateRequest": { + "type": "object", + "required": [ + "actionName" + ], + "properties": { + "actionCode": { + "type": "string" + }, + "actionName": { + "type": "string" + }, + "clientId": { + "type": "string" + }, + "description": { + "type": "string" + }, + "httpMethod": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "pathUrl": { + "type": "string" + }, + "position": { + "type": "integer" + } + } + }, "request.MenuModulesBatchCreateRequest": { "type": "object", "required": [ @@ -19884,6 +21533,149 @@ } } }, + "request.UserLevelMenuAccessesBatchCreateRequest": { + "type": "object", + "required": [ + "menuIds", + "userLevelId" + ], + "properties": { + "clientId": { + "type": "string" + }, + "menuIds": { + "type": "array", + "minItems": 1, + "items": { + "type": "integer" + } + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuAccessesCreateRequest": { + "type": "object", + "required": [ + "menuId", + "userLevelId" + ], + "properties": { + "canAccess": { + "type": "boolean" + }, + "clientId": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuAccessesUpdateRequest": { + "type": "object", + "properties": { + "canAccess": { + "type": "boolean" + }, + "clientId": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuActionAccessesBatchCreateRequest": { + "type": "object", + "required": [ + "actionCodes", + "menuId", + "userLevelId" + ], + "properties": { + "actionCodes": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "clientId": { + "type": "string" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuActionAccessesCreateRequest": { + "type": "object", + "required": [ + "actionCode", + "menuId", + "userLevelId" + ], + "properties": { + "actionCode": { + "type": "string" + }, + "canAccess": { + "type": "boolean" + }, + "clientId": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, + "request.UserLevelMenuActionAccessesUpdateRequest": { + "type": "object", + "properties": { + "actionCode": { + "type": "string" + }, + "canAccess": { + "type": "boolean" + }, + "clientId": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "menuId": { + "type": "integer" + }, + "userLevelId": { + "type": "integer" + } + } + }, "request.UserLevelModuleAccessesBatchCreateRequest": { "type": "object", "required": [ diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 7174e67..df7f514 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -1082,14 +1082,20 @@ definitions: required: - description - group - - moduleId - name - statusId type: object request.MasterModulesCreateRequest: properties: + actionType: + type: string description: type: string + menuIds: + description: 'Optional: untuk langsung assign ke menu saat create' + items: + type: integer + type: array name: type: string pathUrl: @@ -1104,10 +1110,17 @@ definitions: type: object request.MasterModulesUpdateRequest: properties: + actionType: + type: string description: type: string id: type: integer + menuIds: + description: 'Optional: untuk update relasi dengan menu' + items: + type: integer + type: array name: type: string pathUrl: @@ -1121,6 +1134,69 @@ definitions: - pathUrl - statusId type: object + request.MenuActionsBatchCreateRequest: + properties: + actionCodes: + items: + type: string + minItems: 1 + type: array + clientId: + type: string + menuId: + type: integer + required: + - actionCodes + - menuId + type: object + request.MenuActionsCreateRequest: + properties: + actionCode: + type: string + actionName: + type: string + clientId: + type: string + description: + type: string + httpMethod: + type: string + isActive: + type: boolean + menuId: + type: integer + pathUrl: + type: string + position: + type: integer + required: + - actionCode + - actionName + - menuId + type: object + request.MenuActionsUpdateRequest: + properties: + actionCode: + type: string + actionName: + type: string + clientId: + type: string + description: + type: string + httpMethod: + type: string + isActive: + type: boolean + menuId: + type: integer + pathUrl: + type: string + position: + type: integer + required: + - actionName + type: object request.MenuModulesBatchCreateRequest: properties: client_id: @@ -1409,6 +1485,102 @@ definitions: username: type: string type: object + request.UserLevelMenuAccessesBatchCreateRequest: + properties: + clientId: + type: string + menuIds: + items: + type: integer + minItems: 1 + type: array + userLevelId: + type: integer + required: + - menuIds + - userLevelId + type: object + request.UserLevelMenuAccessesCreateRequest: + properties: + canAccess: + type: boolean + clientId: + type: string + isActive: + type: boolean + menuId: + type: integer + userLevelId: + type: integer + required: + - menuId + - userLevelId + type: object + request.UserLevelMenuAccessesUpdateRequest: + properties: + canAccess: + type: boolean + clientId: + type: string + isActive: + type: boolean + menuId: + type: integer + userLevelId: + type: integer + type: object + request.UserLevelMenuActionAccessesBatchCreateRequest: + properties: + actionCodes: + items: + type: string + minItems: 1 + type: array + clientId: + type: string + menuId: + type: integer + userLevelId: + type: integer + required: + - actionCodes + - menuId + - userLevelId + type: object + request.UserLevelMenuActionAccessesCreateRequest: + properties: + actionCode: + type: string + canAccess: + type: boolean + clientId: + type: string + isActive: + type: boolean + menuId: + type: integer + userLevelId: + type: integer + required: + - actionCode + - menuId + - userLevelId + type: object + request.UserLevelMenuActionAccessesUpdateRequest: + properties: + actionCode: + type: string + canAccess: + type: boolean + clientId: + type: string + isActive: + type: boolean + menuId: + type: integer + userLevelId: + type: integer + type: object request.UserLevelModuleAccessesBatchCreateRequest: properties: can_access: @@ -10405,6 +10577,271 @@ paths: summary: Update MasterStatuses tags: - Untags + /menu-actions: + get: + description: API for getting all MenuActions + parameters: + - description: Menu ID + in: query + name: menu_id + type: integer + - description: Action Code + in: query + name: action_code + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all MenuActions + tags: + - MenuActions + post: + description: API for create MenuAction + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.MenuActionsCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create MenuAction + tags: + - MenuActions + /menu-actions/{id}: + delete: + description: API for delete MenuAction + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: MenuAction ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete MenuAction + tags: + - MenuActions + get: + description: API for getting one MenuAction + parameters: + - description: MenuAction ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one MenuAction + tags: + - MenuActions + put: + description: API for update MenuAction + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: MenuAction ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.MenuActionsUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update MenuAction + tags: + - MenuActions + /menu-actions/batch: + post: + description: API for create MenuActions batch + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.MenuActionsBatchCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create MenuActions batch + tags: + - MenuActions + /menu-actions/menu/{menu_id}: + get: + description: API for getting MenuActions by Menu ID + parameters: + - description: Menu ID + in: path + name: menu_id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get MenuActions by Menu ID + tags: + - MenuActions /menu-modules: get: description: API for getting all MenuModules @@ -11328,6 +11765,716 @@ paths: summary: update Subscription tags: - Subscription + /user-level-menu-accesses: + get: + description: API for getting all UserLevelMenuAccesses + parameters: + - description: User Level ID + in: query + name: user_level_id + type: integer + - description: Menu ID + in: query + name: menu_id + type: integer + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all UserLevelMenuAccesses + tags: + - UserLevelMenuAccesses + post: + description: API for create UserLevelMenuAccess + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLevelMenuAccessesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create UserLevelMenuAccess + tags: + - UserLevelMenuAccesses + /user-level-menu-accesses/{id}: + delete: + description: API for delete UserLevelMenuAccess + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: UserLevelMenuAccess ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete UserLevelMenuAccess + tags: + - UserLevelMenuAccesses + get: + description: API for getting one UserLevelMenuAccess + parameters: + - description: UserLevelMenuAccess ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one UserLevelMenuAccess + tags: + - UserLevelMenuAccesses + put: + description: API for update UserLevelMenuAccess + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: UserLevelMenuAccess ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLevelMenuAccessesUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update UserLevelMenuAccess + tags: + - UserLevelMenuAccesses + /user-level-menu-accesses/batch: + post: + description: API for create UserLevelMenuAccesses batch + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLevelMenuAccessesBatchCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create UserLevelMenuAccesses batch + tags: + - UserLevelMenuAccesses + /user-level-menu-accesses/check/{user_level_id}/{menu_id}: + get: + description: API for checking if user level has access to menu + parameters: + - description: User Level ID + in: path + name: user_level_id + required: true + type: integer + - description: Menu ID + in: path + name: menu_id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Check User Level Menu Access + tags: + - UserLevelMenuAccesses + /user-level-menu-accesses/menu/{menu_id}: + get: + description: API for getting UserLevelMenuAccesses by Menu ID + parameters: + - description: Menu ID + in: path + name: menu_id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get UserLevelMenuAccesses by Menu ID + tags: + - UserLevelMenuAccesses + /user-level-menu-accesses/user-level/{user_level_id}: + get: + description: API for getting UserLevelMenuAccesses by User Level ID + parameters: + - description: User Level ID + in: path + name: user_level_id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get UserLevelMenuAccesses by User Level ID + tags: + - UserLevelMenuAccesses + /user-level-menu-action-accesses: + get: + description: API for getting all UserLevelMenuActionAccesses + parameters: + - description: User Level ID + in: query + name: user_level_id + type: integer + - description: Menu ID + in: query + name: menu_id + type: integer + - description: Action Code + in: query + name: action_code + type: string + - in: query + name: count + type: integer + - in: query + name: limit + type: integer + - in: query + name: nextPage + type: integer + - in: query + name: page + type: integer + - in: query + name: previousPage + type: integer + - in: query + name: sort + type: string + - in: query + name: sortBy + type: string + - in: query + name: totalPage + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get all UserLevelMenuActionAccesses + tags: + - UserLevelMenuActionAccesses + post: + description: API for create UserLevelMenuActionAccess + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLevelMenuActionAccessesCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create UserLevelMenuActionAccess + tags: + - UserLevelMenuActionAccesses + /user-level-menu-action-accesses/{id}: + delete: + description: API for delete UserLevelMenuActionAccess + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: UserLevelMenuActionAccess ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Delete UserLevelMenuActionAccess + tags: + - UserLevelMenuActionAccesses + get: + description: API for getting one UserLevelMenuActionAccess + parameters: + - description: UserLevelMenuActionAccess ID + in: path + name: id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get one UserLevelMenuActionAccess + tags: + - UserLevelMenuActionAccesses + put: + description: API for update UserLevelMenuActionAccess + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: UserLevelMenuActionAccess ID + in: path + name: id + required: true + type: integer + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLevelMenuActionAccessesUpdateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Update UserLevelMenuActionAccess + tags: + - UserLevelMenuActionAccesses + /user-level-menu-action-accesses/batch: + post: + description: API for create UserLevelMenuActionAccesses batch + parameters: + - description: Insert the X-Csrf-Token + in: header + name: X-Csrf-Token + type: string + - description: Required payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/request.UserLevelMenuActionAccessesBatchCreateRequest' + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Create UserLevelMenuActionAccesses batch + tags: + - UserLevelMenuActionAccesses + /user-level-menu-action-accesses/check/{user_level_id}/{menu_id}/{action_code}: + get: + description: API for checking if user level has access to action in menu + parameters: + - description: User Level ID + in: path + name: user_level_id + required: true + type: integer + - description: Menu ID + in: path + name: menu_id + required: true + type: integer + - description: Action Code + in: path + name: action_code + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Check User Level Menu Action Access + tags: + - UserLevelMenuActionAccesses + /user-level-menu-action-accesses/menu/{menu_id}: + get: + description: API for getting UserLevelMenuActionAccesses by Menu ID + parameters: + - description: Menu ID + in: path + name: menu_id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get UserLevelMenuActionAccesses by Menu ID + tags: + - UserLevelMenuActionAccesses + /user-level-menu-action-accesses/user-level/{user_level_id}: + get: + description: API for getting UserLevelMenuActionAccesses by User Level ID + parameters: + - description: User Level ID + in: path + name: user_level_id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get UserLevelMenuActionAccesses by User Level ID + tags: + - UserLevelMenuActionAccesses + /user-level-menu-action-accesses/user-level/{user_level_id}/menu/{menu_id}: + get: + description: API for getting UserLevelMenuActionAccesses by User Level ID and + Menu ID + parameters: + - description: User Level ID + in: path + name: user_level_id + required: true + type: integer + - description: Menu ID + in: path + name: menu_id + required: true + type: integer + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.BadRequestError' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/response.UnauthorizedError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.InternalServerError' + security: + - Bearer: [] + summary: Get UserLevelMenuActionAccesses by User Level ID and Menu ID + tags: + - UserLevelMenuActionAccesses /user-level-module-accesses: get: description: API for getting all UserLevelModuleAccesses diff --git a/main.go b/main.go index 94efe47..cc3da4b 100644 --- a/main.go +++ b/main.go @@ -27,6 +27,9 @@ import ( "netidhub-saas-be/app/module/magazines" "netidhub-saas-be/app/module/master_menus" "netidhub-saas-be/app/module/master_modules" + "netidhub-saas-be/app/module/menu_actions" + "netidhub-saas-be/app/module/user_level_menu_accesses" + "netidhub-saas-be/app/module/user_level_menu_action_accesses" "netidhub-saas-be/app/module/provinces" "netidhub-saas-be/app/module/schedules" "netidhub-saas-be/app/module/subscription" @@ -93,6 +96,9 @@ func main() { magazine_files.NewMagazineFilesModule, master_menus.NewMasterMenusModule, master_modules.NewMasterModulesModule, + menu_actions.NewMenuActionsModule, + user_level_menu_accesses.NewUserLevelMenuAccessesModule, + user_level_menu_action_accesses.NewUserLevelMenuActionAccessesModule, provinces.NewProvincesModule, schedules.NewSchedulesModule, subscription.NewSubscriptionModule,