lifedebugger commited on
Commit
5d4252b
·
1 Parent(s): c0722d3

Deploy files from GitHub repository

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. compile_swagger.ps1 → cmd/compile_swagger.ps1 +0 -0
  2. do_inject_config.ps1 → cmd/do_inject_config.ps1 +0 -0
  3. do_inject_controllers.ps1 → cmd/do_inject_controllers.ps1 +0 -0
  4. do_inject_middleware.ps1 → cmd/do_inject_middleware.ps1 +0 -0
  5. do_inject_repository.ps1 → cmd/do_inject_repository.ps1 +0 -0
  6. do_inject_services.ps1 → cmd/do_inject_services.ps1 +0 -0
  7. controllers/academy_exam_controller.go +96 -96
  8. controllers/controller.go +10 -0
  9. controllers/{exam_event_controller.go → event_exam_controller.go} +98 -98
  10. controllers/event_exam_proctoring_controller.go +152 -0
  11. controllers/exam_controller.go +194 -0
  12. docs/docs.go +0 -312
  13. docs/swagger.json +0 -283
  14. docs/swagger.yaml +0 -169
  15. go.mod +4 -17
  16. go.sum +14 -61
  17. go.work.sum +11 -10
  18. injector.sh +0 -92
  19. models/dto/event_dto.go +10 -0
  20. models/dto/exam_dto.go +22 -2
  21. models/entity/entity.go +42 -25
  22. provider/controller_provider.go +38 -23
  23. provider/provider.go +8 -7
  24. provider/repositories_provider.go +82 -75
  25. provider/services_provider.go +46 -30
  26. repositories/academy_exam_answer_repository.go +55 -0
  27. repositories/academy_exam_assign_repository.go +48 -0
  28. repositories/academy_exam_attempt_repository.go +52 -0
  29. repositories/academy_result_repository.go +8 -8
  30. repositories/{exam_event_answer_repository.go → event_exam_answer_repository.go} +16 -16
  31. repositories/{exam_event_assign_repository.go → event_exam_assign_repository.go} +13 -13
  32. repositories/{exam_event_attempt_repository.go → event_exam_attempt_repository.go} +15 -15
  33. repositories/event_exam_proctoring_repository.go +61 -0
  34. repositories/exam_academy_answer_repository.go +0 -55
  35. repositories/exam_academy_assign_repository.go +0 -48
  36. repositories/exam_academy_attempt_repository.go +0 -52
  37. repositories/{exam_event_repository.go → exam_repository.go} +59 -3
  38. repositories/result_repository.go +2 -2
  39. router/event_exam_proctoring_router.go +24 -0
  40. router/{exam_event_router.go → event_exam_router.go} +6 -6
  41. router/exam_router.go +48 -0
  42. router/router.go +3 -1
  43. services/{exam_academy_service.go → academy_exam_service.go} +279 -279
  44. services/academy_service.go +1 -0
  45. services/email_verification_service.go +3 -0
  46. services/event_exam_proctoring_service.go +100 -0
  47. services/{exam_event_service.go → event_exam_service.go} +83 -82
  48. services/exam_service.go +142 -0
  49. services/external_auth_service.go +1 -0
  50. services/jwt_service.go +2 -3
compile_swagger.ps1 → cmd/compile_swagger.ps1 RENAMED
File without changes
do_inject_config.ps1 → cmd/do_inject_config.ps1 RENAMED
File without changes
do_inject_controllers.ps1 → cmd/do_inject_controllers.ps1 RENAMED
File without changes
do_inject_middleware.ps1 → cmd/do_inject_middleware.ps1 RENAMED
File without changes
do_inject_repository.ps1 → cmd/do_inject_repository.ps1 RENAMED
File without changes
do_inject_services.ps1 → cmd/do_inject_services.ps1 RENAMED
File without changes
controllers/academy_exam_controller.go CHANGED
@@ -1,96 +1,96 @@
1
- package controllers
2
-
3
- import (
4
- "abdanhafidz.com/go-boilerplate/models/dto"
5
- "abdanhafidz.com/go-boilerplate/services"
6
- "github.com/gin-gonic/gin"
7
- )
8
-
9
- type AcademyExamController interface {
10
- Attempt(ctx *gin.Context)
11
- Answer(ctx *gin.Context)
12
- Submit(ctx *gin.Context)
13
- List(ctx *gin.Context)
14
- }
15
-
16
- type academyExamController struct{ academyExamService services.AcademyExamService }
17
-
18
- func NewAcademyExamController(academyExamService services.AcademyExamService) AcademyExamController {
19
- return &academyExamController{academyExamService: academyExamService}
20
- }
21
-
22
- // Attempt godoc
23
- // @Summary Attempt Academy Exam
24
- // @Description Start an attempt for a specific exam in an academy
25
- // @Tags Academy Exam
26
- // @Accept json
27
- // @Produce json
28
- // @Param academy_slug path string true "Academy Slug"
29
- // @Param exam_slug path string true "Exam Slug"
30
- // @Success 200 {object} dto.SuccessResponse[models.ExamAcademyAttempt]
31
- // @Failure 400 {object} dto.ErrorResponse
32
- // @Router /api/v1/academy/{academy_slug}/exam/{exam_slug}/attempt [get]
33
- func (c *academyExamController) Attempt(ctx *gin.Context) {
34
- academySlug := ctx.Param("academy_slug")
35
- examSlug := ctx.Param("exam_slug")
36
- accountId := ParseAccountId(ctx)
37
- res, err := c.academyExamService.AttemptExamAcademy(ctx.Request.Context(), academySlug, examSlug, accountId)
38
- ResponseJSON(ctx, gin.H{"academy_slug": academySlug, "exam_slug": examSlug}, res, err)
39
- }
40
-
41
- // Answer godoc
42
- // @Summary Answer Academy Exam Question
43
- // @Description Submit an answer for a specific question in an exam attempt
44
- // @Tags Academy Exam
45
- // @Accept json
46
- // @Produce json
47
- // @Param academy_slug path string true "Academy Slug"
48
- // @Param attempt_id path string true "Exam Attempt ID"
49
- // @Param request body dto.AnswerExamEventRequest true "Answer Exam Event Request"
50
- // @Success 200 {object} dto.SuccessResponse[any]
51
- // @Failure 400 {object} dto.ErrorResponse
52
- // @Router /api/v1/academy/{academy_slug}/exam/{attempt_id}/answer_question [post]
53
-
54
- func (c *academyExamController) Answer(ctx *gin.Context) {
55
- academySlug := ctx.Param("academy_slug")
56
- attemptId := ParseUUID(ctx, "attempt_id")
57
- req := RequestJSON[dto.AnswerExamEventRequest](ctx)
58
- res, err := c.academyExamService.AnswerExamAcademy(ctx.Request.Context(), academySlug, attemptId, req.QuestionId, req.Answer)
59
- ResponseJSON(ctx, gin.H{"cp_grader_result": res}, req, err)
60
- }
61
-
62
- // Submit godoc
63
- // @Summary Submit Academy Exam
64
- // @Description Submit the exam attempt for evaluation
65
- // @Tags Academy Exam
66
- // @Accept json
67
- // @Produce json
68
- // @Param academy_slug path string true "Academy Slug"
69
- // @Param attempt_id path string true "Exam Attempt ID"
70
- // @Success 200 {object} dto.SuccessResponse[entity.ExamAcademyResult]
71
- // @Failure 400 {object} dto.ErrorResponse
72
- // @Router /api/v1/academy/{academy_slug}/exam/{attempt_id}/submit [post]
73
-
74
- func (c *academyExamController) Submit(ctx *gin.Context) {
75
- attemptId := ParseUUID(ctx, "attempt_id")
76
- res, err := c.academyExamService.SubmitExamAcademy(ctx.Request.Context(), attemptId)
77
- ResponseJSON(ctx, gin.H{}, res, err)
78
- }
79
-
80
- // List godoc
81
- // @Summary List Academy Exams
82
- // @Description Retrieve a list of exams available in a specific academy
83
- // @Tags Academy Exam
84
- // @Accept json
85
- // @Produce json
86
- // @Param academy_slug path string true "Academy Slug"
87
- // @Success 200 {object} dto.SuccessResponse[[]entity.Exam]
88
- // @Failure 400 {object} dto.ErrorResponse
89
- // @Router /api/v1/academy/{academy_slug}/exam [get]
90
-
91
- func (c *academyExamController) List(ctx *gin.Context) {
92
- academySlug := ctx.Param("academy_slug")
93
- accountId := ParseAccountId(ctx)
94
- res, err := c.academyExamService.ListExamByAcademy(ctx.Request.Context(), academySlug, accountId)
95
- ResponseJSON(ctx, gin.H{}, res, err)
96
- }
 
1
+ package controllers
2
+
3
+ import (
4
+ "abdanhafidz.com/go-boilerplate/models/dto"
5
+ "abdanhafidz.com/go-boilerplate/services"
6
+ "github.com/gin-gonic/gin"
7
+ )
8
+
9
+ type AcademyExamController interface {
10
+ Attempt(ctx *gin.Context)
11
+ Answer(ctx *gin.Context)
12
+ Submit(ctx *gin.Context)
13
+ List(ctx *gin.Context)
14
+ }
15
+
16
+ type academyExamController struct{ academyExamService services.AcademyExamService }
17
+
18
+ func NewAcademyExamController(academyExamService services.AcademyExamService) AcademyExamController {
19
+ return &academyExamController{academyExamService: academyExamService}
20
+ }
21
+
22
+ // Attempt godoc
23
+ // @Summary Attempt Academy Exam
24
+ // @Description Start an attempt for a specific exam in an academy
25
+ // @Tags Academy Exam
26
+ // @Accept json
27
+ // @Produce json
28
+ // @Param academy_slug path string true "Academy Slug"
29
+ // @Param exam_slug path string true "Exam Slug"
30
+ // @Success 200 {object} dto.SuccessResponse[models.AcademyExamAttempt]
31
+ // @Failure 400 {object} dto.ErrorResponse
32
+ // @Router /api/v1/academy/{academy_slug}/exam/{exam_slug}/attempt [get]
33
+ func (c *academyExamController) Attempt(ctx *gin.Context) {
34
+ academySlug := ctx.Param("academy_slug")
35
+ examSlug := ctx.Param("exam_slug")
36
+ accountId := ParseAccountId(ctx)
37
+ res, err := c.academyExamService.AttemptAcademyExam(ctx.Request.Context(), academySlug, examSlug, accountId)
38
+ ResponseJSON(ctx, gin.H{"academy_slug": academySlug, "exam_slug": examSlug}, res, err)
39
+ }
40
+
41
+ // Answer godoc
42
+ // @Summary Answer Academy Exam Question
43
+ // @Description Submit an answer for a specific question in an exam attempt
44
+ // @Tags Academy Exam
45
+ // @Accept json
46
+ // @Produce json
47
+ // @Param academy_slug path string true "Academy Slug"
48
+ // @Param attempt_id path string true "Exam Attempt ID"
49
+ // @Param request body dto.AnswerEventExamRequest true "Answer Exam Event Request"
50
+ // @Success 200 {object} dto.SuccessResponse[any]
51
+ // @Failure 400 {object} dto.ErrorResponse
52
+ // @Router /api/v1/academy/{academy_slug}/exam/{attempt_id}/answer_question [post]
53
+
54
+ func (c *academyExamController) Answer(ctx *gin.Context) {
55
+ academySlug := ctx.Param("academy_slug")
56
+ attemptId := ParseUUID(ctx, "attempt_id")
57
+ req := RequestJSON[dto.AnswerEventExamRequest](ctx)
58
+ res, err := c.academyExamService.AnswerAcademyExam(ctx.Request.Context(), academySlug, attemptId, req.QuestionId, req.Answer)
59
+ ResponseJSON(ctx, gin.H{"cp_grader_result": res}, req, err)
60
+ }
61
+
62
+ // Submit godoc
63
+ // @Summary Submit Academy Exam
64
+ // @Description Submit the exam attempt for evaluation
65
+ // @Tags Academy Exam
66
+ // @Accept json
67
+ // @Produce json
68
+ // @Param academy_slug path string true "Academy Slug"
69
+ // @Param attempt_id path string true "Exam Attempt ID"
70
+ // @Success 200 {object} dto.SuccessResponse[entity.AcademyExamResult]
71
+ // @Failure 400 {object} dto.ErrorResponse
72
+ // @Router /api/v1/academy/{academy_slug}/exam/{attempt_id}/submit [post]
73
+
74
+ func (c *academyExamController) Submit(ctx *gin.Context) {
75
+ attemptId := ParseUUID(ctx, "attempt_id")
76
+ res, err := c.academyExamService.SubmitAcademyExam(ctx.Request.Context(), attemptId)
77
+ ResponseJSON(ctx, gin.H{}, res, err)
78
+ }
79
+
80
+ // List godoc
81
+ // @Summary List Academy Exams
82
+ // @Description Retrieve a list of exams available in a specific academy
83
+ // @Tags Academy Exam
84
+ // @Accept json
85
+ // @Produce json
86
+ // @Param academy_slug path string true "Academy Slug"
87
+ // @Success 200 {object} dto.SuccessResponse[[]entity.Exam]
88
+ // @Failure 400 {object} dto.ErrorResponse
89
+ // @Router /api/v1/academy/{academy_slug}/exam [get]
90
+
91
+ func (c *academyExamController) List(ctx *gin.Context) {
92
+ academySlug := ctx.Param("academy_slug")
93
+ accountId := ParseAccountId(ctx)
94
+ res, err := c.academyExamService.ListExamByAcademy(ctx.Request.Context(), academySlug, accountId)
95
+ ResponseJSON(ctx, gin.H{}, res, err)
96
+ }
controllers/controller.go CHANGED
@@ -38,6 +38,16 @@ func RequestJSON[TRequest any](ctx *gin.Context) TRequest {
38
  }
39
  }
40
 
 
 
 
 
 
 
 
 
 
 
41
  func ResponseJSON[TResponse any, TMetaData any](ctx *gin.Context, metaData TMetaData, res TResponse, err error) {
42
  utils.SendResponse(ctx, metaData, res, err)
43
  }
 
38
  }
39
  }
40
 
41
+ func RequestForm[TRequest any](ctx *gin.Context) TRequest {
42
+ var request TRequest
43
+ if err := ctx.ShouldBind(&request); err != nil {
44
+ utils.ResponseFAILED(ctx, request, http_error.BAD_REQUEST_ERROR)
45
+ ctx.Abort()
46
+ return request
47
+ }
48
+ return request
49
+ }
50
+
51
  func ResponseJSON[TResponse any, TMetaData any](ctx *gin.Context, metaData TMetaData, res TResponse, err error) {
52
  utils.SendResponse(ctx, metaData, res, err)
53
  }
controllers/{exam_event_controller.go → event_exam_controller.go} RENAMED
@@ -1,98 +1,98 @@
1
- package controllers
2
-
3
- import (
4
- "abdanhafidz.com/go-boilerplate/models/dto"
5
- "abdanhafidz.com/go-boilerplate/services"
6
- "github.com/gin-gonic/gin"
7
- )
8
-
9
- type ExamController interface {
10
- Attempt(ctx *gin.Context)
11
- Answer(ctx *gin.Context)
12
- Submit(ctx *gin.Context)
13
- List(ctx *gin.Context)
14
- }
15
-
16
- type examController struct {
17
- examService services.ExamService
18
- }
19
-
20
- func NewExamController(examService services.ExamService) ExamController {
21
- return &examController{
22
- examService: examService,
23
- }
24
- }
25
-
26
- // Exam Event Attempt godoc
27
- // @Summary Attempt Exam Event
28
- // @Description Start an attempt for a specific exam in an event
29
- // @Tags Exam Event
30
- // @Accept json
31
- // @Produce json
32
- // @Param event_slug path string true "Event Slug"
33
- // @Param exam_slug path string true "Exam Slug"
34
- // @Success 200 {object} dto.SuccessResponse[models.ExamEventAttempt]
35
- // @Failure 400 {object} dto.ErrorResponse
36
- // @Router /api/v1/events/{event_slug}/exam/{exam_slug}/attempt [get]
37
- func (c *examController) Attempt(ctx *gin.Context) {
38
- eventSlug := ctx.Param("event_slug")
39
- examSlug := ctx.Param("exam_slug")
40
- accountId := ParseAccountId(ctx)
41
- res, err := c.examService.AttemptExamEvent(ctx.Request.Context(), eventSlug, examSlug, accountId)
42
- ResponseJSON(ctx, gin.H{"event_slug": eventSlug, "exam_slug": examSlug}, res, err)
43
- }
44
-
45
- // Answer Exam Event godoc
46
- // @Summary Answer Exam Event Question
47
- // @Description Submit an answer for a specific question in an exam attempt
48
- // @Tags Exam Event
49
- // @Accept json
50
- // @Produce json
51
- // @Param event_slug path string true "Event Slug"
52
- // @Param attempt_id path string true "Exam Attempt ID"
53
- // @Param request body dto.AnswerExamEventRequest true "Answer Exam Event Request"
54
- // @Success 200 {object} dto.SuccessResponse[dto.AnswerExamEventRequest]
55
- // @Failure 400 {object} dto.ErrorResponse
56
- // @Router /api/v1/events/{event_slug}/exam/{attempt_id}/answer_question [post]
57
- func (c *examController) Answer(ctx *gin.Context) {
58
- eventSlug := ctx.Param("event_slug")
59
- attemptId := ParseUUID(ctx, "attempt_id")
60
- req := RequestJSON[dto.AnswerExamEventRequest](ctx)
61
- res, err := c.examService.AnswerExamEvent(ctx.Request.Context(), eventSlug, attemptId, req.QuestionId, req.Answer)
62
- ResponseJSON(ctx, gin.H{"cp_grader_result": res}, req, err)
63
- }
64
-
65
- // Submit Exam Event godoc
66
- // @Summary Submit Exam Event
67
- // @Description Submit the exam attempt for evaluation
68
- // @Tags Exam Event
69
- // @Accept json
70
- // @Produce json
71
- // @Param event_slug path string true "Event Slug"
72
- // @Param attempt_id path string true "Exam Attempt ID"
73
- // @Success 200 {object} dto.SuccessResponse[entity.Result]
74
- // @Failure 400 {object} dto.ErrorResponse
75
- // @Router /api/v1/events/{event_slug}/exam/{attempt_id}/submit [post]
76
-
77
- func (c *examController) Submit(ctx *gin.Context) {
78
- attemptId := ParseUUID(ctx, "attempt_id")
79
- res, err := c.examService.SubmitExamEvent(ctx.Request.Context(), attemptId)
80
- ResponseJSON(ctx, gin.H{}, res, err)
81
- }
82
-
83
- // List Exam by Event godoc
84
- // @Summary List Exams by Event
85
- // @Description Retrieve a list of exams associated with a specific event
86
- // @Tags Exam Event
87
- // @Accept json
88
- // @Produce json
89
- // @Param event_slug path string true "Event Slug"
90
- // @Success 200 {object} dto.SuccessResponse[[]models.Exam]
91
- // @Failure 400 {object} dto.ErrorResponse
92
- // @Router /api/v1/events/{event_slug}/exam [get]
93
- func (c *examController) List(ctx *gin.Context) {
94
- eventSlug := ctx.Param("event_slug")
95
- accountId := ParseAccountId(ctx)
96
- res, err := c.examService.ListExamByEvent(ctx.Request.Context(), eventSlug, accountId)
97
- ResponseJSON(ctx, gin.H{}, res, err)
98
- }
 
