feat sketch 文件预览仓库

This commit is contained in:
bing 2022-07-04 14:23:36 +08:00
parent 3dda735270
commit 91aaa95ef2
25 changed files with 9562 additions and 1 deletions

2
.gitignore vendored
View File

@ -25,4 +25,4 @@ docs/_book
# TODO: where does this rule come from?
test/
bin

16
backend/api/docs.go Normal file
View File

@ -0,0 +1,16 @@
package api
import "gorm.io/gorm"
type Doc struct {
gorm.Model
Name string `json:"name,omitempty"`
File string `json:"file,omitempty"`
Preview string `json:"preview,omitempty"`
Permission int `json:"permission,omitempty"`
Size int64 `json:"size,omitempty"`
State int `json:"state,omitempty"`
Owner int `json:"owner,omitempty"`
Category string `josn:"category,omitempty"`
Labels string `json:"labels,omitempty"`
}

8
backend/api/groups.go Normal file
View File

@ -0,0 +1,8 @@
package api
import "gorm.io/gorm"
type Group struct {
gorm.Model
Name string
}

11
backend/api/users.go Normal file
View File

@ -0,0 +1,11 @@
package api
import "gorm.io/gorm"
type User struct {
gorm.Model
Name string `json:"name,omitempty"`
Phone string `json:"phone,omitempty"`
Email string `json:"mobile,omitempty"`
Password string `json:"password,omitempty"`
}

142
backend/cmd/main.go Normal file
View File

