Swag Docs
This commit is contained in:
parent
0c3d31c129
commit
5b5870f354
6
Makefile
6
Makefile
@ -1,4 +1,4 @@
|
|||||||
FUNCTION_NAME =superrichquiz-octet
|
FUNCTION_NAME =learsteam-quiz-api
|
||||||
BUILD =$(CURDIR)/build
|
BUILD =$(CURDIR)/build
|
||||||
BIN =$(CURDIR)/bin
|
BIN =$(CURDIR)/bin
|
||||||
MAIN =$(CURDIR)/cmd/main.go
|
MAIN =$(CURDIR)/cmd/main.go
|
||||||
@ -12,6 +12,10 @@ DEV_HANDLER =bootstrap
|
|||||||
|
|
||||||
default: help
|
default: help
|
||||||
|
|
||||||
|
swag: ## swagger docs
|
||||||
|
@echo "\033[32mSwagger API Docs ...\033[0m"
|
||||||
|
@swag init --parseDependency -g cmd/main.go
|
||||||
|
|
||||||
deps: ## install dependency
|
deps: ## install dependency
|
||||||
@echo "\033[32mDependency ...\033[0m"
|
@echo "\033[32mDependency ...\033[0m"
|
||||||
@go install gorm.io/gorm
|
@go install gorm.io/gorm
|
||||||
|
27
cmd/main.go
27
cmd/main.go
@ -1,17 +1,25 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
configs "learnsteam/learsteam-quiz-api/configs"
|
||||||
|
_ "learnsteam/learsteam-quiz-api/docs"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/database"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/helpers"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/routers"
|
||||||
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
configs "studioj/boilerplate_go/configs"
|
|
||||||
"studioj/boilerplate_go/internal/database"
|
|
||||||
"studioj/boilerplate_go/internal/helpers"
|
|
||||||
"studioj/boilerplate_go/internal/routers"
|
|
||||||
|
|
||||||
"github.com/apex/gateway"
|
"github.com/apex/gateway"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// @title Learsteam Quiz API
|
||||||
|
// @version 1.0
|
||||||
|
// @description Learnsteam Quiz 서비스 API
|
||||||
|
|
||||||
|
// @contact.name Jay Sheen
|
||||||
|
// @contact.email sheen@jongyeob.com
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
Init()
|
Init()
|
||||||
Run()
|
Run()
|
||||||
@ -20,6 +28,7 @@ func main() {
|
|||||||
func Init() {
|
func Init() {
|
||||||
database.Init()
|
database.Init()
|
||||||
routers.Init()
|
routers.Init()
|
||||||
|
|
||||||
database.AutoMigrate()
|
database.AutoMigrate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,3 +39,11 @@ func Run() {
|
|||||||
log.Fatal(http.ListenAndServe(configs.PORT, routers.Router))
|
log.Fatal(http.ListenAndServe(configs.PORT, routers.Router))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InitSwagger() {
|
||||||
|
// swagger:route GET /health health
|
||||||
|
// Check server health
|
||||||
|
//
|
||||||
|
// responses:
|
||||||
|
// 200: healthResponse
|
||||||
|
}
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
const PORT = ":3030"
|
const (
|
||||||
const DATABASE_URL = "root:omHO7EEzHm52s9DlZD70P6KPKm2TbODC@tcp(db:3306)/boilerplate?charset=utf8&parseTime=True&loc=Local"
|
BASE_URL = "https://learnsteam-quiz-api.jongyeob.com"
|
||||||
const SECRET_KEY = "5a14e06d-55a3-418c-9f3f-8fde328d6c49"
|
PORT = ":3030"
|
||||||
|
DATABASE_URL = "learnsteam:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(localhost:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local"
|
||||||
|
|
||||||
|
KAKAO_CLIENT_ID = "986830"
|
||||||
|
KAKAO_CLIENT_SECRET = "14f63a8e91c4e0fe88bc40e3ff348233"
|
||||||
|
|
||||||
|
SECRET_KEY = "GmhiMJuAIyF3jwkd97iODSoJoN3bIVkF"
|
||||||
|
ACCESS_KEY = "NQD9AXmpn13asz84oPf5Dyc9rhLyhmHp"
|
||||||
|
REFRESH_KEY = "qFzFMlX2PfTSXgA9QPFltcNhJk1SVF0a"
|
||||||
|
)
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
const PORT = ":3030"
|
const (
|
||||||
const DATABASE_URL = "root:sswha123@tcp(localhost:3306)/boilerplate?charset=utf8&parseTime=True&loc=Local"
|
BASE_URL = "http://localhost:3030"
|
||||||
|
PORT = ":3030"
|
||||||
|
DATABASE_URL = "learnsteam:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(localhost:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local"
|
||||||
|
|
||||||
// const DATABASE_URL = "sqlite.db"
|
KAKAO_CLIENT_ID = "986830"
|
||||||
const SECRET_KEY = "5a14e06d-55a3-418c-9f3f-8fde328d6c49"
|
KAKAO_CLIENT_SECRET = "14f63a8e91c4e0fe88bc40e3ff348233"
|
||||||
|
|
||||||
|
SECRET_KEY = "GmhiMJuAIyF3jwkd97iODSoJoN3bIVkF"
|
||||||
|
ACCESS_KEY = "NQD9AXmpn13asz84oPf5Dyc9rhLyhmHp"
|
||||||
|
REFRESH_KEY = "qFzFMlX2PfTSXgA9QPFltcNhJk1SVF0a"
|
||||||
|
)
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
const PORT = ":3030"
|
const (
|
||||||
const DATABASE_URL = "root:sswha123@tcp(localhost:3306)/boilerplate?charset=utf8&parseTime=True&loc=Local"
|
BASE_URL = "http://localhost:3030"
|
||||||
//const DATABASE_URL = "sqlite.db"
|
PORT = ":3030"
|
||||||
const SECRET_KEY = "5a14e06d-55a3-418c-9f3f-8fde328d6c49"
|
DATABASE_URL = "learnsteam:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(localhost:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local"
|
||||||
|
|
||||||
|
KAKAO_CLIENT_ID = "986830"
|
||||||
|
KAKAO_CLIENT_SECRET = "14f63a8e91c4e0fe88bc40e3ff348233"
|
||||||
|
|
||||||
|
SECRET_KEY = "GmhiMJuAIyF3jwkd97iODSoJoN3bIVkF"
|
||||||
|
ACCESS_KEY = "NQD9AXmpn13asz84oPf5Dyc9rhLyhmHp"
|
||||||
|
REFRESH_KEY = "qFzFMlX2PfTSXgA9QPFltcNhJk1SVF0a"
|
||||||
|
)
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
const PORT = ":3030"
|
const (
|
||||||
const DATABASE_URL = "root:omHO7EEzHm52s9DlZD70P6KPKm2TbODC@tcp(localhost:3306)/boilerplate?charset=utf8&parseTime=True&loc=Local"
|
BASE_URL = "https://learnsteam-quiz-api.jongyeob.com"
|
||||||
const SECRET_KEY = "5a14e06d-55a3-418c-9f3f-8fde328d6c49"
|
PORT = ":3030"
|
||||||
|
DATABASE_URL = "learnsteam:fbOgZ6Xxn5VXBYihjqygRXyaK6ZUgKL6@tcp(localhost:3306)/learnsteam_quiz?charset=utf8&parseTime=True&loc=Local"
|
||||||
|
|
||||||
|
KAKAO_CLIENT_ID = "986830"
|
||||||
|
KAKAO_CLIENT_SECRET = "14f63a8e91c4e0fe88bc40e3ff348233"
|
||||||
|
|
||||||
|
SECRET_KEY = "GmhiMJuAIyF3jwkd97iODSoJoN3bIVkF"
|
||||||
|
ACCESS_KEY = "NQD9AXmpn13asz84oPf5Dyc9rhLyhmHp"
|
||||||
|
REFRESH_KEY = "qFzFMlX2PfTSXgA9QPFltcNhJk1SVF0a"
|
||||||
|
)
|
||||||
|
1051
docs/docs.go
Normal file
1051
docs/docs.go
Normal file
File diff suppressed because it is too large
Load Diff
1025
docs/swagger.json
Normal file
1025
docs/swagger.json
Normal file
File diff suppressed because it is too large
Load Diff
688
docs/swagger.yaml
Normal file
688
docs/swagger.yaml
Normal file
@ -0,0 +1,688 @@
|
|||||||
|
definitions:
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.LoginResponse:
|
||||||
|
properties:
|
||||||
|
refresh_token:
|
||||||
|
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDc4OTcwMjcs
|
||||||
|
type: string
|
||||||
|
token:
|
||||||
|
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDI3MTMwMjcsInN1
|
||||||
|
type: string
|
||||||
|
user:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.User'
|
||||||
|
type: object
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.Program:
|
||||||
|
properties:
|
||||||
|
content:
|
||||||
|
example: 코스 설명
|
||||||
|
type: string
|
||||||
|
course:
|
||||||
|
example: 코스 이름
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
example: ef74c59a-c707-4162-a52b-455906c81ec1
|
||||||
|
type: string
|
||||||
|
publish_at:
|
||||||
|
example: "2023-11-10T00:00:00+09:00"
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
example: "on"
|
||||||
|
type: string
|
||||||
|
subject:
|
||||||
|
example: 프로그램 제목
|
||||||
|
type: string
|
||||||
|
tag:
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.ProgramListResponse:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.Program'
|
||||||
|
type: array
|
||||||
|
page:
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
pageSize:
|
||||||
|
example: 10
|
||||||
|
type: integer
|
||||||
|
total:
|
||||||
|
example: 999
|
||||||
|
type: integer
|
||||||
|
totalPage:
|
||||||
|
example: 99
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.ProgramResponse:
|
||||||
|
properties:
|
||||||
|
content:
|
||||||
|
example: 코스 설명
|
||||||
|
type: string
|
||||||
|
course:
|
||||||
|
example: 코스 이름
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
example: ef74c59a-c707-4162-a52b-455906c81ec1
|
||||||
|
type: string
|
||||||
|
publish_at:
|
||||||
|
example: "2023-11-10T00:00:00+09:00"
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
example: "on"
|
||||||
|
type: string
|
||||||
|
subject:
|
||||||
|
example: 프로그램 제목
|
||||||
|
type: string
|
||||||
|
tag:
|
||||||
|
example:
|
||||||
|
- tag1
|
||||||
|
- tag2
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
type: object
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.Quiz:
|
||||||
|
properties:
|
||||||
|
answer:
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
choice:
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
comment:
|
||||||
|
example: 퀴즈 해설
|
||||||
|
type: string
|
||||||
|
hint:
|
||||||
|
example: 퀴즈 힌트
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
example: 1b066168-68c4-4b50-bc9a-b6c4fceaf378
|
||||||
|
type: string
|
||||||
|
program_id:
|
||||||
|
example: 2036023a-fb56-4b6c-b3bb-c787c681ada6
|
||||||
|
type: string
|
||||||
|
question:
|
||||||
|
example: 퀴즈 질문입니다.
|
||||||
|
type: string
|
||||||
|
quiz_type:
|
||||||
|
example: choice
|
||||||
|
type: string
|
||||||
|
sequence:
|
||||||
|
example: 5
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.QuizListResponse:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.Quiz'
|
||||||
|
type: array
|
||||||
|
page:
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
pageSize:
|
||||||
|
example: 10
|
||||||
|
type: integer
|
||||||
|
total:
|
||||||
|
example: 5
|
||||||
|
type: integer
|
||||||
|
totalPage:
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.QuizResponse:
|
||||||
|
properties:
|
||||||
|
answer:
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
choice:
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
|
comment:
|
||||||
|
example: 퀴즈 해설
|
||||||
|
type: string
|
||||||
|
hint:
|
||||||
|
example: 퀴즈 힌트
|
||||||
|
type: string
|
||||||
|
id:
|
||||||
|
example: 1b066168-68c4-4b50-bc9a-b6c4fceaf378
|
||||||
|
type: string
|
||||||
|
program_id:
|
||||||
|
example: 2036023a-fb56-4b6c-b3bb-c787c681ada6
|
||||||
|
type: string
|
||||||
|
question:
|
||||||
|
example: 퀴즈 질문입니다.
|
||||||
|
type: string
|
||||||
|
quiz_type:
|
||||||
|
example: check
|
||||||
|
type: string
|
||||||
|
sequence:
|
||||||
|
example: 5
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.RegisterResponse:
|
||||||
|
properties:
|
||||||
|
refresh_token:
|
||||||
|
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDc4OTcwMjcs
|
||||||
|
type: string
|
||||||
|
token:
|
||||||
|
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkIjp0cnVlLCJleHAiOjE3MDI3MTMwMjcsInN1
|
||||||
|
type: string
|
||||||
|
user:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.User'
|
||||||
|
type: object
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.User:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
example: 137c1683-2ad6-4201-b256-253828b61c49
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
example: 홍길동
|
||||||
|
type: string
|
||||||
|
score:
|
||||||
|
example: 9999
|
||||||
|
type: integer
|
||||||
|
username:
|
||||||
|
example: user0
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
learnsteam_learsteam-quiz-api_internal_models.UserListResponse:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.User'
|
||||||
|
type: array
|
||||||
|
page:
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
pageSize:
|
||||||
|
example: 10
|
||||||
|
type: integer
|
||||||
|
total:
|
||||||
|
example: 90
|
||||||
|
type: integer
|
||||||
|
totalPage:
|
||||||
|
example: 9
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
|
info:
|
||||||
|
contact:
|
||||||
|
email: sheen@jongyeob.com
|
||||||
|
name: Jay Sheen
|
||||||
|
description: Learnsteam Quiz 서비스 API
|
||||||
|
title: Learsteam Quiz API
|
||||||
|
version: "1.0"
|
||||||
|
paths:
|
||||||
|
/auth/login:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: username, password 를 입력하여 로그인
|
||||||
|
parameters:
|
||||||
|
- description: username
|
||||||
|
in: body
|
||||||
|
name: username
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 비밀번호
|
||||||
|
in: body
|
||||||
|
name: password
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.LoginResponse'
|
||||||
|
summary: 로그인
|
||||||
|
/auth/register:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: username, name, password 를 입력하여 회원가입
|
||||||
|
parameters:
|
||||||
|
- description: username
|
||||||
|
in: body
|
||||||
|
name: username
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 이름
|
||||||
|
in: body
|
||||||
|
name: name
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 비밀번호
|
||||||
|
in: body
|
||||||
|
name: password
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.RegisterResponse'
|
||||||
|
summary: 회원가입
|
||||||
|
/program:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 퀴즈 프로그램 목록을 가져옵니다.
|
||||||
|
parameters:
|
||||||
|
- description: 태그
|
||||||
|
in: query
|
||||||
|
name: tag
|
||||||
|
type: string
|
||||||
|
- description: 검색어
|
||||||
|
in: query
|
||||||
|
name: q
|
||||||
|
type: string
|
||||||
|
- description: 페이지
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 페이지 사이즈
|
||||||
|
in: query
|
||||||
|
name: limit
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramListResponse'
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: 프로그램 목록 가져오기
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 퀴즈 프로그램을 만듭니다.
|
||||||
|
parameters:
|
||||||
|
- description: 프로그램 제목
|
||||||
|
in: body
|
||||||
|
name: subject
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 프로그램 코스
|
||||||
|
in: body
|
||||||
|
name: course
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 프로그램 내용
|
||||||
|
in: body
|
||||||
|
name: content
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 프로그램 태그
|
||||||
|
in: body
|
||||||
|
name: tag
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
- description: 프로그램 상태 on 또는 off
|
||||||
|
in: body
|
||||||
|
name: status
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 프로그램 발행 날짜
|
||||||
|
in: body
|
||||||
|
name: publish_at
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramResponse'
|
||||||
|
summary: 퀴즈 프로그램 생성
|
||||||
|
put:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 퀴즈 프로그램을 수정합니다.
|
||||||
|
parameters:
|
||||||
|
- description: 프로그램 제목
|
||||||
|
in: body
|
||||||
|
name: subject
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 프로그램 코스
|
||||||
|
in: body
|
||||||
|
name: course
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 프로그램 내용
|
||||||
|
in: body
|
||||||
|
name: content
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 프로그램 태그
|
||||||
|
in: body
|
||||||
|
name: tag
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
- description: 프로그램 상태 on 또는 off
|
||||||
|
in: body
|
||||||
|
name: status
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 프로그램 발행 날짜
|
||||||
|
in: body
|
||||||
|
name: publish_at
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramResponse'
|
||||||
|
summary: 퀴즈 프로그램 수정
|
||||||
|
/program/{id}:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: ID로 퀴즈 프로그램을 가져옵니다.
|
||||||
|
parameters:
|
||||||
|
- description: 퀴즈 프로그램 ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
summary: 퀴즈 프로그램 가져오기
|
||||||
|
/quiz:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 퀴즈 목록을 가져옵니다.
|
||||||
|
parameters:
|
||||||
|
- description: 프로그램 ID
|
||||||
|
in: query
|
||||||
|
name: program_id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- description: 검색어
|
||||||
|
in: query
|
||||||
|
name: q
|
||||||
|
type: string
|
||||||
|
- description: 페이지
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 페이지 사이즈
|
||||||
|
in: query
|
||||||
|
name: limit
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizListResponse'
|
||||||
|
summary: 퀴즈 목록 가져오기
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 퀴즈를 만듭니다.
|
||||||
|
parameters:
|
||||||
|
- description: 프로그램 ID
|
||||||
|
in: body
|
||||||
|
name: program_id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 퀴즈 순서
|
||||||
|
in: body
|
||||||
|
name: sequence
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- description: '퀴즈 타입 : choice, check, ox, input'
|
||||||
|
in: body
|
||||||
|
name: quiz_type
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 퀴즈 문제
|
||||||
|
in: body
|
||||||
|
name: question
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: '퀴즈 선택지 : [''선택1'',''선택2'',''선택3'', ''선택4'']'
|
||||||
|
in: body
|
||||||
|
name: choice
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
- description: '퀴즈 정답 : [1,3]'
|
||||||
|
in: body
|
||||||
|
name: answer
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
- description: 퀴즈 힌트
|
||||||
|
in: body
|
||||||
|
name: hint
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 퀴즈 해설
|
||||||
|
in: body
|
||||||
|
name: comment
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizResponse'
|
||||||
|
summary: 퀴즈 생성
|
||||||
|
put:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 퀴즈를 수정합니다.
|
||||||
|
parameters:
|
||||||
|
- description: 퀴즈 ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- description: 프로그램 ID
|
||||||
|
in: body
|
||||||
|
name: program_id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 퀴즈 순서
|
||||||
|
in: body
|
||||||
|
name: sequence
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
- description: '퀴즈 타입 : choice, check, ox, input'
|
||||||
|
in: body
|
||||||
|
name: quiz_type
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 퀴즈 문제
|
||||||
|
in: body
|
||||||
|
name: question
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: '퀴즈 선택지 : [''선택1'',''선택2'',''선택3'', ''선택4'']'
|
||||||
|
in: body
|
||||||
|
name: choice
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
- description: '퀴즈 정답 : [1,3]'
|
||||||
|
in: body
|
||||||
|
name: answer
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
type: array
|
||||||
|
- description: 퀴즈 힌트
|
||||||
|
in: body
|
||||||
|
name: hint
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
- description: 퀴즈 해설
|
||||||
|
in: body
|
||||||
|
name: comment
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizResponse'
|
||||||
|
summary: 퀴즈 수정
|
||||||
|
/quiz/{id}:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: ID로 퀴즈를 가져옵니다.
|
||||||
|
parameters:
|
||||||
|
- description: 퀴즈 ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.QuizResponse'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
summary: 퀴즈 가져오기
|
||||||
|
/token/refresh:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: AccessToken을 RefreshToken으로 갱신합니다.
|
||||||
|
parameters:
|
||||||
|
- description: RefreshToken
|
||||||
|
in: body
|
||||||
|
name: refresh_token
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.ProgramResponse'
|
||||||
|
summary: AccessToken Refresh
|
||||||
|
/user:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 사용자 목록을 가져옵니다.
|
||||||
|
parameters:
|
||||||
|
- description: 검색어
|
||||||
|
in: query
|
||||||
|
name: q
|
||||||
|
type: string
|
||||||
|
- description: 페이지
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: 페이지 사이즈
|
||||||
|
in: query
|
||||||
|
name: limit
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.UserListResponse'
|
||||||
|
summary: 사용자 목록 가져오기
|
||||||
|
/user/{id}:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: ID로 사용자 정보를 가져옵니다.
|
||||||
|
parameters:
|
||||||
|
- description: 사용자 ID
|
||||||
|
in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/learnsteam_learsteam-quiz-api_internal_models.User'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
summary: 사용자 정보 가져오기
|
||||||
|
swagger: "2.0"
|
64
go.mod
64
go.mod
@ -1,46 +1,80 @@
|
|||||||
module studioj/boilerplate_go
|
module learnsteam/learsteam-quiz-api
|
||||||
|
|
||||||
go 1.20
|
go 1.21
|
||||||
|
|
||||||
|
toolchain go1.21.3
|
||||||
|
|
||||||
require github.com/gin-gonic/gin v1.9.1
|
require github.com/gin-gonic/gin v1.9.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
|
github.com/PuerkitoBio/purell v1.2.1 // indirect
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||||
github.com/aws/aws-lambda-go v1.17.0 // indirect
|
github.com/aws/aws-lambda-go v1.17.0 // indirect
|
||||||
|
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.20.0 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
|
github.com/go-openapi/spec v0.20.9 // indirect
|
||||||
|
github.com/go-openapi/swag v0.22.4 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
|
github.com/swaggo/files v1.0.1 // indirect
|
||||||
|
github.com/swaggo/gin-swagger v1.6.0 // indirect
|
||||||
|
github.com/swaggo/swag v1.16.2 // indirect
|
||||||
|
github.com/urfave/cli/v2 v2.25.7 // indirect
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
|
golang.org/x/tools v0.15.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/apex/gateway v1.1.2
|
github.com/apex/gateway v1.1.2
|
||||||
github.com/bytedance/sonic v1.9.1 // indirect
|
github.com/bytedance/sonic v1.10.2 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
|
github.com/gin-contrib/cors v1.4.0
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/golang-jwt/jwt/v5 v5.0.0
|
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||||
github.com/google/uuid v1.3.1
|
github.com/google/uuid v1.3.1
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.20 // 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.0.8 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||||
github.com/stretchr/testify v1.8.4 // indirect
|
github.com/stretchr/testify v1.8.4 // indirect
|
||||||
|
github.com/tj/assert v0.0.3
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.6.0 // indirect
|
||||||
golang.org/x/crypto v0.9.0
|
golang.org/x/crypto v0.15.0
|
||||||
golang.org/x/net v0.10.0 // indirect
|
golang.org/x/net v0.18.0 // indirect
|
||||||
golang.org/x/sys v0.8.0 // indirect
|
golang.org/x/oauth2 v0.14.0
|
||||||
golang.org/x/text v0.9.0 // indirect
|
golang.org/x/sys v0.14.0 // indirect
|
||||||
google.golang.org/protobuf v1.30.0 // indirect
|
golang.org/x/text v0.14.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/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
|
||||||
)
|
)
|
||||||
|
162
go.sum
162
go.sum
@ -1,4 +1,10 @@
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||||
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
|
github.com/PuerkitoBio/purell v1.2.1 h1:QsZ4TjvwiMpat6gBCBxEQI0rcS9ehtkKtSpiUnd9N28=
|
||||||
|
github.com/PuerkitoBio/purell v1.2.1/go.mod h1:ZwHcC/82TOaovDi//J/804umJFFmbOHPngi8iYYv/Eo=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/apex/gateway v1.1.2 h1:OWyLov8eaau8YhkYKkRuOAYqiUhpBJalBR1o+3FzX+8=
|
github.com/apex/gateway v1.1.2 h1:OWyLov8eaau8YhkYKkRuOAYqiUhpBJalBR1o+3FzX+8=
|
||||||
github.com/apex/gateway v1.1.2/go.mod h1:AMTkVbz5u5Hvd6QOGhhg0JUrNgCcLVu3XNJOGntdoB4=
|
github.com/apex/gateway v1.1.2/go.mod h1:AMTkVbz5u5Hvd6QOGhhg0JUrNgCcLVu3XNJOGntdoB4=
|
||||||
github.com/aws/aws-lambda-go v1.17.0 h1:Ogihmi8BnpmCNktKAGpNwSiILNNING1MiosnKUfU8m0=
|
github.com/aws/aws-lambda-go v1.17.0 h1:Ogihmi8BnpmCNktKAGpNwSiILNNING1MiosnKUfU8m0=
|
||||||
@ -6,35 +12,79 @@ github.com/aws/aws-lambda-go v1.17.0/go.mod h1:FEwgPLE6+8wcGBTe5cJN3JWurd1Ztm9zN
|
|||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
|
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||||
|
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
|
||||||
|
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
||||||
|
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||||
|
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
||||||
|
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
|
github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g=
|
||||||
|
github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||||
|
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
|
||||||
|
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
|
||||||
|
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
|
||||||
|
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||||
|
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||||
|
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
|
||||||
|
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
|
||||||
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||||
|
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
|
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||||
|
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
|
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||||
|
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
|
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
|
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
|
||||||
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@ -42,27 +92,59 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
|||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
@ -78,41 +160,121 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
|||||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||||
|
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||||
|
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
|
||||||
|
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
|
||||||
|
github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
|
||||||
|
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
|
||||||
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
|
github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
|
||||||
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
|
github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||||
|
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
|
||||||
|
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
|
||||||
|
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||||
|
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||||
|
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||||
|
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||||
|
golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0=
|
||||||
|
golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||||
|
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
|
||||||
|
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco=
|
||||||
|
gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04=
|
||||||
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
|
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
|
||||||
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
|
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
|
||||||
|
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
|
||||||
|
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||||
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
|
||||||
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||||
|
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||||
|
5
go.work.sum
Normal file
5
go.work.sum
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
@ -3,8 +3,8 @@ package controllers
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"studioj/boilerplate_go/internal/models"
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
"studioj/boilerplate_go/internal/services"
|
"learnsteam/learsteam-quiz-api/internal/services"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -15,15 +15,30 @@ type AuthController interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type authController struct {
|
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{
|
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) {
|
func (controller *authController) Register(c *gin.Context) {
|
||||||
var params models.RegisterRequest
|
var params models.RegisterRequest
|
||||||
if c.BindJSON(¶ms) != nil {
|
if c.BindJSON(¶ms) != nil {
|
||||||
@ -37,7 +52,7 @@ func (controller *authController) Register(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := controller.service.CreateToken(user.ID)
|
token, err := controller.tokenService.Create(user.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()})
|
||||||
return
|
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})
|
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) {
|
func (controller *authController) Login(c *gin.Context) {
|
||||||
var params models.LoginRequest
|
var request models.LoginRequest
|
||||||
if c.BindJSON(¶ms) != nil {
|
if c.BindJSON(&request) != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := controller.service.Login(¶ms)
|
user, err := controller.service.Login(&request)
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := controller.service.CreateToken(user.ID)
|
token, err := controller.tokenService.Create(user.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()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"user": user, "token": token.Token, "refresh_token": token.RefreshToken})
|
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 (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"studioj/boilerplate_go/internal/services"
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/services"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TokenController interface {
|
type TokenController interface {
|
||||||
Find(*gin.Context)
|
Refresh(*gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type tokenController struct {
|
type tokenController struct {
|
||||||
@ -22,41 +23,27 @@ func NewTokenController(service services.TokenService) TokenController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (controller *tokenController) Find(c *gin.Context) {
|
// Refresh Token
|
||||||
id := c.Param("id")
|
//
|
||||||
user_id := c.GetString("user_id")
|
// @Summary AccessToken Refresh
|
||||||
|
// @Description AccessToken을 RefreshToken으로 갱신합니다.
|
||||||
if user_id != id {
|
// @Accept json
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Wrong user"})
|
// @Produce json
|
||||||
return
|
// @Param refresh_token body string true "RefreshToken"
|
||||||
}
|
// @Router /token/refresh [post]
|
||||||
|
// @Success 200 {object} models.ProgramResponse
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (controller *tokenController) Refresh(c *gin.Context) {
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"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"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserController interface {
|
type UserController interface {
|
||||||
|
List(*gin.Context)
|
||||||
Find(*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) {
|
func (controller *userController) Find(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
user_id := c.GetString("sub")
|
user_id := c.GetString("sub")
|
||||||
@ -36,7 +104,7 @@ func (controller *userController) Find(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := controller.service.FindByID(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()})
|
||||||
return
|
return
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
config "studioj/boilerplate_go/configs"
|
"fmt"
|
||||||
"studioj/boilerplate_go/internal/models"
|
config "learnsteam/learsteam-quiz-api/configs"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
|
|
||||||
"gorm.io/driver/mysql"
|
"gorm.io/driver/mysql"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@ -33,8 +34,12 @@ func GetDB() *gorm.DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AutoMigrate() {
|
func AutoMigrate() {
|
||||||
|
fmt.Println("AugoMigrate")
|
||||||
DB.AutoMigrate(
|
DB.AutoMigrate(
|
||||||
&models.User{},
|
&models.User{},
|
||||||
&models.Token{},
|
&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
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,14 +12,6 @@ func InLambda() bool {
|
|||||||
return false
|
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) {
|
func Datetime(timeString string) (time.Time, error) {
|
||||||
layout := "2006-01-02T15:04:05.000Z"
|
layout := "2006-01-02T15:04:05.000Z"
|
||||||
result, err := time.Parse(layout, timeString)
|
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"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
config "studioj/boilerplate_go/configs"
|
config "learnsteam/learsteam-quiz-api/configs"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
@ -5,7 +5,27 @@ type LoginRequest struct {
|
|||||||
Password string `json:"password"`
|
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 {
|
type RegisterRequest struct {
|
||||||
|
Name string `json:"name"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
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"
|
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 {
|
type Token struct {
|
||||||
ID string `json:"id" db:"id" gorm:"column:id;size:255;primary_key;"`
|
ID string `json:"-" db:"id" gorm:"primary_key"`
|
||||||
UserID string `json:"user_id" db:"user_id" gorm:"column:user_id;size:255;index;"`
|
UserID string `json:"-" gorm:"index;"`
|
||||||
Token string `json:"token" gorm:"column:token;size:255;index;"`
|
Token string `json:"token" gorm:"size:255;index;"`
|
||||||
RefreshToken string `json:"refresh_token" gorm:"column:token;size:255;index;"`
|
RefreshToken string `json:"refreshToken" gorm:"size:255"`
|
||||||
Status string `json:"status" gorm:"column:status;size:10;index;"`
|
|
||||||
ExpireAt time.Time `json:"expire_at" gorm:"column:expire_at;index;"`
|
Expires int64 `json:"expires"`
|
||||||
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at;index;"`
|
RefreshExpires int64 `json:"refreshExpires"`
|
||||||
CreatedAt time.Time `json:"created_at" gorm:"column:created_at;index;"`
|
|
||||||
|
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 {
|
type TokenResponse struct {
|
||||||
|
@ -1,9 +1,21 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string `json:"id" db:"id" gorm:"column:id;size:255;primary_key;"`
|
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" gorm:"column:username;size:50;uniqueIndex;"`
|
Username string `json:"username" db:"username" example:"user0" gorm:"column:username;size:50;uniqueIndex;"`
|
||||||
Score int32 `json:"score" db:"score" gorm:"column:score;"`
|
Name string `json:"name" db:"name" example:"홍길동" gorm:"column:name;size:50;"`
|
||||||
Money int32 `json:"money" db:"money" gorm:"column:money;"`
|
Score int32 `json:"score" db:"score" example:"9999" gorm:"column:score;"`
|
||||||
Password string `json:"-" db:"password" gorm:"column:password;size:255;not null;"`
|
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
|
package repositories
|
||||||
|
|
||||||
import (
|
import (
|
||||||
config "studioj/boilerplate_go/configs"
|
config "learnsteam/learsteam-quiz-api/configs"
|
||||||
"studioj/boilerplate_go/internal/models"
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@ -21,6 +21,7 @@ func NewTokenRepository(db *gorm.DB) TokenRepository {
|
|||||||
type TokenRepository interface {
|
type TokenRepository interface {
|
||||||
Generate(string, int64) (string, error)
|
Generate(string, int64) (string, error)
|
||||||
Find(string) (*models.Token, error)
|
Find(string) (*models.Token, error)
|
||||||
|
FindByRefreshToken(string, string) (*models.Token, error)
|
||||||
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
|
||||||
@ -43,6 +44,12 @@ func (r *tokenRepository) Find(id string) (*models.Token, error) {
|
|||||||
return token, err
|
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) {
|
func (r *tokenRepository) Create(token *models.Token) (*models.Token, error) {
|
||||||
err := r.DB.Create(&token).Error
|
err := r.DB.Create(&token).Error
|
||||||
return token, err
|
return token, err
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package repositories
|
package repositories
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"studioj/boilerplate_go/internal/models"
|
"fmt"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@ -17,21 +18,42 @@ func NewUserRepository(db *gorm.DB) UserRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserRepository interface {
|
type UserRepository interface {
|
||||||
List() (*[]models.User, error)
|
List(string, int, int) (*[]models.User, error)
|
||||||
FindByID(string) (*models.User, error)
|
Total(string) (int64, error)
|
||||||
|
Find(string) (*models.User, error)
|
||||||
FindByUsername(string) (*models.User, error)
|
FindByUsername(string) (*models.User, error)
|
||||||
Create(*models.User) (*models.User, error)
|
Create(*models.User) (*models.User, error)
|
||||||
Update(*models.User) (*models.User, error)
|
Update(*models.User) (*models.User, error)
|
||||||
Delete(string) 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
|
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
|
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
|
var user *models.User
|
||||||
err := r.DB.Where("id = ?", id).First(&user).Error
|
err := r.DB.Where("id = ?", id).First(&user).Error
|
||||||
return user, err
|
return user, err
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
package routers
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"studioj/boilerplate_go/internal/controllers"
|
"learnsteam/learsteam-quiz-api/internal/controllers"
|
||||||
"studioj/boilerplate_go/internal/repositories"
|
"learnsteam/learsteam-quiz-api/internal/repositories"
|
||||||
"studioj/boilerplate_go/internal/services"
|
"learnsteam/learsteam-quiz-api/internal/services"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func InitAuthRouter(db *gorm.DB, router *gin.Engine) {
|
||||||
|
r := NewAuthRouter(db, router)
|
||||||
|
r.SetAuthRouter(db, router)
|
||||||
|
}
|
||||||
|
|
||||||
type AuthRouter interface {
|
type AuthRouter interface {
|
||||||
SetRouter(db *gorm.DB, router *gin.Engine)
|
SetRouter(db *gorm.DB, router *gin.Engine)
|
||||||
}
|
}
|
||||||
@ -23,17 +28,12 @@ type authRouter struct {
|
|||||||
router *gin.Engine
|
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 {
|
func NewAuthRouter(db *gorm.DB, router *gin.Engine) *authRouter {
|
||||||
userRepository := repositories.NewUserRepository(db)
|
userRepository := repositories.NewUserRepository(db)
|
||||||
tokenRepository := repositories.NewTokenRepository(db)
|
tokenRepository := repositories.NewTokenRepository(db)
|
||||||
service := services.NewAuthService(userRepository, tokenRepository)
|
service := services.NewAuthService(userRepository, tokenRepository)
|
||||||
tokenService := services.NewTokenService(tokenRepository)
|
tokenService := services.NewTokenService(tokenRepository)
|
||||||
controller := controllers.NewAuthController(service)
|
controller := controllers.NewAuthController(service, tokenService)
|
||||||
|
|
||||||
return &authRouter{
|
return &authRouter{
|
||||||
db: db,
|
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
|
package routers
|
||||||
|
|
||||||
import (
|
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
|
var Router *gin.Engine
|
||||||
@ -11,9 +12,19 @@ var Router *gin.Engine
|
|||||||
func Init() {
|
func Init() {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
Router = gin.Default()
|
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()
|
maindb := database.GetDB()
|
||||||
|
|
||||||
InitAuthRouter(maindb, Router)
|
InitAuthRouter(maindb, Router)
|
||||||
InitTokenRouter(maindb, Router)
|
InitTokenRouter(maindb, Router)
|
||||||
InitUserRouter(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
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"studioj/boilerplate_go/internal/controllers"
|
"learnsteam/learsteam-quiz-api/internal/controllers"
|
||||||
"studioj/boilerplate_go/internal/repositories"
|
"learnsteam/learsteam-quiz-api/internal/repositories"
|
||||||
"studioj/boilerplate_go/internal/services"
|
"learnsteam/learsteam-quiz-api/internal/services"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func InitTokenRouter(db *gorm.DB, router *gin.Engine) {
|
||||||
|
r := NewTokenRouter(db, router)
|
||||||
|
r.SetTokenRouter(db, router)
|
||||||
|
}
|
||||||
|
|
||||||
type TokenRouter interface {
|
type TokenRouter interface {
|
||||||
SetRouter(db *gorm.DB, router *gin.Engine)
|
SetRouter(db *gorm.DB, router *gin.Engine)
|
||||||
}
|
}
|
||||||
@ -21,11 +26,6 @@ type tokenRouter struct {
|
|||||||
router *gin.Engine
|
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 {
|
func NewTokenRouter(db *gorm.DB, router *gin.Engine) *tokenRouter {
|
||||||
repository := repositories.NewTokenRepository(db)
|
repository := repositories.NewTokenRepository(db)
|
||||||
service := services.NewTokenService(repository)
|
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) {
|
func (r *tokenRouter) SetTokenRouter(db *gorm.DB, router *gin.Engine) {
|
||||||
// group := router.Group("/token")
|
group := router.Group("/token")
|
||||||
// group.GET("refresh", middleware.Auth(), r.controller.Refresh)
|
group.POST("refresh", r.controller.Refresh)
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
package routers
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"studioj/boilerplate_go/internal/controllers"
|
"learnsteam/learsteam-quiz-api/internal/controllers"
|
||||||
"studioj/boilerplate_go/internal/middleware"
|
"learnsteam/learsteam-quiz-api/internal/middleware"
|
||||||
"studioj/boilerplate_go/internal/repositories"
|
"learnsteam/learsteam-quiz-api/internal/repositories"
|
||||||
"studioj/boilerplate_go/internal/services"
|
"learnsteam/learsteam-quiz-api/internal/services"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func InitUserRouter(db *gorm.DB, router *gin.Engine) {
|
||||||
|
r := NewUserRouter(db, router)
|
||||||
|
r.SetUserRouter(db, router)
|
||||||
|
}
|
||||||
|
|
||||||
type UserRouter interface {
|
type UserRouter interface {
|
||||||
SetRouter(db *gorm.DB, router *gin.Engine)
|
SetRouter(db *gorm.DB, router *gin.Engine)
|
||||||
}
|
}
|
||||||
@ -22,11 +27,6 @@ type userRouter struct {
|
|||||||
router *gin.Engine
|
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 {
|
func NewUserRouter(db *gorm.DB, router *gin.Engine) *userRouter {
|
||||||
repository := repositories.NewUserRepository(db)
|
repository := repositories.NewUserRepository(db)
|
||||||
tokenRepository := repositories.NewTokenRepository(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) {
|
func (r *userRouter) SetUserRouter(db *gorm.DB, router *gin.Engine) {
|
||||||
group := router.Group("/user")
|
group := router.Group("/user")
|
||||||
|
group.GET("", middleware.Auth(), r.controller.List)
|
||||||
group.GET("/:id", middleware.Auth(), r.controller.Find)
|
group.GET("/:id", middleware.Auth(), r.controller.Find)
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,8 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"studioj/boilerplate_go/internal/models"
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
"studioj/boilerplate_go/internal/repositories"
|
"learnsteam/learsteam-quiz-api/internal/repositories"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
@ -18,7 +17,6 @@ type authService struct {
|
|||||||
type AuthService interface {
|
type AuthService interface {
|
||||||
Register(*models.RegisterRequest) (*models.User, error)
|
Register(*models.RegisterRequest) (*models.User, error)
|
||||||
Login(*models.LoginRequest) (*models.User, error)
|
Login(*models.LoginRequest) (*models.User, error)
|
||||||
CreateToken(string) (*models.Token, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthService(userRepository repositories.UserRepository, tokenRepository repositories.TokenRepository) AuthService {
|
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{
|
newUser := models.User{
|
||||||
ID: uuid.NewString(),
|
ID: uuid.NewString(),
|
||||||
Username: request.Username,
|
Username: request.Username,
|
||||||
|
Name: request.Name,
|
||||||
|
Score: 0,
|
||||||
Password: string(hash),
|
Password: string(hash),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,30 +61,3 @@ func (s *authService) Login(request *models.LoginRequest) (*models.User, error)
|
|||||||
}
|
}
|
||||||
return user, nil
|
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
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
config "studioj/boilerplate_go/configs"
|
config "learnsteam/learsteam-quiz-api/configs"
|
||||||
"studioj/boilerplate_go/internal/models"
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
"studioj/boilerplate_go/internal/repositories"
|
"learnsteam/learsteam-quiz-api/internal/repositories"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tokenService struct {
|
type tokenService struct {
|
||||||
@ -17,10 +20,12 @@ type tokenService struct {
|
|||||||
|
|
||||||
type TokenService interface {
|
type TokenService interface {
|
||||||
Find(string) (*models.Token, error)
|
Find(string) (*models.Token, error)
|
||||||
Create(*models.Token) (*models.Token, error)
|
Create(string) (*models.Token, error)
|
||||||
Update(*models.Token) (*models.Token, error)
|
Update(*models.Token) (*models.Token, error)
|
||||||
Delete(string) error
|
Delete(string) error
|
||||||
|
|
||||||
|
Refresh(string) (*models.Token, error)
|
||||||
|
|
||||||
Generate(string, int64) (string, error)
|
Generate(string, int64) (string, error)
|
||||||
Verify(tokenString string) (*jwt.Token, 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)
|
return s.repository.Find(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *tokenService) Create(token *models.Token) (*models.Token, error) {
|
func (s *tokenService) Create(sub string) (*models.Token, error) {
|
||||||
return s.repository.Create(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
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
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
|
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
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"studioj/boilerplate_go/internal/models"
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
"studioj/boilerplate_go/internal/repositories"
|
"learnsteam/learsteam-quiz-api/internal/repositories"
|
||||||
)
|
)
|
||||||
|
|
||||||
type userService struct {
|
type userService struct {
|
||||||
@ -11,7 +11,9 @@ type userService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserService interface {
|
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)
|
FindByUsername(string) (*models.User, error)
|
||||||
Create(*models.User) (*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) {
|
func (s *userService) List(q string, page int, limit int) (*[]models.User, error) {
|
||||||
return s.repository.FindByID(id)
|
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) {
|
func (s *userService) FindByUsername(username string) (*models.User, error) {
|
||||||
|
190
test/program_test.go
Normal file
190
test/program_test.go
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package learsteam_quiz_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/controllers"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/repositories"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/services"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/tj/assert"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProgramTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
db *gorm.DB
|
||||||
|
repository repositories.ProgramRepository
|
||||||
|
service services.ProgramService
|
||||||
|
controller controllers.ProgramController
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ProgramTestSuite) SetupSuite() {
|
||||||
|
// err := os.Remove("test.db")
|
||||||
|
// if err != nil {
|
||||||
|
// suite.Fail("Failed to remove the test database file")
|
||||||
|
// }
|
||||||
|
|
||||||
|
gorm_config := gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}}
|
||||||
|
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm_config)
|
||||||
|
db.AutoMigrate(&models.Program{})
|
||||||
|
|
||||||
|
repository := repositories.NewProgramRepository(db)
|
||||||
|
service := services.NewProgramService(repository)
|
||||||
|
controller := controllers.NewProgramController(service)
|
||||||
|
|
||||||
|
suite.db = db
|
||||||
|
suite.service = service
|
||||||
|
suite.repository = repository
|
||||||
|
suite.controller = controller
|
||||||
|
|
||||||
|
suite.CreateSampleData()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ProgramTestSuite) CreateSampleData() {
|
||||||
|
var programs []models.Program
|
||||||
|
for i := 1; i < 101; i++ {
|
||||||
|
t := []string{"tag" + strconv.Itoa(i), "taga", "tagb"}
|
||||||
|
var j datatypes.JSON
|
||||||
|
j, _ = json.Marshal(t)
|
||||||
|
p := models.Program{
|
||||||
|
ID: strconv.Itoa(i) + "_id",
|
||||||
|
Subject: strconv.Itoa(i) + " subject",
|
||||||
|
Content: strconv.Itoa(i) + " content",
|
||||||
|
Tag: j,
|
||||||
|
Tags: j.String(),
|
||||||
|
Status: "on",
|
||||||
|
PublishAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
programs = append(programs, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, program := range programs {
|
||||||
|
suite.db.Create(&program)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ProgramTestSuite) TearDownSuite() {
|
||||||
|
// DB 삭제
|
||||||
|
suite.db.Migrator().DropTable(&models.Program{})
|
||||||
|
err := os.Remove("test.db")
|
||||||
|
if err != nil {
|
||||||
|
suite.Fail("Failed to remove the test database file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ProgramTestSuite) SetupTest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ProgramTestSuite) TearDownTest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProgramSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(ProgramTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 목록 테스트 : 1page, 10개
|
||||||
|
func (suite *ProgramTestSuite) TestListProgramSuccess() {
|
||||||
|
programs, err := suite.repository.List("", "", 1, 10)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
assert.NotNil(suite.T(), programs)
|
||||||
|
assert.Equal(suite.T(), 10, len(*programs))
|
||||||
|
program0 := (*programs)[0]
|
||||||
|
assert.Equal(suite.T(), "100_id", program0.ID)
|
||||||
|
|
||||||
|
program9 := (*programs)[9]
|
||||||
|
assert.Equal(suite.T(), "91_id", program9.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 목록 테스트 : tag 검색
|
||||||
|
func (suite *ProgramTestSuite) TestListProgramTagSearchSuccess() {
|
||||||
|
programs, err := suite.repository.List("", "tag9", 1, 10)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
assert.NotNil(suite.T(), programs)
|
||||||
|
assert.Equal(suite.T(), 10, len(*programs))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total 테스트
|
||||||
|
func (suite *ProgramTestSuite) TestTotalSuccess() {
|
||||||
|
total, err := suite.repository.Total("", "")
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
assert.Equal(suite.T(), int64(100), total)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total 테스트 : tag 검색
|
||||||
|
func (suite *ProgramTestSuite) TestTotalTagSuccess() {
|
||||||
|
total, err := suite.repository.Total("", "tag2")
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
assert.Equal(suite.T(), int64(11), total)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 수정 테스트
|
||||||
|
func (suite *ProgramTestSuite) TestUpdateProgramSuccess() {
|
||||||
|
t := []string{"TAG"}
|
||||||
|
var j datatypes.JSON
|
||||||
|
j, _ = json.Marshal(t)
|
||||||
|
p := &models.Program{
|
||||||
|
ID: uuid.NewString(),
|
||||||
|
Subject: "My Subject",
|
||||||
|
Content: "My Content",
|
||||||
|
Tag: j,
|
||||||
|
Tags: j.String(),
|
||||||
|
Status: "on",
|
||||||
|
PublishAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
program, err := suite.repository.Create(p)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
program.Subject = "My Subject Updated"
|
||||||
|
program.Content = "My Content Updated"
|
||||||
|
program.Tag = j
|
||||||
|
program.Tags = j.String()
|
||||||
|
program.Status = "draft"
|
||||||
|
|
||||||
|
updatedProgram, err := suite.repository.Update(program)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
assert.Equal(suite.T(), program.Subject, updatedProgram.Subject)
|
||||||
|
assert.Equal(suite.T(), program.Content, updatedProgram.Content)
|
||||||
|
assert.Equal(suite.T(), program.Tag, updatedProgram.Tag)
|
||||||
|
assert.Equal(suite.T(), program.Status, updatedProgram.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 삭제 테스트
|
||||||
|
func (suite *ProgramTestSuite) TestDeleteProgramSuccess() {
|
||||||
|
t := []string{"TAG"}
|
||||||
|
var j datatypes.JSON
|
||||||
|
j, _ = json.Marshal(t)
|
||||||
|
p := &models.Program{
|
||||||
|
ID: uuid.NewString(),
|
||||||
|
Subject: "My Subject",
|
||||||
|
Content: "My Content",
|
||||||
|
Tag: j,
|
||||||
|
Tags: j.String(),
|
||||||
|
Status: "on",
|
||||||
|
PublishAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
_, err := suite.repository.Create(p)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
err = suite.repository.Delete(p.ID)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
_, err = suite.repository.Find(p.ID)
|
||||||
|
assert.Error(suite.T(), err)
|
||||||
|
}
|
183
test/quiz_test.go
Normal file
183
test/quiz_test.go
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
package learsteam_quiz_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/controllers"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/models"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/repositories"
|
||||||
|
"learnsteam/learsteam-quiz-api/internal/services"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/tj/assert"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QuizTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
db *gorm.DB
|
||||||
|
repository repositories.QuizRepository
|
||||||
|
service services.QuizService
|
||||||
|
controller controllers.QuizController
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuizTestSuite) SetupSuite() {
|
||||||
|
// err := os.Remove("test.db")
|
||||||
|
// if err != nil {
|
||||||
|
// suite.Fail("Failed to remove the test database file")
|
||||||
|
// }
|
||||||
|
|
||||||
|
gorm_config := gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}}
|
||||||
|
db, _ := gorm.Open(sqlite.Open("test.db"), &gorm_config)
|
||||||
|
db.AutoMigrate(&models.Quiz{})
|
||||||
|
|
||||||
|
repository := repositories.NewQuizRepository(db)
|
||||||
|
service := services.NewQuizService(repository)
|
||||||
|
controller := controllers.NewQuizController(service)
|
||||||
|
|
||||||
|
suite.db = db
|
||||||
|
suite.service = service
|
||||||
|
suite.repository = repository
|
||||||
|
suite.controller = controller
|
||||||
|
|
||||||
|
suite.CreateSampleData()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuizTestSuite) CreateSampleData() {
|
||||||
|
var quizzes []models.Quiz
|
||||||
|
program_id := "1_id"
|
||||||
|
|
||||||
|
c := []string{"1", "2", "3", "4"}
|
||||||
|
var j datatypes.JSON
|
||||||
|
j, _ = json.Marshal(c)
|
||||||
|
|
||||||
|
for i := 1; i < 6; i++ {
|
||||||
|
q := models.Quiz{
|
||||||
|
ID: strconv.Itoa(i),
|
||||||
|
ProgramID: program_id,
|
||||||
|
Sequence: i,
|
||||||
|
QuizType: "multiple",
|
||||||
|
Question: strconv.Itoa(i) + " question",
|
||||||
|
Choice: j,
|
||||||
|
Answer: j,
|
||||||
|
Hint: "힌트입니다.",
|
||||||
|
Comment: "설명입니다.",
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
quizzes = append(quizzes, q)
|
||||||
|
}
|
||||||
|
for _, quiz := range quizzes {
|
||||||
|
suite.db.Create(&quiz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuizTestSuite) TearDownSuite() {
|
||||||
|
// DB 삭제
|
||||||
|
suite.db.Migrator().DropTable(&models.Quiz{})
|
||||||
|
err := os.Remove("test.db")
|
||||||
|
if err != nil {
|
||||||
|
suite.Fail("Failed to remove the test database file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuizTestSuite) SetupTest() {
|
||||||
|
suite.CreateSampleData()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *QuizTestSuite) TearDownTest() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuizSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(QuizTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 목록 테스트
|
||||||
|
func (suite *QuizTestSuite) TestListQuizSuccess() {
|
||||||
|
quizzes, err := suite.repository.List("id_1", 1, 10)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
assert.NotNil(suite.T(), quizzes)
|
||||||
|
assert.Equal(suite.T(), 5, len(*quizzes))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find 테스트
|
||||||
|
func (suite *QuizTestSuite) TestFindQuizSuccess() {
|
||||||
|
quiz, err := suite.repository.Find("1")
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
assert.Equal(suite.T(), "1", quiz.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find 테스트
|
||||||
|
func (suite *QuizTestSuite) TestFindQuizFail() {
|
||||||
|
_, err := suite.repository.Find("x")
|
||||||
|
assert.Error(suite.T(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 수정 테스트
|
||||||
|
func (suite *QuizTestSuite) TestUpdateQuizFail() {
|
||||||
|
c := []string{"1", "2", "3", "4"}
|
||||||
|
var j datatypes.JSON
|
||||||
|
j, _ = json.Marshal(c)
|
||||||
|
q := &models.Quiz{
|
||||||
|
ID: uuid.NewString(),
|
||||||
|
ProgramID: uuid.NewString(),
|
||||||
|
Sequence: 1,
|
||||||
|
QuizType: "multiple",
|
||||||
|
Question: "question",
|
||||||
|
Choice: j,
|
||||||
|
Answer: j,
|
||||||
|
Hint: "힌트입니다.",
|
||||||
|
Comment: "설명입니다.",
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
quiz, err := suite.repository.Create(q)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
quiz.QuizType = "check"
|
||||||
|
quiz.Choice = j
|
||||||
|
quiz.Answer = j
|
||||||
|
quiz.Hint = "힌트입니다!"
|
||||||
|
|
||||||
|
updatedQuiz, err := suite.repository.Update(quiz)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
assert.Equal(suite.T(), updatedQuiz.QuizType, quiz.QuizType)
|
||||||
|
assert.Equal(suite.T(), updatedQuiz.Choice, quiz.Choice)
|
||||||
|
assert.Equal(suite.T(), updatedQuiz.Answer, quiz.Answer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 삭제 테스트
|
||||||
|
func (suite *QuizTestSuite) TestDeleteQuizSuccess() {
|
||||||
|
c := []string{"1", "2", "3", "4"}
|
||||||
|
var j datatypes.JSON
|
||||||
|
j, _ = json.Marshal(c)
|
||||||
|
q := &models.Quiz{
|
||||||
|
ID: uuid.NewString(),
|
||||||
|
ProgramID: uuid.NewString(),
|
||||||
|
Sequence: 1,
|
||||||
|
QuizType: "multiple",
|
||||||
|
Question: "question",
|
||||||
|
Choice: j,
|
||||||
|
Answer: j,
|
||||||
|
Hint: "힌트입니다.",
|
||||||
|
Comment: "설명입니다.",
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
_, err := suite.repository.Create(q)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
err = suite.repository.Delete(q.ID)
|
||||||
|
assert.NoError(suite.T(), err)
|
||||||
|
_, err = suite.repository.Find(q.ID)
|
||||||
|
assert.Error(suite.T(), err)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package octet_test
|
package learsteam_quiz_test
|
||||||
|
|
||||||
// type TokenTestSuite struct {
|
// type TokenTestSuite struct {
|
||||||
// suite.Suite
|
// suite.Suite
|
BIN
tests/test.db
BIN
tests/test.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user