Compare commits
213 Commits
v1.1.1
...
gpg-encryp
Author | SHA1 | Date | |
---|---|---|---|
|
a7b600c562 | ||
|
715f2b4cd9 | ||
|
597554a59e | ||
|
368431fb6b | ||
|
e3bb49993c | ||
|
cff0a88bf3 | ||
|
492731e31f | ||
|
f062af9fc5 | ||
|
2fbd19365c | ||
|
5932a194b2 | ||
|
acd6fb084f | ||
|
6f49951bc0 | ||
|
d2a0e77814 | ||
|
014b95ff07 | ||
|
0eec27586d | ||
|
c7164856d2 | ||
|
96723b2685 | ||
|
4a56bad05f | ||
|
fa74be02d2 | ||
|
2959fc2992 | ||
|
694b3ce246 | ||
|
92948fab23 | ||
|
e5455d9713 | ||
|
6ab75b30e5 | ||
|
6b8eff8322 | ||
|
2da62eb235 | ||
|
156daa5a24 | ||
|
e97fdcb293 | ||
|
9fe5f9a5c9 | ||
|
788dfa203f | ||
|
440ccf3a57 | ||
|
224f8dca42 | ||
|
d16a6cc25e | ||
|
49a083d8a2 | ||
|
f38eb6fb77 | ||
|
8f5a0a80f3 | ||
|
a895846bbc | ||
|
3990c3c5b7 | ||
|
3ea4ffd0e3 | ||
|
88003018e6 | ||
|
9baefbc4bf | ||
|
e9148e0c20 | ||
|
2ecedb93c6 | ||
|
1ff9ccd01e | ||
|
ca17555c22 | ||
|
a7a3e685fe | ||
|
94f94f27ff | ||
|
69d979ca48 | ||
|
d9198e78db | ||
|
811679f8fb | ||
|
ce0c9b8915 | ||
|
c93c585566 | ||
|
0eb3ba9ff8 | ||
|
1594f95abf | ||
|
8f6e44b212 | ||
|
2e2c07c3a0 | ||
|
9df18fdc69 | ||
|
49c6d7ee4f | ||
|
fdfd453222 | ||
|
b5ffdb5095 | ||
|
0512452111 | ||
|
16e94447f7 | ||
|
941ec1fe0f | ||
|
6bd3e97186 | ||
|
31ef712847 | ||
|
4daca97f89 | ||
|
69519d8fa4 | ||
|
a90d29700d | ||
|
f9700ee237 | ||
|
3ed153904d | ||
|
b36711c1ea | ||
|
27f84e719a | ||
|
0b075554ff | ||
|
eaf048690a | ||
|
4dbc2a8085 | ||
|
644c3a1d23 | ||
|
ccef887edf | ||
|
32621f7dd3 | ||
|
7ea65c5a24 | ||
|
27bf261844 | ||
|
7522cf5f31 | ||
|
ba21621d66 | ||
|
7cf688e86e | ||
|
1abc7cfb0e | ||
|
be6dea2546 | ||
|
d554eb8e2d | ||
|
fe9e699255 | ||
|
d3b6fd5e90 | ||
|
322310d47f | ||
|
76fd83b599 | ||
|
d3381a2293 | ||
|
6ac6c8fa99 | ||
|
2721ece759 | ||
|
e53d599e09 | ||
|
bfb34634de | ||
|
614a7fd23d | ||
|
fb8f0e781b | ||
|
06d87eac8e | ||
|
580cd8d978 | ||
|
111f1794fb | ||
|
b372dc97b5 | ||
|
ae00f8ceaa | ||
|
ec9db7a42e | ||
|
bcf40c15a2 | ||
|
fe1cde6614 | ||
|
e182376100 | ||
|
4a2fce4344 | ||
|
ffb2cf0011 | ||
|
085e9f8414 | ||
|
a4bf102d29 | ||
|
c8497fb27d | ||
|
68212d100e | ||
|
18f4b42cf1 | ||
|
92055f1b3c | ||
|
9430e53689 | ||
|
a26b32dd86 | ||
|
0a6b5817a9 | ||
|
5e7e3a1b39 | ||
|
42adceb4c6 | ||
|
f909ad3ce2 | ||
|
d830bf1afc | ||
|
f366e8217e | ||
|
8a5c737140 | ||
|
b920eb842a | ||
|
45e0967a37 | ||
|
28614c991d | ||
|
ef28bcb28f | ||
|
2dd23bff3c | ||
|
663c59e754 | ||
|
c89b7f56c2 | ||
|
8bb5094ccc | ||
|
b7deec505b | ||
|
c62e5757ef | ||
|
38d643e12b | ||
|
0411db0443 | ||
|
3b1b5b890a | ||
|
fb3d410004 | ||
|
924275e45b | ||
|
fdca501681 | ||
|
38996c9c7f | ||
|
9f9e1b66b2 | ||
|
b3f2319cba | ||
|
867471aa9c | ||
|
96fb3cbe61 | ||
|
800dd6658e | ||
|
2e33f7c716 | ||
|
5fab83bde8 | ||
|
dcaf572f3d | ||
|
ce4e015641 | ||
|
9c15248adc | ||
|
d8e1248949 | ||
|
7dc3982c77 | ||
|
82617a25c1 | ||
|
fcbddcee17 | ||
|
8b92f5bac4 | ||
|
8aa835f21f | ||
|
9297c253aa | ||
|
b22a410a78 | ||
|
229482fa3d | ||
|
f22bfbf804 | ||
|
8ecc94ed7c | ||
|
0ee2dfd7e1 | ||
|
b90bd685e7 | ||
|
73e2bca999 | ||
|
d09f4fdd81 | ||
|
356412c838 | ||
|
6b249d21c4 | ||
|
c0d5d99e5d | ||
|
9e27485bbb | ||
|
b8ca25fb9b | ||
|
bef766f605 | ||
|
c2a152c622 | ||
|
c403f9a2f1 | ||
|
8d196039ba | ||
|
11ad244584 | ||
|
060dbdf152 | ||
|
2127b4f1e4 | ||
|
8c5ef8f2e1 | ||
|
b40e9d1fb8 | ||
|
071ecb491c | ||
|
806006a0b8 | ||
|
76f00c5d04 | ||
|
6b148d3574 | ||
|
6eabb248da | ||
|
5bb0e84fa5 | ||
|
1e815d382c | ||
|
cf00af36f2 | ||
|
79c5130066 | ||
|
c1691c58dc | ||
|
d5e07fe2f4 | ||
|
874268d80e | ||
|
59014f2110 | ||
|
6c54ddccef | ||
|
a2ab63c7b8 | ||
|
11dffa22b4 | ||
|
758cd881ce | ||
|
f00209973e | ||
|
0b27adba9d | ||
|
8463d4169b | ||
|
e340ad9e45 | ||
|
4e288cc4e4 | ||
|
ee7011d55b | ||
|
6c73f13fd0 | ||
|
e5b5ee7140 | ||
|
6a956a445f | ||
|
ce7e008bb3 | ||
|
b9a03ac180 | ||
|
cccbaac6d7 | ||
|
a09bcf3bf0 | ||
|
0b23354473 | ||
|
372ff5f368 | ||
|
fbf9a4facc | ||
|
0c844c1f11 |
33
.github/build/friendly-filenames.json
vendored
Normal file
33
.github/build/friendly-filenames.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"android-arm64": { "friendlyName": "android-arm64-v8a" },
|
||||||
|
"darwin-amd64": { "friendlyName": "darwin-amd64" },
|
||||||
|
"darwin-arm64": { "friendlyName": "darwin-arm64" },
|
||||||
|
"dragonfly-amd64": { "friendlyName": "dragonfly-amd64" },
|
||||||
|
"freebsd-386": { "friendlyName": "freebsd-386" },
|
||||||
|
"freebsd-amd64": { "friendlyName": "freebsd-amd64" },
|
||||||
|
"freebsd-arm64": { "friendlyName": "freebsd-arm64-v8a" },
|
||||||
|
"freebsd-arm7": { "friendlyName": "freebsd-arm32-v7a" },
|
||||||
|
"linux-386": { "friendlyName": "linux-386" },
|
||||||
|
"linux-amd64": { "friendlyName": "linux-amd64" },
|
||||||
|
"linux-arm5": { "friendlyName": "linux-arm32-v5" },
|
||||||
|
"linux-arm64": { "friendlyName": "linux-arm64-v8a" },
|
||||||
|
"linux-arm6": { "friendlyName": "linux-arm32-v6" },
|
||||||
|
"linux-arm7": { "friendlyName": "linux-armv7" },
|
||||||
|
"linux-mips64le": { "friendlyName": "linux-mips64le" },
|
||||||
|
"linux-mips64": { "friendlyName": "linux-mips64" },
|
||||||
|
"linux-mipslesoftfloat": { "friendlyName": "linux-mips32le-softfloat" },
|
||||||
|
"linux-mipsle": { "friendlyName": "linux-mips32le" },
|
||||||
|
"linux-mipssoftfloat": { "friendlyName": "linux-mips32-softfloat" },
|
||||||
|
"linux-mips": { "friendlyName": "linux-mips32" },
|
||||||
|
"linux-ppc64le": { "friendlyName": "linux-ppc64le" },
|
||||||
|
"linux-ppc64": { "friendlyName": "linux-ppc64" },
|
||||||
|
"linux-riscv64": { "friendlyName": "linux-riscv64" },
|
||||||
|
"linux-s390x": { "friendlyName": "linux-s390x" },
|
||||||
|
"openbsd-386": { "friendlyName": "openbsd-386" },
|
||||||
|
"openbsd-amd64": { "friendlyName": "openbsd-amd64" },
|
||||||
|
"openbsd-arm64": { "friendlyName": "openbsd-arm64-v8a" },
|
||||||
|
"openbsd-arm7": { "friendlyName": "openbsd-arm32-v7a" },
|
||||||
|
"windows-386": { "friendlyName": "windows-386" },
|
||||||
|
"windows-amd64": { "friendlyName": "windows-amd64" },
|
||||||
|
"windows-arm7": { "friendlyName": "windows-arm32-v7a" }
|
||||||
|
}
|
89
.github/workflows/build-docker-images.yml
vendored
Normal file
89
.github/workflows/build-docker-images.yml
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
name: deploy multi-architecture Docker images for transfer.sh with buildx
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *' # everyday at midnight UTC
|
||||||
|
pull_request:
|
||||||
|
branches: main
|
||||||
|
push:
|
||||||
|
branches: main
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
buildx:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
-
|
||||||
|
name: Prepare
|
||||||
|
id: prepare
|
||||||
|
run: |
|
||||||
|
DOCKER_IMAGE=dutchcoders/transfer.sh
|
||||||
|
DOCKER_PLATFORMS=linux/amd64,linux/arm/v7,linux/arm64,linux/386
|
||||||
|
VERSION=edge
|
||||||
|
|
||||||
|
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||||
|
VERSION=v${GITHUB_REF#refs/tags/v}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||||
|
VERSION=nightly
|
||||||
|
fi
|
||||||
|
|
||||||
|
TAGS="--tag ${DOCKER_IMAGE}:${VERSION}"
|
||||||
|
|
||||||
|
if [ $VERSION = edge -o $VERSION = nightly ]; then
|
||||||
|
TAGS="$TAGS --tag ${DOCKER_IMAGE}:latest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ::set-output name=docker_image::${DOCKER_IMAGE}
|
||||||
|
echo ::set-output name=version::${VERSION}
|
||||||
|
echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} \
|
||||||
|
--build-arg VERSION=${VERSION} \
|
||||||
|
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
|
||||||
|
--build-arg VCS_REF=${GITHUB_SHA::8} \
|
||||||
|
${TAGS} .
|
||||||
|
-
|
||||||
|
name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
with:
|
||||||
|
platforms: all
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
-
|
||||||
|
name: Available platforms
|
||||||
|
run: echo ${{ steps.buildx.outputs.platforms }}
|
||||||
|
-
|
||||||
|
name: Docker Buildx (build)
|
||||||
|
run: |
|
||||||
|
docker buildx build --no-cache --pull --output "type=image,push=false" ${{ steps.prepare.outputs.buildx_args }}
|
||||||
|
-
|
||||||
|
name: Docker Login
|
||||||
|
if: success() && github.event_name != 'pull_request'
|
||||||
|
env:
|
||||||
|
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin
|
||||||
|
-
|
||||||
|
name: Docker Buildx (push)
|
||||||
|
if: success() && github.event_name != 'pull_request'
|
||||||
|
run: |
|
||||||
|
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }}
|
||||||
|
-
|
||||||
|
name: Docker Check Manifest
|
||||||
|
if: always() && github.event_name != 'pull_request'
|
||||||
|
run: |
|
||||||
|
docker run --rm mplatform/mquery ${{ steps.prepare.outputs.docker_image }}:${{ steps.prepare.outputs.version }}
|
||||||
|
-
|
||||||
|
name: Clear
|
||||||
|
if: always() && github.event_name != 'pull_request'
|
||||||
|
run: |
|
||||||
|
rm -f ${HOME}/.docker/config.json
|
171
.github/workflows/release.yml
vendored
Normal file
171
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
name: Build and Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
# Include amd64 on all platforms.
|
||||||
|
goos: [windows, freebsd, openbsd, linux, dragonfly, darwin]
|
||||||
|
goarch: [amd64, 386]
|
||||||
|
exclude:
|
||||||
|
# Exclude i386 on darwin and dragonfly.
|
||||||
|
- goarch: 386
|
||||||
|
goos: dragonfly
|
||||||
|
- goarch: 386
|
||||||
|
goos: darwin
|
||||||
|
include:
|
||||||
|
# BEIGIN MacOS ARM64
|
||||||
|
- goos: darwin
|
||||||
|
goarch: arm64
|
||||||
|
# END MacOS ARM64
|
||||||
|
# BEGIN Linux ARM 5 6 7
|
||||||
|
- goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
- goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: 6
|
||||||
|
- goos: linux
|
||||||
|
goarch: arm
|
||||||
|
goarm: 5
|
||||||
|
# END Linux ARM 5 6 7
|
||||||
|
# BEGIN Android ARM 8
|
||||||
|
- goos: android
|
||||||
|
goarch: arm64
|
||||||
|
# END Android ARM 8
|
||||||
|
# Windows ARM 7
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
# BEGIN Other architectures
|
||||||
|
# BEGIN riscv64 & ARM64
|
||||||
|
- goos: linux
|
||||||
|
goarch: arm64
|
||||||
|
- goos: linux
|
||||||
|
goarch: riscv64
|
||||||
|
# END riscv64 & ARM64
|
||||||
|
# BEGIN MIPS
|
||||||
|
- goos: linux
|
||||||
|
goarch: mips64
|
||||||
|
- goos: linux
|
||||||
|
goarch: mips64le
|
||||||
|
- goos: linux
|
||||||
|
goarch: mipsle
|
||||||
|
- goos: linux
|
||||||
|
goarch: mips
|
||||||
|
# END MIPS
|
||||||
|
# BEGIN PPC
|
||||||
|
- goos: linux
|
||||||
|
goarch: ppc64
|
||||||
|
- goos: linux
|
||||||
|
goarch: ppc64le
|
||||||
|
# END PPC
|
||||||
|
# BEGIN FreeBSD ARM
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm64
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
# END FreeBSD ARM
|
||||||
|
# BEGIN S390X
|
||||||
|
- goos: linux
|
||||||
|
goarch: s390x
|
||||||
|
# END S390X
|
||||||
|
# END Other architectures
|
||||||
|
# BEGIN OPENBSD ARM
|
||||||
|
- goos: openbsd
|
||||||
|
goarch: arm64
|
||||||
|
- goos: openbsd
|
||||||
|
goarch: arm
|
||||||
|
goarm: 7
|
||||||
|
# END OPENBSD ARM
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
GOARM: ${{ matrix.goarm }}
|
||||||
|
CGO_ENABLED: 0
|
||||||
|
steps:
|
||||||
|
- name: Checkout codebase
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Show workflow information
|
||||||
|
id: get_filename
|
||||||
|
run: |
|
||||||
|
export _NAME=$(jq ".[\"$GOOS-$GOARCH$GOARM$GOMIPS\"].friendlyName" -r < .github/build/friendly-filenames.json)
|
||||||
|
echo "GOOS: $GOOS, GOARCH: $GOARCH, GOARM: $GOARM, GOMIPS: $GOMIPS, RELEASE_NAME: $_NAME"
|
||||||
|
echo "::set-output name=ASSET_NAME::$_NAME"
|
||||||
|
echo "::set-output name=GIT_TAG::${GITHUB_REF##*/}"
|
||||||
|
echo "ASSET_NAME=$_NAME" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ^1.17
|
||||||
|
|
||||||
|
- name: Get project dependencies
|
||||||
|
run: go mod download
|
||||||
|
|
||||||
|
- name: Build Transfersh
|
||||||
|
run: |
|
||||||
|
mkdir -p build_assets
|
||||||
|
go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=${GITHUB_REF##*/} -a -s -w -extldflags '-static'" -o build_assets/transfersh-${GITHUB_REF##*/}-${ASSET_NAME}
|
||||||
|
|
||||||
|
- name: Build Mips softfloat Transfersh
|
||||||
|
if: matrix.goarch == 'mips' || matrix.goarch == 'mipsle'
|
||||||
|
run: |
|
||||||
|
GOMIPS=softfloat go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=${GITHUB_REF##*/} -a -s -w -extldflags '-static'" -o build_assets/transfersh-softfloat-${GITHUB_REF##*/}-${ASSET_NAME}
|
||||||
|
|
||||||
|
- name: Rename Windows Transfersh
|
||||||
|
if: matrix.goos == 'windows'
|
||||||
|
run: |
|
||||||
|
cd ./build_assets || exit 1
|
||||||
|
mv transfersh-${GITHUB_REF##*/}-${ASSET_NAME} transfersh-${GITHUB_REF##*/}-${ASSET_NAME}.exe
|
||||||
|
|
||||||
|
- name: Prepare to release
|
||||||
|
run: |
|
||||||
|
cp ${GITHUB_WORKSPACE}/README.md ./build_assets/README.md
|
||||||
|
cp ${GITHUB_WORKSPACE}/LICENSE ./build_assets/LICENSE
|
||||||
|
|
||||||
|
- name: Create Gzip archive
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
pushd build_assets || exit 1
|
||||||
|
touch -mt $(date +%Y01010000) *
|
||||||
|
tar zcvf transfersh-${GITHUB_REF##*/}-${ASSET_NAME}.tar.gz *
|
||||||
|
mv transfersh-${GITHUB_REF##*/}-${ASSET_NAME}.tar.gz ../
|
||||||
|
FILE=`find . -name "transfersh-${GITHUB_REF##*/}-${ASSET_NAME}*"`
|
||||||
|
DGST=$FILE.sha256sum
|
||||||
|
echo `sha256sum $FILE` > $DGST
|
||||||
|
popd || exit 1
|
||||||
|
FILE=./transfersh-${GITHUB_REF##*/}-${ASSET_NAME}.tar.gz
|
||||||
|
DGST=$FILE.sha256sum
|
||||||
|
echo `sha256sum $FILE` > $DGST
|
||||||
|
|
||||||
|
- name: Change the name
|
||||||
|
run: |
|
||||||
|
mv build_assets transfersh-${GITHUB_REF##*/}-${ASSET_NAME}
|
||||||
|
|
||||||
|
- name: Upload files to Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}
|
||||||
|
path: |
|
||||||
|
./transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}/*
|
||||||
|
|
||||||
|
- name: Upload binaries to release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
if: github.event_name == 'release'
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
./transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}.tar.gz*
|
||||||
|
./transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}/transfersh-${{ steps.get_filename.outputs.GIT_TAG }}-${{ steps.get_filename.outputs.ASSET_NAME }}*
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
45
.github/workflows/test.yml
vendored
Normal file
45
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: test
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
go_version:
|
||||||
|
- 1.13.x
|
||||||
|
- 1.14.x
|
||||||
|
- 1.15.x
|
||||||
|
- 1.16.x
|
||||||
|
- 1.17.x
|
||||||
|
name: Test with ${{ matrix.go_version }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-go@v1
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go_version }}
|
||||||
|
- name: Vet and test
|
||||||
|
run: |
|
||||||
|
go version
|
||||||
|
go vet ./...
|
||||||
|
go test ./...
|
||||||
|
golangci:
|
||||||
|
name: Linting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-go@v1
|
||||||
|
with:
|
||||||
|
go-version: 1.17
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v2
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
skip-go-installation: true
|
||||||
|
args: "--config .golangci.yml"
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -20,3 +20,5 @@ transfersh-server/run.sh
|
|||||||
.elasticbeanstalk/*
|
.elasticbeanstalk/*
|
||||||
!.elasticbeanstalk/*.cfg.yml
|
!.elasticbeanstalk/*.cfg.yml
|
||||||
!.elasticbeanstalk/*.global.yml
|
!.elasticbeanstalk/*.global.yml
|
||||||
|
|
||||||
|
!.github/build/
|
||||||
|
24
.golangci.yml
Normal file
24
.golangci.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
run:
|
||||||
|
deadline: 10m
|
||||||
|
issues-exit-code: 1
|
||||||
|
tests: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
format: colored-line-number
|
||||||
|
print-issued-lines: true
|
||||||
|
print-linter-name: true
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable:
|
||||||
|
- deadcode
|
||||||
|
- unused
|
||||||
|
|
||||||
|
issues:
|
||||||
|
max-issues-per-linter: 0
|
||||||
|
max-same-issues: 0
|
||||||
|
new: false
|
||||||
|
exclude-use-default: false
|
||||||
|
exclude-rules:
|
||||||
|
- linters:
|
||||||
|
- staticcheck
|
||||||
|
text: "SA1019:"
|
21
.travis.yml
21
.travis.yml
@@ -1,21 +0,0 @@
|
|||||||
language: go
|
|
||||||
sudo: false
|
|
||||||
|
|
||||||
os:
|
|
||||||
- linux
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.9.x
|
|
||||||
- 1.10.x
|
|
||||||
- 1.11.x
|
|
||||||
- 1.12.x
|
|
||||||
- tip
|
|
||||||
|
|
||||||
install:
|
|
||||||
- echo "This is an override of the default install deps step in travis."
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go get -t -u -v ./...
|
|
||||||
- go build -v .
|
|
||||||
- go vet ./...
|
|
||||||
- go test ./...
|
|
25
CODE_OF_CONDUCT.md
Normal file
25
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
# Contributor Code of Conduct
|
||||||
|
|
||||||
|
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||||
|
|
||||||
|
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery
|
||||||
|
* Personal attacks
|
||||||
|
* Trolling or insulting/derogatory comments
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing other's private information, such as physical or electronic addresses, without explicit permission
|
||||||
|
* Other unethical or unprofessional conduct
|
||||||
|
* Use of harsh language
|
||||||
|
|
||||||
|
Project maintainers 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. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
|
||||||
|
|
||||||
|
This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant] (https://www.contributor-covenant.org), version 1.2.0, available at https://www.contributor-covenant.org/version/1/2/0/code-of-conduct.html
|
||||||
|
|
@@ -1,5 +1,5 @@
|
|||||||
# Default to Go 1.12
|
# Default to Go 1.17
|
||||||
ARG GO_VERSION=1.12
|
ARG GO_VERSION=1.17
|
||||||
FROM golang:${GO_VERSION}-alpine as build
|
FROM golang:${GO_VERSION}-alpine as build
|
||||||
|
|
||||||
# Necessary to run 'go get' and to compile the linked binary
|
# Necessary to run 'go get' and to compile the linked binary
|
||||||
@@ -12,7 +12,7 @@ WORKDIR /go/src/github.com/dutchcoders/transfer.sh
|
|||||||
ENV GO111MODULE=on
|
ENV GO111MODULE=on
|
||||||
|
|
||||||
# build & install server
|
# build & install server
|
||||||
RUN go get -u ./... && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags -a -tags netgo -ldflags '-w -extldflags "-static"' -o /go/bin/transfersh github.com/dutchcoders/transfer.sh
|
RUN CGO_ENABLED=0 go build -tags netgo -ldflags "-X github.com/dutchcoders/transfer.sh/cmd.Version=$(git describe --tags) -a -s -w -extldflags '-static'" -o /go/bin/transfersh
|
||||||
|
|
||||||
FROM scratch AS final
|
FROM scratch AS final
|
||||||
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>"
|
LABEL maintainer="Andrea Spacca <andrea.spacca@gmail.com>"
|
||||||
|
2
LICENSE
2
LICENSE
@@ -1,6 +1,8 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2018 DutchCoders [https://github.com/dutchcoders/]
|
Copyright (c) 2014-2018 DutchCoders [https://github.com/dutchcoders/]
|
||||||
|
Copyright (c) 2018-2020 Andrea Spacca.
|
||||||
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
5
Makefile
Normal file
5
Makefile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.PHONY: lint
|
||||||
|
|
||||||
|
lint:
|
||||||
|
golangci-lint run --out-format=github-actions --config .golangci.yml
|
||||||
|
|
230
README.md
230
README.md
@@ -1,13 +1,12 @@
|
|||||||
# transfer.sh [](https://gitter.im/dutchcoders/transfer.sh?utm_source=badge&utm_medium=badge&utm_campaign=&utm_campaign=pr-badge&utm_content=badge) [](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [](https://hub.docker.com/r/dutchcoders/transfer.sh/) [](https://travis-ci.org/dutchcoders/transfer.sh)
|
# transfer.sh [](https://goreportcard.com/report/github.com/dutchcoders/transfer.sh) [](https://hub.docker.com/r/dutchcoders/transfer.sh/) [](https://github.com/dutchcoders/transfer.sh/actions/workflows/test.yml?query=branch%3Amain)
|
||||||
|
|
||||||
Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance.
|
Easy and fast file sharing from the command-line. This code contains the server with everything you need to create your own instance.
|
||||||
|
|
||||||
Transfer.sh currently supports the s3 (Amazon S3), gdrive (Google Drive) providers, and local file system (local).
|
Transfer.sh currently supports the s3 (Amazon S3), gdrive (Google Drive), storj (Storj) providers, and local file system (local).
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
This project repository has no relation with the service at https://transfer.sh that's managed by https://storj.io.
|
|
||||||
So far we cannot address any issue related to the service at https://transfer.sh.
|
|
||||||
|
|
||||||
|
The service at transfersh.com is of unknown origin and reported as cloud malware.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -16,17 +15,17 @@ So far we cannot address any issue related to the service at https://transfer.sh
|
|||||||
$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt
|
$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### Encrypt & upload:
|
### Encrypt & Upload:
|
||||||
```bash
|
```bash
|
||||||
$ cat /tmp/hello.txt|gpg -ac -o-|curl -X PUT --upload-file "-" https://transfer.sh/test.txt
|
$ cat /tmp/hello.txt|gpg -ac -o-|curl -X PUT --upload-file "-" https://transfer.sh/test.txt
|
||||||
````
|
````
|
||||||
|
|
||||||
### Download & decrypt:
|
### Download & Decrypt:
|
||||||
```bash
|
```bash
|
||||||
$ curl https://transfer.sh/1lDau/test.txt|gpg -o- > /tmp/hello.txt
|
$ curl https://transfer.sh/1lDau/test.txt|gpg -o- > /tmp/hello.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### Upload to virustotal:
|
### Upload to Virustotal:
|
||||||
```bash
|
```bash
|
||||||
$ curl -X PUT --upload-file nhgbhhj https://transfer.sh/test.txt/virustotal
|
$ curl -X PUT --upload-file nhgbhhj https://transfer.sh/test.txt/virustotal
|
||||||
```
|
```
|
||||||
@@ -48,86 +47,29 @@ $ curl --upload-file ./hello.txt https://transfer.sh/hello.txt -H "Max-Downloads
|
|||||||
$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt -H "Max-Days: 1" # Set the number of days before deletion
|
$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt -H "Max-Days: 1" # Set the number of days before deletion
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### X-Encrypt-Password
|
||||||
|
```bash
|
||||||
|
$ curl --upload-file ./hello.txt https://transfer.sh/hello.txt -H "X-Encrypt-Password: test" # Encrypt the content sever side with AES265 using "test" as password
|
||||||
|
```
|
||||||
|
|
||||||
|
### X-Decrypt-Password
|
||||||
|
```bash
|
||||||
|
$ curl https://transfer.sh/BAYh0/hello.txt -H "X-Decrypt-Password: test" # Decrypt the content sever side with AES265 using "test" as password
|
||||||
|
```
|
||||||
|
|
||||||
## Response Headers
|
## Response Headers
|
||||||
|
|
||||||
### X-Url-Delete
|
### X-Url-Delete
|
||||||
|
|
||||||
The URL used to request the deletion of a file. Returned as a response header.
|
The URL used to request the deletion of a file and returned as a response header.
|
||||||
```bash
|
```bash
|
||||||
curl -sD - --upload-file ./hello https://transfer.sh/hello.txt | grep 'X-Url-Delete'
|
curl -sD - --upload-file ./hello https://transfer.sh/hello.txt | grep 'X-Url-Delete'
|
||||||
X-Url-Delete: https://transfer.sh/hello.txt/BAYh0/hello.txt/PDw0NHPcqU
|
X-Url-Delete: https://transfer.sh/hello.txt/BAYh0/hello.txt/PDw0NHPcqU
|
||||||
```
|
```
|
||||||
|
|
||||||
## Add alias to .bashrc or .zshrc
|
## Examples
|
||||||
|
|
||||||
### Using curl
|
See good usage examples on [examples.md](examples.md)
|
||||||
```bash
|
|
||||||
transfer() {
|
|
||||||
curl --progress-bar --upload-file "$1" https://transfer.sh/$(basename "$1") | tee /dev/null;
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
alias transfer=transfer
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using wget
|
|
||||||
```bash
|
|
||||||
transfer() {
|
|
||||||
wget -t 1 -qO - --method=PUT --body-file="$1" --header="Content-Type: $(file -b --mime-type "$1")" https://transfer.sh/$(basename "$1");
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
alias transfer=transfer
|
|
||||||
```
|
|
||||||
|
|
||||||
## Add alias for fish-shell
|
|
||||||
|
|
||||||
### Using curl
|
|
||||||
```fish
|
|
||||||
function transfer --description 'Upload a file to transfer.sh'
|
|
||||||
if [ $argv[1] ]
|
|
||||||
# write to output to tmpfile because of progress bar
|
|
||||||
set -l tmpfile ( mktemp -t transferXXX )
|
|
||||||
curl --progress-bar --upload-file "$argv[1]" https://transfer.sh/(basename $argv[1]) >> $tmpfile
|
|
||||||
cat $tmpfile
|
|
||||||
command rm -f $tmpfile
|
|
||||||
else
|
|
||||||
echo 'usage: transfer FILE_TO_TRANSFER'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
funcsave transfer
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using wget
|
|
||||||
```fish
|
|
||||||
function transfer --description 'Upload a file to transfer.sh'
|
|
||||||
if [ $argv[1] ]
|
|
||||||
wget -t 1 -qO - --method=PUT --body-file="$argv[1]" --header="Content-Type: (file -b --mime-type $argv[1])" https://transfer.sh/(basename $argv[1])
|
|
||||||
else
|
|
||||||
echo 'usage: transfer FILE_TO_TRANSFER'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
funcsave transfer
|
|
||||||
```
|
|
||||||
|
|
||||||
Now run it like this:
|
|
||||||
```bash
|
|
||||||
$ transfer test.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
## Add alias on Windows
|
|
||||||
|
|
||||||
Put a file called `transfer.cmd` somewhere in your PATH with this inside it:
|
|
||||||
```cmd
|
|
||||||
@echo off
|
|
||||||
setlocal
|
|
||||||
:: use env vars to pass names to PS, to avoid escaping issues
|
|
||||||
set FN=%~nx1
|
|
||||||
set FULL=%1
|
|
||||||
powershell -noprofile -command "$(Invoke-Webrequest -Method put -Infile $Env:FULL https://transfer.sh/$Env:FN).Content"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Link aliases
|
## Link aliases
|
||||||
|
|
||||||
@@ -143,39 +85,52 @@ https://transfer.sh/1lDau/test.txt --> https://transfer.sh/inline/1lDau/test.txt
|
|||||||
|
|
||||||
Parameter | Description | Value | Env
|
Parameter | Description | Value | Env
|
||||||
--- | --- | --- | ---
|
--- | --- | --- | ---
|
||||||
listener | port to use for http (:80) | |
|
listener | port to use for http (:80) | | LISTENER |
|
||||||
profile-listener | port to use for profiler (:6060)| |
|
profile-listener | port to use for profiler (:6060) | | PROFILE_LISTENER |
|
||||||
force-https | redirect to https | false |
|
force-https | redirect to https | false | FORCE_HTTPS
|
||||||
tls-listener | port to use for https (:443) | |
|
tls-listener | port to use for https (:443) | | TLS_LISTENER |
|
||||||
tls-listener-only | flag to enable tls listener only | |
|
tls-listener-only | flag to enable tls listener only | | TLS_LISTENER_ONLY |
|
||||||
tls-cert-file | path to tls certificate | |
|
tls-cert-file | path to tls certificate | | TLS_CERT_FILE |
|
||||||
tls-private-key | path to tls private key | |
|
tls-private-key | path to tls private key | | TLS_PRIVATE_KEY |
|
||||||
http-auth-user | user for basic http auth on upload | |
|
http-auth-user | user for basic http auth on upload | | HTTP_AUTH_USER |
|
||||||
http-auth-pass | pass for basic http auth on upload | |
|
http-auth-pass | pass for basic http auth on upload | | HTTP_AUTH_PASS |
|
||||||
ip-whitelist | comma separated list of ips allowed to connect to the service | |
|
ip-whitelist | comma separated list of ips allowed to connect to the service | | IP_WHITELIST |
|
||||||
ip-blacklist | comma separated list of ips not allowed to connect to the service | |
|
ip-blacklist | comma separated list of ips not allowed to connect to the service | | IP_BLACKLIST |
|
||||||
temp-path | path to temp folder | system temp |
|
temp-path | path to temp folder | system temp | TEMP_PATH |
|
||||||
web-path | path to static web files (for development or custom front end) | |
|
web-path | path to static web files (for development or custom front end) | | WEB_PATH |
|
||||||
proxy-path | path prefix when service is run behind a proxy | |
|
proxy-path | path prefix when service is run behind a proxy | | PROXY_PATH |
|
||||||
ga-key | google analytics key for the front end | |
|
proxy-port | port of the proxy when the service is run behind a proxy | | PROXY_PORT |
|
||||||
uservoice-key | user voice key for the front end | |
|
email-contact | email contact for the front end | | EMAIL_CONTACT |
|
||||||
provider | which storage provider to use | (s3, grdrive or local) |
|
ga-key | google analytics key for the front end | | GA_KEY |
|
||||||
aws-access-key | aws access key | | AWS_ACCESS_KEY
|
provider | which storage provider to use | (s3, storj, gdrive or local) |
|
||||||
aws-secret-key | aws access key | | AWS_SECRET_KEY
|
uservoice-key | user voice key for the front end | | USERVOICE_KEY |
|
||||||
bucket | aws bucket | | BUCKET
|
aws-access-key | aws access key | | AWS_ACCESS_KEY |
|
||||||
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION
|
aws-secret-key | aws access key | | AWS_SECRET_KEY |
|
||||||
s3-no-multipart | disables s3 multipart upload | false | |
|
bucket | aws bucket | | BUCKET |
|
||||||
s3-path-style | Forces path style URLs, required for Minio. | false | |
|
s3-endpoint | Custom S3 endpoint. | | S3_ENDPOINT |
|
||||||
basedir | path storage for local/gdrive provider| |
|
s3-region | region of the s3 bucket | eu-west-1 | S3_REGION |
|
||||||
gdrive-client-json-filepath | path to oauth client json config for gdrive provider| |
|
s3-no-multipart | disables s3 multipart upload | false | S3_NO_MULTIPART |
|
||||||
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| |
|
s3-path-style | Forces path style URLs, required for Minio. | false | S3_PATH_STYLE |
|
||||||
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | |
|
storj-access | Access for the project | | STORJ_ACCESS |
|
||||||
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | |
|
storj-bucket | Bucket to use within the project | | STORJ_BUCKET |
|
||||||
log | path to log file| |
|
basedir | path storage for local/gdrive provider | | BASEDIR |
|
||||||
|
gdrive-client-json-filepath | path to oauth client json config for gdrive provider | | GDRIVE_CLIENT_JSON_FILEPATH |
|
||||||
|
gdrive-local-config-path | path to store local transfer.sh config cache for gdrive provider| | GDRIVE_LOCAL_CONFIG_PATH |
|
||||||
|
gdrive-chunk-size | chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB) | | GDRIVE_CHUNK_SIZE |
|
||||||
|
lets-encrypt-hosts | hosts to use for lets encrypt certificates (comma seperated) | | HOSTS |
|
||||||
|
log | path to log file| | LOG |
|
||||||
|
cors-domains | comma separated list of domains for CORS, setting it enable CORS | | CORS_DOMAINS |
|
||||||
|
clamav-host | host for clamav feature | | CLAMAV_HOST |
|
||||||
|
perform-clamav-prescan | prescan every upload through clamav feature (clamav-host must be a local clamd unix socket) | | PERFORM_CLAMAV_PRESCAN |
|
||||||
|
rate-limit | request per minute | | RATE_LIMIT |
|
||||||
|
max-upload-size | max upload size in kilobytes | | MAX_UPLOAD_SIZE |
|
||||||
|
purge-days | number of days after the uploads are purged automatically | | PURGE_DAYS |
|
||||||
|
purge-interval | interval in hours to run the automatic purge for (not applicable to S3 and Storj) | | PURGE_INTERVAL |
|
||||||
|
random-token-length | length of the random token for the upload path (double the size for delete path) | 6 | RANDOM_TOKEN_LENGTH |
|
||||||
|
|
||||||
If you want to use TLS using lets encrypt certificates, set lets-encrypt-hosts to your domain, set tls-listener to :443 and enable force-https.
|
If you want to use TLS using lets encrypt certificates, set lets-encrypt-hosts to your domain, set tls-listener to :443 and enable force-https.
|
||||||
|
|
||||||
If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert=file and tls-private-key.
|
If you want to use TLS using your own certificates, set tls-listener to :443, force-https, tls-cert-file and tls-private-key.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
@@ -187,13 +142,10 @@ go run main.go --provider=local --listener :8080 --temp-path=/tmp/ --basedir=/tm
|
|||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
If on go < 1.11
|
|
||||||
```bash
|
```bash
|
||||||
go get -u -v ./...
|
$ git clone git@github.com:dutchcoders/transfer.sh.git
|
||||||
```
|
$ cd transfer.sh
|
||||||
|
$ go build -o transfersh main.go
|
||||||
```bash
|
|
||||||
go build -o transfersh main.go
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
@@ -217,7 +169,50 @@ If you specify the s3-region, you don't need to set the endpoint URL since the c
|
|||||||
|
|
||||||
### Custom S3 providers
|
### Custom S3 providers
|
||||||
|
|
||||||
To use a custom non-AWS S3 provider, you need to specify the endpoint as definied from your cloud provider.
|
To use a custom non-AWS S3 provider, you need to specify the endpoint as defined from your cloud provider.
|
||||||
|
|
||||||
|
## Storj Network Provider
|
||||||
|
|
||||||
|
To use the Storj Network as a storage provider you need to specify the following flags:
|
||||||
|
- provider `--provider storj`
|
||||||
|
- storj-access _(either via flag or environment variable STORJ_ACCESS)_
|
||||||
|
- storj-bucket _(either via flag or environment variable STORJ_BUCKET)_
|
||||||
|
|
||||||
|
### Creating Bucket and Scope
|
||||||
|
|
||||||
|
You need to create an access grant (or copy it from the uplink configuration) and a bucket in preparation.
|
||||||
|
|
||||||
|
To get started, log in to your account and go to the Access Grant Menu and start the Wizard on the upper right.
|
||||||
|
|
||||||
|
Enter your access grant name of choice, hit *Next* and restrict it as necessary/preferred.
|
||||||
|
Afterwards continue either in CLI or within the Browser. Next, you'll be asked for a Passphrase used as Encryption Key.
|
||||||
|
**Make sure to save it in a safe place. Without it, you will lose the ability to decrypt your files!**
|
||||||
|
|
||||||
|
Afterwards, you can copy the access grant and then start the startup of the transfer.sh endpoint.
|
||||||
|
It is recommended to provide both the access grant and the bucket name as ENV Variables for enhanced security.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
export STORJ_BUCKET=<BUCKET NAME>
|
||||||
|
export STORJ_ACCESS=<ACCESS GRANT>
|
||||||
|
transfer.sh --provider storj
|
||||||
|
```
|
||||||
|
|
||||||
|
## Google Drive Usage
|
||||||
|
|
||||||
|
For the usage with Google drive, you need to specify the following options:
|
||||||
|
- provider
|
||||||
|
- gdrive-client-json-filepath
|
||||||
|
- gdrive-local-config-path
|
||||||
|
- basedir
|
||||||
|
|
||||||
|
### Creating Gdrive Client Json
|
||||||
|
|
||||||
|
You need to create an OAuth Client id from console.cloud.google.com, download the file, and place it into a safe directory.
|
||||||
|
|
||||||
|
### Usage example
|
||||||
|
|
||||||
|
```go run main.go --provider gdrive --basedir /tmp/ --gdrive-client-json-filepath /[credential_dir] --gdrive-local-config-path [directory_to_save_config] ```
|
||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
|
|
||||||
@@ -235,7 +230,12 @@ Contributions are welcome.
|
|||||||
|
|
||||||
**Andrea Spacca**
|
**Andrea Spacca**
|
||||||
|
|
||||||
## Copyright and license
|
**Stefan Benten**
|
||||||
|
|
||||||
|
## Copyright and License
|
||||||
|
|
||||||
Code and documentation copyright 2011-2018 Remco Verhoef.
|
Code and documentation copyright 2011-2018 Remco Verhoef.
|
||||||
|
Code and documentation copyright 2018-2020 Andrea Spacca.
|
||||||
|
Code and documentation copyright 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
Code released under [the MIT license](LICENSE).
|
Code released under [the MIT license](LICENSE).
|
||||||
|
288
cmd/cmd.go
288
cmd/cmd.go
@@ -8,11 +8,12 @@ import (
|
|||||||
|
|
||||||
"github.com/dutchcoders/transfer.sh/server"
|
"github.com/dutchcoders/transfer.sh/server"
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/minio/cli"
|
"github.com/urfave/cli"
|
||||||
"google.golang.org/api/googleapi"
|
"google.golang.org/api/googleapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "1.1.0"
|
// Version is inject at build time
|
||||||
|
var Version = "0.0.0"
|
||||||
var helpTemplate = `NAME:
|
var helpTemplate = `NAME:
|
||||||
{{.Name}} - {{.Usage}}
|
{{.Name}} - {{.Usage}}
|
||||||
|
|
||||||
@@ -34,67 +35,92 @@ VERSION:
|
|||||||
|
|
||||||
var globalFlags = []cli.Flag{
|
var globalFlags = []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "listener",
|
Name: "listener",
|
||||||
Usage: "127.0.0.1:8080",
|
Usage: "127.0.0.1:8080",
|
||||||
Value: "127.0.0.1:8080",
|
Value: "127.0.0.1:8080",
|
||||||
|
EnvVar: "LISTENER",
|
||||||
},
|
},
|
||||||
// redirect to https?
|
// redirect to https?
|
||||||
// hostnames
|
// hostnames
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "profile-listener",
|
Name: "profile-listener",
|
||||||
Usage: "127.0.0.1:6060",
|
Usage: "127.0.0.1:6060",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "PROFILE_LISTENER",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "force-https",
|
Name: "force-https",
|
||||||
Usage: "",
|
Usage: "",
|
||||||
|
EnvVar: "FORCE_HTTPS",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "tls-listener",
|
Name: "tls-listener",
|
||||||
Usage: "127.0.0.1:8443",
|
Usage: "127.0.0.1:8443",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "TLS_LISTENER",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "tls-listener-only",
|
Name: "tls-listener-only",
|
||||||
Usage: "",
|
Usage: "",
|
||||||
|
EnvVar: "TLS_LISTENER_ONLY",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "tls-cert-file",
|
Name: "tls-cert-file",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "TLS_CERT_FILE",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "tls-private-key",
|
Name: "tls-private-key",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "TLS_PRIVATE_KEY",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "temp-path",
|
Name: "temp-path",
|
||||||
Usage: "path to temp files",
|
Usage: "path to temp files",
|
||||||
Value: os.TempDir(),
|
Value: os.TempDir(),
|
||||||
|
EnvVar: "TEMP_PATH",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "web-path",
|
Name: "web-path",
|
||||||
Usage: "path to static web files",
|
Usage: "path to static web files",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "WEB_PATH",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "proxy-path",
|
Name: "proxy-path",
|
||||||
Usage: "path prefix when service is run behind a proxy",
|
Usage: "path prefix when service is run behind a proxy",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "PROXY_PATH",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "ga-key",
|
Name: "proxy-port",
|
||||||
Usage: "key for google analytics (front end)",
|
Usage: "port of the proxy when the service is run behind a proxy",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "PROXY_PORT",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "uservoice-key",
|
Name: "email-contact",
|
||||||
Usage: "key for user voice (front end)",
|
Usage: "email address to link in Contact Us (front end)",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "EMAIL_CONTACT",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "provider",
|
Name: "ga-key",
|
||||||
Usage: "s3|gdrive|local",
|
Usage: "key for google analytics (front end)",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "GA_KEY",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "uservoice-key",
|
||||||
|
Usage: "key for user voice (front end)",
|
||||||
|
Value: "",
|
||||||
|
EnvVar: "USERVOICE_KEY",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "provider",
|
||||||
|
Usage: "s3|gdrive|local",
|
||||||
|
Value: "",
|
||||||
|
EnvVar: "PROVIDER",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "s3-endpoint",
|
Name: "s3-endpoint",
|
||||||
@@ -127,33 +153,68 @@ var globalFlags = []cli.Flag{
|
|||||||
EnvVar: "BUCKET",
|
EnvVar: "BUCKET",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "s3-no-multipart",
|
Name: "s3-no-multipart",
|
||||||
Usage: "Disables S3 Multipart Puts",
|
Usage: "Disables S3 Multipart Puts",
|
||||||
|
EnvVar: "S3_NO_MULTIPART",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "s3-path-style",
|
Name: "s3-path-style",
|
||||||
Usage: "Forces path style URLs, required for Minio.",
|
Usage: "Forces path style URLs, required for Minio.",
|
||||||
|
EnvVar: "S3_PATH_STYLE",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "gdrive-client-json-filepath",
|
Name: "gdrive-client-json-filepath",
|
||||||
Usage: "",
|
Usage: "",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "GDRIVE_CLIENT_JSON_FILEPATH",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "gdrive-local-config-path",
|
Name: "gdrive-local-config-path",
|
||||||
Usage: "",
|
Usage: "",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "GDRIVE_LOCAL_CONFIG_PATH",
|
||||||
},
|
},
|
||||||
cli.IntFlag{
|
cli.IntFlag{
|
||||||
Name: "gdrive-chunk-size",
|
Name: "gdrive-chunk-size",
|
||||||
Usage: "",
|
Usage: "",
|
||||||
Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
|
Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
|
||||||
|
EnvVar: "GDRIVE_CHUNK_SIZE",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "storj-access",
|
||||||
|
Usage: "Access for the project",
|
||||||
|
Value: "",
|
||||||
|
EnvVar: "STORJ_ACCESS",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "storj-bucket",
|
||||||
|
Usage: "Bucket to use within the project",
|
||||||
|
Value: "",
|
||||||
|
EnvVar: "STORJ_BUCKET",
|
||||||
},
|
},
|
||||||
cli.IntFlag{
|
cli.IntFlag{
|
||||||
Name: "rate-limit",
|
Name: "rate-limit",
|
||||||
Usage: "requests per minute",
|
Usage: "requests per minute",
|
||||||
Value: 0,
|
Value: 0,
|
||||||
EnvVar: "",
|
EnvVar: "RATE_LIMIT",
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "purge-days",
|
||||||
|
Usage: "number of days after uploads are purged automatically",
|
||||||
|
Value: 0,
|
||||||
|
EnvVar: "PURGE_DAYS",
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "purge-interval",
|
||||||
|
Usage: "interval in hours to run the automatic purge for",
|
||||||
|
Value: 0,
|
||||||
|
EnvVar: "PURGE_INTERVAL",
|
||||||
|
},
|
||||||
|
cli.Int64Flag{
|
||||||
|
Name: "max-upload-size",
|
||||||
|
Usage: "max limit for upload, in kilobytes",
|
||||||
|
Value: 0,
|
||||||
|
EnvVar: "MAX_UPLOAD_SIZE",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "lets-encrypt-hosts",
|
Name: "lets-encrypt-hosts",
|
||||||
@@ -162,14 +223,16 @@ var globalFlags = []cli.Flag{
|
|||||||
EnvVar: "HOSTS",
|
EnvVar: "HOSTS",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "log",
|
Name: "log",
|
||||||
Usage: "/var/log/transfersh.log",
|
Usage: "/var/log/transfersh.log",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "LOG",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "basedir",
|
Name: "basedir",
|
||||||
Usage: "path to storage",
|
Usage: "path to storage",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "BASEDIR",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "clamav-host",
|
Name: "clamav-host",
|
||||||
@@ -177,6 +240,11 @@ var globalFlags = []cli.Flag{
|
|||||||
Value: "",
|
Value: "",
|
||||||
EnvVar: "CLAMAV_HOST",
|
EnvVar: "CLAMAV_HOST",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "perform-clamav-prescan",
|
||||||
|
Usage: "perform-clamav-prescan",
|
||||||
|
EnvVar: "PERFORM_CLAMAV_PRESCAN",
|
||||||
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "virustotal-key",
|
Name: "virustotal-key",
|
||||||
Usage: "virustotal-key",
|
Usage: "virustotal-key",
|
||||||
@@ -184,39 +252,58 @@ var globalFlags = []cli.Flag{
|
|||||||
EnvVar: "VIRUSTOTAL_KEY",
|
EnvVar: "VIRUSTOTAL_KEY",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "profiler",
|
Name: "profiler",
|
||||||
Usage: "enable profiling",
|
Usage: "enable profiling",
|
||||||
|
EnvVar: "PROFILER",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "http-auth-user",
|
Name: "http-auth-user",
|
||||||
Usage: "user for http basic auth",
|
Usage: "user for http basic auth",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "HTTP_AUTH_USER",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "http-auth-pass",
|
Name: "http-auth-pass",
|
||||||
Usage: "pass for http basic auth",
|
Usage: "pass for http basic auth",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "HTTP_AUTH_PASS",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "ip-whitelist",
|
Name: "ip-whitelist",
|
||||||
Usage: "comma separated list of ips allowed to connect to the service",
|
Usage: "comma separated list of ips allowed to connect to the service",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "IP_WHITELIST",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "ip-blacklist",
|
Name: "ip-blacklist",
|
||||||
Usage: "comma separated list of ips not allowed to connect to the service",
|
Usage: "comma separated list of ips not allowed to connect to the service",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
EnvVar: "IP_BLACKLIST",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "cors-domains",
|
||||||
|
Usage: "comma separated list of domains allowed for CORS requests",
|
||||||
|
Value: "",
|
||||||
|
EnvVar: "CORS_DOMAINS",
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "random-token-length",
|
||||||
|
Usage: "",
|
||||||
|
Value: 6,
|
||||||
|
EnvVar: "RANDOM_TOKEN_LENGTH",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cmd wraps cli.app
|
||||||
type Cmd struct {
|
type Cmd struct {
|
||||||
*cli.App
|
*cli.App
|
||||||
}
|
}
|
||||||
|
|
||||||
func VersionAction(c *cli.Context) {
|
func versionAction(c *cli.Context) {
|
||||||
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line")))
|
fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh %s: Easy file sharing from the command line", Version)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New is the factory for transfer.sh
|
||||||
func New() *Cmd {
|
func New() *Cmd {
|
||||||
logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)
|
logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)
|
||||||
|
|
||||||
@@ -231,7 +318,7 @@ func New() *Cmd {
|
|||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "version",
|
Name: "version",
|
||||||
Action: VersionAction,
|
Action: versionAction,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,6 +332,10 @@ func New() *Cmd {
|
|||||||
options = append(options, server.Listener(v))
|
options = append(options, server.Listener(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v := c.String("cors-domains"); v != "" {
|
||||||
|
options = append(options, server.CorsDomains(v))
|
||||||
|
}
|
||||||
|
|
||||||
if v := c.String("tls-listener"); v == "" {
|
if v := c.String("tls-listener"); v == "" {
|
||||||
} else if c.Bool("tls-listener-only") {
|
} else if c.Bool("tls-listener-only") {
|
||||||
options = append(options, server.TLSListener(v, true))
|
options = append(options, server.TLSListener(v, true))
|
||||||
@@ -264,6 +355,14 @@ func New() *Cmd {
|
|||||||
options = append(options, server.ProxyPath(v))
|
options = append(options, server.ProxyPath(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v := c.String("proxy-port"); v != "" {
|
||||||
|
options = append(options, server.ProxyPort(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := c.String("email-contact"); v != "" {
|
||||||
|
options = append(options, server.EmailContact(v))
|
||||||
|
}
|
||||||
|
|
||||||
if v := c.String("ga-key"); v != "" {
|
if v := c.String("ga-key"); v != "" {
|
||||||
options = append(options, server.GoogleAnalytics(v))
|
options = append(options, server.GoogleAnalytics(v))
|
||||||
}
|
}
|
||||||
@@ -294,10 +393,31 @@ func New() *Cmd {
|
|||||||
options = append(options, server.ClamavHost(v))
|
options = append(options, server.ClamavHost(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v := c.Bool("perform-clamav-prescan"); v {
|
||||||
|
if c.String("clamav-host") == "" {
|
||||||
|
panic("clamav-host not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, server.PerformClamavPrescan(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := c.Int64("max-upload-size"); v > 0 {
|
||||||
|
options = append(options, server.MaxUploadSize(v))
|
||||||
|
}
|
||||||
|
|
||||||
if v := c.Int("rate-limit"); v > 0 {
|
if v := c.Int("rate-limit"); v > 0 {
|
||||||
options = append(options, server.RateLimit(v))
|
options = append(options, server.RateLimit(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v := c.Int("random-token-length")
|
||||||
|
options = append(options, server.RandomTokenLength(v))
|
||||||
|
|
||||||
|
purgeDays := c.Int("purge-days")
|
||||||
|
purgeInterval := c.Int("purge-interval")
|
||||||
|
if purgeDays > 0 && purgeInterval > 0 {
|
||||||
|
options = append(options, server.Purge(purgeDays, purgeInterval))
|
||||||
|
}
|
||||||
|
|
||||||
if cert := c.String("tls-cert-file"); cert == "" {
|
if cert := c.String("tls-cert-file"); cert == "" {
|
||||||
} else if pk := c.String("tls-private-key"); pk == "" {
|
} else if pk := c.String("tls-private-key"); pk == "" {
|
||||||
} else {
|
} else {
|
||||||
@@ -309,13 +429,13 @@ func New() *Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Bool("force-https") {
|
if c.Bool("force-https") {
|
||||||
options = append(options, server.ForceHTTPs())
|
options = append(options, server.ForceHTTPS())
|
||||||
}
|
}
|
||||||
|
|
||||||
if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
|
if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
|
||||||
} else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
|
} else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
|
||||||
} else {
|
} else {
|
||||||
options = append(options, server.HttpAuthCredentials(httpAuthUser, httpAuthPass))
|
options = append(options, server.HTTPAuthCredentials(httpAuthUser, httpAuthPass))
|
||||||
}
|
}
|
||||||
|
|
||||||
applyIPFilter := false
|
applyIPFilter := false
|
||||||
@@ -343,7 +463,7 @@ func New() *Cmd {
|
|||||||
panic("secret-key not set.")
|
panic("secret-key not set.")
|
||||||
} else if bucket := c.String("bucket"); bucket == "" {
|
} else if bucket := c.String("bucket"); bucket == "" {
|
||||||
panic("bucket not set.")
|
panic("bucket not set.")
|
||||||
} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"), c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil {
|
} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, purgeDays, c.String("s3-region"), c.String("s3-endpoint"), c.Bool("s3-no-multipart"), c.Bool("s3-path-style"), logger); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else {
|
} else {
|
||||||
options = append(options, server.UseStorage(storage))
|
options = append(options, server.UseStorage(storage))
|
||||||
@@ -351,13 +471,23 @@ func New() *Cmd {
|
|||||||
case "gdrive":
|
case "gdrive":
|
||||||
chunkSize := c.Int("gdrive-chunk-size")
|
chunkSize := c.Int("gdrive-chunk-size")
|
||||||
|
|
||||||
if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
|
if clientJSONFilepath := c.String("gdrive-client-json-filepath"); clientJSONFilepath == "" {
|
||||||
panic("client-json-filepath not set.")
|
panic("client-json-filepath not set.")
|
||||||
} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
|
} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
|
||||||
panic("local-config-path not set.")
|
panic("local-config-path not set.")
|
||||||
} else if basedir := c.String("basedir"); basedir == "" {
|
} else if basedir := c.String("basedir"); basedir == "" {
|
||||||
panic("basedir not set.")
|
panic("basedir not set.")
|
||||||
} else if storage, err := server.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
|
} else if storage, err := server.NewGDriveStorage(clientJSONFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
options = append(options, server.UseStorage(storage))
|
||||||
|
}
|
||||||
|
case "storj":
|
||||||
|
if access := c.String("storj-access"); access == "" {
|
||||||
|
panic("storj-access not set.")
|
||||||
|
} else if bucket := c.String("storj-bucket"); bucket == "" {
|
||||||
|
panic("storj-bucket not set.")
|
||||||
|
} else if storage, err := server.NewStorjStorage(access, bucket, purgeDays, logger); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
} else {
|
} else {
|
||||||
options = append(options, server.UseStorage(storage))
|
options = append(options, server.UseStorage(storage))
|
||||||
|
263
examples.md
Normal file
263
examples.md
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Aliases](#aliases)
|
||||||
|
* [Uploading and downloading](#uploading-and-downloading)
|
||||||
|
* [Archiving and backups](#archiving-and-backups)
|
||||||
|
* [Encrypting and decrypting](#encrypting-and-decrypting)
|
||||||
|
* [Scanning for viruses](#scanning-for-viruses)
|
||||||
|
* [Uploading and copy download command](#uploading-and-copy-download-command)
|
||||||
|
|
||||||
|
## Aliases
|
||||||
|
<a name="aliases"/>
|
||||||
|
|
||||||
|
## Add alias to .bashrc or .zshrc
|
||||||
|
|
||||||
|
### Using curl
|
||||||
|
```bash
|
||||||
|
transfer() {
|
||||||
|
curl --progress-bar --upload-file "$1" https://transfer.sh/$(basename "$1") | tee /dev/null;
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
alias transfer=transfer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using wget
|
||||||
|
```bash
|
||||||
|
transfer() {
|
||||||
|
wget -t 1 -qO - --method=PUT --body-file="$1" --header="Content-Type: $(file -b --mime-type "$1")" https://transfer.sh/$(basename "$1");
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
alias transfer=transfer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add alias for fish-shell
|
||||||
|
|
||||||
|
### Using curl
|
||||||
|
```fish
|
||||||
|
function transfer --description 'Upload a file to transfer.sh'
|
||||||
|
if [ $argv[1] ]
|
||||||
|
# write to output to tmpfile because of progress bar
|
||||||
|
set -l tmpfile ( mktemp -t transferXXXXXX )
|
||||||
|
curl --progress-bar --upload-file "$argv[1]" https://transfer.sh/(basename $argv[1]) >> $tmpfile
|
||||||
|
cat $tmpfile
|
||||||
|
command rm -f $tmpfile
|
||||||
|
else
|
||||||
|
echo 'usage: transfer FILE_TO_TRANSFER'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
funcsave transfer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using wget
|
||||||
|
```fish
|
||||||
|
function transfer --description 'Upload a file to transfer.sh'
|
||||||
|
if [ $argv[1] ]
|
||||||
|
wget -t 1 -qO - --method=PUT --body-file="$argv[1]" --header="Content-Type: (file -b --mime-type $argv[1])" https://transfer.sh/(basename $argv[1])
|
||||||
|
else
|
||||||
|
echo 'usage: transfer FILE_TO_TRANSFER'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
funcsave transfer
|
||||||
|
```
|
||||||
|
|
||||||
|
Now run it like this:
|
||||||
|
```bash
|
||||||
|
$ transfer test.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add alias on Windows
|
||||||
|
|
||||||
|
Put a file called `transfer.cmd` somewhere in your PATH with this inside it:
|
||||||
|
```cmd
|
||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
:: use env vars to pass names to PS, to avoid escaping issues
|
||||||
|
set FN=%~nx1
|
||||||
|
set FULL=%1
|
||||||
|
powershell -noprofile -command "$(Invoke-Webrequest -Method put -Infile $Env:FULL https://transfer.sh/$Env:FN).Content"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uploading and Downloading
|
||||||
|
<a name="uploading-and-downloading"/>
|
||||||
|
|
||||||
|
### Uploading with wget
|
||||||
|
```bash
|
||||||
|
$ wget --method PUT --body-file=/tmp/file.tar https://transfer.sh/file.tar -O - -nv
|
||||||
|
```
|
||||||
|
|
||||||
|
### Uploading with PowerShell
|
||||||
|
```posh
|
||||||
|
PS H:\> invoke-webrequest -method put -infile .\file.txt https://transfer.sh/file.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Upload using HTTPie
|
||||||
|
```bash
|
||||||
|
$ http https://transfer.sh/ -vv < /tmp/test.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Uploading a filtered text file
|
||||||
|
```bash
|
||||||
|
$ grep 'pound' /var/log/syslog | curl --upload-file - https://transfer.sh/pound.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### Downloading with curl
|
||||||
|
```bash
|
||||||
|
$ curl https://transfer.sh/1lDau/test.txt -o test.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Downloading with wget
|
||||||
|
```bash
|
||||||
|
$ wget https://transfer.sh/1lDau/test.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Archiving and backups
|
||||||
|
<a name="archiving-and-backups"/>
|
||||||
|
|
||||||
|
### Backup, encrypt and transfer a MySQL dump
|
||||||
|
```bash
|
||||||
|
$ mysqldump --all-databases | gzip | gpg -ac -o- | curl -X PUT --upload-file "-" https://transfer.sh/test.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Archive and upload directory
|
||||||
|
```bash
|
||||||
|
$ tar -czf - /var/log/journal | curl --upload-file - https://transfer.sh/journal.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
### Uploading multiple files at once
|
||||||
|
```bash
|
||||||
|
$ curl -i -F filedata=@/tmp/hello.txt -F filedata=@/tmp/hello2.txt https://transfer.sh/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Combining downloads as zip or tar.gz archive
|
||||||
|
```bash
|
||||||
|
$ curl https://transfer.sh/(15HKz/hello.txt,15HKz/hello.txt).tar.gz
|
||||||
|
$ curl https://transfer.sh/(15HKz/hello.txt,15HKz/hello.txt).zip
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transfer and send email with link (using an alias)
|
||||||
|
```bash
|
||||||
|
$ transfer /tmp/hello.txt | mail -s "Hello World" user@yourmaildomain.com
|
||||||
|
```
|
||||||
|
## Encrypting and decrypting
|
||||||
|
<a name="encrypting-and-decrypting"/>
|
||||||
|
|
||||||
|
### Encrypting files with password using gpg
|
||||||
|
```bash
|
||||||
|
$ cat /tmp/hello.txt | gpg -ac -o- | curl -X PUT --upload-file "-" https://transfer.sh/test.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Downloading and decrypting
|
||||||
|
```bash
|
||||||
|
$ curl https://transfer.sh/1lDau/test.txt | gpg -o- > /tmp/hello.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Import keys from [keybase](https://keybase.io/)
|
||||||
|
```bash
|
||||||
|
$ keybase track [them] # Encrypt for recipient(s)
|
||||||
|
$ cat somebackupfile.tar.gz | keybase encrypt [them] | curl --upload-file '-' https://transfer.sh/test.txt # Decrypt
|
||||||
|
$ curl https://transfer.sh/sqUFi/test.md | keybase decrypt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scanning for viruses
|
||||||
|
<a name="scanning-for-viruses"/>
|
||||||
|
|
||||||
|
### Scan for malware or viruses using Clamav
|
||||||
|
```bash
|
||||||
|
$ wget http://www.eicar.org/download/eicar.com
|
||||||
|
$ curl -X PUT --upload-file ./eicar.com https://transfer.sh/eicar.com/scan
|
||||||
|
```
|
||||||
|
|
||||||
|
### Upload malware to VirusTotal, get a permalink in return
|
||||||
|
```bash
|
||||||
|
$ curl -X PUT --upload-file nhgbhhj https://transfer.sh/test.txt/virustotal
|
||||||
|
```
|
||||||
|
## Uploading and copy download command
|
||||||
|
|
||||||
|
Download commands can be automatically copied to the clipboard after files are uploaded using transfer.sh.
|
||||||
|
|
||||||
|
It was designed for Linux or macOS.
|
||||||
|
|
||||||
|
### 1. Install xclip or xsel for Linux, macOS skips this step
|
||||||
|
|
||||||
|
- install xclip see https://command-not-found.com/xclip
|
||||||
|
|
||||||
|
- install xsel see https://command-not-found.com/xsel
|
||||||
|
|
||||||
|
Install later, add pbcopy and pbpaste to .bashrc or .zshrc or its equivalent.
|
||||||
|
|
||||||
|
- If use xclip, paste the following lines:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
alias pbcopy='xclip -selection clipboard'
|
||||||
|
alias pbpaste='xclip -selection clipboard -o'
|
||||||
|
```
|
||||||
|
|
||||||
|
- If use xsel, paste the following lines:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
alias pbcopy='xsel --clipboard --input'
|
||||||
|
alias pbpaste='xsel --clipboard --output'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Add Uploading and copy download command shell function
|
||||||
|
|
||||||
|
1. Open .bashrc or .zshrc or its equivalent.
|
||||||
|
|
||||||
|
2. Add the following shell script:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
transfer() {
|
||||||
|
curl --progress-bar --upload-file "$1" https://transfer.sh/$(basename "$1") | pbcopy;
|
||||||
|
echo "1) Download link:"
|
||||||
|
echo "$(pbpaste)"
|
||||||
|
|
||||||
|
echo "\n2) Linux or macOS download command:"
|
||||||
|
linux_macos_download_command="wget $(pbpaste)"
|
||||||
|
echo $linux_macos_download_command
|
||||||
|
|
||||||
|
echo "\n3) Windows download command:"
|
||||||
|
windows_download_command="Invoke-WebRequest -Uri "$(pbpaste)" -OutFile $(basename $1)"
|
||||||
|
echo $windows_download_command
|
||||||
|
|
||||||
|
case $2 in
|
||||||
|
l|m) echo $linux_macos_download_command | pbcopy
|
||||||
|
;;
|
||||||
|
w) echo $windows_download_command | pbcopy
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### 3. Test
|
||||||
|
|
||||||
|
The transfer command has two parameters:
|
||||||
|
|
||||||
|
1. The first parameter is the path to upload the file.
|
||||||
|
|
||||||
|
2. The second parameter indicates which system's download command is copied. optional:
|
||||||
|
|
||||||
|
- This parameter is empty to copy the download link.
|
||||||
|
|
||||||
|
- `l` or `m` copy the Linux or macOS command that downloaded the file.
|
||||||
|
|
||||||
|
- `w` copy the Windows command that downloaded the file.
|
||||||
|
|
||||||
|
For example, The command to download the file on Windows will be copied:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ transfer ~/temp/a.log w
|
||||||
|
######################################################################## 100.0%
|
||||||
|
1) Download link:
|
||||||
|
https://transfer.sh/y0qr2c/a.log
|
||||||
|
|
||||||
|
2) Linux or macOS download command:
|
||||||
|
wget https://transfer.sh/y0qr2c/a.log
|
||||||
|
|
||||||
|
3) Windows download command:
|
||||||
|
Invoke-WebRequest -Uri https://transfer.sh/y0qr2c/a.log -OutFile a.log
|
||||||
|
```
|
41
flake.lock
generated
Normal file
41
flake.lock
generated
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1631561581,
|
||||||
|
"narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1632470817,
|
||||||
|
"narHash": "sha256-tGyOesdpqQEVqlmVeElsC98OJ2GDy+LNaCThSby/GQM=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "39e8ec2db68b863543bd377e44fbe02f8d05864e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
212
flake.nix
Normal file
212
flake.nix
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
{
|
||||||
|
description = "Transfer.sh";
|
||||||
|
|
||||||
|
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
let
|
||||||
|
transfer-sh = pkgs: pkgs.buildGoModule {
|
||||||
|
src = self;
|
||||||
|
name = "transfer.sh";
|
||||||
|
vendorSha256 = "sha256-bgQUMiC33yVorcKOWhegT1/YU+fvxsz2pkeRvjf3R7g=";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
|
||||||
|
flake-utils.lib.eachDefaultSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
packages = flake-utils.lib.flattenTree {
|
||||||
|
transfer-sh = transfer-sh pkgs;
|
||||||
|
};
|
||||||
|
defaultPackage = packages.transfer-sh;
|
||||||
|
apps.transfer-sh = flake-utils.lib.mkApp { drv = packages.transfer-sh; };
|
||||||
|
defaultApp = apps.transfer-sh;
|
||||||
|
}
|
||||||
|
) // rec {
|
||||||
|
|
||||||
|
nixosModules = {
|
||||||
|
transfer-sh = { config, lib, pkgs, ... }: with lib; let
|
||||||
|
RUNTIME_DIR = "/var/lib/transfer.sh";
|
||||||
|
cfg = config.services.transfer-sh;
|
||||||
|
|
||||||
|
general_options = {
|
||||||
|
|
||||||
|
enable = mkEnableOption "Transfer.sh service";
|
||||||
|
listener = mkOption { default = 80; type = types.int; description = "port to use for http (:80)"; };
|
||||||
|
profile-listener = mkOption { default = 6060; type = types.int; description = "port to use for profiler (:6060)"; };
|
||||||
|
force-https = mkOption { type = types.nullOr types.bool; description = "redirect to https"; };
|
||||||
|
tls-listener = mkOption { default = 443; type = types.int; description = "port to use for https (:443)"; };
|
||||||
|
tls-listener-only = mkOption { type = types.nullOr types.bool; description = "flag to enable tls listener only"; };
|
||||||
|
tls-cert-file = mkOption { type = types.nullOr types.str; description = "path to tls certificate"; };
|
||||||
|
tls-private-key = mkOption { type = types.nullOr types.str; description = "path to tls private key "; };
|
||||||
|
http-auth-user = mkOption { type = types.nullOr types.str; description = "user for basic http auth on upload"; };
|
||||||
|
http-auth-pass = mkOption { type = types.nullOr types.str; description = "pass for basic http auth on upload"; };
|
||||||
|
ip-whitelist = mkOption { type = types.nullOr types.str; description = "comma separated list of ips allowed to connect to the service"; };
|
||||||
|
ip-blacklist = mkOption { type = types.nullOr types.str; description = "comma separated list of ips not allowed to connect to the service"; };
|
||||||
|
temp-path = mkOption { type = types.nullOr types.str; description = "path to temp folder"; };
|
||||||
|
web-path = mkOption { type = types.nullOr types.str; description = "path to static web files (for development or custom front end)"; };
|
||||||
|
proxy-path = mkOption { type = types.nullOr types.str; description = "path prefix when service is run behind a proxy"; };
|
||||||
|
proxy-port = mkOption { type = types.nullOr types.str; description = "port of the proxy when the service is run behind a proxy"; };
|
||||||
|
ga-key = mkOption { type = types.nullOr types.str; description = "google analytics key for the front end"; };
|
||||||
|
email-contact = mkOption { type = types.nullOr types.str; description = "email contact for the front end"; };
|
||||||
|
uservoice-key = mkOption { type = types.nullOr types.str; description = "user voice key for the front end"; };
|
||||||
|
lets-encrypt-hosts = mkOption { type = types.nullOr (types.listOf types.str); description = "hosts to use for lets encrypt certificates"; };
|
||||||
|
log = mkOption { type = types.nullOr types.str; description = "path to log file"; };
|
||||||
|
cors-domains = mkOption { type = types.nullOr (types.listOf types.str); description = "comma separated list of domains for CORS, setting it enable CORS "; };
|
||||||
|
clamav-host = mkOption { type = types.nullOr types.str; description = "host for clamav feature"; };
|
||||||
|
rate-limit = mkOption { type = types.nullOr types.int; description = "request per minute"; };
|
||||||
|
max-upload-size = mkOption { type = types.nullOr types.int; description = "max upload size in kilobytes "; };
|
||||||
|
purge-days = mkOption { type = types.nullOr types.int; description = "number of days after the uploads are purged automatically "; };
|
||||||
|
random-token-length = mkOption { type = types.nullOr types.int; description = "length of the random token for the upload path (double the size for delete path)"; };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
provider_options = {
|
||||||
|
|
||||||
|
aws = {
|
||||||
|
enable = mkEnableOption "Enable AWS backend";
|
||||||
|
aws-access-key = mkOption { type = types.str; description = "aws access key"; };
|
||||||
|
aws-secret-key = mkOption { type = types.str; description = "aws secret key"; };
|
||||||
|
bucket = mkOption { type = types.str; description = "aws bucket "; };
|
||||||
|
s3-endpoint = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
description = ''
|
||||||
|
Custom S3 endpoint.
|
||||||
|
If you specify the s3-region, you don't need to set the endpoint URL since the correct endpoint will used automatically.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
s3-region = mkOption { type = types.str; description = "region of the s3 bucket eu-west-"; };
|
||||||
|
s3-no-multipart = mkOption { type = types.nullOr types.bool; description = "disables s3 multipart upload "; };
|
||||||
|
s3-path-style = mkOption { type = types.nullOr types.str; description = "Forces path style URLs, required for Minio. "; };
|
||||||
|
};
|
||||||
|
|
||||||
|
storj = {
|
||||||
|
enable = mkEnableOption "Enable storj backend";
|
||||||
|
storj-access = mkOption { type = types.str; description = "Access for the project"; };
|
||||||
|
storj-bucket = mkOption { type = types.str; description = "Bucket to use within the project"; };
|
||||||
|
};
|
||||||
|
|
||||||
|
gdrive = {
|
||||||
|
enable = mkEnableOption "Enable gdrive backend";
|
||||||
|
gdrive-client-json = mkOption { type = types.str; description = "oauth client json config for gdrive provider"; };
|
||||||
|
gdrive-chunk-size = mkOption { default = 8; type = types.nullOr types.int; description = "chunk size for gdrive upload in megabytes, must be lower than available memory (8 MB)"; };
|
||||||
|
basedir = mkOption { type = types.str; description = "path storage for gdrive provider"; default = "${cfg.stateDir}/store"; };
|
||||||
|
purge-interval = mkOption { type = types.nullOr types.int; description = "interval in hours to run the automatic purge for (not applicable to S3 and Storj)"; };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
local = {
|
||||||
|
enable = mkEnableOption "Enable local backend";
|
||||||
|
basedir = mkOption { type = types.str; description = "path storage for local provider"; default = "${cfg.stateDir}/store"; };
|
||||||
|
purge-interval = mkOption { type = types.nullOr types.int; description = "interval in hours to run the automatic purge for (not applicable to S3 and Storj)"; };
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.transfer-sh = fold recursiveUpdate {} [
|
||||||
|
general_options
|
||||||
|
{
|
||||||
|
provider = provider_options;
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "User to run the service under";
|
||||||
|
default = "transfer.sh";
|
||||||
|
};
|
||||||
|
group = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Group to run the service under";
|
||||||
|
default = "transfer.sh";
|
||||||
|
};
|
||||||
|
stateDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
description = "Variable state directory";
|
||||||
|
default = RUNTIME_DIR;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
config = let
|
||||||
|
|
||||||
|
mkFlags = cfg: options:
|
||||||
|
let
|
||||||
|
mkBoolFlag = option: if cfg.${option} then [ "--${option}" ] else [];
|
||||||
|
mkFlag = option:
|
||||||
|
if isBool cfg.${option}
|
||||||
|
then mkBoolFlag option
|
||||||
|
else [ "--${option}" "${cfg.${option}}" ];
|
||||||
|
|
||||||
|
in
|
||||||
|
lists.flatten (map (mkFlag) (filter (option: cfg.${option} != null && option != "enable") options));
|
||||||
|
|
||||||
|
aws-config = (mkFlags cfg.provider.aws (attrNames provider_options)) ++ [ "--provider" "aws" ];
|
||||||
|
gdrive-config = mkFlags cfg.provider.gdrive (attrNames provider_options.gdrive) ++ [ "--provider" "gdrive" ];
|
||||||
|
storj-config = mkFlags cfg.provider.storj (attrNames provider_options.storj) ++ [ "--provider" "storj" ];
|
||||||
|
local-config = mkFlags cfg.provider.local (attrNames provider_options.local) ++ [ "--provider" "local" ];
|
||||||
|
|
||||||
|
general-config = concatStringsSep " " (mkFlags cfg (attrNames general_options));
|
||||||
|
provider-config = concatStringsSep " " (
|
||||||
|
if cfg.provider.aws.enable && !cfg.provider.storj.enable && !cfg.provider.gdrive.enable && !cfg.provider.local.enable then aws-config
|
||||||
|
else if !cfg.provider.aws.enable && cfg.provider.storj.enable && !cfg.provider.gdrive.enable && !cfg.provider.local.enable then storj-config
|
||||||
|
else if !cfg.provider.aws.enable && !cfg.provider.storj.enable && cfg.provider.gdrive.enable && !cfg.provider.local.enable then gdrive-config
|
||||||
|
else if !cfg.provider.aws.enable && !cfg.provider.storj.enable && !cfg.provider.gdrive.enable && cfg.provider.local.enable then local-config
|
||||||
|
else throw "transfer.sh requires exactly one provider (aws, storj, gdrive, local)"
|
||||||
|
);
|
||||||
|
|
||||||
|
in
|
||||||
|
lib.mkIf cfg.enable
|
||||||
|
{
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${cfg.stateDir} 0750 ${cfg.user} ${cfg.group} - -"
|
||||||
|
] ++ optional cfg.provider.gdrive.enable cfg.provider.gdrive.basedir
|
||||||
|
++ optional cfg.provider.local.enable cfg.provider.local.basedir;
|
||||||
|
|
||||||
|
systemd.services.transfer-sh = {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "network.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
ExecStart = "${transfer-sh pkgs}/bin/transfer.sh ${general-config} ${provider-config} ";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ cfg.listener cfg.profile-listener cfg.tls-listener ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
default = { self, pkgs, ... }: {
|
||||||
|
imports = [ nixosModules.transfer-sh ];
|
||||||
|
# Network configuration.
|
||||||
|
|
||||||
|
# useDHCP is generally considered to better be turned off in favor
|
||||||
|
# of <adapter>.useDHCP
|
||||||
|
networking.useDHCP = false;
|
||||||
|
networking.firewall.allowedTCPPorts = [];
|
||||||
|
|
||||||
|
# Enable the inventaire server.
|
||||||
|
services.transfer-sh = {
|
||||||
|
enable = true;
|
||||||
|
provider.local = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nixpkgs.config.allowUnfree = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
nixosConfigurations."container" = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
modules = [
|
||||||
|
nixosModules.default
|
||||||
|
({ ... }: { boot.isContainer = true; })
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
48
go.mod
48
go.mod
@@ -1,36 +1,36 @@
|
|||||||
module github.com/dutchcoders/transfer.sh
|
module github.com/dutchcoders/transfer.sh
|
||||||
|
|
||||||
go 1.12
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cloud.google.com/go v0.77.0 // indirect
|
||||||
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14
|
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14
|
||||||
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2
|
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2
|
||||||
github.com/aws/aws-sdk-go v1.20.6
|
github.com/aws/aws-sdk-go v1.37.14
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
|
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
|
||||||
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329
|
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329
|
||||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20190716184912-96e06a2276ba
|
github.com/dutchcoders/transfer.sh-web v0.0.0-20211215083008-31e11925a9d3
|
||||||
github.com/elazarl/go-bindata-assetfs v1.0.0
|
github.com/elazarl/go-bindata-assetfs v1.0.1
|
||||||
github.com/fatih/color v1.7.0
|
github.com/fatih/color v1.10.0
|
||||||
github.com/garyburd/redigo v1.6.0 // indirect
|
github.com/garyburd/redigo v1.6.2 // indirect
|
||||||
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721
|
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f
|
||||||
github.com/gorilla/mux v1.7.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/microcosm-cc/bluemonday v1.0.16
|
||||||
github.com/mattn/go-colorable v0.1.1 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.7 // indirect
|
|
||||||
github.com/microcosm-cc/bluemonday v1.0.2
|
|
||||||
github.com/minio/cli v1.3.0
|
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0
|
||||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
github.com/stretchr/testify v1.3.0 // indirect
|
|
||||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
|
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529
|
github.com/urfave/cli v1.22.5
|
||||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5
|
go.opencensus.io v0.22.6 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa
|
||||||
google.golang.org/api v0.5.0
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99
|
||||||
gopkg.in/russross/blackfriday.v2 v2.0.1
|
google.golang.org/api v0.40.0
|
||||||
|
google.golang.org/genproto v0.0.0-20210218151259-fe80b386bf06 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15
|
||||||
|
storj.io/common v0.0.0-20220131120956-e74f624a3d55
|
||||||
|
storj.io/uplink v1.7.2-0.20220131124001-c1db742c840d
|
||||||
)
|
)
|
||||||
|
|
||||||
replace gopkg.in/russross/blackfriday.v2 v2.0.1 => github.com/russross/blackfriday/v2 v2.0.1
|
|
||||||
|
737
go.sum
737
go.sum
@@ -1,123 +1,746 @@
|
|||||||
|
cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
cloud.google.com/go v0.31.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.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||||
|
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 v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||||
|
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||||
|
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||||
|
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||||
|
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||||
|
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||||
|
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||||
|
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||||
|
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||||
|
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||||
|
cloud.google.com/go v0.77.0 h1:qA5V5+uQf6Mgr+tmFI8UT3D/ELyhIYkPwNGao/3Y+sQ=
|
||||||
|
cloud.google.com/go v0.77.0/go.mod h1:R8fYSLIilC247Iu8WS2OGHw1E/Ufn7Pd7HiDjTqiURs=
|
||||||
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
|
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||||
|
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||||
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
|
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||||
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
|
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||||
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||||
|
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||||
|
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||||
|
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
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/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14 h1:3zOOc7WdrATDXof+h/rBgMsg0sAmZIEVHft1UbWHh94=
|
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14 h1:3zOOc7WdrATDXof+h/rBgMsg0sAmZIEVHft1UbWHh94=
|
||||||
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14/go.mod h1:+VFiaivV54Sa94ijzA/ZHQLoHuoUIS9hIqCK6f/76Zw=
|
github.com/PuerkitoBio/ghost v0.0.0-20160324114900-206e6e460e14/go.mod h1:+VFiaivV54Sa94ijzA/ZHQLoHuoUIS9hIqCK6f/76Zw=
|
||||||
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2 h1:sIvihcW4qpN5qGSjmrsDDAbLpEq5tuHjJJfWY0Hud5Y=
|
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2 h1:sIvihcW4qpN5qGSjmrsDDAbLpEq5tuHjJJfWY0Hud5Y=
|
||||||
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2/go.mod h1:3YwJE8rEisS9eraee0hygGG4G3gqX8H8Nyu+nPTUnGU=
|
github.com/VojtechVitek/ratelimit v0.0.0-20160722140851-dc172bc0f6d2/go.mod h1:3YwJE8rEisS9eraee0hygGG4G3gqX8H8Nyu+nPTUnGU=
|
||||||
github.com/aws/aws-sdk-go v1.20.6 h1:kmy4Gvdlyez1fV4kw5RYxZzWKVyuHZHgPWeU/YvRsV4=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.37.14 h1:thuR1hd1doCvsaMDYDMhqCGSmw39bSvZaw+DPGhMm5w=
|
||||||
|
github.com/aws/aws-sdk-go v1.37.14/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
|
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
||||||
|
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||||
|
github.com/calebcase/tmpfile v1.0.3 h1:BZrOWZ79gJqQ3XbAQlihYZf/YCV0H4KPIdM5K5oMpJo=
|
||||||
|
github.com/calebcase/tmpfile v1.0.3/go.mod h1:UAUc01aHeC+pudPagY/lWvt2qS9ZO5Zzof6/tIUzqeI=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
||||||
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||||
|
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.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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e h1:rcHHSQqzCgvlwP0I/fQ8rQMn/MpHE5gWSLdtpxtP6KQ=
|
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e h1:rcHHSQqzCgvlwP0I/fQ8rQMn/MpHE5gWSLdtpxtP6KQ=
|
||||||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e/go.mod h1:Byz7q8MSzSPkouskHJhX0er2mZY/m0Vj5bMeMCkkyY4=
|
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e/go.mod h1:Byz7q8MSzSPkouskHJhX0er2mZY/m0Vj5bMeMCkkyY4=
|
||||||
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329 h1:ERqCkG/uSyT74P1m/j9yR+so+7ynY4fbTvLY/Mr1ZMg=
|
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329 h1:ERqCkG/uSyT74P1m/j9yR+so+7ynY4fbTvLY/Mr1ZMg=
|
||||||
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329/go.mod h1:G5qOfE5bQZ5scycLpB7fYWgN4y3xdfXo+pYWM8z2epY=
|
github.com/dutchcoders/go-virustotal v0.0.0-20140923143438-24cc8e6fa329/go.mod h1:G5qOfE5bQZ5scycLpB7fYWgN4y3xdfXo+pYWM8z2epY=
|
||||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20190520063132-37110d436c89 h1:FJc5t2SEtRmLXDWxHFQG4QB98Vqr7/60cIiUT2F5yAA=
|
github.com/dutchcoders/transfer.sh-web v0.0.0-20211215083008-31e11925a9d3 h1:HyfU90/8y9S5IkHTQgIfzs4dT3iagbJUJLncCsSwZCI=
|
||||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20190520063132-37110d436c89/go.mod h1:UjR1zlrq/R2Sef7e4q3TeJm4HcbLh4vRzlCEGJP+wLg=
|
github.com/dutchcoders/transfer.sh-web v0.0.0-20211215083008-31e11925a9d3/go.mod h1:F6Q37CxDh2MHr5KXkcZmNB3tdkK7v+bgE+OpBY+9ilI=
|
||||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20190716184912-96e06a2276ba h1:474BcGUqYKnxg49p7O7i3m9EuFfOag+OtPw5b/nmGVk=
|
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||||
github.com/dutchcoders/transfer.sh-web v0.0.0-20190716184912-96e06a2276ba/go.mod h1:UjR1zlrq/R2Sef7e4q3TeJm4HcbLh4vRzlCEGJP+wLg=
|
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||||
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||||
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721 h1:KRMr9A3qfbVM7iV/WcLY/rL5LICqwMHLhwRXKu99fXw=
|
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||||
github.com/golang/gddo v0.0.0-20190419222130-af0f2af80721/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
|
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||||
|
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
|
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
|
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||||
|
github.com/garyburd/redigo v1.6.2 h1:yE/pwKCrbLpLpQICzYTeZ7JsTA/C53wFTJHaEtRqniM=
|
||||||
|
github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg=
|
||||||
|
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||||
|
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
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/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
|
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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
|
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/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
|
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
|
github.com/google/pprof v0.0.0-20211108044417-e9b028704de0 h1:rsq1yB2xiFLDYYaYdlGBsSkwVzsCo500wMhxvW5A/bk=
|
||||||
|
github.com/google/pprof v0.0.0-20211108044417-e9b028704de0/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
||||||
|
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||||
|
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/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||||
|
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||||
|
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||||
|
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
|
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||||
|
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||||
|
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
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/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
|
github.com/lucas-clemente/quic-go v0.23.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
|
github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
||||||
github.com/minio/cli v1.3.0 h1:vB0iUpmyaH54+1jJJj62Aa0qFF3xO3i0J3IcKiM6bHM=
|
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
|
||||||
github.com/minio/cli v1.3.0/go.mod h1:hLsWNQy2wIf3FKFnMlH69f4RdEyn8nbRA2shaulTjGY=
|
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
|
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||||
|
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
github.com/mattn/go-isatty v0.0.2/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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.16 h1:kHmAq2t7WPWLjiGvzKa5o3HzSfahUKiOq7fAPUiMNIc=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.16/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
|
||||||
|
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||||
|
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||||
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||||
|
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||||
|
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
||||||
|
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||||
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
|
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
||||||
|
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||||
|
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||||
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/shuLhan/go-bindata v4.0.0+incompatible/go.mod h1:pkcPAATLBDD2+SpAPnX5vEM90F7fcwHCvvLCMXcmw3g=
|
||||||
|
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||||
|
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||||
|
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||||
|
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||||
|
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||||
|
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||||
|
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||||
|
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||||
|
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||||
|
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||||
|
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||||
|
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||||
|
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||||
|
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||||
|
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||||
|
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||||
|
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||||
|
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||||
|
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
|
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
|
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
|
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||||
|
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||||
|
github.com/spacemonkeygo/monkit/v3 v3.0.4/go.mod h1:JcK1pCbReQsOsMKF/POFSZCq7drXFybgGmbc27tuwes=
|
||||||
|
github.com/spacemonkeygo/monkit/v3 v3.0.17 h1:rqIuLhRUr2UtS3WNVbPY/BwvjlwKVvSOVY5p0QVocxE=
|
||||||
|
github.com/spacemonkeygo/monkit/v3 v3.0.17/go.mod h1:kj1ViJhlyADa7DiA4xVnTuPA46lFKbM7mxQTrXCuJP4=
|
||||||
|
github.com/spacemonkeygo/monotime v0.0.0-20180824235756-e3f48a95f98a/go.mod h1:ul4bvvnCOPZgq8w0nTkSmWVg/hauVpFS97Am1YM1XXo=
|
||||||
|
github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||||
|
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
|
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
|
||||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
|
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
|
||||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||||
|
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
|
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||||
|
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||||
|
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3 h1:zMsHhfK9+Wdl1F7sIKLyx3wrOFofpb3rWFbA4HgcK5k=
|
||||||
|
github.com/vivint/infectious v0.0.0-20200605153912-25a574ae18a3/go.mod h1:R0Gbuw7ElaGSLOZUSwBm/GgVwMd30jWxBDdAyMOeTuc=
|
||||||
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/zeebo/admission/v3 v3.0.3/go.mod h1:2OWyAS5yo0Xvj2AEUosOjTUHxaY0oIIiCrXGKCYzWpo=
|
||||||
|
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||||
|
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
|
github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g=
|
||||||
|
github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
|
||||||
|
github.com/zeebo/float16 v0.1.0/go.mod h1:fssGvvXu+XS8MH57cKmyrLB/cqioYeYX/2mXCN3a5wo=
|
||||||
|
github.com/zeebo/incenc v0.0.0-20180505221441-0d92902eec54/go.mod h1:EI8LcOBDlSL3POyqwC1eJhOYlMBMidES+613EtmmT5w=
|
||||||
|
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
|
go.opencensus.io v0.22.6 h1:BdkrbWrzDlV9dnbzoP7sfN+dHheJ4J9JOaYxcUDL+ok=
|
||||||
|
go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
|
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||||
|
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||||
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
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/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4=
|
||||||
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
|
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-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
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-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-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/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
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/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-20180826012351-8a410e7b638d/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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181106065722-10aee1819953/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-20190108225652-1e06a53dbb7e/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-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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak=
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/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/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/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-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||||
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||||
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||||
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99 h1:5vD4XjIc0X5+kHZjx4UecYdjA6mJo+XXNoaW0EjU5Os=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
|
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||||
|
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
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-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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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 h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
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-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211109065445-02f5c0300f6e h1:i6Vklmyu+fZMFYpum+sR4ZWABGW7MyIxfJZXYvcnbns=
|
||||||
|
golang.org/x/sys v0.0.0-20211109065445-02f5c0300f6e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
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-20181030000716-a0a13e073c7b/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-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-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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
google.golang.org/api v0.5.0 h1:lj9SyhMzyoa38fgFF0oO2T6pjs5IzkLPKfVtxpyCRMM=
|
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
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-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
|
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||||
|
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||||
|
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/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||||
|
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||||
|
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
|
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||||
|
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||||
|
google.golang.org/api v0.40.0 h1:uWrpz12dpVPn7cojP82mk02XDgTJLDPc2KbVTxrWb4A=
|
||||||
|
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
|
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||||
|
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
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-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210212180131-e7f2df4ecc2d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20210218151259-fe80b386bf06 h1:Px6YyLaNKEo5eoniIBAv6Es0jbvyEmSYqOac64iS2Rs=
|
||||||
|
google.golang.org/genproto v0.0.0-20210218151259-fe80b386bf06/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
|
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||||
|
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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
|
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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||||
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||||
|
google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8=
|
||||||
|
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
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/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.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||||
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||||
|
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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
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=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||||
|
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||||
|
storj.io/common v0.0.0-20220131120956-e74f624a3d55 h1:HgFcPy1vohRJ4VRRGej92N31i5znL1KGWhDVgGgjBG4=
|
||||||
|
storj.io/common v0.0.0-20220131120956-e74f624a3d55/go.mod h1:m0489td5+rKDdoiYOzCkh3CfGW/cLyntZiYfso+QfMs=
|
||||||
|
storj.io/drpc v0.0.29 h1:Ihd4ls/JQFr0lctefie3iu+3QM4duccCKr9uMzf4sKY=
|
||||||
|
storj.io/drpc v0.0.29/go.mod h1:6rcOyR/QQkSTX/9L5ZGtlZaE2PtXTTZl8d+ulSeeYEg=
|
||||||
|
storj.io/uplink v1.7.2-0.20220131124001-c1db742c840d h1:Fb9Tht/h8KJbn02obTM1mseR4b7WQ7PCf+Nc3wXC3vk=
|
||||||
|
storj.io/uplink v1.7.2-0.20220131124001-c1db742c840d/go.mod h1:BYnzl+ordVACxbWM7eKUvkqC+GvSnMmVbIyA2BNGBmk=
|
||||||
|
203
lock.json
203
lock.json
@@ -1,203 +0,0 @@
|
|||||||
{
|
|
||||||
"memo": "5b27aecb0272e40f3b8b8f9a5deeb7c9f5dbf06c53b1134de2b84eac466d27e0",
|
|
||||||
"projects": [
|
|
||||||
{
|
|
||||||
"name": "github.com/PuerkitoBio/ghost",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "206e6e460e14a42d1d811c970b30248db058e9b2",
|
|
||||||
"packages": [
|
|
||||||
".",
|
|
||||||
"handlers"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/VojtechVitek/ratelimit",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "dc172bc0f6d241e980010dbc63957ef1a2c8ca33",
|
|
||||||
"packages": [
|
|
||||||
".",
|
|
||||||
"memory"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/dutchcoders/go-clamd",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "a9a81beaffff0392094052913ec45fa140eb8511",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/dutchcoders/go-virustotal",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "24cc8e6fa329f020c70a3b32330b5743f1ba7971",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/dutchcoders/transfer.sh-web",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "cdb97c4b64a33dd1eec7e94b2c200cd9b06296a1",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/elazarl/go-bindata-assetfs",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "30f82fa23fd844bd5bb1e5f216db87fd77b5eb43",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/fatih/color",
|
|
||||||
"version": "v1.4.1",
|
|
||||||
"revision": "9131ab34cf20d2f6d83fdc67168a5430d1c7dc23",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/garyburd/redigo",
|
|
||||||
"version": "v1.0.0",
|
|
||||||
"revision": "8873b2f1995f59d4bcdd2b0dc9858e2cb9bf0c13",
|
|
||||||
"packages": [
|
|
||||||
"internal",
|
|
||||||
"redis"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/goamz/goamz",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "c35091c30f44b7f151ec9028b895465a191d1ea7",
|
|
||||||
"packages": [
|
|
||||||
"aws",
|
|
||||||
"s3"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/golang/gddo",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "72302b972abba39585150723aea3cf343e99437c",
|
|
||||||
"packages": [
|
|
||||||
"httputil/header"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/gorilla/context",
|
|
||||||
"version": "v1.1",
|
|
||||||
"revision": "1ea25387ff6f684839d82767c1733ff4d4d15d0a",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/gorilla/mux",
|
|
||||||
"version": "v1.3.0",
|
|
||||||
"revision": "392c28fe23e1c45ddba891b0320b3b5df220beea",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/gorilla/securecookie",
|
|
||||||
"version": "v1.1",
|
|
||||||
"revision": "667fe4e3466a040b780561fe9b51a83a3753eefc",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/mattn/go-colorable",
|
|
||||||
"version": "v0.0.7",
|
|
||||||
"revision": "d228849504861217f796da67fae4f6e347643f15",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/mattn/go-isatty",
|
|
||||||
"version": "v0.0.1",
|
|
||||||
"revision": "3a115632dcd687f9c8cd01679c83a06a0e21c1f3",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/minio/cli",
|
|
||||||
"version": "v1.3.0",
|
|
||||||
"revision": "8683fa7fef37cc8cb092f47bdb6b403e0049f9ee",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/nu7hatch/gouuid",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "179d4d0c4d8d407a32af483c2354df1d2c91e6c3",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/russross/blackfriday",
|
|
||||||
"version": "v1.4",
|
|
||||||
"revision": "0b647d0506a698cca42caca173e55559b12a69f2",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/shurcooL/sanitized_anchor_name",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "1dba4b3954bc059efc3991ec364f9f9a35f597d2",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "github.com/vaughan0/go-ini",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "golang.org/x/crypto",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "459e26527287adbc2adcc5d0d49abff9a5f315a7",
|
|
||||||
"packages": [
|
|
||||||
"acme",
|
|
||||||
"acme/autocert"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "golang.org/x/net",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "a6577fac2d73be281a500b310739095313165611",
|
|
||||||
"packages": [
|
|
||||||
"context",
|
|
||||||
"context/ctxhttp"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "golang.org/x/sys",
|
|
||||||
"branch": "master",
|
|
||||||
"revision": "99f16d856c9836c42d24e7ab64ea72916925fa97",
|
|
||||||
"packages": [
|
|
||||||
"unix"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gopkg.in/check.v1",
|
|
||||||
"branch": "v1",
|
|
||||||
"revision": "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec",
|
|
||||||
"packages": [
|
|
||||||
"."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
12
main.go
12
main.go
@@ -1,8 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/dutchcoders/transfer.sh/cmd"
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/dutchcoders/transfer.sh/cmd"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := cmd.New()
|
app := cmd.New()
|
||||||
app.RunAndExitOnError()
|
err := app.Run(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
||||||
|
Copyright (c) 2018-2020 Andrea Spacca.
|
||||||
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -25,20 +27,19 @@ THE SOFTWARE.
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// _ "transfer.sh/app/handlers"
|
"errors"
|
||||||
// _ "transfer.sh/app/utils"
|
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
clamd "github.com/dutchcoders/go-clamd"
|
clamd "github.com/dutchcoders/go-clamd"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const clamavScanStatusOK = "OK"
|
||||||
|
|
||||||
func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
|
|
||||||
@@ -49,26 +50,53 @@ func (s *Server) scanHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
s.logger.Printf("Scanning %s %d %s", filename, contentLength, contentType)
|
s.logger.Printf("Scanning %s %d %s", filename, contentLength, contentType)
|
||||||
|
|
||||||
var reader io.Reader
|
file, err := ioutil.TempFile(s.tempPath, "clamav-")
|
||||||
|
defer s.cleanTmpFile(file)
|
||||||
reader = r.Body
|
|
||||||
|
|
||||||
c := clamd.NewClamd(s.ClamAVDaemonHost)
|
|
||||||
|
|
||||||
abort := make(chan bool)
|
|
||||||
response, err := c.ScanStream(reader, abort)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("%s", err.Error())
|
s.logger.Printf("%s", err.Error())
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
_, err = io.Copy(file, r.Body)
|
||||||
case s := <-response:
|
if err != nil {
|
||||||
w.Write([]byte(fmt.Sprintf("%v\n", s.Status)))
|
s.logger.Printf("%s", err.Error())
|
||||||
case <-time.After(time.Second * 60):
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
abort <- true
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
close(abort)
|
status, err := s.performScan(file.Name())
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Printf("%s", err.Error())
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = w.Write([]byte(fmt.Sprintf("%v\n", status)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) performScan(path string) (string, error) {
|
||||||
|
c := clamd.NewClamd(s.ClamAVDaemonHost)
|
||||||
|
|
||||||
|
responseCh := make(chan chan *clamd.ScanResult)
|
||||||
|
errCh := make(chan error)
|
||||||
|
go func(responseCh chan chan *clamd.ScanResult, errCh chan error) {
|
||||||
|
response, err := c.ScanFile(path)
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
responseCh <- response
|
||||||
|
}(responseCh, errCh)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errCh:
|
||||||
|
return "", err
|
||||||
|
case response := <-responseCh:
|
||||||
|
st := <-response
|
||||||
|
return st.Status, nil
|
||||||
|
case <-time.After(time.Second * 60):
|
||||||
|
return "", errors.New("clamav scan timeout")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
https://github.com/fs111/kurz.go/blob/master/src/codec.go
|
|
||||||
|
|
||||||
Originally written and Copyright (c) 2011 André Kelpe
|
|
||||||
Modifications Copyright (c) 2015 John Ko
|
|
||||||
|
|
||||||
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 server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// characters used for short-urls
|
|
||||||
SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
|
|
||||||
// someone set us up the bomb !!
|
|
||||||
BASE = int64(len(SYMBOLS))
|
|
||||||
)
|
|
||||||
|
|
||||||
// encodes a number into our *base* representation
|
|
||||||
// TODO can this be made better with some bitshifting?
|
|
||||||
func Encode(number int64) string {
|
|
||||||
rest := number % BASE
|
|
||||||
// strings are a bit weird in go...
|
|
||||||
result := string(SYMBOLS[rest])
|
|
||||||
if number-rest != 0 {
|
|
||||||
newnumber := (number - rest) / BASE
|
|
||||||
result = Encode(newnumber) + result
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decodes a string given in our encoding and returns the decimal
|
|
||||||
// integer.
|
|
||||||
func Decode(input string) int64 {
|
|
||||||
const floatbase = float64(BASE)
|
|
||||||
l := len(input)
|
|
||||||
var sum int = 0
|
|
||||||
for index := l - 1; index > -1; index -= 1 {
|
|
||||||
current := string(input[index])
|
|
||||||
pos := strings.Index(SYMBOLS, current)
|
|
||||||
sum = sum + (pos * int(math.Pow(floatbase, float64((l-index-1)))))
|
|
||||||
}
|
|
||||||
return int64(sum)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@@ -13,16 +13,16 @@ import (
|
|||||||
func Test(t *testing.T) { TestingT(t) }
|
func Test(t *testing.T) { TestingT(t) }
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ = Suite(&SuiteRedirectWithForceHTTPs{})
|
_ = Suite(&suiteRedirectWithForceHTTPS{})
|
||||||
_ = Suite(&SuiteRedirectWithoutForceHTTPs{})
|
_ = Suite(&suiteRedirectWithoutForceHTTPS{})
|
||||||
)
|
)
|
||||||
|
|
||||||
type SuiteRedirectWithForceHTTPs struct {
|
type suiteRedirectWithForceHTTPS struct {
|
||||||
handler http.HandlerFunc
|
handler http.HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) SetUpTest(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) SetUpTest(c *C) {
|
||||||
srvr, err := New(ForceHTTPs())
|
srvr, err := New(ForceHTTPS())
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -32,7 +32,7 @@ func (s *SuiteRedirectWithForceHTTPs) SetUpTest(c *C) {
|
|||||||
s.handler = srvr.RedirectHandler(handler)
|
s.handler = srvr.RedirectHandler(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) TestHTTPs(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) TestHTTPs(c *C) {
|
||||||
req := httptest.NewRequest("GET", "https://test/test", nil)
|
req := httptest.NewRequest("GET", "https://test/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@@ -42,7 +42,7 @@ func (s *SuiteRedirectWithForceHTTPs) TestHTTPs(c *C) {
|
|||||||
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) TestOnion(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) TestOnion(c *C) {
|
||||||
req := httptest.NewRequest("GET", "http://test.onion/test", nil)
|
req := httptest.NewRequest("GET", "http://test.onion/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@@ -52,7 +52,7 @@ func (s *SuiteRedirectWithForceHTTPs) TestOnion(c *C) {
|
|||||||
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) TestXForwardedFor(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) TestXForwardedFor(c *C) {
|
||||||
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
||||||
req.Header.Set("X-Forwarded-Proto", "https")
|
req.Header.Set("X-Forwarded-Proto", "https")
|
||||||
|
|
||||||
@@ -63,7 +63,7 @@ func (s *SuiteRedirectWithForceHTTPs) TestXForwardedFor(c *C) {
|
|||||||
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithForceHTTPs) TestHTTP(c *C) {
|
func (s *suiteRedirectWithForceHTTPS) TestHTTP(c *C) {
|
||||||
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@@ -74,11 +74,11 @@ func (s *SuiteRedirectWithForceHTTPs) TestHTTP(c *C) {
|
|||||||
c.Assert(resp.Header.Get("Location"), Equals, "https://127.0.0.1/test")
|
c.Assert(resp.Header.Get("Location"), Equals, "https://127.0.0.1/test")
|
||||||
}
|
}
|
||||||
|
|
||||||
type SuiteRedirectWithoutForceHTTPs struct {
|
type suiteRedirectWithoutForceHTTPS struct {
|
||||||
handler http.HandlerFunc
|
handler http.HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithoutForceHTTPs) SetUpTest(c *C) {
|
func (s *suiteRedirectWithoutForceHTTPS) SetUpTest(c *C) {
|
||||||
srvr, err := New()
|
srvr, err := New()
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ func (s *SuiteRedirectWithoutForceHTTPs) SetUpTest(c *C) {
|
|||||||
s.handler = srvr.RedirectHandler(handler)
|
s.handler = srvr.RedirectHandler(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithoutForceHTTPs) TestHTTP(c *C) {
|
func (s *suiteRedirectWithoutForceHTTPS) TestHTTP(c *C) {
|
||||||
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
req := httptest.NewRequest("GET", "http://127.0.0.1/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
@@ -99,7 +99,7 @@ func (s *SuiteRedirectWithoutForceHTTPs) TestHTTP(c *C) {
|
|||||||
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
c.Assert(resp.StatusCode, Equals, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SuiteRedirectWithoutForceHTTPs) TestHTTPs(c *C) {
|
func (s *suiteRedirectWithoutForceHTTPS) TestHTTPs(c *C) {
|
||||||
req := httptest.NewRequest("GET", "https://127.0.0.1/test", nil)
|
req := httptest.NewRequest("GET", "https://127.0.0.1/test", nil)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
@@ -21,7 +21,7 @@ import (
|
|||||||
"github.com/tomasen/realip"
|
"github.com/tomasen/realip"
|
||||||
)
|
)
|
||||||
|
|
||||||
//IPFilterOptions for IPFilter. Allowed takes precendence over Blocked.
|
//IPFilterOptions for ipFilter. Allowed takes precedence over Blocked.
|
||||||
//IPs can be IPv4 or IPv6 and can optionally contain subnet
|
//IPs can be IPv4 or IPv6 and can optionally contain subnet
|
||||||
//masks (/24). Note however, determining if a given IP is
|
//masks (/24). Note however, determining if a given IP is
|
||||||
//included in a subnet requires a linear scan so is less performant
|
//included in a subnet requires a linear scan so is less performant
|
||||||
@@ -43,7 +43,8 @@ type IPFilterOptions struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type IPFilter struct {
|
// ipFilter
|
||||||
|
type ipFilter struct {
|
||||||
opts IPFilterOptions
|
opts IPFilterOptions
|
||||||
//mut protects the below
|
//mut protects the below
|
||||||
//rw since writes are rare
|
//rw since writes are rare
|
||||||
@@ -59,13 +60,12 @@ type subnet struct {
|
|||||||
allowed bool
|
allowed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
//New constructs IPFilter instance.
|
func newIPFilter(opts IPFilterOptions) *ipFilter {
|
||||||
func NewIPFilter(opts IPFilterOptions) *IPFilter {
|
|
||||||
if opts.Logger == nil {
|
if opts.Logger == nil {
|
||||||
flags := log.LstdFlags
|
flags := log.LstdFlags
|
||||||
opts.Logger = log.New(os.Stdout, "", flags)
|
opts.Logger = log.New(os.Stdout, "", flags)
|
||||||
}
|
}
|
||||||
f := &IPFilter{
|
f := &ipFilter{
|
||||||
opts: opts,
|
opts: opts,
|
||||||
ips: map[string]bool{},
|
ips: map[string]bool{},
|
||||||
defaultAllowed: !opts.BlockByDefault,
|
defaultAllowed: !opts.BlockByDefault,
|
||||||
@@ -79,15 +79,15 @@ func NewIPFilter(opts IPFilterOptions) *IPFilter {
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *IPFilter) AllowIP(ip string) bool {
|
func (f *ipFilter) AllowIP(ip string) bool {
|
||||||
return f.ToggleIP(ip, true)
|
return f.ToggleIP(ip, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *IPFilter) BlockIP(ip string) bool {
|
func (f *ipFilter) BlockIP(ip string) bool {
|
||||||
return f.ToggleIP(ip, false)
|
return f.ToggleIP(ip, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *IPFilter) ToggleIP(str string, allowed bool) bool {
|
func (f *ipFilter) ToggleIP(str string, allowed bool) bool {
|
||||||
//check if has subnet
|
//check if has subnet
|
||||||
if ip, net, err := net.ParseCIDR(str); err == nil {
|
if ip, net, err := net.ParseCIDR(str); err == nil {
|
||||||
// containing only one ip?
|
// containing only one ip?
|
||||||
@@ -128,19 +128,19 @@ func (f *IPFilter) ToggleIP(str string, allowed bool) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//ToggleDefault alters the default setting
|
//ToggleDefault alters the default setting
|
||||||
func (f *IPFilter) ToggleDefault(allowed bool) {
|
func (f *ipFilter) ToggleDefault(allowed bool) {
|
||||||
f.mut.Lock()
|
f.mut.Lock()
|
||||||
f.defaultAllowed = allowed
|
f.defaultAllowed = allowed
|
||||||
f.mut.Unlock()
|
f.mut.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Allowed returns if a given IP can pass through the filter
|
//Allowed returns if a given IP can pass through the filter
|
||||||
func (f *IPFilter) Allowed(ipstr string) bool {
|
func (f *ipFilter) Allowed(ipstr string) bool {
|
||||||
return f.NetAllowed(net.ParseIP(ipstr))
|
return f.NetAllowed(net.ParseIP(ipstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
//NetAllowed returns if a given net.IP can pass through the filter
|
//NetAllowed returns if a given net.IP can pass through the filter
|
||||||
func (f *IPFilter) NetAllowed(ip net.IP) bool {
|
func (f *ipFilter) NetAllowed(ip net.IP) bool {
|
||||||
//invalid ip
|
//invalid ip
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return false
|
return false
|
||||||
@@ -173,35 +173,35 @@ func (f *IPFilter) NetAllowed(ip net.IP) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Blocked returns if a given IP can NOT pass through the filter
|
//Blocked returns if a given IP can NOT pass through the filter
|
||||||
func (f *IPFilter) Blocked(ip string) bool {
|
func (f *ipFilter) Blocked(ip string) bool {
|
||||||
return !f.Allowed(ip)
|
return !f.Allowed(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
//NetBlocked returns if a given net.IP can NOT pass through the filter
|
//NetBlocked returns if a given net.IP can NOT pass through the filter
|
||||||
func (f *IPFilter) NetBlocked(ip net.IP) bool {
|
func (f *ipFilter) NetBlocked(ip net.IP) bool {
|
||||||
return !f.NetAllowed(ip)
|
return !f.NetAllowed(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
//WrapIPFilter the provided handler with simple IP blocking middleware
|
//WrapIPFilter the provided handler with simple IP blocking middleware
|
||||||
//using this IP filter and its configuration
|
//using this IP filter and its configuration
|
||||||
func (f *IPFilter) Wrap(next http.Handler) http.Handler {
|
func (f *ipFilter) Wrap(next http.Handler) http.Handler {
|
||||||
return &ipFilterMiddleware{IPFilter: f, next: next}
|
return &ipFilterMiddleware{ipFilter: f, next: next}
|
||||||
}
|
}
|
||||||
|
|
||||||
//WrapIPFilter is equivalent to NewIPFilter(opts) then Wrap(next)
|
//WrapIPFilter is equivalent to newIPFilter(opts) then Wrap(next)
|
||||||
func WrapIPFilter(next http.Handler, opts IPFilterOptions) http.Handler {
|
func WrapIPFilter(next http.Handler, opts IPFilterOptions) http.Handler {
|
||||||
return NewIPFilter(opts).Wrap(next)
|
return newIPFilter(opts).Wrap(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ipFilterMiddleware struct {
|
type ipFilterMiddleware struct {
|
||||||
*IPFilter
|
*ipFilter
|
||||||
next http.Handler
|
next http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ipFilterMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (m *ipFilterMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
remoteIP := realip.FromRequest(r)
|
remoteIP := realip.FromRequest(r)
|
||||||
|
|
||||||
if !m.IPFilter.Allowed(remoteIP) {
|
if !m.ipFilter.Allowed(remoteIP) {
|
||||||
//show simple forbidden text
|
//show simple forbidden text
|
||||||
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||||
return
|
return
|
||||||
|
167
server/server.go
167
server/server.go
@@ -25,7 +25,10 @@ THE SOFTWARE.
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
crypto_rand "crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
gorillaHandlers "github.com/gorilla/handlers"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"mime"
|
"mime"
|
||||||
@@ -45,6 +48,7 @@ import (
|
|||||||
"github.com/VojtechVitek/ratelimit/memory"
|
"github.com/VojtechVitek/ratelimit/memory"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
// import pprof
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
|
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
@@ -56,28 +60,37 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
const SERVER_INFO = "transfer.sh"
|
|
||||||
|
|
||||||
// parse request with maximum memory of _24Kilobits
|
// parse request with maximum memory of _24Kilobits
|
||||||
const _24K = (1 << 3) * 24
|
const _24K = (1 << 3) * 24
|
||||||
|
|
||||||
// parse request with maximum memory of _5Megabytes
|
// parse request with maximum memory of _5Megabytes
|
||||||
const _5M = (1 << 20) * 5
|
const _5M = (1 << 20) * 5
|
||||||
|
|
||||||
|
// OptionFn is the option function type
|
||||||
type OptionFn func(*Server)
|
type OptionFn func(*Server)
|
||||||
|
|
||||||
|
// ClamavHost sets clamav host
|
||||||
func ClamavHost(s string) OptionFn {
|
func ClamavHost(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.ClamAVDaemonHost = s
|
srvr.ClamAVDaemonHost = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PerformClamavPrescan enables clamav prescan on upload
|
||||||
|
func PerformClamavPrescan(b bool) OptionFn {
|
||||||
|
return func(srvr *Server) {
|
||||||
|
srvr.performClamavPrescan = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirustotalKey sets virus total key
|
||||||
func VirustotalKey(s string) OptionFn {
|
func VirustotalKey(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.VirusTotalKey = s
|
srvr.VirusTotalKey = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Listener set listener
|
||||||
func Listener(s string) OptionFn {
|
func Listener(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.ListenerString = s
|
srvr.ListenerString = s
|
||||||
@@ -85,18 +98,36 @@ func Listener(s string) OptionFn {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CorsDomains sets CORS domains
|
||||||
|
func CorsDomains(s string) OptionFn {
|
||||||
|
return func(srvr *Server) {
|
||||||
|
srvr.CorsDomains = s
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmailContact sets email contact
|
||||||
|
func EmailContact(emailContact string) OptionFn {
|
||||||
|
return func(srvr *Server) {
|
||||||
|
srvr.emailContact = emailContact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoogleAnalytics sets GA key
|
||||||
func GoogleAnalytics(gaKey string) OptionFn {
|
func GoogleAnalytics(gaKey string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.gaKey = gaKey
|
srvr.gaKey = gaKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserVoice sets UV key
|
||||||
func UserVoice(userVoiceKey string) OptionFn {
|
func UserVoice(userVoiceKey string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.userVoiceKey = userVoiceKey
|
srvr.userVoiceKey = userVoiceKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSListener sets TLS listener and option
|
||||||
func TLSListener(s string, t bool) OptionFn {
|
func TLSListener(s string, t bool) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.TLSListenerString = s
|
srvr.TLSListenerString = s
|
||||||
@@ -105,12 +136,14 @@ func TLSListener(s string, t bool) OptionFn {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProfileListener sets profile listener
|
||||||
func ProfileListener(s string) OptionFn {
|
func ProfileListener(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.ProfileListenerString = s
|
srvr.ProfileListenerString = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebPath sets web path
|
||||||
func WebPath(s string) OptionFn {
|
func WebPath(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
if s[len(s)-1:] != "/" {
|
if s[len(s)-1:] != "/" {
|
||||||
@@ -121,6 +154,7 @@ func WebPath(s string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProxyPath sets proxy path
|
||||||
func ProxyPath(s string) OptionFn {
|
func ProxyPath(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
if s[len(s)-1:] != "/" {
|
if s[len(s)-1:] != "/" {
|
||||||
@@ -131,6 +165,14 @@ func ProxyPath(s string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProxyPort sets proxy port
|
||||||
|
func ProxyPort(s string) OptionFn {
|
||||||
|
return func(srvr *Server) {
|
||||||
|
srvr.proxyPort = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TempPath sets temp path
|
||||||
func TempPath(s string) OptionFn {
|
func TempPath(s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
if s[len(s)-1:] != "/" {
|
if s[len(s)-1:] != "/" {
|
||||||
@@ -141,11 +183,12 @@ func TempPath(s string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LogFile sets log file
|
||||||
func LogFile(logger *log.Logger, s string) OptionFn {
|
func LogFile(logger *log.Logger, s string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
f, err := os.OpenFile(s, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
f, err := os.OpenFile(s, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error opening file: %v", err)
|
logger.Fatalf("error opening file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.SetOutput(f)
|
logger.SetOutput(f)
|
||||||
@@ -153,36 +196,65 @@ func LogFile(logger *log.Logger, s string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logger sets logger
|
||||||
func Logger(logger *log.Logger) OptionFn {
|
func Logger(logger *log.Logger) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.logger = logger
|
srvr.logger = logger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaxUploadSize sets max upload size
|
||||||
|
func MaxUploadSize(kbytes int64) OptionFn {
|
||||||
|
return func(srvr *Server) {
|
||||||
|
srvr.maxUploadSize = kbytes * 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimit set rate limit
|
||||||
func RateLimit(requests int) OptionFn {
|
func RateLimit(requests int) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.rateLimitRequests = requests
|
srvr.rateLimitRequests = requests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ForceHTTPs() OptionFn {
|
// RandomTokenLength sets random token length
|
||||||
|
func RandomTokenLength(length int) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.forceHTTPs = true
|
srvr.randomTokenLength = length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge sets purge days and option
|
||||||
|
func Purge(days, interval int) OptionFn {
|
||||||
|
return func(srvr *Server) {
|
||||||
|
srvr.purgeDays = time.Duration(days) * time.Hour * 24
|
||||||
|
srvr.purgeInterval = time.Duration(interval) * time.Hour
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForceHTTPS sets forcing https
|
||||||
|
func ForceHTTPS() OptionFn {
|
||||||
|
return func(srvr *Server) {
|
||||||
|
srvr.forceHTTPS = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableProfiler sets enable profiler
|
||||||
func EnableProfiler() OptionFn {
|
func EnableProfiler() OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.profilerEnabled = true
|
srvr.profilerEnabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseStorage set storage to use
|
||||||
func UseStorage(s Storage) OptionFn {
|
func UseStorage(s Storage) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.storage = s
|
srvr.storage = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UseLetsEncrypt set letsencrypt usage
|
||||||
func UseLetsEncrypt(hosts []string) OptionFn {
|
func UseLetsEncrypt(hosts []string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
cacheDir := "./cache/"
|
cacheDir := "./cache/"
|
||||||
@@ -211,6 +283,7 @@ func UseLetsEncrypt(hosts []string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSConfig sets TLS config
|
||||||
func TLSConfig(cert, pk string) OptionFn {
|
func TLSConfig(cert, pk string) OptionFn {
|
||||||
certificate, err := tls.LoadX509KeyPair(cert, pk)
|
certificate, err := tls.LoadX509KeyPair(cert, pk)
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
@@ -222,13 +295,15 @@ func TLSConfig(cert, pk string) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func HttpAuthCredentials(user string, pass string) OptionFn {
|
// HTTPAuthCredentials sets basic http auth credentials
|
||||||
|
func HTTPAuthCredentials(user string, pass string) OptionFn {
|
||||||
return func(srvr *Server) {
|
return func(srvr *Server) {
|
||||||
srvr.AuthUser = user
|
srvr.AuthUser = user
|
||||||
srvr.AuthPass = pass
|
srvr.AuthPass = pass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FilterOptions sets ip filtering
|
||||||
func FilterOptions(options IPFilterOptions) OptionFn {
|
func FilterOptions(options IPFilterOptions) OptionFn {
|
||||||
for i, allowedIP := range options.AllowedIPs {
|
for i, allowedIP := range options.AllowedIPs {
|
||||||
options.AllowedIPs[i] = strings.TrimSpace(allowedIP)
|
options.AllowedIPs[i] = strings.TrimSpace(allowedIP)
|
||||||
@@ -243,6 +318,7 @@ func FilterOptions(options IPFilterOptions) OptionFn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Server is the main application
|
||||||
type Server struct {
|
type Server struct {
|
||||||
AuthUser string
|
AuthUser string
|
||||||
AuthPass string
|
AuthPass string
|
||||||
@@ -253,28 +329,38 @@ type Server struct {
|
|||||||
|
|
||||||
profilerEnabled bool
|
profilerEnabled bool
|
||||||
|
|
||||||
locks map[string]*sync.Mutex
|
locks sync.Map
|
||||||
|
|
||||||
|
maxUploadSize int64
|
||||||
rateLimitRequests int
|
rateLimitRequests int
|
||||||
|
|
||||||
|
purgeDays time.Duration
|
||||||
|
purgeInterval time.Duration
|
||||||
|
|
||||||
storage Storage
|
storage Storage
|
||||||
|
|
||||||
forceHTTPs bool
|
forceHTTPS bool
|
||||||
|
|
||||||
|
randomTokenLength int
|
||||||
|
|
||||||
ipFilterOptions *IPFilterOptions
|
ipFilterOptions *IPFilterOptions
|
||||||
|
|
||||||
VirusTotalKey string
|
VirusTotalKey string
|
||||||
ClamAVDaemonHost string
|
ClamAVDaemonHost string
|
||||||
|
performClamavPrescan bool
|
||||||
|
|
||||||
tempPath string
|
tempPath string
|
||||||
|
|
||||||
webPath string
|
webPath string
|
||||||
proxyPath string
|
proxyPath string
|
||||||
|
proxyPort string
|
||||||
|
emailContact string
|
||||||
gaKey string
|
gaKey string
|
||||||
userVoiceKey string
|
userVoiceKey string
|
||||||
|
|
||||||
TLSListenerOnly bool
|
TLSListenerOnly bool
|
||||||
|
|
||||||
|
CorsDomains string
|
||||||
ListenerString string
|
ListenerString string
|
||||||
TLSListenerString string
|
TLSListenerString string
|
||||||
ProfileListenerString string
|
ProfileListenerString string
|
||||||
@@ -284,9 +370,10 @@ type Server struct {
|
|||||||
LetsEncryptCache string
|
LetsEncryptCache string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New is the factory fot Server
|
||||||
func New(options ...OptionFn) (*Server, error) {
|
func New(options ...OptionFn) (*Server, error) {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
locks: map[string]*sync.Mutex{},
|
locks: sync.Map{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, optionFn := range options {
|
for _, optionFn := range options {
|
||||||
@@ -297,9 +384,14 @@ func New(options ...OptionFn) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rand.Seed(time.Now().UTC().UnixNano())
|
var seedBytes [8]byte
|
||||||
|
if _, err := crypto_rand.Read(seedBytes[:]); err != nil {
|
||||||
|
panic("cannot obtain cryptographically secure seed")
|
||||||
|
}
|
||||||
|
rand.Seed(int64(binary.LittleEndian.Uint64(seedBytes[:])))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run starts Server
|
||||||
func (s *Server) Run() {
|
func (s *Server) Run() {
|
||||||
listening := false
|
listening := false
|
||||||
|
|
||||||
@@ -309,7 +401,7 @@ func (s *Server) Run() {
|
|||||||
go func() {
|
go func() {
|
||||||
s.logger.Println("Profiled listening at: :6060")
|
s.logger.Println("Profiled listening at: :6060")
|
||||||
|
|
||||||
http.ListenAndServe(":6060", nil)
|
_ = http.ListenAndServe(":6060", nil)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,8 +432,18 @@ func (s *Server) Run() {
|
|||||||
s.logger.Panicf("Unable to parse: path=%s, err=%s", path, err)
|
s.logger.Panicf("Unable to parse: path=%s, err=%s", path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlTemplates.New(stripPrefix(path)).Parse(string(bytes))
|
if strings.HasSuffix(path, ".html") {
|
||||||
textTemplates.New(stripPrefix(path)).Parse(string(bytes))
|
_, err = htmlTemplates.New(stripPrefix(path)).Parse(string(bytes))
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Println("Unable to parse html template", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(path, ".txt") {
|
||||||
|
_, err = textTemplates.New(stripPrefix(path)).Parse(string(bytes))
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Println("Unable to parse text template", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +457,7 @@ func (s *Server) Run() {
|
|||||||
r.HandleFunc("/favicon.ico", staticHandler.ServeHTTP).Methods("GET")
|
r.HandleFunc("/favicon.ico", staticHandler.ServeHTTP).Methods("GET")
|
||||||
r.HandleFunc("/robots.txt", staticHandler.ServeHTTP).Methods("GET")
|
r.HandleFunc("/robots.txt", staticHandler.ServeHTTP).Methods("GET")
|
||||||
|
|
||||||
r.HandleFunc("/{filename:(?:favicon\\.ico|robots\\.txt|health\\.html)}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
r.HandleFunc("/{filename:(?:favicon\\.ico|robots\\.txt|health\\.html)}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
||||||
|
|
||||||
r.HandleFunc("/health.html", healthHandler).Methods("GET")
|
r.HandleFunc("/health.html", healthHandler).Methods("GET")
|
||||||
r.HandleFunc("/", s.viewHandler).Methods("GET")
|
r.HandleFunc("/", s.viewHandler).Methods("GET")
|
||||||
@@ -399,25 +501,38 @@ func (s *Server) Run() {
|
|||||||
|
|
||||||
r.HandleFunc("/{filename}/virustotal", s.virusTotalHandler).Methods("PUT")
|
r.HandleFunc("/{filename}/virustotal", s.virusTotalHandler).Methods("PUT")
|
||||||
r.HandleFunc("/{filename}/scan", s.scanHandler).Methods("PUT")
|
r.HandleFunc("/{filename}/scan", s.scanHandler).Methods("PUT")
|
||||||
r.HandleFunc("/put/{filename}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
r.HandleFunc("/put/{filename}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
||||||
r.HandleFunc("/upload/{filename}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
r.HandleFunc("/upload/{filename}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
||||||
r.HandleFunc("/{filename}", s.BasicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
r.HandleFunc("/{filename}", s.basicAuthHandler(http.HandlerFunc(s.putHandler))).Methods("PUT")
|
||||||
r.HandleFunc("/", s.BasicAuthHandler(http.HandlerFunc(s.postHandler))).Methods("POST")
|
r.HandleFunc("/", s.basicAuthHandler(http.HandlerFunc(s.postHandler))).Methods("POST")
|
||||||
// r.HandleFunc("/{page}", viewHandler).Methods("GET")
|
// r.HandleFunc("/{page}", viewHandler).Methods("GET")
|
||||||
|
|
||||||
r.HandleFunc("/{token}/{filename}/{deletionToken}", s.deleteHandler).Methods("DELETE")
|
r.HandleFunc("/{token}/{filename}/{deletionToken}", s.deleteHandler).Methods("DELETE")
|
||||||
|
|
||||||
r.NotFoundHandler = http.HandlerFunc(s.notFoundHandler)
|
r.NotFoundHandler = http.HandlerFunc(s.notFoundHandler)
|
||||||
|
|
||||||
mime.AddExtensionType(".md", "text/x-markdown")
|
_ = mime.AddExtensionType(".md", "text/x-markdown")
|
||||||
|
|
||||||
s.logger.Printf("Transfer.sh server started.\nusing temp folder: %s\nusing storage provider: %s", s.tempPath, s.storage.Type())
|
s.logger.Printf("Transfer.sh server started.\nusing temp folder: %s\nusing storage provider: %s", s.tempPath, s.storage.Type())
|
||||||
|
|
||||||
|
var cors func(http.Handler) http.Handler
|
||||||
|
if len(s.CorsDomains) > 0 {
|
||||||
|
cors = gorillaHandlers.CORS(
|
||||||
|
gorillaHandlers.AllowedHeaders([]string{"*"}),
|
||||||
|
gorillaHandlers.AllowedOrigins(strings.Split(s.CorsDomains, ",")),
|
||||||
|
gorillaHandlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
cors = func(h http.Handler) http.Handler {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
h := handlers.PanicHandler(
|
h := handlers.PanicHandler(
|
||||||
IPFilterHandler(
|
ipFilterHandler(
|
||||||
handlers.LogHandler(
|
handlers.LogHandler(
|
||||||
LoveHandler(
|
LoveHandler(
|
||||||
s.RedirectHandler(r)),
|
s.RedirectHandler(cors(r))),
|
||||||
handlers.NewLogOptions(s.logger.Printf, "_default_"),
|
handlers.NewLogOptions(s.logger.Printf, "_default_"),
|
||||||
),
|
),
|
||||||
s.ipFilterOptions,
|
s.ipFilterOptions,
|
||||||
@@ -435,7 +550,7 @@ func (s *Server) Run() {
|
|||||||
s.logger.Printf("listening on port: %v\n", s.ListenerString)
|
s.logger.Printf("listening on port: %v\n", s.ListenerString)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
srvr.ListenAndServe()
|
_ = srvr.ListenAndServe()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,6 +573,10 @@ func (s *Server) Run() {
|
|||||||
|
|
||||||
s.logger.Printf("---------------------------")
|
s.logger.Printf("---------------------------")
|
||||||
|
|
||||||
|
if s.purgeDays > 0 {
|
||||||
|
go s.purgeHandler()
|
||||||
|
}
|
||||||
|
|
||||||
term := make(chan os.Signal, 1)
|
term := make(chan os.Signal, 1)
|
||||||
signal.Notify(term, os.Interrupt)
|
signal.Notify(term, os.Interrupt)
|
||||||
signal.Notify(term, syscall.SIGTERM)
|
signal.Notify(term, syscall.SIGTERM)
|
||||||
|
@@ -1,54 +1,72 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"mime"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/s3"
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
"golang.org/x/net/context"
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/google"
|
"golang.org/x/oauth2/google"
|
||||||
"google.golang.org/api/drive/v3"
|
"google.golang.org/api/drive/v3"
|
||||||
"google.golang.org/api/googleapi"
|
"google.golang.org/api/googleapi"
|
||||||
|
|
||||||
|
"storj.io/common/fpath"
|
||||||
|
"storj.io/common/storj"
|
||||||
|
"storj.io/uplink"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Storage is the interface for storage operation
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error)
|
// Get retrieves a file from storage
|
||||||
Head(token string, filename string) (contentType string, contentLength uint64, err error)
|
Get(ctx context.Context, token string, filename string) (reader io.ReadCloser, contentLength uint64, err error)
|
||||||
Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error
|
// Head retrieves content length of a file from storage
|
||||||
Delete(token string, filename string) error
|
Head(ctx context.Context, token string, filename string) (contentLength uint64, err error)
|
||||||
|
// Put saves a file on storage
|
||||||
|
Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) error
|
||||||
|
// Delete removes a file from storage
|
||||||
|
Delete(ctx context.Context, token string, filename string) error
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
IsNotExist(err error) bool
|
IsNotExist(err error) bool
|
||||||
|
// Purge cleans up the storage
|
||||||
|
Purge(ctx context.Context, days time.Duration) error
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
Type() string
|
Type() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LocalStorage is a local storage
|
||||||
type LocalStorage struct {
|
type LocalStorage struct {
|
||||||
Storage
|
Storage
|
||||||
basedir string
|
basedir string
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewLocalStorage is the factory for LocalStorage
|
||||||
func NewLocalStorage(basedir string, logger *log.Logger) (*LocalStorage, error) {
|
func NewLocalStorage(basedir string, logger *log.Logger) (*LocalStorage, error) {
|
||||||
return &LocalStorage{basedir: basedir, logger: logger}, nil
|
return &LocalStorage{basedir: basedir, logger: logger}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
func (s *LocalStorage) Type() string {
|
func (s *LocalStorage) Type() string {
|
||||||
return "local"
|
return "local"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalStorage) Head(token string, filename string) (contentType string, contentLength uint64, err error) {
|
// Head retrieves content length of a file from storage
|
||||||
|
func (s *LocalStorage) Head(ctx context.Context, token string, filename string) (contentLength uint64, err error) {
|
||||||
path := filepath.Join(s.basedir, token, filename)
|
path := filepath.Join(s.basedir, token, filename)
|
||||||
|
|
||||||
var fi os.FileInfo
|
var fi os.FileInfo
|
||||||
@@ -58,12 +76,11 @@ func (s *LocalStorage) Head(token string, filename string) (contentType string,
|
|||||||
|
|
||||||
contentLength = uint64(fi.Size())
|
contentLength = uint64(fi.Size())
|
||||||
|
|
||||||
contentType = mime.TypeByExtension(filepath.Ext(filename))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) {
|
// Get retrieves a file from storage
|
||||||
|
func (s *LocalStorage) Get(ctx context.Context, token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
|
||||||
path := filepath.Join(s.basedir, token, filename)
|
path := filepath.Join(s.basedir, token, filename)
|
||||||
|
|
||||||
// content type , content length
|
// content type , content length
|
||||||
@@ -78,20 +95,42 @@ func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser,
|
|||||||
|
|
||||||
contentLength = uint64(fi.Size())
|
contentLength = uint64(fi.Size())
|
||||||
|
|
||||||
contentType = mime.TypeByExtension(filepath.Ext(filename))
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalStorage) Delete(token string, filename string) (err error) {
|
// Delete removes a file from storage
|
||||||
|
func (s *LocalStorage) Delete(ctx context.Context, token string, filename string) (err error) {
|
||||||
metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename))
|
metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename))
|
||||||
os.Remove(metadata)
|
_ = os.Remove(metadata)
|
||||||
|
|
||||||
path := filepath.Join(s.basedir, token, filename)
|
path := filepath.Join(s.basedir, token, filename)
|
||||||
err = os.Remove(path)
|
err = os.Remove(path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge cleans up the storage
|
||||||
|
func (s *LocalStorage) Purge(ctx context.Context, days time.Duration) (err error) {
|
||||||
|
err = filepath.Walk(s.basedir,
|
||||||
|
func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.ModTime().Before(time.Now().Add(-1 * days)) {
|
||||||
|
err = os.Remove(path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
func (s *LocalStorage) IsNotExist(err error) bool {
|
func (s *LocalStorage) IsNotExist(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
@@ -100,22 +139,24 @@ func (s *LocalStorage) IsNotExist(err error) bool {
|
|||||||
return os.IsNotExist(err)
|
return os.IsNotExist(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
|
// Put saves a file on storage
|
||||||
|
func (s *LocalStorage) Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
|
||||||
var f io.WriteCloser
|
var f io.WriteCloser
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
path := filepath.Join(s.basedir, token)
|
path := filepath.Join(s.basedir, token)
|
||||||
|
|
||||||
if err = os.Mkdir(path, 0700); err != nil && !os.IsExist(err) {
|
if err = os.MkdirAll(path, 0700); err != nil && !os.IsExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if f, err = os.OpenFile(filepath.Join(path, filename), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
|
f, err = os.OpenFile(filepath.Join(path, filename), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
defer safeClose(f)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
if _, err = io.Copy(f, reader); err != nil {
|
if _, err = io.Copy(f, reader); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -123,26 +164,38 @@ func (s *LocalStorage) Put(token string, filename string, reader io.Reader, cont
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// S3Storage is a storage backed by AWS S3
|
||||||
type S3Storage struct {
|
type S3Storage struct {
|
||||||
Storage
|
Storage
|
||||||
bucket string
|
bucket string
|
||||||
session *session.Session
|
session *session.Session
|
||||||
s3 *s3.S3
|
s3 *s3.S3
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
|
purgeDays time.Duration
|
||||||
noMultipart bool
|
noMultipart bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewS3Storage(accessKey, secretKey, bucketName, region, endpoint string, logger *log.Logger, disableMultipart bool, forcePathStyle bool) (*S3Storage, error) {
|
// NewS3Storage is the factory for S3Storage
|
||||||
|
func NewS3Storage(accessKey, secretKey, bucketName string, purgeDays int, region, endpoint string, disableMultipart bool, forcePathStyle bool, logger *log.Logger) (*S3Storage, error) {
|
||||||
sess := getAwsSession(accessKey, secretKey, region, endpoint, forcePathStyle)
|
sess := getAwsSession(accessKey, secretKey, region, endpoint, forcePathStyle)
|
||||||
|
|
||||||
return &S3Storage{bucket: bucketName, s3: s3.New(sess), session: sess, logger: logger, noMultipart: disableMultipart}, nil
|
return &S3Storage{
|
||||||
|
bucket: bucketName,
|
||||||
|
s3: s3.New(sess),
|
||||||
|
session: sess,
|
||||||
|
logger: logger,
|
||||||
|
noMultipart: disableMultipart,
|
||||||
|
purgeDays: time.Duration(purgeDays*24) * time.Hour,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
func (s *S3Storage) Type() string {
|
func (s *S3Storage) Type() string {
|
||||||
return "s3"
|
return "s3"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S3Storage) Head(token string, filename string) (contentType string, contentLength uint64, err error) {
|
// Head retrieves content length of a file from storage
|
||||||
|
func (s *S3Storage) Head(ctx context.Context, token string, filename string) (contentLength uint64, err error) {
|
||||||
key := fmt.Sprintf("%s/%s", token, filename)
|
key := fmt.Sprintf("%s/%s", token, filename)
|
||||||
|
|
||||||
headRequest := &s3.HeadObjectInput{
|
headRequest := &s3.HeadObjectInput{
|
||||||
@@ -151,15 +204,11 @@ func (s *S3Storage) Head(token string, filename string) (contentType string, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
// content type , content length
|
// content type , content length
|
||||||
response, err := s.s3.HeadObject(headRequest)
|
response, err := s.s3.HeadObjectWithContext(ctx, headRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.ContentType != nil {
|
|
||||||
contentType = *response.ContentType
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.ContentLength != nil {
|
if response.ContentLength != nil {
|
||||||
contentLength = uint64(*response.ContentLength)
|
contentLength = uint64(*response.ContentLength)
|
||||||
}
|
}
|
||||||
@@ -167,6 +216,13 @@ func (s *S3Storage) Head(token string, filename string) (contentType string, con
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Purge cleans up the storage
|
||||||
|
func (s *S3Storage) Purge(ctx context.Context, days time.Duration) (err error) {
|
||||||
|
// NOOP expiration is set at upload time
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
func (s *S3Storage) IsNotExist(err error) bool {
|
func (s *S3Storage) IsNotExist(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
@@ -182,7 +238,8 @@ func (s *S3Storage) IsNotExist(err error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) {
|
// Get retrieves a file from storage
|
||||||
|
func (s *S3Storage) Get(ctx context.Context, token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
|
||||||
key := fmt.Sprintf("%s/%s", token, filename)
|
key := fmt.Sprintf("%s/%s", token, filename)
|
||||||
|
|
||||||
getRequest := &s3.GetObjectInput{
|
getRequest := &s3.GetObjectInput{
|
||||||
@@ -190,15 +247,11 @@ func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, co
|
|||||||
Key: aws.String(key),
|
Key: aws.String(key),
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.s3.GetObject(getRequest)
|
response, err := s.s3.GetObjectWithContext(ctx, getRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.ContentType != nil {
|
|
||||||
contentType = *response.ContentType
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.ContentLength != nil {
|
if response.ContentLength != nil {
|
||||||
contentLength = uint64(*response.ContentLength)
|
contentLength = uint64(*response.ContentLength)
|
||||||
}
|
}
|
||||||
@@ -207,14 +260,15 @@ func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, co
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S3Storage) Delete(token string, filename string) (err error) {
|
// Delete removes a file from storage
|
||||||
|
func (s *S3Storage) Delete(ctx context.Context, token string, filename string) (err error) {
|
||||||
metadata := fmt.Sprintf("%s/%s.metadata", token, filename)
|
metadata := fmt.Sprintf("%s/%s.metadata", token, filename)
|
||||||
deleteRequest := &s3.DeleteObjectInput{
|
deleteRequest := &s3.DeleteObjectInput{
|
||||||
Bucket: aws.String(s.bucket),
|
Bucket: aws.String(s.bucket),
|
||||||
Key: aws.String(metadata),
|
Key: aws.String(metadata),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.s3.DeleteObject(deleteRequest)
|
_, err = s.s3.DeleteObjectWithContext(ctx, deleteRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -225,12 +279,13 @@ func (s *S3Storage) Delete(token string, filename string) (err error) {
|
|||||||
Key: aws.String(key),
|
Key: aws.String(key),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.s3.DeleteObject(deleteRequest)
|
_, err = s.s3.DeleteObjectWithContext(ctx, deleteRequest)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
|
// Put saves a file on storage
|
||||||
|
func (s *S3Storage) Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
|
||||||
key := fmt.Sprintf("%s/%s", token, filename)
|
key := fmt.Sprintf("%s/%s", token, filename)
|
||||||
|
|
||||||
s.logger.Printf("Uploading file %s to S3 Bucket", filename)
|
s.logger.Printf("Uploading file %s to S3 Bucket", filename)
|
||||||
@@ -243,32 +298,38 @@ func (s *S3Storage) Put(token string, filename string, reader io.Reader, content
|
|||||||
|
|
||||||
// Create an uploader with the session and custom options
|
// Create an uploader with the session and custom options
|
||||||
uploader := s3manager.NewUploader(s.session, func(u *s3manager.Uploader) {
|
uploader := s3manager.NewUploader(s.session, func(u *s3manager.Uploader) {
|
||||||
u.PartSize = (1 << 20) * 5 // The minimum/default allowed part size is 5MB
|
|
||||||
u.Concurrency = concurrency // default is 5
|
u.Concurrency = concurrency // default is 5
|
||||||
u.MaxUploadParts = concurrency
|
|
||||||
u.LeavePartsOnError = false
|
u.LeavePartsOnError = false
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err = uploader.Upload(&s3manager.UploadInput{
|
var expire *time.Time
|
||||||
Bucket: aws.String(s.bucket),
|
if s.purgeDays.Hours() > 0 {
|
||||||
Key: aws.String(key),
|
expire = aws.Time(time.Now().Add(s.purgeDays))
|
||||||
Body: reader,
|
}
|
||||||
|
|
||||||
|
_, err = uploader.UploadWithContext(ctx, &s3manager.UploadInput{
|
||||||
|
Bucket: aws.String(s.bucket),
|
||||||
|
Key: aws.String(key),
|
||||||
|
Body: reader,
|
||||||
|
Expires: expire,
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GDrive is a storage backed by GDrive
|
||||||
type GDrive struct {
|
type GDrive struct {
|
||||||
service *drive.Service
|
service *drive.Service
|
||||||
rootId string
|
rootID string
|
||||||
basedir string
|
basedir string
|
||||||
localConfigPath string
|
localConfigPath string
|
||||||
chunkSize int
|
chunkSize int
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) {
|
// NewGDriveStorage is the factory for GDrive
|
||||||
b, err := ioutil.ReadFile(clientJsonFilepath)
|
func NewGDriveStorage(clientJSONFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) {
|
||||||
|
b, err := ioutil.ReadFile(clientJSONFilepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -279,13 +340,14 @@ func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
srv, err := drive.New(getGDriveClient(config, localConfigPath, logger))
|
// ToDo: Upgrade deprecated version
|
||||||
|
srv, err := drive.New(getGDriveClient(context.TODO(), config, localConfigPath, logger)) // nolint: staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkSize = chunkSize * 1024 * 1024
|
chunkSize = chunkSize * 1024 * 1024
|
||||||
storage := &GDrive{service: srv, basedir: basedir, rootId: "", localConfigPath: localConfigPath, chunkSize: chunkSize, logger: logger}
|
storage := &GDrive{service: srv, basedir: basedir, rootID: "", localConfigPath: localConfigPath, chunkSize: chunkSize, logger: logger}
|
||||||
err = storage.setupRoot()
|
err = storage.setupRoot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -294,26 +356,26 @@ func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir
|
|||||||
return storage, nil
|
return storage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const GDriveRootConfigFile = "root_id.conf"
|
const gdriveRootConfigFile = "root_id.conf"
|
||||||
const GDriveTokenJsonFile = "token.json"
|
const gdriveTokenJSONFile = "token.json"
|
||||||
const GDriveDirectoryMimeType = "application/vnd.google-apps.folder"
|
const gdriveDirectoryMimeType = "application/vnd.google-apps.folder"
|
||||||
|
|
||||||
func (s *GDrive) setupRoot() error {
|
func (s *GDrive) setupRoot() error {
|
||||||
rootFileConfig := filepath.Join(s.localConfigPath, GDriveRootConfigFile)
|
rootFileConfig := filepath.Join(s.localConfigPath, gdriveRootConfigFile)
|
||||||
|
|
||||||
rootId, err := ioutil.ReadFile(rootFileConfig)
|
rootID, err := ioutil.ReadFile(rootFileConfig)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(rootId) != "" {
|
if string(rootID) != "" {
|
||||||
s.rootId = string(rootId)
|
s.rootID = string(rootID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
dir := &drive.File{
|
dir := &drive.File{
|
||||||
Name: s.basedir,
|
Name: s.basedir,
|
||||||
MimeType: GDriveDirectoryMimeType,
|
MimeType: gdriveDirectoryMimeType,
|
||||||
}
|
}
|
||||||
|
|
||||||
di, err := s.service.Files.Create(dir).Fields("id").Do()
|
di, err := s.service.Files.Create(dir).Fields("id").Do()
|
||||||
@@ -321,8 +383,8 @@ func (s *GDrive) setupRoot() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.rootId = di.Id
|
s.rootID = di.Id
|
||||||
err = ioutil.WriteFile(rootFileConfig, []byte(s.rootId), os.FileMode(0600))
|
err = ioutil.WriteFile(rootFileConfig, []byte(s.rootID), os.FileMode(0600))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -338,13 +400,13 @@ func (s *GDrive) list(nextPageToken string, q string) (*drive.FileList, error) {
|
|||||||
return s.service.Files.List().Fields("nextPageToken, files(id, name, mimeType)").Q(q).PageToken(nextPageToken).Do()
|
return s.service.Files.List().Fields("nextPageToken, files(id, name, mimeType)").Q(q).PageToken(nextPageToken).Do()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GDrive) findId(filename string, token string) (string, error) {
|
func (s *GDrive) findID(filename string, token string) (string, error) {
|
||||||
filename = strings.Replace(filename, `'`, `\'`, -1)
|
filename = strings.Replace(filename, `'`, `\'`, -1)
|
||||||
filename = strings.Replace(filename, `"`, `\"`, -1)
|
filename = strings.Replace(filename, `"`, `\"`, -1)
|
||||||
|
|
||||||
fileId, tokenId, nextPageToken := "", "", ""
|
fileID, tokenID, nextPageToken := "", "", ""
|
||||||
|
|
||||||
q := fmt.Sprintf("'%s' in parents and name='%s' and mimeType='%s' and trashed=false", s.rootId, token, GDriveDirectoryMimeType)
|
q := fmt.Sprintf("'%s' in parents and name='%s' and mimeType='%s' and trashed=false", s.rootID, token, gdriveDirectoryMimeType)
|
||||||
l, err := s.list(nextPageToken, q)
|
l, err := s.list(nextPageToken, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -352,7 +414,7 @@ func (s *GDrive) findId(filename string, token string) (string, error) {
|
|||||||
|
|
||||||
for 0 < len(l.Files) {
|
for 0 < len(l.Files) {
|
||||||
for _, fi := range l.Files {
|
for _, fi := range l.Files {
|
||||||
tokenId = fi.Id
|
tokenID = fi.Id
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,15 +423,18 @@ func (s *GDrive) findId(filename string, token string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
l, err = s.list(l.NextPageToken, q)
|
l, err = s.list(l.NextPageToken, q)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if filename == "" {
|
if filename == "" {
|
||||||
return tokenId, nil
|
return tokenID, nil
|
||||||
} else if tokenId == "" {
|
} else if tokenID == "" {
|
||||||
return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
|
return "", fmt.Errorf("cannot find file %s/%s", token, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
q = fmt.Sprintf("'%s' in parents and name='%s' and mimeType!='%s' and trashed=false", tokenId, filename, GDriveDirectoryMimeType)
|
q = fmt.Sprintf("'%s' in parents and name='%s' and mimeType!='%s' and trashed=false", tokenID, filename, gdriveDirectoryMimeType)
|
||||||
l, err = s.list(nextPageToken, q)
|
l, err = s.list(nextPageToken, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -378,7 +443,7 @@ func (s *GDrive) findId(filename string, token string) (string, error) {
|
|||||||
for 0 < len(l.Files) {
|
for 0 < len(l.Files) {
|
||||||
for _, fi := range l.Files {
|
for _, fi := range l.Files {
|
||||||
|
|
||||||
fileId = fi.Id
|
fileID = fi.Id
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,58 +452,63 @@ func (s *GDrive) findId(filename string, token string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
l, err = s.list(l.NextPageToken, q)
|
l, err = s.list(l.NextPageToken, q)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileId == "" {
|
if fileID == "" {
|
||||||
return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
|
return "", fmt.Errorf("cannot find file %s/%s", token, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileId, nil
|
return fileID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
func (s *GDrive) Type() string {
|
func (s *GDrive) Type() string {
|
||||||
return "gdrive"
|
return "gdrive"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GDrive) Head(token string, filename string) (contentType string, contentLength uint64, err error) {
|
// Head retrieves content length of a file from storage
|
||||||
var fileId string
|
func (s *GDrive) Head(ctx context.Context, token string, filename string) (contentLength uint64, err error) {
|
||||||
fileId, err = s.findId(filename, token)
|
var fileID string
|
||||||
|
fileID, err = s.findID(filename, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var fi *drive.File
|
var fi *drive.File
|
||||||
if fi, err = s.service.Files.Get(fileId).Fields("mimeType", "size").Do(); err != nil {
|
if fi, err = s.service.Files.Get(fileID).Fields("size").Do(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
contentLength = uint64(fi.Size)
|
contentLength = uint64(fi.Size)
|
||||||
|
|
||||||
contentType = fi.MimeType
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) {
|
// Get retrieves a file from storage
|
||||||
var fileId string
|
func (s *GDrive) Get(ctx context.Context, token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
|
||||||
fileId, err = s.findId(filename, token)
|
var fileID string
|
||||||
|
fileID, err = s.findID(filename, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var fi *drive.File
|
var fi *drive.File
|
||||||
fi, err = s.service.Files.Get(fileId).Fields("mimeType", "size", "md5Checksum").Do()
|
fi, err = s.service.Files.Get(fileID).Fields("size", "md5Checksum").Do()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if !s.hasChecksum(fi) {
|
if !s.hasChecksum(fi) {
|
||||||
err = fmt.Errorf("Cannot find file %s/%s", token, filename)
|
err = fmt.Errorf("cannot find file %s/%s", token, filename)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
contentLength = uint64(fi.Size)
|
contentLength = uint64(fi.Size)
|
||||||
contentType = fi.MimeType
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
var res *http.Response
|
var res *http.Response
|
||||||
res, err = s.service.Files.Get(fileId).Context(ctx).Download()
|
res, err = s.service.Files.Get(fileID).Context(ctx).Download()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -448,41 +518,79 @@ func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, conte
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GDrive) Delete(token string, filename string) (err error) {
|
// Delete removes a file from storage
|
||||||
metadata, _ := s.findId(fmt.Sprintf("%s.metadata", filename), token)
|
func (s *GDrive) Delete(ctx context.Context, token string, filename string) (err error) {
|
||||||
s.service.Files.Delete(metadata).Do()
|
metadata, _ := s.findID(fmt.Sprintf("%s.metadata", filename), token)
|
||||||
|
_ = s.service.Files.Delete(metadata).Do()
|
||||||
|
|
||||||
var fileId string
|
var fileID string
|
||||||
fileId, err = s.findId(filename, token)
|
fileID, err = s.findID(filename, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.service.Files.Delete(fileId).Do()
|
err = s.service.Files.Delete(fileID).Do()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GDrive) IsNotExist(err error) bool {
|
// Purge cleans up the storage
|
||||||
|
func (s *GDrive) Purge(ctx context.Context, days time.Duration) (err error) {
|
||||||
|
nextPageToken := ""
|
||||||
|
|
||||||
|
expirationDate := time.Now().Add(-1 * days).Format(time.RFC3339)
|
||||||
|
q := fmt.Sprintf("'%s' in parents and modifiedTime < '%s' and mimeType!='%s' and trashed=false", s.rootID, expirationDate, gdriveDirectoryMimeType)
|
||||||
|
l, err := s.list(nextPageToken, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if e, ok := err.(*googleapi.Error); ok {
|
return err
|
||||||
return e.Code == http.StatusNotFound
|
}
|
||||||
|
|
||||||
|
for 0 < len(l.Files) {
|
||||||
|
for _, fi := range l.Files {
|
||||||
|
err = s.service.Files.Delete(fi.Id).Do()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if l.NextPageToken == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err = s.list(l.NextPageToken, q)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
|
func (s *GDrive) IsNotExist(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if e, ok := err.(*googleapi.Error); ok {
|
||||||
|
return e.Code == http.StatusNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GDrive) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
|
// Put saves a file on storage
|
||||||
dirId, err := s.findId("", token)
|
func (s *GDrive) Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
|
||||||
|
dirID, err := s.findID("", token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if dirId == "" {
|
if dirID == "" {
|
||||||
dir := &drive.File{
|
dir := &drive.File{
|
||||||
Name: token,
|
Name: token,
|
||||||
Parents: []string{s.rootId},
|
Parents: []string{s.rootID},
|
||||||
MimeType: GDriveDirectoryMimeType,
|
MimeType: gdriveDirectoryMimeType,
|
||||||
|
Size: int64(contentLength),
|
||||||
}
|
}
|
||||||
|
|
||||||
di, err := s.service.Files.Create(dir).Fields("id").Do()
|
di, err := s.service.Files.Create(dir).Fields("id").Do()
|
||||||
@@ -490,17 +598,16 @@ func (s *GDrive) Put(token string, filename string, reader io.Reader, contentTyp
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dirId = di.Id
|
dirID = di.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instantiate empty drive file
|
// Instantiate empty drive file
|
||||||
dst := &drive.File{
|
dst := &drive.File{
|
||||||
Name: filename,
|
Name: filename,
|
||||||
Parents: []string{dirId},
|
Parents: []string{dirID},
|
||||||
MimeType: contentType,
|
MimeType: contentType,
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
_, err = s.service.Files.Create(dst).Context(ctx).Media(reader, googleapi.ChunkSize(s.chunkSize)).Do()
|
_, err = s.service.Files.Create(dst).Context(ctx).Media(reader, googleapi.ChunkSize(s.chunkSize)).Do()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -511,19 +618,19 @@ func (s *GDrive) Put(token string, filename string, reader io.Reader, contentTyp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a token, saves the token, then returns the generated client.
|
// Retrieve a token, saves the token, then returns the generated client.
|
||||||
func getGDriveClient(config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client {
|
func getGDriveClient(ctx context.Context, config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client {
|
||||||
tokenFile := filepath.Join(localConfigPath, GDriveTokenJsonFile)
|
tokenFile := filepath.Join(localConfigPath, gdriveTokenJSONFile)
|
||||||
tok, err := gDriveTokenFromFile(tokenFile)
|
tok, err := gDriveTokenFromFile(tokenFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tok = getGDriveTokenFromWeb(config, logger)
|
tok = getGDriveTokenFromWeb(ctx, config, logger)
|
||||||
saveGDriveToken(tokenFile, tok, logger)
|
saveGDriveToken(tokenFile, tok, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
return config.Client(context.Background(), tok)
|
return config.Client(ctx, tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request a token from the web, then returns the retrieved token.
|
// Request a token from the web, then returns the retrieved token.
|
||||||
func getGDriveTokenFromWeb(config *oauth2.Config, logger *log.Logger) *oauth2.Token {
|
func getGDriveTokenFromWeb(ctx context.Context, config *oauth2.Config, logger *log.Logger) *oauth2.Token {
|
||||||
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
|
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
|
||||||
fmt.Printf("Go to the following link in your browser then type the "+
|
fmt.Printf("Go to the following link in your browser then type the "+
|
||||||
"authorization code: \n%v\n", authURL)
|
"authorization code: \n%v\n", authURL)
|
||||||
@@ -533,7 +640,7 @@ func getGDriveTokenFromWeb(config *oauth2.Config, logger *log.Logger) *oauth2.To
|
|||||||
logger.Fatalf("Unable to read authorization code %v", err)
|
logger.Fatalf("Unable to read authorization code %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tok, err := config.Exchange(context.TODO(), authCode)
|
tok, err := config.Exchange(ctx, authCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatalf("Unable to retrieve token from web %v", err)
|
logger.Fatalf("Unable to retrieve token from web %v", err)
|
||||||
}
|
}
|
||||||
@@ -543,7 +650,7 @@ func getGDriveTokenFromWeb(config *oauth2.Config, logger *log.Logger) *oauth2.To
|
|||||||
// Retrieves a token from a local file.
|
// Retrieves a token from a local file.
|
||||||
func gDriveTokenFromFile(file string) (*oauth2.Token, error) {
|
func gDriveTokenFromFile(file string) (*oauth2.Token, error) {
|
||||||
f, err := os.Open(file)
|
f, err := os.Open(file)
|
||||||
defer f.Close()
|
defer safeClose(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -556,10 +663,150 @@ func gDriveTokenFromFile(file string) (*oauth2.Token, error) {
|
|||||||
func saveGDriveToken(path string, token *oauth2.Token, logger *log.Logger) {
|
func saveGDriveToken(path string, token *oauth2.Token, logger *log.Logger) {
|
||||||
logger.Printf("Saving credential file to: %s\n", path)
|
logger.Printf("Saving credential file to: %s\n", path)
|
||||||
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
defer f.Close()
|
defer safeClose(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatalf("Unable to cache oauth token: %v", err)
|
logger.Fatalf("Unable to cache oauth token: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
json.NewEncoder(f).Encode(token)
|
err = json.NewEncoder(f).Encode(token)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("Unable to encode oauth token: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorjStorage is a storage backed by Storj
|
||||||
|
type StorjStorage struct {
|
||||||
|
Storage
|
||||||
|
project *uplink.Project
|
||||||
|
bucket *uplink.Bucket
|
||||||
|
purgeDays time.Duration
|
||||||
|
logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStorjStorage is the factory for StorjStorage
|
||||||
|
func NewStorjStorage(access, bucket string, purgeDays int, logger *log.Logger) (*StorjStorage, error) {
|
||||||
|
var instance StorjStorage
|
||||||
|
var err error
|
||||||
|
|
||||||
|
pCtx := context.TODO()
|
||||||
|
|
||||||
|
ctx := fpath.WithTempData(pCtx, "", true)
|
||||||
|
|
||||||
|
uplConf := &uplink.Config{
|
||||||
|
UserAgent: "transfer-sh",
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedAccess, err := uplink.ParseAccess(access)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.project, err = uplConf.OpenProject(ctx, parsedAccess)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.bucket, err = instance.project.EnsureBucket(ctx, bucket)
|
||||||
|
if err != nil {
|
||||||
|
//Ignoring the error to return the one that occurred first, but try to clean up.
|
||||||
|
_ = instance.project.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.purgeDays = time.Duration(purgeDays*24) * time.Hour
|
||||||
|
|
||||||
|
instance.logger = logger
|
||||||
|
|
||||||
|
return &instance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the storage type
|
||||||
|
func (s *StorjStorage) Type() string {
|
||||||
|
return "storj"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head retrieves content length of a file from storage
|
||||||
|
func (s *StorjStorage) Head(ctx context.Context, token string, filename string) (contentLength uint64, err error) {
|
||||||
|
key := storj.JoinPaths(token, filename)
|
||||||
|
|
||||||
|
obj, err := s.project.StatObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contentLength = uint64(obj.System.ContentLength)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves a file from storage
|
||||||
|
func (s *StorjStorage) Get(ctx context.Context, token string, filename string) (reader io.ReadCloser, contentLength uint64, err error) {
|
||||||
|
key := storj.JoinPaths(token, filename)
|
||||||
|
|
||||||
|
s.logger.Printf("Getting file %s from Storj Bucket", filename)
|
||||||
|
|
||||||
|
download, err := s.project.DownloadObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contentLength = uint64(download.Info().System.ContentLength)
|
||||||
|
|
||||||
|
reader = download
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes a file from storage
|
||||||
|
func (s *StorjStorage) Delete(ctx context.Context, token string, filename string) (err error) {
|
||||||
|
key := storj.JoinPaths(token, filename)
|
||||||
|
|
||||||
|
s.logger.Printf("Deleting file %s from Storj Bucket", filename)
|
||||||
|
|
||||||
|
_, err = s.project.DeleteObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge cleans up the storage
|
||||||
|
func (s *StorjStorage) Purge(ctx context.Context, days time.Duration) (err error) {
|
||||||
|
// NOOP expiration is set at upload time
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put saves a file on storage
|
||||||
|
func (s *StorjStorage) Put(ctx context.Context, token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
|
||||||
|
key := storj.JoinPaths(token, filename)
|
||||||
|
|
||||||
|
s.logger.Printf("Uploading file %s to Storj Bucket", filename)
|
||||||
|
|
||||||
|
var uploadOptions *uplink.UploadOptions
|
||||||
|
if s.purgeDays.Hours() > 0 {
|
||||||
|
uploadOptions = &uplink.UploadOptions{Expires: time.Now().Add(s.purgeDays)}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer, err := s.project.UploadObject(fpath.WithTempData(ctx, "", true), s.bucket.Name, key, uploadOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := io.Copy(writer, reader)
|
||||||
|
if err != nil || uint64(n) != contentLength {
|
||||||
|
//Ignoring the error to return the one that occurred first, but try to clean up.
|
||||||
|
_ = writer.Abort()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = writer.SetCustomMetadata(ctx, uplink.CustomMetadata{"content-type": contentType})
|
||||||
|
if err != nil {
|
||||||
|
//Ignoring the error to return the one that occurred first, but try to clean up.
|
||||||
|
_ = writer.Abort()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writer.Commit()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotExist indicates if a file doesn't exist on storage
|
||||||
|
func (s *StorjStorage) IsNotExist(err error) bool {
|
||||||
|
return errors.Is(err, uplink.ErrObjectNotFound)
|
||||||
}
|
}
|
||||||
|
45
server/token.go
Normal file
45
server/token.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
|
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 server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SYMBOLS characters used for short-urls
|
||||||
|
SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
)
|
||||||
|
|
||||||
|
// generate a token
|
||||||
|
func token(length int) string {
|
||||||
|
result := ""
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
x := rand.Intn(len(SYMBOLS) - 1)
|
||||||
|
result = string(SYMBOLS[x]) + result
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
15
server/token_test.go
Normal file
15
server/token_test.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func BenchmarkTokenConcat(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = token(5) + token(5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkTokenLonger(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = token(10)
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,8 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
Copyright (c) 2014-2017 DutchCoders [https://github.com/dutchcoders/]
|
||||||
|
Copyright (c) 2018-2020 Andrea Spacca.
|
||||||
|
Copyright (c) 2020- Andrea Spacca and Stefan Benten.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -25,6 +27,8 @@ THE SOFTWARE.
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
@@ -47,8 +51,7 @@ func getAwsSession(accessKey, secretKey, region, endpoint string, forcePathStyle
|
|||||||
}
|
}
|
||||||
|
|
||||||
func formatNumber(format string, s uint64) string {
|
func formatNumber(format string, s uint64) string {
|
||||||
|
return renderFloat(format, float64(s))
|
||||||
return RenderFloat(format, float64(s))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var renderFloatPrecisionMultipliers = [10]float64{
|
var renderFloatPrecisionMultipliers = [10]float64{
|
||||||
@@ -77,7 +80,7 @@ var renderFloatPrecisionRounders = [10]float64{
|
|||||||
0.0000000005,
|
0.0000000005,
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderFloat(format string, n float64) string {
|
func renderFloat(format string, n float64) string {
|
||||||
// Special cases:
|
// Special cases:
|
||||||
// NaN = "NaN"
|
// NaN = "NaN"
|
||||||
// +Inf = "+Infinity"
|
// +Inf = "+Infinity"
|
||||||
@@ -125,7 +128,7 @@ func RenderFloat(format string, n float64) string {
|
|||||||
// +0000
|
// +0000
|
||||||
if formatDirectiveIndices[0] == 0 {
|
if formatDirectiveIndices[0] == 0 {
|
||||||
if formatDirectiveChars[formatDirectiveIndices[0]] != '+' {
|
if formatDirectiveChars[formatDirectiveIndices[0]] != '+' {
|
||||||
panic("RenderFloat(): invalid positive sign directive")
|
panic("renderFloat(): invalid positive sign directive")
|
||||||
}
|
}
|
||||||
positiveStr = "+"
|
positiveStr = "+"
|
||||||
formatDirectiveIndices = formatDirectiveIndices[1:]
|
formatDirectiveIndices = formatDirectiveIndices[1:]
|
||||||
@@ -139,7 +142,7 @@ func RenderFloat(format string, n float64) string {
|
|||||||
// 000,000.00
|
// 000,000.00
|
||||||
if len(formatDirectiveIndices) == 2 {
|
if len(formatDirectiveIndices) == 2 {
|
||||||
if (formatDirectiveIndices[1] - formatDirectiveIndices[0]) != 4 {
|
if (formatDirectiveIndices[1] - formatDirectiveIndices[0]) != 4 {
|
||||||
panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
|
panic("renderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
|
||||||
}
|
}
|
||||||
thousandStr = string(formatDirectiveChars[formatDirectiveIndices[0]])
|
thousandStr = string(formatDirectiveChars[formatDirectiveIndices[0]])
|
||||||
formatDirectiveIndices = formatDirectiveIndices[1:]
|
formatDirectiveIndices = formatDirectiveIndices[1:]
|
||||||
@@ -199,8 +202,8 @@ func RenderFloat(format string, n float64) string {
|
|||||||
return signStr + intStr + decimalStr + fracStr
|
return signStr + intStr + decimalStr + fracStr
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderInteger(format string, n int) string {
|
func renderInteger(format string, n int) string {
|
||||||
return RenderFloat(format, float64(n))
|
return renderFloat(format, float64(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request.RemoteAddress contains port, which we want to remove i.e.:
|
// Request.RemoteAddress contains port, which we want to remove i.e.:
|
||||||
@@ -253,3 +256,37 @@ func acceptsHTML(hdr http.Header) bool {
|
|||||||
|
|
||||||
return (false)
|
return (false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatSize(size int64) string {
|
||||||
|
sizeFloat := float64(size)
|
||||||
|
base := math.Log(sizeFloat) / math.Log(1024)
|
||||||
|
|
||||||
|
sizeOn := math.Pow(1024, base-math.Floor(base))
|
||||||
|
|
||||||
|
var round float64
|
||||||
|
pow := math.Pow(10, float64(2))
|
||||||
|
digit := pow * sizeOn
|
||||||
|
round = math.Floor(digit)
|
||||||
|
|
||||||
|
newVal := round / pow
|
||||||
|
|
||||||
|
var suffixes [5]string
|
||||||
|
suffixes[0] = "B"
|
||||||
|
suffixes[1] = "KB"
|
||||||
|
suffixes[2] = "MB"
|
||||||
|
suffixes[3] = "GB"
|
||||||
|
suffixes[4] = "TB"
|
||||||
|
|
||||||
|
getSuffix := suffixes[int(math.Floor(base))]
|
||||||
|
return fmt.Sprintf("%s %s", strconv.FormatFloat(newVal, 'f', -1, 64), getSuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeClose(c io.Closer) {
|
||||||
|
if c == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.Close(); err != nil {
|
||||||
|
fmt.Println("Received close error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -26,10 +26,8 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
_ "github.com/PuerkitoBio/ghost/handlers"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
virustotal "github.com/dutchcoders/go-virustotal"
|
virustotal "github.com/dutchcoders/go-virustotal"
|
||||||
@@ -47,18 +45,16 @@ func (s *Server) virusTotalHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
vt, err := virustotal.NewVirusTotal(s.VirusTotalKey)
|
vt, err := virustotal.NewVirusTotal(s.VirusTotalKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
var reader io.Reader
|
reader := r.Body
|
||||||
|
|
||||||
reader = r.Body
|
|
||||||
|
|
||||||
result, err := vt.Scan(filename, reader)
|
result, err := vt.Scan(filename, reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Println(result)
|
s.logger.Println(result)
|
||||||
w.Write([]byte(fmt.Sprintf("%v\n", result.Permalink)))
|
_, _ = w.Write([]byte(fmt.Sprintf("%v\n", result.Permalink)))
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user