This commit is contained in:
JongYeob Sheen 2023-12-29 00:27:12 +09:00
parent b4a0b72f66
commit c31a16843d
35 changed files with 451 additions and 261 deletions

View File

@ -1,10 +1,10 @@
package config package config
const ( const (
BASE_URL = "https://cslms-api.jongyeob.com" MODE = "debug"
PORT = ":3030" BASE_URL = "https://codingschool.co.kr"
DATABASE_URL = "cslms:atobot2013!#%@tcp(ma.codingschool.co.kr:3306)/cslms5?charset=utf8&parseTime=True&loc=Local" PORT = ":30303"
//DATABASE_URL = "root:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(learnsteam-quiz-db:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local" DATABASE_URL = "cslms:atobot2013!#%@tcp(localhost:3306)/cslms5?charset=utf8&parseTime=True&loc=Local"
KAKAO_CLIENT_ID = "986830" KAKAO_CLIENT_ID = "986830"
KAKAO_CLIENT_SECRET = "14f63a8e91c4e0fe88bc40e3ff348233" KAKAO_CLIENT_SECRET = "14f63a8e91c4e0fe88bc40e3ff348233"

View File

@ -1,6 +1,7 @@
package config package config
const ( const (
MODE = "debug"
BASE_URL = "http://localhost:3030" BASE_URL = "http://localhost:3030"
PORT = ":3030" PORT = ":3030"
DATABASE_URL = "cslms:atobot2013!#%@tcp(localhost:33061)/cslms5?charset=utf8&parseTime=True&loc=Local" DATABASE_URL = "cslms:atobot2013!#%@tcp(localhost:33061)/cslms5?charset=utf8&parseTime=True&loc=Local"

View File

@ -1,6 +1,7 @@
package config package config
const ( const (
MODE = "debug"
BASE_URL = "http://localhost:3030" BASE_URL = "http://localhost:3030"
PORT = ":3030" PORT = ":3030"
DATABASE_URL = "cslms:atobot2013!#%@tcp(localhost:33061)/cslms5?charset=utf8&parseTime=True&loc=Local" DATABASE_URL = "cslms:atobot2013!#%@tcp(localhost:33061)/cslms5?charset=utf8&parseTime=True&loc=Local"

View File

@ -1,8 +1,9 @@
package config package config
const ( const (
BASE_URL = "https://cslms-api.jongyeob.com" MODE = "release"
PORT = ":3030" BASE_URL = "https://cslms-api.codingschool.co.kr"
PORT = ":30303"
DATABASE_URL = "root:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(learnsteam-quiz-db:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local" DATABASE_URL = "root:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(learnsteam-quiz-db:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local"
KAKAO_CLIENT_ID = "986830" KAKAO_CLIENT_ID = "986830"

View File

@ -15,7 +15,7 @@ WORKDIR /app
RUN apk add --no-cache sqlite-libs mariadb-connector-c libgcc RUN apk add --no-cache sqlite-libs mariadb-connector-c libgcc
COPY --from=builder /app/bootstrap . COPY --from=builder /app/bootstrap .
EXPOSE 3030 EXPOSE 30303
ENV PORT 3030 ENV PORT 30303
CMD ["sh", "-c", "./bootstrap"] CMD ["sh", "-c", "./bootstrap"]

View File

@ -7,4 +7,6 @@ services:
dockerfile: docker/dev/Dockerfile dockerfile: docker/dev/Dockerfile
image: learnsteam/cslms-api:dev image: learnsteam/cslms-api:dev
ports: ports:
- "3030:3030" - "30303:30303"
network_mode: "host"
restart: on-failure

View File

@ -8,3 +8,5 @@ services:
image: learnsteam/cslms-api:local image: learnsteam/cslms-api:local
ports: ports:
- "3030:3030" - "3030:3030"
network_mode: "host"
restart: on-failure

View File

@ -15,7 +15,7 @@ WORKDIR /app
RUN apk add --no-cache sqlite-libs mariadb-connector-c libgcc RUN apk add --no-cache sqlite-libs mariadb-connector-c libgcc
COPY --from=builder /app/bootstrap . COPY --from=builder /app/bootstrap .
EXPOSE 3030 EXPOSE 30303
ENV PORT 3030 ENV PORT 30303
CMD ["sh", "-c", "./bootstrap"] CMD ["sh", "-c", "./bootstrap"]

View File

@ -7,4 +7,6 @@ services:
dockerfile: docker/prod/Dockerfile dockerfile: docker/prod/Dockerfile
image: learnsteam/cslms-api image: learnsteam/cslms-api
ports: ports:
- "3030:3030" - "30303:30303"
network_mode: "host"
restart: on-failure

View File

@ -52,6 +52,27 @@ const docTemplate = `{
} }
} }
}, },
"/auth/logout": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "로그아웃하고 Token, Cookie 삭제",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"로그아웃"
],
"summary": "로그아웃",
"responses": {}
}
},
"/auth/register": { "/auth/register": {
"post": { "post": {
"description": "회원가입", "description": "회원가입",
@ -1319,7 +1340,7 @@ const docTemplate = `{
"data": { "data": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.Quiz" "$ref": "#/definitions/learnsteam_cslms-api_internal_models.QuizResponse"
} }
}, },
"page": { "page": {
@ -1459,8 +1480,14 @@ const docTemplate = `{
"tag": { "tag": {
"type": "array", "type": "array",
"items": { "items": {
"type": "integer" "type": "string"
} },
"example": [
"Python",
"AI",
"Robot",
"파이썬"
]
}, },
"title": { "title": {
"type": "string", "type": "string",
@ -1498,8 +1525,14 @@ const docTemplate = `{
"tag": { "tag": {
"type": "array", "type": "array",
"items": { "items": {
"type": "integer" "type": "string"
} },
"example": [
"Python",
"AI",
"Robot",
"파이썬"
]
}, },
"title": { "title": {
"type": "string", "type": "string",
@ -1867,7 +1900,13 @@ const docTemplate = `{
"type": "array", "type": "array",
"items": { "items": {
"type": "integer" "type": "integer"
} },
"example": [
1000001,
1000002,
1000003,
1000004
]
} }
} }
}, },
@ -1975,6 +2014,10 @@ const docTemplate = `{
"type": "string", "type": "string",
"example": "choice" "example": "choice"
}, },
"quiz_id": {
"type": "integer",
"example": 1000001
},
"quiz_paper_id": { "quiz_paper_id": {
"type": "integer", "type": "integer",
"example": 1000001 "example": 1000001
@ -2037,6 +2080,10 @@ const docTemplate = `{
"type": "string", "type": "string",
"example": "check" "example": "check"
}, },
"quiz_id": {
"type": "integer",
"example": 1000001
},
"result": { "result": {
"type": "string", "type": "string",
"example": "success" "example": "success"

View File

@ -44,6 +44,27 @@
} }
} }
}, },
"/auth/logout": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "로그아웃하고 Token, Cookie 삭제",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"로그아웃"
],
"summary": "로그아웃",
"responses": {}
}
},
"/auth/register": { "/auth/register": {
"post": { "post": {
"description": "회원가입", "description": "회원가입",
@ -1311,7 +1332,7 @@
"data": { "data": {
"type": "array", "type": "array",
"items": { "items": {
"$ref": "#/definitions/learnsteam_cslms-api_internal_models.Quiz" "$ref": "#/definitions/learnsteam_cslms-api_internal_models.QuizResponse"
} }
}, },
"page": { "page": {
@ -1451,8 +1472,14 @@
"tag": { "tag": {
"type": "array", "type": "array",
"items": { "items": {
"type": "integer" "type": "string"
} },
"example": [
"Python",
"AI",
"Robot",
"파이썬"
]
}, },
"title": { "title": {
"type": "string", "type": "string",
@ -1490,8 +1517,14 @@
"tag": { "tag": {
"type": "array", "type": "array",
"items": { "items": {
"type": "integer" "type": "string"
} },
"example": [
"Python",
"AI",
"Robot",
"파이썬"
]
}, },
"title": { "title": {
"type": "string", "type": "string",
@ -1859,7 +1892,13 @@
"type": "array", "type": "array",
"items": { "items": {
"type": "integer" "type": "integer"
} },
"example": [
1000001,
1000002,
1000003,
1000004
]
} }
} }
}, },
@ -1967,6 +2006,10 @@
"type": "string", "type": "string",
"example": "choice" "example": "choice"
}, },
"quiz_id": {
"type": "integer",
"example": 1000001
},
"quiz_paper_id": { "quiz_paper_id": {
"type": "integer", "type": "integer",
"example": 1000001 "example": 1000001
@ -2029,6 +2072,10 @@
"type": "string", "type": "string",
"example": "check" "example": "check"
}, },
"quiz_id": {
"type": "integer",
"example": 1000001
},
"result": { "result": {
"type": "string", "type": "string",
"example": "success" "example": "success"

View File

@ -146,7 +146,7 @@ definitions:
properties: properties:
data: data:
items: items:
$ref: '#/definitions/learnsteam_cslms-api_internal_models.Quiz' $ref: '#/definitions/learnsteam_cslms-api_internal_models.QuizResponse'
type: array type: array
page: page:
example: 1 example: 1
@ -244,8 +244,13 @@ definitions:
example: "on" example: "on"
type: string type: string
tag: tag:
example:
- Python
- AI
- Robot
- 파이썬
items: items:
type: integer type: string
type: array type: array
title: title:
example: 퀴즈 시트 제목 example: 퀴즈 시트 제목
@ -272,8 +277,13 @@ definitions:
example: "on" example: "on"
type: string type: string
tag: tag:
example:
- Python
- AI
- Robot
- 파이썬
items: items:
type: integer type: string
type: array type: array
title: title:
example: 퀴즈 시트 제목 example: 퀴즈 시트 제목
@ -533,6 +543,11 @@ definitions:
example: 1000002 example: 1000002
type: integer type: integer
users: users:
example:
- 1000001
- 1000002
- 1000003
- 1000004
items: items:
type: integer type: integer
type: array type: array
@ -612,6 +627,9 @@ definitions:
question_type: question_type:
example: choice example: choice
type: string type: string
quiz_id:
example: 1000001
type: integer
quiz_paper_id: quiz_paper_id:
example: 1000001 example: 1000001
type: integer type: integer
@ -657,6 +675,9 @@ definitions:
question_type: question_type:
example: check example: check
type: string type: string
quiz_id:
example: 1000001
type: integer
result: result:
example: success example: success
type: string type: string
@ -706,6 +727,19 @@ paths:
summary: 사용자 로그인 로그인 summary: 사용자 로그인 로그인
tags: tags:
- 로그인 - 로그인
/auth/logout:
post:
consumes:
- application/json
description: 로그아웃하고 Token, Cookie 삭제
produces:
- application/json
responses: {}
security:
- Bearer: []
summary: 로그아웃
tags:
- 로그아웃
/auth/register: /auth/register:
post: post:
consumes: consumes:

4
go.mod
View File

@ -14,6 +14,7 @@ require (
golang.org/x/crypto v0.15.0 golang.org/x/crypto v0.15.0
gorm.io/datatypes v1.2.0 gorm.io/datatypes v1.2.0
gorm.io/driver/mysql v1.5.2 gorm.io/driver/mysql v1.5.2
gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.5 gorm.io/gorm v1.25.5
) )
@ -43,6 +44,7 @@ require (
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect
@ -56,11 +58,11 @@ require (
golang.org/x/tools v0.15.0 // indirect golang.org/x/tools v0.15.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/sqlite v1.5.4 // indirect
) )
require ( require (
github.com/aws/aws-lambda-go v1.17.0 // indirect github.com/aws/aws-lambda-go v1.17.0 // indirect
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/tj/assert v0.0.3
) )

View File

@ -1,7 +1,9 @@
package controllers package controllers
import ( import (
"fmt"
"net/http" "net/http"
"strings"
"learnsteam/cslms-api/internal/models" "learnsteam/cslms-api/internal/models"
"learnsteam/cslms-api/internal/services" "learnsteam/cslms-api/internal/services"
@ -12,6 +14,7 @@ import (
type AuthController interface { type AuthController interface {
Register(*gin.Context) Register(*gin.Context)
Login(*gin.Context) Login(*gin.Context)
Logout(*gin.Context)
} }
type authController struct { type authController struct {
@ -98,25 +101,47 @@ func (controller *authController) Login(c *gin.Context) {
}) })
} }
// Logout // Logout 로그아웃
//
// @Summary 로그아웃
// @Description 로그아웃하고 Token, Cookie 삭제
// @Tags 로그아웃
//
// @Accept json
// @Produce json
//
// @Security Bearer
//
// @Router /auth/logout [post]
func (controller *authController) Logout(c *gin.Context) { func (controller *authController) Logout(c *gin.Context) {
token := c.GetHeader("Authorization") token, err := func() (*string, error) {
if token == "" { authorization := c.GetHeader("Authorization")
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"}) strArr := strings.Split(authorization, " ")
return if len(strArr) == 2 {
return &strArr[1], nil
} else {
return nil, fmt.Errorf("invalid authorization header")
} }
}()
err := controller.tokenService.Delete(token)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return return
} }
user_id := c.GetInt64("sub")
err = controller.tokenService.Invalid(user_id, *token)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.SetCookie("Authorization", "", -1, "/", "localhost", false, true) c.SetCookie("Authorization", "", -1, "/", "localhost", false, true)
c.SetCookie("RefreshToken", "", -1, "/", "localhost", false, true) c.SetCookie("RefreshToken", "", -1, "/", "localhost", false, true)
c.SetCookie("ExpiresAt", "", -1, "/", "localhost", false, true) c.SetCookie("ExpiresAt", "", -1, "/", "localhost", false, true)
c.SetCookie("RefreshExpiresAt", "", -1, "/", "localhost", false, true) c.SetCookie("RefreshExpiresAt", "", -1, "/", "localhost", false, true)
c.SetCookie("RefreshTokenExpiresAt", "", -1, "/", "localhost", false, true) c.SetCookie("RefreshTokenExpiresAt", "", -1, "/", "localhost", false, true)
c.SetCookie("RefreshTokenExpiresAt", "", -1, "/", "localhost", false, true)
c.SetCookie("RefreshTokenExpiresAt", "", -1, "/", "localhost", false, true)
c.JSON(http.StatusOK, gin.H{"message": "logout"}) c.JSON(http.StatusOK, gin.H{"message": "logout"})
} }

View File

@ -1,6 +1,7 @@
package controllers package controllers
import ( import (
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
@ -11,6 +12,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
"gorm.io/datatypes"
) )
type QuizPaperController interface { type QuizPaperController interface {
@ -147,6 +149,12 @@ func (controller *quizPaperController) Create(c *gin.Context) {
user_id := c.GetInt64("sub") user_id := c.GetInt64("sub")
var tag datatypes.JSON
tag, err := json.Marshal(request.Tag)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
quizPaper := &models.QuizPaper{ quizPaper := &models.QuizPaper{
GUID: uuid.NewString(), GUID: uuid.NewString(),
CenterID: request.CenterID, CenterID: request.CenterID,
@ -154,7 +162,7 @@ func (controller *quizPaperController) Create(c *gin.Context) {
Title: request.Title, Title: request.Title,
Content: request.Content, Content: request.Content,
Category: request.Category, Category: request.Category,
Tag: request.Tag, Tag: tag,
Status: request.Status, Status: request.Status,
CreatedAt: time.Now(), CreatedAt: time.Now(),
UpdatedAt: time.Now(), UpdatedAt: time.Now(),
@ -204,13 +212,20 @@ func (controller *quizPaperController) Update(c *gin.Context) {
return return
} }
var tag datatypes.JSON
tag, err = json.Marshal(request.Tag)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user_id := c.GetInt64("sub") user_id := c.GetInt64("sub")
quizPaper.ManagerID = user_id quizPaper.ManagerID = user_id
quizPaper.CenterID = request.CenterID quizPaper.CenterID = request.CenterID
quizPaper.Title = request.Title quizPaper.Title = request.Title
quizPaper.Content = request.Content quizPaper.Content = request.Content
quizPaper.Category = request.Category quizPaper.Category = request.Category
quizPaper.Tag = request.Tag quizPaper.Tag = tag
quizPaper.Status = request.Status quizPaper.Status = request.Status
quizPaper.UpdatedAt = time.Now() quizPaper.UpdatedAt = time.Now()

View File

@ -109,18 +109,6 @@ func (controller *userController) Find(c *gin.Context) {
return return
} }
sub := c.GetString("sub")
user_id, err := strconv.ParseInt(sub, 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if user_id != id {
c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong user"})
return
}
result, err := controller.service.Find(id) result, err := controller.service.Find(id)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})

View File

@ -159,6 +159,7 @@ func (controller *userQuizController) Create(c *gin.Context) {
CenterID: request.CenterID, CenterID: request.CenterID,
UserQuizPaperID: request.UserQuizPaperID, UserQuizPaperID: request.UserQuizPaperID,
No: request.No, No: request.No,
QuizID: request.QuizID,
QuestionType: request.QuestionType, QuestionType: request.QuestionType,
Question: request.Question, Question: request.Question,
Content: content, Content: content,
@ -223,8 +224,29 @@ func (controller *userQuizController) Update(c *gin.Context) {
// Result string `json:"result" example:"success"` // Result string `json:"result" example:"success"`
// Score float32 `json:"score" example:"10"` // Score float32 `json:"score" example:"10"`
var content datatypes.JSON
content, err = json.Marshal(request.Content)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var answer datatypes.JSON
answer, err = json.Marshal([]string{})
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userquiz.CenterID = request.CenterID userquiz.CenterID = request.CenterID
userquiz.UserQuizPaperID = request.UserQuizPaperID userquiz.UserQuizPaperID = request.UserQuizPaperID
userquiz.No = request.No
userquiz.QuestionType = request.QuestionType
userquiz.Question = request.Question
userquiz.Content = content
userquiz.Answer = answer
userquiz.Status = request.Status
userquiz.Score = request.Score
result, err := controller.service.Update(userquiz) result, err := controller.service.Update(userquiz)
if err != nil { if err != nil {

View File

@ -1,7 +1,6 @@
package controllers package controllers
import ( import (
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
@ -160,14 +159,14 @@ func (controller *userQuizPaperController) Create(c *gin.Context) {
return return
} }
var users []int64 // var users []int64
err = json.Unmarshal(request.Users, &users) // err = json.Unmarshal(request.Users, &users)
if err != nil { // if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) // c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return // return
} // }
templates := controller.service.Generate(users, quizPaper) templates := controller.service.Generate(request.Users, quizPaper)
userQuizPapers, err := controller.service.Insert(templates) userQuizPapers, err := controller.service.Insert(templates)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})

View File

@ -7,6 +7,8 @@ import (
"strings" "strings"
config "learnsteam/cslms-api/configs" config "learnsteam/cslms-api/configs"
"learnsteam/cslms-api/internal/database"
"learnsteam/cslms-api/internal/models"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
@ -35,10 +37,14 @@ func Auth(permission string) gin.HandlerFunc {
return return
} }
fmt.Println("token", extract(c.Request)) valid := Valid(c.Request)
fmt.Println("sub", sub) if !valid {
c.JSON(http.StatusUnauthorized, gin.H{"error": "token is not valid"})
c.Abort()
return
}
c.Set("token", extract(c.Request)) c.Set("token", Extract(c.Request))
c.Set("sub", sub) c.Set("sub", sub)
c.Set("role", role) c.Set("role", role)
c.Next() c.Next()
@ -67,17 +73,21 @@ func Permission(permission *string) gin.HandlerFunc {
return return
} }
fmt.Println("token", extract(c.Request)) valid := Valid(c.Request)
fmt.Println("sub", sub) if !valid {
c.JSON(http.StatusUnauthorized, gin.H{"error": "token is not valid"})
c.Abort()
return
}
c.Set("token", extract(c.Request)) c.Set("token", Extract(c.Request))
c.Set("sub", sub) c.Set("sub", sub)
c.Set("role", role) c.Set("role", role)
c.Next() c.Next()
} }
} }
func extract(r *http.Request) string { func Extract(r *http.Request) string {
authorization := r.Header.Get("Authorization") authorization := r.Header.Get("Authorization")
strArr := strings.Split(authorization, " ") strArr := strings.Split(authorization, " ")
if len(strArr) == 2 { if len(strArr) == 2 {
@ -86,8 +96,8 @@ func extract(r *http.Request) string {
return "" return ""
} }
func verify(r *http.Request) (*jwt.Token, error) { func Verify(r *http.Request) (*jwt.Token, error) {
tokenString := extract(r) tokenString := Extract(r)
jwtToken, err := jwt.Parse(tokenString, func(jwtToken *jwt.Token) (interface{}, error) { jwtToken, err := jwt.Parse(tokenString, func(jwtToken *jwt.Token) (interface{}, error) {
if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok { if _, ok := jwtToken.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"]) return nil, fmt.Errorf("unexpected signing method: %v", jwtToken.Header["alg"])
@ -99,8 +109,9 @@ func verify(r *http.Request) (*jwt.Token, error) {
} }
func UserID(r *http.Request) (int64, error) { func UserID(r *http.Request) (int64, error) {
jwtToken, err := verify(r) jwtToken, err := Verify(r)
if err != nil { if err != nil {
fmt.Println(err)
return -1, err return -1, err
} }
@ -120,7 +131,7 @@ func UserID(r *http.Request) (int64, error) {
} }
func Role(r *http.Request) (*string, error) { func Role(r *http.Request) (*string, error) {
jwtToken, err := verify(r) jwtToken, err := Verify(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -134,3 +145,12 @@ func Role(r *http.Request) (*string, error) {
return &role, nil return &role, nil
} }
func Valid(r *http.Request) bool {
tokenString := Extract(r)
var token models.Token
fmt.Println(tokenString)
err := database.DB.Where("token = ? AND status = ?", tokenString, "on").First(&token).Error
fmt.Println(&token)
return err == nil
}

View File

@ -4,21 +4,6 @@ import (
"time" "time"
) )
// CREATE TABLE `Centers` (
// `id` bigint(20) NOT NULL AUTO_INCREMENT,
// `guid_id` varchar(36) DEFAULT '',
// `center_title` varchar(50) DEFAULT '' COMMENT '강동런스팀로봇코딩학원',
// `center_name` varchar(50) DEFAULT '' COMMENT 'learnsteam_kd',
// `owner_id` bigint(20) DEFAULT 0,
// `content_page` text DEFAULT '' COMMENT '학원상세페이지, html/마크다운/text',
// `company_info` text DEFAULT '' COMMENT '사업자정보-json\r\n기타 정보 추가 가능\r\n\r\n{\r\n "company_ceo": "대표자이름",\r\n "company_code": "사업자번호",\r\n "company_phone": "대표전화번호",\r\n "company_email": "대표전자메일",\r\n "company_homepage": "대표홈페이지 url",\r\n "company_logo": "로고이미지 url",\r\n "company_cover_image": "대표 이미지", \r\n}',
// `memo` varchar(256) DEFAULT '',
// `created_at` timestamp NULL DEFAULT current_timestamp(),
// `updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
// `status` varchar(10) DEFAULT NULL,
// PRIMARY KEY (`id`)
// ) ENGINE=InnoDB AUTO_INCREMENT=1000005 DEFAULT CHARSET=utf8mb4 COMMENT='Centers의 이미지/영상은 Links에서 관리됨. ';
type Center struct { type Center struct {
ID int64 `json:"id" db:"id" example:"100001" gorm:"column:id;primary_key;"` ID int64 `json:"id" db:"id" example:"100001" gorm:"column:id;primary_key;"`
GUID string `json:"guid_id" db:"guid_id" example:"2036023a-fb56-4b6c-b3bb-c787c681ada6" gorm:"column:guid_id;size:255;index;"` GUID string `json:"guid_id" db:"guid_id" example:"2036023a-fb56-4b6c-b3bb-c787c681ada6" gorm:"column:guid_id;size:255;index;"`

View File

@ -59,7 +59,7 @@ type QuizResponse struct {
} }
type QuizListResponse struct { type QuizListResponse struct {
Data []Quiz `json:"data"` Data []QuizResponse `json:"data"`
Total int64 `json:"total" example:"5"` Total int64 `json:"total" example:"5"`
Page int `json:"page" example:"1"` Page int `json:"page" example:"1"`
TotalPage int64 `json:"totalPage" example:"1"` TotalPage int64 `json:"totalPage" example:"1"`

View File

@ -6,24 +6,6 @@ import (
"gorm.io/datatypes" "gorm.io/datatypes"
) )
// CREATE TABLE `QuizPapers` (
// `id` bigint(20) NOT NULL AUTO_INCREMENT,
// `guid_id` char(36) NOT NULL DEFAULT '0',
// `center_id` bigint(20) NOT NULL DEFAULT 0,
// `manager_id` bigint(20) NOT NULL DEFAULT 0,
// `title` varchar(256) NOT NULL DEFAULT '' COMMENT '퀴즈시트 제목',
// `status` varchar(10) NOT NULL DEFAULT '0' COMMENT 'on/off',
// `content` text NOT NULL DEFAULT '' COMMENT 'markdown 문서',
// `tags` varchar(256) NOT NULL DEFAULT '' COMMENT '출력,hello world,for반복문...',
// `category` varchar(256) NOT NULL DEFAULT '파이썬기본',
// `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
// `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
// PRIMARY KEY (`id`) USING BTREE,
// KEY `guid_id` (`guid_id`) USING BTREE,
// KEY `center_id` (`center_id`) USING BTREE,
// KEY `manager_id` (`manager_id`) USING BTREE
// ) ENGINE=InnoDB AUTO_INCREMENT=10000001 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='퀴즈시험지 관리 테이블\r\n\r\n';
type QuizPaper struct { type QuizPaper struct {
ID int64 `json:"id" db:"id" example:"100001" gorm:"column:id;primary_key;"` ID int64 `json:"id" db:"id" example:"100001" gorm:"column:id;primary_key;"`
GUID string `json:"guid_id" db:"guid_id" example:"ef74c59a-c707-4162-a52b-455906c81ec1" gorm:"column:guid_id;size:255;index;"` GUID string `json:"guid_id" db:"guid_id" example:"ef74c59a-c707-4162-a52b-455906c81ec1" gorm:"column:guid_id;size:255;index;"`
@ -48,7 +30,7 @@ type QuizPaperRequest struct {
Title string `json:"title" example:"퀴즈 시트 제목"` Title string `json:"title" example:"퀴즈 시트 제목"`
Content string `json:"content" example:"퀴즈 시트 설명"` Content string `json:"content" example:"퀴즈 시트 설명"`
Category string `json:"category" example:"파이썬기본"` Category string `json:"category" example:"파이썬기본"`
Tag datatypes.JSON `json:"tag"` Tag []string `json:"tag" example:"Python,AI,Robot,파이썬"`
Status string `json:"status" example:"on"` Status string `json:"status" example:"on"`
} }
@ -59,7 +41,7 @@ type QuizPaperResponse struct {
Title string `json:"title" example:"퀴즈 시트 제목"` Title string `json:"title" example:"퀴즈 시트 제목"`
Content string `json:"content" example:"퀴즈 시트 설명"` Content string `json:"content" example:"퀴즈 시트 설명"`
Category string `json:"category" example:"파이썬기본"` Category string `json:"category" example:"파이썬기본"`
Tag datatypes.JSON `json:"tag"` Tag []string `json:"tag" example:"Python,AI,Robot,파이썬"`
Status string `json:"status" example:"on"` Status string `json:"status" example:"on"`
} }

View File

@ -2,17 +2,6 @@ package models
import "time" import "time"
// CREATE TABLE `UserTokens` (
// `id` bigint(20) NOT NULL AUTO_INCREMENT,
// `user_id` bigint(20) DEFAULT 0,
// `token` varchar(256) DEFAULT '',
// `refresh_token` varchar(256) DEFAULT '',
// `status` varchar(3) DEFAULT 'on' COMMENT 'on/off',
// `register_at` timestamp NULL DEFAULT current_timestamp(),
// `ending_at` timestamp NULL DEFAULT NULL COMMENT '종료날짜',
// PRIMARY KEY (`id`) USING BTREE
// ) ENGINE=InnoDB AUTO_INCREMENT=1000017 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
type Token struct { type Token struct {
ID int64 `json:"-" db:"id" gorm:"primary_key"` ID int64 `json:"-" db:"id" gorm:"primary_key"`
UserID int64 `json:"user_id" db:"user_id" gorm:"index;"` UserID int64 `json:"user_id" db:"user_id" gorm:"index;"`

View File

@ -2,27 +2,6 @@ package models
import "time" import "time"
// CREATE TABLE `Users` (
// `id` bigint(20) NOT NULL AUTO_INCREMENT,
// `guid_id` longtext DEFAULT NULL,
// `first_name` longtext DEFAULT NULL,
// `last_name` longtext DEFAULT NULL,
// `user_name` longtext DEFAULT NULL,
// `password` longtext DEFAULT NULL,
// `gender` char(1) DEFAULT 'M' COMMENT 'M(남)/F(여)',
// `user_role` longtext DEFAULT NULL,
// `memo_cs` longtext DEFAULT NULL,
// `register_at` datetime(3) DEFAULT NULL,
// `updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT '업데이트될때마다 자동저장',
// `phone_cs` longtext DEFAULT NULL,
// `upload_image` longtext DEFAULT NULL,
// PRIMARY KEY (`id`) USING BTREE,
// KEY `guid_id` (`guid_id`(768)),
// KEY `first_name` (`first_name`(768)),
// KEY `last_name` (`last_name`(768)),
// KEY `user_name` (`user_name`(768))
// ) ENGINE=InnoDB AUTO_INCREMENT=1000023 DEFAULT CHARSET=utf8mb4;
type User struct { type User struct {
ID int64 `json:"id" db:"id" example:"100001" gorm:"column:id;primary_key;"` ID int64 `json:"id" db:"id" example:"100001" gorm:"column:id;primary_key;"`
GUID string `json:"guid_id" db:"guid_id" example:"137c1683-2ad6-4201-b256-253828b61c49" gorm:"column:guid_id;size:255;"` GUID string `json:"guid_id" db:"guid_id" example:"137c1683-2ad6-4201-b256-253828b61c49" gorm:"column:guid_id;size:255;"`

View File

@ -12,6 +12,7 @@ type UserQuiz struct {
CenterID int64 `json:"center_id" db:"center_id" example:"100001" gorm:"column:center_id;index;"` CenterID int64 `json:"center_id" db:"center_id" example:"100001" gorm:"column:center_id;index;"`
UserQuizPaperID int64 `json:"user_quiz_paper_id" db:"user_quiz_paper_id" example:"1000001" gorm:"column:user_quiz_paper_id;index;"` UserQuizPaperID int64 `json:"user_quiz_paper_id" db:"user_quiz_paper_id" example:"1000001" gorm:"column:user_quiz_paper_id;index;"`
UserID int64 `json:"user_id" db:"user_id" example:"1000001" gorm:"column:user_id;index;"` UserID int64 `json:"user_id" db:"user_id" example:"1000001" gorm:"column:user_id;index;"`
QuizID int64 `json:"quiz_id" db:"quiz_id" example:"1000001" gorm:"column:quiz_id;index;"`
No int `json:"vol_no" db:"vol_no" example:"5" gorm:"column:vol_no;index;"` No int `json:"vol_no" db:"vol_no" example:"5" gorm:"column:vol_no;index;"`
QuestionType string `json:"question_type" db:"question_type" example:"choice" gorm:"column:question_type;size:10;index;"` QuestionType string `json:"question_type" db:"question_type" example:"choice" gorm:"column:question_type;size:10;index;"`
Question string `json:"question" db:"question" example:"퀴즈 질문입니다." gorm:"column:question;size:512;"` Question string `json:"question" db:"question" example:"퀴즈 질문입니다." gorm:"column:question;size:512;"`
@ -33,6 +34,7 @@ type UserQuizRequest struct {
CenterID int64 `json:"center_id" example:"1000001"` CenterID int64 `json:"center_id" example:"1000001"`
UserQuizPaperID int64 `json:"quiz_paper_id" example:"1000001"` UserQuizPaperID int64 `json:"quiz_paper_id" example:"1000001"`
UserID int64 `json:"user_id" example:"1000001"` UserID int64 `json:"user_id" example:"1000001"`
QuizID int64 `json:"quiz_id" example:"1000001"`
No int `json:"vol_no" example:"1"` No int `json:"vol_no" example:"1"`
QuestionType string `json:"question_type" example:"choice"` QuestionType string `json:"question_type" example:"choice"`
Question string `json:"question" example:"질문입니다."` Question string `json:"question" example:"질문입니다."`
@ -49,6 +51,7 @@ type UserQuizResponse struct {
CenterD string `json:"center_id" example:"2036023a-fb56-4b6c-b3bb-c787c681ada6"` CenterD string `json:"center_id" example:"2036023a-fb56-4b6c-b3bb-c787c681ada6"`
UserQuizPaperID int64 `json:"user_quiz_paper_id" example:"1000001"` UserQuizPaperID int64 `json:"user_quiz_paper_id" example:"1000001"`
UserID int64 `json:"user_id" example:"1000001"` UserID int64 `json:"user_id" example:"1000001"`
QuizID int64 `json:"quiz_id" example:"1000001"`
No int `json:"vol_no" example:"5"` No int `json:"vol_no" example:"5"`
QuestionType string `json:"question_type" example:"check"` QuestionType string `json:"question_type" example:"check"`
Question string `json:"question" example:"퀴즈 질문입니다."` Question string `json:"question" example:"퀴즈 질문입니다."`

View File

@ -2,30 +2,8 @@ package models
import ( import (
"time" "time"
"gorm.io/datatypes"
) )
// CREATE TABLE `UserQuizPapers` (
// `id` bigint(20) NOT NULL AUTO_INCREMENT,
// `guid_id` char(36) NOT NULL DEFAULT '0',
// `center_id` bigint(20) NOT NULL DEFAULT 0,
// `quiz_paper_id` bigint(20) NOT NULL DEFAULT 0,
// `status` varchar(10) NOT NULL DEFAULT '0' COMMENT '매칭된 학생퀴즈시험지 상태: \r\nwaiting(대기)/processing(시험진행중)/abort(취소)/marking(채점중)/done(완료) ',
// `user_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '매칭된 학생',
// `user_score` float NOT NULL DEFAULT 5 COMMENT '10',
// `total_score` float NOT NULL DEFAULT 100 COMMENT '100.0, 5.0',
// `start_at` timestamp NULL DEFAULT NULL COMMENT '학생시험시작시각',
// `done_at` timestamp NULL DEFAULT NULL COMMENT '학생시험종료시각',
// `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
// `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
// PRIMARY KEY (`id`) USING BTREE,
// KEY `guid_id` (`guid_id`) USING BTREE,
// KEY `center_id` (`center_id`) USING BTREE,
// KEY `user_id` (`user_id`),
// KEY `manager_id` (`quiz_paper_id`) USING BTREE
// ) ENGINE=InnoDB AUTO_INCREMENT=10000001 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='학생퀴즈시험지 매칭\r\n';
type UserQuizPaper struct { type UserQuizPaper struct {
ID int64 `json:"id" db:"id" example:"1000015" gorm:"column:id;primary_key;"` ID int64 `json:"id" db:"id" example:"1000015" gorm:"column:id;primary_key;"`
GUID string `json:"guid_id" db:"guid_id" example:"7f9329f5-2e36-4638-92d2-73064b7291a4" gorm:"column:guid_id;size:255;uniqueIndex"` GUID string `json:"guid_id" db:"guid_id" example:"7f9329f5-2e36-4638-92d2-73064b7291a4" gorm:"column:guid_id;size:255;uniqueIndex"`
@ -48,7 +26,7 @@ func (UserQuizPaper) TableName() string {
type UserQuizPaperRequest struct { type UserQuizPaperRequest struct {
QuizPaperID int64 `json:"quiz_paper_id" example:"1000002"` QuizPaperID int64 `json:"quiz_paper_id" example:"1000002"`
Users datatypes.JSON `json:"users"` Users []int64 `json:"users" example:"1000001,1000002,1000003,1000004"`
} }
type UserQuizPaperResponse struct { type UserQuizPaperResponse struct {

View File

@ -26,6 +26,7 @@ type TokenRepository interface {
Create(*models.Token) (*models.Token, error) Create(*models.Token) (*models.Token, error)
Update(*models.Token) (*models.Token, error) Update(*models.Token) (*models.Token, error)
Delete(string) error Delete(string) error
Invalid(int64, string) error
} }
func (s *tokenRepository) Generate(user_id int64, expire_at int64, role string) (string, error) { func (s *tokenRepository) Generate(user_id int64, expire_at int64, role string) (string, error) {
@ -69,6 +70,11 @@ func (r *tokenRepository) Update(token *models.Token) (*models.Token, error) {
return row, err return row, err
} }
func (r *tokenRepository) Invalid(user_id int64, tokenString string) error {
err := r.DB.Model(&models.Token{}).Where("user_id = ? AND token = ?", user_id, tokenString).Update("status", "off").Error
return err
}
func (r *tokenRepository) Delete(id string) error { func (r *tokenRepository) Delete(id string) error {
var token *models.Token var token *models.Token
if err := r.DB.Where("id=?", id).First(&token).Error; err != nil { if err := r.DB.Where("id=?", id).First(&token).Error; err != nil {

View File

@ -1,10 +1,10 @@
package repositories package repositories
import ( import (
"fmt"
"learnsteam/cslms-api/internal/models" "learnsteam/cslms-api/internal/models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause"
) )
type userQuizRepository struct { type userQuizRepository struct {
@ -64,8 +64,7 @@ func (r *userQuizRepository) Create(row *models.UserQuiz) (*models.UserQuiz, err
} }
func (r *userQuizRepository) Insert(items []models.UserQuiz) ([]models.UserQuiz, error) { func (r *userQuizRepository) Insert(items []models.UserQuiz) ([]models.UserQuiz, error) {
fmt.Println(items) err := r.DB.Clauses(clause.OnConflict{DoNothing: true}).Create(&items).Error
err := r.DB.Create(&items).Error
return items, err return items, err
} }

View File

@ -4,6 +4,7 @@ import (
"learnsteam/cslms-api/internal/models" "learnsteam/cslms-api/internal/models"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause"
) )
type userQuizPaperRepository struct { type userQuizPaperRepository struct {
@ -63,7 +64,7 @@ func (r *userQuizPaperRepository) Create(userQuizPaper *models.UserQuizPaper) (*
} }
func (r *userQuizPaperRepository) Insert(items []models.UserQuizPaper) ([]models.UserQuizPaper, error) { func (r *userQuizPaperRepository) Insert(items []models.UserQuizPaper) ([]models.UserQuizPaper, error) {
err := r.DB.Create(&items).Error err := r.DB.Clauses(clause.OnConflict{DoNothing: true}).Create(&items).Error
return items, err return items, err
} }

View File

@ -2,6 +2,7 @@ package routers
import ( import (
"learnsteam/cslms-api/internal/controllers" "learnsteam/cslms-api/internal/controllers"
"learnsteam/cslms-api/internal/middleware"
"learnsteam/cslms-api/internal/repositories" "learnsteam/cslms-api/internal/repositories"
"learnsteam/cslms-api/internal/services" "learnsteam/cslms-api/internal/services"
@ -50,4 +51,5 @@ func (r *authRouter) SetAuthRouter() {
group := r.router.Group("/auth") group := r.router.Group("/auth")
group.POST("login", r.controller.Login) group.POST("login", r.controller.Login)
group.POST("register", r.controller.Register) group.POST("register", r.controller.Register)
group.POST("logout", middleware.Auth("member"), r.controller.Logout)
} }

View File

@ -1,6 +1,7 @@
package routers package routers
import ( import (
configs "learnsteam/cslms-api/configs"
"learnsteam/cslms-api/internal/database" "learnsteam/cslms-api/internal/database"
"github.com/gin-contrib/cors" "github.com/gin-contrib/cors"
@ -10,10 +11,11 @@ import (
var Router *gin.Engine var Router *gin.Engine
func Init() { func Init() {
gin.SetMode(gin.ReleaseMode) gin.SetMode(configs.MODE)
Router = gin.Default() Router = gin.Default()
config := cors.DefaultConfig() config := cors.DefaultConfig()
config.AllowOrigins = []string{"http://127.0.0.1:3000", "http://localhost:3000", "http://localhost:3030", "https://learnsteam-quiz.jongyeob.com"}
config.AllowOrigins = []string{"http://127.0.0.1:3000", "http://localhost:3000", "http://localhost:3030", "https://cslms-api.codingschool.co.kr"}
config.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "PATCH"} config.AllowMethods = []string{"GET", "POST", "PUT", "DELETE", "PATCH"}
config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type"} config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type"}
Router.Use(cors.New(config)) Router.Use(cors.New(config))
@ -21,6 +23,7 @@ func Init() {
maindb := database.GetDB() maindb := database.GetDB()
InitAuthRouter(maindb, Router) InitAuthRouter(maindb, Router)
InitCenterRouter(maindb, Router) InitCenterRouter(maindb, Router)
InitQuizPaperRouter(maindb, Router) InitQuizPaperRouter(maindb, Router)
InitQuizRouter(maindb, Router) InitQuizRouter(maindb, Router)

View File

@ -25,8 +25,9 @@ type TokenService interface {
Refresh(string) (*models.Token, error) Refresh(string) (*models.Token, error)
Generate(string, int64, string) (string, error) Generate(int64, int64, string) (string, error)
Verify(tokenString string) (*jwt.Token, error) Verify(string) (*jwt.Token, error)
Invalid(int64, string) error
GetJwtToken(string) (*jwt.Token, error) GetJwtToken(string) (*jwt.Token, error)
ExtractTokenString(string) string ExtractTokenString(string) string
@ -90,7 +91,12 @@ func (s *tokenService) Verify(tokenString string) (*jwt.Token, error) {
return jwtToken, err return jwtToken, err
} }
func (s *tokenService) Generate(user_id string, expire_at int64, role string) (string, error) { func (s *tokenService) Invalid(user_id int64, tokenString string) error {
err := s.repository.Invalid(user_id, tokenString)
return err
}
func (s *tokenService) Generate(user_id int64, expire_at int64, role string) (string, error) {
claims := jwt.MapClaims{} claims := jwt.MapClaims{}
claims["authorized"] = true claims["authorized"] = true
claims["sub"] = user_id claims["sub"] = user_id
@ -153,14 +159,6 @@ func (s *tokenService) Refresh(refreshToken string) (*models.Token, error) {
}) })
if err != 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 return nil, err
} }
@ -173,6 +171,7 @@ func (s *tokenService) Refresh(refreshToken string) (*models.Token, error) {
if err != nil { if err != nil {
return nil, errors.New("wrong user") return nil, errors.New("wrong user")
} }
role := claims["role"].(string) role := claims["role"].(string)
token, err := s.repository.FindByRefreshToken(sub, refreshToken) token, err := s.repository.FindByRefreshToken(sub, refreshToken)

View File

@ -80,6 +80,7 @@ func (s *userQuizService) Generate(quiz_paper_id int64, userQuizPaper *models.Us
UserQuizPaperID: userQuizPaper.ID, UserQuizPaperID: userQuizPaper.ID,
UserID: userQuizPaper.UserID, UserID: userQuizPaper.UserID,
No: quiz.No, No: quiz.No,
QuizID: quiz.ID,
QuestionType: quiz.QuestionType, QuestionType: quiz.QuestionType,
Question: quiz.Question, Question: quiz.Question,
Content: quiz.Content, Content: quiz.Content,

View File

@ -2,8 +2,11 @@ package learsteam_quiz_test
import ( import (
"learnsteam/cslms-api/internal/controllers" "learnsteam/cslms-api/internal/controllers"
"learnsteam/cslms-api/internal/models"
"learnsteam/cslms-api/internal/repositories" "learnsteam/cslms-api/internal/repositories"
"learnsteam/cslms-api/internal/services" "learnsteam/cslms-api/internal/services"
"os"
"testing"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"gorm.io/gorm" "gorm.io/gorm"
@ -36,7 +39,7 @@ type QuizTestSuite struct {
// suite.repository = repository // suite.repository = repository
// suite.controller = controller // suite.controller = controller
// suite.CreateSampleData() // // suite.CreateSampleData()
// } // }
// func (suite *QuizTestSuite) CreateSampleData() { // func (suite *QuizTestSuite) CreateSampleData() {
@ -68,26 +71,26 @@ type QuizTestSuite struct {
// } // }
// } // }
// func (suite *QuizTestSuite) TearDownSuite() { func (suite *QuizTestSuite) TearDownSuite() {
// // DB 삭제 // DB 삭제
// suite.db.Migrator().DropTable(&models.Quiz{}) suite.db.Migrator().DropTable(&models.Quiz{})
// err := os.Remove("test.db") err := os.Remove("test.db")
// if err != nil { if err != nil {
// suite.Fail("Failed to remove the test database file") suite.Fail("Failed to remove the test database file")
// } }
// } }
// func (suite *QuizTestSuite) SetupTest() { func (suite *QuizTestSuite) SetupTest() {
// suite.CreateSampleData() // suite.CreateSampleData()
// } }
// func (suite *QuizTestSuite) TearDownTest() { func (suite *QuizTestSuite) TearDownTest() {
// } }
// func TestQuizSuite(t *testing.T) { func TestQuizSuite(t *testing.T) {
// suite.Run(t, new(QuizTestSuite)) suite.Run(t, new(QuizTestSuite))
// } }
// // 목록 테스트 // // 목록 테스트
// func (suite *QuizTestSuite) TestListQuizSuccess() { // func (suite *QuizTestSuite) TestListQuizSuccess() {

View File

@ -1,33 +1,49 @@
package learsteam_quiz_test package learsteam_quiz_test
// type TokenTestSuite struct { import (
// suite.Suite "fmt"
// db *gorm.DB config "learnsteam/cslms-api/configs"
// repository repositories.TokenRepository "learnsteam/cslms-api/internal/controllers"
// service services.TokenService "learnsteam/cslms-api/internal/repositories"
// controller controllers.TokenController "learnsteam/cslms-api/internal/services"
// } "testing"
"time"
// func (suite *TokenTestSuite) SetupSuite() { "github.com/golang-jwt/jwt/v5"
// err := os.Remove("test.db") "github.com/stretchr/testify/suite"
// if err != nil { "github.com/tj/assert"
// suite.Fail("Failed to remove the test database file") "gorm.io/driver/sqlite"
// } "gorm.io/gorm"
"gorm.io/gorm/schema"
)
// database.Init() type TokenTestSuite struct {
// gorm_config := gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}} suite.Suite
// db, _ := gorm.Open(sqlite.Open("test.db"), &gorm_config) db *gorm.DB
// repository := repositories.NewTokenRepository(db) repository repositories.TokenRepository
// service := services.NewTokenService(repository) service services.TokenService
// controller := controllers.NewTokenController(service) controller controllers.TokenController
}
// suite.db = db func (suite *TokenTestSuite) SetupSuite() {
// suite.service = service // err := os.Remove("test.db")
// suite.repository = repository // if err != nil {
// suite.controller = controller // suite.Fail("Failed to remove the test database file")
// }
// suite.CreateSampleData() gorm_config := gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}}
// } db, _ := gorm.Open(sqlite.Open("test.db"), &gorm_config)
repository := repositories.NewTokenRepository(db)
service := services.NewTokenService(repository)
controller := controllers.NewTokenController(service)
suite.db = db
suite.service = service
suite.repository = repository
suite.controller = controller
// suite.CreateSampleData()
}
// func (suite *TokenTestSuite) CreateSampleData() { // func (suite *TokenTestSuite) CreateSampleData() {
// suite.db.AutoMigrate(&models.Token{}) // suite.db.AutoMigrate(&models.Token{})
@ -51,34 +67,36 @@ package learsteam_quiz_test
// } // }
// } // }
// func (suite *TokenTestSuite) TearDownSuite() { func (suite *TokenTestSuite) TearDownSuite() {
// // suite.db.Migrator().DropTable(&models.Token{}) // suite.db.Migrator().DropTable(&models.Token{})
// // err := os.Remove("test.db") // err := os.Remove("test.db")
// // if err != nil { // if err != nil {
// // suite.Fail("Failed to remove the test database file") // suite.Fail("Failed to remove the test database file")
// // } // }
// } }
// func (suite *TokenTestSuite) SetupTest() {
// // suite.userRepository.DeleteByName("testUserName0001")
// }
// func (suite *TokenTestSuite) TearDownTest() { func (suite *TokenTestSuite) SetupTest() {
// suite.userRepository.DeleteByName("testUserName0001")
}
// } func (suite *TokenTestSuite) TearDownTest() {
// func TestTokenSuite(t *testing.T) { }
// suite.Run(t, new(TokenTestSuite))
// }
// // 토큰 생성 테스트 func TestTokenSuite(t *testing.T) {
// func (suite *TokenTestSuite) TestGenerateTokenSuccess() { suite.Run(t, new(TokenTestSuite))
// user_id := "testuser" }
// expire_at := time.Now().Add(time.Hour * 24 * 365).Unix()
// token, err := suite.service.Generate(user_id, expire_at) // 토큰 생성 테스트
// assert.NoError(suite.T(), err) func (suite *TokenTestSuite) TestGenerateTokenSuccess() {
// assert.NotNil(suite.T(), token) user_id := int64(100000001)
// } role := "admin"
expire_at := time.Now().Add(time.Hour * 24 * 365).Unix()
token, err := suite.service.Generate(user_id, expire_at, role)
assert.NoError(suite.T(), err)
assert.NotNil(suite.T(), token)
}
// // 토큰 생성 테스트 // // 토큰 생성 테스트
// func (suite *TokenTestSuite) TestGenerateTokenString() { // func (suite *TokenTestSuite) TestGenerateTokenString() {
@ -243,3 +261,37 @@ package learsteam_quiz_test
// assert.True(suite.T(), true) // assert.True(suite.T(), true)
// } // }
// } // }
func (suite *TokenTestSuite) TestVerifyValidToken() {
expire_at := time.Now().Add(time.Hour * 24 * 365 * 100).Unix()
validToken, err := suite.service.Generate(10001, expire_at, "admin")
assert.NoError(suite.T(), err)
jwtToken, err := jwt.Parse(validToken, 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
})
assert.NoError(suite.T(), err)
assert.True(suite.T(), jwtToken.Valid)
}
func (suite *TokenTestSuite) TestVerifyExpiredToken() {
expire_at := time.Now().Add(-time.Hour * 24 * 365 * 100).Unix()
expiredToken, err := suite.service.Generate(10001, expire_at, "admin")
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), "1", expiredToken)
jwtToken, err := jwt.Parse(expiredToken, 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
})
assert.Error(suite.T(), err)
assert.False(suite.T(), jwtToken.Valid)
}