@ -0,0 +1,142 @@
package main
import (
"flag"
"io"
"log"
"os"
"path"
"strings"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"sketchlib.bing89.com/backend/api"
"sketchlib.bing89.com/backend/utils/reader"
)
type sketchFile struct {
Dir string
Name string
Path string
}
func getFiles(input, parent string) ([]sketchFile, error) {
res := []sketchFile{}
dirs, err := os.ReadDir(input)
if err != nil {
return res, err
}
for _, d := range dirs {
if d.IsDir() {
p := d.Name()
if parent != "" {
p = path.Join(parent, d.Name())
}
r, err := getFiles(path.Join(input, d.Name()), p)
if err == nil {
res = append(res, r...)
}
} else {
if fileExt := strings.ToLower(path.Ext(d.Name())); fileExt == ".sketch" {
f := sketchFile{Dir: parent, Name: d.Name(), Path: path.Join(input, d.Name())}
res = append(res, f)
}
}
}
return res, nil
}
func copy(src, dest string) (int64, error) {
state, err := os.Stat(src)
if err != nil {
return 0, err
}
srcf, err := os.Open(src)
if err != nil {
return 0, err
}
defer srcf.Close()
destf, err := os.Create(dest)
if err != nil {
if os.IsExist(err) {
return state.Size(), nil
}
return 0, err
}
defer destf.Close()
return io.Copy(destf, srcf)
}
func process(db *gorm.DB, output string, files ...sketchFile) ([]sketchFile, error) {
res := []sketchFile{}
sketch := reader.Sketch{}
for _, file := range files {
log.Println("process file", file.Name, " path:", file.Path, " dir:", file.Dir)
destDir := path.Join(output, file.Dir)
if _, err := os.Stat(destDir); err != nil {
err := os.MkdirAll(destDir, os.ModePerm)
if err != nil {
res = append(res, file)
log.Println("create dir failed:", destDir, err)
continue
}
}
dest := path.Join(destDir, file.Name)
n, err := copy(file.Path, dest)
if err != nil {
log.Println("copyfile failed:", err)
res = append(res, file)
continue
}
doc := api.Doc{
Name: file.Name,
File: path.Join(file.Dir, file.Name),
Preview: path.Join(file.Dir, file.Name) + ".png",
Permission: -1,
Size: n,
Labels: "sketch",
State: 1,
}
err = db.Create(&doc).Error
if err != nil {
log.Println("insert data failed:", err)
res = append(res, file)
continue
}
err = sketch.SavePreview(dest)
if err != nil {
log.Println("save preview failed:", err)
res = append(res, file)
}
}
return res, nil
}
func main() {
var uri, input, output string
var init bool
flag.BoolVar(&init, "init", false, "init database")
flag.StringVar(&uri, "uri", "root:Hello2022@tcp(192.168.0.134:3306)/sketchlib?charset=utf8mb4&parseTime=True&loc=Local", "db uri")
flag.StringVar(&input, "input", "", "scketch file")
flag.StringVar(&output, "output", "", "output to")
flag.Parse()
db, err := gorm.Open(mysql.Open(uri), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
files, err := getFiles(input, "/")
if err != nil {
log.Fatal(err)
}
if output == "" {
log.Fatal("output not set")
}
fs, err := process(db, output, files...)
if err != nil {
log.Println(err)
}
for _, f := range fs {
log.Println("file process failed:", f)
}
}

41
backend/main.go Normal file
View File

@ -0,0 +1,41 @@
package main
import (
"flag"
"log"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"sketchlib.bing89.com/backend/api"
"sketchlib.bing89.com/backend/server"
)
func initDatabase(db *gorm.DB)error{
return db.AutoMigrate(&api.Doc{}, &api.Group{}, &api.User{})
}
func main(){
var uri, dir, addr string
var init bool
flag.BoolVar(&init, "init", false, "init database")
flag.StringVar(&uri, "uri", "root:Hello2022@tcp(192.168.0.134:3306)/sketchlib?charset=utf8mb4&parseTime=True&loc=Local", "db uri")
flag.StringVar(&dir, "dir", "/opt/sketchlib", "file dir")
flag.StringVar(&addr, "addr", "0.0.0.0:8080", "addr to listen")
flag.Parse()
db, err := gorm.Open(mysql.Open(uri), &gorm.Config{})
if err != nil {
log.Fatal(err)
}
if init {
err = initDatabase(db)
if err != nil {
log.Fatal(err)
}
return
}
svr := server.NewServer(dir, db)
err = svr.Run(addr)
if err != nil {
log.Fatal(err)
}
}

200
backend/server/server.go Normal file
View File

@ -0,0 +1,200 @@
package server
import (
"crypto/md5"
"encoding/hex"
"fmt"
"net/http"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"sketchlib.bing89.com/backend/api"
"sketchlib.bing89.com/backend/utils/reader"
)
const (
SUCCESS = "success"
)
type Server struct{
sketchReader reader.SlmReader
tokenMap map[string]string
fileDir string
database *gorm.DB
}
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func NewServer(dir string, db *gorm.DB)*Server{
return &Server{
sketchReader: &reader.Sketch{},
tokenMap: make(map[string]string),
fileDir: dir,
database: db,
}
}
func (s *Server)HandleLogin(c *gin.Context){
user := api.User{}
err := c.ShouldBind(&user)
if err != nil {
s.ResponseError(c, 1000, err)
return
}
token := s.GenToken(&user)
s.ResponseSuccess(c, SUCCESS, token)
}
func (s *Server)GenToken(user *api.User)string{
return s.md5(fmt.Sprintf("%d-%s-%s", user.ID, user.Name, user.Phone))
}
func (s *Server)md5(str string)string{
m := md5.New()
m.Write([]byte(str))
res := hex.EncodeToString(m.Sum(nil))
fmt.Println("md5:", string(res))
return string(res)
}
func (s *Server)ResponseSuccess(c *gin.Context, msg string, data interface{}){
c.JSON(http.StatusOK, Response{Code: 0, Message: msg, Data: data})
}
func (s *Server)ResponseError(c *gin.Context, code int, err error){
c.JSON(http.StatusOK, Response{Code: code, Message: err.Error(), Data: err})
}
func (s *Server)Upload(c *gin.Context){
file, err := c.FormFile("file")
if err != nil {
s.ResponseError(c, 1001, err)
return
}
fileExt:=strings.ToLower(path.Ext(file.Filename))
if fileExt!=".sketch" && fileExt!=".xk"{
s.ResponseError(c, 1002, fmt.Errorf("only support sketch file"))
return
}
// fileName:=s.md5(fmt.Sprintf("%s%s",file.Filename, time.Now().String()))
fildDir := path.Join(fmt.Sprintf("%d", time.Now().Year()),fmt.Sprintf("%d", time.Now().Month()))
err = s.checkDir(fildDir)
if err != nil {
s.ResponseError(c, 1002, err)
return
}
filepath := path.Join(fildDir,file.Filename)
err = c.SaveUploadedFile(file, path.Join(s.fileDir, filepath))
if err != nil {
s.ResponseError(c, 1003, err)
return
}
go s.sketchReader.SavePreview(path.Join(s.fileDir, filepath))
doc := api.Doc{
Name: file.Filename,
File: filepath,
Preview: filepath+".png",
Permission: -1,
State: 1,
Owner: 0,
Size: file.Size,
}
err = s.database.Save(&doc).Error
if err != nil {
s.ResponseError(c, 1100, err)
return
}
s.ResponseSuccess(c,SUCCESS, doc)
}
func (s *Server)UploadBatch(c *gin.Context){
file, err := c.FormFile("file")
if err != nil {
s.ResponseError(c, 1001, err)
return
}
fileExt:=strings.ToLower(path.Ext(file.Filename))
if fileExt!=".sketch" && fileExt!=".xk"{
s.ResponseError(c, 1002, fmt.Errorf("only support sketch file"))
return
}
fileName:=s.md5(fmt.Sprintf("%s%s",file.Filename, time.Now().String()))
fildDir := path.Join(fmt.Sprintf("%d", time.Now().Year()),fmt.Sprintf("%d", time.Now().Month()))
err = s.checkDir(fildDir)
if err != nil {
s.ResponseError(c, 1002, err)
return
}
filepath := path.Join(fildDir,fileName + fileExt)
err = c.SaveUploadedFile(file, path.Join(s.fileDir, filepath))
if err != nil {
s.ResponseError(c, 1003, err)
return
}
go s.sketchReader.SavePreview(path.Join(s.fileDir, filepath))
doc := api.Doc{
Name: file.Filename,
File: filepath,
Preview: filepath+".png",
Permission: -1,
State: 1,
Owner: 0,
}
err = s.database.Save(&doc).Error
if err != nil {
s.ResponseError(c, 1100, err)
return
}
s.ResponseSuccess(c,SUCCESS, doc)
}
func (s *Server)List(c *gin.Context){
limit := -1
offset := -1
if n, err := strconv.Atoi(c.Query("limit")); err == nil && n >0 {
limit = n
}
if n, err := strconv.Atoi(c.Query("offset")); err == nil && n >0 {
offset = n
}
size := 0
if n, err := strconv.Atoi(c.Query("size")); err == nil{
size = n
}
db := s.database.Limit(limit).Offset(offset).Where("size > ?", size)
name := c.Query("name")
if name != "" {
db = db.Where(" name like '%"+ name+"%' ")
}
docs := []api.Doc{}
err := db.Find(&docs).Error
if err != nil {
s.ResponseError(c, 1102, err)
return
}
s.ResponseSuccess(c, SUCCESS, docs)
}
func (s *Server)checkDir(dir string)error{
if _, err := os.Stat(dir); !os.IsNotExist(err) {
return err
}
return os.MkdirAll(dir, os.ModePerm)
}
func (s *Server)Run(addr string)error{
engine := gin.Default()
apiGroup := engine.Group("/api/v1")
apiGroup.POST("/upload", s.Upload)
apiGroup.GET("/docs", s.List)
return engine.Run(addr)
}

View File

@ -0,0 +1,7 @@
package reader
type SlmReader interface{
ReadPreview(string)([]byte, error)
ReadPreviewFromData([]byte)([]byte, error)
SavePreview(string)error
}

View File

@ -0,0 +1,51 @@
package reader
import (
"archive/zip"
"bytes"
"io/ioutil"
"os"
)
const SketchPreviewPath = "previews/preview.png"
type Sketch struct {
}
func (s *Sketch)ReadPreviewFromData(data []byte)([]byte, error){
readAt := bytes.NewReader(data)
ziper, err := zip.NewReader(readAt, int64(len(data)))
if err != nil {
return nil, err
}
pf, err := ziper.Open(SketchPreviewPath)
if err != nil {
return nil, err
}
defer pf.Close()
res, err := ioutil.ReadAll(pf)
return res, err
}
func (s *Sketch)ReadPreview(name string)([]byte, error){
ziper, err := zip.OpenReader(name)
if err != nil {
return nil, err
}
pf, err := ziper.Open(SketchPreviewPath)
if err != nil {
return nil, err
}
defer pf.Close()
res, err := ioutil.ReadAll(pf)
return res, err
}
func (s *Sketch)SavePreview(file string)error{
data, err := s.ReadPreview(file)
if err != nil {
return err
}
return os.WriteFile(file+".png", data, os.ModePerm)
}

23
frontend/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

24
frontend/README.md Normal file
View File

@ -0,0 +1,24 @@
# frontend
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

5
frontend/babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

19
frontend/jsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

8550
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

45
frontend/package.json Normal file
View File

@ -0,0 +1,45 @@
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.27.2",
"core-js": "^3.8.3",
"view-ui-plus": "^1.3.1",
"vue": "^3.2.13"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}

