├── .gitignore
├── README.md
├── client
├── README.md
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── todo.png
│ ├── components
│ │ ├── HelloWorld.vue
│ │ └── Todo.vue
│ ├── main.js
│ ├── router.js
│ └── views
│ │ ├── About.vue
│ │ ├── Health.vue
│ │ └── Home.vue
├── tests
│ └── unit
│ │ ├── .eslintrc.js
│ │ └── example.spec.js
└── vue.config.js
├── server
├── config.yaml
├── config
│ └── config.go
├── docs
│ ├── docs.go
│ ├── swagger.json
│ └── swagger.yaml
├── errno
│ ├── code.go
│ └── errno.go
├── go.mod
├── go.sum
├── handler
│ ├── handler.go
│ ├── health.go
│ └── todo.go
├── main.go
├── middleware
│ └── middleware.go
├── model
│ ├── init.go
│ └── todo.go
├── router
│ └── router.go
└── token
│ └── token.go
└── todogif.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
23 | package-lock.json
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # todos-go
2 | A todos restful-api service base on go language, use gin+gorm+vue.
3 |
4 | This todos servie is for learning go web development, Use a front-end and back-end separation approach.
5 | front-end use Vue and back-end use Gin. just for fun, for learn.
6 |
7 | ## show
8 | 
9 |
10 | ## how to run
11 | ```
12 | > git clone https://github.com/shiniao/mini_todo.git
13 |
14 | # mysql create database:
15 | > create database todo;
16 |
17 | # Then change the file config.yaml for your needs.
18 |
19 | # run server
20 | > cd server
21 | > go run main.go
22 |
23 | # run client
24 | cd client
25 | npm install
26 | npm run serve
27 | ```
28 |
29 | ## todos api
30 |
31 | You can visit `.../swagger/index.html` for more detail.
32 |
33 | First, use todos api you need to get a token, Then take tokens with each visit:
34 |
35 | ```shell script
36 | POST /token get a token, need take `key:matata`
37 | ```
38 |
39 | That's all api:
40 | ```
41 | GET /v1/todos
42 | GET /v1/todos/:id
43 | POST /v1/todos
44 | PUT /v1/todos/:id
45 | DELETE /v1/todos/:id
46 | ```
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # client
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Run your tests
19 | ```
20 | npm run test
21 | ```
22 |
23 | ### Lints and fixes files
24 | ```
25 | npm run lint
26 | ```
27 |
28 | ### Run your unit tests
29 | ```
30 | npm run test:unit
31 | ```
32 |
33 | ### Customize configuration
34 | See [Configuration Reference](https://cli.vuejs.org/config/).
35 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint",
9 | "test:unit": "vue-cli-service test:unit"
10 | },
11 | "dependencies": {
12 | "ant-design-vue": "^1.3.17",
13 | "axios": "^0.19.0",
14 | "vue": "^2.6.10",
15 | "vue-notification": "^1.3.16",
16 | "vue-router": "^3.0.3"
17 | },
18 | "devDependencies": {
19 | "@vue/cli-plugin-eslint": "^3.11.0",
20 | "@vue/cli-plugin-unit-mocha": "^3.11.0",
21 | "@vue/cli-service": "^3.11.0",
22 | "@vue/test-utils": "1.0.0-beta.29",
23 | "babel-eslint": "^10.0.1",
24 | "chai": "^4.1.2",
25 | "eslint": "^5.16.0",
26 | "eslint-plugin-vue": "^5.0.0",
27 | "less": "^3.0.4",
28 | "less-loader": "^5.0.0",
29 | "vue-template-compiler": "^2.6.10"
30 | },
31 | "eslintConfig": {
32 | "root": true,
33 | "env": {
34 | "node": true
35 | },
36 | "extends": [
37 | "plugin:vue/essential",
38 | "eslint:recommended"
39 | ],
40 | "rules": {},
41 | "parserOptions": {
42 | "parser": "babel-eslint"
43 | },
44 | "overrides": [
45 | {
46 | "files": [
47 | "**/__tests__/*.{j,t}s?(x)"
48 | ],
49 | "env": {
50 | "mocha": true
51 | }
52 | }
53 | ]
54 | },
55 | "postcss": {
56 | "plugins": {
57 | "autoprefixer": {}
58 | }
59 | },
60 | "browserslist": [
61 | "> 1%",
62 | "last 2 versions"
63 | ]
64 | }
65 |
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ischaojie/gtodo/6f4b39ae6aea83158943b22ad2119d9205465354/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | client
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/client/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 | Home
10 | |
11 | Health
12 | |
13 | About
14 |
15 |
16 |
17 |
18 |
19 |
20 |
63 |
64 |
--------------------------------------------------------------------------------
/client/src/assets/todo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ischaojie/gtodo/6f4b39ae6aea83158943b22ad2119d9205465354/client/src/assets/todo.png
--------------------------------------------------------------------------------
/client/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
5 | For a guide and recipes on how to configure / customize this project,
6 | check out the
7 | vue-cli documentation.
8 |
9 |
Installed CLI Plugins
10 |
14 |
Essential Links
15 |
22 |
Ecosystem
23 |
30 |
31 |
32 |
33 |
41 |
42 |
43 |
59 |
--------------------------------------------------------------------------------
/client/src/components/Todo.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
51 |
52 |
--------------------------------------------------------------------------------
/client/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import router from './router'
4 | import Antd from 'ant-design-vue'
5 | import 'ant-design-vue/dist/antd.css'
6 | import axios from 'axios'
7 | import Notifications from 'vue-notification'
8 |
9 | Vue.config.productionTip = false
10 |
11 | Vue.use(Antd)
12 | Vue.use(Notifications)
13 |
14 | axios.post('/api/token', JSON.stringify({
15 | key: 'matata'
16 | })).then(res => {
17 | axios.defaults.headers.common['Authorization'] = "Bearer "+res.data.data.token;
18 | console.info("token", res.data.data.token)
19 |
20 | new Vue({
21 | router,
22 | render: function (h) {
23 | return h(App)
24 | }
25 | }).$mount('#app')
26 | }).catch((error) => {
27 | console.error(error)
28 | });
29 |
30 |
--------------------------------------------------------------------------------
/client/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import Home from './views/Home.vue'
4 | import Health from "./views/Health";
5 |
6 | Vue.use(Router)
7 |
8 | export default new Router({
9 | mode: 'history',
10 | base: process.env.BASE_URL,
11 | routes: [
12 | {
13 | path: '/',
14 | name: 'home',
15 | component: Home
16 | },
17 | {
18 | path: '/health',
19 | name: 'health',
20 | component: Health
21 | },
22 | {
23 | path: '/about',
24 | name: 'about',
25 | // route level code-splitting
26 | // this generates a separate chunk (about.[hash].js) for this route
27 | // which is lazy-loaded when the route is visited.
28 | component: function () {
29 | return import(/* webpackChunkName: "about" */ './views/About.vue')
30 | }
31 | }
32 | ]
33 | })
34 |
--------------------------------------------------------------------------------
/client/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
a todo restful-api service base on go language.
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/client/src/views/Health.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
cpu使用量:
4 |
status: {{cpu.status}}, info: {{cpu.info}}
5 |
6 |
ram 使用量:
7 |
status: {{ram.status}}, info: {{ram.info}}
8 |
9 |
disk 使用量:
10 |
status: {{disk.status}}, info: {{disk.info}}
11 |
12 |
13 |
14 |
51 |
52 |
--------------------------------------------------------------------------------
/client/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
今日土豆
6 |
明日复明日,明日何其多。——《明日歌》
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
84 |
85 |
89 |
--------------------------------------------------------------------------------
/client/tests/unit/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | mocha: true
4 | }
5 | }
--------------------------------------------------------------------------------
/client/tests/unit/example.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai'
2 | import { shallowMount } from '@vue/test-utils'
3 | import HelloWorld from '@/components/HelloWorld.vue'
4 |
5 | describe('HelloWorld.vue', () => {
6 | it('renders props.msg when passed', () => {
7 | const msg = 'new message'
8 | const wrapper = shallowMount(HelloWorld, {
9 | propsData: { msg }
10 | })
11 | expect(wrapper.text()).to.include(msg)
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/client/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | devServer: {
3 | proxy: {
4 | '/api': {
5 | target: 'http://localhost:8081/', //对应自己的接口
6 | changeOrigin: true,
7 | ws: true,
8 | pathRewrite: {
9 | '^/api': ''
10 | }
11 | }
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/server/config.yaml:
--------------------------------------------------------------------------------
1 | runmode: debug # 开发模式, debug, release, test
2 | addr: :8081 # HTTP绑定端口
3 | name: apiserver # API Server的名字
4 | url: http://127.0.0.1:8081 # pingServer函数请求的API服务器的ip:port
5 | max_ping_count: 10 # pingServer函数try的次数
6 | key: matata
7 | jwt_secret: Rtg8BPKNEf2mB4mgvKONGPZZQSaJWNLijxR42qRgq0iBb5
8 | db:
9 | dbname: todo
10 | addr: 127.0.0.1:3306
11 | username: root
12 | password: zhezhezhu
--------------------------------------------------------------------------------
/server/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/fsnotify/fsnotify"
5 | "github.com/spf13/viper"
6 | "log"
7 | "strings"
8 | )
9 |
10 | type Config struct {
11 | Name string
12 | }
13 |
14 | // * 初始化配置文件
15 | func Init(cfg string) error {
16 | c := Config{Name:cfg}
17 | if err := c.initConfig(); err != nil {
18 | return err
19 | }
20 | // * 热更新
21 | c.WatchConfig()
22 | return nil
23 | }
24 |
25 | func (c *Config) initConfig() error {
26 | // * 指定配置文件
27 | if c.Name != "" {
28 | // * 设置配置文件名
29 | viper.SetConfigFile(c.Name)
30 | }else {
31 | // * 未指定配置文件,读取默认路径
32 | viper.AddConfigPath(".")
33 | viper.SetConfigName("config")
34 | }
35 | // * 设置config文件类型
36 | viper.SetConfigType("yaml")
37 |
38 | // * 读取环境变量
39 | viper.AutomaticEnv()
40 | // * 设置环境变量前缀
41 | viper.SetEnvPrefix("api")
42 | replacer := strings.NewReplacer(",", "_")
43 | viper.SetEnvKeyReplacer(replacer)
44 |
45 | // * 读取配置文件内容,使用viper.get获取配置
46 | if err:=viper.ReadInConfig(); err != nil {
47 | return err
48 | }
49 | return nil
50 | }
51 |
52 | // * 监控配置文件变化并热加载
53 | func (c *Config) WatchConfig() {
54 | viper.WatchConfig()
55 | viper.OnConfigChange(func(e fsnotify.Event) {
56 | log.Printf("Config file changed: %s", e.Name)
57 | })
58 | }
59 |
60 |
61 |
--------------------------------------------------------------------------------
/server/docs/docs.go:
--------------------------------------------------------------------------------
1 | // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
2 | // This file was generated by swaggo/swag at
3 | // 2019-10-15 10:46:00.642260008 +0800 CST m=+0.029875648
4 |
5 | package docs
6 |
7 | import (
8 | "bytes"
9 | "encoding/json"
10 | "strings"
11 |
12 | "github.com/alecthomas/template"
13 | "github.com/swaggo/swag"
14 | )
15 |
16 | var doc = `{
17 | "schemes": {{ marshal .Schemes }},
18 | "swagger": "2.0",
19 | "info": {
20 | "description": "{{.Description}}",
21 | "title": "{{.Title}}",
22 | "termsOfService": "http://me.shiniao.fun/",
23 | "contact": {
24 | "name": "shiniao",
25 | "url": "http://me.shiniao.fun/",
26 | "email": "zhuzhezhe5@gmail.com"
27 | },
28 | "license": {
29 | "name": "Apache 2.0",
30 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
31 | },
32 | "version": "{{.Version}}"
33 | },
34 | "host": "{{.Host}}",
35 | "basePath": "{{.BasePath}}",
36 | "paths": {
37 | "/sd/cpu": {
38 | "get": {
39 | "description": "Checks the cpu usage",
40 | "consumes": [
41 | "application/json"
42 | ],
43 | "produces": [
44 | "application/json"
45 | ],
46 | "tags": [
47 | "sd"
48 | ],
49 | "summary": "Checks the cpu usage",
50 | "responses": {
51 | "200": {
52 | "description": "{\"status\":\"OK\", \"info\":\"Load average: 1.82, 1.05, 0.85 | Cores: 2\"}",
53 | "schema": {
54 | "$ref": "#/definitions/handler.message"
55 | }
56 | }
57 | }
58 | }
59 | },
60 | "/sd/disk": {
61 | "get": {
62 | "description": "Checks the disk usage",
63 | "consumes": [
64 | "application/json"
65 | ],
66 | "produces": [
67 | "application/json"
68 | ],
69 | "tags": [
70 | "sd"
71 | ],
72 | "summary": "Checks the disk usage",
73 | "responses": {
74 | "200": {
75 | "description": "{\"status\":\"OK\", \"info\":\"Free space: 44429MB (43GB) / 119674MB (116GB) | Used: 39%\"}",
76 | "schema": {
77 | "$ref": "#/definitions/handler.message"
78 | }
79 | }
80 | }
81 | }
82 | },
83 | "/sd/health": {
84 | "get": {
85 | "description": "Shows OK as the ping-pong result",
86 | "consumes": [
87 | "application/json"
88 | ],
89 | "produces": [
90 | "application/json"
91 | ],
92 | "tags": [
93 | "sd"
94 | ],
95 | "summary": "Shows OK as the ping-pong result",
96 | "responses": {
97 | "200": {
98 | "description": "{\"status\":\"OK\", \"info\":\"\"}",
99 | "schema": {
100 | "$ref": "#/definitions/handler.message"
101 | }
102 | }
103 | }
104 | }
105 | },
106 | "/sd/ram": {
107 | "get": {
108 | "description": "Checks the ram usage",
109 | "consumes": [
110 | "application/json"
111 | ],
112 | "produces": [
113 | "application/json"
114 | ],
115 | "tags": [
116 | "sd"
117 | ],
118 | "summary": "Checks the ram usage",
119 | "responses": {
120 | "200": {
121 | "description": "{\"status\":\"OK\", \"info\":\"Free space: 5293MB (5GB) / 7665MB (7GB) | Used: 69%\"}",
122 | "schema": {
123 | "$ref": "#/definitions/handler.message"
124 | }
125 | }
126 | }
127 | }
128 | },
129 | "/v1/todos": {
130 | "get": {
131 | "description": "Get all todos from database",
132 | "consumes": [
133 | "application/json"
134 | ],
135 | "produces": [
136 | "application/json"
137 | ],
138 | "tags": [
139 | "todo"
140 | ],
141 | "summary": "Get all todos",
142 | "responses": {
143 | "200": {
144 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":{\"total\":233, \"todos\":[{\"ID\":91,\"title\": \"烫头\", \"completed\": 1,\"CreatedAt\": \"2019-10-12T10:10:05+08:00\",\"UpdatedAt\": \"2019-10-12T10:16:24+08:00\",\"DeletedAt\": null}]}}",
145 | "schema": {
146 | "$ref": "#/definitions/model.TodoModel"
147 | }
148 | }
149 | }
150 | }
151 | },
152 | "/v1/todos/": {
153 | "post": {
154 | "description": "Add a new todo",
155 | "consumes": [
156 | "application/json"
157 | ],
158 | "produces": [
159 | "application/json"
160 | ],
161 | "tags": [
162 | "todo"
163 | ],
164 | "summary": "Add new todos to the database",
165 | "parameters": [
166 | {
167 | "description": "The todo info",
168 | "name": "todo",
169 | "in": "body",
170 | "required": true,
171 | "schema": {
172 | "type": "object",
173 | "$ref": "#/definitions/model.TodoModel"
174 | }
175 | }
176 | ],
177 | "responses": {
178 | "200": {
179 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":\"create successful.\"}",
180 | "schema": {
181 | "$ref": "#/definitions/handler.Response"
182 | }
183 | }
184 | }
185 | }
186 | },
187 | "/v1/todos/{id}": {
188 | "get": {
189 | "description": "Get a todo",
190 | "consumes": [
191 | "application/json"
192 | ],
193 | "produces": [
194 | "application/json"
195 | ],
196 | "tags": [
197 | "todo"
198 | ],
199 | "summary": "Get a todo",
200 | "responses": {
201 | "200": {
202 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":{\"ID\":91,\"title\": \"烫头\", \"completed\": 1,\"CreatedAt\": \"2019-10-12T10:10:05+08:00\",\"UpdatedAt\": \"2019-10-12T10:16:24+08:00\",\"DeletedAt\": null}}",
203 | "schema": {
204 | "$ref": "#/definitions/model.TodoModel"
205 | }
206 | }
207 | }
208 | },
209 | "put": {
210 | "description": "Update a todo",
211 | "consumes": [
212 | "application/json"
213 | ],
214 | "produces": [
215 | "application/json"
216 | ],
217 | "tags": [
218 | "todo"
219 | ],
220 | "summary": "Update a todo",
221 | "parameters": [
222 | {
223 | "type": "integer",
224 | "description": "The todo's database id index num",
225 | "name": "id",
226 | "in": "path",
227 | "required": true
228 | },
229 | {
230 | "description": "The todo info",
231 | "name": "todo",
232 | "in": "body",
233 | "required": true,
234 | "schema": {
235 | "type": "object",
236 | "$ref": "#/definitions/model.TodoModel"
237 | }
238 | }
239 | ],
240 | "responses": {
241 | "200": {
242 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":\"update successful.\"}",
243 | "schema": {
244 | "$ref": "#/definitions/handler.Response"
245 | }
246 | }
247 | }
248 | },
249 | "delete": {
250 | "description": "Delete a todo",
251 | "consumes": [
252 | "application/json"
253 | ],
254 | "produces": [
255 | "application/json"
256 | ],
257 | "tags": [
258 | "todo"
259 | ],
260 | "summary": "Delete a todo",
261 | "responses": {
262 | "200": {
263 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":\"delete successful.\"}",
264 | "schema": {
265 | "$ref": "#/definitions/handler.Response"
266 | }
267 | }
268 | }
269 | }
270 | }
271 | },
272 | "definitions": {
273 | "handler.Response": {
274 | "type": "object",
275 | "properties": {
276 | "code": {
277 | "type": "integer"
278 | },
279 | "data": {
280 | "type": "object"
281 | },
282 | "message": {
283 | "type": "string"
284 | }
285 | }
286 | },
287 | "handler.message": {
288 | "type": "object",
289 | "properties": {
290 | "info": {
291 | "type": "string"
292 | },
293 | "status": {
294 | "type": "string"
295 | }
296 | }
297 | },
298 | "model.TodoModel": {
299 | "type": "object",
300 | "properties": {
301 | "completed": {
302 | "type": "integer"
303 | },
304 | "title": {
305 | "type": "string"
306 | }
307 | }
308 | }
309 | }
310 | }`
311 |
312 | type swaggerInfo struct {
313 | Version string
314 | Host string
315 | BasePath string
316 | Schemes []string
317 | Title string
318 | Description string
319 | }
320 |
321 | // SwaggerInfo holds exported Swagger Info so clients can modify it
322 | var SwaggerInfo = swaggerInfo{
323 | Version: "1.0",
324 | Host: "todo.shiniao.fun",
325 | BasePath: "/v1",
326 | Schemes: []string{},
327 | Title: "A todos application API",
328 | Description: "This is a todos application server.",
329 | }
330 |
331 | type s struct{}
332 |
333 | func (s *s) ReadDoc() string {
334 | sInfo := SwaggerInfo
335 | sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
336 |
337 | t, err := template.New("swagger_info").Funcs(template.FuncMap{
338 | "marshal": func(v interface{}) string {
339 | a, _ := json.Marshal(v)
340 | return string(a)
341 | },
342 | }).Parse(doc)
343 | if err != nil {
344 | return doc
345 | }
346 |
347 | var tpl bytes.Buffer
348 | if err := t.Execute(&tpl, sInfo); err != nil {
349 | return doc
350 | }
351 |
352 | return tpl.String()
353 | }
354 |
355 | func init() {
356 | swag.Register(swag.Name, &s{})
357 | }
358 |
--------------------------------------------------------------------------------
/server/docs/swagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "swagger": "2.0",
3 | "info": {
4 | "description": "This is a todos application server.",
5 | "title": "A todos application API",
6 | "termsOfService": "http://me.shiniao.fun/",
7 | "contact": {
8 | "name": "shiniao",
9 | "url": "http://me.shiniao.fun/",
10 | "email": "zhuzhezhe5@gmail.com"
11 | },
12 | "license": {
13 | "name": "Apache 2.0",
14 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
15 | },
16 | "version": "1.0"
17 | },
18 | "host": "todo.shiniao.fun",
19 | "basePath": "/v1",
20 | "paths": {
21 | "/sd/cpu": {
22 | "get": {
23 | "description": "Checks the cpu usage",
24 | "consumes": [
25 | "application/json"
26 | ],
27 | "produces": [
28 | "application/json"
29 | ],
30 | "tags": [
31 | "sd"
32 | ],
33 | "summary": "Checks the cpu usage",
34 | "responses": {
35 | "200": {
36 | "description": "{\"status\":\"OK\", \"info\":\"Load average: 1.82, 1.05, 0.85 | Cores: 2\"}",
37 | "schema": {
38 | "$ref": "#/definitions/handler.message"
39 | }
40 | }
41 | }
42 | }
43 | },
44 | "/sd/disk": {
45 | "get": {
46 | "description": "Checks the disk usage",
47 | "consumes": [
48 | "application/json"
49 | ],
50 | "produces": [
51 | "application/json"
52 | ],
53 | "tags": [
54 | "sd"
55 | ],
56 | "summary": "Checks the disk usage",
57 | "responses": {
58 | "200": {
59 | "description": "{\"status\":\"OK\", \"info\":\"Free space: 44429MB (43GB) / 119674MB (116GB) | Used: 39%\"}",
60 | "schema": {
61 | "$ref": "#/definitions/handler.message"
62 | }
63 | }
64 | }
65 | }
66 | },
67 | "/sd/health": {
68 | "get": {
69 | "description": "Shows OK as the ping-pong result",
70 | "consumes": [
71 | "application/json"
72 | ],
73 | "produces": [
74 | "application/json"
75 | ],
76 | "tags": [
77 | "sd"
78 | ],
79 | "summary": "Shows OK as the ping-pong result",
80 | "responses": {
81 | "200": {
82 | "description": "{\"status\":\"OK\", \"info\":\"\"}",
83 | "schema": {
84 | "$ref": "#/definitions/handler.message"
85 | }
86 | }
87 | }
88 | }
89 | },
90 | "/sd/ram": {
91 | "get": {
92 | "description": "Checks the ram usage",
93 | "consumes": [
94 | "application/json"
95 | ],
96 | "produces": [
97 | "application/json"
98 | ],
99 | "tags": [
100 | "sd"
101 | ],
102 | "summary": "Checks the ram usage",
103 | "responses": {
104 | "200": {
105 | "description": "{\"status\":\"OK\", \"info\":\"Free space: 5293MB (5GB) / 7665MB (7GB) | Used: 69%\"}",
106 | "schema": {
107 | "$ref": "#/definitions/handler.message"
108 | }
109 | }
110 | }
111 | }
112 | },
113 | "/v1/todos": {
114 | "get": {
115 | "description": "Get all todos from database",
116 | "consumes": [
117 | "application/json"
118 | ],
119 | "produces": [
120 | "application/json"
121 | ],
122 | "tags": [
123 | "todo"
124 | ],
125 | "summary": "Get all todos",
126 | "responses": {
127 | "200": {
128 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":{\"total\":233, \"todos\":[{\"ID\":91,\"title\": \"烫头\", \"completed\": 1,\"CreatedAt\": \"2019-10-12T10:10:05+08:00\",\"UpdatedAt\": \"2019-10-12T10:16:24+08:00\",\"DeletedAt\": null}]}}",
129 | "schema": {
130 | "$ref": "#/definitions/model.TodoModel"
131 | }
132 | }
133 | }
134 | }
135 | },
136 | "/v1/todos/": {
137 | "post": {
138 | "description": "Add a new todo",
139 | "consumes": [
140 | "application/json"
141 | ],
142 | "produces": [
143 | "application/json"
144 | ],
145 | "tags": [
146 | "todo"
147 | ],
148 | "summary": "Add new todos to the database",
149 | "parameters": [
150 | {
151 | "description": "The todo info",
152 | "name": "todo",
153 | "in": "body",
154 | "required": true,
155 | "schema": {
156 | "type": "object",
157 | "$ref": "#/definitions/model.TodoModel"
158 | }
159 | }
160 | ],
161 | "responses": {
162 | "200": {
163 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":\"create successful.\"}",
164 | "schema": {
165 | "$ref": "#/definitions/handler.Response"
166 | }
167 | }
168 | }
169 | }
170 | },
171 | "/v1/todos/{id}": {
172 | "get": {
173 | "description": "Get a todo",
174 | "consumes": [
175 | "application/json"
176 | ],
177 | "produces": [
178 | "application/json"
179 | ],
180 | "tags": [
181 | "todo"
182 | ],
183 | "summary": "Get a todo",
184 | "responses": {
185 | "200": {
186 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":{\"ID\":91,\"title\": \"烫头\", \"completed\": 1,\"CreatedAt\": \"2019-10-12T10:10:05+08:00\",\"UpdatedAt\": \"2019-10-12T10:16:24+08:00\",\"DeletedAt\": null}}",
187 | "schema": {
188 | "$ref": "#/definitions/model.TodoModel"
189 | }
190 | }
191 | }
192 | },
193 | "put": {
194 | "description": "Update a todo",
195 | "consumes": [
196 | "application/json"
197 | ],
198 | "produces": [
199 | "application/json"
200 | ],
201 | "tags": [
202 | "todo"
203 | ],
204 | "summary": "Update a todo",
205 | "parameters": [
206 | {
207 | "type": "integer",
208 | "description": "The todo's database id index num",
209 | "name": "id",
210 | "in": "path",
211 | "required": true
212 | },
213 | {
214 | "description": "The todo info",
215 | "name": "todo",
216 | "in": "body",
217 | "required": true,
218 | "schema": {
219 | "type": "object",
220 | "$ref": "#/definitions/model.TodoModel"
221 | }
222 | }
223 | ],
224 | "responses": {
225 | "200": {
226 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":\"update successful.\"}",
227 | "schema": {
228 | "$ref": "#/definitions/handler.Response"
229 | }
230 | }
231 | }
232 | },
233 | "delete": {
234 | "description": "Delete a todo",
235 | "consumes": [
236 | "application/json"
237 | ],
238 | "produces": [
239 | "application/json"
240 | ],
241 | "tags": [
242 | "todo"
243 | ],
244 | "summary": "Delete a todo",
245 | "responses": {
246 | "200": {
247 | "description": "{\"code\":0,\"message\":\"OK\",\"data\":\"delete successful.\"}",
248 | "schema": {
249 | "$ref": "#/definitions/handler.Response"
250 | }
251 | }
252 | }
253 | }
254 | }
255 | },
256 | "definitions": {
257 | "handler.Response": {
258 | "type": "object",
259 | "properties": {
260 | "code": {
261 | "type": "integer"
262 | },
263 | "data": {
264 | "type": "object"
265 | },
266 | "message": {
267 | "type": "string"
268 | }
269 | }
270 | },
271 | "handler.message": {
272 | "type": "object",
273 | "properties": {
274 | "info": {
275 | "type": "string"
276 | },
277 | "status": {
278 | "type": "string"
279 | }
280 | }
281 | },
282 | "model.TodoModel": {
283 | "type": "object",
284 | "properties": {
285 | "completed": {
286 | "type": "integer"
287 | },
288 | "title": {
289 | "type": "string"
290 | }
291 | }
292 | }
293 | }
294 | }
--------------------------------------------------------------------------------
/server/docs/swagger.yaml:
--------------------------------------------------------------------------------
1 | basePath: /v1
2 | definitions:
3 | handler.Response:
4 | properties:
5 | code:
6 | type: integer
7 | data:
8 | type: object
9 | message:
10 | type: string
11 | type: object
12 | handler.message:
13 | properties:
14 | info:
15 | type: string
16 | status:
17 | type: string
18 | type: object
19 | model.TodoModel:
20 | properties:
21 | completed:
22 | type: integer
23 | title:
24 | type: string
25 | type: object
26 | host: todo.shiniao.fun
27 | info:
28 | contact:
29 | email: zhuzhezhe5@gmail.com
30 | name: shiniao
31 | url: http://me.shiniao.fun/
32 | description: This is a todos application server.
33 | license:
34 | name: Apache 2.0
35 | url: http://www.apache.org/licenses/LICENSE-2.0.html
36 | termsOfService: http://me.shiniao.fun/
37 | title: A todos application API
38 | version: "1.0"
39 | paths:
40 | /sd/cpu:
41 | get:
42 | consumes:
43 | - application/json
44 | description: Checks the cpu usage
45 | produces:
46 | - application/json
47 | responses:
48 | "200":
49 | description: '{"status":"OK", "info":"Load average: 1.82, 1.05, 0.85 | Cores:
50 | 2"}'
51 | schema:
52 | $ref: '#/definitions/handler.message'
53 | summary: Checks the cpu usage
54 | tags:
55 | - sd
56 | /sd/disk:
57 | get:
58 | consumes:
59 | - application/json
60 | description: Checks the disk usage
61 | produces:
62 | - application/json
63 | responses:
64 | "200":
65 | description: '{"status":"OK", "info":"Free space: 44429MB (43GB) / 119674MB
66 | (116GB) | Used: 39%"}'
67 | schema:
68 | $ref: '#/definitions/handler.message'
69 | summary: Checks the disk usage
70 | tags:
71 | - sd
72 | /sd/health:
73 | get:
74 | consumes:
75 | - application/json
76 | description: Shows OK as the ping-pong result
77 | produces:
78 | - application/json
79 | responses:
80 | "200":
81 | description: '{"status":"OK", "info":""}'
82 | schema:
83 | $ref: '#/definitions/handler.message'
84 | summary: Shows OK as the ping-pong result
85 | tags:
86 | - sd
87 | /sd/ram:
88 | get:
89 | consumes:
90 | - application/json
91 | description: Checks the ram usage
92 | produces:
93 | - application/json
94 | responses:
95 | "200":
96 | description: '{"status":"OK", "info":"Free space: 5293MB (5GB) / 7665MB
97 | (7GB) | Used: 69%"}'
98 | schema:
99 | $ref: '#/definitions/handler.message'
100 | summary: Checks the ram usage
101 | tags:
102 | - sd
103 | /v1/todos:
104 | get:
105 | consumes:
106 | - application/json
107 | description: Get all todos from database
108 | produces:
109 | - application/json
110 | responses:
111 | "200":
112 | description: '{"code":0,"message":"OK","data":{"total":233, "todos":[{"ID":91,"title":
113 | "烫头", "completed": 1,"CreatedAt": "2019-10-12T10:10:05+08:00","UpdatedAt":
114 | "2019-10-12T10:16:24+08:00","DeletedAt": null}]}}'
115 | schema:
116 | $ref: '#/definitions/model.TodoModel'
117 | summary: Get all todos
118 | tags:
119 | - todo
120 | /v1/todos/:
121 | post:
122 | consumes:
123 | - application/json
124 | description: Add a new todo
125 | parameters:
126 | - description: The todo info
127 | in: body
128 | name: todo
129 | required: true
130 | schema:
131 | $ref: '#/definitions/model.TodoModel'
132 | type: object
133 | produces:
134 | - application/json
135 | responses:
136 | "200":
137 | description: '{"code":0,"message":"OK","data":"create successful."}'
138 | schema:
139 | $ref: '#/definitions/handler.Response'
140 | summary: Add new todos to the database
141 | tags:
142 | - todo
143 | /v1/todos/{id}:
144 | delete:
145 | consumes:
146 | - application/json
147 | description: Delete a todo
148 | produces:
149 | - application/json
150 | responses:
151 | "200":
152 | description: '{"code":0,"message":"OK","data":"delete successful."}'
153 | schema:
154 | $ref: '#/definitions/handler.Response'
155 | summary: Delete a todo
156 | tags:
157 | - todo
158 | get:
159 | consumes:
160 | - application/json
161 | description: Get a todo
162 | produces:
163 | - application/json
164 | responses:
165 | "200":
166 | description: '{"code":0,"message":"OK","data":{"ID":91,"title": "烫头", "completed":
167 | 1,"CreatedAt": "2019-10-12T10:10:05+08:00","UpdatedAt": "2019-10-12T10:16:24+08:00","DeletedAt":
168 | null}}'
169 | schema:
170 | $ref: '#/definitions/model.TodoModel'
171 | summary: Get a todo
172 | tags:
173 | - todo
174 | put:
175 | consumes:
176 | - application/json
177 | description: Update a todo
178 | parameters:
179 | - description: The todo's database id index num
180 | in: path
181 | name: id
182 | required: true
183 | type: integer
184 | - description: The todo info
185 | in: body
186 | name: todo
187 | required: true
188 | schema:
189 | $ref: '#/definitions/model.TodoModel'
190 | type: object
191 | produces:
192 | - application/json
193 | responses:
194 | "200":
195 | description: '{"code":0,"message":"OK","data":"update successful."}'
196 | schema:
197 | $ref: '#/definitions/handler.Response'
198 | summary: Update a todo
199 | tags:
200 | - todo
201 | swagger: "2.0"
202 |
--------------------------------------------------------------------------------
/server/errno/code.go:
--------------------------------------------------------------------------------
1 | /*定义错误代码*/
2 |
3 | package errno
4 |
5 |
6 | var (
7 | // Common errors
8 | OK = &Errno{Code: 0, Message: "OK"}
9 | InternalServerError = &Errno{Code: 10001, Message: "Internal server error."}
10 | ErrBind = &Errno{Code: 10002, Message: "Error occurred while binding the request body to the struct."}
11 |
12 | // user errors
13 | ErrUserNotFound = &Errno{Code: 20102, Message: "The user was not found."}
14 |
15 | ErrValidation = &Errno{Code: 20001, Message: "Validation failed."}
16 | ErrDatabase = &Errno{Code: 20002, Message: "Database error."}
17 | ErrToken = &Errno{Code: 20003, Message: "Error occurred while signing the JSON web token."}
18 |
19 | // user errors
20 | ErrEncrypt = &Errno{Code: 20101, Message: "Error occurred while encrypting the user password."}
21 | ErrTokenInvalid = &Errno{Code: 20103, Message: "The token was invalid."}
22 | ErrKeyIncorrect = &Errno{Code: 20104, Message: "The key was incorrect"}
23 | )
--------------------------------------------------------------------------------
/server/errno/errno.go:
--------------------------------------------------------------------------------
1 | package errno
2 |
3 | import "fmt"
4 |
5 | // * 自定义错误代码(code+message)
6 | // * 这是展示给用户的
7 | type Errno struct {
8 | Code int
9 | Message string
10 | }
11 |
12 | // * 实现Error接口,自定义错误类型
13 | /*
14 | type error interface {
15 | Error() string
16 | }
17 | */
18 |
19 | func (err Errno) Error() string {
20 | return err.Message
21 | }
22 |
23 | // * 定义错误
24 | // * 这是展示给后台的
25 | type Err struct {
26 | Code int
27 | Message string
28 | Err error
29 | }
30 |
31 | func (err *Err) Error() string {
32 | return fmt.Sprintf("Err - code: %d, message: %s, error: %s", err.Code, err.Message, err.Err)
33 | }
34 |
35 | // * 新建一个自定义错误(传入code+message, err)
36 | func New(errno *Errno, err error) *Err {
37 | return &Err{
38 | Code: errno.Code,
39 | Message: errno.Message,
40 | Err: err,
41 | }
42 | }
43 |
44 | // * 解析定制的错误
45 | func DecodeErr(err error) (int, string) {
46 | // * 没有错误返回ok
47 | if err == nil {
48 | return OK.Code, OK.Message
49 | }
50 |
51 | switch typed := err.(type) {
52 | case *Err:
53 | return typed.Code, typed.Message
54 | case *Errno:
55 | return typed.Code, typed.Message
56 | default:
57 | }
58 |
59 | return InternalServerError.Code, err.Error()
60 | }
--------------------------------------------------------------------------------
/server/go.mod:
--------------------------------------------------------------------------------
1 | module mini_todo
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
7 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
8 | github.com/dgrijalva/jwt-go v3.2.0+incompatible
9 | github.com/fsnotify/fsnotify v1.4.7
10 | github.com/gin-gonic/gin v1.4.0
11 | github.com/go-ole/go-ole v1.2.4 // indirect
12 | github.com/jinzhu/gorm v1.9.11
13 | github.com/shirou/gopsutil v2.19.9+incompatible
14 | github.com/spf13/pflag v1.0.5
15 | github.com/spf13/viper v1.4.0
16 | github.com/swaggo/gin-swagger v1.2.0
17 | github.com/swaggo/swag v1.6.3
18 | )
19 |
--------------------------------------------------------------------------------
/server/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3 | cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
4 | cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
5 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
6 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
7 | github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
8 | github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
9 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
10 | github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
11 | github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
12 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
13 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
14 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
15 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
16 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
17 | github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
18 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
19 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
20 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
21 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
22 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
23 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
24 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
25 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
26 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
27 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
28 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
29 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
30 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
31 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
32 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
33 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
34 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
35 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
36 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
37 | github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
38 | github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
39 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
40 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
41 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
42 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
43 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
44 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
45 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
46 | github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
47 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
48 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
49 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
50 | github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=
51 | github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
52 | github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
53 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
54 | github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
55 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
56 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
57 | github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
58 | github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
59 | github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
60 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
61 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
62 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
63 | github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
64 | github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
65 | github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
66 | github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
67 | github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
68 | github.com/go-openapi/jsonreference v0.19.0 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=
69 | github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
70 | github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
71 | github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
72 | github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
73 | github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
74 | github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
75 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
76 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
77 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
78 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
79 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
80 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
81 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
82 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
83 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
84 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
85 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
86 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
87 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
88 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
89 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
90 | github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
91 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
92 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
93 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
94 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
95 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
96 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
97 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
98 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
99 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
100 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
101 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
102 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
103 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
104 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
105 | github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE=
106 | github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw=
107 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
108 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
109 | github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
110 | github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
111 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
112 | github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
113 | github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
114 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
115 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
116 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
117 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
118 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
119 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
120 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
121 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
122 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
123 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
124 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
125 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
126 | github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
127 | github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
128 | github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
129 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
130 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
131 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
132 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
133 | github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
134 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
135 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
136 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
137 | github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
138 | github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
139 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
140 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
141 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
142 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
143 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
144 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
145 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
146 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
147 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
148 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
149 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
150 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
151 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
152 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
153 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
154 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
155 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
156 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
157 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
158 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
159 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
160 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
161 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
162 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
163 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
164 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
165 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
166 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
167 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
168 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
169 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
170 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
171 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
172 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
173 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
174 | github.com/shirou/gopsutil v2.19.9+incompatible h1:IrPVlK4nfwW10DF7pW+7YJKws9NkgNzWozwwWv9FsgY=
175 | github.com/shirou/gopsutil v2.19.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
176 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
177 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
178 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
179 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
180 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
181 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
182 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
183 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
184 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
185 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
186 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
187 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
188 | github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
189 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
190 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
191 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
192 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
193 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
194 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
195 | github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
196 | github.com/swaggo/gin-swagger v1.2.0 h1:YskZXEiv51fjOMTsXrOetAjrMDfFaXD79PEoQBOe2W0=
197 | github.com/swaggo/gin-swagger v1.2.0/go.mod h1:qlH2+W7zXGZkczuL+r2nEBR2JTT+/lX05Nn6vPhc7OI=
198 | github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
199 | github.com/swaggo/swag v1.6.3 h1:N+uVPGP4H2hXoss2pt5dctoSUPKKRInr6qcTMOm0usI=
200 | github.com/swaggo/swag v1.6.3/go.mod h1:wcc83tB4Mb2aNiL/HP4MFeQdpHUrca+Rp/DRNgWAUio=
201 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
202 | github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
203 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
204 | github.com/ugorji/go v1.1.5-pre h1:jyJKFOSEbdOc2HODrf2qcCkYOdq7zzXqA9bhW5oV4fM=
205 | github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0=
206 | github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
207 | github.com/ugorji/go/codec v1.1.5-pre h1:5YV9PsFAN+ndcCtTM7s60no7nY7eTG3LPtxhSwuxzCs=
208 | github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI=
209 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
210 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
211 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
212 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
213 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
214 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
215 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
216 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
217 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
218 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
219 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
220 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
221 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
222 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
223 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
224 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
225 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
226 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
227 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
228 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
229 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
230 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
231 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
232 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
233 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
234 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
235 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
236 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
237 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
238 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
239 | golang.org/x/net v0.0.0-20190611141213-3f473d35a33a h1:+KkCgOMgnKSgenxTBoiwkMqTiouMIy/3o8RLdmSbGoY=
240 | golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
241 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
242 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
243 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
244 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
245 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
246 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
247 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
248 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
249 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
250 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
251 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
252 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
253 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
254 | golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
255 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
256 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
257 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
258 | golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae h1:xiXzMMEQdQcric9hXtr1QU98MHunKK7OTtsoU6bYWs4=
259 | golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
260 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
261 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
262 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
263 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
264 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
265 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
266 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
267 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
268 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
269 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
270 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
271 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
272 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
273 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
274 | golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
275 | golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b h1:/mJ+GKieZA6hFDQGdWZrjj4AXPl5ylY+5HusG80roy0=
276 | golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
277 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
278 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
279 | google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
280 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
281 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
282 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
283 | google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
284 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
285 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
286 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
287 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
288 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
289 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
290 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
291 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
292 | gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
293 | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
294 | gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
295 | gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
296 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
297 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
298 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
299 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
300 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
301 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
302 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
303 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
304 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
305 |
--------------------------------------------------------------------------------
/server/handler/handler.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/spf13/viper"
6 | "mini_todo/errno"
7 | "mini_todo/token"
8 | "net/http"
9 | )
10 |
11 | /*统一api返回内容*/
12 |
13 | type Response struct {
14 | Code int `json:"code"`
15 | Message string `json:"message"`
16 | Data interface{} `json:"data"`
17 | }
18 |
19 | // * 统一返回给client的内容
20 | func SendResponse(c *gin.Context, err error, data interface{}) {
21 | code, message := errno.DecodeErr(err)
22 |
23 | c.JSON(http.StatusOK, Response{
24 | Code: code,
25 | Message: message,
26 | Data: data,
27 | })
28 | }
29 |
30 | type Key struct {
31 | Key string `json:"key"`
32 | }
33 |
34 | func Token(c *gin.Context) {
35 | var key Key
36 |
37 | c.BindJSON(&key)
38 | //key := c.PostForm("key")
39 | // * 判断key是否正确
40 | if key.Key != viper.GetString("key") {
41 | SendResponse(c, errno.ErrKeyIncorrect, nil)
42 | return
43 | }
44 | // * Sign the json web token.
45 | t, err := token.Sign(c,key.Key, "")
46 | if err != nil {
47 | SendResponse(c, errno.ErrToken, nil)
48 | return
49 | }
50 |
51 | SendResponse(c, nil, token.Token{Token: t})
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/server/handler/health.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | "github.com/shirou/gopsutil/cpu"
7 | "github.com/shirou/gopsutil/disk"
8 | "github.com/shirou/gopsutil/load"
9 | "github.com/shirou/gopsutil/mem"
10 | "net/http"
11 | )
12 |
13 | const (
14 | B = 1
15 | KB = 1024 * B
16 | MB = 1024 * KB
17 | GB = 1024 * MB
18 | )
19 |
20 | type message struct {
21 | Status string `json:"status"`
22 | Info string `json:"info, omitempty"`
23 | }
24 |
25 | // @Summary Shows OK as the ping-pong result
26 | // @Description Shows OK as the ping-pong result
27 | // @Tags sd
28 | // @Accept json
29 | // @Produce json
30 | // @Success 200 {object} message "{"status":"OK", "info":""}"
31 | // @Router /sd/health [get]
32 | func HealthCheck(c *gin.Context) {
33 | mess := "ok"
34 | c.JSON(http.StatusOK, message{
35 | Status: mess,
36 | })
37 | }
38 |
39 | // * 检测硬盘使用量
40 | // @Summary Checks the disk usage
41 | // @Description Checks the disk usage
42 | // @Tags sd
43 | // @Accept json
44 | // @Produce json
45 | // @Success 200 {object} message "{"status":"OK", "info":"Free space: 44429MB (43GB) / 119674MB (116GB) | Used: 39%"}"
46 | // @Router /sd/disk [get]
47 | func DiskCheck(c *gin.Context) {
48 | u, _ := disk.Usage("/")
49 |
50 | usedMB := int(u.Used) / MB
51 | usedGB := int(u.Used) / GB
52 | totalMB := int(u.Total) / MB
53 | totalGB := int(u.Total) / GB
54 | usedPercent := int(u.UsedPercent)
55 |
56 | status := http.StatusOK
57 | text := "OK"
58 |
59 | if usedPercent >= 95 {
60 | status = http.StatusOK
61 | text = "CRITICAL"
62 | } else if usedPercent >= 90 {
63 | status = http.StatusTooManyRequests
64 | text = "WARNING"
65 | }
66 |
67 | mess := fmt.Sprintf("Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", usedMB, usedGB, totalMB, totalGB, usedPercent)
68 | c.JSON(status, message{
69 | Status: text,
70 | Info: mess,
71 | })
72 |
73 | }
74 |
75 | // * 检测cpu使用量
76 | // @Summary Checks the cpu usage
77 | // @Description Checks the cpu usage
78 | // @Tags sd
79 | // @Accept json
80 | // @Produce json
81 | // @Success 200 {object} message "{"status":"OK", "info":"Load average: 1.82, 1.05, 0.85 | Cores: 2"}"
82 | // @Router /sd/cpu [get]
83 | func CPUCheck(c *gin.Context) {
84 | cores, _ := cpu.Counts(false)
85 |
86 | a, _ := load.Avg()
87 | l1 := a.Load1
88 | l5 := a.Load5
89 | l15 := a.Load15
90 |
91 | status := http.StatusOK
92 | text := "OK"
93 |
94 | if l5 >= float64(cores) {
95 | status = http.StatusInternalServerError
96 | text = "CRITICAL"
97 | } else if l5 >= float64(cores) {
98 | status = http.StatusTooManyRequests
99 | text = "WARNING"
100 | }
101 |
102 | mess := fmt.Sprintf("Load average: %.2f, %.2f, %.2f | Cores: %d", l1, l5, l15, cores)
103 | c.JSON(status, message{
104 | Status: text,
105 | Info: mess,
106 | })
107 | }
108 |
109 | // * RAM 使用量
110 | // @Summary Checks the ram usage
111 | // @Description Checks the ram usage
112 | // @Tags sd
113 | // @Accept json
114 | // @Produce json
115 | // @Success 200 {object} message "{"status":"OK", "info":"Free space: 5293MB (5GB) / 7665MB (7GB) | Used: 69%"}"
116 | // @Router /sd/ram [get]
117 | func RAMCheck(c *gin.Context) {
118 | u, _ := mem.VirtualMemory()
119 |
120 | usedMB := int(u.Used) / MB
121 | usedGB := int(u.Used) / GB
122 | totalMB := int(u.Total) / MB
123 | totalGB := int(u.Total) / GB
124 | usedPercent := int(u.UsedPercent)
125 |
126 | status := http.StatusOK
127 | text := "OK"
128 |
129 | if usedPercent >= 95 {
130 | status = http.StatusInternalServerError
131 | text = "CRITICAL"
132 | } else if usedPercent >= 90 {
133 | status = http.StatusTooManyRequests
134 | text = "WARNING"
135 | }
136 |
137 | mess := fmt.Sprintf("Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", usedMB, usedGB, totalMB, totalGB, usedPercent)
138 | c.JSON(status, message{
139 | Status: text,
140 | Info: mess,
141 | })
142 | }
143 |
--------------------------------------------------------------------------------
/server/handler/todo.go:
--------------------------------------------------------------------------------
1 | package handler
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "mini_todo/errno"
6 | "mini_todo/model"
7 | "strconv"
8 | )
9 |
10 | type ListTodo struct {
11 | Total uint64 `json:"total"`
12 | TodoList []model.TodoModel `json:"todos"`
13 | }
14 |
15 | type createTodo struct {
16 | }
17 |
18 | /*todos 路由相关处理函数*/
19 |
20 | // * get all todos api.
21 | // @Summary Get all todos
22 | // @Description Get all todos from database
23 | // @Tags todo
24 | // @Accept json
25 | // @Produce json
26 | // @Success 200 {object} model.TodoModel "{"code":0,"message":"OK","data":{"total":233, "todos":[{"ID":91,"title": "烫头", "completed": 1,"CreatedAt": "2019-10-12T10:10:05+08:00","UpdatedAt": "2019-10-12T10:16:24+08:00","DeletedAt": null}]}}"
27 | // @Router /v1/todos [get]
28 | func FetchAllTodo(c *gin.Context) {
29 | var todo model.TodoModel
30 | count, todos, err := todo.GetAll()
31 | if err != nil {
32 | SendResponse(c, errno.ErrDatabase, nil)
33 | return
34 | }
35 |
36 | SendResponse(c, nil, ListTodo{
37 | Total: count,
38 | TodoList: todos,
39 | })
40 | }
41 |
42 | // @Summary Get a todo
43 | // @Description Get a todo
44 | // @Tags todo
45 | // @Accept json
46 | // @Produce json
47 | // @Success 200 {object} model.TodoModel "{"code":0,"message":"OK","data":{"ID":91,"title": "烫头", "completed": 1,"CreatedAt": "2019-10-12T10:10:05+08:00","UpdatedAt": "2019-10-12T10:16:24+08:00","DeletedAt": null}}"
48 | // @Router /v1/todos/{id} [get]
49 | func FetchSingleTodo(c *gin.Context) {
50 | var todo model.TodoModel
51 | var err error
52 |
53 | id, _ := strconv.Atoi(c.Param("id"))
54 | todo.ID = uint(id)
55 |
56 | if todo, err = todo.Get(); err != nil {
57 | SendResponse(c, errno.ErrDatabase, nil)
58 | return
59 | }
60 | SendResponse(c, nil, todo)
61 | }
62 |
63 | // TODO title为空时错误
64 | // @Summary Add new todos to the database
65 | // @Description Add a new todo
66 | // @Tags todo
67 | // @Accept json
68 | // @Produce json
69 | // @Param todo body model.TodoModel true "The todo info"
70 | // @Success 200 {object} handler.Response "{"code":0,"message":"OK","data":"create successful."}"
71 | // @Router /v1/todos/ [post]
72 | func AddTodo(c *gin.Context) {
73 | completed, _ := strconv.Atoi(c.PostForm("completed"))
74 |
75 | todo := model.TodoModel{
76 | Title: c.PostForm("title"),
77 | Completed: completed,
78 | }
79 |
80 | // * 创建一条记录,错误处理
81 | if err := todo.Create(); err != nil {
82 | SendResponse(c, errno.ErrDatabase, nil)
83 | return
84 | }
85 |
86 | // * 返回结果
87 | SendResponse(c, nil, "create successful.")
88 | }
89 |
90 | // @Summary Update a todo
91 | // @Description Update a todo
92 | // @Tags todo
93 | // @Accept json
94 | // @Produce json
95 | // @Param id path uint64 true "The todo's database id index num"
96 | // @Param todo body model.TodoModel true "The todo info"
97 | // @Success 200 {object} handler.Response "{"code":0,"message":"OK","data":"update successful."}"
98 | // @Router /v1/todos/{id} [put]
99 | func UpdateTodo(c *gin.Context) {
100 | id, _ := strconv.Atoi(c.Param("id"))
101 | completed, _ := strconv.Atoi(c.PostForm("completed"))
102 | todo := model.TodoModel{
103 | Title: c.PostForm("title"),
104 | Completed: completed,
105 | }
106 | todo.ID = uint(id)
107 | if err := todo.Update(); err != nil {
108 | SendResponse(c, errno.ErrDatabase, nil)
109 | return
110 | }
111 |
112 | // * 返回结果
113 | SendResponse(c, nil, "update successful.")
114 | }
115 |
116 | // @Summary Delete a todo
117 | // @Description Delete a todo
118 | // @Tags todo
119 | // @Accept json
120 | // @Produce json
121 | // @Success 200 {object} handler.Response "{"code":0,"message":"OK","data":"delete successful."}"
122 | // @Router /v1/todos/{id} [delete]
123 | func DeleteTodo(c *gin.Context) {
124 | id, _ := strconv.Atoi(c.Param("id"))
125 | todo := model.TodoModel{}
126 |
127 | todo.ID = uint(id)
128 |
129 | if err := todo.Delete(); err != nil {
130 | SendResponse(c, errno.ErrDatabase, nil)
131 | return
132 | }
133 | SendResponse(c, nil, "delete successful.")
134 | }
135 |
--------------------------------------------------------------------------------
/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "errors"
5 | "github.com/gin-gonic/gin"
6 | _ "github.com/jinzhu/gorm/dialects/mysql"
7 | "github.com/spf13/pflag"
8 | "github.com/spf13/viper"
9 | "log"
10 | "mini_todo/config"
11 | "mini_todo/model"
12 | "mini_todo/router"
13 | "net/http"
14 | "time"
15 | )
16 |
17 | // * 读取命令行配置(config文件)
18 | var cfg = pflag.StringP("config", "c", "", "apiserver config file path.")
19 |
20 | // @title A todos application API
21 | // @version 1.0
22 | // @description This is a todos application server.
23 | // @termsOfService http://me.shiniao.fun/
24 |
25 | // @contact.name shiniao
26 | // @contact.url http://me.shiniao.fun/
27 | // @contact.email zhuzhezhe5@gmail.com
28 |
29 | // @license.name Apache 2.0
30 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html
31 |
32 | // @host todo.shiniao.fun
33 | // @BasePath /v1
34 | func main() {
35 |
36 | // * 初始化配置
37 | pflag.Parse()
38 | if err := config.Init(*cfg); err != nil {
39 | panic(err)
40 | }
41 | log.Printf("The config inited.")
42 |
43 | // * 初始化数据库
44 | model.DB.Init()
45 | defer model.DB.Close()
46 | log.Printf("The database inited.")
47 |
48 | // * 设置 run mode
49 | gin.SetMode(viper.GetString("runmode"))
50 | log.Printf("Gin run mode set to: %s", viper.GetString("runmode"))
51 |
52 | // * gin engine.
53 | g := gin.New()
54 | router.Load(g)
55 | log.Printf("The gin engine started, and the router loaded.")
56 |
57 | go func() {
58 | if err := pingServer(); err != nil {
59 | log.Fatal("The router has no response, or it might took too long to start up.", err)
60 | }
61 | log.Println("The router has been deployed successfully.")
62 | }()
63 |
64 | log.Printf("Start to listening on: %s", viper.GetString("addr"))
65 | g.Run(viper.GetString("addr")).Error()
66 | }
67 |
68 | // * api 状态自检
69 | func pingServer() error {
70 | for i := 0; i < viper.GetInt("max_ping_count"); i++ {
71 | resp, err := http.Get(viper.GetString("url") + "/sd/health")
72 | if err == nil && resp.StatusCode == 200 {
73 | return nil
74 | }
75 |
76 | log.Print("Waiting for the router, retry in 1 second.")
77 | time.Sleep(time.Second)
78 | }
79 | return errors.New("Cannot connect to the router.")
80 | }
81 |
--------------------------------------------------------------------------------
/server/middleware/middleware.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "mini_todo/errno"
6 | "mini_todo/handler"
7 | "mini_todo/token"
8 | "net/http"
9 | "time"
10 | )
11 |
12 | func AuthMiddleware() gin.HandlerFunc {
13 | return func(c *gin.Context) {
14 | // Parse the json web token.
15 | if err := token.ParseRequest(c); err != nil {
16 | handler.SendResponse(c, errno.ErrTokenInvalid, nil)
17 | c.Abort()
18 | return
19 | }
20 |
21 | c.Next()
22 | }
23 | }
24 |
25 | // * 不使用cache
26 | func NoCache(c *gin.Context) {
27 | c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
28 | c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
29 | c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
30 | c.Next()
31 | }
32 |
33 | // * 浏览器跨域
34 | func Options(c *gin.Context) {
35 | c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
36 | c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
37 | c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
38 | c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
39 |
40 | if c.Request.Method == "OPTIONS" {
41 | c.AbortWithStatus(204)
42 | return
43 | }
44 | c.Next()
45 |
46 | }
47 |
48 | // * 安全方面设置
49 | func Secure(c *gin.Context) {
50 | c.Header("Access-Control-Allow-Origin", "*")
51 | c.Header("X-Frame-Options", "DENY")
52 | c.Header("X-Content-Type-Options", "nosniff")
53 | c.Header("X-XSS-Protection", "1; mode=block")
54 | if c.Request.TLS != nil {
55 | c.Header("Strict-Transport-Security", "max-age=31536000")
56 | }
57 |
58 | // Also consider adding Content-Security-Policy headers
59 | // c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
60 | }
61 |
--------------------------------------------------------------------------------
/server/model/init.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "fmt"
5 | "github.com/jinzhu/gorm"
6 | _ "github.com/jinzhu/gorm/dialects/mysql"
7 | "github.com/spf13/viper"
8 | "log"
9 | )
10 |
11 | type Database struct {
12 | Self *gorm.DB
13 | }
14 |
15 |
16 | func (db *Database) Init() {
17 |
18 | // * 初始化数据库
19 | config := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=%t&loc=%s",
20 | viper.GetString("db.username"),
21 | viper.GetString("db.password"),
22 | viper.GetString("db.addr"),
23 | viper.GetString("db.dbname"),
24 | true,
25 | //"Asia/Shanghai"),
26 | "Local")
27 | // * 以config打开mysql数据库
28 | _db, err := gorm.Open("mysql", config)
29 | DB = &Database{Self: _db}
30 | if err != nil {
31 | log.Printf("Database connection failed. Database name: %s", viper.GetString("db.dbname"))
32 | }
33 | // * 解决中文字符问题:Error 1366
34 | _db = _db.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8 auto_increment=1")
35 | _db.AutoMigrate(&TodoModel{})
36 |
37 | }
38 |
39 | func (db *Database) Close() {
40 | // * 关闭数据库
41 | db.Self.Close().Error()
42 | }
43 |
44 | var DB *Database
45 |
--------------------------------------------------------------------------------
/server/model/todo.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "github.com/jinzhu/gorm"
4 |
5 | // * bind orm
6 | type TodoModel struct {
7 | gorm.Model
8 | Title string `gorm:"not null" json:"title"`
9 | Completed int `json:"completed"`
10 | }
11 |
12 |
13 |
14 | // * 设置表名
15 | func (todo TodoModel) TableName() string {
16 | return "todos"
17 | }
18 |
19 | // * 添加todo到数据库
20 | func (todo TodoModel) Create() error {
21 | return DB.Self.Create(&todo).Error
22 | }
23 |
24 | // * 删除某个todo
25 | func (todo TodoModel) Delete() error {
26 | return DB.Self.Delete(&todo).Error
27 | }
28 |
29 | // * Update
30 | func (todo TodoModel) Update() error {
31 | return DB.Self.Save(&todo).Error
32 | }
33 |
34 | // * 获取某一条todo
35 | func (todo TodoModel) Get() (TodoModel, error) {
36 | return todo, DB.Self.First(&todo, todo.ID).Error
37 | }
38 |
39 | func (todo TodoModel) GetAll() (uint64, []TodoModel, error) {
40 |
41 | var todos []TodoModel
42 | var count uint64
43 |
44 | if err := DB.Self.Table(todo.TableName()).Count(&count).Error; err != nil {
45 | return count, todos, err
46 | }
47 | if err := DB.Self.Find(&todos).Error; err != nil {
48 | return count, todos, err
49 | }
50 | return count, todos, nil
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/server/router/router.go:
--------------------------------------------------------------------------------
1 | package router
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/swaggo/gin-swagger"
6 | "github.com/swaggo/gin-swagger/swaggerFiles"
7 |
8 | _ "mini_todo/docs"
9 | "mini_todo/handler"
10 | "mini_todo/middleware"
11 | "net/http"
12 | )
13 |
14 | func Load(g *gin.Engine) *gin.Engine {
15 |
16 | // * 全局middlewares
17 |
18 | g.Use(gin.Recovery())
19 | g.Use(middleware.NoCache)
20 | g.Use(middleware.Options)
21 | g.Use(middleware.Secure)
22 |
23 | // * 404 handler
24 | g.NoRoute(func(c *gin.Context) {
25 | c.String(http.StatusNotFound, "The incorrect API route.")
26 | })
27 |
28 | // swagger api docs
29 | g.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
30 |
31 | // * token下发
32 | g.POST("/token", handler.Token)
33 |
34 | // * todos 路由组
35 | v1 := g.Group("/v1/todos")
36 | v1.Use(middleware.AuthMiddleware())
37 | {
38 | v1.GET("/", handler.FetchAllTodo)
39 | v1.GET("/:id", handler.FetchSingleTodo)
40 | v1.POST("/", handler.AddTodo)
41 | v1.PUT("/:id", handler.UpdateTodo)
42 | v1.DELETE("/:id", handler.DeleteTodo)
43 | }
44 |
45 | // * api server 健康状况
46 | svcd := g.Group("/sd")
47 |
48 | {
49 | svcd.GET("/health", handler.HealthCheck)
50 | svcd.GET("/disk", handler.DiskCheck)
51 | svcd.GET("/cpu", handler.CPUCheck)
52 | svcd.GET("/ram", handler.RAMCheck)
53 | }
54 | return g
55 | }
56 |
--------------------------------------------------------------------------------
/server/token/token.go:
--------------------------------------------------------------------------------
1 | package token
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "github.com/dgrijalva/jwt-go"
7 | "github.com/gin-gonic/gin"
8 | "github.com/spf13/viper"
9 | "time"
10 | )
11 |
12 | type Token struct {
13 | Token string `json:"token"`
14 | }
15 |
16 | var ErrMissingHeader = errors.New("The length of the `Authorization` header is zero.")
17 |
18 |
19 | // * 下发token
20 | func Sign(ctx *gin.Context, key, secret string) (tokenString string, err error) {
21 |
22 | // * 读取config
23 | if secret == "" {
24 | secret = viper.GetString("jwt_secret")
25 | }
26 | // * jwt claims
27 | // TODO 过期时间处理
28 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
29 | // * 用户申请token携带的key
30 | "key": key,
31 | // * 生效时间
32 | "nbf": time.Now().Unix(),
33 | // * 签发时间
34 | "iat": time.Now().Unix(),
35 | // * 过期时间
36 | //"exp": time.Now().Add(time.Hour * 2).Unix(),
37 | })
38 | // * Sign the token with the specified secret.
39 | tokenString, err = token.SignedString([]byte(secret))
40 |
41 | return
42 | }
43 |
44 |
45 | func ParseRequest(c *gin.Context) error {
46 | header := c.Request.Header.Get("Authorization")
47 |
48 | // * Load the jwt secret from config
49 | secret := viper.GetString("jwt_secret")
50 |
51 | if len(header) == 0 {
52 | return ErrMissingHeader
53 | }
54 |
55 | var t string
56 | // * Parse the header to get the token part.
57 | fmt.Sscanf(header, "Bearer %s", &t)
58 | return Parse(t, secret)
59 | }
60 |
61 |
62 | // * 解析token
63 | func Parse(token, secret string) error {
64 |
65 | // * parse the token
66 | t, err := jwt.Parse(token, secretFunc(secret))
67 |
68 | // Parse error.
69 | if err != nil {
70 | return err
71 |
72 | // * Read the token if it's valid.
73 | } else if _, ok := t.Claims.(jwt.MapClaims); ok && t.Valid {
74 | return nil
75 |
76 | } else {
77 | return err
78 | }
79 | }
80 |
81 | func secretFunc(secret string) jwt.Keyfunc {
82 | return func(token *jwt.Token) (interface{}, error) {
83 | // * Make sure the `alg` is what we except.
84 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
85 | return nil, jwt.ErrSignatureInvalid
86 | }
87 |
88 | return []byte(secret), nil
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/todogif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ischaojie/gtodo/6f4b39ae6aea83158943b22ad2119d9205465354/todogif.gif
--------------------------------------------------------------------------------