1
+ package controllers
2
+
3
+ import (
4
+ "abdanhafidz.com/go-boilerplate/models/dto"
5
+ "abdanhafidz.com/go-boilerplate/services"
6
+ "github.com/gin-gonic/gin"
7
+ )
8
+
9
+ type EventExamController interface {
10
+ Attempt(ctx *gin.Context)
11
+ Answer(ctx *gin.Context)
12
+ Submit(ctx *gin.Context)
13
+ List(ctx *gin.Context)
14
+ }
15
+
16
+ type eventExamController struct {
17
+ eventExamService services.EventExamService
18
+ }
19
+
20
+ func NewEventExamController(eventExamService services.EventExamService) EventExamController {
21
+ return &eventExamController{
22
+ eventExamService: eventExamService,
23
+ }
24
+ }
25
+
26
+ // Exam Event Attempt godoc
27
+ // @Summary Attempt Exam Event
28
+ // @Description Start an attempt for a specific exam in an event
29
+ // @Tags Exam Event
30
+ // @Accept json
31
+ // @Produce json
32
+ // @Param event_slug path string true "Event Slug"
33
+ // @Param exam_slug path string true "Exam Slug"
34
+ // @Success 200 {object} dto.SuccessResponse[models.EventExamAttempt]
35
+ // @Failure 400 {object} dto.ErrorResponse
36
+ // @Router /api/v1/events/{event_slug}/exam/{exam_slug}/attempt [get]
37
+ func (c *eventExamController) Attempt(ctx *gin.Context) {
38
+ eventSlug := ctx.Param("event_slug")
39
+ examSlug := ctx.Param("exam_slug")
40
+ accountId := ParseAccountId(ctx)
41
+ res, err := c.eventExamService.AttemptEventExam(ctx.Request.Context(), eventSlug, examSlug, accountId)
42
+ ResponseJSON(ctx, gin.H{"event_slug": eventSlug, "exam_slug": examSlug}, res, err)
43
+ }
44
+
45
+ // Answer Exam Event godoc
46
+ // @Summary Answer Exam Event Question
47
+ // @Description Submit an answer for a specific question in an exam attempt
48
+ // @Tags Exam Event
49
+ // @Accept json
50
+ // @Produce json
51
+ // @Param event_slug path string true "Event Slug"
52
+ // @Param attempt_id path string true "Exam Attempt ID"
53
+ // @Param request body dto.AnswerEventExamRequest true "Answer Exam Event Request"
54
+ // @Success 200 {object} dto.SuccessResponse[dto.AnswerEventExamRequest]
55
+ // @Failure 400 {object} dto.ErrorResponse
56
+ // @Router /api/v1/events/{event_slug}/exam/{attempt_id}/answer_question [post]
57
+ func (c *eventExamController) Answer(ctx *gin.Context) {
58
+ eventSlug := ctx.Param("event_slug")
59
+ attemptId := ParseUUID(ctx, "attempt_id")
60
+ req := RequestJSON[dto.AnswerEventExamRequest](ctx)
61
+ res, err := c.eventExamService.AnswerEventExam(ctx.Request.Context(), eventSlug, attemptId, req.QuestionId, req.Answer)
62
+ ResponseJSON(ctx, gin.H{"cp_grader_result": res}, req, err)
63
+ }
64
+
65
+ // Submit Exam Event godoc
66
+ // @Summary Submit Exam Event
67
+ // @Description Submit the exam attempt for evaluation
68
+ // @Tags Exam Event
69
+ // @Accept json
70
+ // @Produce json
71
+ // @Param event_slug path string true "Event Slug"
72
+ // @Param attempt_id path string true "Exam Attempt ID"
73
+ // @Success 200 {object} dto.SuccessResponse[entity.Result]
74
+ // @Failure 400 {object} dto.ErrorResponse
75
+ // @Router /api/v1/events/{event_slug}/exam/{attempt_id}/submit [post]
76
+
77
+ func (c *eventExamController) Submit(ctx *gin.Context) {
78
+ attemptId := ParseUUID(ctx, "attempt_id")
79
+ res, err := c.eventExamService.SubmitEventExam(ctx.Request.Context(), attemptId)
80
+ ResponseJSON(ctx, gin.H{}, res, err)
81
+ }
82
+
83
+ // List Exam by Event godoc
84
+ // @Summary List Exams by Event
85
+ // @Description Retrieve a list of exams associated with a specific event
86
+ // @Tags Exam Event
87
+ // @Accept json
88
+ // @Produce json
89
+ // @Param event_slug path string true "Event Slug"
90
+ // @Success 200 {object} dto.SuccessResponse[[]models.Exam]
91
+ // @Failure 400 {object} dto.ErrorResponse
92
+ // @Router /api/v1/events/{event_slug}/exam [get]
93
+ func (c *eventExamController) List(ctx *gin.Context) {
94
+ eventSlug := ctx.Param("event_slug")
95
+ accountId := ParseAccountId(ctx)
96
+ res, err := c.eventExamService.ListExamByEvent(ctx.Request.Context(), eventSlug, accountId)
97
+ ResponseJSON(ctx, gin.H{}, res, err)
98
+ }
controllers/event_exam_proctoring_controller.go ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package controllers
2
+
3
+ import (
4
+ "abdanhafidz.com/go-boilerplate/models/dto"
5
+ "abdanhafidz.com/go-boilerplate/services"
6
+ "github.com/gin-gonic/gin"
7
+ "github.com/google/uuid"
8
+ )
9
+
10
+ type EventExamProctoringController interface {
11
+ CreateLog(c *gin.Context)
12
+ ListLogs(c *gin.Context)
13
+ GetLogById(c *gin.Context)
14
+ UpdateLog(c *gin.Context)
15
+ DeleteLog(c *gin.Context)
16
+ }
17
+
18
+ type eventExamProctoringController struct {
19
+ service services.EventExamProctoringService
20
+ }
21
+
22
+ func NewEventExamProctoringController(service services.EventExamProctoringService) EventExamProctoringController {
23
+ return &eventExamProctoringController{service: service}
24
+ }
25
+
26
+ // CreateLog godoc
27
+ // @Summary Create Proctoring Log
28
+ // @Description Create a new proctoring log entry with optional file attachment
29
+ // @Tags Event Exam Proctoring
30
+ // @Accept multipart/form-data
31
+ // @Produce json
32
+ // @Param id_event formData string true "Event ID"
33
+ // @Param id_exam formData string true "Exam ID"
34
+ // @Param id_account formData string true "Account ID"
35
+ // @Param violation_score formData int true "Violation Score"
36
+ // @Param violation_category formData string true "Violation Category"
37
+ // @Param file formData file false "Attachment File"
38
+ // @Success 200 {object} dto.SuccessResponse[string]
39
+ // @Failure 400 {object} dto.ErrorResponse
40
+ // @Failure 500 {object} dto.ErrorResponse
41
+ // @Router /api/v1/events/{event_slug}/exam/{exam_slug}/proctoring [post]
42
+ func (ctrl *eventExamProctoringController) CreateLog(c *gin.Context) {
43
+ req := RequestForm[dto.EventExamProctoringLogsRequest](c)
44
+
45
+ file, _ := c.FormFile("file")
46
+
47
+ err := ctrl.service.CreateLog(c.Request.Context(), req, file)
48
+ ResponseJSON(c, req, "OK", err)
49
+ }
50
+
51
+ // ListLogs godoc
52
+ // @Summary List Proctoring Logs
53
+ // @Description List proctoring logs by account, exam, or event
54
+ // @Tags Event Exam Proctoring
55
+ // @Accept json
56
+ // @Produce json
57
+ // @Param event_slug path string true "Event Slug"
58
+ // @Param exam_slug path string true "Exam Slug"
59
+ // @Param account_id query string false "Account ID"
60
+ // @Param exam_id query string false "Exam ID"
61
+ // @Param event_id query string false "Event ID"
62
+ // @Success 200 {object} dto.SuccessResponse[[]models.EventExamProctoringLogs]
63
+ // @Failure 400 {object} dto.ErrorResponse
64
+ // @Failure 500 {object} dto.ErrorResponse
65
+ // @Router /api/v1/events/{event_slug}/exam/{exam_slug}/proctoring [get]
66
+ func (ctrl *eventExamProctoringController) ListLogs(c *gin.Context) {
67
+ accountId := ParseUUIDFromQuery(c, "account_id")
68
+ examId := ParseUUIDFromQuery(c, "exam_id")
69
+ eventId := ParseUUIDFromQuery(c, "event_id")
70
+
71
+ logs, err := ctrl.service.ListLogs(c.Request.Context(), accountId, examId, eventId)
72
+ ResponseJSON(c, gin.H{}, logs, err)
73
+ }
74
+
75
+ // GetLogById godoc
76
+ // @Summary Get Proctoring Log By ID
77
+ // @Description Get details of a specific proctoring log
78
+ // @Tags Event Exam Proctoring
79
+ // @Accept json
80
+ // @Produce json
81
+ // @Param event_slug path string true "Event Slug"
82
+ // @Param exam_slug path string true "Exam Slug"
83
+ // @Param log_id path string true "Log ID"
84
+ // @Success 200 {object} dto.SuccessResponse[models.EventExamProctoringLogs]
85
+ // @Failure 400 {object} dto.ErrorResponse
86
+ // @Failure 404 {object} dto.ErrorResponse
87
+ // @Failure 500 {object} dto.ErrorResponse
88
+ // @Router /api/v1/events/{event_slug}/exam/{exam_slug}/proctoring/{log_id} [get]
89
+ func (ctrl *eventExamProctoringController) GetLogById(c *gin.Context) {
90
+ id := ParseUUID(c, "log_id")
91
+ log, err := ctrl.service.GetLogById(c.Request.Context(), id)
92
+ ResponseJSON(c, gin.H{}, log, err)
93
+ }
94
+
95
+ // UpdateLog godoc
96
+ // @Summary Update Proctoring Log
97
+ // @Description Update an existing proctoring log
98
+ // @Tags Event Exam Proctoring
99
+ // @Accept multipart/form-data
100
+ // @Produce json
101
+ // @Param event_slug path string true "Event Slug"
102
+ // @Param exam_slug path string true "Exam Slug"
103
+ // @Param log_id path string true "Log ID"
104
+ // @Param violation_score formData int false "Violation Score"
105
+ // @Param violation_category formData string false "Violation Category"
106
+ // @Param file formData file false "Attachment File"
107
+ // @Param id_account formData string true "Account ID (required for upload context)"
108
+ // @Success 200 {object} dto.SuccessResponse[string]
109
+ // @Failure 400 {object} dto.ErrorResponse
110
+ // @Failure 404 {object} dto.ErrorResponse
111
+ // @Failure 500 {object} dto.ErrorResponse
112
+ // @Router /api/v1/events/{event_slug}/exam/{exam_slug}/proctoring/{log_id} [put]
113
+ func (ctrl *eventExamProctoringController) UpdateLog(c *gin.Context) {
114
+ id := ParseUUID(c, "log_id")
115
+
116
+ req := RequestForm[dto.EventExamProctoringLogsRequest](c)
117
+
118
+ file, _ := c.FormFile("file")
119
+
120
+ err := ctrl.service.UpdateLog(c.Request.Context(), id, req, file)
121
+ ResponseJSON(c, req, "OK", err)
122
+ }
123
+
124
+ // DeleteLog godoc
125
+ // @Summary Delete Proctoring Log
126
+ // @Description Delete a proctoring log entry
127
+ // @Tags Event Exam Proctoring
128
+ // @Accept json
129
+ // @Produce json
130
+ // @Param event_slug path string true "Event Slug"
131
+ // @Param exam_slug path string true "Exam Slug"
132
+ // @Param log_id path string true "Log ID"
133
+ // @Success 200 {object} dto.SuccessResponse[string]
134
+ // @Failure 400 {object} dto.ErrorResponse
135
+ // @Failure 404 {object} dto.ErrorResponse
136
+ // @Failure 500 {object} dto.ErrorResponse
137
+ // @Router /api/v1/events/{event_slug}/exam/{exam_slug}/proctoring/{log_id} [delete]
138
+ func (ctrl *eventExamProctoringController) DeleteLog(c *gin.Context) {
139
+ id := ParseUUID(c, "log_id")
140
+ err := ctrl.service.DeleteLog(c.Request.Context(), id)
141
+ ResponseJSON(c, gin.H{"id": id}, "OK", err)
142
+ }
143
+
144
+ // Helper to parse UUID from query param since ParseUUID didn't seem to do it
145
+ func ParseUUIDFromQuery(c *gin.Context, key string) uuid.UUID {
146
+ val := c.Query(key)
147
+ if val == "" {
148
+ return uuid.Nil
149
+ }
150
+ id, _ := uuid.Parse(val)
151
+ return id
152
+ }
controllers/exam_controller.go ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package controllers
2
+
3
+ import (
4
+ "strconv"
5
+
6
+ "abdanhafidz.com/go-boilerplate/models/dto"
7
+ entity "abdanhafidz.com/go-boilerplate/models/entity"
8
+ "abdanhafidz.com/go-boilerplate/services"
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ type ExamController interface {
13
+ CreateExam(ctx *gin.Context)
14
+ UpdateExam(ctx *gin.Context)
15
+ DeleteExam(ctx *gin.Context)
16
+ ListExam(ctx *gin.Context)
17
+ GetExamDetail(ctx *gin.Context)
18
+ AssignExamToEvent(ctx *gin.Context)
19
+ AssignExamToAcademy(ctx *gin.Context)
20
+ }
21
+
22
+ type examController struct {
23
+ examService services.ExamService
24
+ }
25
+
26
+ func NewExamController(examService services.ExamService) ExamController {
27
+ return &examController{examService}
28
+ }
29
+
30
+ // CreateExam godoc
31
+ // @Summary Create Exam
32
+ // @Description Create a new exam with configuration and proctoring settings
33
+ // @Tags Exam
34
+ // @Accept json
35
+ // @Produce json
36
+ // @Param request body dto.CreateExamRequest true "Create Exam Request"
37
+ // @Success 200 {object} dto.SuccessResponse[entity.Exam]
38
+ // @Failure 400 {object} dto.ErrorResponse
39
+ // @Security BearerAuth
40
+ // @Router /api/v1/admin/exam [post]
41
+ func (c *examController) CreateExam(ctx *gin.Context) {
42
+ req := RequestJSON[dto.CreateExamRequest](ctx)
43
+ res, err := c.examService.CreateExam(ctx.Request.Context(), req)
44
+ ResponseJSON(ctx, req, res, err)
45
+ }
46
+
47
+ // UpdateExam godoc
48
+ // @Summary Update Exam
49
+ // @Description Update an existing exam
50
+ // @Tags Exam
51
+ // @Accept json
52
+ // @Produce json
53
+ // @Param id path string true "Exam ID"
54
+ // @Param request body dto.CreateExamRequest true "Update Exam Request"
55
+ // @Success 200 {object} dto.SuccessResponse[entity.Exam]
56
+ // @Failure 400 {object} dto.ErrorResponse
57
+ // @Security BearerAuth
58
+ // @Router /api/v1/admin/exam/{id} [put]
59
+ func (c *examController) UpdateExam(ctx *gin.Context) {
60
+ id := ParseUUID(ctx, "id")
61
+ req := RequestJSON[dto.CreateExamRequest](ctx)
62
+ res, err := c.examService.UpdateExam(ctx.Request.Context(), id, req)
63
+ ResponseJSON(ctx, req, res, err)
64
+ }
65
+
66
+ // DeleteExam godoc
67
+ // @Summary Delete Exam
68
+ // @Description Delete an existing exam
69
+ // @Tags Exam
70
+ // @Accept json
71
+ // @Produce json
72
+ // @Param id path string true "Exam ID"
73
+ // @Success 200 {object} dto.SuccessResponse[any]
74
+ // @Failure 400 {object} dto.ErrorResponse
75
+ // @Security BearerAuth
76
+ // @Router /api/v1/admin/exam/{id} [delete]
77
+ func (c *examController) DeleteExam(ctx *gin.Context) {
78
+ id := ParseUUID(ctx, "id")
79
+ err := c.examService.DeleteExam(ctx.Request.Context(), id)
80
+ ResponseJSON(ctx, gin.H{"id": id}, gin.H{"deleted": true}, err)
81
+ }
82
+
83
+ // ListExam godoc
84
+ // @Summary List Exams
85
+ // @Description Retrieve a paginated list of exams
86
+ // @Tags Exam
87
+ // @Accept json
88
+ // @Produce json
89
+ // @Param limit query int false "Number of items per page" default(10)
90
+ // @Param page query int false "Page number" default(1)
91
+ // @Param search query string false "Search term for exam title"
92
+ // @Param sortBy query string false "Field to sort by"
93
+ // @Param order query string false "Sort order (asc or desc)"
94
+ // @Success 200 {object} dto.SuccessResponse[[]entity.Exam]
95
+ // @Failure 400 {object} dto.ErrorResponse
96
+ // @Security BearerAuth
97
+ // @Router /api/v1/admin/exam [get]
98
+ func (c *examController) ListExam(ctx *gin.Context) {
99
+ limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
100
+ page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
101
+ search := ctx.DefaultQuery("search", "")
102
+ sortBy := ctx.DefaultQuery("sortBy", "")
103
+ order := ctx.DefaultQuery("order", "")
104
+
105
+ if limit < 1 {
106
+ limit = 10
107
+ } else if limit > 50 {
108
+ limit = 50
109
+ }
110
+
111
+ if page < 1 {
112
+ page = 1
113
+ }
114
+
115
+ offset := (page - 1) * limit
116
+ p := entity.Pagination{Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order}
117
+
118
+ list, total, err := c.examService.GetExamList(ctx.Request.Context(), p)
119
+
120
+ if err != nil {
121
+ ResponseJSON[any, any](ctx, nil, nil, err)
122
+ return
123
+ }
124
+
125
+ totalPages := int((total + int64(limit) - 1) / int64(limit))
126
+ if total == 0 {
127
+ totalPages = 1
128
+ }
129
+
130
+ meta := gin.H{
131
+ "totalItems": total,
132
+ "totalPages": totalPages,
133
+ "currentPage": page,
134
+ }
135
+
136
+ ResponseJSON(ctx, meta, list, err)
137
+ }
138
+
139
+ // GetExamDetail godoc
140
+ // @Summary Get Exam Detail
141
+ // @Description Retrieve detailed exam information
142
+ // @Tags Exam
143
+ // @Accept json
144
+ // @Produce json
145
+ // @Param id path string true "Exam ID"
146
+ // @Success 200 {object} dto.SuccessResponse[entity.Exam]
147
+ // @Failure 400 {object} dto.ErrorResponse
148
+ // @Security BearerAuth
149
+ // @Router /api/v1/admin/exam/{id} [get]
150
+ func (c *examController) GetExamDetail(ctx *gin.Context) {
151
+ id := ParseUUID(ctx, "id")
152
+ res, err := c.examService.GetExamDetail(ctx.Request.Context(), id)
153
+ ResponseJSON(ctx, gin.H{"id": id}, res, err)
154
+ }
155
+
156
+ // AssignExamToEvent godoc
157
+ // @Summary Assign Exam to Event
158
+ // @Description Assign an exam to an event
159
+ // @Tags Exam
160
+ // @Accept json
161
+ // @Produce json
162
+ // @Param exam_id path string true "Exam ID"
163
+ // @Param event_id path string true "Event ID"
164
+ // @Success 200 {object} dto.SuccessResponse[any]
165
+ // @Failure 400 {object} dto.ErrorResponse
166
+ // @Security BearerAuth
167
+ // @Router /api/v1/admin/exam/{exam_id}/event/{event_id} [post]
168
+ func (c *examController) AssignExamToEvent(ctx *gin.Context) {
169
+ examId := ParseUUID(ctx, "exam_id")
170
+ eventId := ParseUUID(ctx, "event_id")
171
+
172
+ err := c.examService.AssignExamToEvent(ctx.Request.Context(), examId, eventId)
173
+ ResponseJSON(ctx, gin.H{"exam_id": examId, "event_id": eventId}, gin.H{"assigned": true}, err)
174
+ }
175
+
176
+ // AssignExamToAcademy godoc
177
+ // @Summary Assign Exam to Academy
178
+ // @Description Assign an exam to an academy
179
+ // @Tags Exam
180
+ // @Accept json
181
+ // @Produce json
182
+ // @Param exam_id path string true "Exam ID"
183
+ // @Param academy_id path string true "Academy ID"
184
+ // @Success 200 {object} dto.SuccessResponse[any]
185
+ // @Failure 400 {object} dto.ErrorResponse
186
+ // @Security BearerAuth
187
+ // @Router /api/v1/admin/exam/{exam_id}/academy/{academy_id} [post]
188
+ func (c *examController) AssignExamToAcademy(ctx *gin.Context) {
189
+ examId := ParseUUID(ctx, "exam_id")
190
+ academyId := ParseUUID(ctx, "academy_id")
191
+
192
+ err := c.examService.AssignExamToAcademy(ctx.Request.Context(), examId, academyId)
193
+ ResponseJSON(ctx, gin.H{"exam_id": examId, "academy_id": academyId}, gin.H{"assigned": true}, err)
194
+ }
docs/docs.go DELETED
@@ -1,312 +0,0 @@
1
- // Package docs Code generated by swaggo/swag. DO NOT EDIT
2
- package docs
3
-
4
- import "github.com/swaggo/swag"
5
-
6
- const docTemplate = `{
7
- "schemes": {{ marshal .Schemes }},
8
- "swagger": "2.0",
9
- "info": {
10
- "description": "{{escape .Description}}",
11
- "title": "{{.Title}}",
12
- "contact": {},
13
- "version": "{{.Version}}"
14
- },
15
- "host": "{{.Host}}",
16
- "basePath": "{{.BasePath}}",
17
- "paths": {
18
- "/academy": {
19
- "get": {
20
- "security": [
21
- {
22
- "BearerAuth": []
23
- }
24
- ],
25
- "produces": [
26
- "application/json"
27
- ],
28
- "tags": [
29
- "Academy"
30
- ],
31
- "summary": "Academy example endpoint",
32
- "responses": {
33
- "200": {
34
- "description": "OK",
35
- "schema": {
36
- "type": "object",
37
- "additionalProperties": true
38
- }
39
- }
40
- }
41
- }
42
- },
43
- "/academy-exam": {
44
- "get": {
45
- "security": [
46
- {
47
- "BearerAuth": []
48
- }
49
- ],
50
- "produces": [
51
- "application/json"
52
- ],
53
- "tags": [
54
- "Academy_exam"
55
- ],
56
- "summary": "Academy_exam example endpoint",
57
- "responses": {
58
- "200": {
59
- "description": "OK",
60
- "schema": {
61
- "type": "object",
62
- "additionalProperties": true
63
- }
64
- }
65
- }
66
- }
67
- },
68
- "/account": {
69
- "get": {
70
- "security": [
71
- {
72
- "BearerAuth": []
73
- }
74
- ],
75
- "produces": [
76
- "application/json"
77
- ],
78
- "tags": [
79
- "Account"
80
- ],
81
- "summary": "Account example endpoint",
82
- "responses": {
83
- "200": {
84
- "description": "OK",
85
- "schema": {
86
- "type": "object",
87
- "additionalProperties": true
88
- }
89
- }
90
- }
91
- }
92
- },
93
- "/admin": {
94
- "get": {
95
- "security": [
96
- {
97
- "BearerAuth": []
98
- }
99
- ],
100
- "produces": [
101
- "application/json"
102
- ],
103
- "tags": [
104
- "Admin"
105
- ],
106
- "summary": "Admin example endpoint",
107
- "responses": {
108
- "200": {
109
- "description": "OK",
110
- "schema": {
111
- "type": "object",
112
- "additionalProperties": true
113
- }
114
- }
115
- }
116
- }
117
- },
118
- "/auth": {
119
- "get": {
120
- "security": [
121
- {
122
- "BearerAuth": []
123
- }
124
- ],
125
- "produces": [
126
- "application/json"
127
- ],
128
- "tags": [
129
- "Auth"
130
- ],
131
- "summary": "Auth example endpoint",
132
- "responses": {
133
- "200": {
134
- "description": "OK",
135
- "schema": {
136
- "type": "object",
137
- "additionalProperties": true
138
- }
139
- }
140
- }
141
- }
142
- },
143
- "/email": {
144
- "get": {
145
- "security": [
146
- {
147
- "BearerAuth": []
148
- }
149
- ],
150
- "produces": [
151
- "application/json"
152
- ],
153
- "tags": [
154
- "Email"
155
- ],
156
- "summary": "Email example endpoint",
157
- "responses": {
158
- "200": {
159
- "description": "OK",
160
- "schema": {
161
- "type": "object",
162
- "additionalProperties": true
163
- }
164
- }
165
- }
166
- }
167
- },
168
- "/event": {
169
- "get": {
170
- "security": [
171
- {
172
- "BearerAuth": []
173
- }
174
- ],
175
- "produces": [
176
- "application/json"
177
- ],
178
- "tags": [
179
- "Event"
180
- ],
181
- "summary": "Event example endpoint",
182
- "responses": {
183
- "200": {
184
- "description": "OK",
185
- "schema": {
186
- "type": "object",
187
- "additionalProperties": true
188
- }
189
- }
190
- }
191
- }
192
- },
193
- "/exam-event": {
194
- "get": {
195
- "security": [
196
- {
197
- "BearerAuth": []
198
- }
199
- ],
200
- "produces": [
201
- "application/json"
202
- ],
203
- "tags": [
204
- "Exam_event"
205
- ],
206
- "summary": "Exam_event example endpoint",
207
- "responses": {
208
- "200": {
209
- "description": "OK",
210
- "schema": {
211
- "type": "object",
212
- "additionalProperties": true
213
- }
214
- }
215
- }
216
- }
217
- },
218
- "/forgot-password": {
219
- "get": {
220
- "security": [
221
- {
222
- "BearerAuth": []
223
- }
224
- ],
225
- "produces": [
226
- "application/json"
227
- ],
228
- "tags": [
229
- "Forgot_password"
230
- ],
231
- "summary": "Forgot_password example endpoint",
232
- "responses": {
233
- "200": {
234
- "description": "OK",
235
- "schema": {
236
- "type": "object",
237
- "additionalProperties": true
238
- }
239
- }
240
- }
241
- }
242
- },
243
- "/option": {
244
- "get": {
245
- "security": [
246
- {
247
- "BearerAuth": []
248
- }
249
- ],
250
- "produces": [
251
- "application/json"
252
- ],
253
- "tags": [
254
- "Option"
255
- ],
256
- "summary": "Option example endpoint",
257
- "responses": {
258
- "200": {
259
- "description": "OK",
260
- "schema": {
261
- "type": "object",
262
- "additionalProperties": true
263
- }
264
- }
265
- }
266
- }
267
- },
268
- "/upload": {
269
- "get": {
270
- "security": [
271
- {
272
- "BearerAuth": []
273
- }
274
- ],
275
- "produces": [
276
- "application/json"
277
- ],
278
- "tags": [
279
- "Upload"
280
- ],
281
- "summary": "Upload example endpoint",
282
- "responses": {
283
- "200": {
284
- "description": "OK",
285
- "schema": {
286
- "type": "object",
287
- "additionalProperties": true
288
- }
289
- }
290
- }
291
- }
292
- }
293
- }
294
- }`
295
-
296
- // SwaggerInfo holds exported Swagger Info so clients can modify it
297
- var SwaggerInfo = &swag.Spec{
298
- Version: "",
299
- Host: "",
300
- BasePath: "",
301
- Schemes: []string{},
302
- Title: "",
303
- Description: "",
304
- InfoInstanceName: "swagger",
305
- SwaggerTemplate: docTemplate,
306
- LeftDelim: "{{",
307
- RightDelim: "}}",
308
- }
309
-
310
- func init() {
311
- swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
312
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/swagger.json DELETED
@@ -1,283 +0,0 @@
1
- {
2
- "swagger": "2.0",
3
- "info": {
4
- "contact": {}
5
- },
6
- "paths": {
7
- "/academy": {
8
- "get": {
9
- "security": [
10
- {
11
- "BearerAuth": []
12
- }
13
- ],
14
- "produces": [
15
- "application/json"
16
- ],
17
- "tags": [
18
- "Academy"
19
- ],
20
- "summary": "Academy example endpoint",
21
- "responses": {
22
- "200": {
23
- "description": "OK",
24
- "schema": {
25
- "type": "object",
26
- "additionalProperties": true
27
- }
28
- }
29
- }
30
- }
31
- },
32
- "/academy-exam": {
33
- "get": {
34
- "security": [
35
- {
36
- "BearerAuth": []
37
- }
38
- ],
39
- "produces": [
40
- "application/json"
41
- ],
42
- "tags": [
43
- "Academy_exam"
44
- ],
45
- "summary": "Academy_exam example endpoint",
46
- "responses": {
47
- "200": {
48
- "description": "OK",
49
- "schema": {
50
- "type": "object",
51
- "additionalProperties": true
52
- }
53
- }
54
- }
55
- }
56
- },
57
- "/account": {
58
- "get": {
59
- "security": [
60
- {
61
- "BearerAuth": []
62
- }
63
- ],
64
- "produces": [
65
- "application/json"
66
- ],
67
- "tags": [
68
- "Account"
69
- ],
70
- "summary": "Account example endpoint",
71
- "responses": {
72
- "200": {
73
- "description": "OK",
74
- "schema": {
75
- "type": "object",
76
- "additionalProperties": true
77
- }
78
- }
79
- }
80
- }
81
- },
82
- "/admin": {
83
- "get": {
84
- "security": [
85
- {
86
- "BearerAuth": []
87
- }
88
- ],
89
- "produces": [
90
- "application/json"
91
- ],
92
- "tags": [
93
- "Admin"
94
- ],
95
- "summary": "Admin example endpoint",
96
- "responses": {
97
- "200": {
98
- "description": "OK",
99
- "schema": {
100
- "type": "object",
101
- "additionalProperties": true
102
- }
103
- }
104
- }
105
- }
106
- },
107
- "/auth": {
108
- "get": {
109
- "security": [
110
- {
111
- "BearerAuth": []
112
- }
113
- ],
114
- "produces": [
115
- "application/json"
116
- ],
117
- "tags": [
118
- "Auth"
119
- ],
120
- "summary": "Auth example endpoint",
121
- "responses": {
122
- "200": {
123
- "description": "OK",
124
- "schema": {
125
- "type": "object",
126
- "additionalProperties": true
127
- }
128
- }
129
- }
130
- }
131
- },
132
- "/email": {
133
- "get": {
134
- "security": [
135
- {
136
- "BearerAuth": []
137
- }
138
- ],
139
- "produces": [
140
- "application/json"
141
- ],
142
- "tags": [
143
- "Email"
144
- ],
145
- "summary": "Email example endpoint",
146
- "responses": {
147
- "200": {
148
- "description": "OK",
149
- "schema": {
150
- "type": "object",
151
- "additionalProperties": true
152
- }
153
- }
154
- }
155
- }
156
- },
157
- "/event": {
158
- "get": {
159
- "security": [
160
- {
161
- "BearerAuth": []
162
- }
163
- ],
164
- "produces": [
165
- "application/json"
166
- ],
167
- "tags": [
168
- "Event"
169
- ],
170
- "summary": "Event example endpoint",
171
- "responses": {
172
- "200": {
173
- "description": "OK",
174
- "schema": {
175
- "type": "object",
176
- "additionalProperties": true
177
- }
178
- }
179
- }
180
- }
181
- },
182
- "/exam-event": {
183
- "get": {
184
- "security": [
185
- {
186
- "BearerAuth": []
187
- }
188
- ],
189
- "produces": [
190
- "application/json"
191
- ],
192
- "tags": [
193
- "Exam_event"
194
- ],
195
- "summary": "Exam_event example endpoint",
196
- "responses": {
197
- "200": {
198
- "description": "OK",
199
- "schema": {
200
- "type": "object",
201
- "additionalProperties": true
202
- }
203
- }
204
- }
205
- }
206
- },
207
- "/forgot-password": {
208
- "get": {
209
- "security": [
210
- {
211
- "BearerAuth": []
212
- }
213
- ],
214
- "produces": [
215
- "application/json"
216
- ],
217
- "tags": [
218
- "Forgot_password"
219
- ],
220
- "summary": "Forgot_password example endpoint",
221
- "responses": {
222
- "200": {
223
- "description": "OK",
224
- "schema": {
225
- "type": "object",
226
- "additionalProperties": true
227
- }
228
- }
229
- }
230
- }
231
- },
232
- "/option": {
233
- "get": {
234
- "security": [
235
- {
236
- "BearerAuth": []
237
- }
238
- ],
239
- "produces": [
240
- "application/json"
241
- ],
242
- "tags": [
243
- "Option"
244
- ],
245
- "summary": "Option example endpoint",
246
- "responses": {
247
- "200": {
248
- "description": "OK",
249
- "schema": {
250
- "type": "object",
251
- "additionalProperties": true
252
- }
253
- }
254
- }
255
- }
256
- },
257
- "/upload": {
258
- "get": {
259
- "security": [
260
- {
261
- "BearerAuth": []
262
- }
263
- ],
264
- "produces": [
265
- "application/json"
266
- ],
267
- "tags": [
268
- "Upload"
269
- ],
270
- "summary": "Upload example endpoint",
271
- "responses": {
272
- "200": {
273
- "description": "OK",
274
- "schema": {
275
- "type": "object",
276
- "additionalProperties": true
277
- }
278
- }
279
- }
280
- }
281
- }
282
- }
283
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docs/swagger.yaml DELETED
@@ -1,169 +0,0 @@
1
- info:
2
- contact: {}
3
- paths:
4
- /academy:
5
- get:
6
- produces:
7
- - application/json
8
- responses:
9
- "200":
10
- description: OK
11
- schema:
12
- additionalProperties: true
13
- type: object
14
- security:
15
- - BearerAuth: []
16
- summary: Academy example endpoint
17
- tags:
18
- - Academy
19
- /academy-exam:
20
- get:
21
- produces:
22
- - application/json
23
- responses:
24
- "200":
25
- description: OK
26
- schema:
27
- additionalProperties: true
28
- type: object
29
- security:
30
- - BearerAuth: []
31
- summary: Academy_exam example endpoint
32
- tags:
33
- - Academy_exam
34
- /account:
35
- get:
36
- produces:
37
- - application/json
38
- responses:
39
- "200":
40
- description: OK
41
- schema:
42
- additionalProperties: true
43
- type: object
44
- security:
45
- - BearerAuth: []
46
- summary: Account example endpoint
47
- tags:
48
- - Account
49
- /admin:
50
- get:
51
- produces:
52
- - application/json
53
- responses:
54
- "200":
55
- description: OK
56
- schema:
57
- additionalProperties: true
58
- type: object
59
- security:
60
- - BearerAuth: []
61
- summary: Admin example endpoint
62
- tags:
63
- - Admin
64
- /auth:
65
- get:
66
- produces:
67
- - application/json
68
- responses:
69
- "200":
70
- description: OK
71
- schema:
72
- additionalProperties: true
73
- type: object
74
- security:
75
- - BearerAuth: []
76
- summary: Auth example endpoint
77
- tags:
78
- - Auth
79
- /email:
80
- get:
81
- produces:
82
- - application/json
83
- responses:
84
- "200":
85
- description: OK
86
- schema:
87
- additionalProperties: true
88
- type: object
89
- security:
90
- - BearerAuth: []
91
- summary: Email example endpoint
92
- tags:
93
- - Email
94
- /event:
95
- get:
96
- produces:
97
- - application/json
98
- responses:
99
- "200":
100
- description: OK
101
- schema:
102
- additionalProperties: true
103
- type: object
104
- security:
105
- - BearerAuth: []
106
- summary: Event example endpoint
107
- tags:
108
- - Event
109
- /exam-event:
110
- get:
111
- produces:
112
- - application/json
113
- responses:
114
- "200":
115
- description: OK
116
- schema:
117
- additionalProperties: true
118
- type: object
119
- security:
120
- - BearerAuth: []
121
- summary: Exam_event example endpoint
122
- tags:
123
- - Exam_event
124
- /forgot-password:
125
- get:
126
- produces:
127
- - application/json
128
- responses:
129
- "200":
130
- description: OK
131
- schema:
132
- additionalProperties: true
133
- type: object
134
- security:
135
- - BearerAuth: []
136
- summary: Forgot_password example endpoint
137
- tags:
138
- - Forgot_password
139
- /option:
140
- get:
141
- produces:
142
- - application/json
143
- responses:
144
- "200":
145
- description: OK
146
- schema:
147
- additionalProperties: true
148
- type: object
149
- security:
150
- - BearerAuth: []
151
- summary: Option example endpoint
152
- tags:
153
- - Option
154
- /upload:
155
- get:
156
- produces:
157
- - application/json
158
- responses:
159
- "200":
160
- description: OK
161
- schema:
162
- additionalProperties: true
163
- type: object
164
- security:
165
- - BearerAuth: []
166
- summary: Upload example endpoint
167
- tags:
168
- - Upload
169
- swagger: "2.0"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
go.mod CHANGED
@@ -11,6 +11,10 @@ require (
11
  github.com/joho/godotenv v1.5.1
12
  github.com/lib/pq v1.1.1
13
  github.com/supabase-community/storage-go v0.8.1
 
 
 
 
14
  golang.org/x/crypto v0.46.0
15
  google.golang.org/api v0.253.0
16
  gorm.io/driver/postgres v1.6.0
@@ -22,13 +26,10 @@ require (
22
  cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
23
  cloud.google.com/go/compute/metadata v0.9.0 // indirect
24
  github.com/KyleBanks/depth v1.2.1 // indirect
25
- github.com/PuerkitoBio/purell v1.2.1 // indirect
26
- github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
27
  github.com/bytedance/gopkg v0.1.3 // indirect
28
  github.com/bytedance/sonic v1.14.2 // indirect
29
  github.com/bytedance/sonic/loader v0.4.0 // indirect
30
  github.com/cloudwego/base64x v0.1.6 // indirect
31
- github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
32
  github.com/felixge/httpsnoop v1.0.4 // indirect
33
  github.com/gabriel-vasile/mimetype v1.4.12 // indirect
34
  github.com/gin-contrib/sse v1.1.0 // indirect
@@ -37,7 +38,6 @@ require (
37
  github.com/go-openapi/jsonpointer v0.22.4 // indirect
38
  github.com/go-openapi/jsonreference v0.21.4 // indirect
39
  github.com/go-openapi/spec v0.22.2 // indirect
40
- github.com/go-openapi/swag v0.25.4 // indirect
41
  github.com/go-openapi/swag/conv v0.25.4 // indirect
42
  github.com/go-openapi/swag/jsonname v0.25.4 // indirect
43
  github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
@@ -60,33 +60,22 @@ require (
60
  github.com/jackc/puddle/v2 v2.2.2 // indirect
61
  github.com/jinzhu/inflection v1.0.0 // indirect
62
  github.com/jinzhu/now v1.1.5 // indirect
63
- github.com/josharian/intern v1.0.0 // indirect
64
  github.com/json-iterator/go v1.1.12 // indirect
65
  github.com/klauspost/cpuid/v2 v2.3.0 // indirect
66
  github.com/leodido/go-urn v1.4.0 // indirect
67
- github.com/mailru/easyjson v0.9.1 // indirect
68
  github.com/mattn/go-isatty v0.0.20 // indirect
69
  github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
70
  github.com/modern-go/reflect2 v1.0.2 // indirect
71
  github.com/pelletier/go-toml/v2 v2.2.4 // indirect
72
  github.com/quic-go/qpack v0.6.0 // indirect
73
  github.com/quic-go/quic-go v0.58.0 // indirect
74
- github.com/russross/blackfriday/v2 v2.1.0 // indirect
75
- github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
76
- github.com/swaggo/files v1.0.1 // indirect
77
- github.com/swaggo/gin-swagger v1.6.1 // indirect
78
- github.com/swaggo/swag v1.16.6 // indirect
79
  github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
80
  github.com/ugorji/go/codec v1.3.1 // indirect
81
- github.com/urfave/cli/v2 v2.27.7 // indirect
82
- github.com/xendit/xendit-go/v7 v7.0.0 // indirect
83
- github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect
84
  go.opentelemetry.io/auto/sdk v1.1.0 // indirect
85
  go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
86
  go.opentelemetry.io/otel v1.37.0 // indirect
87
  go.opentelemetry.io/otel/metric v1.37.0 // indirect
88
  go.opentelemetry.io/otel/trace v1.37.0 // indirect
89
- go.yaml.in/yaml/v2 v2.4.3 // indirect
90
  go.yaml.in/yaml/v3 v3.0.4 // indirect
91
  golang.org/x/arch v0.23.0 // indirect
92
  golang.org/x/mod v0.31.0 // indirect
@@ -99,6 +88,4 @@ require (
99
  google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f // indirect
100
  google.golang.org/grpc v1.76.0 // indirect
101
  google.golang.org/protobuf v1.36.11 // indirect
102
- gopkg.in/yaml.v2 v2.4.0 // indirect
103
- sigs.k8s.io/yaml v1.6.0 // indirect
104
  )
 
11
  github.com/joho/godotenv v1.5.1
12
  github.com/lib/pq v1.1.1
13
  github.com/supabase-community/storage-go v0.8.1
14
+ github.com/swaggo/files v1.0.1
15
+ github.com/swaggo/gin-swagger v1.6.1
16
+ github.com/swaggo/swag v1.16.6
17
+ github.com/xendit/xendit-go/v7 v7.0.0
18
  golang.org/x/crypto v0.46.0
19
  google.golang.org/api v0.253.0
20
  gorm.io/driver/postgres v1.6.0
 
26
  cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
27
  cloud.google.com/go/compute/metadata v0.9.0 // indirect
28
  github.com/KyleBanks/depth v1.2.1 // indirect
 
 
29
  github.com/bytedance/gopkg v0.1.3 // indirect
30
  github.com/bytedance/sonic v1.14.2 // indirect
31
  github.com/bytedance/sonic/loader v0.4.0 // indirect
32
  github.com/cloudwego/base64x v0.1.6 // indirect
 
33
  github.com/felixge/httpsnoop v1.0.4 // indirect
34
  github.com/gabriel-vasile/mimetype v1.4.12 // indirect
35
  github.com/gin-contrib/sse v1.1.0 // indirect
 
38
  github.com/go-openapi/jsonpointer v0.22.4 // indirect
39
  github.com/go-openapi/jsonreference v0.21.4 // indirect
40
  github.com/go-openapi/spec v0.22.2 // indirect
 
41
  github.com/go-openapi/swag/conv v0.25.4 // indirect
42
  github.com/go-openapi/swag/jsonname v0.25.4 // indirect
43
  github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
 
60
  github.com/jackc/puddle/v2 v2.2.2 // indirect
61
  github.com/jinzhu/inflection v1.0.0 // indirect
62
  github.com/jinzhu/now v1.1.5 // indirect
 
63
  github.com/json-iterator/go v1.1.12 // indirect
64
  github.com/klauspost/cpuid/v2 v2.3.0 // indirect
65
  github.com/leodido/go-urn v1.4.0 // indirect
 
66
  github.com/mattn/go-isatty v0.0.20 // indirect
67
  github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
68
  github.com/modern-go/reflect2 v1.0.2 // indirect
69
  github.com/pelletier/go-toml/v2 v2.2.4 // indirect
70
  github.com/quic-go/qpack v0.6.0 // indirect
71
  github.com/quic-go/quic-go v0.58.0 // indirect
 
 
 
 
 
72
  github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
73
  github.com/ugorji/go/codec v1.3.1 // indirect
 
 
 
74
  go.opentelemetry.io/auto/sdk v1.1.0 // indirect
75
  go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
76
  go.opentelemetry.io/otel v1.37.0 // indirect
77
  go.opentelemetry.io/otel/metric v1.37.0 // indirect
78
  go.opentelemetry.io/otel/trace v1.37.0 // indirect
 
79
  go.yaml.in/yaml/v3 v3.0.4 // indirect
80
  golang.org/x/arch v0.23.0 // indirect
81
  golang.org/x/mod v0.31.0 // indirect
 
88
  google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f // indirect
89
  google.golang.org/grpc v1.76.0 // indirect
90
  google.golang.org/protobuf v1.36.11 // indirect
 
 
91
  )
go.sum CHANGED
@@ -6,31 +6,19 @@ cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdB
6
  cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
7
  github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
8
  github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
9
- github.com/PuerkitoBio/purell v1.2.1 h1:QsZ4TjvwiMpat6gBCBxEQI0rcS9ehtkKtSpiUnd9N28=
10
- github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo=
11
- github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
12
- github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
13
  github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
14
  github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
15
- github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w=
16
- github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc=
17
  github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
18
  github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
19
- github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
20
- github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
21
  github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
22
  github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
23
  github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
24
  github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
25
- github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
26
- github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
27
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
28
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
29
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
30
  github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
31
  github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
32
- github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
33
- github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
34
  github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
35
  github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
36
  github.com/gin-contrib/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI=
@@ -50,14 +38,15 @@ github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmG
50
  github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
51
  github.com/go-openapi/spec v0.22.2 h1:KEU4Fb+Lp1qg0V4MxrSCPv403ZjBl8Lx1a83gIPU8Qc=
52
  github.com/go-openapi/spec v0.22.2/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
53
- github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
54
- github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
55
  github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
56
  github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
57
  github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
58
  github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
59
  github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
60
  github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
 
 
61
  github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
62
  github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
63
  github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
@@ -66,20 +55,20 @@ github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv4
66
  github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
67
  github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
68
  github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
 
 
 
 
69
  github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
70
  github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
71
  github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
72
  github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
73
  github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
74
  github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
75
- github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
76
- github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
77
  github.com/go-playground/validator/v10 v10.30.0 h1:5YBPNs273uzsZJD1I8uiB4Aqg9sN6sMDVX3s6LxmhWU=
78
  github.com/go-playground/validator/v10 v10.30.0/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
79
  github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
80
  github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
81
- github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
82
- github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
83
  github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
84
  github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
85
  github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
@@ -115,19 +104,18 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
115
  github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
116
  github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
117
  github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
118
- github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
119
- github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
120
  github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
121
  github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
122
  github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
123
  github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 
 
 
124
  github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
125
  github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
126
  github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
127
  github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
128
  github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
129
- github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
130
- github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
131
  github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
132
  github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
133
  github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -139,18 +127,12 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0
139
  github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
140
  github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
141
  github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
142
- github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
143
- github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
144
  github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
145
  github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
146
- github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
147
- github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
148
  github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug=
149
  github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
150
- github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
151
- github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
152
- github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
153
- github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
154
  github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
155
  github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
156
  github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -159,7 +141,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
159
  github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
160
  github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
161
  github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
162
- github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
163
  github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
164
  github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
165
  github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
@@ -174,16 +155,10 @@ github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
174
  github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
175
  github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
176
  github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
177
- github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
178
- github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
179
  github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
180
  github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
181
- github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
182
- github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
183
  github.com/xendit/xendit-go/v7 v7.0.0 h1:A7Nhaulk1a+mOI/KgRcvb5VSQEB6nhsUGkAhi+RkrEM=
184
  github.com/xendit/xendit-go/v7 v7.0.0/go.mod h1:W562aw0zhjzF/OUhZLc77q2iFQc9INa5tBy5xl6OLbo=
185
- github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg=
186
- github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
187
  github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
188
  go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
189
  go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
@@ -203,39 +178,27 @@ go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mx
203
  go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
204
  go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
205
  go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
206
- go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
207
- go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
208
  go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
209
  go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
210
- golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
211
- golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
212
  golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
213
  golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
214
  golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
215
  golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
216
- golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
217
- golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
218
  golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
219
  golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
220
  golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
221
- golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
222
- golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
223
  golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
224
  golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
225
  golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
226
  golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
227
  golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
228
  golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
229
- golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
230
- golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
231
  golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
232
  golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
233
  golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
234
  golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
235
  golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
236
  golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
237
- golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
238
- golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
239
  golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
240
  golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
241
  golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -245,8 +208,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
245
  golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
246
  golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
247
  golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
248
- golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
249
- golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
250
  golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
251
  golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
252
  golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -256,8 +217,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
256
  golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
257
  golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
258
  golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
259
- golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
260
- golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
261
  golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
262
  golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
263
  golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
@@ -265,8 +224,6 @@ golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
265
  golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
266
  golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
267
  golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
268
- golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
269
- golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
270
  golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
271
  golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
272
  golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -278,13 +235,11 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f h1:
278
  google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
279
  google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
280
  google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
281
- google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
282
- google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
283
  google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
284
  google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
285
  gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
286
- gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
287
- gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
288
  gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
289
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
290
  gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -292,5 +247,3 @@ gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
292
  gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
293
  gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
294
  gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
295
- sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
296
- sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
 
6
  cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
7
  github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
8
  github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
 
 
 
 
9
  github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
10
  github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
 
 
11
  github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
12
  github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
 
 
13
  github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
14
  github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
15
  github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
16
  github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
 
 
17
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
18
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
19
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
20
  github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
21
  github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 
 
22
  github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
23
  github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
24
  github.com/gin-contrib/gzip v1.2.5 h1:fIZs0S+l17pIu1P5XRJOo/YNqfIuPCrZZ3TWB7pjckI=
 
38
  github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
39
  github.com/go-openapi/spec v0.22.2 h1:KEU4Fb+Lp1qg0V4MxrSCPv403ZjBl8Lx1a83gIPU8Qc=
40
  github.com/go-openapi/spec v0.22.2/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
41
+ github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
 
42
  github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
43
  github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
44
  github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
45
  github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
46
  github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
47
  github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
48
+ github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
49
+ github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
50
  github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
51
  github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
52
  github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
 
55
  github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
56
  github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
57
  github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
58
+ github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
59
+ github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
60
+ github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
61
+ github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
62
  github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
63
  github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
64
  github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
65
  github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
66
  github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
67
  github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 
 
68
  github.com/go-playground/validator/v10 v10.30.0 h1:5YBPNs273uzsZJD1I8uiB4Aqg9sN6sMDVX3s6LxmhWU=
69
  github.com/go-playground/validator/v10 v10.30.0/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
70
  github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
71
  github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
 
 
72
  github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
73
  github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
74
  github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
 
104
  github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
105
  github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
106
  github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 
 
107
  github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
108
  github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
109
  github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
110
  github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
111
+ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
112
+ github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
113
+ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
114
  github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
115
  github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
116
  github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
117
  github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
118
  github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 
 
119
  github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
120
  github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
121
  github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 
127
  github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
128
  github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
129
  github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 
 
130
  github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
131
  github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
 
 
132
  github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug=
133
  github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
134
+ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
135
+ github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 
 
136
  github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
137
  github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
138
  github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 
141
  github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
142
  github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
143
  github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 
144
  github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
145
  github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
146
  github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
 
155
  github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
156
  github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
157
  github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
 
 
158
  github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
159
  github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
 
 
160
  github.com/xendit/xendit-go/v7 v7.0.0 h1:A7Nhaulk1a+mOI/KgRcvb5VSQEB6nhsUGkAhi+RkrEM=
161
  github.com/xendit/xendit-go/v7 v7.0.0/go.mod h1:W562aw0zhjzF/OUhZLc77q2iFQc9INa5tBy5xl6OLbo=
 
 
162
  github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
163
  go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
164
  go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 
178
  go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
179
  go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
180
  go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
 
 
181
  go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
182
  go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
 
 
183
  golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
184
  golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
185
  golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
186
  golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 
 
187
  golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
188
  golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
189
  golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 
 
190
  golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
191
  golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
192
  golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
193
  golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
194
  golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
195
  golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 
 
196
  golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
197
  golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
198
  golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
199
  golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
200
  golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
201
  golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 
 
202
  golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
203
  golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
204
  golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 
208
  golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
209
  golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
210
  golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 
 
211
  golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
212
  golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
213
  golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 
217
  golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
218
  golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
219
  golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 
 
220
  golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
221
  golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
222
  golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
 
224
  golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
225
  golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
226
  golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 
 
227
  golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
228
  golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
229
  golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 
235
  google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
236
  google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
237
  google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
 
 
238
  google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
239
  google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
240
  gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
241
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
242
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
243
  gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
244
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
245
  gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 
247
  gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
248
  gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY=
249
  gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
 
 
go.work.sum CHANGED
@@ -9,6 +9,8 @@ cloud.google.com/go/translate v1.10.3/go.mod h1:GW0vC1qvPtd3pgtypCv4k4U8B7EdgK9/
9
  github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
10
  github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4=
11
  github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw=
 
 
12
  github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
13
  github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
14
  github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@@ -16,6 +18,7 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
16
  github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
17
  github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
18
  github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
 
19
  github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
20
  github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
21
  github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
@@ -28,13 +31,11 @@ github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJn
28
  github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
29
  github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI=
30
  github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo=
 
31
  github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
32
  github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
33
- github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
34
  github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
35
  github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
36
- github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
37
- github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
38
  github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
39
  github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
40
  github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
@@ -43,9 +44,8 @@ github.com/google/go-pkcs11 v0.3.0 h1:PVRnTgtArZ3QQqTGtbtjtnIkzl2iY2kt24yqbrf7td
43
  github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
44
  github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
45
  github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e/go.mod h1:ZybsQk6DWyN5t7An1MuPm1gtSZ1xDaTXS9ZjIOxvQrk=
46
- github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
47
- github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
48
- github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
49
  github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
50
  github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
51
  github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
@@ -58,11 +58,12 @@ github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSz
58
  github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
59
  github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
60
  github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
61
- github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
62
- github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
63
  github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
64
  github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
65
  github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
 
66
  github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
67
  github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
68
  github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
@@ -84,10 +85,10 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b h1:
84
  google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
85
  google.golang.org/genproto/googleapis/bytestream v0.0.0-20251014184007-4626949a642f h1:T/BJL1nqPyWStq45hQyss5sEketltFJ/eWERyJ98U5M=
86
  google.golang.org/genproto/googleapis/bytestream v0.0.0-20251014184007-4626949a642f/go.mod h1:ejCb7yLmK6GCVHp5qpeKbm4KZew/ldg+9b8kq5MONgk=
87
- gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
88
- gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
89
  gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
90
  gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
91
  rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
92
  rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
93
  sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
 
 
9
  github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
10
  github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4=
11
  github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw=
12
+ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
13
+ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
14
  github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
15
  github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
16
  github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 
18
  github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
19
  github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
20
  github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
21
+ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
22
  github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
23
  github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
24
  github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
 
31
  github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
32
  github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI=
33
  github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo=
34
+ github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
35
  github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
36
  github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
 
37
  github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
38
  github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
 
 
39
  github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
40
  github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
41
  github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
 
44
  github.com/google/go-pkcs11 v0.3.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
45
  github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
46
  github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e/go.mod h1:ZybsQk6DWyN5t7An1MuPm1gtSZ1xDaTXS9ZjIOxvQrk=
47
+ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
48
+ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 
49
  github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
50
  github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
51
  github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
 
58
  github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
59
  github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
60
  github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
61
+ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
62
+ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
63
  github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
64
  github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
65
  github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
66
+ github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
67
  github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
68
  github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
69
  github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
 
85
  google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
86
  google.golang.org/genproto/googleapis/bytestream v0.0.0-20251014184007-4626949a642f h1:T/BJL1nqPyWStq45hQyss5sEketltFJ/eWERyJ98U5M=
87
  google.golang.org/genproto/googleapis/bytestream v0.0.0-20251014184007-4626949a642f/go.mod h1:ejCb7yLmK6GCVHp5qpeKbm4KZew/ldg+9b8kq5MONgk=
88
+ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 
89
  gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
90
  gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
91
  rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
92
  rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
93
  sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
94
+ sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
injector.sh DELETED
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- ROOT_DIR="$(pwd)"
5
- OUT_DIR="${ROOT_DIR}/provider"
6
- OUT_FILE="${OUT_DIR}/generated_providers.go"
7
- SEARCH_DIRS=(config middleware controller services repository)
8
-
9
- TMP_IMPORTS="$(mktemp)"
10
- TMP_CONSTRUCTORS="$(mktemp)"
11
-
12
- cleanup() { rm -f "$TMP_IMPORTS" "$TMP_CONSTRUCTORS"; }
13
- trap cleanup EXIT
14
-
15
- # module name from go.mod
16
- MODULE="$(sed -n 's/^module //p' go.mod | tr -d '\r' || true)"
17
-
18
- mkdir -p "$OUT_DIR"
19
-
20
- echo "// Code generated; DO NOT EDIT." > "$OUT_FILE"
21
- echo "// $(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$OUT_FILE"
22
- echo >> "$OUT_FILE"
23
- echo "package provider" >> "$OUT_FILE"
24
- echo >> "$OUT_FILE"
25
-
26
- echo "import (" > "$TMP_IMPORTS"
27
- echo " \"fmt\"" >> "$TMP_IMPORTS"
28
- echo >> "$TMP_IMPORTS"
29
-
30
- found_any=0
31
-
32
- for d in "${SEARCH_DIRS[@]}"; do
33
- if [ ! -d "${ROOT_DIR}/${d}" ]; then continue; fi
34
-
35
- pkgname=$(grep -h '^package ' "${ROOT_DIR}/${d}"/*.go 2>/dev/null | head -n1 | awk '{print $2}')
36
- [ -z "$pkgname" ] && pkgname="${d}"
37
-
38
- alias="${pkgname}_${d}"
39
- [ -n "$MODULE" ] && import_path="${MODULE}/${d}" || import_path="./${d}"
40
-
41
- for file in $(find "${ROOT_DIR}/${d}" -maxdepth 1 -name '*.go'); do
42
- grep -E "^func[[:space:]]+New" "$file" | while read -r line; do
43
- fname=$(echo "$line" | sed -n 's/^func[[:space:]]\+\(New[A-Za-z0-9_]*\).*/\1/p')
44
- [ -z "$fname" ] && continue
45
- params=$(echo "$line" | sed -n 's/^[^(]*(\([^)]*\)).*$/\1/p' | tr -d '[:space:]')
46
-
47
- echo "${d}|${pkgname}|${alias}|${import_path}|${fname}|${params}|${file}" >> "$TMP_CONSTRUCTORS"
48
- found_any=1
49
- done
50
- done
51
- done
52
-
53
- if [ "$found_any" -eq 1 ]; then
54
- awk -F'|' '{print $3" \""$4"\""}' "$TMP_CONSTRUCTORS" | sort -u | while read -r imp; do
55
- echo " ${imp}" >> "$TMP_IMPORTS"
56
- done
57
- fi
58
-
59
- echo ")" >> "$TMP_IMPORTS"
60
- cat "$TMP_IMPORTS" >> "$OUT_FILE"
61
- echo >> "$OUT_FILE"
62
-
63
- cat >> "$OUT_FILE" <<EOF
64
- func ProvideAll() map[string]interface{} {
65
- out := make(map[string]interface{})
66
- EOF
67
-
68
- if [ "$found_any" -eq 1 ]; then
69
- awk -F'|' '{print $2 "|" $3 "|" $5 "|" $6 "|" $7}' "$TMP_CONSTRUCTORS" |
70
- while IFS='|' read -r pkg alias fname params file; do
71
- key="${pkg}.${fname}"
72
- call="${alias}.${fname}"
73
- if [ -z "$params" ]; then
74
- echo " // $file" >> "$OUT_FILE"
75
- echo " __${fname} := ${call}()" >> "$OUT_FILE"
76
- echo " out[\"${key}\"] = __${fname}" >> "$OUT_FILE"
77
- echo >> "$OUT_FILE"
78
- else
79
- echo " // TODO: ${call}(${params}) requires params" >> "$OUT_FILE"
80
- echo " // out[\"${key}\"] = ${call}(...)" >> "$OUT_FILE"
81
- echo >> "$OUT_FILE"
82
- fi
83
- done
84
- else
85
- echo " // No constructors found" >> "$OUT_FILE"
86
- fi
87
-
88
- echo " return out" >> "$OUT_FILE"
89
- echo "}" >> "$OUT_FILE"
90
-
91
- command -v gofmt >/dev/null && gofmt -w "$OUT_FILE"
92
- echo "✅ Generated at ${OUT_FILE}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
models/dto/event_dto.go CHANGED
@@ -2,6 +2,7 @@ package dto
2
 
3
  import (
4
  entity "abdanhafidz.com/go-boilerplate/models/entity"
 
5
  )
6
 
7
  type EventDetailResponse struct {
@@ -38,3 +39,12 @@ type UpdateEventRequest struct {
38
  ImgBanner string `json:"img_banner"`
39
  IsPublic *bool `json:"is_public"`
40
  }
 
 
 
 
 
 
 
 
 
 
2
 
3
  import (
4
  entity "abdanhafidz.com/go-boilerplate/models/entity"
5
+ "github.com/google/uuid"
6
  )
7
 
8
  type EventDetailResponse struct {
 
39
  ImgBanner string `json:"img_banner"`
40
  IsPublic *bool `json:"is_public"`
41
  }
42
+
43
+ type EventExamProctoringLogsRequest struct {
44
+ EventId uuid.UUID `json:"id_event,omitempty" form:"id_event"`
45
+ ExamId uuid.UUID `json:"id_exam,omitempty" form:"id_exam"`
46
+ AccountId uuid.UUID `json:"id_account,omitempty" form:"id_account"`
47
+ ViolationScore uint `json:"violation_score,omitempty" form:"violation_score"`
48
+ ViolationCategory string `json:"violation_category,omitempty" form:"violation_category"`
49
+ Attachement string `json:"attachement,omitempty" form:"attachement"`
50
+ }
models/dto/exam_dto.go CHANGED
@@ -1,6 +1,8 @@
1
  package dto
2
 
3
  import (
 
 
4
  entity "abdanhafidz.com/go-boilerplate/models/entity"
5
  "github.com/google/uuid"
6
  )
@@ -13,11 +15,29 @@ type UserExamStatus struct {
13
  }
14
 
15
  type AnswerWithQuestion struct {
16
- Answer entity.ExamEventAnswer `json:"answer"`
17
  Question entity.Questions `json:"question"`
18
  }
19
 
20
- type AnswerExamEventRequest struct {
21
  QuestionId uuid.UUID `json:"question_id" binding:"required"`
22
  Answer []string `json:"answer"`
23
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  package dto
2
 
3
  import (
4
+ "time"
5
+
6
  entity "abdanhafidz.com/go-boilerplate/models/entity"
7
  "github.com/google/uuid"
8
  )
 
15
  }
16
 
17
  type AnswerWithQuestion struct {
18
+ Answer entity.EventExamAnswer `json:"answer"`
19
  Question entity.Questions `json:"question"`
20
  }
21
 
22
+ type AnswerEventExamRequest struct {
23
  QuestionId uuid.UUID `json:"question_id" binding:"required"`
24
  Answer []string `json:"answer"`
25
  }
26
+
27
+ type CreateExamRequest struct {
28
+ Slug string `json:"slug,omitempty"`
29
+ Title string `json:"title,omitempty"`
30
+ Description string `json:"description,omitempty"`
31
+ Duration time.Duration `json:"duration,omitempty"`
32
+ Randomize uint `json:"randomize,omitempty"`
33
+ AllowRetake bool `json:"allow_retake,omitempty"`
34
+ AllowReview bool `json:"allow_review,omitempty"`
35
+ EnableTimer bool `json:"enable_timer,omitempty"`
36
+ EnableWebCam bool `json:"enable_webcam,omitempty"`
37
+ EnableVAD bool `json:"enable_vad,omitempty"`
38
+ EnableTabBlock bool `json:"enable_tab_block,omitempty"`
39
+ RequiredFullScreen bool `json:"enable_full_screen,omitempty"`
40
+ EnableEyeTracking bool `json:"enable_eye_tracking,omitempty"`
41
+ DisableCopyPaste bool `json:"disable_copy_paste,omitempty"`
42
+ EnableExamBrowser bool `json:"enable_exam_browser,omitempty"`
43
+ }
models/entity/entity.go CHANGED
@@ -106,9 +106,11 @@ type Announcement struct {
106
  func (Announcement) TableName() string { return "announcement" }
107
 
108
  type ProblemSet struct {
109
- Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_problem_set"`
110
- Title string `json:"title,omitempty"`
111
- Description string `json:"description,omitempty"`
 
 
112
  }
113
 
114
  func (ProblemSet) TableName() string { return "problem_set" }
@@ -120,8 +122,10 @@ type Exam struct {
120
  Description string `json:"description,omitempty"`
121
  Duration time.Duration `json:"duration,omitempty"`
122
  Randomize uint `json:"randomize,omitempty"`
123
- Configuration ExamConfiguration `gorm:"foreignKey:Id;references:Id" json:"configuration,omitempty"`
124
- Proctoring ExamProctoring `gorm:"foreignKey:Id;references:Id" json:"proctoring,omitempty"`
 
 
125
  }
126
 
127
  func (Exam) TableName() string { return "exam" }
@@ -150,6 +154,19 @@ type ExamProctoring struct {
150
 
151
  func (ExamProctoring) TableName() string { return "exam_proctoring" }
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  type OptionCategory struct {
154
  Id uint `gorm:"primaryKey" json:"id"`
155
  OptionName string `json:"option_name,omitempty"`
@@ -230,7 +247,7 @@ type ProblemSetExamAssign struct {
230
 
231
  func (ProblemSetExamAssign) TableName() string { return "problem_set_exam_assign" }
232
 
233
- type ExamEventAssign struct {
234
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_exam_event_assign"`
235
  EventId uuid.UUID `json:"id_event,omitempty"`
236
  ExamId uuid.UUID `json:"id_exam,omitempty"`
@@ -238,7 +255,7 @@ type ExamEventAssign struct {
238
  Event *Events `gorm:"foreignKey:EventId" json:"event,omitempty"`
239
  }
240
 
241
- func (ExamEventAssign) TableName() string { return "exam_event_assign" }
242
 
243
  type CPQuestionVerdict struct {
244
  TimeExecution float32 `json:"time_exec"`
@@ -247,26 +264,26 @@ type CPQuestionVerdict struct {
247
  Score float32 `json:"score"`
248
  }
249
 
250
- type ExamEventAnswer struct {
251
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
252
  AttemptId uuid.UUID `json:"id_attempt,omitempty" gorm:"index"`
253
  QuestionId uuid.UUID `json:"id_question,omitempty" gorm:"index"`
254
  Answers pq.StringArray `gorm:"type:text[]" json:"answer,omitempty"`
255
  Score float32 `json:"score"`
256
- ExamEventAttempt *ExamEventAttempt `gorm:"foreignKey:AttemptId" json:"exam_attempt,omitempty"`
257
  CreatedAt time.Time `json:"created_at,omitempty"`
258
  UpdatedAt time.Time `json:"updated_at,omitempty"`
259
  }
260
 
261
- func (ExamEventAnswer) TableName() string { return "exam_event_answer" }
262
 
263
- type ExamEventAttempt struct {
264
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_attempt"`
265
  AccountId uuid.UUID `json:"id_account,omitempty"`
266
  EventId uuid.UUID `json:"id_event,omitempty"`
267
  ExamId uuid.UUID `json:"id_exam,omitempty"`
268
  Questions []Questions `gorm:"-" json:"questions,omitempty"`
269
- Answers []ExamEventAnswer `gorm:"foreignKey:AttemptId;references:Id" json:"answers,omitempty"`
270
  Account *Account `gorm:"foreignKey:AccountId" json:"account,omitempty"`
271
  Event *Events `gorm:"foreignKey:EventId" json:"event,omitempty"`
272
  Exam *Exam `gorm:"foreignKey:ExamId" json:"exam,omitempty"`
@@ -277,13 +294,13 @@ type ExamEventAttempt struct {
277
  Submitted bool `json:"submitted,omitempty"`
278
  }
279
 
280
- func (ExamEventAttempt) TableName() string { return "exam_event_attempt" }
281
 
282
  type Result struct {
283
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_result"`
284
  AttemptId uuid.UUID `json:"id_attempt,omitempty"`
285
  FinalScore float32 `json:"final_score"`
286
- ExamEventAttempt *ExamEventAttempt `gorm:"foreignKey:AttemptId" json:"exam_attempt,omitempty"`
287
  }
288
 
289
  func (Result) TableName() string { return "result" }
@@ -401,7 +418,7 @@ type File struct {
401
 
402
  func (File) TableName() string { return "files" }
403
 
404
- type ExamAcademyAssign struct {
405
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_exam_academy_assign"`
406
  AcademyId uuid.UUID `json:"id_academy,omitempty"`
407
  ExamId uuid.UUID `json:"id_exam,omitempty"`
@@ -409,28 +426,28 @@ type ExamAcademyAssign struct {
409
  Academy *Academy `gorm:"foreignKey:AcademyId" json:"academy,omitempty"`
410
  }
411
 
412
- func (ExamAcademyAssign) TableName() string { return "exam_academy_assign" }
413
 
414
- type ExamAcademyAnswer struct {
415
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
416
  AttemptId uuid.UUID `json:"id_attempt,omitempty" gorm:"index"`
417
  QuestionId uuid.UUID `json:"id_question,omitempty" gorm:"index"`
418
  Answers pq.StringArray `gorm:"type:text[]" json:"answer,omitempty"`
419
  Score float32 `json:"score"`
420
- ExamAcademyAttempt *ExamAcademyAttempt `gorm:"foreignKey:AttemptId" json:"exam_attempt,omitempty"`
421
  CreatedAt time.Time `json:"created_at,omitempty"`
422
  UpdatedAt time.Time `json:"updated_at,omitempty"`
423
  }
424
 
425
- func (ExamAcademyAnswer) TableName() string { return "exam_academy_answer" }
426
 
427
- type ExamAcademyAttempt struct {
428
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_attempt"`
429
  AccountId uuid.UUID `json:"id_account,omitempty"`
430
  AcademyId uuid.UUID `json:"id_academy,omitempty"`
431
  ExamId uuid.UUID `json:"id_exam,omitempty"`
432
  Questions []Questions `gorm:"-" json:"questions,omitempty"`
433
- Answers []ExamAcademyAnswer `gorm:"foreignKey:AttemptId;references:Id" json:"answers,omitempty"`
434
  Account *Account `gorm:"foreignKey:AccountId" json:"account,omitempty"`
435
  Academy *Academy `gorm:"foreignKey:AcademyId" json:"academy,omitempty"`
436
  Exam *Exam `gorm:"foreignKey:ExamId" json:"exam,omitempty"`
@@ -441,16 +458,16 @@ type ExamAcademyAttempt struct {
441
  Submitted bool `json:"submitted,omitempty"`
442
  }
443
 
444
- func (ExamAcademyAttempt) TableName() string { return "exam_academy_attempt" }
445
 
446
- type ExamAcademyResult struct {
447
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_result"`
448
  AttemptId uuid.UUID `json:"id_attempt,omitempty"`
449
  FinalScore float32 `json:"final_score"`
450
- ExamAcademyAttempt *ExamAcademyAttempt `gorm:"foreignKey:AttemptId" json:"exam_attempt,omitempty"`
451
  }
452
 
453
- func (ExamAcademyResult) TableName() string { return "academy_exam_result" }
454
 
455
  type AcademyPaymentTransaction struct {
456
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
 
106
  func (Announcement) TableName() string { return "announcement" }
107
 
108
  type ProblemSet struct {
109
+ Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_problem_set"`
110
+ Title string `json:"title,omitempty"`
111
+ Description string `json:"description,omitempty"`
112
+ CreatedAt time.Time `json:"created_at,omitempty"`
113
+ DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty" gorm:"index"`
114
  }
115
 
116
  func (ProblemSet) TableName() string { return "problem_set" }
 
122
  Description string `json:"description,omitempty"`
123
  Duration time.Duration `json:"duration,omitempty"`
124
  Randomize uint `json:"randomize,omitempty"`
125
+ Configuration ExamConfiguration `gorm:"foreignKey:ExamId;references:Id" json:"configuration,omitempty"`
126
+ Proctoring ExamProctoring `gorm:"foreignKey:ExamId;references:Id" json:"proctoring,omitempty"`
127
+ CreatedAt time.Time `json:"created_at,omitempty"`
128
+ DeletedAt gorm.DeletedAt `json:"deleted_at,omitempty" gorm:"index"`
129
  }
130
 
131
  func (Exam) TableName() string { return "exam" }
 
154
 
155
  func (ExamProctoring) TableName() string { return "exam_proctoring" }
156
 
157
+ type EventExamProctoringLogs struct {
158
+ Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_result"`
159
+ EventId uuid.UUID `json:"id_event,omitempty"`
160
+ ExamId uuid.UUID `json:"id_exam,omitempty"`
161
+ AccountId uuid.UUID `json:"id_account,omitempty"`
162
+ ViolationScore uint `json:"violation_score,omitempty"`
163
+ ViolationCategory string `json:"violation_category,omitempty"`
164
+ Attachement string `json:"attachement,omitempty"`
165
+ CreatedAt time.Time `json:"created_at,omitempty"`
166
+ }
167
+
168
+ func (EventExamProctoringLogs) TableName() string { return "exam_event_proctoring_logs" }
169
+
170
  type OptionCategory struct {
171
  Id uint `gorm:"primaryKey" json:"id"`
172
  OptionName string `json:"option_name,omitempty"`
 
247
 
248
  func (ProblemSetExamAssign) TableName() string { return "problem_set_exam_assign" }
249
 
250
+ type EventExamAssign struct {
251
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_exam_event_assign"`
252
  EventId uuid.UUID `json:"id_event,omitempty"`
253
  ExamId uuid.UUID `json:"id_exam,omitempty"`
 
255
  Event *Events `gorm:"foreignKey:EventId" json:"event,omitempty"`
256
  }
257
 
258
+ func (EventExamAssign) TableName() string { return "exam_event_assign" }
259
 
260
  type CPQuestionVerdict struct {
261
  TimeExecution float32 `json:"time_exec"`
 
264
  Score float32 `json:"score"`
265
  }
266
 
267
+ type EventExamAnswer struct {
268
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
269
  AttemptId uuid.UUID `json:"id_attempt,omitempty" gorm:"index"`
270
  QuestionId uuid.UUID `json:"id_question,omitempty" gorm:"index"`
271
  Answers pq.StringArray `gorm:"type:text[]" json:"answer,omitempty"`
272
  Score float32 `json:"score"`
273
+ EventExamAttempt *EventExamAttempt `gorm:"foreignKey:AttemptId" json:"exam_attempt,omitempty"`
274
  CreatedAt time.Time `json:"created_at,omitempty"`
275
  UpdatedAt time.Time `json:"updated_at,omitempty"`
276
  }
277
 
278
+ func (EventExamAnswer) TableName() string { return "exam_event_answer" }
279
 
280
+ type EventExamAttempt struct {
281
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_attempt"`
282
  AccountId uuid.UUID `json:"id_account,omitempty"`
283
  EventId uuid.UUID `json:"id_event,omitempty"`
284
  ExamId uuid.UUID `json:"id_exam,omitempty"`
285
  Questions []Questions `gorm:"-" json:"questions,omitempty"`
286
+ Answers []EventExamAnswer `gorm:"foreignKey:AttemptId;references:Id" json:"answers,omitempty"`
287
  Account *Account `gorm:"foreignKey:AccountId" json:"account,omitempty"`
288
  Event *Events `gorm:"foreignKey:EventId" json:"event,omitempty"`
289
  Exam *Exam `gorm:"foreignKey:ExamId" json:"exam,omitempty"`
 
294
  Submitted bool `json:"submitted,omitempty"`
295
  }
296
 
297
+ func (EventExamAttempt) TableName() string { return "exam_event_attempt" }
298
 
299
  type Result struct {
300
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_result"`
301
  AttemptId uuid.UUID `json:"id_attempt,omitempty"`
302
  FinalScore float32 `json:"final_score"`
303
+ EventExamAttempt *EventExamAttempt `gorm:"foreignKey:AttemptId" json:"exam_attempt,omitempty"`
304
  }
305
 
306
  func (Result) TableName() string { return "result" }
 
418
 
419
  func (File) TableName() string { return "files" }
420
 
421
+ type AcademyExamAssign struct {
422
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_exam_academy_assign"`
423
  AcademyId uuid.UUID `json:"id_academy,omitempty"`
424
  ExamId uuid.UUID `json:"id_exam,omitempty"`
 
426
  Academy *Academy `gorm:"foreignKey:AcademyId" json:"academy,omitempty"`
427
  }
428
 
429
+ func (AcademyExamAssign) TableName() string { return "exam_academy_assign" }
430
 
431
+ type AcademyExamAnswer struct {
432
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
433
  AttemptId uuid.UUID `json:"id_attempt,omitempty" gorm:"index"`
434
  QuestionId uuid.UUID `json:"id_question,omitempty" gorm:"index"`
435
  Answers pq.StringArray `gorm:"type:text[]" json:"answer,omitempty"`
436
  Score float32 `json:"score"`
437
+ AcademyExamAttempt *AcademyExamAttempt `gorm:"foreignKey:AttemptId" json:"exam_attempt,omitempty"`
438
  CreatedAt time.Time `json:"created_at,omitempty"`
439
  UpdatedAt time.Time `json:"updated_at,omitempty"`
440
  }
441
 
442
+ func (AcademyExamAnswer) TableName() string { return "exam_academy_answer" }
443
 
444
+ type AcademyExamAttempt struct {
445
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_attempt"`
446
  AccountId uuid.UUID `json:"id_account,omitempty"`
447
  AcademyId uuid.UUID `json:"id_academy,omitempty"`
448
  ExamId uuid.UUID `json:"id_exam,omitempty"`
449
  Questions []Questions `gorm:"-" json:"questions,omitempty"`
450
+ Answers []AcademyExamAnswer `gorm:"foreignKey:AttemptId;references:Id" json:"answers,omitempty"`
451
  Account *Account `gorm:"foreignKey:AccountId" json:"account,omitempty"`
452
  Academy *Academy `gorm:"foreignKey:AcademyId" json:"academy,omitempty"`
453
  Exam *Exam `gorm:"foreignKey:ExamId" json:"exam,omitempty"`
 
458
  Submitted bool `json:"submitted,omitempty"`
459
  }
460
 
461
+ func (AcademyExamAttempt) TableName() string { return "exam_academy_attempt" }
462
 
463
+ type AcademyExamResult struct {
464
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id_result"`
465
  AttemptId uuid.UUID `json:"id_attempt,omitempty"`
466
  FinalScore float32 `json:"final_score"`
467
+ AcademyExamAttempt *AcademyExamAttempt `gorm:"foreignKey:AttemptId" json:"exam_attempt,omitempty"`
468
  }
469
 
470
+ func (AcademyExamResult) TableName() string { return "academy_exam_result" }
471
 
472
  type AcademyPaymentTransaction struct {
473
  Id uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
provider/controller_provider.go CHANGED
@@ -9,6 +9,8 @@ type ControllerProvider interface {
9
  ProvideAuthenticationController() controllers.AuthenticationController
10
  ProvideEmailVerificationController() controllers.EmailVerificationController
11
  ProvideEventController() controllers.EventController
 
 
12
  ProvideExamController() controllers.ExamController
13
  ProvideForgotPasswordController() controllers.ForgotPasswordController
14
  ProvideOptionController() controllers.OptionController
@@ -17,17 +19,19 @@ type ControllerProvider interface {
17
  }
18
 
19
  type controllerProvider struct {
20
- academyController controllers.AcademyController
21
- academyExamController controllers.AcademyExamController
22
- accountDetailController controllers.AccountDetailController
23
- authenticationController controllers.AuthenticationController
24
- emailVerificationController controllers.EmailVerificationController
25
- eventController controllers.EventController
26
- examController controllers.ExamController
27
- forgotPasswordController controllers.ForgotPasswordController
28
- optionController controllers.OptionController
29
- regionController controllers.RegionController
30
- uploadController controllers.UploadController
 
 
31
  }
32
 
33
  func NewControllerProvider(servicesProvider ServicesProvider) ControllerProvider {
@@ -38,23 +42,27 @@ func NewControllerProvider(servicesProvider ServicesProvider) ControllerProvider
38
  authenticationController := controllers.NewAuthenticationController(servicesProvider.ProvideAccountService(), servicesProvider.ProvideExternalAuthService())
39
  emailVerificationController := controllers.NewEmailVerificationController(servicesProvider.ProvideEmailVerificationService())
40
  eventController := controllers.NewEventController(servicesProvider.ProvideEventService())
 
 
41
  examController := controllers.NewExamController(servicesProvider.ProvideExamService())
42
  forgotPasswordController := controllers.NewForgotPasswordController(servicesProvider.ProvideForgotPasswordService())
43
  optionController := controllers.NewOptionController(servicesProvider.ProvideOptionService())
44
  regionController := controllers.NewRegionController(servicesProvider.ProvideRegionService())
45
  uploadController := controllers.NewUploadController(servicesProvider.ProvideUploadService())
46
  return &controllerProvider{
47
- academyController: academyController,
48
- academyExamController: academyExamController,
49
- accountDetailController: accountDetailController,
50
- authenticationController: authenticationController,
51
- emailVerificationController: emailVerificationController,
52
- eventController: eventController,
53
- examController: examController,
54
- forgotPasswordController: forgotPasswordController,
55
- optionController: optionController,
56
- regionController: regionController,
57
- uploadController: uploadController,
 
 
58
  }
59
  }
60
 
@@ -84,6 +92,14 @@ func (c *controllerProvider) ProvideEventController() controllers.EventControlle
84
  return c.eventController
85
  }
86
 
 
 
 
 
 
 
 
 
87
  func (c *controllerProvider) ProvideExamController() controllers.ExamController {
88
  return c.examController
89
  }
@@ -103,4 +119,3 @@ func (c *controllerProvider) ProvideRegionController() controllers.RegionControl
103
  func (c *controllerProvider) ProvideUploadController() controllers.UploadController {
104
  return c.uploadController
105
  }
106
-
 
9
  ProvideAuthenticationController() controllers.AuthenticationController
10
  ProvideEmailVerificationController() controllers.EmailVerificationController
11
  ProvideEventController() controllers.EventController
12
+ ProvideEventExamController() controllers.EventExamController
13
+ ProvideEventExamProctoringController() controllers.EventExamProctoringController
14
  ProvideExamController() controllers.ExamController
15
  ProvideForgotPasswordController() controllers.ForgotPasswordController
16
  ProvideOptionController() controllers.OptionController
 
19
  }
20
 
21
  type controllerProvider struct {
22
+ academyController controllers.AcademyController
23
+ academyExamController controllers.AcademyExamController
24
+ accountDetailController controllers.AccountDetailController
25
+ authenticationController controllers.AuthenticationController
26
+ emailVerificationController controllers.EmailVerificationController
27
+ eventController controllers.EventController
28
+ eventExamController controllers.EventExamController
29
+ eventExamProctoringController controllers.EventExamProctoringController
30
+ examController controllers.ExamController
31
+ forgotPasswordController controllers.ForgotPasswordController
32
+ optionController controllers.OptionController
33
+ regionController controllers.RegionController
34
+ uploadController controllers.UploadController
35
  }
36
 
37
  func NewControllerProvider(servicesProvider ServicesProvider) ControllerProvider {
 
42
  authenticationController := controllers.NewAuthenticationController(servicesProvider.ProvideAccountService(), servicesProvider.ProvideExternalAuthService())
43
  emailVerificationController := controllers.NewEmailVerificationController(servicesProvider.ProvideEmailVerificationService())
44
  eventController := controllers.NewEventController(servicesProvider.ProvideEventService())
45
+ eventExamController := controllers.NewEventExamController(servicesProvider.ProvideEventExamService())
46
+ eventExamProctoringController := controllers.NewEventExamProctoringController(servicesProvider.ProvideEventExamProctoringService())
47
  examController := controllers.NewExamController(servicesProvider.ProvideExamService())
48
  forgotPasswordController := controllers.NewForgotPasswordController(servicesProvider.ProvideForgotPasswordService())
49
  optionController := controllers.NewOptionController(servicesProvider.ProvideOptionService())
50
  regionController := controllers.NewRegionController(servicesProvider.ProvideRegionService())
51
  uploadController := controllers.NewUploadController(servicesProvider.ProvideUploadService())
52
  return &controllerProvider{
53
+ academyController: academyController,
54
+ academyExamController: academyExamController,
55
+ accountDetailController: accountDetailController,
56
+ authenticationController: authenticationController,
57
+ emailVerificationController: emailVerificationController,
58
+ eventController: eventController,
59
+ eventExamController: eventExamController,
60
+ eventExamProctoringController: eventExamProctoringController,
61
+ examController: examController,
62
+ forgotPasswordController: forgotPasswordController,
63
+ optionController: optionController,
64
+ regionController: regionController,
65
+ uploadController: uploadController,
66
  }
67
  }
68
 
 
92
  return c.eventController
93
  }
94
 
95
+ func (c *controllerProvider) ProvideEventExamController() controllers.EventExamController {
96
+ return c.eventExamController
97
+ }
98
+
99
+ func (c *controllerProvider) ProvideEventExamProctoringController() controllers.EventExamProctoringController {
100
+ return c.eventExamProctoringController
101
+ }
102
+
103
  func (c *controllerProvider) ProvideExamController() controllers.ExamController {
104
  return c.examController
105
  }
 
119
  func (c *controllerProvider) ProvideUploadController() controllers.UploadController {
120
  return c.uploadController
121
  }
 
provider/provider.go CHANGED
@@ -72,12 +72,13 @@ func NewAppProvider() AppProvider {
72
  &entity.Questions{},
73
  &entity.Exam{},
74
  &entity.ProblemSetExamAssign{},
75
- &entity.ExamEventAssign{},
76
 
77
  // Exam Attempt & Result
78
- &entity.ExamEventAnswer{},
79
- &entity.ExamEventAttempt{},
80
  &entity.Result{},
 
81
 
82
  // Academy LMS
83
  &entity.Academy{},
@@ -86,10 +87,10 @@ func NewAppProvider() AppProvider {
86
  &entity.AcademyMaterialProgress{},
87
  &entity.AcademyContentProgress{},
88
  &entity.AcademyProgress{},
89
- &entity.ExamAcademyAssign{},
90
- &entity.ExamAcademyAnswer{},
91
- &entity.ExamAcademyAttempt{},
92
- &entity.ExamAcademyResult{},
93
  &entity.AcademyAssign{},
94
 
95
  // Options & Regions
 
72
  &entity.Questions{},
73
  &entity.Exam{},
74
  &entity.ProblemSetExamAssign{},
75
+ &entity.EventExamAssign{},
76
 
77
  // Exam Attempt & Result
78
+ &entity.EventExamAnswer{},
79
+ &entity.EventExamAttempt{},
80
  &entity.Result{},
81
+ &entity.EventExamProctoringLogs{},
82
 
83
  // Academy LMS
84
  &entity.Academy{},
 
87
  &entity.AcademyMaterialProgress{},
88
  &entity.AcademyContentProgress{},
89
  &entity.AcademyProgress{},
90
+ &entity.AcademyExamAssign{},
91
+ &entity.AcademyExamAnswer{},
92
+ &entity.AcademyExamAttempt{},
93
+ &entity.AcademyExamResult{},
94
  &entity.AcademyAssign{},
95
 
96
  // Options & Regions
provider/repositories_provider.go CHANGED
@@ -12,12 +12,13 @@ type RepositoriesProvider interface {
12
  ProvideEventAssignRepository() repositories.EventAssignRepository
13
  ProvideEventPaymentRepository() repositories.EventPaymentRepository
14
  ProvideEventsRepository() repositories.EventsRepository
15
- ProvideExamAcademyAnswerRepository() repositories.ExamAcademyAnswerRepository
16
- ProvideExamAcademyAssignRepository() repositories.ExamAcademyAssignRepository
17
- ProvideExamAcademyAttemptRepository() repositories.ExamAcademyAttemptRepository
18
- ProvideExamEventAnswerRepository() repositories.ExamEventAnswerRepository
19
- ProvideExamEventAssignRepository() repositories.ExamEventAssignRepository
20
- ProvideExamEventAttemptRepository() repositories.ExamEventAttemptRepository
 
21
  ProvideExamRepository() repositories.ExamRepository
22
  ProvideExternalAuthRepository() repositories.ExternalAuthRepository
23
  ProvideFCMRepository() repositories.FCMRepository
@@ -32,32 +33,33 @@ type RepositoriesProvider interface {
32
  }
33
 
34
  type repositoriesProvider struct {
35
- academyPaymentRepository repositories.AcademyPaymentRepository
36
- academyRepository repositories.AcademyRepository
37
- academyResultRepository repositories.AcademyResultRepository
38
- accountDetailRepository repositories.AccountDetailRepository
39
- accountRepository repositories.AccountRepository
40
- emailVerificationRepository repositories.EmailVerificationRepository
41
- eventAssignRepository repositories.EventAssignRepository
42
- eventPaymentRepository repositories.EventPaymentRepository
43
- eventsRepository repositories.EventsRepository
44
- examAcademyAnswerRepository repositories.ExamAcademyAnswerRepository
45
- examAcademyAssignRepository repositories.ExamAcademyAssignRepository
46
- examAcademyAttemptRepository repositories.ExamAcademyAttemptRepository
47
- examEventAnswerRepository repositories.ExamEventAnswerRepository
48
- examEventAssignRepository repositories.ExamEventAssignRepository
49
- examEventAttemptRepository repositories.ExamEventAttemptRepository
50
- examRepository repositories.ExamRepository
51
- externalAuthRepository repositories.ExternalAuthRepository
52
- fCMRepository repositories.FCMRepository
53
- fileRepository repositories.FileRepository
54
- forgotPasswordRepository repositories.ForgotPasswordRepository
55
- optionRepository repositories.OptionRepository
 
56
  problemSetExamAssignRepository repositories.ProblemSetExamAssignRepository
57
- problemSetRepository repositories.ProblemSetRepository
58
- questionsRepository repositories.QuestionsRepository
59
- regionRepository repositories.RegionRepository
60
- resultRepository repositories.ResultRepository
61
  }
62
 
63
  func NewRepositoriesProvider(cfg ConfigProvider) RepositoriesProvider {
@@ -73,12 +75,13 @@ func NewRepositoriesProvider(cfg ConfigProvider) RepositoriesProvider {
73
  eventAssignRepository := repositories.NewEventAssignRepository(db)
74
  eventPaymentRepository := repositories.NewEventPaymentRepository(db)
75
  eventsRepository := repositories.NewEventsRepository(db)
76
- examAcademyAnswerRepository := repositories.NewExamAcademyAnswerRepository(db)
77
- examAcademyAssignRepository := repositories.NewExamAcademyAssignRepository(db)
78
- examAcademyAttemptRepository := repositories.NewExamAcademyAttemptRepository(db)
79
- examEventAnswerRepository := repositories.NewExamEventAnswerRepository(db)
80
- examEventAssignRepository := repositories.NewExamEventAssignRepository(db)
81
- examEventAttemptRepository := repositories.NewExamEventAttemptRepository(db)
 
82
  examRepository := repositories.NewExamRepository(db)
83
  externalAuthRepository := repositories.NewExternalAuthRepository(db)
84
  fCMRepository := repositories.NewFCMRepository(db)
@@ -92,32 +95,33 @@ func NewRepositoriesProvider(cfg ConfigProvider) RepositoriesProvider {
92
  resultRepository := repositories.NewResultRepository(db)
93
 
94
  return &repositoriesProvider{
95
- academyPaymentRepository: academyPaymentRepository,
96
- academyRepository: academyRepository,
97
- academyResultRepository: academyResultRepository,
98
- accountDetailRepository: accountDetailRepository,
99
- accountRepository: accountRepository,
100
- emailVerificationRepository: emailVerificationRepository,
101
- eventAssignRepository: eventAssignRepository,
102
- eventPaymentRepository: eventPaymentRepository,
103
- eventsRepository: eventsRepository,
104
- examAcademyAnswerRepository: examAcademyAnswerRepository,
105
- examAcademyAssignRepository: examAcademyAssignRepository,
106
- examAcademyAttemptRepository: examAcademyAttemptRepository,
107
- examEventAnswerRepository: examEventAnswerRepository,
108
- examEventAssignRepository: examEventAssignRepository,
109
- examEventAttemptRepository: examEventAttemptRepository,
110
- examRepository: examRepository,
111
- externalAuthRepository: externalAuthRepository,
112
- fCMRepository: fCMRepository,
113
- fileRepository: fileRepository,
114
- forgotPasswordRepository: forgotPasswordRepository,
115
- optionRepository: optionRepository,
 
116
  problemSetExamAssignRepository: problemSetExamAssignRepository,
117
- problemSetRepository: problemSetRepository,
118
- questionsRepository: questionsRepository,
119
- regionRepository: regionRepository,
120
- resultRepository: resultRepository,
121
  }
122
  }
123
 
@@ -157,28 +161,32 @@ func (r *repositoriesProvider) ProvideEventsRepository() repositories.EventsRepo
157
  return r.eventsRepository
158
  }
159
 
160
- func (r *repositoriesProvider) ProvideExamAcademyAnswerRepository() repositories.ExamAcademyAnswerRepository {
161
- return r.examAcademyAnswerRepository
162
  }
163
 
164
- func (r *repositoriesProvider) ProvideExamAcademyAssignRepository() repositories.ExamAcademyAssignRepository {
165
- return r.examAcademyAssignRepository
166
  }
167
 
168
- func (r *repositoriesProvider) ProvideExamAcademyAttemptRepository() repositories.ExamAcademyAttemptRepository {
169
- return r.examAcademyAttemptRepository
170
  }
171
 
172
- func (r *repositoriesProvider) ProvideExamEventAnswerRepository() repositories.ExamEventAnswerRepository {
173
- return r.examEventAnswerRepository
174
  }
175
 
176
- func (r *repositoriesProvider) ProvideExamEventAssignRepository() repositories.ExamEventAssignRepository {
177
- return r.examEventAssignRepository
178
  }
179
 
180
- func (r *repositoriesProvider) ProvideExamEventAttemptRepository() repositories.ExamEventAttemptRepository {
181
- return r.examEventAttemptRepository
 
 
 
 
182
  }
183
 
184
  func (r *repositoriesProvider) ProvideExamRepository() repositories.ExamRepository {
@@ -224,4 +232,3 @@ func (r *repositoriesProvider) ProvideRegionRepository() repositories.RegionRepo
224
  func (r *repositoriesProvider) ProvideResultRepository() repositories.ResultRepository {
225
  return r.resultRepository
226
  }
227
-
 
12
  ProvideEventAssignRepository() repositories.EventAssignRepository
13
  ProvideEventPaymentRepository() repositories.EventPaymentRepository
14
  ProvideEventsRepository() repositories.EventsRepository
15
+ ProvideAcademyExamAnswerRepository() repositories.AcademyExamAnswerRepository
16
+ ProvideAcademyExamAssignRepository() repositories.AcademyExamAssignRepository
17
+ ProvideAcademyExamAttemptRepository() repositories.AcademyExamAttemptRepository
18
+ ProvideEventExamAnswerRepository() repositories.EventExamAnswerRepository
19
+ ProvideEventExamAssignRepository() repositories.EventExamAssignRepository
20
+ ProvideEventExamAttemptRepository() repositories.EventExamAttemptRepository
21
+ ProvideEventExamProctoringRepository() repositories.EventExamProctoringRepository
22
  ProvideExamRepository() repositories.ExamRepository
23
  ProvideExternalAuthRepository() repositories.ExternalAuthRepository
24
  ProvideFCMRepository() repositories.FCMRepository
 
33
  }
34
 
35
  type repositoriesProvider struct {
36
+ academyPaymentRepository repositories.AcademyPaymentRepository
37
+ academyRepository repositories.AcademyRepository
38
+ academyResultRepository repositories.AcademyResultRepository
39
+ accountDetailRepository repositories.AccountDetailRepository
40
+ accountRepository repositories.AccountRepository
41
+ emailVerificationRepository repositories.EmailVerificationRepository
42
+ eventAssignRepository repositories.EventAssignRepository
43
+ eventPaymentRepository repositories.EventPaymentRepository
44
+ eventsRepository repositories.EventsRepository
45
+ academyExamAnswerRepository repositories.AcademyExamAnswerRepository
46
+ academyExamAssignRepository repositories.AcademyExamAssignRepository
47
+ academyExamAttemptRepository repositories.AcademyExamAttemptRepository
48
+ eventExamAnswerRepository repositories.EventExamAnswerRepository
49
+ eventExamAssignRepository repositories.EventExamAssignRepository
50
+ eventExamAttemptRepository repositories.EventExamAttemptRepository
51
+ eventExamProctoringRepository repositories.EventExamProctoringRepository
52
+ examRepository repositories.ExamRepository
53
+ externalAuthRepository repositories.ExternalAuthRepository
54
+ fCMRepository repositories.FCMRepository
55
+ fileRepository repositories.FileRepository
56
+ forgotPasswordRepository repositories.ForgotPasswordRepository
57
+ optionRepository repositories.OptionRepository
58
  problemSetExamAssignRepository repositories.ProblemSetExamAssignRepository
59
+ problemSetRepository repositories.ProblemSetRepository
60
+ questionsRepository repositories.QuestionsRepository
61
+ regionRepository repositories.RegionRepository
62
+ resultRepository repositories.ResultRepository
63
  }
64
 
65
  func NewRepositoriesProvider(cfg ConfigProvider) RepositoriesProvider {
 
75
  eventAssignRepository := repositories.NewEventAssignRepository(db)
76
  eventPaymentRepository := repositories.NewEventPaymentRepository(db)
77
  eventsRepository := repositories.NewEventsRepository(db)
78
+ academyExamAnswerRepository := repositories.NewAcademyExamAnswerRepository(db)
79
+ academyExamAssignRepository := repositories.NewAcademyExamAssignRepository(db)
80
+ academyExamAttemptRepository := repositories.NewAcademyExamAttemptRepository(db)
81
+ eventExamAnswerRepository := repositories.NewEventExamAnswerRepository(db)
82
+ eventExamAssignRepository := repositories.NewEventExamAssignRepository(db)
83
+ eventExamAttemptRepository := repositories.NewEventExamAttemptRepository(db)
84
+ eventExamProctoringRepository := repositories.NewEventExamProctoringRepository(db)
85
  examRepository := repositories.NewExamRepository(db)
86
  externalAuthRepository := repositories.NewExternalAuthRepository(db)
87
  fCMRepository := repositories.NewFCMRepository(db)
 
95
  resultRepository := repositories.NewResultRepository(db)
96
 
97
  return &repositoriesProvider{
98
+ academyPaymentRepository: academyPaymentRepository,
99
+ academyRepository: academyRepository,
100
+ academyResultRepository: academyResultRepository,
101
+ accountDetailRepository: accountDetailRepository,
102
+ accountRepository: accountRepository,
103
+ emailVerificationRepository: emailVerificationRepository,
104
+ eventAssignRepository: eventAssignRepository,
105
+ eventPaymentRepository: eventPaymentRepository,
106
+ eventsRepository: eventsRepository,
107
+ academyExamAnswerRepository: academyExamAnswerRepository,
108
+ academyExamAssignRepository: academyExamAssignRepository,
109
+ academyExamAttemptRepository: academyExamAttemptRepository,
110
+ eventExamAnswerRepository: eventExamAnswerRepository,
111
+ eventExamAssignRepository: eventExamAssignRepository,
112
+ eventExamAttemptRepository: eventExamAttemptRepository,
113
+ eventExamProctoringRepository: eventExamProctoringRepository,
114
+ examRepository: examRepository,
115
+ externalAuthRepository: externalAuthRepository,
116
+ fCMRepository: fCMRepository,
117
+ fileRepository: fileRepository,
118
+ forgotPasswordRepository: forgotPasswordRepository,
119
+ optionRepository: optionRepository,
120
  problemSetExamAssignRepository: problemSetExamAssignRepository,
121
+ problemSetRepository: problemSetRepository,
122
+ questionsRepository: questionsRepository,
123
+ regionRepository: regionRepository,
124
+ resultRepository: resultRepository,
125
  }
126
  }
127
 
 
161
  return r.eventsRepository
162
  }
163
 
164
+ func (r *repositoriesProvider) ProvideAcademyExamAnswerRepository() repositories.AcademyExamAnswerRepository {
165
+ return r.academyExamAnswerRepository
166
  }
167
 
168
+ func (r *repositoriesProvider) ProvideAcademyExamAssignRepository() repositories.AcademyExamAssignRepository {
169
+ return r.academyExamAssignRepository
170
  }
171
 
172
+ func (r *repositoriesProvider) ProvideAcademyExamAttemptRepository() repositories.AcademyExamAttemptRepository {
173
+ return r.academyExamAttemptRepository
174
  }
175
 
176
+ func (r *repositoriesProvider) ProvideEventExamAnswerRepository() repositories.EventExamAnswerRepository {
177
+ return r.eventExamAnswerRepository
178
  }
179
 
180
+ func (r *repositoriesProvider) ProvideEventExamAssignRepository() repositories.EventExamAssignRepository {
181
+ return r.eventExamAssignRepository
182
  }
183
 
184
+ func (r *repositoriesProvider) ProvideEventExamAttemptRepository() repositories.EventExamAttemptRepository {
185
+ return r.eventExamAttemptRepository
186
+ }
187
+
188
+ func (r *repositoriesProvider) ProvideEventExamProctoringRepository() repositories.EventExamProctoringRepository {
189
+ return r.eventExamProctoringRepository
190
  }
191
 
192
  func (r *repositoriesProvider) ProvideExamRepository() repositories.ExamRepository {
 
232
  func (r *repositoriesProvider) ProvideResultRepository() repositories.ResultRepository {
233
  return r.resultRepository
234
  }
 
provider/services_provider.go CHANGED
@@ -19,24 +19,28 @@ type ServicesProvider interface {
19
  ProvideAcademyExamService() services.AcademyExamService
20
  ProvideEmailVerificationService() services.EmailVerificationService
21
  ProvideExternalAuthService() services.ExternalAuthService
 
 
22
  ProvideExamService() services.ExamService
23
  }
24
 
25
  type servicesProvider struct {
26
- regionService services.RegionService
27
- jWTService services.JWTService
28
- academyService services.AcademyService
29
- paymentService services.PaymentService
30
- uploadService services.UploadService
31
- problemSetService services.ProblemSetService
32
- optionService services.OptionService
33
- accountService services.AccountService
34
- forgotPasswordService services.ForgotPasswordService
35
- eventService services.EventService
36
- academyExamService services.AcademyExamService
37
- emailVerificationService services.EmailVerificationService
38
- externalAuthService services.ExternalAuthService
39
- examService services.ExamService
 
 
40
  }
41
 
42
  func NewServicesProvider(repoProvider RepositoriesProvider, configProvider ConfigProvider) ServicesProvider {
@@ -56,26 +60,30 @@ func NewServicesProvider(repoProvider RepositoriesProvider, configProvider Confi
56
  accountService := services.NewAccountService(jWTService, repoProvider.ProvideAccountRepository(), repoProvider.ProvideAccountDetailRepository())
57
  forgotPasswordService := services.NewForgotPasswordService(jWTService, repoProvider.ProvideAccountRepository(), repoProvider.ProvideForgotPasswordRepository())
58
  eventService := services.NewEventService(paymentService, repoProvider.ProvideEventsRepository(), repoProvider.ProvideEventAssignRepository())
59
- academyExamService := services.NewAcademyExamService(academyService, problemSetService, repoProvider.ProvideExamRepository(), repoProvider.ProvideExamAcademyAttemptRepository(), repoProvider.ProvideExamAcademyAssignRepository(), repoProvider.ProvideExamAcademyAnswerRepository(), repoProvider.ProvideAcademyResultRepository())
60
  emailVerificationService := services.NewEmailVerificationService(accountService, repoProvider.ProvideEmailVerificationRepository())
61
  externalAuthService := services.NewExternalAuthService(jWTService, accountService, repoProvider.ProvideExternalAuthRepository())
62
- examService := services.NewExamService(eventService, problemSetService, repoProvider.ProvideProblemSetExamAssignRepository(), repoProvider.ProvideExamRepository(), repoProvider.ProvideExamEventAttemptRepository(), repoProvider.ProvideExamEventAssignRepository(), repoProvider.ProvideExamEventAnswerRepository(), repoProvider.ProvideResultRepository())
 
 
63
 
64
  return &servicesProvider{
65
- regionService: regionService,
66
- jWTService: jWTService,
67
- academyService: academyService,
68
- paymentService: paymentService,
69
- uploadService: uploadService,
70
- problemSetService: problemSetService,
71
- optionService: optionService,
72
- accountService: accountService,
73
- forgotPasswordService: forgotPasswordService,
74
- eventService: eventService,
75
- academyExamService: academyExamService,
76
- emailVerificationService: emailVerificationService,
77
- externalAuthService: externalAuthService,
78
- examService: examService,
 
 
79
  }
80
  }
81
 
@@ -134,3 +142,11 @@ func (s *servicesProvider) ProvideExternalAuthService() services.ExternalAuthSer
134
  func (s *servicesProvider) ProvideExamService() services.ExamService {
135
  return s.examService
136
  }
 
 
 
 
 
 
 
 
 
19
  ProvideAcademyExamService() services.AcademyExamService
20
  ProvideEmailVerificationService() services.EmailVerificationService
21
  ProvideExternalAuthService() services.ExternalAuthService
22
+ ProvideEventExamService() services.EventExamService
23
+ ProvideEventExamProctoringService() services.EventExamProctoringService
24
  ProvideExamService() services.ExamService
25
  }
26
 
27
  type servicesProvider struct {
28
+ regionService services.RegionService
29
+ jWTService services.JWTService
30
+ academyService services.AcademyService
31
+ paymentService services.PaymentService
32
+ uploadService services.UploadService
33
+ problemSetService services.ProblemSetService
34
+ optionService services.OptionService
35
+ accountService services.AccountService
36
+ forgotPasswordService services.ForgotPasswordService
37
+ eventService services.EventService
38
+ academyExamService services.AcademyExamService
39
+ emailVerificationService services.EmailVerificationService
40
+ externalAuthService services.ExternalAuthService
41
+ eventExamService services.EventExamService
42
+ eventExamProctoringService services.EventExamProctoringService
43
+ examService services.ExamService
44
  }
45
 
46
  func NewServicesProvider(repoProvider RepositoriesProvider, configProvider ConfigProvider) ServicesProvider {
 
60
  accountService := services.NewAccountService(jWTService, repoProvider.ProvideAccountRepository(), repoProvider.ProvideAccountDetailRepository())
61
  forgotPasswordService := services.NewForgotPasswordService(jWTService, repoProvider.ProvideAccountRepository(), repoProvider.ProvideForgotPasswordRepository())
62
  eventService := services.NewEventService(paymentService, repoProvider.ProvideEventsRepository(), repoProvider.ProvideEventAssignRepository())
63
+ academyExamService := services.NewAcademyExamService(academyService, problemSetService, repoProvider.ProvideExamRepository(), repoProvider.ProvideAcademyExamAttemptRepository(), repoProvider.ProvideAcademyExamAssignRepository(), repoProvider.ProvideAcademyExamAnswerRepository(), repoProvider.ProvideAcademyResultRepository())
64
  emailVerificationService := services.NewEmailVerificationService(accountService, repoProvider.ProvideEmailVerificationRepository())
65
  externalAuthService := services.NewExternalAuthService(jWTService, accountService, repoProvider.ProvideExternalAuthRepository())
66
+ eventExamService := services.NewEventExamService(eventService, problemSetService, repoProvider.ProvideProblemSetExamAssignRepository(), repoProvider.ProvideExamRepository(), repoProvider.ProvideEventExamAttemptRepository(), repoProvider.ProvideEventExamAssignRepository(), repoProvider.ProvideEventExamAnswerRepository(), repoProvider.ProvideResultRepository())
67
+ eventExamProctoringService := services.NewEventExamProctoringService(repoProvider.ProvideEventExamProctoringRepository(), uploadService)
68
+ examService := services.NewExamService(repoProvider.ProvideExamRepository(), repoProvider.ProvideEventExamAssignRepository(), repoProvider.ProvideAcademyExamAssignRepository())
69
 
70
  return &servicesProvider{
71
+ regionService: regionService,
72
+ jWTService: jWTService,
73
+ academyService: academyService,
74
+ paymentService: paymentService,
75
+ uploadService: uploadService,
76
+ problemSetService: problemSetService,
77
+ optionService: optionService,
78
+ accountService: accountService,
79
+ forgotPasswordService: forgotPasswordService,
80
+ eventService: eventService,
81
+ academyExamService: academyExamService,
82
+ emailVerificationService: emailVerificationService,
83
+ externalAuthService: externalAuthService,
84
+ eventExamService: eventExamService,
85
+ eventExamProctoringService: eventExamProctoringService,
86
+ examService: examService,
87
  }
88
  }
89
 
 
142
  func (s *servicesProvider) ProvideExamService() services.ExamService {
143
  return s.examService
144
  }
145
+
146
+ func (s *servicesProvider) ProvideEventExamService() services.EventExamService {
147
+ return s.eventExamService
148
+ }
149
+
150
+ func (s *servicesProvider) ProvideEventExamProctoringService() services.EventExamProctoringService {
151
+ return s.eventExamProctoringService
152
+ }
repositories/academy_exam_answer_repository.go ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "context"
5
+
6
+ entity "abdanhafidz.com/go-boilerplate/models/entity"
7
+ "github.com/google/uuid"
8
+ "gorm.io/gorm"
9
+ )
10
+
11
+ type AcademyExamAnswerRepository interface {
12
+ Create(ctx context.Context, ans *entity.AcademyExamAnswer) error
13
+ Update(ctx context.Context, ans *entity.AcademyExamAnswer) error
14
+ GetByAttemptAndQuestion(ctx context.Context, attemptId uuid.UUID, questionId uuid.UUID) (entity.AcademyExamAnswer, error)
15
+ ListByAttempt(ctx context.Context, attemptId uuid.UUID) ([]entity.AcademyExamAnswer, error)
16
+ DeleteByAttempt(ctx context.Context, attemptId uuid.UUID) error
17
+ }
18
+
19
+ type academyExamAnswerRepository struct{ db *gorm.DB }
20
+
21
+ func NewAcademyExamAnswerRepository(db *gorm.DB) AcademyExamAnswerRepository {
22
+ return &academyExamAnswerRepository{db: db}
23
+ }
24
+
25
+ func (r *academyExamAnswerRepository) Create(ctx context.Context, ans *entity.AcademyExamAnswer) error {
26
+ return r.db.WithContext(ctx).Create(ans).Error
27
+ }
28
+
29
+ func (r *academyExamAnswerRepository) Update(ctx context.Context, ans *entity.AcademyExamAnswer) error {
30
+ return r.db.WithContext(ctx).
31
+ Where("attempt_id = ? AND question_id = ?", ans.AttemptId, ans.QuestionId).
32
+ Updates(ans).Error
33
+ }
34
+
35
+ func (r *academyExamAnswerRepository) GetByAttemptAndQuestion(ctx context.Context, attemptId uuid.UUID, questionId uuid.UUID) (entity.AcademyExamAnswer, error) {
36
+ var ans entity.AcademyExamAnswer
37
+ err := r.db.WithContext(ctx).
38
+ Where("attempt_id = ? AND question_id = ?", attemptId, questionId).
39
+ First(&ans).Error
40
+ return ans, err
41
+ }
42
+
43
+ func (r *academyExamAnswerRepository) ListByAttempt(ctx context.Context, attemptId uuid.UUID) ([]entity.AcademyExamAnswer, error) {
44
+ var answers []entity.AcademyExamAnswer
45
+ err := r.db.WithContext(ctx).
46
+ Where("attempt_id = ?", attemptId).
47
+ Find(&answers).Error
48
+ return answers, err
49
+ }
50
+
51
+ func (r *academyExamAnswerRepository) DeleteByAttempt(ctx context.Context, attemptId uuid.UUID) error {
52
+ return r.db.WithContext(ctx).
53
+ Where("attempt_id = ?", attemptId).
54
+ Delete(&entity.AcademyExamAnswer{}).Error
55
+ }
repositories/academy_exam_assign_repository.go ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "context"
5
+
6
+ entity "abdanhafidz.com/go-boilerplate/models/entity"
7
+ "github.com/google/uuid"
8
+ "gorm.io/gorm"
9
+ )
10
+
11
+ type AcademyExamAssignRepository interface {
12
+ Create(ctx context.Context, m entity.AcademyExamAssign) error
13
+ ListByAcademy(ctx context.Context, academyId uuid.UUID) ([]entity.AcademyExamAssign, error)
14
+ Delete(ctx context.Context, id uuid.UUID) error
15
+ Check(ctx context.Context, academyId uuid.UUID, examId uuid.UUID) error
16
+ }
17
+
18
+ type academyExamAssignRepository struct{ db *gorm.DB }
19
+
20
+ func NewAcademyExamAssignRepository(db *gorm.DB) AcademyExamAssignRepository {
21
+ return &academyExamAssignRepository{db}
22
+ }
23
+
24
+ func (r *academyExamAssignRepository) Check(ctx context.Context, academyId uuid.UUID, examId uuid.UUID) error {
25
+ return r.db.WithContext(ctx).
26
+ Where("academy_id = ?", academyId).
27
+ Where("exam_id = ?", examId).
28
+ First(&entity.AcademyExamAssign{}).Error
29
+ }
30
+
31
+ func (r *academyExamAssignRepository) Create(ctx context.Context, m entity.AcademyExamAssign) error {
32
+ return r.db.WithContext(ctx).Create(&m).Error
33
+ }
34
+
35
+ func (r *academyExamAssignRepository) ListByAcademy(ctx context.Context, academyId uuid.UUID) ([]entity.AcademyExamAssign, error) {
36
+ var items []entity.AcademyExamAssign
37
+ err := r.db.WithContext(ctx).
38
+ Where("academy_id = ?", academyId).
39
+ Preload("Exam").
40
+ Find(&items).Error
41
+ return items, err
42
+ }
43
+
44
+ func (r *academyExamAssignRepository) Delete(ctx context.Context, id uuid.UUID) error {
45
+ return r.db.WithContext(ctx).
46
+ Where("id = ?", id).
47
+ Delete(&entity.AcademyExamAssign{}).Error
48
+ }
repositories/academy_exam_attempt_repository.go ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "context"
5
+
6
+ entity "abdanhafidz.com/go-boilerplate/models/entity"
7
+ "github.com/google/uuid"
8
+ "gorm.io/gorm"
9
+ )
10
+
11
+ type AcademyExamAttemptRepository interface {
12
+ Create(ctx context.Context, a *entity.AcademyExamAttempt) error
13
+ GetById(ctx context.Context, attemptId uuid.UUID) (entity.AcademyExamAttempt, error)
14
+ GetByAcademyExam(ctx context.Context, academyId uuid.UUID, examId uuid.UUID, accountId uuid.UUID) (entity.AcademyExamAttempt, error)
15
+ Update(ctx context.Context, a *entity.AcademyExamAttempt) error
16
+ }
17
+
18
+ type academyExamAttemptRepository struct{ db *gorm.DB }
19
+
20
+ func NewAcademyExamAttemptRepository(db *gorm.DB) AcademyExamAttemptRepository {
21
+ return &academyExamAttemptRepository{db}
22
+ }
23
+
24
+ func (r *academyExamAttemptRepository) Create(ctx context.Context, a *entity.AcademyExamAttempt) error {
25
+ return r.db.WithContext(ctx).Create(a).Error
26
+ }
27
+
28
+ func (r *academyExamAttemptRepository) GetById(ctx context.Context, attemptId uuid.UUID) (entity.AcademyExamAttempt, error) {
29
+ var a entity.AcademyExamAttempt
30
+ err := r.db.WithContext(ctx).
31
+ Preload("Answers").
32
+ First(&a, "id = ?", attemptId).Error
33
+ return a, err
34
+ }
35
+
36
+ func (r *academyExamAttemptRepository) GetByAcademyExam(ctx context.Context, academyId uuid.UUID, examId uuid.UUID, accountId uuid.UUID) (entity.AcademyExamAttempt, error) {
37
+ var attempt entity.AcademyExamAttempt
38
+ err := r.db.WithContext(ctx).
39
+ Preload("Answers").
40
+ Where("academy_id = ?", academyId).
41
+ Where("exam_id = ?", examId).
42
+ Where("account_id = ?", accountId).
43
+ First(&attempt).Error
44
+ return attempt, err
45
+ }
46
+
47
+ func (r *academyExamAttemptRepository) Update(ctx context.Context, a *entity.AcademyExamAttempt) error {
48
+ return r.db.WithContext(ctx).
49
+ Model(&entity.AcademyExamAttempt{}).
50
+ Where("id = ?", a.Id).
51
+ Updates(a).Error
52
+ }
repositories/academy_result_repository.go CHANGED
@@ -9,9 +9,9 @@ import (
9
  )
10
 
11
  type AcademyResultRepository interface {
12
- Create(ctx context.Context, r *entity.ExamAcademyResult) error
13
- GetById(ctx context.Context, id uuid.UUID) (entity.ExamAcademyResult, error)
14
- Update(ctx context.Context, r *entity.ExamAcademyResult) error
15
  }
16
 
17
  type academyResultRepository struct{ db *gorm.DB }
@@ -20,20 +20,20 @@ func NewAcademyResultRepository(db *gorm.DB) AcademyResultRepository {
20
  return &academyResultRepository{db}
21
  }
22
 
23
- func (r *academyResultRepository) Create(ctx context.Context, rec *entity.ExamAcademyResult) error {
24
  return r.db.WithContext(ctx).Create(rec).Error
25
  }
26
 
27
- func (r *academyResultRepository) GetById(ctx context.Context, id uuid.UUID) (entity.ExamAcademyResult, error) {
28
- var rec entity.ExamAcademyResult
29
  err := r.db.WithContext(ctx).
30
  First(&rec, "id = ?", id).Error
31
  return rec, err
32
  }
33
 
34
- func (r *academyResultRepository) Update(ctx context.Context, rec *entity.ExamAcademyResult) error {
35
  return r.db.WithContext(ctx).
36
- Model(&entity.ExamAcademyResult{}).
37
  Where("id = ?", rec.Id).
38
  Updates(rec).Error
39
  }
 
9
  )
10
 
11
  type AcademyResultRepository interface {
12
+ Create(ctx context.Context, r *entity.AcademyExamResult) error
13
+ GetById(ctx context.Context, id uuid.UUID) (entity.AcademyExamResult, error)
14
+ Update(ctx context.Context, r *entity.AcademyExamResult) error
15
  }
16
 
17
  type academyResultRepository struct{ db *gorm.DB }
 
20
  return &academyResultRepository{db}
21
  }
22
 
23
+ func (r *academyResultRepository) Create(ctx context.Context, rec *entity.AcademyExamResult) error {
24
  return r.db.WithContext(ctx).Create(rec).Error
25
  }
26
 
27
+ func (r *academyResultRepository) GetById(ctx context.Context, id uuid.UUID) (entity.AcademyExamResult, error) {
28
+ var rec entity.AcademyExamResult
29
  err := r.db.WithContext(ctx).
30
  First(&rec, "id = ?", id).Error
31
  return rec, err
32
  }
33
 
34
+ func (r *academyResultRepository) Update(ctx context.Context, rec *entity.AcademyExamResult) error {
35
  return r.db.WithContext(ctx).
36
+ Model(&entity.AcademyExamResult{}).
37
  Where("id = ?", rec.Id).
38
  Updates(rec).Error
39
  }
repositories/{exam_event_answer_repository.go → event_exam_answer_repository.go} RENAMED
@@ -8,52 +8,52 @@ import (
8
  "gorm.io/gorm"
9
  )
10
 
11
- type ExamEventAnswerRepository interface {
12
- Create(ctx context.Context, ans *entity.ExamEventAnswer) error
13
- Update(ctx context.Context, ans *entity.ExamEventAnswer) error
14
- GetByAttemptAndQuestion(ctx context.Context, attemptId uuid.UUID, questionId uuid.UUID) (entity.ExamEventAnswer, error)
15
- ListByAttempt(ctx context.Context, attemptId uuid.UUID) ([]entity.ExamEventAnswer, error)
16
  DeleteByAttempt(ctx context.Context, attemptId uuid.UUID) error
17
 
18
  // decomposed result (answer + question)
19
  }
20
 
21
- type examEventAnswerRepository struct {
22
  db *gorm.DB
23
  }
24
 
25
- func NewExamEventAnswerRepository(db *gorm.DB) ExamEventAnswerRepository {
26
- return &examEventAnswerRepository{db: db}
27
  }
28
 
29
- func (r *examEventAnswerRepository) Create(ctx context.Context, ans *entity.ExamEventAnswer) error {
30
  return r.db.WithContext(ctx).Create(ans).Error
31
  }
32
 
33
- func (r *examEventAnswerRepository) Update(ctx context.Context, ans *entity.ExamEventAnswer) error {
34
  return r.db.WithContext(ctx).
35
  Where("attempt_id = ? AND question_id = ?", ans.AttemptId, ans.QuestionId).
36
  Updates(ans).Error
37
  }
38
 
39
- func (r *examEventAnswerRepository) GetByAttemptAndQuestion(ctx context.Context, attemptId uuid.UUID, questionId uuid.UUID) (entity.ExamEventAnswer, error) {
40
- var ans entity.ExamEventAnswer
41
  err := r.db.WithContext(ctx).
42
  Where("attempt_id = ? AND question_id = ?", attemptId, questionId).
43
  First(&ans).Error
44
  return ans, err
45
  }
46
 
47
- func (r *examEventAnswerRepository) ListByAttempt(ctx context.Context, attemptId uuid.UUID) ([]entity.ExamEventAnswer, error) {
48
- var answers []entity.ExamEventAnswer
49
  err := r.db.WithContext(ctx).
50
  Where("attempt_id = ?", attemptId).
51
  Find(&answers).Error
52
  return answers, err
53
  }
54
 
55
- func (r *examEventAnswerRepository) DeleteByAttempt(ctx context.Context, attemptId uuid.UUID) error {
56
  return r.db.WithContext(ctx).
57
  Where("attempt_id = ?", attemptId).
58
- Delete(&entity.ExamEventAnswer{}).Error
59
  }
 
8
  "gorm.io/gorm"
9
  )
10
 
11
+ type EventExamAnswerRepository interface {
12
+ Create(ctx context.Context, ans *entity.EventExamAnswer) error
13
+ Update(ctx context.Context, ans *entity.EventExamAnswer) error
14
+ GetByAttemptAndQuestion(ctx context.Context, attemptId uuid.UUID, questionId uuid.UUID) (entity.EventExamAnswer, error)
15
+ ListByAttempt(ctx context.Context, attemptId uuid.UUID) ([]entity.EventExamAnswer, error)
16
  DeleteByAttempt(ctx context.Context, attemptId uuid.UUID) error
17
 
18
  // decomposed result (answer + question)
19
  }
20
 
21
+ type eventExamAnswerRepository struct {
22
  db *gorm.DB
23
  }
24
 
25
+ func NewEventExamAnswerRepository(db *gorm.DB) EventExamAnswerRepository {
26
+ return &eventExamAnswerRepository{db: db}
27
  }
28
 
29
+ func (r *eventExamAnswerRepository) Create(ctx context.Context, ans *entity.EventExamAnswer) error {
30
  return r.db.WithContext(ctx).Create(ans).Error
31
  }
32
 
33
+ func (r *eventExamAnswerRepository) Update(ctx context.Context, ans *entity.EventExamAnswer) error {
34
  return r.db.WithContext(ctx).
35
  Where("attempt_id = ? AND question_id = ?", ans.AttemptId, ans.QuestionId).
36
  Updates(ans).Error
37
  }
38
 
39
+ func (r *eventExamAnswerRepository) GetByAttemptAndQuestion(ctx context.Context, attemptId uuid.UUID, questionId uuid.UUID) (entity.EventExamAnswer, error) {
40
+ var ans entity.EventExamAnswer
41
  err := r.db.WithContext(ctx).
42
  Where("attempt_id = ? AND question_id = ?", attemptId, questionId).
43
  First(&ans).Error
44
  return ans, err
45
  }
46
 
47
+ func (r *eventExamAnswerRepository) ListByAttempt(ctx context.Context, attemptId uuid.UUID) ([]entity.EventExamAnswer, error) {
48
+ var answers []entity.EventExamAnswer
49
  err := r.db.WithContext(ctx).
50
  Where("attempt_id = ?", attemptId).
51
  Find(&answers).Error
52
  return answers, err
53
  }
54
 
55
+ func (r *eventExamAnswerRepository) DeleteByAttempt(ctx context.Context, attemptId uuid.UUID) error {
56
  return r.db.WithContext(ctx).
57
  Where("attempt_id = ?", attemptId).
58
+ Delete(&entity.EventExamAnswer{}).Error
59
  }
repositories/{exam_event_assign_repository.go → event_exam_assign_repository.go} RENAMED
@@ -8,31 +8,31 @@ import (
8
  "gorm.io/gorm"
9
  )
10
 
11
- type ExamEventAssignRepository interface {
12
- Create(ctx context.Context, m entity.ExamEventAssign) error
13
- ListByEvent(ctx context.Context, eventId uuid.UUID) ([]entity.ExamEventAssign, error)
14
  Delete(ctx context.Context, id uuid.UUID) error
15
  Check(ctx context.Context, eventId uuid.UUID, examId uuid.UUID) error
16
  }
17
 
18
- type examEventAssignRepository struct{ db *gorm.DB }
19
 
20
- func NewExamEventAssignRepository(db *gorm.DB) ExamEventAssignRepository {
21
- return &examEventAssignRepository{db}
22
  }
23
 
24
- func (r *examEventAssignRepository) Check(ctx context.Context, eventId uuid.UUID, examId uuid.UUID) error {
25
  return r.db.WithContext(ctx).
26
  Where("event_id = ?", eventId).
27
  Where("exam_id = ?", examId).
28
- First(&entity.ExamEventAssign{}).Error
29
  }
30
- func (r *examEventAssignRepository) Create(ctx context.Context, m entity.ExamEventAssign) error {
31
  return r.db.WithContext(ctx).Create(&m).Error
32
  }
33
 
34
- func (r *examEventAssignRepository) ListByEvent(ctx context.Context, eventId uuid.UUID) ([]entity.ExamEventAssign, error) {
35
- var items []entity.ExamEventAssign
36
  err := r.db.WithContext(ctx).
37
  Where("event_id = ?", eventId).
38
  Preload("Exam").
@@ -40,8 +40,8 @@ func (r *examEventAssignRepository) ListByEvent(ctx context.Context, eventId uui
40
  return items, err
41
  }
42
 
43
- func (r *examEventAssignRepository) Delete(ctx context.Context, id uuid.UUID) error {
44
  return r.db.WithContext(ctx).
45
  Where("id = ?", id).
46
- Delete(&entity.ExamEventAssign{}).Error
47
  }
 
8
  "gorm.io/gorm"
9
  )
10
 
11
+ type EventExamAssignRepository interface {
12
+ Create(ctx context.Context, m entity.EventExamAssign) error
13
+ ListByEvent(ctx context.Context, eventId uuid.UUID) ([]entity.EventExamAssign, error)
14
  Delete(ctx context.Context, id uuid.UUID) error
15
  Check(ctx context.Context, eventId uuid.UUID, examId uuid.UUID) error
16
  }
17
 
18
+ type eventExamAssignRepository struct{ db *gorm.DB }
19
 
20
+ func NewEventExamAssignRepository(db *gorm.DB) EventExamAssignRepository {
21
+ return &eventExamAssignRepository{db}
22
  }
23
 
24
+ func (r *eventExamAssignRepository) Check(ctx context.Context, eventId uuid.UUID, examId uuid.UUID) error {
25
  return r.db.WithContext(ctx).
26
  Where("event_id = ?", eventId).
27
  Where("exam_id = ?", examId).
28
+ First(&entity.EventExamAssign{}).Error
29
  }
30
+ func (r *eventExamAssignRepository) Create(ctx context.Context, m entity.EventExamAssign) error {
31
  return r.db.WithContext(ctx).Create(&m).Error
32
  }
33
 
34
+ func (r *eventExamAssignRepository) ListByEvent(ctx context.Context, eventId uuid.UUID) ([]entity.EventExamAssign, error) {
35
+ var items []entity.EventExamAssign
36
  err := r.db.WithContext(ctx).
37
  Where("event_id = ?", eventId).
38
  Preload("Exam").
 
40
  return items, err
41
  }
42
 
43
+ func (r *eventExamAssignRepository) Delete(ctx context.Context, id uuid.UUID) error {
44
  return r.db.WithContext(ctx).
45
  Where("id = ?", id).
46
+ Delete(&entity.EventExamAssign{}).Error
47
  }
repositories/{exam_event_attempt_repository.go → event_exam_attempt_repository.go} RENAMED
@@ -8,34 +8,34 @@ import (
8
  "gorm.io/gorm"
9
  )
10
 
11
- type ExamEventAttemptRepository interface {
12
- Create(ctx context.Context, a *entity.ExamEventAttempt) error
13
- GetById(ctx context.Context, attemptId uuid.UUID) (entity.ExamEventAttempt, error)
14
- GetByExamEvent(ctx context.Context, eventId uuid.UUID, examId uuid.UUID, accountId uuid.UUID) (entity.ExamEventAttempt, error)
15
- Update(ctx context.Context, a *entity.ExamEventAttempt) error
16
  }
17
 
18
- type examEventAttemptRepository struct{ db *gorm.DB }
19
 
20
- func NewExamEventAttemptRepository(db *gorm.DB) ExamEventAttemptRepository {
21
- return &examEventAttemptRepository{db}
22
  }
23
 
24
- func (r *examEventAttemptRepository) Create(ctx context.Context, a *entity.ExamEventAttempt) error {
25
  return r.db.WithContext(ctx).Create(a).Error
26
  }
27
 
28
- func (r *examEventAttemptRepository) GetById(ctx context.Context, attemptId uuid.UUID) (entity.ExamEventAttempt, error) {
29
- var a entity.ExamEventAttempt
30
  err := r.db.WithContext(ctx).
31
  Preload("Answers").
32
  First(&a, "id = ?", attemptId).Error
33
  return a, err
34
  }
35
 
36
- func (r *examEventAttemptRepository) GetByExamEvent(ctx context.Context, eventId uuid.UUID, examId uuid.UUID, accountId uuid.UUID) (entity.ExamEventAttempt, error) {
37
 
38
- var attempt entity.ExamEventAttempt
39
 
40
  err := r.db.WithContext(ctx).
41
  Preload("Answers").
@@ -47,9 +47,9 @@ func (r *examEventAttemptRepository) GetByExamEvent(ctx context.Context, eventId
47
  return attempt, err
48
  }
49
 
50
- func (r *examEventAttemptRepository) Update(ctx context.Context, a *entity.ExamEventAttempt) error {
51
  return r.db.WithContext(ctx).
52
- Model(&entity.ExamEventAttempt{}).
53
  Where("id = ?", a.Id).
54
  Updates(a).Error
55
  }
 
8
  "gorm.io/gorm"
9
  )
10
 
11
+ type EventExamAttemptRepository interface {
12
+ Create(ctx context.Context, a *entity.EventExamAttempt) error
13
+ GetById(ctx context.Context, attemptId uuid.UUID) (entity.EventExamAttempt, error)
14
+ GetByEventExam(ctx context.Context, eventId uuid.UUID, examId uuid.UUID, accountId uuid.UUID) (entity.EventExamAttempt, error)
15
+ Update(ctx context.Context, a *entity.EventExamAttempt) error
16
  }
17
 
18
+ type eventExamAttemptRepository struct{ db *gorm.DB }
19
 
20
+ func NewEventExamAttemptRepository(db *gorm.DB) EventExamAttemptRepository {
21
+ return &eventExamAttemptRepository{db}
22
  }
23
 
24
+ func (r *eventExamAttemptRepository) Create(ctx context.Context, a *entity.EventExamAttempt) error {
25
  return r.db.WithContext(ctx).Create(a).Error
26
  }
27
 
28
+ func (r *eventExamAttemptRepository) GetById(ctx context.Context, attemptId uuid.UUID) (entity.EventExamAttempt, error) {
29
+ var a entity.EventExamAttempt
30
  err := r.db.WithContext(ctx).
31
  Preload("Answers").
32
  First(&a, "id = ?", attemptId).Error
33
  return a, err
34
  }
35
 
36
+ func (r *eventExamAttemptRepository) GetByEventExam(ctx context.Context, eventId uuid.UUID, examId uuid.UUID, accountId uuid.UUID) (entity.EventExamAttempt, error) {
37
 
38
+ var attempt entity.EventExamAttempt
39
 
40
  err := r.db.WithContext(ctx).
41
  Preload("Answers").
 
47
  return attempt, err
48
  }
49
 
50
+ func (r *eventExamAttemptRepository) Update(ctx context.Context, a *entity.EventExamAttempt) error {
51
  return r.db.WithContext(ctx).
52
+ Model(&entity.EventExamAttempt{}).
53
  Where("id = ?", a.Id).
54
  Updates(a).Error
55
  }
repositories/event_exam_proctoring_repository.go ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package repositories
2
+
3
+ import (
4
+ "context"
5
+
6
+ entity "abdanhafidz.com/go-boilerplate/models/entity"
7
+ "github.com/google/uuid"
8
+ "gorm.io/gorm"
9
+ )
10
+
11
+ type EventExamProctoringRepository interface {
12
+ Create(ctx context.Context, log *entity.EventExamProctoringLogs) error
13
+ List(ctx context.Context, accountId uuid.UUID, examId uuid.UUID, eventId uuid.UUID) ([]entity.EventExamProctoringLogs, error)
14
+ GetById(ctx context.Context, id uuid.UUID) (entity.EventExamProctoringLogs, error)
15
+ Update(ctx context.Context, log *entity.EventExamProctoringLogs) error
16
+ Delete(ctx context.Context, id uuid.UUID) error
17
+ }
18
+
19
+ type eventExamProctoringRepository struct {
20
+ db *gorm.DB
21
+ }
22
+
23
+ func NewEventExamProctoringRepository(db *gorm.DB) EventExamProctoringRepository {
24
+ return &eventExamProctoringRepository{db}
25
+ }
26
+
27
+ func (r *eventExamProctoringRepository) Create(ctx context.Context, log *entity.EventExamProctoringLogs) error {
28
+ return r.db.WithContext(ctx).Create(log).Error
29
+ }
30
+
31
+ func (r *eventExamProctoringRepository) List(ctx context.Context, accountId uuid.UUID, examId uuid.UUID, eventId uuid.UUID) ([]entity.EventExamProctoringLogs, error) {
32
+ var logs []entity.EventExamProctoringLogs
33
+ query := r.db.WithContext(ctx)
34
+
35
+ if accountId != uuid.Nil {
36
+ query = query.Where("account_id = ?", accountId)
37
+ }
38
+ if examId != uuid.Nil {
39
+ query = query.Where("exam_id = ?", examId)
40
+ }
41
+ if eventId != uuid.Nil {
42
+ query = query.Where("event_id = ?", eventId)
43
+ }
44
+
45
+ err := query.Find(&logs).Error
46
+ return logs, err
47
+ }
48
+
49
+ func (r *eventExamProctoringRepository) GetById(ctx context.Context, id uuid.UUID) (entity.EventExamProctoringLogs, error) {
50
+ var log entity.EventExamProctoringLogs
51
+ err := r.db.WithContext(ctx).First(&log, "id = ?", id).Error
52
+ return log, err
53
+ }
54
+
55
+ func (r *eventExamProctoringRepository) Update(ctx context.Context, log *entity.EventExamProctoringLogs) error {
56
+ return r.db.WithContext(ctx).Save(log).Error
57
+ }
58
+
59
+ func (r *eventExamProctoringRepository) Delete(ctx context.Context, id uuid.UUID) error {
60
+ return r.db.WithContext(ctx).Delete(&entity.EventExamProctoringLogs{}, "id = ?", id).Error
61
+ }
repositories/exam_academy_answer_repository.go DELETED
@@ -1,55 +0,0 @@
1
- package repositories
2
-
3
- import (
4
- "context"
5
-
6
- entity "abdanhafidz.com/go-boilerplate/models/entity"
7
- "github.com/google/uuid"
8
- "gorm.io/gorm"
9
- )
10
-
11
- type ExamAcademyAnswerRepository interface {
12
- Create(ctx context.Context, ans *entity.ExamAcademyAnswer) error
13
- Update(ctx context.Context, ans *entity.ExamAcademyAnswer) error
14
- GetByAttemptAndQuestion(ctx context.Context, attemptId uuid.UUID, questionId uuid.UUID) (entity.ExamAcademyAnswer, error)
15
- ListByAttempt(ctx context.Context, attemptId uuid.UUID) ([]entity.ExamAcademyAnswer, error)
16
- DeleteByAttempt(ctx context.Context, attemptId uuid.UUID) error
17
- }
18
-
19
- type examAcademyAnswerRepository struct { db *gorm.DB }
20
-
21
- func NewExamAcademyAnswerRepository(db *gorm.DB) ExamAcademyAnswerRepository {
22
- return &examAcademyAnswerRepository{ db: db }
23
- }
24
-
25
- func (r *examAcademyAnswerRepository) Create(ctx context.Context, ans *entity.ExamAcademyAnswer) error {
26
- return r.db.WithContext(ctx).Create(ans).Error
27
- }
28
-
29
- func (r *examAcademyAnswerRepository) Update(ctx context.Context, ans *entity.ExamAcademyAnswer) error {
30
- return r.db.WithContext(ctx).
31
- Where("attempt_id = ? AND question_id = ?", ans.AttemptId, ans.QuestionId).
32
- Updates(ans).Error
33
- }
34
-
35
- func (r *examAcademyAnswerRepository) GetByAttemptAndQuestion(ctx context.Context, attemptId uuid.UUID, questionId uuid.UUID) (entity.ExamAcademyAnswer, error) {
36
- var ans entity.ExamAcademyAnswer
37
- err := r.db.WithContext(ctx).
38
- Where("attempt_id = ? AND question_id = ?", attemptId, questionId).
39
- First(&ans).Error
40
- return ans, err
41
- }
42
-
43
- func (r *examAcademyAnswerRepository) ListByAttempt(ctx context.Context, attemptId uuid.UUID) ([]entity.ExamAcademyAnswer, error) {
44
- var answers []entity.ExamAcademyAnswer
45
- err := r.db.WithContext(ctx).
46
- Where("attempt_id = ?", attemptId).
47
- Find(&answers).Error
48
- return answers, err
49
- }
50
-
51
- func (r *examAcademyAnswerRepository) DeleteByAttempt(ctx context.Context, attemptId uuid.UUID) error {
52
- return r.db.WithContext(ctx).
53
- Where("attempt_id = ?", attemptId).
54
- Delete(&entity.ExamAcademyAnswer{}).Error
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
repositories/exam_academy_assign_repository.go DELETED
@@ -1,48 +0,0 @@
1
- package repositories
2
-
3
- import (
4
- "context"
5
-
6
- entity "abdanhafidz.com/go-boilerplate/models/entity"
7
- "github.com/google/uuid"
8
- "gorm.io/gorm"
9
- )
10
-
11
- type ExamAcademyAssignRepository interface {
12
- Create(ctx context.Context, m entity.ExamAcademyAssign) error
13
- ListByAcademy(ctx context.Context, academyId uuid.UUID) ([]entity.ExamAcademyAssign, error)
14
- Delete(ctx context.Context, id uuid.UUID) error
15
- Check(ctx context.Context, academyId uuid.UUID, examId uuid.UUID) error
16
- }
17
-
18
- type examAcademyAssignRepository struct{ db *gorm.DB }
19
-
20
- func NewExamAcademyAssignRepository(db *gorm.DB) ExamAcademyAssignRepository {
21
- return &examAcademyAssignRepository{db}
22
- }
23
-
24
- func (r *examAcademyAssignRepository) Check(ctx context.Context, academyId uuid.UUID, examId uuid.UUID) error {
25
- return r.db.WithContext(ctx).
26
- Where("academy_id = ?", academyId).
27
- Where("exam_id = ?", examId).
28
- First(&entity.ExamAcademyAssign{}).Error
29
- }
30
-
31
- func (r *examAcademyAssignRepository) Create(ctx context.Context, m entity.ExamAcademyAssign) error {
32
- return r.db.WithContext(ctx).Create(&m).Error
33
- }
34
-
35
- func (r *examAcademyAssignRepository) ListByAcademy(ctx context.Context, academyId uuid.UUID) ([]entity.ExamAcademyAssign, error) {
36
- var items []entity.ExamAcademyAssign
37
- err := r.db.WithContext(ctx).
38
- Where("academy_id = ?", academyId).
39
- Preload("Exam").
40
- Find(&items).Error
41
- return items, err
42
- }
43
-
44
- func (r *examAcademyAssignRepository) Delete(ctx context.Context, id uuid.UUID) error {
45
- return r.db.WithContext(ctx).
46
- Where("id = ?", id).
47
- Delete(&entity.ExamAcademyAssign{}).Error
48
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
repositories/exam_academy_attempt_repository.go DELETED
@@ -1,52 +0,0 @@
1
- package repositories
2
-
3
- import (
4
- "context"
5
-
6
- entity "abdanhafidz.com/go-boilerplate/models/entity"
7
- "github.com/google/uuid"
8
- "gorm.io/gorm"
9
- )
10
-
11
- type ExamAcademyAttemptRepository interface {
12
- Create(ctx context.Context, a *entity.ExamAcademyAttempt) error
13
- GetById(ctx context.Context, attemptId uuid.UUID) (entity.ExamAcademyAttempt, error)
14
- GetByExamAcademy(ctx context.Context, academyId uuid.UUID, examId uuid.UUID, accountId uuid.UUID) (entity.ExamAcademyAttempt, error)
15
- Update(ctx context.Context, a *entity.ExamAcademyAttempt) error
16
- }
17
-
18
- type examAcademyAttemptRepository struct{ db *gorm.DB }
19
-
20
- func NewExamAcademyAttemptRepository(db *gorm.DB) ExamAcademyAttemptRepository {
21
- return &examAcademyAttemptRepository{db}
22
- }
23
-
24
- func (r *examAcademyAttemptRepository) Create(ctx context.Context, a *entity.ExamAcademyAttempt) error {
25
- return r.db.WithContext(ctx).Create(a).Error
26
- }
27
-
28
- func (r *examAcademyAttemptRepository) GetById(ctx context.Context, attemptId uuid.UUID) (entity.ExamAcademyAttempt, error) {
29
- var a entity.ExamAcademyAttempt
30
- err := r.db.WithContext(ctx).
31
- Preload("Answers").
32
- First(&a, "id = ?", attemptId).Error
33
- return a, err
34
- }
35
-
36
- func (r *examAcademyAttemptRepository) GetByExamAcademy(ctx context.Context, academyId uuid.UUID, examId uuid.UUID, accountId uuid.UUID) (entity.ExamAcademyAttempt, error) {
37
- var attempt entity.ExamAcademyAttempt
38
- err := r.db.WithContext(ctx).
39
- Preload("Answers").
40
- Where("academy_id = ?", academyId).
41
- Where("exam_id = ?", examId).
42
- Where("account_id = ?", accountId).
43
- First(&attempt).Error
44
- return attempt, err
45
- }
46
-
47
- func (r *examAcademyAttemptRepository) Update(ctx context.Context, a *entity.ExamAcademyAttempt) error {
48
- return r.db.WithContext(ctx).
49
- Model(&entity.ExamAcademyAttempt{}).
50
- Where("id = ?", a.Id).
51
- Updates(a).Error
52
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
repositories/{exam_event_repository.go → exam_repository.go} RENAMED
@@ -9,12 +9,13 @@ import (
9
  )
10
 
11
  type ExamRepository interface {
12
- Create(ctx context.Context, e entity.Exam) error
13
  Get(ctx context.Context, id uuid.UUID) (entity.Exam, error)
14
  GetBySlug(ctx context.Context, slug string) (entity.Exam, error)
15
  Update(ctx context.Context, e entity.Exam) error
16
  Delete(ctx context.Context, id uuid.UUID) error
17
  List(ctx context.Context) ([]entity.Exam, error)
 
18
 
19
  // Additional business need
20
  ListByEvent(ctx context.Context, eventId uuid.UUID) ([]entity.Exam, error)
@@ -30,8 +31,35 @@ func NewExamRepository(db *gorm.DB) ExamRepository {
30
 
31
  // ================= CRUD =================
32
 
33
- func (r *examRepository) Create(ctx context.Context, e entity.Exam) error {
34
- return r.db.WithContext(ctx).Create(&e).Error
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
36
 
37
  func (r *examRepository) Get(ctx context.Context, id uuid.UUID) (entity.Exam, error) {
@@ -70,6 +98,34 @@ func (r *examRepository) List(ctx context.Context) ([]entity.Exam, error) {
70
  return list, err
71
  }
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  // =========== Business Specific ============
74
 
75
  func (r *examRepository) ListByEvent(ctx context.Context, eventId uuid.UUID) ([]entity.Exam, error) {
 
9
  )
10
 
11
  type ExamRepository interface {
12
+ Create(ctx context.Context, e *entity.Exam) error
13
  Get(ctx context.Context, id uuid.UUID) (entity.Exam, error)
14
  GetBySlug(ctx context.Context, slug string) (entity.Exam, error)
15
  Update(ctx context.Context, e entity.Exam) error
16
  Delete(ctx context.Context, id uuid.UUID) error
17
  List(ctx context.Context) ([]entity.Exam, error)
18
+ ListWithPagination(ctx context.Context, p entity.Pagination) ([]entity.Exam, int64, error)
19
 
20
  // Additional business need
21
  ListByEvent(ctx context.Context, eventId uuid.UUID) ([]entity.Exam, error)
 
31
 
32
  // ================= CRUD =================
33
 
34
+ func (r *examRepository) Create(ctx context.Context, e *entity.Exam) error {
35
+ tx := r.db.WithContext(ctx).Begin()
36
+ if tx.Error != nil {
37
+ return tx.Error
38
+ }
39
+
40
+ // 1. Create Exam
41
+ if err := tx.Create(&e).Error; err != nil {
42
+ tx.Rollback()
43
+ return err
44
+ }
45
+
46
+ // 2. Inject ExamId
47
+ e.Configuration.ExamId = e.Id
48
+ e.Proctoring.ExamId = e.Id
49
+
50
+ // 3. Create Configuration
51
+ if err := tx.Create(&e.Configuration).Error; err != nil {
52
+ tx.Rollback()
53
+ return err
54
+ }
55
+
56
+ // 4. Create Proctoring
57
+ if err := tx.Create(&e.Proctoring).Error; err != nil {
58
+ tx.Rollback()
59
+ return err
60
+ }
61
+
62
+ return tx.Commit().Error
63
  }
64
 
65
  func (r *examRepository) Get(ctx context.Context, id uuid.UUID) (entity.Exam, error) {
 
98
  return list, err
99
  }
100
 
101
+ func (r *examRepository) ListWithPagination(ctx context.Context, p entity.Pagination) ([]entity.Exam, int64, error) {
102
+ var list []entity.Exam
103
+ var total int64
104
+
105
+ db := r.db.WithContext(ctx).Model(&entity.Exam{})
106
+
107
+ if p.Search != "" {
108
+ db = db.Where("title ILIKE ?", "%"+p.Search+"%")
109
+ }
110
+
111
+ if err := db.Count(&total).Error; err != nil {
112
+ return nil, 0, err
113
+ }
114
+
115
+ if p.SortBy != "" {
116
+ order := "ASC"
117
+ if p.Order == "desc" {
118
+ order = "DESC"
119
+ }
120
+ db = db.Order(p.SortBy + " " + order)
121
+ } else {
122
+ db = db.Order("created_at DESC")
123
+ }
124
+
125
+ err := db.Limit(p.Limit).Offset(p.Offset).Find(&list).Error
126
+ return list, total, err
127
+ }
128
+
129
  // =========== Business Specific ============
130
 
131
  func (r *examRepository) ListByEvent(ctx context.Context, eventId uuid.UUID) ([]entity.Exam, error) {
repositories/result_repository.go CHANGED
@@ -28,7 +28,7 @@ func (r *resultRepository) Create(ctx context.Context, rs *entity.Result) error
28
  func (r *resultRepository) GetByAttemptId(ctx context.Context, attemptId uuid.UUID) (entity.Result, error) {
29
  var rs entity.Result
30
  err := r.db.WithContext(ctx).
31
- Preload("ExamEventAttempt").
32
  First(&rs, "attempt_id = ?", attemptId).Error
33
  return rs, err
34
  }
@@ -38,7 +38,7 @@ func (r *resultRepository) GetById(ctx context.Context, id uuid.UUID) (entity.Re
38
  Preload("Account").
39
  Preload("Event").
40
  Preload("ProblemSet").
41
- Preload("ExamEventAttempt").
42
  First(&rs, "id = ?", id).Error
43
  return rs, err
44
  }
 
28
  func (r *resultRepository) GetByAttemptId(ctx context.Context, attemptId uuid.UUID) (entity.Result, error) {
29
  var rs entity.Result
30
  err := r.db.WithContext(ctx).
31
+ Preload("EventExamAttempt").
32
  First(&rs, "attempt_id = ?", attemptId).Error
33
  return rs, err
34
  }
 
38
  Preload("Account").
39
  Preload("Event").
40
  Preload("ProblemSet").
41
+ Preload("EventExamAttempt").
42
  First(&rs, "id = ?", id).Error
43
  return rs, err
44
  }
router/event_exam_proctoring_router.go ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package router
2
+
3
+ import (
4
+ "abdanhafidz.com/go-boilerplate/provider"
5
+ "github.com/gin-gonic/gin"
6
+ )
7
+
8
+ func EventExamProctoringRouter(router *gin.Engine, middleware provider.MiddlewareProvider, controller provider.ControllerProvider) {
9
+ proctoringController := controller.ProvideEventExamProctoringController()
10
+ auth := middleware.ProvideAuthenticationMiddleware()
11
+
12
+ // Group under api/v1/events to match existing event structure
13
+ routerGroup := router.Group("api/v1/events")
14
+ {
15
+ // :event_slug and :exam_slug are kept for context/consistency with other routes,
16
+ // even if not strictly used for lookup (IDs are in body/query).
17
+
18
+ routerGroup.POST("/:event_slug/exam/:exam_slug/proctoring", auth.VerifyAccount, proctoringController.CreateLog)
19
+ routerGroup.GET("/:event_slug/exam/:exam_slug/proctoring", auth.VerifyAccount, proctoringController.ListLogs)
20
+ routerGroup.GET("/:event_slug/exam/:exam_slug/proctoring/:log_id", auth.VerifyAccount, proctoringController.GetLogById)
21
+ routerGroup.PUT("/:event_slug/exam/:exam_slug/proctoring/:log_id", auth.VerifyAccount, proctoringController.UpdateLog)
22
+ routerGroup.DELETE("/:event_slug/exam/:exam_slug/proctoring/:log_id", auth.VerifyAccount, proctoringController.DeleteLog)
23
+ }
24
+ }
router/{exam_event_router.go → event_exam_router.go} RENAMED
@@ -5,14 +5,14 @@ import (
5
  "github.com/gin-gonic/gin"
6
  )
7
 
8
- func ExamEventRouter(router *gin.Engine, middleware provider.MiddlewareProvider, controller provider.ControllerProvider) {
9
- examController := controller.ProvideExamController()
10
  auth := middleware.ProvideAuthenticationMiddleware()
11
  routerGroup := router.Group("api/v1/events")
12
  {
13
- routerGroup.GET("/:event_slug/exam", auth.VerifyAccount, examController.List)
14
- routerGroup.GET("/:event_slug/exam/:exam_slug/attempt", auth.VerifyAccount, examController.Attempt)
15
- routerGroup.POST("/:event_slug/exam/:attempt_id/answer_question", auth.VerifyAccount, examController.Answer)
16
- routerGroup.POST("/:event_slug/exam/:attempt_id/submit", auth.VerifyAccount, examController.Submit)
17
  }
18
  }
 
5
  "github.com/gin-gonic/gin"
6
  )
7
 
8
+ func EventExamRouter(router *gin.Engine, middleware provider.MiddlewareProvider, controller provider.ControllerProvider) {
9
+ eventExamController := controller.ProvideEventExamController()
10
  auth := middleware.ProvideAuthenticationMiddleware()
11
  routerGroup := router.Group("api/v1/events")
12
  {
13
+ routerGroup.GET("/:event_slug/exam", auth.VerifyAccount, eventExamController.List)
14
+ routerGroup.GET("/:event_slug/exam/:exam_slug/attempt", auth.VerifyAccount, eventExamController.Attempt)
15
+ routerGroup.POST("/:event_slug/exam/:attempt_id/answer_question", auth.VerifyAccount, eventExamController.Answer)
16
+ routerGroup.POST("/:event_slug/exam/:attempt_id/submit", auth.VerifyAccount, eventExamController.Submit)
17
  }
18
  }
router/exam_router.go ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package router
2
+
3
+ import (
4
+ "abdanhafidz.com/go-boilerplate/provider"
5
+ "github.com/gin-gonic/gin"
6
+ )
7
+
8
+ func ExamRouter(router *gin.Engine, middleware provider.MiddlewareProvider, controller provider.ControllerProvider) {
9
+ examCtrl := controller.ProvideExamController()
10
+ authMiddleware := middleware.ProvideAuthenticationMiddleware()
11
+
12
+ v1 := router.Group("/api/v1")
13
+ {
14
+ admin := v1.Group("/admin")
15
+ admin.Use(authMiddleware.VerifyAccount) // Assuming VerifyAccount or similar check if admin constraint needed.
16
+ // Note: Code snippet for admin_router.go used RolesEnum.Admin. Let's assume Middleware works as in my previous artifact or check admin_router usage.
17
+ // Checking admin_router.go usage in router.go: AdminRouter(router, middleware, controller).
18
+ // My previous artifact used `admin.Use(Middleware(entity.RolesEnum.Admin))`.
19
+ // existing router/academy_router.go uses `authenticationMiddleware.VerifyAccount`.
20
+ // Let's check if there is a role based middleware.
21
+ // `ProvideAuthenticationMiddleware` likely gives `VerifyAccount`.
22
+ // existing `router/admin_router.go` probably has `VerifyAdmin` or similar?
23
+ // I'll stick to VerifyAccount for now or check admin_router.go.
24
+ // BUT the user asked for "router".
25
+ // Let's assume `VerifyAccount` is enough for now or use `VerifyAccount` + Role check if I see how used.
26
+ // Actually, `admin_router.go` usage would be helpful.
27
+ // I will use `VerifyAccount` for now as in AcademyRouter.
28
+ // Wait, `Exam` operations seem admin related (`/api/v1/admin/exam`).
29
+
30
+ // Let's try to match strict pattern.
31
+ // If I use `authenticationMiddleware.VerifyAccount`, it's safe.
32
+
33
+ admin.Use(authMiddleware.VerifyAccount)
34
+ {
35
+ exam := admin.Group("/exam")
36
+ {
37
+ exam.POST("", examCtrl.CreateExam)
38
+ exam.GET("", examCtrl.ListExam)
39
+ exam.PUT("/:id", examCtrl.UpdateExam)
40
+ exam.DELETE("/:id", examCtrl.DeleteExam)
41
+ exam.GET("/:id", examCtrl.GetExamDetail)
42
+
43
+ exam.POST("/:exam_id/event/:event_id", examCtrl.AssignExamToEvent)
44
+ exam.POST("/:exam_id/academy/:academy_id", examCtrl.AssignExamToAcademy)
45
+ }
46
+ }
47
+ }
48
+ }
router/router.go CHANGED
@@ -30,10 +30,12 @@ func RunRouter(appProvider provider.AppProvider) {
30
  EventRouter(router, middleware, controller)
31
  OptionsRouter(router, controller)
32
  AcademyRouter(router, middleware, controller)
33
- ExamEventRouter(router, middleware, controller)
 
34
  AcademyExamRouter(router, middleware, controller)
35
  UploadRouter(router, middleware, controller)
36
  AdminRouter(router, middleware, controller)
 
37
  SwaggerRouter(router)
38
  router.Run(config.ProvideEnvConfig().GetTCPAddress())
39
  }
 
30
  EventRouter(router, middleware, controller)
31
  OptionsRouter(router, controller)
32
  AcademyRouter(router, middleware, controller)
33
+ EventExamRouter(router, middleware, controller)
34
+ EventExamProctoringRouter(router, middleware, controller)
35
  AcademyExamRouter(router, middleware, controller)
36
  UploadRouter(router, middleware, controller)
37
  AdminRouter(router, middleware, controller)
38
+ ExamRouter(router, middleware, controller)
39
  SwaggerRouter(router)
40
  router.Run(config.ProvideEnvConfig().GetTCPAddress())
41
  }
services/{exam_academy_service.go → academy_exam_service.go} RENAMED
@@ -1,279 +1,279 @@
1
- package services
2
-
3
- import (
4
- "context"
5
- "errors"
6
- "fmt"
7
- "time"
8
-
9
- "abdanhafidz.com/go-boilerplate/models/dto"
10
- entity "abdanhafidz.com/go-boilerplate/models/entity"
11
- http_error "abdanhafidz.com/go-boilerplate/models/error"
12
- "abdanhafidz.com/go-boilerplate/repositories"
13
- "abdanhafidz.com/go-boilerplate/utils"
14
- "github.com/google/uuid"
15
- "gorm.io/gorm"
16
- )
17
-
18
- type AcademyExamService interface {
19
- ListExamByAcademy(ctx context.Context, academySlug string, accountId uuid.UUID) ([]entity.Exam, error)
20
- GetAcademyExamExisting(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (entity.Academy, entity.Exam, error)
21
- GetExamAcademyAttempt(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (dto.UserExamStatus, entity.ExamAcademyAttempt, error)
22
- AttemptExamAcademy(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (entity.ExamAcademyAttempt, error)
23
- SetupQuestions(ctx context.Context, academySlug string, examId uuid.UUID, accountId uuid.UUID) ([]entity.Questions, error)
24
- SetupAnswer(ctx context.Context, questions []entity.Questions, attemptId uuid.UUID) ([]entity.ExamAcademyAnswer, error)
25
- SetupExamTimer(ctx context.Context, exam entity.Exam) (time.Time, time.Time)
26
- SubmitExamAcademy(ctx context.Context, attemptId uuid.UUID) (entity.ExamAcademyResult, error)
27
- AnswerExamAcademy(ctx context.Context, academySlug string, attemptId uuid.UUID, questionId uuid.UUID, answer []string) (entity.CPQuestionVerdict, error)
28
- }
29
-
30
- type academyExamService struct {
31
- academyService AcademyService
32
- problemSetService ProblemSetService
33
- examRepo repositories.ExamRepository
34
- examAcademyAttemptRepo repositories.ExamAcademyAttemptRepository
35
- examAcademyAnswerRepo repositories.ExamAcademyAnswerRepository
36
- examAcademyAssignRepo repositories.ExamAcademyAssignRepository
37
- academyResultRepo repositories.AcademyResultRepository
38
- }
39
-
40
- func NewAcademyExamService(academyService AcademyService, problemSetService ProblemSetService, examRepo repositories.ExamRepository, examAcademyAttemptRepo repositories.ExamAcademyAttemptRepository, examAcademyAssignRepo repositories.ExamAcademyAssignRepository, examAcademyAnswerRepo repositories.ExamAcademyAnswerRepository, academyResultRepo repositories.AcademyResultRepository) AcademyExamService {
41
- return &academyExamService{
42
- academyService: academyService,
43
- problemSetService: problemSetService,
44
- examRepo: examRepo,
45
- examAcademyAttemptRepo: examAcademyAttemptRepo,
46
- examAcademyAssignRepo: examAcademyAssignRepo,
47
- examAcademyAnswerRepo: examAcademyAnswerRepo,
48
- academyResultRepo: academyResultRepo,
49
- }
50
- }
51
-
52
- func (s *academyExamService) ListExamByAcademy(ctx context.Context, academySlug string, accountId uuid.UUID) ([]entity.Exam, error) {
53
- academy, err := s.academyService.GetAcademy(ctx, accountId, academySlug)
54
- if err != nil {
55
- return []entity.Exam{}, err
56
- }
57
- assigns, err := s.examAcademyAssignRepo.ListByAcademy(ctx, academy.Id)
58
- if err != nil {
59
- return []entity.Exam{}, err
60
- }
61
- var exams []entity.Exam
62
- for _, a := range assigns {
63
- exams = append(exams, *a.Exam)
64
- }
65
- return exams, nil
66
- }
67
-
68
- func (s *academyExamService) GetAcademyExamExisting(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (entity.Academy, entity.Exam, error) {
69
- academy, err := s.academyService.GetAcademy(ctx, accountId, academySlug)
70
- if err != nil {
71
- return entity.Academy{}, entity.Exam{}, err
72
- }
73
- exam, err := s.examRepo.GetBySlug(ctx, examSlug)
74
- if err != nil {
75
- return entity.Academy{}, entity.Exam{}, err
76
- }
77
- if err := s.examAcademyAssignRepo.Check(ctx, academy.Id, exam.Id); err != nil {
78
- return entity.Academy{}, entity.Exam{}, err
79
- }
80
- return academy, exam, nil
81
- }
82
-
83
- func (s *academyExamService) GetExamAcademyAttempt(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (dto.UserExamStatus, entity.ExamAcademyAttempt, error) {
84
- academy, exam, err := s.GetAcademyExamExisting(ctx, academySlug, examSlug, accountId)
85
- if err != nil {
86
- return dto.UserExamStatus{}, entity.ExamAcademyAttempt{}, err
87
- }
88
- attempt, err := s.examAcademyAttemptRepo.GetByExamAcademy(ctx, academy.Id, exam.Id, accountId)
89
- if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
90
- return dto.UserExamStatus{}, entity.ExamAcademyAttempt{}, err
91
- }
92
- var status dto.UserExamStatus
93
- status.IsNotAttempt = errors.Is(err, gorm.ErrRecordNotFound)
94
- status.IsTimeOut = (utils.CalculateRemainingTime(attempt.CreatedAt, attempt.DueAt) == 0) || false
95
- status.IsSubmitted = attempt.Submitted
96
- status.IsOnAttempt = !status.IsNotAttempt && !status.IsTimeOut && !status.IsSubmitted
97
- return status, attempt, nil
98
- }
99
-
100
- func (s *academyExamService) AttemptExamAcademy(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (entity.ExamAcademyAttempt, error) {
101
- academy, exam, err := s.GetAcademyExamExisting(ctx, academySlug, examSlug, accountId)
102
- if err != nil {
103
- return entity.ExamAcademyAttempt{}, err
104
- }
105
- status, attempt, err := s.GetExamAcademyAttempt(ctx, academySlug, examSlug, accountId)
106
- if err != nil {
107
- return entity.ExamAcademyAttempt{}, err
108
- }
109
- questions, err := s.SetupQuestions(ctx, academySlug, exam.Id, accountId)
110
- attempt.Questions = questions
111
- if err != nil {
112
- return entity.ExamAcademyAttempt{}, err
113
- }
114
- if status.IsNotAttempt {
115
- startTime, dueTime := s.SetupExamTimer(ctx, exam)
116
- remTime := utils.CalculateRemainingTime(startTime, dueTime)
117
- attempt = entity.ExamAcademyAttempt{
118
- AccountId: accountId,
119
- AcademyId: academy.Id,
120
- ExamId: exam.Id,
121
- CreatedAt: startTime,
122
- DueAt: dueTime,
123
- Submitted: false,
124
- RemTime: remTime,
125
- Questions: questions,
126
- }
127
- if err := s.examAcademyAttemptRepo.Create(ctx, &attempt); err != nil {
128
- return entity.ExamAcademyAttempt{}, err
129
- }
130
- answers, err := s.SetupAnswer(ctx, questions, attempt.Id)
131
- if err != nil {
132
- return entity.ExamAcademyAttempt{}, err
133
- }
134
- attempt.Answers = answers
135
- return ProtectExamAcademyAttempt(attempt), nil
136
- }
137
- return ProtectExamAcademyAttempt(attempt), nil
138
- }
139
-
140
- func (s *academyExamService) SetupQuestions(ctx context.Context, academySlug string, examId uuid.UUID, accountId uuid.UUID) ([]entity.Questions, error) {
141
- qs, err := s.problemSetService.ListQuestionsByExam(ctx, examId)
142
- if err != nil {
143
- return []entity.Questions{}, err
144
- }
145
- return qs, nil
146
- }
147
-
148
- func (s *academyExamService) SetupAnswer(ctx context.Context, questions []entity.Questions, attemptId uuid.UUID) ([]entity.ExamAcademyAnswer, error) {
149
- var answers []entity.ExamAcademyAnswer
150
- for _, q := range questions {
151
- ans := entity.ExamAcademyAnswer{Id: uuid.New(), AttemptId: attemptId, QuestionId: q.Id, Score: 0}
152
- if err := s.examAcademyAnswerRepo.Create(ctx, &ans); err != nil {
153
- return []entity.ExamAcademyAnswer{}, err
154
- }
155
- answers = append(answers, ans)
156
- }
157
- return answers, nil
158
- }
159
-
160
- func (s *academyExamService) SetupExamTimer(ctx context.Context, exam entity.Exam) (time.Time, time.Time) {
161
- start := time.Now()
162
- due := start.Add(exam.Duration * time.Minute)
163
- return start, due
164
- }
165
-
166
- func (s *academyExamService) SubmitExamAcademy(ctx context.Context, attemptId uuid.UUID) (entity.ExamAcademyResult, error) {
167
- attempt, err := s.examAcademyAttemptRepo.GetById(ctx, attemptId)
168
- if err != nil {
169
- return entity.ExamAcademyResult{}, err
170
- }
171
- if attempt.Submitted {
172
- return entity.ExamAcademyResult{}, http_error.EXAMS_SUBMITTED
173
- }
174
- answers, err := s.examAcademyAnswerRepo.ListByAttempt(ctx, attemptId)
175
- if err != nil {
176
- return entity.ExamAcademyResult{}, err
177
- }
178
- var sum float32
179
- for _, a := range answers {
180
- sum += a.Score
181
- }
182
- rec := entity.ExamAcademyResult{Id: uuid.New(), AttemptId: attemptId, FinalScore: sum}
183
- if err := s.academyResultRepo.Create(ctx, &rec); err != nil {
184
- return entity.ExamAcademyResult{}, err
185
- }
186
- attempt.Submitted = true
187
- if err := s.examAcademyAttemptRepo.Update(ctx, &attempt); err != nil {
188
- return entity.ExamAcademyResult{}, err
189
- }
190
- return rec, nil
191
- }
192
-
193
- func (s *academyExamService) AnswerExamAcademy(ctx context.Context, academySlug string, attemptId uuid.UUID, questionId uuid.UUID, answer []string) (entity.CPQuestionVerdict, error) {
194
- attempt, err := s.examAcademyAttemptRepo.GetById(ctx, attemptId)
195
- if err != nil {
196
- return entity.CPQuestionVerdict{}, err
197
- }
198
- if attempt.Submitted {
199
- return entity.CPQuestionVerdict{}, http_error.EXAMS_SUBMITTED
200
- }
201
- if utils.CalculateRemainingTime(attempt.CreatedAt, attempt.DueAt) == 0 || time.Now().After(attempt.DueAt) {
202
- return entity.CPQuestionVerdict{}, http_error.EXAMS_TIME_EXCEEDED
203
- }
204
- question, err := s.problemSetService.GetQuestionById(ctx, questionId)
205
- if err != nil {
206
- return entity.CPQuestionVerdict{}, err
207
- }
208
- score, verdict := s.EvaluateAnswer(ctx, question)(answer)
209
- err = s.examAcademyAnswerRepo.Update(ctx, &entity.ExamAcademyAnswer{AttemptId: attemptId, QuestionId: questionId, Answers: answer, Score: score})
210
- return verdict, err
211
- }
212
-
213
- func (s *academyExamService) EvaluateAnswer(ctx context.Context, question entity.Questions) evaluator {
214
-
215
- nonCPEvaluator := func(answer []string) (float32, entity.CPQuestionVerdict) {
216
- score := float32(0)
217
- isCorrect := true
218
- for i, ans := range answer {
219
- fmt.Println("User Answer :", ans)
220
- fmt.Println("Answer Key :", question.AnsKey[i])
221
- if ans != question.AnsKey[i] && ans != "" {
222
- score += float32(question.IncorrMark)
223
- isCorrect = false
224
- break
225
- } else if ans == "" {
226
- score += float32(question.NullMark)
227
- isCorrect = false
228
- break
229
- }
230
- }
231
-
232
- if isCorrect {
233
- score += float32(question.CorrMark)
234
- }
235
-
236
- return score, entity.CPQuestionVerdict{}
237
- }
238
-
239
- CPEvaluator := func(answer []string) (float32, entity.CPQuestionVerdict) {
240
- return 0, entity.CPQuestionVerdict{
241
- TimeExecution: 0.01,
242
- MemoryUsage: 256.0,
243
- Verdict: "AC",
244
- Score: 100.0,
245
- }
246
- }
247
-
248
- var examEvaluator = map[string]evaluator{
249
- "multiple_choices": nonCPEvaluator,
250
- "multiple_choices_complex": nonCPEvaluator,
251
- "short_answer": nonCPEvaluator,
252
- "true_false": nonCPEvaluator,
253
- "code_puzzle": nonCPEvaluator,
254
- "code_type": nonCPEvaluator,
255
- "competitive_programming": CPEvaluator,
256
- }
257
-
258
- return examEvaluator[question.Type]
259
- }
260
- func ProtectExamAcademyAttempt(attempt entity.ExamAcademyAttempt) entity.ExamAcademyAttempt {
261
- var cleanQuestions []entity.Questions
262
- for _, q := range attempt.Questions {
263
- qc := q
264
- qc.AnsKey = nil
265
- qc.CorrMark = 0
266
- qc.IncorrMark = 0
267
- qc.NullMark = 0
268
- cleanQuestions = append(cleanQuestions, qc)
269
- }
270
- attempt.Questions = cleanQuestions
271
- var cleanAnswers []entity.ExamAcademyAnswer
272
- for _, a := range attempt.Answers {
273
- ac := a
274
- ac.Score = 0
275
- cleanAnswers = append(cleanAnswers, ac)
276
- }
277
- attempt.Answers = cleanAnswers
278
- return attempt
279
- }
 
1
+ package services
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+ "fmt"
7
+ "time"
8
+
9
+ "abdanhafidz.com/go-boilerplate/models/dto"
10
+ entity "abdanhafidz.com/go-boilerplate/models/entity"
11
+ http_error "abdanhafidz.com/go-boilerplate/models/error"
12
+ "abdanhafidz.com/go-boilerplate/repositories"
13
+ "abdanhafidz.com/go-boilerplate/utils"
14
+ "github.com/google/uuid"
15
+ "gorm.io/gorm"
16
+ )
17
+
18
+ type AcademyExamService interface {
19
+ ListExamByAcademy(ctx context.Context, academySlug string, accountId uuid.UUID) ([]entity.Exam, error)
20
+ GetAcademyExamExisting(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (entity.Academy, entity.Exam, error)
21
+ GetAcademyExamAttempt(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (dto.UserExamStatus, entity.AcademyExamAttempt, error)
22
+ AttemptAcademyExam(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (entity.AcademyExamAttempt, error)
23
+ SetupQuestions(ctx context.Context, academySlug string, examId uuid.UUID, accountId uuid.UUID) ([]entity.Questions, error)
24
+ SetupAnswer(ctx context.Context, questions []entity.Questions, attemptId uuid.UUID) ([]entity.AcademyExamAnswer, error)
25
+ SetupExamTimer(ctx context.Context, exam entity.Exam) (time.Time, time.Time)
26
+ SubmitAcademyExam(ctx context.Context, attemptId uuid.UUID) (entity.AcademyExamResult, error)
27
+ AnswerAcademyExam(ctx context.Context, academySlug string, attemptId uuid.UUID, questionId uuid.UUID, answer []string) (entity.CPQuestionVerdict, error)
28
+ }
29
+
30
+ type academyExamService struct {
31
+ academyService AcademyService
32
+ problemSetService ProblemSetService
33
+ examRepo repositories.ExamRepository
34
+ academyExamAttemptRepo repositories.AcademyExamAttemptRepository
35
+ academyExamAnswerRepo repositories.AcademyExamAnswerRepository
36
+ academyExamAssignRepo repositories.AcademyExamAssignRepository
37
+ academyResultRepo repositories.AcademyResultRepository
38
+ }
39
+
40
+ func NewAcademyExamService(academyService AcademyService, problemSetService ProblemSetService, examRepo repositories.ExamRepository, academyExamAttemptRepo repositories.AcademyExamAttemptRepository, academyExamAssignRepo repositories.AcademyExamAssignRepository, academyExamAnswerRepo repositories.AcademyExamAnswerRepository, academyResultRepo repositories.AcademyResultRepository) AcademyExamService {
41
+ return &academyExamService{
42
+ academyService: academyService,
43
+ problemSetService: problemSetService,
44
+ examRepo: examRepo,
45
+ academyExamAttemptRepo: academyExamAttemptRepo,
46
+ academyExamAssignRepo: academyExamAssignRepo,
47
+ academyExamAnswerRepo: academyExamAnswerRepo,
48
+ academyResultRepo: academyResultRepo,
49
+ }
50
+ }
51
+
52
+ func (s *academyExamService) ListExamByAcademy(ctx context.Context, academySlug string, accountId uuid.UUID) ([]entity.Exam, error) {
53
+ academy, err := s.academyService.GetAcademy(ctx, accountId, academySlug)
54
+ if err != nil {
55
+ return []entity.Exam{}, err
56
+ }
57
+ assigns, err := s.academyExamAssignRepo.ListByAcademy(ctx, academy.Id)
58
+ if err != nil {
59
+ return []entity.Exam{}, err
60
+ }
61
+ var exams []entity.Exam
62
+ for _, a := range assigns {
63
+ exams = append(exams, *a.Exam)
64
+ }
65
+ return exams, nil
66
+ }
67
+
68
+ func (s *academyExamService) GetAcademyExamExisting(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (entity.Academy, entity.Exam, error) {
69
+ academy, err := s.academyService.GetAcademy(ctx, accountId, academySlug)
70
+ if err != nil {
71
+ return entity.Academy{}, entity.Exam{}, err
72
+ }
73
+ exam, err := s.examRepo.GetBySlug(ctx, examSlug)
74
+ if err != nil {
75
+ return entity.Academy{}, entity.Exam{}, err
76
+ }
77
+ if err := s.academyExamAssignRepo.Check(ctx, academy.Id, exam.Id); err != nil {
78
+ return entity.Academy{}, entity.Exam{}, err
79
+ }
80
+ return academy, exam, nil
81
+ }
82
+
83
+ func (s *academyExamService) GetAcademyExamAttempt(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (dto.UserExamStatus, entity.AcademyExamAttempt, error) {
84
+ academy, exam, err := s.GetAcademyExamExisting(ctx, academySlug, examSlug, accountId)
85
+ if err != nil {
86
+ return dto.UserExamStatus{}, entity.AcademyExamAttempt{}, err
87
+ }
88
+ attempt, err := s.academyExamAttemptRepo.GetByAcademyExam(ctx, academy.Id, exam.Id, accountId)
89
+ if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
90
+ return dto.UserExamStatus{}, entity.AcademyExamAttempt{}, err
91
+ }
92
+ var status dto.UserExamStatus
93
+ status.IsNotAttempt = errors.Is(err, gorm.ErrRecordNotFound)
94
+ status.IsTimeOut = (utils.CalculateRemainingTime(attempt.CreatedAt, attempt.DueAt) == 0) || false
95
+ status.IsSubmitted = attempt.Submitted
96
+ status.IsOnAttempt = !status.IsNotAttempt && !status.IsTimeOut && !status.IsSubmitted
97
+ return status, attempt, nil
98
+ }
99
+
100
+ func (s *academyExamService) AttemptAcademyExam(ctx context.Context, academySlug string, examSlug string, accountId uuid.UUID) (entity.AcademyExamAttempt, error) {
101
+ academy, exam, err := s.GetAcademyExamExisting(ctx, academySlug, examSlug, accountId)
102
+ if err != nil {
103
+ return entity.AcademyExamAttempt{}, err
104
+ }
105
+ status, attempt, err := s.GetAcademyExamAttempt(ctx, academySlug, examSlug, accountId)
106
+ if err != nil {
107
+ return entity.AcademyExamAttempt{}, err
108
+ }
109
+ questions, err := s.SetupQuestions(ctx, academySlug, exam.Id, accountId)
110
+ attempt.Questions = questions
111
+ if err != nil {
112
+ return entity.AcademyExamAttempt{}, err
113
+ }
114
+ if status.IsNotAttempt {
115
+ startTime, dueTime := s.SetupExamTimer(ctx, exam)
116
+ remTime := utils.CalculateRemainingTime(startTime, dueTime)
117
+ attempt = entity.AcademyExamAttempt{
118
+ AccountId: accountId,
119
+ AcademyId: academy.Id,
120
+ ExamId: exam.Id,
121
+ CreatedAt: startTime,
122
+ DueAt: dueTime,
123
+ Submitted: false,
124
+ RemTime: remTime,
125
+ Questions: questions,
126
+ }
127
+ if err := s.academyExamAttemptRepo.Create(ctx, &attempt); err != nil {
128
+ return entity.AcademyExamAttempt{}, err
129
+ }
130
+ answers, err := s.SetupAnswer(ctx, questions, attempt.Id)
131
+ if err != nil {
132
+ return entity.AcademyExamAttempt{}, err
133
+ }
134
+ attempt.Answers = answers
135
+ return ProtectAcademyExamAttempt(attempt), nil
136
+ }
137
+ return ProtectAcademyExamAttempt(attempt), nil
138
+ }
139
+
140
+ func (s *academyExamService) SetupQuestions(ctx context.Context, academySlug string, examId uuid.UUID, accountId uuid.UUID) ([]entity.Questions, error) {
141
+ qs, err := s.problemSetService.ListQuestionsByExam(ctx, examId)
142
+ if err != nil {
143
+ return []entity.Questions{}, err
144
+ }
145
+ return qs, nil
146
+ }
147
+
148
+ func (s *academyExamService) SetupAnswer(ctx context.Context, questions []entity.Questions, attemptId uuid.UUID) ([]entity.AcademyExamAnswer, error) {
149
+ var answers []entity.AcademyExamAnswer
150
+ for _, q := range questions {
151
+ ans := entity.AcademyExamAnswer{Id: uuid.New(), AttemptId: attemptId, QuestionId: q.Id, Score: 0}
152
+ if err := s.academyExamAnswerRepo.Create(ctx, &ans); err != nil {
153
+ return []entity.AcademyExamAnswer{}, err
154
+ }
155
+ answers = append(answers, ans)
156
+ }
157
+ return answers, nil
158
+ }
159
+
160
+ func (s *academyExamService) SetupExamTimer(ctx context.Context, exam entity.Exam) (time.Time, time.Time) {
161
+ start := time.Now()
162
+ due := start.Add(exam.Duration * time.Minute)
163
+ return start, due
164
+ }
165
+
166
+ func (s *academyExamService) SubmitAcademyExam(ctx context.Context, attemptId uuid.UUID) (entity.AcademyExamResult, error) {
167
+ attempt, err := s.academyExamAttemptRepo.GetById(ctx, attemptId)
168
+ if err != nil {
169
+ return entity.AcademyExamResult{}, err
170
+ }
171
+ if attempt.Submitted {
172
+ return entity.AcademyExamResult{}, http_error.EXAMS_SUBMITTED
173
+ }
174
+ answers, err := s.academyExamAnswerRepo.ListByAttempt(ctx, attemptId)
175
+ if err != nil {
176
+ return entity.AcademyExamResult{}, err
177
+ }
178
+ var sum float32
179
+ for _, a := range answers {
180
+ sum += a.Score
181
+ }
182
+ rec := entity.AcademyExamResult{Id: uuid.New(), AttemptId: attemptId, FinalScore: sum}
183
+ if err := s.academyResultRepo.Create(ctx, &rec); err != nil {
184
+ return entity.AcademyExamResult{}, err
185
+ }
186
+ attempt.Submitted = true
187
+ if err := s.academyExamAttemptRepo.Update(ctx, &attempt); err != nil {
188
+ return entity.AcademyExamResult{}, err
189
+ }
190
+ return rec, nil
191
+ }
192
+
193
+ func (s *academyExamService) AnswerAcademyExam(ctx context.Context, academySlug string, attemptId uuid.UUID, questionId uuid.UUID, answer []string) (entity.CPQuestionVerdict, error) {
194
+ attempt, err := s.academyExamAttemptRepo.GetById(ctx, attemptId)
195
+ if err != nil {
196
+ return entity.CPQuestionVerdict{}, err
197
+ }
198
+ if attempt.Submitted {
199
+ return entity.CPQuestionVerdict{}, http_error.EXAMS_SUBMITTED
200
+ }
201
+ if utils.CalculateRemainingTime(attempt.CreatedAt, attempt.DueAt) == 0 || time.Now().After(attempt.DueAt) {
202
+ return entity.CPQuestionVerdict{}, http_error.EXAMS_TIME_EXCEEDED
203
+ }
204
+ question, err := s.problemSetService.GetQuestionById(ctx, questionId)
205
+ if err != nil {
206
+ return entity.CPQuestionVerdict{}, err
207
+ }
208
+ score, verdict := s.EvaluateAnswer(ctx, question)(answer)
209
+ err = s.academyExamAnswerRepo.Update(ctx, &entity.AcademyExamAnswer{AttemptId: attemptId, QuestionId: questionId, Answers: answer, Score: score})
210
+ return verdict, err
211
+ }
212
+
213
+ func (s *academyExamService) EvaluateAnswer(ctx context.Context, question entity.Questions) evaluator {
214
+
215
+ nonCPEvaluator := func(answer []string) (float32, entity.CPQuestionVerdict) {
216
+ score := float32(0)
217
+ isCorrect := true
218
+ for i, ans := range answer {
219
+ fmt.Println("User Answer :", ans)
220
+ fmt.Println("Answer Key :", question.AnsKey[i])
221
+ if ans != question.AnsKey[i] && ans != "" {
222
+ score += float32(question.IncorrMark)
223
+ isCorrect = false
224
+ break
225
+ } else if ans == "" {
226
+ score += float32(question.NullMark)
227
+ isCorrect = false
228
+ break
229
+ }
230
+ }
231
+
232
+ if isCorrect {
233
+ score += float32(question.CorrMark)
234
+ }
235
+
236
+ return score, entity.CPQuestionVerdict{}
237
+ }
238
+
239
+ CPEvaluator := func(answer []string) (float32, entity.CPQuestionVerdict) {
240
+ return 0, entity.CPQuestionVerdict{
241
+ TimeExecution: 0.01,
242
+ MemoryUsage: 256.0,
243
+ Verdict: "AC",
244
+ Score: 100.0,
245
+ }
246
+ }
247
+
248
+ var examEvaluator = map[string]evaluator{
249
+ "multiple_choices": nonCPEvaluator,
250
+ "multiple_choices_complex": nonCPEvaluator,
251
+ "short_answer": nonCPEvaluator,
252
+ "true_false": nonCPEvaluator,
253
+ "code_puzzle": nonCPEvaluator,
254
+ "code_type": nonCPEvaluator,
255
+ "competitive_programming": CPEvaluator,
256
+ }
257
+
258
+ return examEvaluator[question.Type]
259
+ }
260
+ func ProtectAcademyExamAttempt(attempt entity.AcademyExamAttempt) entity.AcademyExamAttempt {
261
+ var cleanQuestions []entity.Questions
262
+ for _, q := range attempt.Questions {
263
+ qc := q
264
+ qc.AnsKey = nil
265
+ qc.CorrMark = 0
266
+ qc.IncorrMark = 0
267
+ qc.NullMark = 0
268
+ cleanQuestions = append(cleanQuestions, qc)
269
+ }
270
+ attempt.Questions = cleanQuestions
271
+ var cleanAnswers []entity.AcademyExamAnswer
272
+ for _, a := range attempt.Answers {
273
+ ac := a
274
+ ac.Score = 0
275
+ cleanAnswers = append(cleanAnswers, ac)
276
+ }
277
+ attempt.Answers = cleanAnswers
278
+ return attempt
279
+ }
services/academy_service.go CHANGED
@@ -205,6 +205,7 @@ func (s *academyService) CreateAcademy(ctx context.Context, req dto.CreateAcadem
205
  if strings.TrimSpace(req.ImageUrl) == "" {
206
  return entity.Academy{}, http_error.IMAGE_REQUIRED
207
  }
 
208
  if err := utils.ValidateCode(req.Code); err != nil {
209
  return entity.Academy{}, err
210
  }
 
205
  if strings.TrimSpace(req.ImageUrl) == "" {
206
  return entity.Academy{}, http_error.IMAGE_REQUIRED
207
  }
208
+
209
  if err := utils.ValidateCode(req.Code); err != nil {
210
  return entity.Academy{}, err
211
  }
services/email_verification_service.go CHANGED
@@ -29,12 +29,15 @@ func NewEmailVerificationService(accountService AccountService, emailVerificatio
29
 
30
  func (s *emailVerificationService) CreateToken(ctx context.Context, email string, token uint, due time.Time) (entity.EmailVerification, error) {
31
  acc, err := s.accountService.GetByEmail(ctx, email)
 
32
  if err != nil {
33
  return entity.EmailVerification{}, err
34
  }
 
35
  if due.IsZero() {
36
  due = time.Now().Add(15 * time.Minute)
37
  }
 
38
  ev := entity.EmailVerification{AccountId: acc.Id, Token: token, IsExpired: false, CreatedAt: time.Now(), ExpiredAt: due}
39
  return s.emailVerificationRepo.Create(ctx, ev)
40
  }
 
29
 
30
  func (s *emailVerificationService) CreateToken(ctx context.Context, email string, token uint, due time.Time) (entity.EmailVerification, error) {
31
  acc, err := s.accountService.GetByEmail(ctx, email)
32
+
33
  if err != nil {
34
  return entity.EmailVerification{}, err
35
  }
36
+
37
  if due.IsZero() {
38
  due = time.Now().Add(15 * time.Minute)
39
  }
40
+
41
  ev := entity.EmailVerification{AccountId: acc.Id, Token: token, IsExpired: false, CreatedAt: time.Now(), ExpiredAt: due}
42
  return s.emailVerificationRepo.Create(ctx, ev)
43
  }
services/event_exam_proctoring_service.go ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "context"
5
+ "mime/multipart"
6
+ "time"
7
+
8
+ "abdanhafidz.com/go-boilerplate/models/dto"
9
+ entity "abdanhafidz.com/go-boilerplate/models/entity"
10
+ "abdanhafidz.com/go-boilerplate/repositories"
11
+ "github.com/google/uuid"
12
+ )
13
+
14
+ type EventExamProctoringService interface {
15
+ CreateLog(ctx context.Context, req dto.EventExamProctoringLogsRequest, file *multipart.FileHeader) error
16
+ ListLogs(ctx context.Context, accountId uuid.UUID, examId uuid.UUID, eventId uuid.UUID) ([]entity.EventExamProctoringLogs, error)
17
+ GetLogById(ctx context.Context, id uuid.UUID) (entity.EventExamProctoringLogs, error)
18
+ UpdateLog(ctx context.Context, id uuid.UUID, req dto.EventExamProctoringLogsRequest, file *multipart.FileHeader) error
19
+ DeleteLog(ctx context.Context, id uuid.UUID) error
20
+ }
21
+
22
+ type eventExamProctoringService struct {
23
+ repo repositories.EventExamProctoringRepository
24
+ uploadService UploadService
25
+ }
26
+
27
+ func NewEventExamProctoringService(repo repositories.EventExamProctoringRepository, uploadService UploadService) EventExamProctoringService {
28
+ return &eventExamProctoringService{
29
+ repo: repo,
30
+ uploadService: uploadService,
31
+ }
32
+ }
33
+
34
+ func (s *eventExamProctoringService) CreateLog(ctx context.Context, req dto.EventExamProctoringLogsRequest, file *multipart.FileHeader) error {
35
+ var attachmentUrl string
36
+ if file != nil {
37
+ files, err := s.uploadService.UploadFiles(ctx, []*multipart.FileHeader{file}, "submission", req.AccountId)
38
+ if err != nil {
39
+ return err
40
+ }
41
+ if len(files) > 0 {
42
+ attachmentUrl = files[0].Path
43
+ }
44
+ }
45
+
46
+ log := entity.EventExamProctoringLogs{
47
+ Id: uuid.New(),
48
+ EventId: req.EventId,
49
+ ExamId: req.ExamId,
50
+ AccountId: req.AccountId,
51
+ ViolationScore: req.ViolationScore,
52
+ ViolationCategory: req.ViolationCategory,
53
+ Attachement: attachmentUrl,
54
+ CreatedAt: time.Now(),
55
+ }
56
+
57
+ return s.repo.Create(ctx, &log)
58
+ }
59
+
60
+ func (s *eventExamProctoringService) ListLogs(ctx context.Context, accountId uuid.UUID, examId uuid.UUID, eventId uuid.UUID) ([]entity.EventExamProctoringLogs, error) {
61
+ return s.repo.List(ctx, accountId, examId, eventId)
62
+ }
63
+
64
+ func (s *eventExamProctoringService) GetLogById(ctx context.Context, id uuid.UUID) (entity.EventExamProctoringLogs, error) {
65
+ return s.repo.GetById(ctx, id)
66
+ }
67
+
68
+ func (s *eventExamProctoringService) UpdateLog(ctx context.Context, id uuid.UUID, req dto.EventExamProctoringLogsRequest, file *multipart.FileHeader) error {
69
+ log, err := s.repo.GetById(ctx, id)
70
+ if err != nil {
71
+ return err
72
+ }
73
+
74
+ var attachmentUrl = log.Attachement
75
+ if file != nil {
76
+ files, err := s.uploadService.UploadFiles(ctx, []*multipart.FileHeader{file}, "submission", req.AccountId)
77
+ if err != nil {
78
+ return err
79
+ }
80
+ if len(files) > 0 {
81
+ attachmentUrl = files[0].Path
82
+ }
83
+ }
84
+
85
+ // Update fields if they are provided (for non-zero values) or logic requires
86
+ // Here I assume we update what's in request.
87
+ if req.ViolationScore != 0 {
88
+ log.ViolationScore = req.ViolationScore
89
+ }
90
+ if req.ViolationCategory != "" {
91
+ log.ViolationCategory = req.ViolationCategory
92
+ }
93
+ log.Attachement = attachmentUrl
94
+
95
+ return s.repo.Update(ctx, &log)
96
+ }
97
+
98
+ func (s *eventExamProctoringService) DeleteLog(ctx context.Context, id uuid.UUID) error {
99
+ return s.repo.Delete(ctx, id)
100
+ }
services/{exam_event_service.go → event_exam_service.go} RENAMED
@@ -15,44 +15,45 @@ import (
15
  "gorm.io/gorm"
16
  )
17
 
18
- type ExamService interface {
19
  ListExamByEvent(ctx context.Context, eventSlug string, accountId uuid.UUID) ([]entity.Exam, error)
20
  GetEventExamExisting(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (ev dto.EventDetailResponse, exam entity.Exam, err error)
21
- GetExamEventAttempt(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (dto.UserExamStatus, entity.ExamEventAttempt, error)
22
- AttemptExamEvent(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (entity.ExamEventAttempt, error)
23
  SetupQuestions(ctx context.Context, eventSlug string, examId uuid.UUID, accountId uuid.UUID) ([]entity.Questions, error)
24
- SetupAnswer(ctx context.Context, questions []entity.Questions, attemptId uuid.UUID) ([]entity.ExamEventAnswer, error)
25
  SetupExamTimer(ctx context.Context, exam entity.Exam) (time.Time, time.Time)
26
- SubmitExamEvent(ctx context.Context, attemptId uuid.UUID) (result entity.Result, err error)
27
- AnswerExamEvent(ctx context.Context, eventSlug string, attemptId uuid.UUID, questionId uuid.UUID, answer []string) (entity.CPQuestionVerdict, error)
28
  }
 
29
  type evaluator func(answer []string) (float32, entity.CPQuestionVerdict)
30
 
31
- type examService struct {
32
  eventService EventService
33
  problemSetService ProblemSetService
34
  problemSetExamAssignRepo repositories.ProblemSetExamAssignRepository
35
  examRepo repositories.ExamRepository
36
- examEventAttemptRepo repositories.ExamEventAttemptRepository
37
- examEventAnswerRepo repositories.ExamEventAnswerRepository
38
- examEventAssignRepo repositories.ExamEventAssignRepository
39
  resultRepo repositories.ResultRepository
40
  }
41
 
42
- func NewExamService(eventService EventService, problemSetService ProblemSetService, problemSetExamAssignRepo repositories.ProblemSetExamAssignRepository, examRepo repositories.ExamRepository, examEventAttemptRepo repositories.ExamEventAttemptRepository, examEventAssignRepo repositories.ExamEventAssignRepository, examEventAnswerRepo repositories.ExamEventAnswerRepository, resultRepo repositories.ResultRepository) ExamService {
43
- return &examService{
44
  eventService: eventService,
45
  problemSetService: problemSetService,
46
  problemSetExamAssignRepo: problemSetExamAssignRepo,
47
  examRepo: examRepo,
48
- examEventAttemptRepo: examEventAttemptRepo,
49
- examEventAssignRepo: examEventAssignRepo,
50
- examEventAnswerRepo: examEventAnswerRepo,
51
  resultRepo: resultRepo,
52
  }
53
  }
54
 
55
- func ProtectExamEventAttempt(attempt entity.ExamEventAttempt) entity.ExamEventAttempt {
56
 
57
  var cleanQuestions []entity.Questions
58
  for _, q := range attempt.Questions {
@@ -67,7 +68,7 @@ func ProtectExamEventAttempt(attempt entity.ExamEventAttempt) entity.ExamEventAt
67
  attempt.Questions = cleanQuestions
68
 
69
  // protect answers verdict info
70
- var cleanAnswers []entity.ExamEventAnswer
71
 
72
  for _, a := range attempt.Answers {
73
  aCopy := a
@@ -81,7 +82,7 @@ func ProtectExamEventAttempt(attempt entity.ExamEventAttempt) entity.ExamEventAt
81
  return attempt
82
  }
83
 
84
- func (s *examService) ListExamByEvent(ctx context.Context, eventSlug string, accountId uuid.UUID) ([]entity.Exam, error) {
85
  ev, err := s.eventService.DetailBySlug(ctx, eventSlug, accountId)
86
 
87
  if err != nil {
@@ -98,7 +99,7 @@ func (s *examService) ListExamByEvent(ctx context.Context, eventSlug string, acc
98
 
99
  }
100
 
101
- func (s *examService) GetEventExamExisting(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (ev dto.EventDetailResponse, exam entity.Exam, err error) {
102
 
103
  if ev, err = s.eventService.DetailBySlug(ctx, eventSlug, accountId); err != nil {
104
  return ev, exam, err
@@ -108,37 +109,37 @@ func (s *examService) GetEventExamExisting(ctx context.Context, eventSlug string
108
  return ev, exam, err
109
  }
110
 
111
- if err := s.examEventAssignRepo.Check(ctx, ev.Data.Id, exam.Id); err != nil {
112
  return dto.EventDetailResponse{}, entity.Exam{}, err
113
  }
114
 
115
  return ev, exam, err
116
  }
117
 
118
- func (s *examService) GetExamEventAttempt(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (dto.UserExamStatus, entity.ExamEventAttempt, error) {
119
 
120
  ev, exam, err := s.GetEventExamExisting(ctx, eventSlug, examSlug, accountId)
121
 
122
  if err != nil {
123
- return dto.UserExamStatus{}, entity.ExamEventAttempt{}, err
124
  }
125
 
126
- examEventAttempt, err := s.examEventAttemptRepo.GetByExamEvent(ctx, ev.Data.Id, exam.Id, accountId)
127
  fmt.Println("Error Exam Event Attempt", errors.Is(err, gorm.ErrRecordNotFound))
128
 
129
  if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
130
- return dto.UserExamStatus{}, entity.ExamEventAttempt{}, err
131
  }
132
 
133
  var attemptStatus dto.UserExamStatus
134
  attemptStatus.IsNotAttempt = errors.Is(err, gorm.ErrRecordNotFound)
135
- attemptStatus.IsTimeOut = !attemptStatus.IsNotAttempt && (utils.CalculateRemainingTime(examEventAttempt.CreatedAt, examEventAttempt.DueAt) == 0)
136
- attemptStatus.IsSubmitted = examEventAttempt.Submitted
137
  attemptStatus.IsOnAttempt = !attemptStatus.IsNotAttempt && !attemptStatus.IsTimeOut && !attemptStatus.IsSubmitted
138
- return attemptStatus, examEventAttempt, nil
139
 
140
  }
141
- func (s *examService) SetupQuestions(ctx context.Context, eventSlug string, examId uuid.UUID, accountId uuid.UUID) ([]entity.Questions, error) {
142
  examAssign, err := s.problemSetExamAssignRepo.GetByExam(ctx, examId)
143
 
144
  if err != nil {
@@ -154,33 +155,33 @@ func (s *examService) SetupQuestions(ctx context.Context, eventSlug string, exam
154
  return questions, err
155
  }
156
 
157
- func (s *examService) SetupAnswer(ctx context.Context, questions []entity.Questions, attemptId uuid.UUID) ([]entity.ExamEventAnswer, error) {
158
- var examEventAnswers []entity.ExamEventAnswer
159
  for _, q := range questions {
160
 
161
- blank_ans := entity.ExamEventAnswer{
162
  AttemptId: attemptId,
163
  QuestionId: q.Id,
164
  }
165
 
166
- err := s.examEventAnswerRepo.Create(ctx, &blank_ans)
167
  if err != nil {
168
- return []entity.ExamEventAnswer{}, err
169
  }
170
- examEventAnswers = append(examEventAnswers, blank_ans)
171
  }
172
 
173
- return examEventAnswers, nil
174
  }
175
 
176
- func (s *examService) SetupExamTimer(ctx context.Context, exam entity.Exam) (time.Time, time.Time) {
177
  startTime := time.Now()
178
  dueTime := startTime.Add(exam.Duration * time.Minute)
179
  return startTime, dueTime
180
  }
181
 
182
- func (s *examService) SubmitExamEvent(ctx context.Context, attemptId uuid.UUID) (result entity.Result, err error) {
183
- attempt, err := s.examEventAttemptRepo.GetById(ctx, attemptId)
184
  finalScore := float32(0)
185
  if err != nil {
186
  return entity.Result{}, err
@@ -194,10 +195,10 @@ func (s *examService) SubmitExamEvent(ctx context.Context, attemptId uuid.UUID)
194
 
195
  attempt.Submitted = true
196
  result.AttemptId = attempt.Id
197
- result.ExamEventAttempt = &attempt
198
  result.FinalScore = float32(finalScore)
199
 
200
- s.examEventAttemptRepo.Update(ctx, &attempt)
201
  err := s.resultRepo.Create(ctx, &result)
202
 
203
  if err != nil {
@@ -219,47 +220,47 @@ func (s *examService) SubmitExamEvent(ctx context.Context, attemptId uuid.UUID)
219
  return result, err
220
 
221
  }
222
- func (s *examService) AttemptExamEvent(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (entity.ExamEventAttempt, error) {
223
  eventStatus, err := s.eventService.GetStatus(ctx, eventSlug, accountId)
224
 
225
  if err != nil {
226
- return entity.ExamEventAttempt{}, err
227
  }
228
 
229
  if eventStatus.IsHasNotStarted {
230
- return entity.ExamEventAttempt{}, http_error.EVENT_NOT_STARTED
231
  }
232
 
233
  ev, exam, err := s.GetEventExamExisting(ctx, eventSlug, examSlug, accountId)
234
 
235
  if err != nil {
236
- return entity.ExamEventAttempt{}, err
237
  }
238
- attemptStatus, examEventAttempt, err := s.GetExamEventAttempt(ctx, eventSlug, examSlug, accountId)
239
 
240
  fmt.Println("Get AttemptStatus = ", attemptStatus, "Err =", err)
241
 
242
  if err != nil {
243
- return entity.ExamEventAttempt{}, err
244
  }
245
 
246
  questions, err := s.SetupQuestions(ctx, eventSlug, exam.Id, accountId)
247
- examEventAttempt.Questions = questions
248
 
249
  if err != nil {
250
- return entity.ExamEventAttempt{}, err
251
  }
252
 
253
  if attemptStatus.IsNotAttempt {
254
  if eventStatus.IsFinished {
255
- return entity.ExamEventAttempt{}, err
256
  }
257
 
258
  startTime, dueTime := s.SetupExamTimer(ctx, exam)
259
  remTime := utils.CalculateRemainingTime(startTime, dueTime)
260
 
261
  fmt.Println("Rem Time = ", remTime)
262
- examEventAttempt = entity.ExamEventAttempt{
263
  AccountId: accountId,
264
  EventId: ev.Data.Id,
265
  ExamId: exam.Id,
@@ -270,65 +271,65 @@ func (s *examService) AttemptExamEvent(ctx context.Context, eventSlug string, ex
270
  Questions: questions,
271
  }
272
 
273
- if err := s.examEventAttemptRepo.Create(ctx, &examEventAttempt); err != nil {
274
- return entity.ExamEventAttempt{}, err
275
  }
276
 
277
- answers, err := s.SetupAnswer(ctx, questions, examEventAttempt.Id)
278
  fmt.Println("Answer = ", answers)
279
  if err != nil {
280
- return entity.ExamEventAttempt{}, err
281
  }
282
 
283
- examEventAttempt.Answers = answers
284
- return ProtectExamEventAttempt(examEventAttempt), err
285
 
286
  } else if attemptStatus.IsOnAttempt {
287
 
288
  if eventStatus.IsFinished {
289
- s.SubmitExamEvent(ctx, examEventAttempt.Id)
290
- examEventAttempt.Submitted = true
291
- if err := s.examEventAttemptRepo.Update(ctx, &examEventAttempt); err != nil {
292
- return entity.ExamEventAttempt{}, err
293
  }
294
- return examEventAttempt, err
295
  }
296
- remTime := utils.CalculateRemainingTime(examEventAttempt.CreatedAt, examEventAttempt.DueAt)
297
- examEventAttempt.RemTime = remTime
298
 
299
- if err := s.examEventAttemptRepo.Update(ctx, &examEventAttempt); err != nil {
300
- return entity.ExamEventAttempt{}, err
301
  }
302
 
303
- examEventAttempt.Questions = questions
304
 
305
- return ProtectExamEventAttempt(examEventAttempt), err
306
 
307
  } else if attemptStatus.IsTimeOut {
308
- if examEventAttempt.RemTime != 0 {
309
  remTime := 0
310
- examEventAttempt.RemTime = remTime
311
  }
312
 
313
- s.SubmitExamEvent(ctx, examEventAttempt.Id)
 
 
314
 
315
- examEventAttempt.Submitted = true
316
-
317
- if err := s.examEventAttemptRepo.Update(ctx, &examEventAttempt); err != nil {
318
- return entity.ExamEventAttempt{}, err
319
  }
320
- return examEventAttempt, err
321
 
322
  } else if attemptStatus.IsSubmitted {
323
  if !exam.Configuration.AllowReview {
324
- examEventAttempt = ProtectExamEventAttempt(examEventAttempt)
325
  }
326
- return examEventAttempt, nil
327
  }
328
- return entity.ExamEventAttempt{}, http_error.INTERNAL_SERVER_ERROR
329
  }
330
 
331
- func (s *examService) EvaluateAnswer(ctx context.Context, question entity.Questions) evaluator {
332
 
333
  nonCPEvaluator := func(answer []string) (float32, entity.CPQuestionVerdict) {
334
  score := float32(0)
@@ -373,9 +374,9 @@ func (s *examService) EvaluateAnswer(ctx context.Context, question entity.Questi
373
 
374
  return examEvaluator[question.Type]
375
  }
376
- func (s *examService) AnswerExamEvent(ctx context.Context, eventSlug string, attemptId uuid.UUID, questionId uuid.UUID, answer []string) (entity.CPQuestionVerdict, error) {
377
 
378
- attempt, err := s.examEventAttemptRepo.GetById(ctx, attemptId)
379
 
380
  if err != nil {
381
  return entity.CPQuestionVerdict{}, err
@@ -402,7 +403,7 @@ func (s *examService) AnswerExamEvent(ctx context.Context, eventSlug string, att
402
 
403
  score, CPQuestionVerdict := s.EvaluateAnswer(ctx, question)(answer)
404
 
405
- err = s.examEventAnswerRepo.Update(ctx, &entity.ExamEventAnswer{
406
  AttemptId: attemptId,
407
  QuestionId: questionId,
408
  Answers: answer,
 
15
  "gorm.io/gorm"
16
  )
17
 
18
+ type EventExamService interface {
19
  ListExamByEvent(ctx context.Context, eventSlug string, accountId uuid.UUID) ([]entity.Exam, error)
20
  GetEventExamExisting(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (ev dto.EventDetailResponse, exam entity.Exam, err error)
21
+ GetEventExamAttempt(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (dto.UserExamStatus, entity.EventExamAttempt, error)
22
+ AttemptEventExam(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (entity.EventExamAttempt, error)
23
  SetupQuestions(ctx context.Context, eventSlug string, examId uuid.UUID, accountId uuid.UUID) ([]entity.Questions, error)
24
+ SetupAnswer(ctx context.Context, questions []entity.Questions, attemptId uuid.UUID) ([]entity.EventExamAnswer, error)
25
  SetupExamTimer(ctx context.Context, exam entity.Exam) (time.Time, time.Time)
26
+ SubmitEventExam(ctx context.Context, attemptId uuid.UUID) (result entity.Result, err error)
27
+ AnswerEventExam(ctx context.Context, eventSlug string, attemptId uuid.UUID, questionId uuid.UUID, answer []string) (entity.CPQuestionVerdict, error)
28
  }
29
+
30
  type evaluator func(answer []string) (float32, entity.CPQuestionVerdict)
31
 
32
+ type eventExamService struct {
33
  eventService EventService
34
  problemSetService ProblemSetService
35
  problemSetExamAssignRepo repositories.ProblemSetExamAssignRepository
36
  examRepo repositories.ExamRepository
37
+ eventExamAttemptRepo repositories.EventExamAttemptRepository
38
+ eventExamAnswerRepo repositories.EventExamAnswerRepository
39
+ eventExamAssignRepo repositories.EventExamAssignRepository
40
  resultRepo repositories.ResultRepository
41
  }
42
 
43
+ func NewEventExamService(eventService EventService, problemSetService ProblemSetService, problemSetExamAssignRepo repositories.ProblemSetExamAssignRepository, examRepo repositories.ExamRepository, eventExamAttemptRepo repositories.EventExamAttemptRepository, eventExamAssignRepo repositories.EventExamAssignRepository, eventExamAnswerRepo repositories.EventExamAnswerRepository, resultRepo repositories.ResultRepository) EventExamService {
44
+ return &eventExamService{
45
  eventService: eventService,
46
  problemSetService: problemSetService,
47
  problemSetExamAssignRepo: problemSetExamAssignRepo,
48
  examRepo: examRepo,
49
+ eventExamAttemptRepo: eventExamAttemptRepo,
50
+ eventExamAssignRepo: eventExamAssignRepo,
51
+ eventExamAnswerRepo: eventExamAnswerRepo,
52
  resultRepo: resultRepo,
53
  }
54
  }
55
 
56
+ func ProtectEventExamAttempt(attempt entity.EventExamAttempt) entity.EventExamAttempt {
57
 
58
  var cleanQuestions []entity.Questions
59
  for _, q := range attempt.Questions {
 
68
  attempt.Questions = cleanQuestions
69
 
70
  // protect answers verdict info
71
+ var cleanAnswers []entity.EventExamAnswer
72
 
73
  for _, a := range attempt.Answers {
74
  aCopy := a
 
82
  return attempt
83
  }
84
 
85
+ func (s *eventExamService) ListExamByEvent(ctx context.Context, eventSlug string, accountId uuid.UUID) ([]entity.Exam, error) {
86
  ev, err := s.eventService.DetailBySlug(ctx, eventSlug, accountId)
87
 
88
  if err != nil {
 
99
 
100
  }
101
 
102
+ func (s *eventExamService) GetEventExamExisting(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (ev dto.EventDetailResponse, exam entity.Exam, err error) {
103
 
104
  if ev, err = s.eventService.DetailBySlug(ctx, eventSlug, accountId); err != nil {
105
  return ev, exam, err
 
109
  return ev, exam, err
110
  }
111
 
112
+ if err := s.eventExamAssignRepo.Check(ctx, ev.Data.Id, exam.Id); err != nil {
113
  return dto.EventDetailResponse{}, entity.Exam{}, err
114
  }
115
 
116
  return ev, exam, err
117
  }
118
 
119
+ func (s *eventExamService) GetEventExamAttempt(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (dto.UserExamStatus, entity.EventExamAttempt, error) {
120
 
121
  ev, exam, err := s.GetEventExamExisting(ctx, eventSlug, examSlug, accountId)
122
 
123
  if err != nil {
124
+ return dto.UserExamStatus{}, entity.EventExamAttempt{}, err
125
  }
126
 
127
+ eventExamAttempt, err := s.eventExamAttemptRepo.GetByEventExam(ctx, ev.Data.Id, exam.Id, accountId)
128
  fmt.Println("Error Exam Event Attempt", errors.Is(err, gorm.ErrRecordNotFound))
129
 
130
  if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
131
+ return dto.UserExamStatus{}, entity.EventExamAttempt{}, err
132
  }
133
 
134
  var attemptStatus dto.UserExamStatus
135
  attemptStatus.IsNotAttempt = errors.Is(err, gorm.ErrRecordNotFound)
136
+ attemptStatus.IsTimeOut = !attemptStatus.IsNotAttempt && (utils.CalculateRemainingTime(eventExamAttempt.CreatedAt, eventExamAttempt.DueAt) == 0)
137
+ attemptStatus.IsSubmitted = eventExamAttempt.Submitted
138
  attemptStatus.IsOnAttempt = !attemptStatus.IsNotAttempt && !attemptStatus.IsTimeOut && !attemptStatus.IsSubmitted
139
+ return attemptStatus, eventExamAttempt, nil
140
 
141
  }
142
+ func (s *eventExamService) SetupQuestions(ctx context.Context, eventSlug string, examId uuid.UUID, accountId uuid.UUID) ([]entity.Questions, error) {
143
  examAssign, err := s.problemSetExamAssignRepo.GetByExam(ctx, examId)
144
 
145
  if err != nil {
 
155
  return questions, err
156
  }
157
 
158
+ func (s *eventExamService) SetupAnswer(ctx context.Context, questions []entity.Questions, attemptId uuid.UUID) ([]entity.EventExamAnswer, error) {
159
+ var eventExamAnswers []entity.EventExamAnswer
160
  for _, q := range questions {
161
 
162
+ blank_ans := entity.EventExamAnswer{
163
  AttemptId: attemptId,
164
  QuestionId: q.Id,
165
  }
166
 
167
+ err := s.eventExamAnswerRepo.Create(ctx, &blank_ans)
168
  if err != nil {
169
+ return []entity.EventExamAnswer{}, err
170
  }
171
+ eventExamAnswers = append(eventExamAnswers, blank_ans)
172
  }
173
 
174
+ return eventExamAnswers, nil
175
  }
176
 
177
+ func (s *eventExamService) SetupExamTimer(ctx context.Context, exam entity.Exam) (time.Time, time.Time) {
178
  startTime := time.Now()
179
  dueTime := startTime.Add(exam.Duration * time.Minute)
180
  return startTime, dueTime
181
  }
182
 
183
+ func (s *eventExamService) SubmitEventExam(ctx context.Context, attemptId uuid.UUID) (result entity.Result, err error) {
184
+ attempt, err := s.eventExamAttemptRepo.GetById(ctx, attemptId)
185
  finalScore := float32(0)
186
  if err != nil {
187
  return entity.Result{}, err
 
195
 
196
  attempt.Submitted = true
197
  result.AttemptId = attempt.Id
198
+ result.EventExamAttempt = &attempt
199
  result.FinalScore = float32(finalScore)
200
 
201
+ s.eventExamAttemptRepo.Update(ctx, &attempt)
202
  err := s.resultRepo.Create(ctx, &result)
203
 
204
  if err != nil {
 
220
  return result, err
221
 
222
  }
223
+ func (s *eventExamService) AttemptEventExam(ctx context.Context, eventSlug string, examSlug string, accountId uuid.UUID) (entity.EventExamAttempt, error) {
224
  eventStatus, err := s.eventService.GetStatus(ctx, eventSlug, accountId)
225
 
226
  if err != nil {
227
+ return entity.EventExamAttempt{}, err
228
  }
229
 
230
  if eventStatus.IsHasNotStarted {
231
+ return entity.EventExamAttempt{}, http_error.EVENT_NOT_STARTED
232
  }
233
 
234
  ev, exam, err := s.GetEventExamExisting(ctx, eventSlug, examSlug, accountId)
235
 
236
  if err != nil {
237
+ return entity.EventExamAttempt{}, err
238
  }
239
+ attemptStatus, eventExamAttempt, err := s.GetEventExamAttempt(ctx, eventSlug, examSlug, accountId)
240
 
241
  fmt.Println("Get AttemptStatus = ", attemptStatus, "Err =", err)
242
 
243
  if err != nil {
244
+ return entity.EventExamAttempt{}, err
245
  }
246
 
247
  questions, err := s.SetupQuestions(ctx, eventSlug, exam.Id, accountId)
248
+ eventExamAttempt.Questions = questions
249
 
250
  if err != nil {
251
+ return entity.EventExamAttempt{}, err
252
  }
253
 
254
  if attemptStatus.IsNotAttempt {
255
  if eventStatus.IsFinished {
256
+ return entity.EventExamAttempt{}, err
257
  }
258
 
259
  startTime, dueTime := s.SetupExamTimer(ctx, exam)
260
  remTime := utils.CalculateRemainingTime(startTime, dueTime)
261
 
262
  fmt.Println("Rem Time = ", remTime)
263
+ eventExamAttempt = entity.EventExamAttempt{
264
  AccountId: accountId,
265
  EventId: ev.Data.Id,
266
  ExamId: exam.Id,
 
271
  Questions: questions,
272
  }
273
 
274
+ if err := s.eventExamAttemptRepo.Create(ctx, &eventExamAttempt); err != nil {
275
+ return entity.EventExamAttempt{}, err
276
  }
277
 
278
+ answers, err := s.SetupAnswer(ctx, questions, eventExamAttempt.Id)
279
  fmt.Println("Answer = ", answers)
280
  if err != nil {
281
+ return entity.EventExamAttempt{}, err
282
  }
283
 
284
+ eventExamAttempt.Answers = answers
285
+ return ProtectEventExamAttempt(eventExamAttempt), err
286
 
287
  } else if attemptStatus.IsOnAttempt {
288
 
289
  if eventStatus.IsFinished {
290
+ s.SubmitEventExam(ctx, eventExamAttempt.Id)
291
+ eventExamAttempt.Submitted = true
292
+ if err := s.eventExamAttemptRepo.Update(ctx, &eventExamAttempt); err != nil {
293
+ return entity.EventExamAttempt{}, err
294
  }
295
+ return eventExamAttempt, err
296
  }
297
+ remTime := utils.CalculateRemainingTime(eventExamAttempt.CreatedAt, eventExamAttempt.DueAt)
298
+ eventExamAttempt.RemTime = remTime
299
 
300
+ if err := s.eventExamAttemptRepo.Update(ctx, &eventExamAttempt); err != nil {
301
+ return entity.EventExamAttempt{}, err
302
  }
303
 
304
+ eventExamAttempt.Questions = questions
305
 
306
+ return ProtectEventExamAttempt(eventExamAttempt), err
307
 
308
  } else if attemptStatus.IsTimeOut {
309
+ if eventExamAttempt.RemTime != 0 {
310
  remTime := 0
311
+ eventExamAttempt.RemTime = remTime
312
  }
313
 
314
+ s.SubmitEventExam(ctx, eventExamAttempt.Id)
315
+
316
+ eventExamAttempt.Submitted = true
317
 
318
+ if err := s.eventExamAttemptRepo.Update(ctx, &eventExamAttempt); err != nil {
319
+ return entity.EventExamAttempt{}, err
 
 
320
  }
321
+ return eventExamAttempt, err
322
 
323
  } else if attemptStatus.IsSubmitted {
324
  if !exam.Configuration.AllowReview {
325
+ eventExamAttempt = ProtectEventExamAttempt(eventExamAttempt)
326
  }
327
+ return eventExamAttempt, nil
328
  }
329
+ return entity.EventExamAttempt{}, http_error.INTERNAL_SERVER_ERROR
330
  }
331
 
332
+ func (s *eventExamService) EvaluateAnswer(ctx context.Context, question entity.Questions) evaluator {
333
 
334
  nonCPEvaluator := func(answer []string) (float32, entity.CPQuestionVerdict) {
335
  score := float32(0)
 
374
 
375
  return examEvaluator[question.Type]
376
  }
377
+ func (s *eventExamService) AnswerEventExam(ctx context.Context, eventSlug string, attemptId uuid.UUID, questionId uuid.UUID, answer []string) (entity.CPQuestionVerdict, error) {
378
 
379
+ attempt, err := s.eventExamAttemptRepo.GetById(ctx, attemptId)
380
 
381
  if err != nil {
382
  return entity.CPQuestionVerdict{}, err
 
403
 
404
  score, CPQuestionVerdict := s.EvaluateAnswer(ctx, question)(answer)
405
 
406
+ err = s.eventExamAnswerRepo.Update(ctx, &entity.EventExamAnswer{
407
  AttemptId: attemptId,
408
  QuestionId: questionId,
409
  Answers: answer,
services/exam_service.go ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "context"
5
+
6
+ dto "abdanhafidz.com/go-boilerplate/models/dto"
7
+ entity "abdanhafidz.com/go-boilerplate/models/entity"
8
+ "abdanhafidz.com/go-boilerplate/repositories"
9
+ "github.com/google/uuid"
10
+ )
11
+
12
+ type ExamService interface {
13
+ CreateExam(ctx context.Context, req dto.CreateExamRequest) (entity.Exam, error)
14
+ UpdateExam(ctx context.Context, id uuid.UUID, req dto.CreateExamRequest) (entity.Exam, error)
15
+ DeleteExam(ctx context.Context, id uuid.UUID) error
16
+ GetExamList(ctx context.Context, p entity.Pagination) ([]entity.Exam, int64, error)
17
+ GetExamDetail(ctx context.Context, id uuid.UUID) (entity.Exam, error)
18
+ AssignExamToEvent(ctx context.Context, examId uuid.UUID, eventId uuid.UUID) error
19
+ AssignExamToAcademy(ctx context.Context, examId uuid.UUID, academyId uuid.UUID) error
20
+ }
21
+
22
+ type examService struct {
23
+ examRepo repositories.ExamRepository
24
+ eventExamAssignRepo repositories.EventExamAssignRepository
25
+ academyExamAssignRepo repositories.AcademyExamAssignRepository
26
+ }
27
+
28
+ func NewExamService(
29
+ examRepo repositories.ExamRepository,
30
+ eventExamAssignRepo repositories.EventExamAssignRepository,
31
+ academyExamAssignRepo repositories.AcademyExamAssignRepository,
32
+ ) ExamService {
33
+ return &examService{
34
+ examRepo: examRepo,
35
+ eventExamAssignRepo: eventExamAssignRepo,
36
+ academyExamAssignRepo: academyExamAssignRepo,
37
+ }
38
+ }
39
+
40
+ func (s *examService) CreateExam(ctx context.Context, req dto.CreateExamRequest) (entity.Exam, error) {
41
+
42
+ exam := entity.Exam{
43
+ Slug: req.Slug,
44
+ Title: req.Title,
45
+ Description: req.Description,
46
+ Duration: req.Duration,
47
+ Randomize: req.Randomize,
48
+ Configuration: entity.ExamConfiguration{
49
+ AllowRetake: req.AllowRetake,
50
+ AllowReview: req.AllowReview,
51
+ EnableTimer: req.EnableTimer,
52
+ },
53
+ Proctoring: entity.ExamProctoring{
54
+ EnableWebCam: req.EnableWebCam,
55
+ EnableVAD: req.EnableVAD,
56
+ EnableTabBlock: req.EnableTabBlock,
57
+ RequiredFullScreen: req.RequiredFullScreen,
58
+ EnableEyeTracking: req.EnableEyeTracking,
59
+ DisableCopyPaste: req.DisableCopyPaste,
60
+ EnableExamBrowser: req.EnableExamBrowser,
61
+ },
62
+ }
63
+
64
+ if err := s.examRepo.Create(ctx, &exam); err != nil {
65
+ return entity.Exam{}, err
66
+ }
67
+
68
+ return exam, nil
69
+ }
70
+
71
+ func (s *examService) UpdateExam(ctx context.Context, id uuid.UUID, req dto.CreateExamRequest) (entity.Exam, error) {
72
+ exam, err := s.examRepo.Get(ctx, id)
73
+ if err != nil {
74
+ return entity.Exam{}, err
75
+ }
76
+
77
+ exam.Slug = req.Slug
78
+ exam.Title = req.Title
79
+ exam.Description = req.Description
80
+ exam.Duration = req.Duration
81
+ exam.Randomize = req.Randomize
82
+
83
+ // Update Configuration
84
+ exam.Configuration.AllowRetake = req.AllowRetake
85
+ exam.Configuration.AllowReview = req.AllowReview
86
+ exam.Configuration.EnableTimer = req.EnableTimer
87
+
88
+ // Update Proctoring
89
+ exam.Proctoring.EnableWebCam = req.EnableWebCam
90
+ exam.Proctoring.EnableVAD = req.EnableVAD
91
+ exam.Proctoring.EnableTabBlock = req.EnableTabBlock
92
+ exam.Proctoring.RequiredFullScreen = req.RequiredFullScreen
93
+ exam.Proctoring.EnableEyeTracking = req.EnableEyeTracking
94
+ exam.Proctoring.DisableCopyPaste = req.DisableCopyPaste
95
+ exam.Proctoring.EnableExamBrowser = req.EnableExamBrowser
96
+
97
+ if err := s.examRepo.Update(ctx, exam); err != nil {
98
+ return entity.Exam{}, err
99
+ }
100
+
101
+ return exam, nil
102
+ }
103
+
104
+ func (s *examService) DeleteExam(ctx context.Context, id uuid.UUID) error {
105
+ return s.examRepo.Delete(ctx, id)
106
+ }
107
+
108
+ func (s *examService) GetExamList(ctx context.Context, p entity.Pagination) ([]entity.Exam, int64, error) {
109
+ return s.examRepo.ListWithPagination(ctx, p)
110
+ }
111
+
112
+ func (s *examService) GetExamDetail(ctx context.Context, id uuid.UUID) (entity.Exam, error) {
113
+ return s.examRepo.Get(ctx, id)
114
+ }
115
+
116
+ func (s *examService) AssignExamToEvent(ctx context.Context, examId uuid.UUID, eventId uuid.UUID) error {
117
+ // Check if already assigned
118
+ if err := s.eventExamAssignRepo.Check(ctx, eventId, examId); err == nil {
119
+ return nil // Already assigned
120
+ }
121
+
122
+ assign := entity.EventExamAssign{
123
+ ExamId: examId,
124
+ EventId: eventId,
125
+ }
126
+
127
+ return s.eventExamAssignRepo.Create(ctx, assign)
128
+ }
129
+
130
+ func (s *examService) AssignExamToAcademy(ctx context.Context, examId uuid.UUID, academyId uuid.UUID) error {
131
+ // Check if already assigned
132
+ if err := s.academyExamAssignRepo.Check(ctx, academyId, examId); err == nil {
133
+ return nil // Already assigned
134
+ }
135
+
136
+ assign := entity.AcademyExamAssign{
137
+ ExamId: examId,
138
+ AcademyId: academyId,
139
+ }
140
+
141
+ return s.academyExamAssignRepo.Create(ctx, assign)
142
+ }
services/external_auth_service.go CHANGED
@@ -83,6 +83,7 @@ func (s *externalAuthService) GoogleAuth(ctx context.Context, idToken string) (d
83
  if errExtAuth != nil {
84
  return dto.AuthenticatedUser{}, errExtAuth
85
  }
 
86
  token, _ := s.jwtService.GenerateToken(ctx, dto.JWTCustomClaims{
87
  AccountId: acc.Id.String(),
88
  Role: acc.Role,
 
83
  if errExtAuth != nil {
84
  return dto.AuthenticatedUser{}, errExtAuth
85
  }
86
+
87
  token, _ := s.jwtService.GenerateToken(ctx, dto.JWTCustomClaims{
88
  AccountId: acc.Id.String(),
89
  Role: acc.Role,
services/jwt_service.go CHANGED
@@ -60,17 +60,16 @@ func (s *jwtService) ValidateToken(ctx context.Context, tokenStr string) (claim
60
  return []byte(s.secretKey), nil
61
  })
62
 
63
- fmt.Println("Token", token)
64
- fmt.Println("secretKey", s.secretKey)
65
-
66
  if err != nil || !token.Valid {
67
  return nil, http_error.INVALID_TOKEN
68
  }
69
 
70
  claims, ok := token.Claims.(jwt.MapClaims)
 
71
  if !ok {
72
  return nil, http_error.INTERNAL_SERVER_ERROR
73
  }
 
74
  account_id, ok := claims["account_id"].(string)
75
  if !ok {
76
  return nil, http_error.INTERNAL_SERVER_ERROR
 
60
  return []byte(s.secretKey), nil
61
  })
62
 
 
 
 
63
  if err != nil || !token.Valid {
64
  return nil, http_error.INVALID_TOKEN
65
  }
66
 
67
  claims, ok := token.Claims.(jwt.MapClaims)
68
+
69
  if !ok {
70
  return nil, http_error.INTERNAL_SERVER_ERROR
71
  }
72
+
73
  account_id, ok := claims["account_id"].(string)
74
  if !ok {
75
  return nil, http_error.INTERNAL_SERVER_ERROR