From 91ad7ad5e0f1de1225e97a3d8b98058469800b75 Mon Sep 17 00:00:00 2001 From: kris Date: Mon, 1 Dec 2025 04:40:38 +0000 Subject: [PATCH 1/2] Update .gitlab-ci.yml change https to ssh --- .gitlab-ci.yml | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3aa6389b..53f28b3e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,24 +6,45 @@ deploy-dev: image: alpine:3.20 variables: DEPLOY_APP: "LTI-MBUGROUP" + # Opsional: kalau pakai submodule, ini bikin clone submodule pakai SSH juga + GIT_SUBMODULE_STRATEGY: recursive + GIT_DEPTH: "1" before_script: - echo "🧰 Installing dependencies..." - - apk update && apk add --no-cache openssh git curl + - apk update && apk add --no-cache openssh git curl bash + + # Setup SSH di runner - mkdir -p ~/.ssh - - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - - eval $(ssh-agent -s) + - eval "$(ssh-agent -s)" - ssh-add ~/.ssh/id_rsa + + # Trust host keys (server + gitlab) biar SSH gak nanya interaktif - ssh-keyscan -H "$SERVER_IP" >> ~/.ssh/known_hosts + - ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts script: - echo "🚀 Deploying latest code to $SERVER_USER@$SERVER_IP" + - > if ssh -o StrictHostKeyChecking=no "$SERVER_USER@$SERVER_IP" " - cd /home/devops/docker/deployment/development/lti-api && - git fetch origin development && - git reset --hard origin/development && + set -e + + cd /home/devops/docker/deployment/development/lti-api + + # Pastikan remote origin SSH (antisipasi kalau pernah ke-set HTTPS) + git remote set-url origin git@gitlab.com:mbugroup/lti-api.git + + # Pastikan server percaya gitlab.com juga (untuk git fetch via SSH) + mkdir -p ~/.ssh + ssh-keyscan -H gitlab.com >> ~/.ssh/known_hosts + + # Fetch/reset pakai SSH + GIT_SSH_COMMAND='ssh -o StrictHostKeyChecking=no' git fetch origin development + git reset --hard origin/development + docker compose restart dev-api-lti || docker compose up -d dev-api-lti "; then STATUS='success'; From fa5609c18305515e8b914b275bb6f75241b6dd99 Mon Sep 17 00:00:00 2001 From: giovanni-ce Date: Wed, 3 Dec 2025 16:12:58 +0700 Subject: [PATCH 2/2] Feat[BE][US#283]: init module closing --- .../controllers/closing.controller.go | 76 +++++++++++++++++++ internal/modules/closings/dto/closing.dto.go | 64 ++++++++++++++++ internal/modules/closings/module.go | 26 +++++++ .../repositories/closing.repository.go | 21 +++++ internal/modules/closings/route.go | 25 ++++++ .../closings/services/closing.service.go | 72 ++++++++++++++++++ .../validations/closing.validation.go | 15 ++++ internal/route/route.go | 2 + 8 files changed, 301 insertions(+) create mode 100644 internal/modules/closings/controllers/closing.controller.go create mode 100644 internal/modules/closings/dto/closing.dto.go create mode 100644 internal/modules/closings/module.go create mode 100644 internal/modules/closings/repositories/closing.repository.go create mode 100644 internal/modules/closings/route.go create mode 100644 internal/modules/closings/services/closing.service.go create mode 100644 internal/modules/closings/validations/closing.validation.go diff --git a/internal/modules/closings/controllers/closing.controller.go b/internal/modules/closings/controllers/closing.controller.go new file mode 100644 index 00000000..4918c28f --- /dev/null +++ b/internal/modules/closings/controllers/closing.controller.go @@ -0,0 +1,76 @@ +package controller + +import ( + "math" + "strconv" + + "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/dto" + service "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services" + validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations" + "gitlab.com/mbugroup/lti-api.git/internal/response" + + "github.com/gofiber/fiber/v2" +) + +type ClosingController struct { + ClosingService service.ClosingService +} + +func NewClosingController(closingService service.ClosingService) *ClosingController { + return &ClosingController{ + ClosingService: closingService, + } +} + +func (u *ClosingController) GetAll(c *fiber.Ctx) error { + query := &validation.Query{ + Page: c.QueryInt("page", 1), + Limit: c.QueryInt("limit", 10), + Search: c.Query("search", ""), + } + + if query.Page < 1 || query.Limit < 1 { + return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0") + } + + result, totalResults, err := u.ClosingService.GetAll(c, query) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.SuccessWithPaginate[dto.ClosingListDTO]{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get all closings successfully", + Meta: response.Meta{ + Page: query.Page, + Limit: query.Limit, + TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))), + TotalResults: totalResults, + }, + Data: dto.ToClosingListDTOs(result), + }) +} + +func (u *ClosingController) GetOne(c *fiber.Ctx) error { + param := c.Params("id") + + id, err := strconv.Atoi(param) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, "Invalid Id") + } + + result, err := u.ClosingService.GetOne(c, uint(id)) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get closing successfully", + Data: dto.ToClosingListDTO(*result), + }) +} diff --git a/internal/modules/closings/dto/closing.dto.go b/internal/modules/closings/dto/closing.dto.go new file mode 100644 index 00000000..ccb014e6 --- /dev/null +++ b/internal/modules/closings/dto/closing.dto.go @@ -0,0 +1,64 @@ +package dto + +import ( + "time" + + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto" +) + +// === DTO Structs === + +type ClosingRelationDTO struct { + Id uint `json:"id"` + Name string `json:"name"` +} + +type ClosingListDTO struct { + Id uint `json:"id"` + Name string `json:"name"` + CreatedUser *userDTO.UserRelationDTO `json:"created_user"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type ClosingDetailDTO struct { + ClosingListDTO +} + +// === Mapper Functions === + +func ToClosingRelationDTO(e entity.ProjectFlock) ClosingRelationDTO { + return ClosingRelationDTO{ + Id: e.Id, + } +} + +func ToClosingListDTO(e entity.ProjectFlock) ClosingListDTO { + var createdUser *userDTO.UserRelationDTO + if e.CreatedUser.Id != 0 { + mapped := userDTO.ToUserRelationDTO(e.CreatedUser) + createdUser = &mapped + } + + return ClosingListDTO{ + Id: e.Id, + CreatedAt: e.CreatedAt, + UpdatedAt: e.UpdatedAt, + CreatedUser: createdUser, + } +} + +func ToClosingListDTOs(e []entity.ProjectFlock) []ClosingListDTO { + result := make([]ClosingListDTO, len(e)) + for i, r := range e { + result[i] = ToClosingListDTO(r) + } + return result +} + +func ToClosingDetailDTO(e entity.ProjectFlock) ClosingDetailDTO { + return ClosingDetailDTO{ + ClosingListDTO: ToClosingListDTO(e), + } +} diff --git a/internal/modules/closings/module.go b/internal/modules/closings/module.go new file mode 100644 index 00000000..d831195c --- /dev/null +++ b/internal/modules/closings/module.go @@ -0,0 +1,26 @@ +package closings + +import ( + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "gorm.io/gorm" + + rClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories" + sClosing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services" + + rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories" + sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" +) + +type ClosingModule struct{} + +func (ClosingModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *validator.Validate) { + closingRepo := rClosing.NewClosingRepository(db) + userRepo := rUser.NewUserRepository(db) + + closingService := sClosing.NewClosingService(closingRepo, validate) + userService := sUser.NewUserService(userRepo, validate) + + ClosingRoutes(router, userService, closingService) +} + diff --git a/internal/modules/closings/repositories/closing.repository.go b/internal/modules/closings/repositories/closing.repository.go new file mode 100644 index 00000000..946797fd --- /dev/null +++ b/internal/modules/closings/repositories/closing.repository.go @@ -0,0 +1,21 @@ +package repository + +import ( + "gitlab.com/mbugroup/lti-api.git/internal/common/repository" + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + "gorm.io/gorm" +) + +type ClosingRepository interface { + repository.BaseRepository[entity.ProjectFlock] +} + +type ClosingRepositoryImpl struct { + *repository.BaseRepositoryImpl[entity.ProjectFlock] +} + +func NewClosingRepository(db *gorm.DB) ClosingRepository { + return &ClosingRepositoryImpl{ + BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlock](db), + } +} diff --git a/internal/modules/closings/route.go b/internal/modules/closings/route.go new file mode 100644 index 00000000..6570a17d --- /dev/null +++ b/internal/modules/closings/route.go @@ -0,0 +1,25 @@ +package closings + +import ( + // m "gitlab.com/mbugroup/lti-api.git/internal/middleware" + controller "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/controllers" + closing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services" + user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" + + "github.com/gofiber/fiber/v2" +) + +func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService) { + ctrl := controller.NewClosingController(s) + + route := v1.Group("/closings") + + // route.Get("/", m.Auth(u), ctrl.GetAll) + // route.Post("/", m.Auth(u), ctrl.CreateOne) + // route.Get("/:id", m.Auth(u), ctrl.GetOne) + // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) + // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) + + route.Get("/", ctrl.GetAll) + route.Get("/:id", ctrl.GetOne) +} diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go new file mode 100644 index 00000000..fd1b42eb --- /dev/null +++ b/internal/modules/closings/services/closing.service.go @@ -0,0 +1,72 @@ +package service + +import ( + "errors" + + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + repository "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/repositories" + validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations" + "gitlab.com/mbugroup/lti-api.git/internal/utils" + + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +type ClosingService interface { + GetAll(ctx *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) + GetOne(ctx *fiber.Ctx, id uint) (*entity.ProjectFlock, error) +} + +type closingService struct { + Log *logrus.Logger + Validate *validator.Validate + Repository repository.ClosingRepository +} + +func NewClosingService(repo repository.ClosingRepository, validate *validator.Validate) ClosingService { + return &closingService{ + Log: utils.Log, + Validate: validate, + Repository: repo, + } +} + +func (s closingService) withRelations(db *gorm.DB) *gorm.DB { + return db.Preload("CreatedUser") +} + +func (s closingService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProjectFlock, int64, error) { + if err := s.Validate.Struct(params); err != nil { + return nil, 0, err + } + + offset := (params.Page - 1) * params.Limit + + closings, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB { + db = s.withRelations(db) + if params.Search != "" { + return db.Where("name LIKE ?", "%"+params.Search+"%") + } + return db.Order("created_at DESC").Order("updated_at DESC") + }) + + if err != nil { + s.Log.Errorf("Failed to get closings: %+v", err) + return nil, 0, err + } + return closings, total, nil +} + +func (s closingService) GetOne(c *fiber.Ctx, id uint) (*entity.ProjectFlock, error) { + closing, err := s.Repository.GetByID(c.Context(), id, s.withRelations) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusNotFound, "Closing not found") + } + if err != nil { + s.Log.Errorf("Failed get closing by id: %+v", err) + return nil, err + } + return closing, nil +} diff --git a/internal/modules/closings/validations/closing.validation.go b/internal/modules/closings/validations/closing.validation.go new file mode 100644 index 00000000..7d16d3ee --- /dev/null +++ b/internal/modules/closings/validations/closing.validation.go @@ -0,0 +1,15 @@ +package validation + +type Create struct { + Name string `json:"name" validate:"required_strict,min=3"` +} + +type Update struct { + Name *string `json:"name,omitempty" validate:"omitempty"` +} + +type Query struct { + Page int `query:"page" validate:"omitempty,number,min=1,gt=0"` + Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"` + Search string `query:"search" validate:"omitempty,max=50"` +} diff --git a/internal/route/route.go b/internal/route/route.go index ac7fb486..4d1c1bae 100644 --- a/internal/route/route.go +++ b/internal/route/route.go @@ -9,6 +9,7 @@ import ( "gorm.io/gorm" approvals "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals" + closings "gitlab.com/mbugroup/lti-api.git/internal/modules/closings" constants "gitlab.com/mbugroup/lti-api.git/internal/modules/constants" expenses "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses" inventory "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory" @@ -40,6 +41,7 @@ func Routes(app *fiber.App, db *gorm.DB) { ssoModule.Module{}, expenses.ExpenseModule{}, ssoModule.Module{}, + closings.ClosingModule{}, // MODULE REGISTRY }