Swag Docs
This commit is contained in:
		@@ -3,8 +3,8 @@ package controllers
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"studioj/boilerplate_go/internal/models"
 | 
			
		||||
	"studioj/boilerplate_go/internal/services"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
@@ -15,15 +15,30 @@ type AuthController interface {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type authController struct {
 | 
			
		||||
	service services.AuthService
 | 
			
		||||
	service      services.AuthService
 | 
			
		||||
	tokenService services.TokenService
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAuthController(service services.AuthService) AuthController {
 | 
			
		||||
func NewAuthController(service services.AuthService, tokenService services.TokenService) AuthController {
 | 
			
		||||
	return &authController{
 | 
			
		||||
		service: service,
 | 
			
		||||
		service:      service,
 | 
			
		||||
		tokenService: tokenService,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register
 | 
			
		||||
//
 | 
			
		||||
// @Summary			회원가입
 | 
			
		||||
// @Description	username, name, password 를 입력하여 회원가입
 | 
			
		||||
// @Accept				json
 | 
			
		||||
// @Produce			json
 | 
			
		||||
//
 | 
			
		||||
// @Param username body string true "username"
 | 
			
		||||
// @Param name body string true "이름"
 | 
			
		||||
// @Param password body string true "비밀번호"
 | 
			
		||||
//
 | 
			
		||||
// @Success			200		{object} models.RegisterResponse
 | 
			
		||||
// @Router				/auth/register [post]
 | 
			
		||||
func (controller *authController) Register(c *gin.Context) {
 | 
			
		||||
	var params models.RegisterRequest
 | 
			
		||||
	if c.BindJSON(¶ms) != nil {
 | 
			
		||||
@@ -37,7 +52,7 @@ func (controller *authController) Register(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	token, err := controller.service.CreateToken(user.ID)
 | 
			
		||||
	token, err := controller.tokenService.Create(user.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
@@ -45,23 +60,36 @@ func (controller *authController) Register(c *gin.Context) {
 | 
			
		||||
	c.JSON(http.StatusOK, gin.H{"user": user, "token": token.Token, "refresh_token": token.RefreshToken})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Login
 | 
			
		||||
//
 | 
			
		||||
// @Summary 			로그인
 | 
			
		||||
// @Description		username, password 를 입력하여 로그인
 | 
			
		||||
// @Accept				json
 | 
			
		||||
// @Produce				json
 | 
			
		||||
//
 | 
			
		||||
// @Param username body string true "username"
 | 
			
		||||
// @Param password body string true "비밀번호"
 | 
			
		||||
//
 | 
			
		||||
// @Success			200		{object} models.LoginResponse
 | 
			
		||||
// @Router				/auth/login [post]
 | 
			
		||||
func (controller *authController) Login(c *gin.Context) {
 | 
			
		||||
	var params models.LoginRequest
 | 
			
		||||
	if c.BindJSON(¶ms) != nil {
 | 
			
		||||
	var request models.LoginRequest
 | 
			
		||||
	if c.BindJSON(&request) != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	user, err := controller.service.Login(¶ms)
 | 
			
		||||
	user, err := controller.service.Login(&request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	token, err := controller.service.CreateToken(user.ID)
 | 
			
		||||
	token, err := controller.tokenService.Create(user.ID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, gin.H{"user": user, "token": token.Token, "refresh_token": token.RefreshToken})
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										207
									
								
								internal/controllers/program.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								internal/controllers/program.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,207 @@
 | 
			
		||||
package controllers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ProgramController interface {
 | 
			
		||||
	List(*gin.Context)
 | 
			
		||||
	Find(*gin.Context)
 | 
			
		||||
	Create(*gin.Context)
 | 
			
		||||
	Update(*gin.Context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type programController struct {
 | 
			
		||||
	service services.ProgramService
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewProgramController(service services.ProgramService) ProgramController {
 | 
			
		||||
	return &programController{
 | 
			
		||||
		service: service,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Program List
 | 
			
		||||
//
 | 
			
		||||
// @Summary 프로그램 목록 가져오기
 | 
			
		||||
// @Description 퀴즈 프로그램 목록을 가져옵니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param tag query string false "태그"
 | 
			
		||||
// @Param q query string false "검색어"
 | 
			
		||||
// @Param page query int false "페이지"
 | 
			
		||||
// @Param limit query int false "페이지 사이즈"
 | 
			
		||||
// @Success 200 {object} models.ProgramListResponse
 | 
			
		||||
// @Security ApiKeyAuth
 | 
			
		||||
// @Router /program  [get]
 | 
			
		||||
func (controller *programController) List(c *gin.Context) {
 | 
			
		||||
	tag := c.DefaultQuery("tag", "")
 | 
			
		||||
	q := c.DefaultQuery("q", "")
 | 
			
		||||
	page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error : page")
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	limit, err := strconv.Atoi(c.DefaultQuery("limit", "10"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error : limit")
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.List(q, tag, page, limit)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	totalNumber, err := controller.service.Total(q, tag)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error : list")
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	totalPage := int64(totalNumber/int64(limit) + 1)
 | 
			
		||||
 | 
			
		||||
	response := models.ProgramListResponse{
 | 
			
		||||
		Data:      *result,
 | 
			
		||||
		Total:     totalPage,
 | 
			
		||||
		Page:      page,
 | 
			
		||||
		TotalPage: totalPage,
 | 
			
		||||
		PageSize:  limit,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get program
 | 
			
		||||
//
 | 
			
		||||
// @Summary 퀴즈 프로그램 가져오기
 | 
			
		||||
// @Description ID로 퀴즈 프로그램을 가져옵니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param id path string true "퀴즈 프로그램 ID"
 | 
			
		||||
// @Success 200 {object} models.ProgramResponse
 | 
			
		||||
// @Failure 400
 | 
			
		||||
// @Router /program/{id} [get]
 | 
			
		||||
func (controller *programController) Find(c *gin.Context) {
 | 
			
		||||
	id := c.Param("id")
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.Find(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create Program
 | 
			
		||||
//
 | 
			
		||||
// @Summary 퀴즈 프로그램 생성
 | 
			
		||||
// @Description 퀴즈 프로그램을 만듭니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param subject body string true "프로그램 제목"
 | 
			
		||||
// @Param course body string true "프로그램 코스"
 | 
			
		||||
// @Param content body string true "프로그램 내용"
 | 
			
		||||
// @Param tag body []string true "프로그램 태그"
 | 
			
		||||
// @Param status body string true "프로그램 상태 on 또는 off"
 | 
			
		||||
// @Param publish_at body string true "프로그램 발행 날짜"
 | 
			
		||||
// @Router /program [post]
 | 
			
		||||
// @Success 200 {object} models.ProgramResponse
 | 
			
		||||
func (controller *programController) Create(c *gin.Context) {
 | 
			
		||||
	var request models.ProgramRequest
 | 
			
		||||
	if err := c.ShouldBindJSON(&request); err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	publish_at, err := time.Parse("2006-01-02", request.PublishAt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	program := &models.Program{
 | 
			
		||||
		ID:        uuid.NewString(),
 | 
			
		||||
		Subject:   request.Subject,
 | 
			
		||||
		Course:    request.Course,
 | 
			
		||||
		Content:   request.Content,
 | 
			
		||||
		Tag:       request.Tag,
 | 
			
		||||
		Tags:      request.Tag.String(),
 | 
			
		||||
		PublishAt: publish_at,
 | 
			
		||||
		Status:    request.Status,
 | 
			
		||||
		CreatedAt: time.Now(),
 | 
			
		||||
		UpdatedAt: time.Now(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Println("publish_at", program.PublishAt)
 | 
			
		||||
	result, err := controller.service.Create(program)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update Program
 | 
			
		||||
//
 | 
			
		||||
// @Summary 퀴즈 프로그램 수정
 | 
			
		||||
// @Description 퀴즈 프로그램을 수정합니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param subject body string true "프로그램 제목"
 | 
			
		||||
// @Param course body string true "프로그램 코스"
 | 
			
		||||
// @Param content body string true "프로그램 내용"
 | 
			
		||||
// @Param tag body []string true "프로그램 태그"
 | 
			
		||||
// @Param status body string true "프로그램 상태 on 또는 off"
 | 
			
		||||
// @Param publish_at body string true "프로그램 발행 날짜"
 | 
			
		||||
// @Router /program [put]
 | 
			
		||||
// @Success 200 {object} models.ProgramResponse
 | 
			
		||||
func (controller *programController) Update(c *gin.Context) {
 | 
			
		||||
	id := c.Param("id")
 | 
			
		||||
	var request models.ProgramRequest
 | 
			
		||||
	if err := c.ShouldBindJSON(&request); err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	program, err := controller.service.Find(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	publish_at, err := time.Parse("2006-01-02", request.PublishAt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	program.Subject = request.Subject
 | 
			
		||||
	program.Course = request.Course
 | 
			
		||||
	program.Content = request.Content
 | 
			
		||||
	program.Tag = request.Tag
 | 
			
		||||
	program.Tags = request.Tag.String()
 | 
			
		||||
	program.PublishAt = publish_at
 | 
			
		||||
	program.Status = request.Status
 | 
			
		||||
	program.UpdatedAt = time.Now()
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.Update(program)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, result)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										193
									
								
								internal/controllers/quiz.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								internal/controllers/quiz.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,193 @@
 | 
			
		||||
package controllers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type QuizController interface {
 | 
			
		||||
	List(*gin.Context)
 | 
			
		||||
	Find(*gin.Context)
 | 
			
		||||
	Create(*gin.Context)
 | 
			
		||||
	Update(*gin.Context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type quizController struct {
 | 
			
		||||
	service services.QuizService
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewQuizController(service services.QuizService) QuizController {
 | 
			
		||||
	return &quizController{
 | 
			
		||||
		service: service,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Quiz List
 | 
			
		||||
//
 | 
			
		||||
// @Summary 퀴즈 목록 가져오기
 | 
			
		||||
// @Description 퀴즈 목록을 가져옵니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param program_id query string true "프로그램 ID"
 | 
			
		||||
// @Param q query string false "검색어"
 | 
			
		||||
// @Param page query int false "페이지"
 | 
			
		||||
// @Param limit query int false "페이지 사이즈"
 | 
			
		||||
// @Success 200 {object} models.QuizListResponse
 | 
			
		||||
// @Router /quiz  [get]
 | 
			
		||||
func (controller *quizController) List(c *gin.Context) {
 | 
			
		||||
	program_id := c.DefaultQuery("program_id", "")
 | 
			
		||||
	page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error : page")
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	limit, err := strconv.Atoi(c.DefaultQuery("limit", "10"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error : limit")
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.List(program_id, page, limit)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	totalNumber, err := controller.service.Total(program_id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error : list")
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	totalPage := int64(totalNumber/int64(limit) + 1)
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, gin.H{"data": result, "total": totalNumber, "page": page, "totalPage": totalPage, "pageSize": limit})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get Quiz
 | 
			
		||||
//
 | 
			
		||||
// @Summary 퀴즈 가져오기
 | 
			
		||||
// @Description ID로 퀴즈를 가져옵니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param id path string true "퀴즈 ID"
 | 
			
		||||
// @Success 200 {object} models.QuizResponse
 | 
			
		||||
// @Failure 400
 | 
			
		||||
// @Router /quiz/{id} [get]
 | 
			
		||||
func (controller *quizController) Find(c *gin.Context) {
 | 
			
		||||
	id := c.Param("id")
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.Find(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create Quiz
 | 
			
		||||
//
 | 
			
		||||
// @Summary 퀴즈 생성
 | 
			
		||||
// @Description 퀴즈를 만듭니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param program_id body string true "프로그램 ID"
 | 
			
		||||
// @Param sequence body int true "퀴즈 순서"
 | 
			
		||||
// @Param quiz_type body string true "퀴즈 타입 : choice, check, ox, input"
 | 
			
		||||
// @Param question body string true "퀴즈 문제"
 | 
			
		||||
// @Param choice body []string true "퀴즈 선택지 : ['선택1','선택2','선택3', '선택4']"
 | 
			
		||||
// @Param answer body []string true "퀴즈 정답 : [1,3]"
 | 
			
		||||
// @Param hint body string true "퀴즈 힌트"
 | 
			
		||||
// @Param comment body string true "퀴즈 해설"
 | 
			
		||||
// @Success 200 {object} models.QuizResponse
 | 
			
		||||
// @Router /quiz [post]
 | 
			
		||||
func (controller *quizController) Create(c *gin.Context) {
 | 
			
		||||
	var request models.QuizRequest
 | 
			
		||||
	if err := c.ShouldBindJSON(&request); err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	quiz := &models.Quiz{
 | 
			
		||||
		ID:        uuid.NewString(),
 | 
			
		||||
		ProgramID: request.ProgramID,
 | 
			
		||||
		Sequence:  request.Sequence,
 | 
			
		||||
		QuizType:  request.QuizType,
 | 
			
		||||
		Question:  request.Question,
 | 
			
		||||
		Choice:    request.Choice,
 | 
			
		||||
		Answer:    request.Answer,
 | 
			
		||||
		Hint:      request.Hint,
 | 
			
		||||
		Comment:   request.Comment,
 | 
			
		||||
		UpdatedAt: time.Now(),
 | 
			
		||||
		CreatedAt: time.Now(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.Create(quiz)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update Quiz
 | 
			
		||||
//
 | 
			
		||||
// @Summary 퀴즈 수정
 | 
			
		||||
// @Description 퀴즈를 수정합니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param id path string true "퀴즈 ID"
 | 
			
		||||
// @Param program_id body string true "프로그램 ID"
 | 
			
		||||
// @Param sequence body int true "퀴즈 순서"
 | 
			
		||||
// @Param quiz_type body string true "퀴즈 타입 : choice, check, ox, input"
 | 
			
		||||
// @Param question body string true "퀴즈 문제"
 | 
			
		||||
// @Param choice body []string true "퀴즈 선택지 : ['선택1','선택2','선택3', '선택4']"
 | 
			
		||||
// @Param answer body []string true "퀴즈 정답 : [1,3]"
 | 
			
		||||
// @Param hint body string true "퀴즈 힌트"
 | 
			
		||||
// @Param comment body string true "퀴즈 해설"
 | 
			
		||||
// @Success 200 {object} models.QuizResponse
 | 
			
		||||
// @Router /quiz [put]
 | 
			
		||||
func (controller *quizController) Update(c *gin.Context) {
 | 
			
		||||
	id := c.Param("id")
 | 
			
		||||
	var request models.QuizRequest
 | 
			
		||||
	if err := c.ShouldBindJSON(&request); err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	quiz, err := controller.service.Find(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	quiz.ProgramID = request.ProgramID
 | 
			
		||||
	quiz.Sequence = request.Sequence
 | 
			
		||||
	quiz.QuizType = request.QuizType
 | 
			
		||||
	quiz.Question = request.Question
 | 
			
		||||
	quiz.Choice = request.Choice
 | 
			
		||||
	quiz.Answer = request.Answer
 | 
			
		||||
	quiz.Hint = request.Hint
 | 
			
		||||
	quiz.Comment = request.Comment
 | 
			
		||||
	quiz.UpdatedAt = time.Now()
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.Update(quiz)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, result)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								internal/controllers/swagger.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								internal/controllers/swagger.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package controllers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SwaggerController interface {
 | 
			
		||||
	Get(*gin.Context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type swaggerController struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSwaggerController() SwaggerController {
 | 
			
		||||
	return &swaggerController{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (controller *swaggerController) Get(c *gin.Context) {
 | 
			
		||||
	c.Redirect(http.StatusFound, "/swagger/index.html")
 | 
			
		||||
}
 | 
			
		||||
@@ -3,13 +3,14 @@ package controllers
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"studioj/boilerplate_go/internal/services"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type TokenController interface {
 | 
			
		||||
	Find(*gin.Context)
 | 
			
		||||
	Refresh(*gin.Context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type tokenController struct {
 | 
			
		||||
@@ -22,41 +23,27 @@ func NewTokenController(service services.TokenService) TokenController {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (controller *tokenController) Find(c *gin.Context) {
 | 
			
		||||
	id := c.Param("id")
 | 
			
		||||
	user_id := c.GetString("user_id")
 | 
			
		||||
 | 
			
		||||
	if user_id != id {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong user"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.Find(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (controller *tokenController) List(c *gin.Context) {
 | 
			
		||||
	id := c.Param("id")
 | 
			
		||||
	user_id := c.GetString("user_id")
 | 
			
		||||
 | 
			
		||||
	if user_id != id {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong user"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.Find(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Refresh Token
 | 
			
		||||
//
 | 
			
		||||
// @Summary AccessToken Refresh
 | 
			
		||||
// @Description AccessToken을 RefreshToken으로 갱신합니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param refresh_token body string true "RefreshToken"
 | 
			
		||||
// @Router /token/refresh [post]
 | 
			
		||||
// @Success 200 {object} models.ProgramResponse
 | 
			
		||||
func (controller *tokenController) Refresh(c *gin.Context) {
 | 
			
		||||
}
 | 
			
		||||
	var request models.RefreshTokenRequest
 | 
			
		||||
	if err := c.ShouldBindJSON(&request); err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.Refresh(request.RefreshToken)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, result)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,16 @@ package controllers
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"studioj/boilerplate_go/internal/services"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UserController interface {
 | 
			
		||||
	List(*gin.Context)
 | 
			
		||||
	Find(*gin.Context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -25,6 +28,71 @@ func NewUserController(service services.UserService, tokenService services.Token
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// User List
 | 
			
		||||
//
 | 
			
		||||
// @Summary 사용자 목록 가져오기
 | 
			
		||||
// @Description 사용자 목록을 가져옵니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
//
 | 
			
		||||
// @Param q query string false "검색어"
 | 
			
		||||
// @Param page query int false "페이지"
 | 
			
		||||
// @Param limit query int false "페이지 사이즈"
 | 
			
		||||
//
 | 
			
		||||
// @Success 200 {object} models.UserListResponse
 | 
			
		||||
// @Router /user  [get]
 | 
			
		||||
func (controller *userController) List(c *gin.Context) {
 | 
			
		||||
	q := c.DefaultQuery("q", "")
 | 
			
		||||
	page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error : page")
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	limit, err := strconv.Atoi(c.DefaultQuery("limit", "10"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error : limit")
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.List(q, page, limit)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	totalNumber, err := controller.service.Total(q)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error : list")
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	totalPage := int64(totalNumber/int64(limit) + 1)
 | 
			
		||||
 | 
			
		||||
	response := models.UserListResponse{
 | 
			
		||||
		Data:      *result,
 | 
			
		||||
		Total:     totalPage,
 | 
			
		||||
		Page:      page,
 | 
			
		||||
		TotalPage: totalPage,
 | 
			
		||||
		PageSize:  limit,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, response)
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get User
 | 
			
		||||
//
 | 
			
		||||
// @Summary 사용자 정보 가져오기
 | 
			
		||||
// @Description ID로 사용자 정보를 가져옵니다.
 | 
			
		||||
// @Accept json
 | 
			
		||||
// @Produce json
 | 
			
		||||
// @Param id path string true "사용자 ID"
 | 
			
		||||
// @Success 200 {object} models.User
 | 
			
		||||
// @Failure 400
 | 
			
		||||
// @Router /user/{id} [get]
 | 
			
		||||
func (controller *userController) Find(c *gin.Context) {
 | 
			
		||||
	id := c.Param("id")
 | 
			
		||||
	user_id := c.GetString("sub")
 | 
			
		||||
@@ -36,7 +104,7 @@ func (controller *userController) Find(c *gin.Context) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result, err := controller.service.FindByID(id)
 | 
			
		||||
	result, err := controller.service.Find(id)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,9 @@
 | 
			
		||||
package database
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	config "studioj/boilerplate_go/configs"
 | 
			
		||||
	"studioj/boilerplate_go/internal/models"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	config "learnsteam/learsteam-quiz-api/configs"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/driver/mysql"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
@@ -33,8 +34,12 @@ func GetDB() *gorm.DB {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func AutoMigrate() {
 | 
			
		||||
	fmt.Println("AugoMigrate")
 | 
			
		||||
	DB.AutoMigrate(
 | 
			
		||||
		&models.User{},
 | 
			
		||||
		&models.Token{},
 | 
			
		||||
		&models.Program{},
 | 
			
		||||
		&models.Quiz{},
 | 
			
		||||
		// &models.Course{},
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								internal/helpers/cipher/cipher.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								internal/helpers/cipher/cipher.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
package cipher
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Crypto interface {
 | 
			
		||||
	Encrypt(plainText string) (string, error)
 | 
			
		||||
	Decrypt(cipherIvKey string) (string, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type niceCrypto struct {
 | 
			
		||||
	cipherKey   string
 | 
			
		||||
	cipherIvKey string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c niceCrypto) Encrypt(plainText string) (string, error) {
 | 
			
		||||
	if strings.TrimSpace(plainText) == "" {
 | 
			
		||||
		return plainText, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	block, err := aes.NewCipher([]byte(c.cipherKey))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	encrypter := cipher.NewCBCEncrypter(block, []byte(c.cipherIvKey))
 | 
			
		||||
	paddedPlainText := padPKCS7([]byte(plainText), encrypter.BlockSize())
 | 
			
		||||
 | 
			
		||||
	cipherText := make([]byte, len(paddedPlainText))
 | 
			
		||||
	// CryptBlocks 함수에 데이터(paddedPlainText)와 암호화 될 데이터를 저장할 슬라이스(cipherText)를 넣으면 암호화가 된다.
 | 
			
		||||
	encrypter.CryptBlocks(cipherText, paddedPlainText)
 | 
			
		||||
 | 
			
		||||
	return base64.StdEncoding.EncodeToString(cipherText), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c niceCrypto) Decrypt(cipherText string) (string, error) {
 | 
			
		||||
	if strings.TrimSpace(cipherText) == "" {
 | 
			
		||||
		return cipherText, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decodedCipherText, err := base64.StdEncoding.DecodeString(cipherText)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	block, err := aes.NewCipher([]byte(c.cipherKey))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decrypter := cipher.NewCBCDecrypter(block, []byte(c.cipherIvKey))
 | 
			
		||||
	plainText := make([]byte, len(decodedCipherText))
 | 
			
		||||
 | 
			
		||||
	decrypter.CryptBlocks(plainText, decodedCipherText)
 | 
			
		||||
	trimmedPlainText := trimPKCS5(plainText)
 | 
			
		||||
 | 
			
		||||
	return string(trimmedPlainText), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewNiceCrypto(cipherKey, cipherIvKey string) (Crypto, error) {
 | 
			
		||||
	if ck := len(cipherKey); ck != 32 {
 | 
			
		||||
		return nil, aes.KeySizeError(ck)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cik := len(cipherIvKey); cik != 16 {
 | 
			
		||||
		return nil, aes.KeySizeError(cik)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &niceCrypto{cipherKey, cipherIvKey}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func padPKCS7(plainText []byte, blockSize int) []byte {
 | 
			
		||||
	padding := blockSize - len(plainText)%blockSize
 | 
			
		||||
	padText := bytes.Repeat([]byte{byte(padding)}, padding)
 | 
			
		||||
	return append(plainText, padText...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func trimPKCS5(text []byte) []byte {
 | 
			
		||||
	padding := text[len(text)-1]
 | 
			
		||||
	return text[:len(text)-int(padding)]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,7 @@
 | 
			
		||||
package helpers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -14,14 +12,6 @@ func InLambda() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func RandomPin() string {
 | 
			
		||||
	min := 100000
 | 
			
		||||
	max := 999999
 | 
			
		||||
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
 | 
			
		||||
	pin := r.Intn(max-min) + min
 | 
			
		||||
	return strconv.Itoa(pin)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Datetime(timeString string) (time.Time, error) {
 | 
			
		||||
	layout := "2006-01-02T15:04:05.000Z"
 | 
			
		||||
	result, err := time.Parse(layout, timeString)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										150
									
								
								internal/helpers/securityutil/securityutil.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								internal/helpers/securityutil/securityutil.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
package securityutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/crypto/bcrypt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func SHA256(data string) string {
 | 
			
		||||
	hash := sha256.New()
 | 
			
		||||
	hash.Write([]byte(data))
 | 
			
		||||
	return hex.EncodeToString(hash.Sum(nil))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func HashPassword(password string) (string, error) {
 | 
			
		||||
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
 | 
			
		||||
	return string(bytes), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CheckPasswordHash(password string, hash string) bool {
 | 
			
		||||
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
 | 
			
		||||
	return err == nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func AES256Encode(plaintext string, key string, iv string, blockSize int) string {
 | 
			
		||||
	bKey := []byte(key)
 | 
			
		||||
	bIV := []byte(iv)
 | 
			
		||||
	bPlaintext := PKCS5Padding([]byte(plaintext), blockSize, len(plaintext))
 | 
			
		||||
	block, err := aes.NewCipher(bKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	ciphertext := make([]byte, len(bPlaintext))
 | 
			
		||||
	mode := cipher.NewCBCEncrypter(block, bIV)
 | 
			
		||||
	mode.CryptBlocks(ciphertext, bPlaintext)
 | 
			
		||||
	// return hex.EncodeToString(ciphertext)
 | 
			
		||||
	fmt.Printf("hex.EncodeToString(ciphertext) : %s\n", hex.EncodeToString(ciphertext))
 | 
			
		||||
	fmt.Printf("base64.StdEncoding.EncodeToString(ciphertext) : %s\n", base64.StdEncoding.EncodeToString(ciphertext))
 | 
			
		||||
	fmt.Printf("base64.RawStdEncoding.EncodeToString(ciphertext) : %s\n", base64.RawStdEncoding.EncodeToString(ciphertext))
 | 
			
		||||
	return base64.StdEncoding.EncodeToString(ciphertext)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func AES256Decode(cipherText string, encKey string, iv string) (decryptedString string) {
 | 
			
		||||
	bKey := []byte(encKey)
 | 
			
		||||
	bIV := []byte(iv)
 | 
			
		||||
	// cipherTextDecoded, err := hex.DecodeString(cipherText)
 | 
			
		||||
	cipherTextDecoded, err := base64.StdEncoding.DecodeString(cipherText)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	block, err := aes.NewCipher(bKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mode := cipher.NewCBCDecrypter(block, bIV)
 | 
			
		||||
	mode.CryptBlocks([]byte(cipherTextDecoded), []byte(cipherTextDecoded))
 | 
			
		||||
	return string(cipherTextDecoded)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PKCS5Padding(ciphertext []byte, blockSize int, after int) []byte {
 | 
			
		||||
	padding := (blockSize - len(ciphertext)%blockSize)
 | 
			
		||||
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
 | 
			
		||||
	return append(ciphertext, padtext...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
 | 
			
		||||
// 	if blocksize <= 0 {
 | 
			
		||||
// 		return nil, ErrInvalidBlockSize
 | 
			
		||||
// 	}
 | 
			
		||||
// 	if b == nil || len(b) == 0 {
 | 
			
		||||
// 		return nil, ErrInvalidPKCS7Data
 | 
			
		||||
// 	}
 | 
			
		||||
// 	n := blocksize - (len(b) % blocksize)
 | 
			
		||||
// 	pb := make([]byte, len(b)+n)
 | 
			
		||||
// 	copy(pb, b)
 | 
			
		||||
// 	copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
 | 
			
		||||
// 	return pb, nil
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// // pkcs7Unpad validates and unpads data from the given bytes slice.
 | 
			
		||||
// // The returned value will be 1 to n bytes smaller depending on the
 | 
			
		||||
// // amount of padding, where n is the block size.
 | 
			
		||||
// func pkcs7Unpad(b []byte, blocksize int) ([]byte, error) {
 | 
			
		||||
// 	if blocksize <= 0 {
 | 
			
		||||
// 		return nil, ErrInvalidBlockSize
 | 
			
		||||
// 	}
 | 
			
		||||
// 	if b == nil || len(b) == 0 {
 | 
			
		||||
// 		return nil, ErrInvalidPKCS7Data
 | 
			
		||||
// 	}
 | 
			
		||||
// 	if len(b)%blocksize != 0 {
 | 
			
		||||
// 		return nil, ErrInvalidPKCS7Padding
 | 
			
		||||
// 	}
 | 
			
		||||
// 	c := b[len(b)-1]
 | 
			
		||||
// 	n := int(c)
 | 
			
		||||
// 	if n == 0 || n > len(b) {
 | 
			
		||||
// 		return nil, ErrInvalidPKCS7Padding
 | 
			
		||||
// 	}
 | 
			
		||||
// 	for i := 0; i < n; i++ {
 | 
			
		||||
// 		if b[len(b)-n+i] != c {
 | 
			
		||||
// 			return nil, ErrInvalidPKCS7Padding
 | 
			
		||||
// 		}
 | 
			
		||||
// 	}
 | 
			
		||||
// 	return b[:len(b)-n], nil
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// // func Encrypt(b cipher.Block, plaintext []byte) []byte {
 | 
			
		||||
// // 	if mod := len(plaintext) % aes.BlockSize; mod != 0 { // 블록 크기의 배수가 되어야함
 | 
			
		||||
// // 		padding := make([]byte, aes.BlockSize-mod) // 블록 크기에서 모자라는 부분을
 | 
			
		||||
// // 		plaintext = append(plaintext, padding...)  // 채워줌
 | 
			
		||||
// // 	}
 | 
			
		||||
 | 
			
		||||
// // 	ciphertext := make([]byte, aes.BlockSize+len(plaintext)) // 초기화 벡터 공간(aes.BlockSize)만큼 더 생성
 | 
			
		||||
// // 	// iv := ciphertext[:aes.BlockSize]                         // 부분 슬라이스로 초기화 벡터 공간을 가져옴
 | 
			
		||||
// // 	// if _, err := io.ReadFull(rand.Reader, iv); err != nil {  // 랜덤 값을 초기화 벡터에 넣어줌
 | 
			
		||||
// // 	// 	fmt.Println(err)
 | 
			
		||||
// // 	// 	return nil
 | 
			
		||||
// // 	// }
 | 
			
		||||
// // 	iv := []byte("0000000000000000")
 | 
			
		||||
// // 	mode := cipher.NewCBCEncrypter(b, iv)                   // 암호화 블록과 초기화 벡터를 넣어서 암호화 블록 모드 인스턴스 생성
 | 
			
		||||
// // 	mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext) // 암호화 블록 모드 인스턴스로
 | 
			
		||||
// // 	// 암호화
 | 
			
		||||
 | 
			
		||||
// // 	return ciphertext
 | 
			
		||||
// // }
 | 
			
		||||
 | 
			
		||||
// // func Decrypt(b cipher.Block, ciphertext []byte) []byte {
 | 
			
		||||
// // 	if len(ciphertext)%aes.BlockSize != 0 { // 블록 크기의 배수가 아니면 리턴
 | 
			
		||||
// // 		fmt.Println("암호화된 데이터의 길이는 블록 크기의 배수가 되어야합니다.")
 | 
			
		||||
// // 		return nil
 | 
			
		||||
// // 	}
 | 
			
		||||
 | 
			
		||||
// // 	// iv := ciphertext[:aes.BlockSize]        // 부분 슬라이스로 초기화 벡터 공간을 가져옴
 | 
			
		||||
// // 	iv := []byte("0000000000000000")
 | 
			
		||||
// // 	ciphertext = ciphertext[aes.BlockSize:] // 부분 슬라이스로 암호화된 데이터를 가져옴
 | 
			
		||||
 | 
			
		||||
// // 	plaintext := make([]byte, len(ciphertext)) // 평문 데이터를 저장할 공간 생성
 | 
			
		||||
// // 	mode := cipher.NewCBCDecrypter(b, iv)      // 암호화 블록과 초기화 벡터를 넣어서
 | 
			
		||||
// // 	// 복호화 블록 모드 인스턴스 생성
 | 
			
		||||
// // 	mode.CryptBlocks(plaintext, ciphertext) // 복호화 블록 모드 인스턴스로 복호화
 | 
			
		||||
 | 
			
		||||
// // 	return plaintext
 | 
			
		||||
// // }
 | 
			
		||||
@@ -6,7 +6,7 @@ import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	config "studioj/boilerplate_go/configs"
 | 
			
		||||
	config "learnsteam/learsteam-quiz-api/configs"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/golang-jwt/jwt/v5"
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,27 @@ type LoginRequest struct {
 | 
			
		||||
	Password string `json:"password"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LoginResponse struct {
 | 
			
		||||
	User         User   `json:"user"`
 | 
			
		||||
	Token        string `json:"token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDI3MTMwMjcsInN1"`
 | 
			
		||||
	RefreshToken string `json:"refresh_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDc4OTcwMjcs"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 	ID           string `json:"id" example:"7f9329f5-2e36-4638-92d2-73064b7291a4"`
 | 
			
		||||
// 	Username     string `json:"username" example:"user0"`
 | 
			
		||||
// 	Name         string `json:"name" example:"홍길동"`
 | 
			
		||||
// 	Token        string `json:"token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDI3MTMwMjcsInN1"`
 | 
			
		||||
// 	RefreshToken string `json:"refresh_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDc4OTcwMjcs"`
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
type RegisterRequest struct {
 | 
			
		||||
	Name     string `json:"name"`
 | 
			
		||||
	Username string `json:"username"`
 | 
			
		||||
	Password string `json:"password"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RegisterResponse struct {
 | 
			
		||||
	User         User   `json:"user"`
 | 
			
		||||
	Token        string `json:"token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDI3MTMwMjcsInN1"`
 | 
			
		||||
	RefreshToken string `json:"refresh_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDc4OTcwMjcs"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								internal/models/program.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								internal/models/program.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/datatypes"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Program struct {
 | 
			
		||||
	ID        string         `json:"id" db:"id" example:"ef74c59a-c707-4162-a52b-455906c81ec1"  gorm:"column:id;size:255;primary_key;"`
 | 
			
		||||
	Course    string         `json:"course" db:"course" example:"코스 이름" gorm:"column:course;size:40;index;"`
 | 
			
		||||
	Subject   string         `json:"subject" db:"subject" example:"프로그램 제목" gorm:"column:subject;size:255;index;"`
 | 
			
		||||
	Content   string         `json:"content" db:"content" example:"코스 설명" gorm:"column:content;size:512;"`
 | 
			
		||||
	Tag       datatypes.JSON `json:"tag" db:"tag" gorm:"column:tag;"`
 | 
			
		||||
	Tags      string         `json:"-" db:"tags" gorm:"column:tags;index;"`
 | 
			
		||||
	Status    string         `json:"status" example:"on" gorm:"column:status;size:10;index;"`
 | 
			
		||||
	PublishAt time.Time      `json:"publish_at" example:"2023-11-10T00:00:00+09:00" gorm:"column:publish_at;index;"`
 | 
			
		||||
	UpdatedAt time.Time      `json:"-" gorm:"column:updated_at;index;"`
 | 
			
		||||
	CreatedAt time.Time      `json:"-" gorm:"column:created_at;index;"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ProgramRequest struct {
 | 
			
		||||
	Course    string         `json:"course" example:"코스 이름"`
 | 
			
		||||
	Subject   string         `json:"subject" example:"프로그램 제목"`
 | 
			
		||||
	Content   string         `json:"content" example:"코스 설명"`
 | 
			
		||||
	Tag       datatypes.JSON `json:"tag"`
 | 
			
		||||
	Status    string         `json:"status" example:"on"`
 | 
			
		||||
	PublishAt string         `json:"publish_at" example:"2023-11-10T00:00:00+09:00"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ProgramResponse struct {
 | 
			
		||||
	ID        string   `json:"id" example:"ef74c59a-c707-4162-a52b-455906c81ec1"`
 | 
			
		||||
	Course    string   `json:"course" example:"코스 이름"`
 | 
			
		||||
	Subject   string   `json:"subject" example:"프로그램 제목"`
 | 
			
		||||
	Content   string   `json:"content" example:"코스 설명"`
 | 
			
		||||
	Tag       []string `json:"tag" example:"tag1,tag2"`
 | 
			
		||||
	Status    string   `json:"status" example:"on"`
 | 
			
		||||
	PublishAt string   `json:"publish_at" example:"2023-11-10T00:00:00+09:00"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ProgramListResponse struct {
 | 
			
		||||
	Data      []Program `json:"data"`
 | 
			
		||||
	Total     int64     `json:"total" example:"999"`
 | 
			
		||||
	Page      int       `json:"page" example:"1"`
 | 
			
		||||
	TotalPage int64     `json:"totalPage" example:"99"`
 | 
			
		||||
	PageSize  int       `json:"pageSize" example:"10"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								internal/models/quiz.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								internal/models/quiz.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/datatypes"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Quiz struct {
 | 
			
		||||
	ID        string         `json:"id" db:"id" example:"1b066168-68c4-4b50-bc9a-b6c4fceaf378" gorm:"column:id;size:255;primary_key;"`
 | 
			
		||||
	ProgramID string         `json:"program_id" db:"program_id" example:"2036023a-fb56-4b6c-b3bb-c787c681ada6" gorm:"column:program_id;size:255;index;"`
 | 
			
		||||
	Sequence  int            `json:"sequence" db:"sequence" example:"5" gorm:"column:sequence;index;"`
 | 
			
		||||
	QuizType  string         `json:"quiz_type" db:"quiz_type" example:"choice" gorm:"column:quiz_type;size:10;index;"`
 | 
			
		||||
	Question  string         `json:"question" db:"question" example:"퀴즈 질문입니다." gorm:"column:question;size:512;"`
 | 
			
		||||
	Choice    datatypes.JSON `json:"choice" db:"choice" gorm:"column:choice;"`
 | 
			
		||||
	Answer    datatypes.JSON `json:"answer" db:"answer" gorm:"column:answer;"`
 | 
			
		||||
	Hint      string         `json:"hint" db:"hint" example:"퀴즈 힌트" gorm:"column:answer;size:1024;"`
 | 
			
		||||
	Comment   string         `json:"comment" db:"comment" example:"퀴즈 해설" gorm:"column:comment;size:1024;"`
 | 
			
		||||
	UpdatedAt time.Time      `json:"-" gorm:"column:updated_at;index;"`
 | 
			
		||||
	CreatedAt time.Time      `json:"-" gorm:"column:created_at;index;"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type QuizRequest struct {
 | 
			
		||||
	ProgramID string         `json:"program_id"`
 | 
			
		||||
	Sequence  int            `json:"sequence"`
 | 
			
		||||
	QuizType  string         `json:"quiz_type"`
 | 
			
		||||
	Question  string         `json:"question"`
 | 
			
		||||
	Choice    datatypes.JSON `json:"choice"`
 | 
			
		||||
	Answer    datatypes.JSON `json:"answer"`
 | 
			
		||||
	Hint      string         `json:"hint"`
 | 
			
		||||
	Comment   string         `json:"comment"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type QuizResponse struct {
 | 
			
		||||
	ID        string         `json:"id" example:"1b066168-68c4-4b50-bc9a-b6c4fceaf378"`
 | 
			
		||||
	ProgramID string         `json:"program_id" example:"2036023a-fb56-4b6c-b3bb-c787c681ada6"`
 | 
			
		||||
	Sequence  int            `json:"sequence" example:"5" gorm:"column:sequence;index;"`
 | 
			
		||||
	QuizType  string         `json:"quiz_type" example:"check"`
 | 
			
		||||
	Question  string         `json:"question" example:"퀴즈 질문입니다."`
 | 
			
		||||
	Choice    datatypes.JSON `json:"choice"`
 | 
			
		||||
	Answer    datatypes.JSON `json:"answer"`
 | 
			
		||||
	Hint      string         `json:"hint" example:"퀴즈 힌트"`
 | 
			
		||||
	Comment   string         `json:"comment" example:"퀴즈 해설"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type QuizListResponse struct {
 | 
			
		||||
	Data      []Quiz `json:"data"`
 | 
			
		||||
	Total     int64  `json:"total" example:"5"`
 | 
			
		||||
	Page      int    `json:"page" example:"1"`
 | 
			
		||||
	TotalPage int64  `json:"totalPage" example:"1"`
 | 
			
		||||
	PageSize  int    `json:"pageSize" example:"10"`
 | 
			
		||||
}
 | 
			
		||||
@@ -2,15 +2,32 @@ package models
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
// type Token struct {
 | 
			
		||||
// 	ID           string    `json:"id" db:"id" gorm:"column:id;size:255;primary_key;"`
 | 
			
		||||
// 	UserID       string    `json:"user_id" db:"user_id" gorm:"column:user_id;size:255;index;"`
 | 
			
		||||
// 	Token        string    `json:"token" gorm:"column:token;size:255;index;"`
 | 
			
		||||
// 	RefreshToken string    `json:"refresh_token" gorm:"column:token;size:255;index;"`
 | 
			
		||||
// 	Status       string    `json:"status" gorm:"column:status;size:10;index;"`
 | 
			
		||||
// 	ExpireAt     time.Time `json:"expire_at" gorm:"column:expire_at;index;"`
 | 
			
		||||
// 	UpdatedAt    time.Time `json:"updated_at" gorm:"column:updated_at;index;"`
 | 
			
		||||
// 	CreatedAt    time.Time `json:"created_at" gorm:"column:created_at;index;"`
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
type Token struct {
 | 
			
		||||
	ID           string    `json:"id" db:"id" gorm:"column:id;size:255;primary_key;"`
 | 
			
		||||
	UserID       string    `json:"user_id" db:"user_id" gorm:"column:user_id;size:255;index;"`
 | 
			
		||||
	Token        string    `json:"token" gorm:"column:token;size:255;index;"`
 | 
			
		||||
	RefreshToken string    `json:"refresh_token" gorm:"column:token;size:255;index;"`
 | 
			
		||||
	Status       string    `json:"status" gorm:"column:status;size:10;index;"`
 | 
			
		||||
	ExpireAt     time.Time `json:"expire_at" gorm:"column:expire_at;index;"`
 | 
			
		||||
	UpdatedAt    time.Time `json:"updated_at" gorm:"column:updated_at;index;"`
 | 
			
		||||
	CreatedAt    time.Time `json:"created_at" gorm:"column:created_at;index;"`
 | 
			
		||||
	ID           string `json:"-" db:"id" gorm:"primary_key"`
 | 
			
		||||
	UserID       string `json:"-" gorm:"index;"`
 | 
			
		||||
	Token        string `json:"token" gorm:"size:255;index;"`
 | 
			
		||||
	RefreshToken string `json:"refreshToken" gorm:"size:255"`
 | 
			
		||||
 | 
			
		||||
	Expires        int64 `json:"expires"`
 | 
			
		||||
	RefreshExpires int64 `json:"refreshExpires"`
 | 
			
		||||
 | 
			
		||||
	UpdatedAt time.Time `json:"-" gorm:"type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;index;->:false"`
 | 
			
		||||
	CreatedAt time.Time `json:"-" gorm:"type:DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;index;->:false;<-:create"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RefreshTokenRequest struct {
 | 
			
		||||
	RefreshToken string `json:"refresh_token" binding:"required"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TokenResponse struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,21 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
type User struct {
 | 
			
		||||
	ID       string `json:"id" db:"id" gorm:"column:id;size:255;primary_key;"`
 | 
			
		||||
	Username string `json:"username" db:"username" gorm:"column:username;size:50;uniqueIndex;"`
 | 
			
		||||
	Score    int32  `json:"score" db:"score" gorm:"column:score;"`
 | 
			
		||||
	Money    int32  `json:"money" db:"money" gorm:"column:money;"`
 | 
			
		||||
	Password string `json:"-" db:"password" gorm:"column:password;size:255;not null;"`
 | 
			
		||||
	ID        string    `json:"id" db:"id" example:"137c1683-2ad6-4201-b256-253828b61c49" gorm:"column:id;size:255;primary_key;"`
 | 
			
		||||
	Username  string    `json:"username" db:"username" example:"user0" gorm:"column:username;size:50;uniqueIndex;"`
 | 
			
		||||
	Name      string    `json:"name" db:"name" example:"홍길동" gorm:"column:name;size:50;"`
 | 
			
		||||
	Score     int32     `json:"score" db:"score" example:"9999" gorm:"column:score;"`
 | 
			
		||||
	Password  string    `json:"-" db:"password" gorm:"column:password;size:255;not null;"`
 | 
			
		||||
	UpdatedAt time.Time `json:"-" gorm:"column:updated_at;index;"`
 | 
			
		||||
	CreatedAt time.Time `json:"-" gorm:"column:created_at;index;"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserListResponse struct {
 | 
			
		||||
	Data      []User `json:"data"`
 | 
			
		||||
	Total     int64  `json:"total" example:"90"`
 | 
			
		||||
	Page      int    `json:"page" example:"1"`
 | 
			
		||||
	TotalPage int64  `json:"totalPage" example:"9"`
 | 
			
		||||
	PageSize  int    `json:"pageSize" example:"10"`
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								internal/repositories/program.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								internal/repositories/program.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
package repositories
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type programRepository struct {
 | 
			
		||||
	DB *gorm.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewProgramRepository(db *gorm.DB) ProgramRepository {
 | 
			
		||||
	return &programRepository{
 | 
			
		||||
		DB: db,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ProgramRepository interface {
 | 
			
		||||
	List(string, string, int, int) (*[]models.Program, error)
 | 
			
		||||
	Total(string, string) (int64, error)
 | 
			
		||||
 | 
			
		||||
	Find(string) (*models.Program, error)
 | 
			
		||||
	Create(*models.Program) (*models.Program, error)
 | 
			
		||||
	Update(*models.Program) (*models.Program, error)
 | 
			
		||||
	Delete(string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *programRepository) List(q string, tag string, page int, limit int) (*[]models.Program, error) {
 | 
			
		||||
	var programs *[]models.Program
 | 
			
		||||
	var err error
 | 
			
		||||
	offset := limit * (page - 1)
 | 
			
		||||
	fmt.Println("q", q)
 | 
			
		||||
	if q != "" {
 | 
			
		||||
		fmt.Println(" 1 q", q)
 | 
			
		||||
		err = r.DB.Offset(offset).Limit(limit).Order("created_at DESC").Where("subject LIKE ? OR course LIKE ? OR tags LIKE ?", "%"+q+"%", "%"+q+"%", "%"+q+"%").Find(&programs).Error
 | 
			
		||||
	} else if tag != "" {
 | 
			
		||||
		fmt.Println(" tag", tag)
 | 
			
		||||
		err = r.DB.Offset(offset).Limit(limit).Order("created_at DESC").Where("tags LIKE ?", "%"+tag+"%").Find(&programs).Error
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println(" query")
 | 
			
		||||
		err = r.DB.Offset(offset).Limit(limit).Order("created_at DESC").Find(&programs).Error
 | 
			
		||||
	}
 | 
			
		||||
	return programs, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *programRepository) Total(q string, tag string) (int64, error) {
 | 
			
		||||
	var total int64
 | 
			
		||||
	var err error
 | 
			
		||||
	if q != "" {
 | 
			
		||||
		err = r.DB.Model(&models.Program{}).Where("subject LIKE ? OR course LIKE ? OR tags LIKE ?", "%"+q+"%", "%"+q+"%", "%"+q+"%").Count(&total).Error
 | 
			
		||||
	} else if tag != "" {
 | 
			
		||||
		err = r.DB.Model(&models.Program{}).Where("tags LIKE ?", "%"+tag+"%").Count(&total).Error
 | 
			
		||||
	} else {
 | 
			
		||||
		err = r.DB.Model(&models.Program{}).Count(&total).Error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return total, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *programRepository) Find(id string) (*models.Program, error) {
 | 
			
		||||
	var program *models.Program
 | 
			
		||||
	err := r.DB.Where("id = ?", id).First(&program).Error
 | 
			
		||||
	return program, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *programRepository) Create(program *models.Program) (*models.Program, error) {
 | 
			
		||||
	err := r.DB.Create(&program).Error
 | 
			
		||||
	return program, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *programRepository) Update(program *models.Program) (*models.Program, error) {
 | 
			
		||||
	var row *models.Program
 | 
			
		||||
	if err := r.DB.Where("id=?", program.ID).First(&row).Error; err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := r.DB.Model(&row).Select("*").Updates(&program).Error
 | 
			
		||||
	return row, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *programRepository) Delete(id string) error {
 | 
			
		||||
	var program *models.Program
 | 
			
		||||
	if err := r.DB.Where("id=?", id).First(&program).Error; err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err := r.DB.Delete(&program).Error
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										84
									
								
								internal/repositories/quiz.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								internal/repositories/quiz.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
			
		||||
package repositories
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type quizRepository struct {
 | 
			
		||||
	DB *gorm.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewQuizRepository(db *gorm.DB) QuizRepository {
 | 
			
		||||
	return &quizRepository{
 | 
			
		||||
		DB: db,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type QuizRepository interface {
 | 
			
		||||
	List(string, int, int) (*[]models.Quiz, error)
 | 
			
		||||
	Total(string) (int64, error)
 | 
			
		||||
 | 
			
		||||
	Find(string) (*models.Quiz, error)
 | 
			
		||||
	Create(*models.Quiz) (*models.Quiz, error)
 | 
			
		||||
	Update(*models.Quiz) (*models.Quiz, error)
 | 
			
		||||
	Delete(string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *quizRepository) List(program_id string, page int, limit int) (*[]models.Quiz, error) {
 | 
			
		||||
	var quizzes *[]models.Quiz
 | 
			
		||||
	var err error
 | 
			
		||||
	offset := limit * (page - 1)
 | 
			
		||||
	if program_id != "" {
 | 
			
		||||
		fmt.Println("program_id", program_id)
 | 
			
		||||
		err = r.DB.Offset(offset).Limit(limit).Order("sequence ASC").Where("program_id = ?", program_id).Find(&quizzes).Error
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println("program_id", "none")
 | 
			
		||||
		err = r.DB.Offset(offset).Limit(limit).Find(&quizzes).Error
 | 
			
		||||
	}
 | 
			
		||||
	return quizzes, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *quizRepository) Total(program_id string) (int64, error) {
 | 
			
		||||
	var total int64
 | 
			
		||||
	var err error
 | 
			
		||||
	if program_id != "" {
 | 
			
		||||
		err = r.DB.Model(&models.Quiz{}).Where("program_id = ?", program_id).Count(&total).Error
 | 
			
		||||
	} else {
 | 
			
		||||
		err = r.DB.Model(&models.Quiz{}).Count(&total).Error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return total, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *quizRepository) Find(id string) (*models.Quiz, error) {
 | 
			
		||||
	var quiz *models.Quiz
 | 
			
		||||
	err := r.DB.Where("id = ?", id).First(&quiz).Error
 | 
			
		||||
	return quiz, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *quizRepository) Create(quiz *models.Quiz) (*models.Quiz, error) {
 | 
			
		||||
	err := r.DB.Create(&quiz).Error
 | 
			
		||||
	return quiz, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *quizRepository) Update(quiz *models.Quiz) (*models.Quiz, error) {
 | 
			
		||||
	var row *models.Quiz
 | 
			
		||||
	if err := r.DB.Where("id=?", quiz.ID).First(&row).Error; err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := r.DB.Model(&row).Select("*").Updates(&quiz).Error
 | 
			
		||||
	return row, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *quizRepository) Delete(id string) error {
 | 
			
		||||
	var quiz *models.Quiz
 | 
			
		||||
	if err := r.DB.Where("id=?", id).First(&quiz).Error; err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err := r.DB.Delete(&quiz).Error
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
package repositories
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	config "studioj/boilerplate_go/configs"
 | 
			
		||||
	"studioj/boilerplate_go/internal/models"
 | 
			
		||||
	config "learnsteam/learsteam-quiz-api/configs"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang-jwt/jwt/v5"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
@@ -21,6 +21,7 @@ func NewTokenRepository(db *gorm.DB) TokenRepository {
 | 
			
		||||
type TokenRepository interface {
 | 
			
		||||
	Generate(string, int64) (string, error)
 | 
			
		||||
	Find(string) (*models.Token, error)
 | 
			
		||||
	FindByRefreshToken(string, string) (*models.Token, error)
 | 
			
		||||
	Create(*models.Token) (*models.Token, error)
 | 
			
		||||
	Update(*models.Token) (*models.Token, error)
 | 
			
		||||
	Delete(string) error
 | 
			
		||||
@@ -43,6 +44,12 @@ func (r *tokenRepository) Find(id string) (*models.Token, error) {
 | 
			
		||||
	return token, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *tokenRepository) FindByRefreshToken(user_id string, refreshToken string) (*models.Token, error) {
 | 
			
		||||
	var token *models.Token
 | 
			
		||||
	err := r.DB.Where("user_id = ? AND refresh_token = ?", user_id, refreshToken).First(&token).Error
 | 
			
		||||
	return token, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *tokenRepository) Create(token *models.Token) (*models.Token, error) {
 | 
			
		||||
	err := r.DB.Create(&token).Error
 | 
			
		||||
	return token, err
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
package repositories
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"studioj/boilerplate_go/internal/models"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
@@ -17,21 +18,42 @@ func NewUserRepository(db *gorm.DB) UserRepository {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserRepository interface {
 | 
			
		||||
	List() (*[]models.User, error)
 | 
			
		||||
	FindByID(string) (*models.User, error)
 | 
			
		||||
	List(string, int, int) (*[]models.User, error)
 | 
			
		||||
	Total(string) (int64, error)
 | 
			
		||||
	Find(string) (*models.User, error)
 | 
			
		||||
	FindByUsername(string) (*models.User, error)
 | 
			
		||||
	Create(*models.User) (*models.User, error)
 | 
			
		||||
	Update(*models.User) (*models.User, error)
 | 
			
		||||
	Delete(string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *userRepository) List() (*[]models.User, error) {
 | 
			
		||||
func (r *userRepository) List(q string, page int, limit int) (*[]models.User, error) {
 | 
			
		||||
	var users *[]models.User
 | 
			
		||||
	err := r.DB.Find(&users).Error
 | 
			
		||||
	var err error
 | 
			
		||||
	offset := limit * (page - 1)
 | 
			
		||||
	fmt.Println("q", q)
 | 
			
		||||
	if q != "" {
 | 
			
		||||
		err = r.DB.Offset(offset).Limit(limit).Order("name ASC").Where("name LIKE ? OR username LIKE ?", "%"+q+"%", "%"+q+"%").Find(&users).Error
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println(" query")
 | 
			
		||||
		err = r.DB.Offset(offset).Limit(limit).Order("created_at DESC").Find(&users).Error
 | 
			
		||||
	}
 | 
			
		||||
	return users, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *userRepository) FindByID(id string) (*models.User, error) {
 | 
			
		||||
func (r *userRepository) Total(q string) (int64, error) {
 | 
			
		||||
	var total int64
 | 
			
		||||
	var err error
 | 
			
		||||
	if q != "" {
 | 
			
		||||
		err = r.DB.Model(&models.User{}).Where("name LIKE ? OR username LIKE ?", "%"+q+"%", "%"+q+"%").Count(&total).Error
 | 
			
		||||
	} else {
 | 
			
		||||
		err = r.DB.Model(&models.User{}).Count(&total).Error
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return total, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *userRepository) Find(id string) (*models.User, error) {
 | 
			
		||||
	var user *models.User
 | 
			
		||||
	err := r.DB.Where("id = ?", id).First(&user).Error
 | 
			
		||||
	return user, err
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,19 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"studioj/boilerplate_go/internal/controllers"
 | 
			
		||||
	"studioj/boilerplate_go/internal/repositories"
 | 
			
		||||
	"studioj/boilerplate_go/internal/services"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/controllers"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func InitAuthRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	r := NewAuthRouter(db, router)
 | 
			
		||||
	r.SetAuthRouter(db, router)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AuthRouter interface {
 | 
			
		||||
	SetRouter(db *gorm.DB, router *gin.Engine)
 | 
			
		||||
}
 | 
			
		||||
@@ -23,17 +28,12 @@ type authRouter struct {
 | 
			
		||||
	router          *gin.Engine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InitAuthRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	r := NewAuthRouter(db, router)
 | 
			
		||||
	r.SetAuthRouter(db, router)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAuthRouter(db *gorm.DB, router *gin.Engine) *authRouter {
 | 
			
		||||
	userRepository := repositories.NewUserRepository(db)
 | 
			
		||||
	tokenRepository := repositories.NewTokenRepository(db)
 | 
			
		||||
	service := services.NewAuthService(userRepository, tokenRepository)
 | 
			
		||||
	tokenService := services.NewTokenService(tokenRepository)
 | 
			
		||||
	controller := controllers.NewAuthController(service)
 | 
			
		||||
	controller := controllers.NewAuthController(service, tokenService)
 | 
			
		||||
 | 
			
		||||
	return &authRouter{
 | 
			
		||||
		db:              db,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										50
									
								
								internal/routers/program.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								internal/routers/program.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/controllers"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/middleware"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func InitProgramRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	r := NewProgramRouter(db, router)
 | 
			
		||||
	r.SetProgramRouter(db, router)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ProgramRouter interface {
 | 
			
		||||
	SetRouter(db *gorm.DB, router *gin.Engine)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type programRouter struct {
 | 
			
		||||
	db         *gorm.DB
 | 
			
		||||
	repository repositories.ProgramRepository
 | 
			
		||||
	service    services.ProgramService
 | 
			
		||||
	controller controllers.ProgramController
 | 
			
		||||
	router     *gin.Engine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewProgramRouter(db *gorm.DB, router *gin.Engine) *programRouter {
 | 
			
		||||
	repository := repositories.NewProgramRepository(db)
 | 
			
		||||
	service := services.NewProgramService(repository)
 | 
			
		||||
	controller := controllers.NewProgramController(service)
 | 
			
		||||
 | 
			
		||||
	return &programRouter{
 | 
			
		||||
		db:         db,
 | 
			
		||||
		repository: repository,
 | 
			
		||||
		service:    service,
 | 
			
		||||
		controller: controller,
 | 
			
		||||
		router:     router,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *programRouter) SetProgramRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	group := router.Group("/program")
 | 
			
		||||
	group.GET("", middleware.Auth(), r.controller.List)
 | 
			
		||||
	group.GET("/:id", middleware.Auth(), r.controller.Find)
 | 
			
		||||
	group.POST("", middleware.Auth(), r.controller.Create)
 | 
			
		||||
	group.PUT("/:id", middleware.Auth(), r.controller.Update)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								internal/routers/quiz.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								internal/routers/quiz.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/controllers"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/middleware"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func InitQuizRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	r := NewQuizRouter(db, router)
 | 
			
		||||
	r.SetQuizRouter(db, router)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type QuizRouter interface {
 | 
			
		||||
	SetRouter(db *gorm.DB, router *gin.Engine)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type quizRouter struct {
 | 
			
		||||
	db         *gorm.DB
 | 
			
		||||
	repository repositories.QuizRepository
 | 
			
		||||
	service    services.QuizService
 | 
			
		||||
	controller controllers.QuizController
 | 
			
		||||
	router     *gin.Engine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewQuizRouter(db *gorm.DB, router *gin.Engine) *quizRouter {
 | 
			
		||||
	repository := repositories.NewQuizRepository(db)
 | 
			
		||||
	service := services.NewQuizService(repository)
 | 
			
		||||
	controller := controllers.NewQuizController(service)
 | 
			
		||||
 | 
			
		||||
	return &quizRouter{
 | 
			
		||||
		db:         db,
 | 
			
		||||
		repository: repository,
 | 
			
		||||
		service:    service,
 | 
			
		||||
		controller: controller,
 | 
			
		||||
		router:     router,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *quizRouter) SetQuizRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	group := router.Group("/quiz")
 | 
			
		||||
	group.GET("", middleware.Auth(), r.controller.List)
 | 
			
		||||
	group.GET("/:id", middleware.Auth(), r.controller.Find)
 | 
			
		||||
	group.POST("", middleware.Auth(), r.controller.Create)
 | 
			
		||||
	group.PUT("/:id", middleware.Auth(), r.controller.Update)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/database"
 | 
			
		||||
 | 
			
		||||
	"studioj/boilerplate_go/internal/database"
 | 
			
		||||
	"github.com/gin-contrib/cors"
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Router *gin.Engine
 | 
			
		||||
@@ -11,9 +12,19 @@ var Router *gin.Engine
 | 
			
		||||
func Init() {
 | 
			
		||||
	gin.SetMode(gin.ReleaseMode)
 | 
			
		||||
	Router = gin.Default()
 | 
			
		||||
	config := cors.DefaultConfig()
 | 
			
		||||
	config.AllowOrigins = []string{"http://127.0.0.1:3000", "http://localhost:3000", "http://localhost:3030", "https://learnsteam-quiz.jongyeob.com"}
 | 
			
		||||
	config.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "PATCH"}
 | 
			
		||||
	config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type"}
 | 
			
		||||
	Router.Use(cors.New(config))
 | 
			
		||||
 | 
			
		||||
	maindb := database.GetDB()
 | 
			
		||||
 | 
			
		||||
	InitAuthRouter(maindb, Router)
 | 
			
		||||
	InitTokenRouter(maindb, Router)
 | 
			
		||||
	InitUserRouter(maindb, Router)
 | 
			
		||||
	InitProgramRouter(maindb, Router)
 | 
			
		||||
	InitQuizRouter(maindb, Router)
 | 
			
		||||
 | 
			
		||||
	InitSwaggerRouter(Router)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								internal/routers/swagger.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								internal/routers/swagger.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/controllers"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
 | 
			
		||||
	swaggerFiles "github.com/swaggo/files"
 | 
			
		||||
	ginSwagger "github.com/swaggo/gin-swagger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func InitSwaggerRouter(router *gin.Engine) {
 | 
			
		||||
	r := NewSwaggerRouter(router)
 | 
			
		||||
	r.SetSwaggerRouter(router)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SwaggerRouter interface {
 | 
			
		||||
	SetRouter(router *gin.Engine)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type swaggerRouter struct {
 | 
			
		||||
	router     *gin.Engine
 | 
			
		||||
	controller controllers.SwaggerController
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSwaggerRouter(router *gin.Engine) *swaggerRouter {
 | 
			
		||||
	controller := controllers.NewSwaggerController()
 | 
			
		||||
	return &swaggerRouter{
 | 
			
		||||
		router:     router,
 | 
			
		||||
		controller: controller,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *swaggerRouter) SetSwaggerRouter(router *gin.Engine) {
 | 
			
		||||
	group := router.Group("/swagger")
 | 
			
		||||
	group.GET("*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +1,19 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"studioj/boilerplate_go/internal/controllers"
 | 
			
		||||
	"studioj/boilerplate_go/internal/repositories"
 | 
			
		||||
	"studioj/boilerplate_go/internal/services"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/controllers"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func InitTokenRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	r := NewTokenRouter(db, router)
 | 
			
		||||
	r.SetTokenRouter(db, router)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TokenRouter interface {
 | 
			
		||||
	SetRouter(db *gorm.DB, router *gin.Engine)
 | 
			
		||||
}
 | 
			
		||||
@@ -21,11 +26,6 @@ type tokenRouter struct {
 | 
			
		||||
	router     *gin.Engine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InitTokenRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	r := NewTokenRouter(db, router)
 | 
			
		||||
	r.SetTokenRouter(db, router)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewTokenRouter(db *gorm.DB, router *gin.Engine) *tokenRouter {
 | 
			
		||||
	repository := repositories.NewTokenRepository(db)
 | 
			
		||||
	service := services.NewTokenService(repository)
 | 
			
		||||
@@ -41,6 +41,6 @@ func NewTokenRouter(db *gorm.DB, router *gin.Engine) *tokenRouter {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *tokenRouter) SetTokenRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	// group := router.Group("/token")
 | 
			
		||||
	// group.GET("refresh", middleware.Auth(), r.controller.Refresh)
 | 
			
		||||
	group := router.Group("/token")
 | 
			
		||||
	group.POST("refresh", r.controller.Refresh)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,20 @@
 | 
			
		||||
package routers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"studioj/boilerplate_go/internal/controllers"
 | 
			
		||||
	"studioj/boilerplate_go/internal/middleware"
 | 
			
		||||
	"studioj/boilerplate_go/internal/repositories"
 | 
			
		||||
	"studioj/boilerplate_go/internal/services"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/controllers"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/middleware"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/services"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"gorm.io/gorm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func InitUserRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	r := NewUserRouter(db, router)
 | 
			
		||||
	r.SetUserRouter(db, router)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserRouter interface {
 | 
			
		||||
	SetRouter(db *gorm.DB, router *gin.Engine)
 | 
			
		||||
}
 | 
			
		||||
@@ -22,11 +27,6 @@ type userRouter struct {
 | 
			
		||||
	router     *gin.Engine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func InitUserRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	r := NewUserRouter(db, router)
 | 
			
		||||
	r.SetUserRouter(db, router)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUserRouter(db *gorm.DB, router *gin.Engine) *userRouter {
 | 
			
		||||
	repository := repositories.NewUserRepository(db)
 | 
			
		||||
	tokenRepository := repositories.NewTokenRepository(db)
 | 
			
		||||
@@ -46,5 +46,6 @@ func NewUserRouter(db *gorm.DB, router *gin.Engine) *userRouter {
 | 
			
		||||
 | 
			
		||||
func (r *userRouter) SetUserRouter(db *gorm.DB, router *gin.Engine) {
 | 
			
		||||
	group := router.Group("/user")
 | 
			
		||||
	group.GET("", middleware.Auth(), r.controller.List)
 | 
			
		||||
	group.GET("/:id", middleware.Auth(), r.controller.Find)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,8 @@ package services
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"studioj/boilerplate_go/internal/models"
 | 
			
		||||
	"studioj/boilerplate_go/internal/repositories"
 | 
			
		||||
	"time"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"golang.org/x/crypto/bcrypt"
 | 
			
		||||
@@ -18,7 +17,6 @@ type authService struct {
 | 
			
		||||
type AuthService interface {
 | 
			
		||||
	Register(*models.RegisterRequest) (*models.User, error)
 | 
			
		||||
	Login(*models.LoginRequest) (*models.User, error)
 | 
			
		||||
	CreateToken(string) (*models.Token, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAuthService(userRepository repositories.UserRepository, tokenRepository repositories.TokenRepository) AuthService {
 | 
			
		||||
@@ -38,6 +36,8 @@ func (s *authService) Register(request *models.RegisterRequest) (*models.User, e
 | 
			
		||||
	newUser := models.User{
 | 
			
		||||
		ID:       uuid.NewString(),
 | 
			
		||||
		Username: request.Username,
 | 
			
		||||
		Name:     request.Name,
 | 
			
		||||
		Score:    0,
 | 
			
		||||
		Password: string(hash),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -61,30 +61,3 @@ func (s *authService) Login(request *models.LoginRequest) (*models.User, error)
 | 
			
		||||
	}
 | 
			
		||||
	return user, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *authService) CreateToken(user_id string) (*models.Token, error) {
 | 
			
		||||
	tokenExpiredAt := time.Now().Add(time.Hour * 24 * 30)
 | 
			
		||||
	accessToken, err := s.tokenRepository.Generate(user_id, tokenExpiredAt.Unix())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refreshExpiredAt := time.Now().Add(time.Hour * 24 * 90)
 | 
			
		||||
	refreshToken, err := s.tokenRepository.Generate(user_id, refreshExpiredAt.Unix())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newToken := &models.Token{
 | 
			
		||||
		ID:           uuid.NewString(),
 | 
			
		||||
		UserID:       user_id,
 | 
			
		||||
		Token:        accessToken,
 | 
			
		||||
		RefreshToken: refreshToken,
 | 
			
		||||
		Status:       "valid",
 | 
			
		||||
		ExpireAt:     tokenExpiredAt,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	token, err := s.tokenRepository.Create(newToken)
 | 
			
		||||
 | 
			
		||||
	return token, err
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										56
									
								
								internal/services/program.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								internal/services/program.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
package services
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type programService struct {
 | 
			
		||||
	repository repositories.ProgramRepository
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ProgramService interface {
 | 
			
		||||
	List(string, string, int, int) (*[]models.Program, error)
 | 
			
		||||
	Total(string, string) (int64, error)
 | 
			
		||||
 | 
			
		||||
	Find(string) (*models.Program, error)
 | 
			
		||||
	Create(*models.Program) (*models.Program, error)
 | 
			
		||||
	Update(*models.Program) (*models.Program, error)
 | 
			
		||||
	Delete(string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewProgramService(repository repositories.ProgramRepository) ProgramService {
 | 
			
		||||
	return &programService{
 | 
			
		||||
		repository: repository,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *programService) List(q string, tag string, page int, limit int) (*[]models.Program, error) {
 | 
			
		||||
	return s.repository.List(q, tag, page, limit)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *programService) Total(q string, tag string) (int64, error) {
 | 
			
		||||
	return s.repository.Total(q, tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *programService) Find(id string) (*models.Program, error) {
 | 
			
		||||
	return s.repository.Find(id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *programService) Create(program *models.Program) (*models.Program, error) {
 | 
			
		||||
	result, err := s.repository.Create(program)
 | 
			
		||||
 | 
			
		||||
	return result, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *programService) Update(program *models.Program) (*models.Program, error) {
 | 
			
		||||
	result, err := s.repository.Update(program)
 | 
			
		||||
 | 
			
		||||
	return result, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *programService) Delete(id string) error {
 | 
			
		||||
	err := s.repository.Delete(id)
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								internal/services/quiz.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								internal/services/quiz.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
package services
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type quizService struct {
 | 
			
		||||
	repository repositories.QuizRepository
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type QuizService interface {
 | 
			
		||||
	List(string, int, int) (*[]models.Quiz, error)
 | 
			
		||||
	Total(string) (int64, error)
 | 
			
		||||
 | 
			
		||||
	Find(string) (*models.Quiz, error)
 | 
			
		||||
	Create(*models.Quiz) (*models.Quiz, error)
 | 
			
		||||
	Update(*models.Quiz) (*models.Quiz, error)
 | 
			
		||||
	Delete(string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewQuizService(repository repositories.QuizRepository) QuizService {
 | 
			
		||||
	return &quizService{
 | 
			
		||||
		repository: repository,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *quizService) List(program_id string, page int, limit int) (*[]models.Quiz, error) {
 | 
			
		||||
	return s.repository.List(program_id, page, limit)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *quizService) Total(tag string) (int64, error) {
 | 
			
		||||
	return s.repository.Total(tag)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *quizService) Find(id string) (*models.Quiz, error) {
 | 
			
		||||
	return s.repository.Find(id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *quizService) Create(quiz *models.Quiz) (*models.Quiz, error) {
 | 
			
		||||
	result, err := s.repository.Create(quiz)
 | 
			
		||||
 | 
			
		||||
	return result, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *quizService) Update(quiz *models.Quiz) (*models.Quiz, error) {
 | 
			
		||||
	result, err := s.repository.Update(quiz)
 | 
			
		||||
 | 
			
		||||
	return result, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *quizService) Delete(id string) error {
 | 
			
		||||
	err := s.repository.Delete(id)
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +1,17 @@
 | 
			
		||||
package services
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	config "studioj/boilerplate_go/configs"
 | 
			
		||||
	"studioj/boilerplate_go/internal/models"
 | 
			
		||||
	"studioj/boilerplate_go/internal/repositories"
 | 
			
		||||
	config "learnsteam/learsteam-quiz-api/configs"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
 | 
			
		||||
	"github.com/golang-jwt/jwt/v5"
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type tokenService struct {
 | 
			
		||||
@@ -17,10 +20,12 @@ type tokenService struct {
 | 
			
		||||
 | 
			
		||||
type TokenService interface {
 | 
			
		||||
	Find(string) (*models.Token, error)
 | 
			
		||||
	Create(*models.Token) (*models.Token, error)
 | 
			
		||||
	Create(string) (*models.Token, error)
 | 
			
		||||
	Update(*models.Token) (*models.Token, error)
 | 
			
		||||
	Delete(string) error
 | 
			
		||||
 | 
			
		||||
	Refresh(string) (*models.Token, error)
 | 
			
		||||
 | 
			
		||||
	Generate(string, int64) (string, error)
 | 
			
		||||
	Verify(tokenString string) (*jwt.Token, error)
 | 
			
		||||
 | 
			
		||||
@@ -40,8 +45,31 @@ func (s *tokenService) Find(id string) (*models.Token, error) {
 | 
			
		||||
	return s.repository.Find(id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *tokenService) Create(token *models.Token) (*models.Token, error) {
 | 
			
		||||
	return s.repository.Create(token)
 | 
			
		||||
func (s *tokenService) Create(sub string) (*models.Token, error) {
 | 
			
		||||
	expiredAt := time.Now().Add(time.Hour * 24 * 30)
 | 
			
		||||
	accessToken, err := s.repository.Generate(sub, expiredAt.Unix())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refreshExpiredAt := time.Now().Add(time.Hour * 24 * 90)
 | 
			
		||||
	refreshToken, err := s.repository.Generate(sub, refreshExpiredAt.Unix())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newToken := &models.Token{
 | 
			
		||||
		ID:             uuid.NewString(),
 | 
			
		||||
		UserID:         sub,
 | 
			
		||||
		Token:          accessToken,
 | 
			
		||||
		RefreshToken:   refreshToken,
 | 
			
		||||
		Expires:        expiredAt.Unix(),
 | 
			
		||||
		RefreshExpires: refreshExpiredAt.Unix(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	token, err := s.repository.Create(newToken)
 | 
			
		||||
 | 
			
		||||
	return token, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *tokenService) Update(token *models.Token) (*models.Token, error) {
 | 
			
		||||
@@ -113,3 +141,60 @@ func (s *tokenService) ValidToken(jwtToken *jwt.Token) (bool, error) {
 | 
			
		||||
	}
 | 
			
		||||
	return jwtToken.Valid, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *tokenService) Refresh(refreshToken string) (*models.Token, error) {
 | 
			
		||||
	fmt.Println("refresh_token", refreshToken)
 | 
			
		||||
 | 
			
		||||
	jwtToken, err := jwt.Parse(refreshToken, func(jwtToken *jwt.Token) (interface{}, error) {
 | 
			
		||||
		if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok {
 | 
			
		||||
			return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"])
 | 
			
		||||
		}
 | 
			
		||||
		return []byte(config.SECRET_KEY), nil
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("refresh token is not valid")
 | 
			
		||||
	} else {
 | 
			
		||||
 | 
			
		||||
		fmt.Println("refresh token is  valid")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("error", err.Error())
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	claims, ok := jwtToken.Claims.(jwt.MapClaims)
 | 
			
		||||
	if !ok || !jwtToken.Valid {
 | 
			
		||||
		return nil, errors.New("refresh token is invalid")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sub := fmt.Sprintf("%s", claims["sub"])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.New("wrong user")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	token, err := s.repository.FindByRefreshToken(sub, refreshToken)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.New("wrong token")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expiredAt := time.Now().Add(time.Hour * 24 * 30)
 | 
			
		||||
	accessToken, err := s.repository.Generate(sub, expiredAt.Unix())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refreshExpiredAt := time.Now().Add(time.Hour * 24 * 90)
 | 
			
		||||
	refreshToken, err = s.repository.Generate(sub, refreshExpiredAt.Unix())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	token.Token = accessToken
 | 
			
		||||
	token.Expires = expiredAt.Unix()
 | 
			
		||||
	token.RefreshToken = refreshToken
 | 
			
		||||
	token.RefreshExpires = refreshExpiredAt.Unix()
 | 
			
		||||
 | 
			
		||||
	return s.repository.Update(token)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
package services
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"studioj/boilerplate_go/internal/models"
 | 
			
		||||
	"studioj/boilerplate_go/internal/repositories"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/models"
 | 
			
		||||
	"learnsteam/learsteam-quiz-api/internal/repositories"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type userService struct {
 | 
			
		||||
@@ -11,7 +11,9 @@ type userService struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserService interface {
 | 
			
		||||
	FindByID(string) (*models.User, error)
 | 
			
		||||
	List(string, int, int) (*[]models.User, error)
 | 
			
		||||
	Total(string) (int64, error)
 | 
			
		||||
	Find(string) (*models.User, error)
 | 
			
		||||
	FindByUsername(string) (*models.User, error)
 | 
			
		||||
	Create(*models.User) (*models.User, error)
 | 
			
		||||
}
 | 
			
		||||
@@ -23,8 +25,16 @@ func NewUserService(repository repositories.UserRepository, tokenRepository repo
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *userService) FindByID(id string) (*models.User, error) {
 | 
			
		||||
	return s.repository.FindByID(id)
 | 
			
		||||
func (s *userService) List(q string, page int, limit int) (*[]models.User, error) {
 | 
			
		||||
	return s.repository.List(q, page, limit)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *userService) Total(q string) (int64, error) {
 | 
			
		||||
	return s.repository.Total(q)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *userService) Find(id string) (*models.User, error) {
 | 
			
		||||
	return s.repository.Find(id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *userService) FindByUsername(username string) (*models.User, error) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user