BIN
frontend/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Sketch样张库</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

189
frontend/src/App.vue Normal file
View File

@ -0,0 +1,189 @@
<template>
<div>
<div>
<Row style="margin-top: 5px;margin-bottom: 5px;padding-left: 20px;padding-right: 20px;height: 30px;">
<Col span="16" offset="4">
<h3>Sketch样张库</h3>
</Col>
<Col span="4">
<Upload multiple action="/api/v1/upload" :on-success="uploadSuccess" clearFiles="clearFiles" ref="upload">
<Button icon="ios-cloud-upload-outline">Upload file</Button>
</Upload>
</Col>
</Row>
<Row style="margin-top: 5px;margin-bottom: 5px;padding-left: 20px;padding-right: 20px;height: 40px;">
<Col span="10" style="padding-right: 10px;">
<Input v-model="filterName" placeholder="输入文件名包含的字符"><template #prepend>
<span>文件名包含:</span>
</template>
</Input>
</Col>
<Col span="10" style="padding-right: 10px;">
<Input v-model="filterSize" placeholder="">
<template #prepend>
<span>文件大于:</span>
</template>
<template #append>
<span>MB</span>
</template>
</Input>
</Col>
<Col span="4">
<Button type="primary" style="width: 8em;" @click="doFilter">筛选</Button>
</Col>
</Row>
</div>
<div>
<Scroll :on-reach-bottom="handleReachTop" :height="scrollHeight">
<Row>
<Col span="3" v-for="(doc, index) in docs" :key="index" style="padding: 5px;">
<Card>
<div>
<Row @click="() => { clickDoc(doc) }">
<Image :src="doc.preview" fit="fill" alt="fill" style="height: 120px;">
<template #error>
<Icon type="ios-image-outline" size="24" color="#ccc" />
</template>
</Image>
<ImagePreview v-model="modal" :preview-list="previewList" />
</Row>
<Row>
<Col span="20">
<h4 style="display: block;overflow: hidden; white-space: nowrap; text-overflow: ellipsis;">{{ doc.name
}}
</h4>
</Col>
<Col span="4" style="text-align: right;">
<!-- <Button type="primary" @click="() => { download(doc) }" small>下载</Button> -->
<Icon @click="() => { download(doc) }" type="md-cloud-download" size="20" />
</Col>
</Row>
</div>
</Card>
</Col>
</Row>
</Scroll>
</div>
</div>
<div>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue'
import { GetDocs } from "@/apis/api.js"
export default {
name: 'App',
components: {
HelloWorld
},
data() {
return {
docs: [],
page: 1,
modal: false,
title: "",
selectedDoc: { name: "", preview: "" },
previewList: [],
filterName: "",
filterSize: 0,
limit: 100,
offset: 0,
scrollHeight: 800,
}
},
methods: {
getDocs: function () {
GetDocs({ limit: this.limit, offset: this.offset, size: this.filterSize * 1024 * 1024, name: this.filterName }).then(res => {
console.log("docs", res)
if (res.code == 0) {
if (this.docs.length == 0) {
this.docs = res.data
} else {
res.data.forEach(doc => {
this.docs.push(doc)
});
}
}
})
},
clickDoc: function (doc) {
// this.title = doc.name;
this.$ImagePreview.show({
previewList: [doc.preview],
toolbar: ['zoomIn', 'zoomOut', 'original', 'rotateLeft', 'rotateRight']
});
},
ok: function () {
},
cancel: function () {
},
download: function (doc) {
let url = location.protocol + '//' + location.host + "/" + doc.file;
window.open(url)
},
uploadSuccess: function (res, file) {
let fileList = []
this.$refs.upload.fileList.forEach(file => {
if (file.status != "finished") {
fileList.push(file)
}
})
this.$refs.upload.fileList = fileList
// this.$Notice.success({
// title: "",
// desc: file.name,
// render: h => {
// return h('span', [file.name
// ])
// },
// duration: 3,
// });
},
doFilter: function () {
this.docs = []
this.getDocs()
},
handleReachTop: function () {
console.info("123")
this.offset = this.offset + this.limit;
return new Promise(resolve => {
GetDocs({ limit: this.limit, offset: this.offset, size: this.filterSize * 1024 * 1024, name: this.filterName }).then(res => {
console.log("docs", res)
if (res.code == 0) {
if (this.docs.length == 0) {
this.docs = res.data
} else {
res.data.forEach(doc => {
this.docs.push(doc)
});
}
}
})
resolve()
}
)
},
},
mounted() {
let height = document.documentElement.clientHeight || document.body.clientHeight;
this.scrollHeight = height - 5 - 30 - 40 - 10
this.getDocs(100, -1);
console.info(this.docs)
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>

9
frontend/src/apis/api.js Normal file
View File

@ -0,0 +1,9 @@
import axios from "axios";
const prefix = "/api/v1";
export const GetDocs = (params)=>{
return axios.get(`${prefix}/docs`, {params:params}).then(res=>res.data)
}
export const UploadDoc = (params)=>{
return axios.get(`${prefix}/docs`, {params:params}).then(res=>res.data)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,58 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

6
frontend/src/main.js Normal file
View File

@ -0,0 +1,6 @@
import { createApp } from 'vue'
import App from './App.vue'
import 'view-ui-plus/dist/styles/viewuiplus.css'
import ViewUIPlus from 'view-ui-plus'
const app = createApp(App)
app.use(ViewUIPlus).mount('#app')

13
frontend/vue.config.js Normal file
View File

@ -0,0 +1,13 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
lintOnSave: false,
devServer: {
proxy: {
'/api': {
target: `http://${process.env.API_HOST || '127.0.0.1:8081'}`,
changeOrigin: true
},
}
}
})

33
go.mod Normal file
View File

@ -0,0 +1,33 @@
module sketchlib.bing89.com
go 1.18
require (
github.com/gin-gonic/gin v1.8.1
gorm.io/driver/mysql v1.3.4
gorm.io/gorm v1.23.6
)
require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/goccy/go-json v0.9.7 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect
golang.org/x/text v0.3.6 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

97
go.sum Normal file
View File

@ -0,0 +1,97 @@
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
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/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 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
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/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
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=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
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.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-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.3.4 h1:/KoBMgsUHC3bExsekDcmNYaBnfH2WNeFuXqqrqMc98Q=
gorm.io/driver/mysql v1.3.4/go.mod h1:s4Tq0KmD0yhPGHbZEwg1VPlH0vT/GBHJZorPzhcxBUE=
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.6 h1:KFLdNgri4ExFFGTRGGFWON2P1ZN28+9SJRN8voOoYe0=
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=