feat: update the code by the goproject template

This commit is contained in:
saltbo
2020-08-07 00:33:37 +08:00
parent 87a81f2439
commit ff286a824d
40 changed files with 1436 additions and 1399 deletions
+9
View File
@@ -0,0 +1,9 @@
github: [saltbo]
patreon: saltbo
open_collective: # Replace with a single open_collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
+39
View File
@@ -0,0 +1,39 @@
name: build
on:
push:
branches:
- develop
- master
pull_request:
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
- name: Check out code
uses: actions/checkout@v1
- name: Run Unit tests.
run: make test
- name: Upload Coverage report to CodeCov
uses: codecov/codecov-action@v1.0.0
with:
token: ${{secrets.CODECOV_TOKEN}}
file: .coverprofile
build:
name: Build
runs-on: ubuntu-latest
needs: [test]
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13
- name: Check out code
uses: actions/checkout@v1
- name: Build
run: make build
+26
View File
@@ -0,0 +1,26 @@
name: release
on:
push:
tags:
- '*'
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Unshallow
run: git fetch --prune --unshallow
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13.x
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
+7 -7
View File
@@ -5,13 +5,13 @@
*.so
*.dylib
# Test binary, build with `go test -c`
# Test binary, built with `go test -c`
*.test
bin
build
coverage.txt
config.yaml
roles.json
.coverprofile
# Output of the go coverage tool, specifically when used with LiteIDE
# Output
build
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
+62
View File
@@ -0,0 +1,62 @@
dist: ./build/release
before:
hooks:
- go mod download
builds:
- id: server
main: ./cmd/main.go
ldflags:
- -s -w -X cmd.release={{.Version}} -X cmd.commit={{.Commit}} -X cmd.repo={{.GitURL}}
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
archives:
- name_template: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}"
replacements:
darwin: macos
format: tar.gz
format_overrides: # archive as zip on Windows
- goos: windows
format: zip
files:
- none*
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: desc
filters:
exclude:
- '^docs:'
- '^test:'
brews:
- github:
owner: saltbo
name: homebrew-bin
commit_author:
name: Ambor
email: saltbo@foxmail.com
folder: Formula
homepage: https://github.com/saltbo/goproject
description: Convenient static file deployment tool that supports multiple platforms
test: |
system "#{bin}/uptoc", "--version"
nfpms:
- file_name_template: '{{ .ProjectName }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
homepage: https://github.com/saltbo/goproject
description: Convenient static file deployment tool that supports multiple platforms
maintainer: Ambor <saltbo@foxmail.com>
license: MIT
formats:
- deb
- rpm
dependencies:
- git
recommends:
- golang
-26
View File
@@ -1,26 +0,0 @@
language: go
go:
- 1.13.x
cache:
directories:
- $GOPATH/pkg/mod
dist: trusty
sudo: false
install: true
script:
- make
- make test
- make release
after_success:
- bash <(curl -s https://codecov.io/bash)
deploy:
- provider: releases
api_key: $GITHUB_TOKEN
skip_cleanup: true
file_glob: true
file:
- build/*.tar.gz
on:
tags: true
+1 -5
View File
@@ -1,5 +1,5 @@
host: http://local.frpx.saltbo.cn:1080
mysqldsn: root:root@tcp(127.0.0.1:3306)/zpan?charset=utf8&interpolateParams=true
mysqldsn: root:admin@tcp(127.0.0.1:3306)/zpan?charset=utf8&interpolateParams=true
provider:
name: alioss
@@ -8,7 +8,3 @@ provider:
access_key: LTAI4FxxxxxxxxxxxxBXmS3
access_secret: Vt1FZgXxxxxxxxxxxxxxxx380AI
email:
host: smtpdm.aliyun.com:25
user: zpan@mail.saltbo.cn
password: SvNxxxxxxxxJZB
+128
View File
@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[saltbo@foxmail.com].
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+73
View File
@@ -0,0 +1,73 @@
# Contributing
By participating to this project, you agree to abide our [code of conduct](/CODEOFCONDUCT.md).
## Setup your machine
`uptoc` is written in [Go](https://golang.org/).
Prerequisites:
- `make`
- [Go 1.13+](https://golang.org/doc/install)
Clone `uptoc` anywhere:
```sh
$ git clone git@github.com:saltbo/uptoc.git
```
Install the build and lint dependencies:
```sh
$ make dep
```
A good way of making sure everything is all right is running the test suite:
```sh
$ make test
```
## Test your change
You can create a branch for your changes and try to build from the source as you go:
```sh
$ make build
```
Which runs all the linters and tests.
## Create a commit
Commit messages should be well formatted, and to make that "standardized", we
are using Conventional Commits.
You can follow the documentation on
[their website](https://www.conventionalcommits.org).
## Submit a pull request
Push your branch to your `uptoc` fork and open a pull request against the
master branch.
## Financial contributions
We also welcome financial contributions in full transparency on our [open collective](https://opencollective.com/saltbo).
Anyone can file an expense. If the expense makes sense for the development of the community, it will be "merged" in the ledger of our open collective by the core contributors and the person who filed the expense will be reimbursed.
## Credits
### Contributors
Thank you to all the people who have already contributed to Uptoc!
<a href="graphs/contributors"><img src="https://opencollective.com/saltbo/contributors.svg?width=890" /></a>
### Backers
Thank you to all our backers! [[Become a backer](https://opencollective.com/saltbo#backer)]
### Sponsors
Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/saltbo#sponsor))
+6 -6
View File
@@ -1,6 +1,6 @@
MIT License
The MIT License (MIT)
Copyright (c) 2019 saltbo
Copyright © 2020 Ambor <saltbo@foxmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+22 -19
View File
@@ -1,27 +1,30 @@
.PHONY: default install build fmt test vet docker clean
.PHONY: all dep lint vet test test-coverage build clean
default: build
# custom define
PROJECT := zpan
MAINFILE := main.go
build:
GO_BUILD_FLAGS="-v" ./scripts/build.sh
./bin/zpan --version
all: build
run:
./bin/zpan
dep: ## Get the dependencies
@go mod download
install:
./scripts/install.sh
lint: ## Lint Golang files
@golangci-lint --version
@golangci-lint run -D errcheck
release:
./scripts/release.sh
test: ## Run tests with coverage
go test -coverprofile .coverprofile ./...
go tool cover --func=.coverprofile
test:
go test -coverprofile=coverage.txt -covermode=atomic ./...
go tool cover --func=coverage.txt
coverage-html: ## show coverage by the html
go tool cover -html=.coverprofile
covhtml:
go tool cover -html=coverage.txt
build: dep ## Build the binary file
@go build -o build/bin/$(PROJECT) $(MAINFILE)
clean:
rm -rf bin
rm -rf build
clean: ## Remove previous build
@rm -rf ./build
help: ## Display this help screen
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
+27 -44
View File
@@ -1,65 +1,48 @@
ZPan - Your Last disk on the cloud.
=========================
[![Build Status](https://travis-ci.org/saltbo/zpan.svg)](https://travis-ci.org/saltbo/zpan)
[![codecov](https://codecov.io/gh/saltbo/zpan/branch/master/graph/badge.svg)](https://codecov.io/gh/saltbo/zpan)
[![codebeat badge](https://codebeat.co/badges/e97d3305-de49-4a9c-9ead-1aca942b9e16)](https://codebeat.co/projects/github-com-saltbo-zpan-master)
[![Go Report Card](https://goreportcard.com/badge/github.com/saltbo/zpan)](https://goreportcard.com/report/github.com/saltbo/zpan)
# zpan
## Features
- [x] File Manage
- [x] Folder Manage
- [x] File & Folder Share
- [x] Picture library
- [x] Storage Capacity Control
- [ ] RecycleBin
- [x] Support AliOSS
- [ ] Support Upyun
- [ ] Support Qiniuyun
[![](https://github.com/saltbo/github.com/saltbo/zpan/workflows/build/badge.svg)](https://github.com/saltbo/github.com/saltbo/zpan/actions?query=workflow%3Abuild)
[![](https://codecov.io/gh/saltbo/github.com/saltbo/zpan/branch/master/graph/badge.svg)](https://codecov.io/gh/saltbo/zpan)
[![](https://wakatime.com/badge/github/saltbo/zpan.svg)](https://wakatime.com/badge/github/saltbo/zpan)
[![](https://api.codacy.com/project/badge/Grade/88817db9b3b04c0293c9d001d574a5ef)](https://app.codacy.com/manual/saltbo/zpan?utm_source=github.com&utm_medium=referral&utm_content=saltbo/zpan&utm_campaign=Badge_Grade_Dashboard)
[![](https://img.shields.io/github/v/release/saltbo/zpan.svg)](https://github.com/saltbo/github.com/saltbo/zpan/releases)
[![](https://img.shields.io/github/license/saltbo/zpan.svg)](https://github.com/saltbo/github.com/saltbo/zpan/blob/master/LICENSE)
## Run environment
- Mac
- Linux
English | [🇨🇳中文](https://saltbo.cn/zpan)
## Dependent modules
- cli (github.com/urfave/cli)
- gin (github.com/gin-gonic/gin)
- jwt-go (github.com/dgrijalva/jwt-go)
- xorm (github.com/go-xorm/xorm)
- grbac (github.com/storyicon/grbac)
- statik (github.com/rakyll/statik)
- oss (github.com/aliyun/aliyun-oss-go-sdk/oss)
## Install the pre-compiled binary
## Install
### Binary
Download the appropriate binary for your platform from the [Releases](https://github.com/saltbo/zpan/releases) page
**homebrew tap**:
```bash
cp config.yaml.tpl config.yaml
vi config.yaml #setup your configs
./zpan
brew install saltbo/bin/uptoc
```
### Source
> depend on [Golang](https://golang.org/dl/) compiler environment
**homebrew** (may not be the latest version):
```bash
git clone https://github.com/saltbo/zpan.git
cd zpan && make && make install
brew install uptoc
```
**deb/rpm**:
Download the `.deb` or `.rpm` from the [releases page](https://github.com/saltbo/github.com/saltbo/zpan/releases) and
install with `dpkg -i` and `rpm -i` respectively.
**manually**:
Download the pre-compiled binaries from the [releases page](https://github.com/saltbo/github.com/saltbo/zpan/releases) and
copy to the desired location.
## Usage
```bash
make run
```
## Contributing
See [CONTRIBUTING](CONTRIBUTING.md) for details on submitting patches and the contribution workflow.
## Contact us
- [Author Blog](https://saltbo.cn).
## Author
- [Saltbo](https://github.com/saltbo)
- [saltbo](https://github.com/saltbo)
## License
- [MIT](https://github.com/saltbo/zpan/blob/master/LICENSE)
- [MIT](https://github.com/saltbo/github.com/saltbo/zpan/blob/master/LICENSE)
-124
View File
@@ -1,124 +0,0 @@
package api
import (
"context"
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/rakyll/statik/fs"
_ "zpan/assets"
"zpan/config"
"zpan/disk"
"zpan/pkg/ginx"
)
type Resource interface {
Register(router *ginx.Router)
}
type RestServer struct {
conf *config.Config
srv *http.Server
router *gin.Engine
resources []Resource
staticRs http.FileSystem
provider disk.Provider
}
func NewRest(conf *config.Config) (*RestServer, error) {
staticRs, err := fs.New()
if err != nil {
return nil, err
}
router := gin.Default()
return &RestServer{
srv: &http.Server{
Addr: ":8081",
Handler: router,
},
conf: conf,
router: router,
staticRs: staticRs,
}, nil
}
func (rs *RestServer) SetupProvider(provider disk.Provider) {
rs.provider = provider
rs.resources = []Resource{
NewUserResource(rs),
NewURLResource(rs),
NewFileResource(rs),
NewShareResource(),
}
}
func (rs *RestServer) Run() error {
rs.setupPing()
rs.setupResource()
log.Printf("[rest server started, listen %s]", rs.srv.Addr)
if err := rs.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("[rest server listen failed: %v]", err)
}
return nil
}
func (rs *RestServer) Stop() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := rs.srv.Shutdown(ctx); err != nil {
log.Fatal("[rest server shutdown err:]", err)
}
log.Printf("[rest server exited.]")
}
func (rs *RestServer) Provider() disk.Provider {
return rs.provider
}
func (rs *RestServer) setupPing() {
pingHandler := func(c *gin.Context) {
c.String(http.StatusOK, "pong")
}
rs.router.HEAD("/ping", pingHandler)
rs.router.GET("/ping", pingHandler)
}
func (rs *RestServer) setupResource() {
rs.router.GET("/", rs.staticFS)
rs.router.GET("/js/*filepath", rs.staticFS)
rs.router.GET("/css/*filepath", rs.staticFS)
rs.router.GET("/fonts/*filepath", rs.staticFS)
rs.router.NoRoute(func(c *gin.Context) {
indexHtml, err := rs.staticRs.Open("/index.html")
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
fsInfo, _ := indexHtml.Stat()
contentType := "text/html"
extraHeaders := map[string]string{}
c.DataFromReader(http.StatusOK, fsInfo.Size(), contentType, indexHtml, extraHeaders)
})
resourceRouter := rs.router.Group("/api")
// resourceRouter.Use(LoggerHandler())
for _, resource := range rs.resources {
resource.Register(ginx.NewRouter(resourceRouter))
}
}
func (rs *RestServer) staticFS(c *gin.Context) {
fileServer := http.FileServer(rs.staticRs)
fileServer.ServeHTTP(c.Writer, c.Request)
}
-150
View File
@@ -1,150 +0,0 @@
package api
import (
"fmt"
"math/rand"
"time"
"github.com/gin-gonic/gin"
"zpan/dao"
"zpan/model"
"zpan/pkg/ginx"
)
type ShareResource struct {
}
func NewShareResource() Resource {
return &ShareResource{}
}
func (rs *ShareResource) Register(router *ginx.Router) {
router.GET("/shares/:alias", rs.find)
router.GET("/shares", rs.findAll)
router.POST("/shares", rs.create)
router.PATCH("/shares/:alias", rs.update)
router.DELETE("/shares/:alias", rs.delete)
}
func (rs *ShareResource) find(c *gin.Context) error {
secret := c.Query("secret")
share := new(model.Share)
if exist, err := dao.DB.Where("alias=?", c.Param("alias")).Get(share); err != nil {
return ginx.Failed(err)
} else if !exist {
return ginx.Error(fmt.Errorf("share not found."))
} else if secret == "" && share.Secret != "" {
return ginx.Json(c, map[string]string{"k": "please submit secret."})
} else if share.Secret != secret {
return ginx.Error(fmt.Errorf("invalid secret."))
} else if time.Now().After(share.ExpireAt) {
return ginx.Error(fmt.Errorf("share expired."))
}
matter := new(model.Matter)
if exist, err := dao.DB.Id(share.MatterId).Get(matter); err != nil {
return ginx.Failed(err)
} else if !exist {
return ginx.Error(fmt.Errorf("matter not exist."))
}
return ginx.Json(c, matter)
}
func (rs *ShareResource) findAll(c *gin.Context) error {
p := new(QueryPage)
if err := c.BindQuery(p); err != nil {
return ginx.Error(err)
}
list := make([]model.Share, 0)
sn := dao.DB.Limit(p.Limit, p.Offset)
total, err := sn.FindAndCount(&list)
if err != nil {
return ginx.Error(err)
}
return ginx.JsonList(c, list, total)
}
func (rs *ShareResource) create(c *gin.Context) error {
p := new(BodyShare)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
}
matter := new(model.Matter)
if exist, err := dao.DB.Id(p.MId).Get(matter); err != nil {
return ginx.Failed(err)
} else if !exist {
return ginx.Error(fmt.Errorf("matter not found."))
}
m := model.Share{
Alias: randomString(12),
Uid: c.GetInt64("uid"),
MatterId: matter.Id,
Name: matter.Name,
ExpireAt: time.Now().Add(time.Second * time.Duration(p.ExpireSec)),
}
if p.Private {
m.Secret = randomString(5)
}
if _, err := dao.DB.Insert(m); err != nil {
return ginx.Failed(err)
}
return ginx.Json(c, m)
}
func (rs *ShareResource) update(c *gin.Context) error {
p := new(BodyShare)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
}
share := new(model.Share)
if exist, err := dao.DB.Id(p.Id).Get(share); err != nil {
return ginx.Failed(err)
} else if !exist {
return ginx.Error(fmt.Errorf("share not found."))
}
if p.Private && share.Secret == "" {
share.Secret = randomString(5)
}
if _, err := dao.DB.Id(share.Id).Update(share); err != nil {
return ginx.Failed(err)
}
return nil
}
func (rs *ShareResource) delete(c *gin.Context) error {
alias := c.Param("alias")
share := new(model.Share)
if exist, err := dao.DB.Where("alias=?", alias).Get(share); err != nil {
return ginx.Failed(err)
} else if !exist {
return ginx.Error(fmt.Errorf("matter not found."))
}
if _, err := dao.DB.Id(share.Id).Delete(share); err != nil {
return ginx.Failed(err)
}
return nil
}
func randomString(length int) (ret string) {
chars := "abcdefghijklmnopqrstuvwxyz0123456789"
for i := 0; i < length; i++ {
offset := rand.Intn(len(chars) - 1)
ret += chars[offset : offset+1]
}
return
}
-335
View File
@@ -1,335 +0,0 @@
package api
import (
"crypto/md5"
"encoding/hex"
"fmt"
"net"
"net/smtp"
"strings"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"github.com/storyicon/grbac"
"zpan/dao"
"zpan/model"
"zpan/pkg/ginx"
)
const (
IN_TOKEN = "intoken"
JWT_SECRET = "d52W5^bqTDMhEAwj1MK6ss%Rp7edT0#utzteGP!6tZwwAZtVSK367g4jd3g01c@E"
ROLE_ADMIN = "admin"
ROLE_MEMBER = "member"
ROLE_ANONYMOUS = "anonymous"
)
type RoleClaims struct {
jwt.StandardClaims
Uid int64 `json:"uid"`
Roles []string `json:"roles"`
}
type UserResource struct {
jwtSecret []byte
siteHost string
mailHost string
mailUser string
mailPwd string
}
func NewUserResource(rs *RestServer) Resource {
return &UserResource{
jwtSecret: []byte(JWT_SECRET),
siteHost: rs.conf.SiteHost,
mailHost: rs.conf.Email.Host,
mailUser: rs.conf.Email.User,
mailPwd: rs.conf.Email.Password,
}
}
func (rs *UserResource) Register(router *ginx.Router) {
router.POST("/users", rs.signUp)
router.POST("/user/tokens", rs.signIn)
router.PATCH("/user/resources", rs.activate)
router.POST("/user/recover-tokens", rs.sendRecoverMail)
router.PATCH("/user/password", rs.resetPassword)
router.Use(rs.Auth())
router.GET("/users", rs.findAll)
router.GET("/users/:uid", rs.find)
}
func (rs *UserResource) findAll(c *gin.Context) error {
p := new(QueryUser)
if err := c.BindQuery(p); err != nil {
return ginx.Error(err)
}
list := make([]model.User, 0)
total, err := dao.DB.Limit(p.Limit, p.Offset).FindAndCount(&list)
if err != nil {
return ginx.Error(err)
}
return ginx.JsonList(c, list, total)
}
func (rs *UserResource) find(c *gin.Context) error {
userId := c.Param("uid")
user := new(model.User)
if _, err := dao.DB.Id(userId).Get(user); err != nil {
return ginx.Error(err)
}
user.Password = ""
return ginx.Json(c, user)
}
func (rs *UserResource) signUp(c *gin.Context) error {
p := new(BodySignup)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
}
user := new(model.User)
if exist, err := dao.DB.Where("email = ?", p.Email).Get(user); err != nil {
return ginx.Failed(err)
} else if exist {
return ginx.Error(fmt.Errorf("user %s exist.", p.Email))
}
pwdHash := md5.Sum([]byte(p.Password))
m := &model.User{
Email: p.Email,
Password: hex.EncodeToString(pwdHash[:]),
Nickname: p.Email[:strings.Index(p.Email, "@")],
StorageMax: 1024 * 1024 * 50, // 50MB
Roles: ROLE_MEMBER,
ActiveToken: randomString(64),
}
if _, err := dao.DB.Insert(m); err != nil {
return ginx.Failed(err)
}
body := `
<h3>账户激活链接</h3>
<p><a href="%s/login?email=%s&atoken=%s">点击此处重置密码</a></p>
<p>如果您没有进行账号注册请忽略!</p>
`
body = fmt.Sprintf(body, rs.siteHost, p.Email, m.ActiveToken)
if err := rs.sendMail("账号注册成功,请激活您的账户", body, p.Email); err != nil {
return ginx.Failed(err)
}
return nil
}
func (rs *UserResource) activate(c *gin.Context) error {
p := new(BodyActivate)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
}
user := new(model.User)
if exist, err := dao.DB.Where("email = ?", p.Email).Get(user); err != nil {
return ginx.Failed(err)
} else if !exist || user.ActiveToken != p.Atoken {
return ginx.Error(fmt.Errorf("invalid activate token."))
}
session := dao.DB.NewSession()
defer session.Close()
// 初始化系统资源
picFolder := model.Matter{Uid: user.Id, Name: ".pics", Dirtype: DIRTYPE_SYS}
if _, err := session.Insert(picFolder); err != nil {
_ = session.Rollback()
return ginx.Failed(err)
}
// 标记激活成功
if _, err := session.ID(user.Id).Cols("active_token").Update(&model.User{ActiveToken: ""}); err != nil {
_ = session.Rollback()
return ginx.Failed(fmt.Errorf("active failed: %s", err))
}
if err := session.Commit(); err != nil {
return ginx.Failed(err)
}
return nil
}
func (rs *UserResource) signIn(c *gin.Context) error {
p := new(BodySignin)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
}
user := new(model.User)
if exist, err := dao.DB.Where("email = ?", p.Email).Get(user); err != nil {
return ginx.Failed(err)
} else if !exist {
return ginx.Error(fmt.Errorf("user %s not exist.", p.Email))
} else if user.ActiveToken != "" {
return ginx.Error(fmt.Errorf("email not activated!"))
}
pwdHash := md5.Sum([]byte(p.Password))
if user.Password != hex.EncodeToString(pwdHash[:]) {
return ginx.Error(fmt.Errorf("invalid password"))
}
token, err := rs.buildToken(user.Id, user.Roles)
if err != nil {
return ginx.Failed(err)
}
ginx.Cookie(c, IN_TOKEN, token)
ginx.Cookie(c, "uid", fmt.Sprint(user.Id))
return nil
}
func (rs *UserResource) signOut(c *gin.Context) error {
return nil
}
func (r *UserResource) buildToken(userId int64, userRole string) (token string, err error) {
timeNow := time.Now()
claims := &RoleClaims{
StandardClaims: jwt.StandardClaims{
Issuer: "ZPan",
Audience: "ZPanAPI",
ExpiresAt: timeNow.Add(7 * 24 * 3600 * time.Second).Unix(),
IssuedAt: timeNow.Unix(),
NotBefore: timeNow.Unix(),
Subject: fmt.Sprint(userId),
},
Uid: userId,
Roles: strings.Split(userRole, ","),
}
token, err = jwt.NewWithClaims(jwt.SigningMethodHS512, claims).SignedString([]byte(JWT_SECRET))
return
}
func (r *UserResource) Auth() ginx.HandlerFunc {
rBac, err := grbac.New(grbac.WithJSON("roles.json", time.Minute*10))
if err != nil {
panic(err)
}
return func(c *gin.Context) error {
roles, err := r.queryRoles(c)
if err != nil {
roles = []string{ROLE_ANONYMOUS}
}
state, err := rBac.IsRequestGranted(c.Request, roles)
if err != nil {
return ginx.Failed(err)
}
if !state.IsGranted() {
return ginx.Forbidden(fmt.Errorf("您没有权限进行此操作,请联系管理员."))
}
return nil
}
}
func (r *UserResource) queryRoles(c *gin.Context) ([]string, error) {
jwtToken, err := c.Cookie(IN_TOKEN)
if err != nil {
return nil, err
}
validation := func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return r.jwtSecret, nil
}
token, err := jwt.ParseWithClaims(jwtToken, &RoleClaims{}, validation)
if err != nil {
return nil, fmt.Errorf("token valid failed: %s", err)
}
rc := token.Claims.(*RoleClaims)
c.Set("uid", rc.Uid)
fmt.Println(rc.Uid)
return rc.Roles, nil
}
func (rs *UserResource) sendRecoverMail(c *gin.Context) error {
p := new(BodySendRecoverMail)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
}
user := new(model.User)
if exist, err := dao.DB.Where("email = ?", p.Email).Get(user); err != nil {
return ginx.Failed(err)
} else if !exist {
return ginx.Error(fmt.Errorf("user %s not exist.", p.Email))
}
// 记录恢复令牌
u := &model.User{RecoverToken: randomString(64)}
if _, err := dao.DB.ID(user.Id).Cols("recover_token").Update(u); err != nil {
return ginx.Failed(fmt.Errorf("active failed: %s", err))
}
body := `
<h3>密码重置链接</h3>
<p><a href="%s/login/resetpwd?email=%s&rtoken=%s">点击此处重置密码</a></p>
<p>如果您没有申请重置密码请忽略!</p>
`
body = fmt.Sprintf(body, rs.siteHost, p.Email, u.RecoverToken)
if err := rs.sendMail("密码重置申请", body, p.Email); err != nil {
return ginx.Failed(err)
}
return nil
}
func (rs *UserResource) resetPassword(c *gin.Context) error {
p := new(BodyResetPassword)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
}
user := new(model.User)
if exist, err := dao.DB.Where("email = ?", p.Email).Get(user); err != nil {
return ginx.Failed(err)
} else if !exist || user.RecoverToken != p.Rtoken {
return ginx.Error(fmt.Errorf("invalid recover token."))
}
// update the new password
pwdHash := md5.Sum([]byte(p.Password))
u := &model.User{Password: hex.EncodeToString(pwdHash[:]), RecoverToken: ""}
if _, err := dao.DB.ID(user.Id).Cols("password", "recover_token").Update(u); err != nil {
return ginx.Failed(err)
}
return nil
}
func (rs *UserResource) sendMail(subject, body, to string) error {
host, _, err := net.SplitHostPort(rs.mailHost)
if err != nil {
return err
}
auth := smtp.PlainAuth("", rs.mailUser, rs.mailPwd, host)
contentType := "Content-Type: text/html; charset=UTF-8"
msg := []byte("To: " + to + "\r\nFrom: " + rs.mailUser + "\r\nSubject: " + subject + "\r\n" + contentType + "\r\n\r\n" + body)
return smtp.SendMail(rs.mailHost, auth, rs.mailUser, []string{to}, msg)
}
-13
View File
File diff suppressed because one or more lines are too long
+97
View File
@@ -0,0 +1,97 @@
/*
Copyright © 2020 Ambor <saltbo@foxmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"fmt"
"os"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "zpan",
Short: "zpan is a cloud disk base on the cloud storage.",
Long: `Zpan is a cloud disk server
built with love by saltbo and friends in Go.
Complete documentation is available at https://saltbo.cn/zpan`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.zpan.yaml.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
//Find home directory.
home, err := homedir.Dir()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Search config in home directory with name ".zpan.yaml" (without extension).
viper.AddConfigPath(home)
viper.AddConfigPath(".")
viper.SetConfigName(".zpan")
}
viper.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
+64 -63
View File
@@ -1,76 +1,77 @@
package main
/*
Copyright © 2020 Ambor <saltbo@foxmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package cmd
import (
"log"
"math/rand"
"os"
"time"
"github.com/urfave/cli"
"github.com/saltbo/gopkg/ginutil"
"github.com/spf13/cobra"
"zpan/api"
"zpan/config"
"zpan/dao"
"zpan/disk"
"zpan/version"
"github.com/saltbo/zpan/config"
"github.com/saltbo/zpan/dao"
"github.com/saltbo/zpan/disk"
"github.com/saltbo/zpan/rest"
)
const (
// uploader configs
UPTOC_UPLOADER_OSS = "alioss"
)
var (
appFlags = []cli.Flag{
cli.StringFlag{
Name: "config,c",
Usage: "specify path of the config file. default: config.yaml",
Value: "config.yaml",
},
}
)
func main() {
app := cli.NewApp()
app.Name = "zpan"
app.Usage = "A cloud disk base on the cloud storage."
app.Copyright = "(c) 2019 zpan.saltbo.cn"
app.Compiled = time.Now()
app.Version = version.Short
app.Flags = appFlags
app.Action = serve
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
func serve(c *cli.Context) {
rand.Seed(time.Now().UnixNano())
conf := config.Parse(c.String("config"))
dao.Init(conf.MySqlDSN)
// select provider
var provider disk.Provider
switch conf.Provider.Name {
case UPTOC_UPLOADER_OSS:
ossProvider, err := disk.NewAliOss(conf.Provider)
// serverCmd represents the server command
var serverCmd = &cobra.Command{
Use: "server",
Short: "A cloud disk base on the cloud storage.",
Run: func(cmd *cobra.Command, args []string) {
conf := config.Parse()
dao.Init(conf.MySqlDSN)
rs, err := ginutil.NewServer(":8080")
if err != nil {
log.Fatalln(err)
}
provider = ossProvider
default:
log.Fatalf("provider %s not support.", conf.Provider.Name)
}
diskProvider, err := disk.New(conf.Provider)
if err != nil {
log.Fatalln(err)
}
// init restServer
rs, err := api.NewRest(conf)
if err != nil {
log.Fatalln(err)
}
rs.SetupProvider(provider)
if err := rs.Run(); err != nil {
log.Fatal(err)
}
rs.SetupResource("/api",
rest.NewFileResource(conf.Provider.Bucket, diskProvider),
rest.NewShareResource(),
rest.NewURLResource(conf, diskProvider),
rest.NewUserResource(),
)
if err := rs.Run(); err != nil {
log.Fatal(err)
}
},
}
func init() {
rootCmd.AddCommand(serverCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// serverCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// serverCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
+39
View File
@@ -0,0 +1,39 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var (
// RELEASE returns the release version
release = "unknown"
// REPO returns the git repository URL
repo = "unknown"
// COMMIT returns the short sha from git
commit = "unknown"
)
// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "print the version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("release: %s, repo: %s, commit: %s", release, repo, commit)
},
}
func init() {
rootCmd.AddCommand(versionCmd)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// versionCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// versionCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
+4 -17
View File
@@ -1,9 +1,9 @@
package config
import (
"gopkg.in/yaml.v2"
"io/ioutil"
"log"
"github.com/spf13/viper"
)
// CloudProvider
@@ -15,29 +15,16 @@ type Provider struct {
AccessSecret string `yaml:"access_secret"`
}
// system send email
type Email struct {
Host string `yaml:"host"`
User string `yaml:"user"`
Password string `yaml:"password"`
}
type Config struct {
SiteHost string `yaml:"site_host"`
StoreHost string `yaml:"store_host"`
MySqlDSN string `yaml:"mysqldsn"`
Provider *Provider `yaml:"provider"`
Email *Email `yaml:"email"`
}
func Parse(filename string) *Config {
buff, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatalf("read config file failed: %v", err)
}
func Parse() *Config {
conf := new(Config)
if err := yaml.Unmarshal(buff, conf); err != nil {
if err := viper.Unmarshal(conf); err != nil {
log.Fatalf("unmarshal yaml config failed: %v", err)
}
+2 -86
View File
@@ -1,10 +1,9 @@
package dao
import (
"fmt"
"log"
"strings"
"zpan/model"
"github.com/saltbo/zpan/model"
_ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
@@ -30,86 +29,3 @@ func Init(dsn string) {
DB = db
}
func DirExist(uid int64, dir string) bool {
if dir == "" {
return true
}
items := strings.Split(dir, "/")
name := items[len(items)-2]
parent := strings.TrimSuffix(dir, name+"/")
exist, err := DB.Where("uid=? and name=? and parent=?", uid, name, parent).Exist(&model.Matter{})
if err != nil {
log.Panicln(err)
}
return exist
}
func FileGet(uid int64, fileId interface{}) (*model.Matter, error) {
m := new(model.Matter)
if exist, err := DB.Id(fileId).Get(m); err != nil {
return nil, err
} else if !exist {
return nil, fmt.Errorf("file not exist.")
} else if m.Uid != uid {
return nil, fmt.Errorf("file not belong to you.")
}
return m, nil
}
func FileCopy(srcFile *model.Matter, dest string) error {
m := &model.Matter{
Uid: srcFile.Uid,
Name: srcFile.Name,
Type: srcFile.Type,
Size: srcFile.Size,
Parent: dest,
Object: srcFile.Object,
}
_, err := DB.Insert(m)
return err
}
func FileMove(id int64, dest string) error {
_, err := DB.ID(id).Cols("parent").Update(&model.Matter{Parent: dest})
return err
}
func FileRename(id int64, name string) error {
_, err := DB.ID(id).Cols("name").Update(&model.Matter{Name: name})
return err
}
func DirRename(id int64, name string) error {
oldMatter := new(model.Matter)
if exist, _ := DB.Id(id).Get(oldMatter); !exist {
return fmt.Errorf("matter not exist.")
}
oldParent := fmt.Sprintf("%s%s/", oldMatter.Parent, oldMatter.Name)
newParent := fmt.Sprintf("%s%s/", oldMatter.Parent, name)
list := make([]model.Matter, 0)
_ = DB.Where("parent like '" + oldParent + "%'").Find(&list)
session := DB.NewSession()
defer session.Close()
for _, v := range list {
v.Parent = strings.Replace(v.Parent, oldParent, newParent, 1)
_, err := session.ID(v.Id).Cols("parent").Update(v)
if err != nil {
_ = session.Rollback()
return err
}
}
m := &model.Matter{Name: name}
if _, err := session.ID(id).Cols("name").Update(m); err != nil {
_ = session.Rollback()
return err
}
return session.Commit()
}
+92
View File
@@ -0,0 +1,92 @@
package dao
import (
"fmt"
"log"
"strings"
"github.com/saltbo/zpan/model"
)
func DirExist(uid int64, dir string) bool {
if dir == "" {
return true
}
items := strings.Split(dir, "/")
name := items[len(items)-2]
parent := strings.TrimSuffix(dir, name+"/")
exist, err := DB.Where("uid=? and name=? and parent=?", uid, name, parent).Exist(&model.Matter{})
if err != nil {
log.Panicln(err)
}
return exist
}
func FileGet(uid int64, fileId interface{}) (*model.Matter, error) {
m := new(model.Matter)
if exist, err := DB.Id(fileId).Get(m); err != nil {
return nil, err
} else if !exist {
return nil, fmt.Errorf("file not exist.")
} else if m.Uid != uid {
return nil, fmt.Errorf("file not belong to you.")
}
return m, nil
}
func FileCopy(srcFile *model.Matter, dest string) error {
m := &model.Matter{
Uid: srcFile.Uid,
Name: srcFile.Name,
Type: srcFile.Type,
Size: srcFile.Size,
Parent: dest,
Object: srcFile.Object,
}
_, err := DB.Insert(m)
return err
}
func FileMove(id int64, dest string) error {
_, err := DB.ID(id).Cols("parent").Update(&model.Matter{Parent: dest})
return err
}
func FileRename(id int64, name string) error {
_, err := DB.ID(id).Cols("name").Update(&model.Matter{Name: name})
return err
}
func DirRename(id int64, name string) error {
oldMatter := new(model.Matter)
if exist, _ := DB.Id(id).Get(oldMatter); !exist {
return fmt.Errorf("matter not exist.")
}
oldParent := fmt.Sprintf("%s%s/", oldMatter.Parent, oldMatter.Name)
newParent := fmt.Sprintf("%s%s/", oldMatter.Parent, name)
list := make([]model.Matter, 0)
_ = DB.Where("parent like '" + oldParent + "%'").Find(&list)
session := DB.NewSession()
defer session.Close()
for _, v := range list {
v.Parent = strings.Replace(v.Parent, oldParent, newParent, 1)
_, err := session.ID(v.Id).Cols("parent").Update(v)
if err != nil {
_ = session.Rollback()
return err
}
}
m := &model.Matter{Name: name}
if _, err := session.ID(id).Cols("name").Update(m); err != nil {
_ = session.Rollback()
return err
}
return session.Commit()
}
-49
View File
@@ -1,49 +0,0 @@
[
{
"id": 0,
"host": "*",
"path": "**",
"method": "*",
"authorized_roles": [
"admin",
"member"
],
"forbidden_roles": [
"black_user"
],
"allow_anyone": false
},
{
"id": 1,
"host": "*",
"path": "/api/files/callback",
"method": "{POST}",
"authorized_roles": [
"anonymous"
],
"forbidden_roles": [],
"allow_anyone": false
},
{
"id": 3,
"host": "*",
"path": "/api/users",
"method": "POST",
"authorized_roles": [
"anonymous"
],
"forbidden_roles": [],
"allow_anyone": false
},
{
"id": 3,
"host": "*",
"path": "/api/user/tokens",
"method": "POST",
"authorized_roles": [
"anonymous"
],
"forbidden_roles": [],
"allow_anyone": false
}
]
-15
View File
@@ -1,15 +0,0 @@
[Unit]
Description=zpan - A cloud disk base on the cloud storage.
Documentation=https://github.com/saltbo/zpan
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/run/zpan.pid
ExecStart=/usr/local/bin/zpan -c /etc/zpan/config.yaml
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
+4 -3
View File
@@ -4,9 +4,10 @@ import (
"fmt"
"net/url"
"strings"
"zpan/config"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
"github.com/saltbo/zpan/config"
)
var urlEncode = url.QueryEscape
@@ -15,8 +16,8 @@ type AliOss struct {
cli *oss.Client
}
func NewAliOss(conf *config.Provider, options ...oss.ClientOption) (*AliOss, error) {
cli, err := oss.New(conf.Endpoint, conf.AccessKey, conf.AccessSecret, options...)
func newAliOss(conf *config.Provider) (*AliOss, error) {
cli, err := oss.New(conf.Endpoint, conf.AccessKey, conf.AccessSecret)
if err != nil {
return nil, err
}
+18
View File
@@ -1,7 +1,10 @@
package disk
import (
"fmt"
"time"
"github.com/saltbo/zpan/config"
)
type Object struct {
@@ -12,6 +15,7 @@ type Object struct {
ETag string `json:"etag"`
LastModified time.Time `json:"last_modified"`
}
type Objects []Object
type Provider interface {
@@ -24,3 +28,17 @@ type Provider interface {
DeleteObject(bucketName, objectKey string) error
DeleteObjects(bucketName string, objectKeys []string) error
}
type ProviderConstructor func(provider *config.Provider) (*AliOss, error)
var providerConstructors = map[string]ProviderConstructor{
"alioss": newAliOss,
}
func New(provider *config.Provider) (Provider, error) {
if providerConstructor, ok := providerConstructors[provider.Name]; ok {
return providerConstructor(provider)
}
return nil, fmt.Errorf("provider %s not found", provider.Name)
}
+10 -14
View File
@@ -1,19 +1,15 @@
module zpan
module github.com/saltbo/zpan
go 1.13
go 1.14
require (
github.com/aliyun/aliyun-oss-go-sdk v2.0.3+incompatible
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-gonic/gin v1.4.0
github.com/go-sql-driver/mysql v1.4.1
github.com/go-xorm/xorm v0.7.7
github.com/rakyll/statik v0.1.6
github.com/aliyun/aliyun-oss-go-sdk v2.1.4+incompatible
github.com/gin-gonic/gin v1.6.3
github.com/go-sql-driver/mysql v1.5.0
github.com/go-xorm/xorm v0.7.9
github.com/mitchellh/go-homedir v1.1.0
github.com/saltbo/gopkg v0.0.0-20200806124254-1f4fecb05e7c
github.com/satori/go.uuid v1.2.0
github.com/storyicon/grbac v0.0.0-20190801030200-88ce5668903d
github.com/urfave/cli v1.22.1
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 // indirect
gopkg.in/yaml.v2 v2.2.2
github.com/spf13/cobra v1.0.0
github.com/spf13/viper v1.7.1
)
+345 -67
View File
@@ -1,85 +1,189 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aliyun/aliyun-oss-go-sdk v2.0.3+incompatible h1:724q2AmQ3m1mrdD9kYqK5+1+Zr77vS21jdQ9iF9t4b8=
github.com/aliyun/aliyun-oss-go-sdk v2.0.3+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/aliyun-oss-go-sdk v1.9.8 h1:BOflvK0Zs/zGmoabyFIzTg5c3kguktWTXEwewwbuba0=
github.com/aliyun/aliyun-oss-go-sdk v2.1.4+incompatible h1:6t4QUhWVCcnilQy+d3aO0zsaO7wH6N3geMo4wgbC4MY=
github.com/aliyun/aliyun-oss-go-sdk v2.1.4+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bxcodec/faker/v3 v3.1.0 h1:VCCPusvvk1My6RjWFnqVbh6EdHDqjWmrHJCHduUksV0=
github.com/bxcodec/faker/v3 v3.1.0/go.mod h1:gF31YgnMSMKgkvl+fyEo1xuSMbEuieyqfeslGYFjneM=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
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/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
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.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
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.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=
github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
github.com/go-xorm/xorm v0.7.7 h1:JssDY9KgQja7oAUkrpmb7qZL+ZrIIuKg5HzcPeVCDb4=
github.com/go-xorm/xorm v0.7.7/go.mod h1:BS8F0smoUxtyUqKnAtvoQecDRNs8SruHci62u9lRAJQ=
github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc=
github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/jinzhu/gorm v1.9.15 h1:OdR1qFvtXktlxk73XFYMiYn9ywzTwytqe4QkuMRqc38=
github.com/jinzhu/gorm v1.9.15/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
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.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@@ -87,147 +191,321 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs=
github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/robfig/cron v1.1.0 h1:jk4/Hud3TTdcrJgUOBgsqrZBarcxl6ADIjSC2iniwLY=
github.com/robfig/cron v1.1.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/saltbo/gopkg v0.0.0-20200804053751-43b380cef46a h1:4UKaAelPC8gURCKX5X2eZYtB7HkPE46XW3OHP6rKDvc=
github.com/saltbo/gopkg v0.0.0-20200804053751-43b380cef46a/go.mod h1:/6/9WzUMdFS1vpASkEoEleG7jM8/eHU3pRJc+w4kDhU=
github.com/saltbo/gopkg v0.0.0-20200805142521-0f999bd67f80 h1:8THMNe3rzdVXcdGuFlRoorh/GkerP/BbJWAM/DOXC5A=
github.com/saltbo/gopkg v0.0.0-20200805142521-0f999bd67f80/go.mod h1:/6/9WzUMdFS1vpASkEoEleG7jM8/eHU3pRJc+w4kDhU=
github.com/saltbo/gopkg v0.0.0-20200806124254-1f4fecb05e7c h1:BYyMFkrmjI1ylQOe/QleN4wY2kC2N6zcUo4gKyRNUCI=
github.com/saltbo/gopkg v0.0.0-20200806124254-1f4fecb05e7c/go.mod h1:/6/9WzUMdFS1vpASkEoEleG7jM8/eHU3pRJc+w4kDhU=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/storyicon/grbac v0.0.0-20190801030200-88ce5668903d h1:uJdRQHM+dxUvZoByaARemXMYE7DWpIxjah+ue/bV55M=
github.com/storyicon/grbac v0.0.0-20190801030200-88ce5668903d/go.mod h1:bkwjmFAuumy2DsLyhHzgPWC6hEFjevSmI7iabVzBJ7A=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed h1:uPxWBzB3+mlnjy9W58qY1j/cjyFjutgw/Vhan2zLy/A=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200805065543-0cf7623e9dbd h1:wefLe/3g5tC0FcXw3NneLA5tHgbyouyZlfcSjNfOdgk=
golang.org/x/sys v0.0.0-20200805065543-0cf7623e9dbd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0 h1:xQwXv67TxFo9nC1GJFyab5eq/5B590r6RlnL/G8Sz7w=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468 h1:fTfk6GjmihJbK0mSUFgPPgYpsdmApQ86Mcd4GuKax9U=
golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA=
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
xorm.io/builder v0.3.6-0.20190906062455-b937eb46ecfb h1:2idZcp79ldX5qLeQ6WKCdS7aEFNOMvQc9wrtt5hSRwM=
xorm.io/builder v0.3.6-0.20190906062455-b937eb46ecfb/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
xorm.io/core v0.7.0 h1:hKxuOKWZNeiFQsSuGet/KV8HZ788hclvAl+7azx3tkM=
xorm.io/core v0.7.0/go.mod h1:TuOJjIVa7e3w/rN8tDcAvuLBMtwzdHPbyOzE6Gk1EUI=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb h1:msX3zG3BPl8Ti+LDzP33/9K7BzO/WqFXk610K1kYKfo=
xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
+28
View File
@@ -0,0 +1,28 @@
/*
Copyright © 2020 Ambor <saltbo@foxmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
package main
import "github.com/saltbo/zpan/cmd"
func main() {
cmd.Execute()
}
-86
View File
@@ -1,86 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ginx
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
type Result struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data,omitempty"`
}
func (r *Result) Error() string {
if r.Code != 0 {
return r.Msg
}
return ""
}
func (r *Result) Unwrap() error {
return fmt.Errorf("%w", r.Msg)
}
func RError(code int, err error) error {
return &Result{Code: code, Msg: err.Error()}
}
func RWithData(code int, msg string, data interface{}) error {
return &Result{
Code: code,
Msg: msg,
Data: data,
}
}
func Error(err error) error {
return RError(http.StatusBadRequest, err)
}
func Unauthorized(err error) error {
return RError(http.StatusUnauthorized, err)
}
func Forbidden(err error) error {
return RError(http.StatusForbidden, err)
}
func Failed(err error) error {
return RError(http.StatusInternalServerError, err)
}
type ListData struct {
List interface{} `json:"list"`
TotalNum int64 `json:"total_num"`
}
func Ok(c *gin.Context) error {
c.JSON(http.StatusOK, RError(0, fmt.Errorf("ok")))
return nil
}
func Json(c *gin.Context, data interface{}) error {
c.JSON(http.StatusOK, RWithData(0, "ok", data))
return nil
}
func JsonList(c *gin.Context, list interface{}, totalNum int64) error {
c.JSON(http.StatusOK, &ListData{list, totalNum})
return nil
}
func Redirect(c *gin.Context, location string) {
c.Redirect(http.StatusMovedPermanently, location)
}
func Cookie(c *gin.Context, name, value string) {
c.SetCookie(name, value, 7*24*3600, "/", "", false, false)
}
-63
View File
@@ -1,63 +0,0 @@
package ginx
import (
"errors"
"log"
"net/http"
"github.com/gin-gonic/gin"
)
type HandlerFunc func(c *gin.Context) error
type Router struct {
rg *gin.RouterGroup
}
func NewRouter(rg *gin.RouterGroup) *Router {
return &Router{rg: rg}
}
func (r *Router) wrapper(handler HandlerFunc) func(c *gin.Context) {
return func(c *gin.Context) {
err := handler(c)
if err == nil {
return
}
log.Println(err)
var ret *Result
if errors.As(err, &ret) {
c.AbortWithStatusJSON(ret.Code, ret)
return
}
_ = c.AbortWithError(http.StatusNotImplemented, err)
}
}
func (r *Router) Use(middlewares ...HandlerFunc) {
for _, middleware := range middlewares {
r.rg.Use(r.wrapper(middleware))
}
}
func (r *Router) GET(relativePath string, handler HandlerFunc) {
r.rg.GET(relativePath, r.wrapper(handler))
}
func (r *Router) POST(relativePath string, handler HandlerFunc) {
r.rg.POST(relativePath, r.wrapper(handler))
}
func (r *Router) DELETE(relativePath string, handler HandlerFunc) {
r.rg.DELETE(relativePath, r.wrapper(handler))
}
func (r *Router) PATCH(relativePath string, handler HandlerFunc) {
r.rg.PATCH(relativePath, r.wrapper(handler))
}
func (r *Router) PUT(relativePath string, handler HandlerFunc) {
r.rg.PUT(relativePath, r.wrapper(handler))
}
+1 -1
View File
@@ -1,4 +1,4 @@
package api
package bind
type QueryPage struct {
Offset int `form:"offset"`
+84 -54
View File
@@ -1,4 +1,4 @@
package api
package rest
import (
"fmt"
@@ -6,10 +6,13 @@ import (
"github.com/gin-gonic/gin"
"zpan/dao"
"zpan/disk"
"zpan/model"
"zpan/pkg/ginx"
"github.com/saltbo/gopkg/ginutil"
"github.com/saltbo/zpan/dao"
"github.com/saltbo/zpan/disk"
"github.com/saltbo/zpan/model"
"github.com/saltbo/zpan/rest/bind"
)
var docTypes = []string{
@@ -34,18 +37,18 @@ const (
)
type FileResource struct {
provider disk.Provider
bucketName string
provider disk.Provider
}
func NewFileResource(rs *RestServer) Resource {
func NewFileResource(bucketName string, provider disk.Provider) ginutil.Resource {
return &FileResource{
provider: rs.provider,
bucketName: rs.conf.Provider.Bucket,
bucketName: bucketName,
provider: provider,
}
}
func (f *FileResource) Register(router *ginx.Router) {
func (f *FileResource) Register(router *gin.RouterGroup) {
router.GET("/files", f.findAll)
router.POST("/files/callback", f.fileCallback)
router.POST("/files/operation", f.fileOperation)
@@ -55,10 +58,11 @@ func (f *FileResource) Register(router *ginx.Router) {
router.POST("/folders", f.createFolder)
}
func (f *FileResource) findAll(c *gin.Context) error {
p := new(QueryFiles)
func (f *FileResource) findAll(c *gin.Context) {
p := new(bind.QueryFiles)
if err := c.BindQuery(p); err != nil {
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
list := make([]model.Matter, 0)
@@ -78,16 +82,18 @@ func (f *FileResource) findAll(c *gin.Context) error {
sn := dao.DB.Where(query, params...).Limit(p.Limit, p.Offset)
total, err := sn.Desc("dirtype").Asc("id").FindAndCount(&list)
if err != nil {
return ginx.Error(err)
ginutil.JSONServerError(c, err)
return
}
return ginx.JsonList(c, list, total)
ginutil.JSONList(c, list, total)
}
func (f *FileResource) findFolders(c *gin.Context) error {
p := new(QueryFolder)
func (f *FileResource) findFolders(c *gin.Context) {
p := new(bind.QueryFolder)
if err := c.BindQuery(p); err != nil {
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
list := make([]model.Matter, 0)
@@ -95,21 +101,24 @@ func (f *FileResource) findFolders(c *gin.Context) error {
sn := dao.DB.Where(query, c.GetInt64("uid"), DIRTYPE_USER, p.Parent)
total, err := sn.Limit(p.Limit, p.Offset).FindAndCount(&list)
if err != nil {
return ginx.Error(err)
ginutil.JSONServerError(c, err)
return
}
return ginx.JsonList(c, list, total)
ginutil.JSONList(c, list, total)
}
func (f *FileResource) createFolder(c *gin.Context) error {
p := new(BodyFolder)
func (f *FileResource) createFolder(c *gin.Context) {
p := new(bind.BodyFolder)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
uid := c.GetInt64("uid")
if !dao.DirExist(uid, p.Dir) {
return ginx.Error(fmt.Errorf("direction %s not exist.", p.Dir))
ginutil.JSONBadRequest(c, fmt.Errorf("direction %s not exist", p.Dir))
return
}
m := model.Matter{
@@ -119,30 +128,36 @@ func (f *FileResource) createFolder(c *gin.Context) error {
Parent: p.Dir,
}
if _, err := dao.DB.Insert(m); err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
return ginx.Json(c, "")
ginutil.JSON(c)
}
func (f *FileResource) fileCallback(c *gin.Context) error {
p := new(BodyFile)
func (f *FileResource) fileCallback(c *gin.Context) {
p := new(bind.BodyFile)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
user := new(model.User)
if exist, err := dao.DB.Id(p.Uid).Get(user); err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
} else if !exist {
return ginx.Error(fmt.Errorf("user not exist."))
ginutil.JSONBadRequest(c, fmt.Errorf("user not exist"))
return
}
exist, err := dao.DB.Where("object=?", p.Object).Exist(&model.Matter{})
if err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
} else if exist {
return ginx.Error(fmt.Errorf("object %s already exist.", p.Object))
ginutil.JSONBadRequest(c, fmt.Errorf("object %s already exist.", p.Object))
return
}
session := dao.DB.NewSession()
@@ -158,32 +173,37 @@ func (f *FileResource) fileCallback(c *gin.Context) error {
}
if _, err := session.Insert(m); err != nil {
_ = session.Rollback()
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
// update the storage
user.StorageUsed += uint64(p.Size)
if _, err := session.ID(p.Uid).Cols("storage_used").Update(user); err != nil {
_ = session.Rollback()
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
if err := session.Commit(); err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
return ginx.Json(c, "")
ginutil.JSON(c)
}
func (f *FileResource) fileOperation(c *gin.Context) error {
p := new(BodyFileOperation)
func (f *FileResource) fileOperation(c *gin.Context) {
p := new(bind.BodyFileOperation)
if err := c.ShouldBindJSON(p); err != nil {
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
file, err := dao.FileGet(c.GetInt64("uid"), p.Id)
if err != nil {
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
switch p.Action {
@@ -194,15 +214,18 @@ func (f *FileResource) fileOperation(c *gin.Context) error {
case OPERATION_RENAME:
if file.Dirtype > 0 {
if err := dao.DirRename(file.Id, p.Dest); err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
return ginx.Json(c, "")
ginutil.JSON(c)
return
}
err = f.provider.TagRename(f.bucketName, file.Object, p.Dest)
if err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
err = dao.FileRename(file.Id, p.Dest)
default:
@@ -210,28 +233,32 @@ func (f *FileResource) fileOperation(c *gin.Context) error {
}
if err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
return ginx.Json(c, "")
ginutil.JSON(c)
}
func (f *FileResource) delete(c *gin.Context) error {
func (f *FileResource) delete(c *gin.Context) {
uid := c.GetInt64("uid")
fileId := c.Param("id")
user := new(model.User)
if _, err := dao.DB.Id(uid).Get(user); err != nil {
return ginx.Failed(err)
ginutil.JSONBadRequest(c, err)
return
}
file, err := dao.FileGet(uid, fileId)
if err != nil {
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
if err := f.provider.DeleteObject(f.bucketName, file.Object); err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
session := dao.DB.NewSession()
@@ -240,19 +267,22 @@ func (f *FileResource) delete(c *gin.Context) error {
// delete for the list
if _, err := session.ID(file.Id).Delete(file); err != nil {
_ = session.Rollback()
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
// update the user storage
user.StorageUsed -= uint64(file.Size)
if _, err := session.ID(file.Uid).Cols("storage_used").Update(user); err != nil {
_ = session.Rollback()
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
if err := session.Commit(); err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
return ginx.Ok(c)
ginutil.JSON(c)
}
+162
View File
@@ -0,0 +1,162 @@
package rest
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
"github.com/saltbo/gopkg/ginutil"
"github.com/saltbo/gopkg/randutil"
"github.com/saltbo/zpan/dao"
"github.com/saltbo/zpan/model"
"github.com/saltbo/zpan/rest/bind"
)
type ShareResource struct {
}
func NewShareResource() ginutil.Resource {
return &ShareResource{}
}
func (rs *ShareResource) Register(router *gin.RouterGroup) {
router.GET("/shares/:alias", rs.find)
router.GET("/shares", rs.findAll)
router.POST("/shares", rs.create)
router.PATCH("/shares/:alias", rs.update)
router.DELETE("/shares/:alias", rs.delete)
}
func (rs *ShareResource) find(c *gin.Context) {
secret := c.Query("secret")
share := new(model.Share)
if exist, err := dao.DB.Where("alias=?", c.Param("alias")).Get(share); err != nil {
ginutil.JSONServerError(c, err)
return
} else if !exist {
ginutil.JSONBadRequest(c, fmt.Errorf("share not found"))
return
} else if secret == "" && share.Secret != "" {
ginutil.JSONData(c, gin.H{"k": "please submit secret."})
return
} else if share.Secret != secret {
ginutil.JSONBadRequest(c, fmt.Errorf("invalid secret"))
return
} else if time.Now().After(share.ExpireAt) {
ginutil.JSONBadRequest(c, fmt.Errorf("share expired"))
return
}
matter := new(model.Matter)
if exist, err := dao.DB.Id(share.MatterId).Get(matter); err != nil {
ginutil.JSONServerError(c, err)
return
} else if !exist {
ginutil.JSONBadRequest(c, fmt.Errorf("matter not exist"))
return
}
ginutil.JSONData(c, matter)
}
func (rs *ShareResource) findAll(c *gin.Context) {
p := new(bind.QueryPage)
if err := c.BindQuery(p); err != nil {
ginutil.JSONBadRequest(c, err)
return
}
list := make([]model.Share, 0)
sn := dao.DB.Limit(p.Limit, p.Offset)
total, err := sn.FindAndCount(&list)
if err != nil {
ginutil.JSONBadRequest(c, err)
return
}
ginutil.JSONList(c, list, total)
}
func (rs *ShareResource) create(c *gin.Context) {
p := new(bind.BodyShare)
if err := c.ShouldBindJSON(p); err != nil {
ginutil.JSONBadRequest(c, err)
return
}
matter := new(model.Matter)
if exist, err := dao.DB.Id(p.MId).Get(matter); err != nil {
ginutil.JSONBadRequest(c, err)
return
} else if !exist {
ginutil.JSONBadRequest(c, fmt.Errorf("matter not found"))
return
}
m := model.Share{
Alias: randutil.RandString(12),
Uid: c.GetInt64("uid"),
MatterId: matter.Id,
Name: matter.Name,
ExpireAt: time.Now().Add(time.Second * time.Duration(p.ExpireSec)),
}
if p.Private {
m.Secret = randutil.RandString(5)
}
if _, err := dao.DB.Insert(m); err != nil {
ginutil.JSONServerError(c, err)
return
}
ginutil.JSONData(c, m)
}
func (rs *ShareResource) update(c *gin.Context) {
p := new(bind.BodyShare)
if err := c.ShouldBindJSON(p); err != nil {
ginutil.JSONBadRequest(c, err)
return
}
share := new(model.Share)
if exist, err := dao.DB.Id(p.Id).Get(share); err != nil {
ginutil.JSONBadRequest(c, err)
return
} else if !exist {
ginutil.JSONBadRequest(c, fmt.Errorf("share not found"))
return
}
if p.Private && share.Secret == "" {
share.Secret = randutil.RandString(5)
}
if _, err := dao.DB.Id(share.Id).Update(share); err != nil {
ginutil.JSONServerError(c, err)
return
}
ginutil.JSON(c)
}
func (rs *ShareResource) delete(c *gin.Context) {
alias := c.Param("alias")
share := new(model.Share)
if exist, err := dao.DB.Where("alias=?", alias).Get(share); err != nil {
ginutil.JSONBadRequest(c, err)
return
} else if !exist {
ginutil.JSONBadRequest(c, fmt.Errorf("matter not found"))
}
if _, err := dao.DB.Id(share.Id).Delete(share); err != nil {
ginutil.JSONServerError(c, err)
return
}
ginutil.JSON(c)
}
+33 -26
View File
@@ -1,4 +1,4 @@
package api
package rest
import (
"bytes"
@@ -8,12 +8,14 @@ import (
"log"
"github.com/gin-gonic/gin"
"github.com/satori/go.uuid"
"github.com/saltbo/gopkg/ginutil"
uuid "github.com/satori/go.uuid"
"zpan/dao"
"zpan/disk"
"zpan/model"
"zpan/pkg/ginx"
"github.com/saltbo/zpan/config"
"github.com/saltbo/zpan/dao"
"github.com/saltbo/zpan/disk"
"github.com/saltbo/zpan/model"
"github.com/saltbo/zpan/rest/bind"
)
type URLResource struct {
@@ -23,43 +25,45 @@ type URLResource struct {
CallbackHost string
}
func NewURLResource(rs *RestServer) Resource {
func NewURLResource(conf *config.Config, provider disk.Provider) ginutil.Resource {
return &URLResource{
provider: rs.provider,
bucketName: rs.conf.Provider.Bucket,
StorageHost: rs.conf.StoreHost,
CallbackHost: rs.conf.SiteHost,
provider: provider,
bucketName: conf.Provider.Bucket,
StorageHost: conf.StoreHost,
CallbackHost: conf.SiteHost,
}
}
func (rs *URLResource) Register(router *ginx.Router) {
func (rs *URLResource) Register(router *gin.RouterGroup) {
router.GET("/urls/store-host", rs.storeHost)
router.GET("/urls/upload", rs.uploadURL)
router.GET("/urls/download/:id", rs.downloadURL)
}
func (rs *URLResource) storeHost(c *gin.Context) error {
return ginx.Json(c, map[string]string{
func (rs *URLResource) storeHost(c *gin.Context) {
ginutil.JSONData(c, gin.H{
"host": rs.StorageHost,
})
}
func (rs *URLResource) uploadURL(c *gin.Context) error {
p := new(QueryMatter)
func (rs *URLResource) uploadURL(c *gin.Context) {
p := new(bind.QueryMatter)
if err := c.ShouldBindQuery(p); err != nil {
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
uid := c.GetInt64("uid")
user := new(model.User)
if _, err := dao.DB.Id(uid).Get(user); err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
} else if user.StorageUsed+uint64(p.Size) >= user.StorageMax {
return ginx.Error(fmt.Errorf("storage not enough space"))
ginutil.JSONBadRequest(c, fmt.Errorf("storage not enough space"))
}
if !dao.DirExist(uid, p.Dir) {
return ginx.Error(fmt.Errorf("direction %s not exist.", p.Dir))
ginutil.JSONBadRequest(c, fmt.Errorf("direction %s not exist.", p.Dir))
}
publicRead := false
@@ -75,31 +79,34 @@ func (rs *URLResource) uploadURL(c *gin.Context) error {
callback := rs.buildCallback(uid, p.Name, p.Type, p.Dir, object)
url, headers, err := rs.provider.UploadURL(rs.bucketName, p.Name, object, p.Type, callback, publicRead)
if err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
return ginx.Json(c, map[string]interface{}{
ginutil.JSONData(c, gin.H{
"url": url,
"object": object,
"headers": headers,
})
}
func (rs *URLResource) downloadURL(c *gin.Context) error {
func (rs *URLResource) downloadURL(c *gin.Context) {
uid := c.GetInt64("uid")
fileId := c.Param("id")
file, err := dao.FileGet(uid, fileId)
if err != nil {
return ginx.Error(err)
ginutil.JSONBadRequest(c, err)
return
}
url, err := rs.provider.DownloadURL(rs.bucketName, file.Object)
if err != nil {
return ginx.Failed(err)
ginutil.JSONServerError(c, err)
return
}
return ginx.Json(c, map[string]string{
ginutil.JSONData(c, gin.H{
"url": url,
})
}
+53
View File
@@ -0,0 +1,53 @@
package rest
import (
"github.com/gin-gonic/gin"
"github.com/saltbo/gopkg/ginutil"
"github.com/saltbo/zpan/dao"
"github.com/saltbo/zpan/model"
"github.com/saltbo/zpan/rest/bind"
)
type UserResource struct {
}
func NewUserResource() ginutil.Resource {
return &UserResource{}
}
func (rs *UserResource) Register(router *gin.RouterGroup) {
router.GET("/users", rs.findAll)
router.GET("/users/:uid", rs.find)
}
func (rs *UserResource) findAll(c *gin.Context) {
p := new(bind.QueryUser)
if err := c.BindQuery(p); err != nil {
ginutil.JSONBadRequest(c, err)
return
}
list := make([]model.User, 0)
total, err := dao.DB.Limit(p.Limit, p.Offset).FindAndCount(&list)
if err != nil {
ginutil.JSONBadRequest(c, err)
return
}
ginutil.JSONList(c, list, total)
}
func (rs *UserResource) find(c *gin.Context) {
userId := c.Param("uid")
user := new(model.User)
if _, err := dao.DB.Id(userId).Get(user); err != nil {
ginutil.JSONBadRequest(c, err)
return
}
user.Password = ""
ginutil.JSONData(c, user)
}
-21
View File
@@ -1,21 +0,0 @@
#!/usr/bin/env bash
set -e
PROJ="zpan"
# git info
COMMIT=git-$(git rev-parse --short HEAD || echo "GitNotFound")
GIT_REPO=$(git config --get remote.origin.url)
RELEASE=$(git describe --tags | awk -F '-' '{print $1}')
# Set GO_LDFLAGS="-s" for building without symbols for debugging.
LDFLAGS="$LDFLAGS -X ${PROJ}/version.release=${RELEASE} -X ${PROJ}/version.commit=${COMMIT} -X ${PROJ}/version.repo=${GIT_REPO}"
function main() {
out="bin"
go build $BUILD_FLAGS \
-ldflags "$LDFLAGS" \
-o "${out}/${PROJ}" ${PROJ}/cmd || return
}
main
-40
View File
@@ -1,40 +0,0 @@
#!/bin/sh
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[1;34m'
YELLOW='\033[1;33m'
DARK='\033[1;30m'
NC='\033[0m'
unameOut="$(uname -s)"
ROOT_DIR=$(dirname $(cd "$(dirname "$0")";pwd))
TARGET_DIR=${ROOT_DIR}"/bin"
case "${unameOut}" in
Darwin*) arch=macos
bin_dir="/usr/local/bin"
;;
*) arch=amd64
bin_dir="${HOME}/bin"
;;
esac
echo "Directory: [${bin_dir}]"
test ! -d ${bin_dir} && mkdir ${bin_dir}
cp ${TARGET_DIR}/zpan ${bin_dir}
cp ${ROOT_DIR}/developments/config.yaml.tpl /etc/zpan/config.yaml
if [ $? -eq 0 ]
then
echo "${GREEN}"
echo "Installation completed successfully."
echo "$ zpan --version"
${bin_dir}/zpan --version
else
echo "${RED}"
echo "Failed installing zpan"
fi
echo "${NC}"
-46
View File
@@ -1,46 +0,0 @@
#!/usr/bin/env bash
PROJ="zpan"
RELEASE=$(git describe --tags | awk -F '-' '{print $1}')
function package() {
local target=${1}
local srcdir="bin"
cp "${srcdir}/${PROJ}" "${target}/${PROJ}"
cp ../developments/* "${target}"
cp ../README.md "${target}"/README.md
cp ../CHANGELOG.md "${target}"/CHANGELOG.md
rm -r ${srcdir}
return
}
function main() {
ROOT_DIR=$(dirname $(cd "$(dirname "$0")";pwd))
TARGET_DIR=${ROOT_DIR}"/build"
echo ${TARGET_DIR}
mkdir -p ${TARGET_DIR}
cd ${TARGET_DIR}
TARGET_ARCH="amd64"
for os in darwin windows linux; do
export GOOS=${os}
export GOARCH=${TARGET_ARCH}
LDFLAGS="-s" ${ROOT_DIR}/scripts/build.sh
TARGET="${PROJ}-${RELEASE}-${GOOS}-${GOARCH}"
mkdir "${TARGET}"
package "${TARGET}"
if [ ${GOOS} == "linux" ]; then
tar cfz "${TARGET}.tar.gz" "${TARGET}"
else
zip -qr "${TARGET}.zip" "${TARGET}"
fi
echo "Wrote ${TARGET}.tar.gz"
done
}
main
-19
View File
@@ -1,19 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package version
import "fmt"
var (
// RELEASE returns the release version
release = "unknown"
// REPO returns the git repository URL
repo = "unknown"
// COMMIT returns the short sha from git
commit = "unknown"
Short = fmt.Sprintf("%s", release)
Long = fmt.Sprintf("release: %s, repo: %s, commit: %s", release, repo, commit)
)