Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7524b20dca | ||
|
|
2ded6a96c8 | ||
|
|
5cad9f0f0d | ||
|
|
9ed714923f | ||
|
|
6ae2f6feb8 | ||
|
|
e10634078a | ||
|
|
8ee0418dd8 | ||
|
|
b1094e178a | ||
|
|
a2a97bc7ef | ||
|
|
aa39d634e9 | ||
|
|
0fa2cc24b9 | ||
|
|
dba3268237 | ||
|
|
0338f9ede4 | ||
|
|
761451e9c1 | ||
|
|
e5b13e465d | ||
|
|
2e2c8204bb | ||
|
|
505921004b | ||
|
|
259594ebce | ||
|
|
eaeeba3d9e | ||
|
|
db1fdd2e93 | ||
|
|
5992d28e9e | ||
|
|
fb23ea6add | ||
|
|
693f7965f1 | ||
|
|
e6edaf76c8 | ||
|
|
8c2e824da7 | ||
|
|
1ab6e9fb7c | ||
|
|
2d372e1b00 | ||
|
|
dfb181f801 | ||
|
|
29b180ea1c | ||
|
|
92ef5916fe | ||
|
|
dca9097d6c | ||
|
|
1c462b8e96 | ||
|
|
e59c9e327d | ||
|
|
8546fa294f | ||
|
|
0a195b9f95 | ||
|
|
62f3953aea | ||
|
|
bc87dcd32c | ||
|
|
cdb49680e3 | ||
|
|
d9e583fea7 | ||
|
|
0281aee56f | ||
|
|
7aa3230649 | ||
|
|
398ac49aa9 | ||
|
|
ac544738a2 | ||
|
|
d574be9e89 | ||
|
|
b747ab19b7 | ||
|
|
e8091dcb24 | ||
|
|
8e4629a0c4 | ||
|
|
9b57eb1060 | ||
|
|
47e1975c12 | ||
|
|
4bab354c99 | ||
|
|
8a84a42cf5 | ||
|
|
3fd8ee7783 | ||
|
|
3c1a852435 | ||
|
|
4894230482 | ||
|
|
d53426b580 | ||
|
|
1429963e76 | ||
|
|
26f31b91fb | ||
|
|
aab3224d2c | ||
|
|
a8d2651a1d | ||
|
|
c476d4b9f5 | ||
|
|
c07e204b97 | ||
|
|
1794bd5241 | ||
|
|
d3daafe9fe | ||
|
|
3f7caf97c5 | ||
|
|
6695a88f6f | ||
|
|
1d1f7da975 | ||
|
|
370b9b11c0 | ||
|
|
4f4cf13e76 | ||
|
|
a33eab2a8c | ||
|
|
05b911143a | ||
|
|
5506135d32 | ||
|
|
8261511881 | ||
|
|
65159848c2 | ||
|
|
d6e9ed6272 | ||
|
|
8cdb79c915 | ||
|
|
cb37a50abc | ||
|
|
88e391abf5 | ||
|
|
cddab82215 | ||
|
|
44fc212146 | ||
|
|
60fd41f11c |
No files matched your search
@@ -1,5 +0,0 @@
|
||||
# These owners will be the default owners for everything in the repo.
|
||||
# Unless a later match takes precedence, @o1egl will be requested for
|
||||
# review when someone opens a pull request.
|
||||
|
||||
* @o1egl
|
||||
@@ -0,0 +1,79 @@
|
||||
name: dev
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'dev_v*'
|
||||
|
||||
jobs:
|
||||
test-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go test -race -v ./...
|
||||
lint-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.59
|
||||
working-directory: backend
|
||||
format-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go fmt ./...
|
||||
lint-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- working-directory: frontend
|
||||
run: npm i eslint && npm run lint
|
||||
|
||||
push_dev_to_registry:
|
||||
name: Push dev image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.0.0
|
||||
# Workaround to fix error:
|
||||
# failed to push: failed to copy: io: read/write on closed pipe
|
||||
# See https://github.com/docker/build-push-action/issues/761
|
||||
with:
|
||||
driver-opts: |
|
||||
image=moby/buildkit:v0.10.6
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: gtstef/filebrowser
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
+35
-13
@@ -1,28 +1,50 @@
|
||||
name: main
|
||||
name: main job
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
test-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21.1
|
||||
- run: cd backend && go test -race -v ./...
|
||||
lint:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go test -race -v ./...
|
||||
lint-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21.1
|
||||
- run: cd backend && go fmt ./...
|
||||
go-version: 'stable'
|
||||
- uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.59
|
||||
working-directory: backend
|
||||
format-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go fmt ./...
|
||||
lint-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- working-directory: frontend
|
||||
run: npm i eslint && npm run lint
|
||||
|
||||
push_latest_to_registry:
|
||||
needs: [lint, test]
|
||||
needs: [lint-frontend, lint-backend, test-backend, format-backend]
|
||||
name: Push latest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -45,7 +67,7 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: gtstef/filebrowser
|
||||
- name: Build and push
|
||||
|
||||
@@ -4,26 +4,48 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'v\d+.\d+.\d+'
|
||||
- 'dev_v*'
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
test-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21.1
|
||||
- run: cd backend && go test -race -v ./...
|
||||
lint:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go test -race -v ./...
|
||||
lint-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21.1
|
||||
- run: cd backend && go fmt ./...
|
||||
go-version: 'stable'
|
||||
- uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.59
|
||||
working-directory: backend
|
||||
format-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go fmt ./...
|
||||
lint-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- working-directory: frontend
|
||||
run: npm i eslint && npm run lint
|
||||
|
||||
push_pr_to_registry:
|
||||
needs: [lint, test]
|
||||
name: Push PR
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -46,7 +68,7 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: gtstef/filebrowser
|
||||
- name: Build and push
|
||||
@@ -56,4 +78,4 @@ jobs:
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -3,29 +3,76 @@ name: release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'v\d+.\d+.\d+'
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
test:
|
||||
create_release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Extract branch name
|
||||
shell: bash
|
||||
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
|
||||
id: extract_branch
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
go-version: 1.21.1
|
||||
- run: cd backend && go test -race -v ./...
|
||||
lint:
|
||||
tag_name: ${{ steps.extract_branch.outputs.branch }}
|
||||
release_name: ${{ steps.extract_branch.outputs.branch }}
|
||||
body: |
|
||||
Please see usage on [readme](https://github.com/gtsteffaniak/filebrowser).
|
||||
Docker repo: https://hub.docker.com/r/gtstef/filebrowser/tags
|
||||
draft: false
|
||||
prerelease: false
|
||||
test-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.21.1
|
||||
- run: cd backend && go fmt ./...
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go test -race -v ./...
|
||||
lint-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: v1.59
|
||||
working-directory: backend
|
||||
format-backend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 'stable'
|
||||
- working-directory: backend
|
||||
run: go fmt ./...
|
||||
lint-frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- working-directory: frontend
|
||||
run: npm i eslint && npm run lint
|
||||
|
||||
push_release_to_registry:
|
||||
needs: [lint, test]
|
||||
needs: [lint-frontend, lint-backend, test-backend, format-backend]
|
||||
name: Push release
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -47,9 +94,16 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: gtstef/filebrowser
|
||||
- name: Strip v from version number
|
||||
id: modify-json
|
||||
run: |
|
||||
JSON="${{ steps.meta.outputs.tags }}"
|
||||
# Use jq to remove 'v' from the version field
|
||||
JSON=$(echo "$JSON" | sed 's/filebrowser:v/filebrowser:/')
|
||||
echo "cleaned_tag=$JSON" >> $GITHUB_OUTPUT
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
@@ -57,5 +111,5 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
tags: ${{ steps.modify-json.outputs.cleaned_tag }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -6,6 +6,8 @@ rice-box.go
|
||||
/filebrowser
|
||||
/filebrowser.exe
|
||||
/frontend/dist
|
||||
/frontend/pkg
|
||||
/frontend/package-lock.json
|
||||
/backend/vendor
|
||||
/backend/*.cov
|
||||
|
||||
|
||||
+108
-40
@@ -1,59 +1,127 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version).
|
||||
|
||||
## v0.2.6
|
||||
|
||||
# v0.2.0
|
||||
This change focuses on minimizing and simplifying build process.
|
||||
|
||||
- improved UI
|
||||
- more unified coehisive look
|
||||
- Adjusted header bar look and icon behavior
|
||||
- The shell is dead.
|
||||
- If you need to use custom commands, exec into the docker container.
|
||||
- The json config file is dead.
|
||||
- All configuration is done via advanced `filebrowser.yaml`
|
||||
- The only flag that is allowed is flag to specify config file.
|
||||
- Removed old code to migrate database versions
|
||||
- Removed all unused cmd code
|
||||
- **Change**: Migrated to Vite / Vue 3
|
||||
- **Change**: removed npm modules
|
||||
- replaced vuex with custom state management via src/store
|
||||
- replaced noty with simple card popup notifications
|
||||
- replaced moment with simple date formatter where needed
|
||||
- replaced vue-simple-progress with vue component
|
||||
- **Feature**: improved error logging
|
||||
- backend errors show the root function that called them during the error
|
||||
- frontend errors print errors to console that fail try/catch
|
||||
- all frontend errors via popup notification & print to console as well
|
||||
- **Fix**: Allow editing blank text based files in editor
|
||||
- tweaked listing styles
|
||||
- Feature: Allow disabling the index via configuration yaml
|
||||
|
||||
# v0.1.4
|
||||
- various UI fixes
|
||||
- Added download button back to toolbar
|
||||
- Added upload button to side menu
|
||||
- breadcrumb spacing fix
|
||||
- Added "compact" view option
|
||||
- fixed slash issue with css rtl logic
|
||||
- various backend fixes
|
||||
- search has a sessionId attached so searches don't collide
|
||||
- search no longer searches by word with spaces, includes space in searches
|
||||
- prepared for full json configuration
|
||||
- made size search work for smaller and larger
|
||||
- made search types not show up in search bar when used
|
||||
## v0.2.5
|
||||
|
||||
- Fix: delete user prompt works using native hovers.
|
||||
|
||||
## v0.2.4
|
||||
|
||||
- Feature: [create-folder-feature](https://github.com/gtsteffaniak/filebrowser/pull/105)
|
||||
- Feature: [playable shared video](https://github.com/filebrowser/filebrowser/issues/2537)
|
||||
- Feature: photos, videos, and audio get embedded preview on share instead of icon
|
||||
- Fix: sharable link bug, now uses special publicUser
|
||||
- Bump go version to 1.22
|
||||
- In prep for vue3 migration, npm modules removed:
|
||||
- js-base64
|
||||
- pretty-bytes
|
||||
- whatwg-fetch
|
||||
- lodash.throttle
|
||||
- lodash.clonedeep
|
||||
|
||||
## v0.2.3
|
||||
|
||||
- Feature: token expiration time now configurable
|
||||
- FIX: Hidden files are still directly accessible. (https://github.com/filebrowser/filebrowser/issues/2698)
|
||||
- FIX: search/user context bug
|
||||
|
||||
## v0.2.2
|
||||
|
||||
- CHG: **Speed:** (0m57s) - Decreased by 78% compared to the previous release.
|
||||
- CHG: **Memory Usage:** (41MB) - Reduced by 45% compared to the previous release.
|
||||
- Feature: Now utilizes the index for file browser listings!
|
||||
- FIX: Editor issues fixed on save and themes.
|
||||
|
||||
## v0.2.1
|
||||
|
||||
- Addressed issue #29 - Rules can now be configured and read from the configuration YAML.
|
||||
- Addressed issue #28 - Allows disabling settings per user.
|
||||
- Addressed issue #27 - Shortened download link for password-protected files.
|
||||
- Addressed issue #26 - Enables dark mode per user and improves switching performance.
|
||||
- Improved styling with more rounded corners and enhanced listing design.
|
||||
- Enhanced search performance.
|
||||
- Fixed authentication issues.
|
||||
- Added compact view mode.
|
||||
- Improved view mode configuration and behavior.
|
||||
- Updated the configuration file to accept new settings.
|
||||
|
||||
## v0.2.0
|
||||
|
||||
- **Improved UI:**
|
||||
- Enhanced the cohesive and unified look.
|
||||
- Adjusted the header bar appearance and icon behavior.
|
||||
- The shell feature has been deprecated.
|
||||
- Custom commands can be executed within the Docker container if needed.
|
||||
- The JSON config file is no longer used.
|
||||
- All configurations are now performed via the advanced `filebrowser.yaml`.
|
||||
- The only allowed flag is specifying the config file.
|
||||
- Removed old code for migrating database versions.
|
||||
- Eliminated all unused `cmd` code.
|
||||
|
||||
## v0.1.4
|
||||
|
||||
- **Various UI fixes:**
|
||||
- Reintroduced the download button to the toolbar.
|
||||
- Added the upload button to the side menu.
|
||||
- Adjusted breadcrumb spacing.
|
||||
- Introduced a "compact" view option.
|
||||
- Fixed a slash issue with CSS right-to-left (RTL) logic.
|
||||
- **Various backend improvements:**
|
||||
- Added session IDs to searches to prevent collisions.
|
||||
- Modified search behavior to include spaces in searches.
|
||||
- Prepared for full JSON configuration support.
|
||||
- Made size-based searches work for both smaller and larger files.
|
||||
- Modified search types not to appear in the search bar when used.
|
||||
|
||||
## v0.1.3
|
||||
|
||||
- improved styling, colors, transparency, blur
|
||||
- Made sidebar hidden on desktop as well
|
||||
- simplified navbar to be three buttons
|
||||
- open menu
|
||||
- search
|
||||
- toggle view
|
||||
- Changed desktop search style and included additional search options.
|
||||
- Enhanced styling with improved colors, transparency, and blur effects.
|
||||
- Hid the sidebar on desktop views.
|
||||
- Simplified the navbar to include three buttons:
|
||||
- Open menu
|
||||
- Search
|
||||
- Toggle view
|
||||
- Revised desktop search style and included additional search options.
|
||||
|
||||
## v0.1.2
|
||||
|
||||
- Updated UI to use search features better
|
||||
- More filter options
|
||||
- Better icons with colors
|
||||
- GUI styling
|
||||
- Improved search performance
|
||||
- Updated the UI to better utilize search features:
|
||||
- Added more filter options.
|
||||
- Enhanced icons with colors.
|
||||
- Improved GUI styling.
|
||||
- Improved search performance.
|
||||
- **Index Changes:**
|
||||
- **Speed:** (0m32s) - Increased by 6% compared to the previous release.
|
||||
- **Memory Usage:** (93MB) - Increased by 3% compared to the previous release.
|
||||
|
||||
## v0.1.1
|
||||
|
||||
- Improved search with indexing
|
||||
- Improved search functionality with indexing.
|
||||
- **Index Changes (Baseline Results):**
|
||||
- **Speed:** (0m30s)
|
||||
- **Memory Usage:** (90MB)
|
||||
|
||||
## v0.1.0
|
||||
|
||||
- nothing changed from origin.
|
||||
- No changes from the original.
|
||||
|
||||
Forked from https://github.com/filebrowser/filebrowser
|
||||
Forked from [filebrowser/filebrowser](https://github.com/filebrowser/filebrowser).
|
||||
+9
-15
@@ -1,24 +1,18 @@
|
||||
FROM node:slim as nbuild
|
||||
WORKDIR /app
|
||||
COPY ./frontend/package*.json ./
|
||||
RUN npm i
|
||||
COPY ./frontend/package*.json ./
|
||||
RUN npm i --maxsockets 1
|
||||
COPY ./frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
FROM golang:1.21-alpine as base
|
||||
FROM golang:1.22-alpine as base
|
||||
WORKDIR /app
|
||||
COPY ./backend ./
|
||||
RUN go get -u golang.org/x/net
|
||||
COPY ./backend ./
|
||||
RUN go build -ldflags="-w -s" -o filebrowser .
|
||||
|
||||
FROM alpine:latest
|
||||
RUN apk --no-cache add \
|
||||
ca-certificates \
|
||||
mailcap
|
||||
VOLUME /srv
|
||||
EXPOSE 8080
|
||||
WORKDIR /
|
||||
COPY --from=base /app/filebrowser.yaml /filebrowser.yaml
|
||||
COPY --from=base /app/filebrowser /filebrowser
|
||||
COPY --from=nbuild /app/dist/ /frontend/dist/
|
||||
ENTRYPOINT [ "./filebrowser" ]
|
||||
ARG app="/app/filebrowser"
|
||||
RUN apk --no-cache add ca-certificates mailcap
|
||||
COPY --from=base /app/filebrowser* ./
|
||||
COPY --from=nbuild /app/dist/ ./frontend/dist/
|
||||
ENTRYPOINT [ "./filebrowser" ]
|
||||
@@ -4,67 +4,70 @@
|
||||
<p align="center">
|
||||
<img src="frontend/public/img/icons/favicon-256x256.png" width="100" title="Login With Custom URL">
|
||||
</p>
|
||||
<h3 align="center">Filebrowser - A modern file manager for the web</h3>
|
||||
<h3 align="center">Filebrowser - A modern web-based file manager</h3>
|
||||
<p align="center">
|
||||
<img width="500" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/459937ef-3f14-408d-aef5-899cde4cf3a1" title="Main Screenshot">
|
||||
<img width="800" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/899152cf-3e69-4179-aa82-752af2df3fc6" title="Main Screenshot">
|
||||
</p>
|
||||
|
||||
> **NOTE**
|
||||
Intended for docker use only
|
||||
|
||||
> **Warning**
|
||||
Starting with v0.2.0, *ALL* configuration is done via `filebrowser.yaml` configuration file. `.filebrowser.json` and any flags other than `-c` and `-config` during execution WILL NO LONGER WORK. This is by design, in order to use the v0.2.0 You can mount your directory and initialize a new DB with a new default `filebrowser.yaml` which you can tweak and use in the future. Or you can copy and paste the default startup `filebrowser.yaml` below.
|
||||
> [!WARNING]
|
||||
> Starting with v0.2.0, *ALL* configuration is done via `filebrowser.yaml` configuration file.
|
||||
> Starting with v0.2.4 *ALL* share links need to be re-created (due to security fix).
|
||||
|
||||
This fork makes the following significant changes to filebrowser for origin:
|
||||
|
||||
1. [x] Improves search to use index instead of filesystem.
|
||||
- Lightning fast, realtime results as you type
|
||||
- Works with more type filters
|
||||
1. [x] Improved and simplified GUI navbar and sidebar menu.
|
||||
1. [x] Updated version and dependencies.
|
||||
1. [x] **IMPORTANT** Moved all configurations to `filebrowser.yaml`.
|
||||
1. [x] Better search
|
||||
- Lightning fast
|
||||
- realtime results as you type
|
||||
- Works with more type filters
|
||||
- interactive results page.
|
||||
2. [x] Revamped and simplified GUI navbar and sidebar menu.
|
||||
- Additional compact view mode as well as refreshed view mode styles.
|
||||
3. [x] Revamped configuration via `filebrowser.yml` config file.
|
||||
- More configurations possible at a per-user level
|
||||
- <img width="450" alt="image" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/625bd7c4-5ee9-4011-aaae-2a388ab0813b">
|
||||
|
||||
## About
|
||||
|
||||
Filebrowser provides a file managing interface within a specified directory
|
||||
and it can be used to upload, delete, preview, rename and edit your files.
|
||||
It allows the creation of multiple users and each user can have its own
|
||||
directory. It can be used as a standalone app.
|
||||
directory.
|
||||
|
||||
This repository is a fork, a collection of changes that make this program
|
||||
work better in terms of asthetics and performance. Improved search,
|
||||
simplified ui (without removing features) and more secure and up-to-date
|
||||
build are just a few examples.
|
||||
work better in terms of asthetics and performance. Improved search,
|
||||
simplified ui (without removing features) and more secure and up-to-date
|
||||
build are just a few examples.
|
||||
|
||||
This Implementation of filebrowser differs significantly to the original.
|
||||
There are hundereds of thousands of lines changed and they are generally
|
||||
no longer compatible with eachother. This has been intentional -- the
|
||||
focus of this fork is on a few key principles:
|
||||
- Simplicity and improved user experience
|
||||
- Efficiency of operations and performance
|
||||
- Minimizing external dependancies and usage of standard libraries.
|
||||
- Of course -- adding much needed features.
|
||||
|
||||
## Look
|
||||
|
||||
One way you can observe the improved user experience is how I changed the UI.
|
||||
The Navbar is simplified to a three component system :
|
||||
|
||||
1. (Left) The slide-out action panel button
|
||||
2. (Middle) The powerful search bar.
|
||||
3. (Right) The view change toggle.
|
||||
|
||||
All other functions are moved either into the action menu or popup menus.
|
||||
If the action is does not depend on context, it will exist in the slide-out action panel.
|
||||
If the action is available based on context, it will showup as a popup menu.
|
||||
|
||||
<p align="center">
|
||||
<img width="500" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/35cdeb3b-ab79-4b04-8001-8f51f6ea06bb" title="Dark mode">
|
||||
<img width="500" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/8d426356-26cf-407b-b078-bf58f198e799" title="Dark mode2">
|
||||
<img width="300" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/37e8f03b-4f5a-4689-aa6c-5cd858a858e9" title="Dark mode">
|
||||
<img width="300" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/b04d3c1f-154b-45ba-927c-2112926ad3a9" title="Dark mode">
|
||||
<img width="500" alt="image" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/55fa4f5c-440e-4a97-b711-96139208a163">
|
||||
<img width="500" alt="image" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/c76f4100-949b-4e17-a3e6-e410fb8ec08f">
|
||||
<img width="500" alt="image" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/0bde26f3-fa90-411e-bd0b-abaa47506d62">
|
||||
<img width="560" alt="image" src="https://github.com/gtsteffaniak/filebrowser/assets/42989099/71d8f2b8-6fe6-4fdc-8aac-503d08c28d86">
|
||||
</p>
|
||||
|
||||
## Search Performance
|
||||
|
||||
100x faster search. However, this will be at expense of RAM. if you have < 1 million
|
||||
files and folders in the given scope, the RAM usage should be less than 200MB total. RAM requirements
|
||||
should scale based on the number of directories.
|
||||
|
||||
Also , the approx. time to fully index will vary widely based on performance. A sufficiently performant
|
||||
system should fully index within the first 5 minutes, potentially within the first few seconds.
|
||||
|
||||
For example, a low end 11th gen i5 with SSD indexes 128K files within 1 second:
|
||||
|
||||
```
|
||||
2023/09/09 21:38:50 Initializing with config file: filebrowser.yaml
|
||||
2023/09/09 21:38:50 Indexing files...
|
||||
2023/09/09 21:38:50 Listening on [::]:8080
|
||||
2023/09/09 21:38:51 Successfully indexed files.
|
||||
2023/09/09 21:38:51 Files found : 123452
|
||||
2023/09/09 21:38:51 Directories found : 1768
|
||||
2023/09/09 21:38:51 Indexing scheduler will run every 5 minutes
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
Using docker:
|
||||
@@ -72,7 +75,7 @@ Using docker:
|
||||
1. docker run (no persistent db):
|
||||
|
||||
```
|
||||
docker run -it -v /path/to/folder:/srv -p 80:8080 gtstef/filebrowser
|
||||
docker run -it -v /path/to/folder:/srv -p 80:80 gtstef/filebrowser
|
||||
```
|
||||
|
||||
1. docker-compose:
|
||||
@@ -88,7 +91,7 @@ services:
|
||||
- './database:/database' # optional if you want db to persist - configure a path under "database" dir in config file.
|
||||
- './filebrowser.yaml:/filebrowser.yaml' # required
|
||||
ports:
|
||||
- '80:8080'
|
||||
- '80:80'
|
||||
image: gtstef/filebrowser
|
||||
restart: always
|
||||
```
|
||||
@@ -104,7 +107,7 @@ services:
|
||||
- './database:/database' # optional if you want db to persist - configure a path under "database" dir in config file.
|
||||
- './filebrowser.yaml:/filebrowser.yaml' # required
|
||||
ports:
|
||||
- '80:8080'
|
||||
- '80:80'
|
||||
image: gtstef/filebrowser
|
||||
restart: always
|
||||
volumes:
|
||||
@@ -116,12 +119,32 @@ volumes:
|
||||
|
||||
```
|
||||
|
||||
Not using docker (not recommended) (Must donwload asset with frontend directory next to filebrowser binary)
|
||||
|
||||
```
|
||||
./filebrowser -f <filebrowser.yml or other /path/to/config.yaml>
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration is now done via a single configuration file: `filebrowser.yaml`, here is an example minimal [configuration file](./backend/filebrowser.yaml).
|
||||
|
||||
View the [Configuration Help Page](./configuration.md) for available configuration options and other help.
|
||||
|
||||
|
||||
## Migration from filebrowser/filebrowser
|
||||
|
||||
If you are currently using filebrowser from the filebrowser/filebrowser repo, but want to try using this. I recommend you start fresh without reusing the database, but there are a few things you'll need to do if you must migrate:
|
||||
|
||||
1. Create a configuration file as mentioned above.
|
||||
2. Copy your database file from the original filebrowser to the path of the new one.
|
||||
3. Update the configuration file to use the database (under server in filebrowser.yml)
|
||||
4. If you are using docker, update the docker-compose file or docker run command to use the config file as described in the install section above.
|
||||
5. If you are not using docker, just make sure you run filebrowser -f filebrowser.yml and have valid filebrowser config.
|
||||
|
||||
|
||||
The filebrowser application should run with the same user and rules that you have from the original. But keep in mind the differences that are mentioned at the top of this readme.
|
||||
|
||||
### background & help
|
||||
|
||||
The original project filebrowser/filebrowser used multiple different ways to configure the server.
|
||||
|
||||
+19
-34
@@ -10,7 +10,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/files"
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
@@ -44,8 +43,8 @@ func (a *HookAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) {
|
||||
}
|
||||
|
||||
a.Users = usr
|
||||
a.Settings = &settings.GlobalConfiguration
|
||||
a.Server = &settings.GlobalConfiguration.Server
|
||||
a.Settings = &settings.Config
|
||||
a.Server = &settings.Config.Server
|
||||
a.Cred = cred
|
||||
|
||||
action, err := a.RunCommand()
|
||||
@@ -146,14 +145,10 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
|
||||
}
|
||||
|
||||
if u == nil {
|
||||
pass, err := users.HashPwd(a.Cred.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// create user with the provided credentials
|
||||
d := &users.User{
|
||||
Username: a.Cred.Username,
|
||||
Password: pass,
|
||||
Password: a.Cred.Password,
|
||||
Scope: a.Settings.UserDefaults.Scope,
|
||||
Locale: a.Settings.UserDefaults.Locale,
|
||||
ViewMode: a.Settings.UserDefaults.ViewMode,
|
||||
@@ -178,16 +173,6 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
|
||||
}
|
||||
} else if p := !users.CheckPwd(a.Cred.Password, u.Password); len(a.Fields.Values) > 1 || p {
|
||||
u = a.GetUser(u)
|
||||
|
||||
// update the password when it doesn't match the current
|
||||
if p {
|
||||
pass, err := users.HashPwd(a.Cred.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u.Password = pass
|
||||
}
|
||||
|
||||
// update user with provided fields
|
||||
err := a.Users.Update(u)
|
||||
if err != nil {
|
||||
@@ -201,31 +186,31 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
|
||||
// GetUser returns a User filled with hook values or provided defaults
|
||||
func (a *HookAuth) GetUser(d *users.User) *users.User {
|
||||
// adds all permissions when user is admin
|
||||
isAdmin := a.Fields.GetBoolean("user.perm.admin", d.Perm.Admin)
|
||||
isAdmin := d.Perm.Admin
|
||||
perms := users.Permissions{
|
||||
Admin: isAdmin,
|
||||
Execute: isAdmin || a.Fields.GetBoolean("user.perm.execute", d.Perm.Execute),
|
||||
Create: isAdmin || a.Fields.GetBoolean("user.perm.create", d.Perm.Create),
|
||||
Rename: isAdmin || a.Fields.GetBoolean("user.perm.rename", d.Perm.Rename),
|
||||
Modify: isAdmin || a.Fields.GetBoolean("user.perm.modify", d.Perm.Modify),
|
||||
Delete: isAdmin || a.Fields.GetBoolean("user.perm.delete", d.Perm.Delete),
|
||||
Share: isAdmin || a.Fields.GetBoolean("user.perm.share", d.Perm.Share),
|
||||
Download: isAdmin || a.Fields.GetBoolean("user.perm.download", d.Perm.Download),
|
||||
Execute: isAdmin || d.Perm.Execute,
|
||||
Create: isAdmin || d.Perm.Create,
|
||||
Rename: isAdmin || d.Perm.Rename,
|
||||
Modify: isAdmin || d.Perm.Modify,
|
||||
Delete: isAdmin || d.Perm.Delete,
|
||||
Share: isAdmin || d.Perm.Share,
|
||||
Download: isAdmin || d.Perm.Download,
|
||||
}
|
||||
user := users.User{
|
||||
ID: d.ID,
|
||||
Username: d.Username,
|
||||
Password: d.Password,
|
||||
Scope: a.Fields.GetString("user.scope", d.Scope),
|
||||
Locale: a.Fields.GetString("user.locale", d.Locale),
|
||||
Scope: d.Scope,
|
||||
Locale: d.Locale,
|
||||
ViewMode: d.ViewMode,
|
||||
SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick),
|
||||
Sorting: files.Sorting{
|
||||
Asc: a.Fields.GetBoolean("user.sorting.asc", d.Sorting.Asc),
|
||||
By: a.Fields.GetString("user.sorting.by", d.Sorting.By),
|
||||
SingleClick: d.SingleClick,
|
||||
Sorting: users.Sorting{
|
||||
Asc: d.Sorting.Asc,
|
||||
By: d.Sorting.By,
|
||||
},
|
||||
Commands: a.Fields.GetArray("user.commands", d.Commands),
|
||||
HideDotfiles: a.Fields.GetBoolean("user.hideDotfiles", d.HideDotfiles),
|
||||
Commands: d.Commands,
|
||||
HideDotfiles: d.HideDotfiles,
|
||||
Perm: perms,
|
||||
LockPassword: true,
|
||||
}
|
||||
|
||||
@@ -24,13 +24,12 @@ type JSONAuth struct {
|
||||
|
||||
// Auth authenticates the user via a json in content body.
|
||||
func (a JSONAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) {
|
||||
config := &settings.GlobalConfiguration
|
||||
config := &settings.Config
|
||||
var cred jsonCred
|
||||
|
||||
if r.Body == nil {
|
||||
return nil, os.ErrPermission
|
||||
}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&cred)
|
||||
if err != nil {
|
||||
return nil, os.ErrPermission
|
||||
|
||||
@@ -15,7 +15,7 @@ type NoAuth struct{}
|
||||
|
||||
// Auth uses authenticates user 1.
|
||||
func (a NoAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) {
|
||||
return usr.Get(settings.GlobalConfiguration.Server.Root, uint(1))
|
||||
return usr.Get(settings.Config.Server.Root, uint(1))
|
||||
}
|
||||
|
||||
// LoginPage tells that no auth doesn't require a login page.
|
||||
|
||||
@@ -21,7 +21,7 @@ type ProxyAuth struct {
|
||||
// Auth authenticates the user via an HTTP header.
|
||||
func (a ProxyAuth) Auth(r *http.Request, usr users.Store) (*users.User, error) {
|
||||
username := r.Header.Get(a.Header)
|
||||
user, err := usr.Get(settings.GlobalConfiguration.Server.Root, username)
|
||||
user, err := usr.Get(settings.Config.Server.Root, username)
|
||||
if err == errors.ErrNotExist {
|
||||
return nil, os.ErrPermission
|
||||
}
|
||||
|
||||
@@ -7,31 +7,42 @@
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/diskcache 0.003s
|
||||
? github.com/gtsteffaniak/filebrowser/errors [no test files]
|
||||
? github.com/gtsteffaniak/filebrowser/files [no test files]
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/gtsteffaniak/filebrowser/files
|
||||
cpu: 11th Gen Intel(R) Core(TM) i5-11320H @ 3.20GHz
|
||||
BenchmarkFillIndex-8 10 3587120 ns/op 273640 B/op 2013 allocs/op
|
||||
BenchmarkSearchAllIndexes-8 10 31291180 ns/op 19500700 B/op 298636 allocs/op
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/files 0.408s
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/fileutils 0.003s
|
||||
2023/09/10 09:17:55 h: 401 <nil>
|
||||
2023/09/10 09:17:55 h: 401 <nil>
|
||||
2023/09/10 09:17:55 h: 401 <nil>
|
||||
2023/09/10 09:17:56 h: 401 <nil>
|
||||
2023/09/10 09:17:56 h: 401 <nil>
|
||||
2023/09/10 09:17:56 h: 401 <nil>
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 h: 401 <nil>
|
||||
2024/02/07 07:16:43 h: 401 <nil>
|
||||
2024/02/07 07:16:43 h: 401 <nil>
|
||||
2024/02/07 07:16:43 h: 401 <nil>
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 Saving new user: publicUser
|
||||
2024/02/07 07:16:43 h: 401 <nil>
|
||||
2024/02/07 07:16:43 h: 401 <nil>
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/http 0.082s
|
||||
ok github.com/gtsteffaniak/filebrowser/http 0.202s
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/img 0.118s
|
||||
ok github.com/gtsteffaniak/filebrowser/img 0.125s
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/rules 0.002s
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/runner 0.003s
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/gtsteffaniak/filebrowser/search
|
||||
cpu: 11th Gen Intel(R) Core(TM) i5-11320H @ 3.20GHz
|
||||
BenchmarkSearchAllIndexes-8 10 5311144 ns/op 2934075 B/op 44569 allocs/op
|
||||
BenchmarkFillIndex-8 10 3142607 ns/op 18473 B/op 452 allocs/op
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/search 0.110s
|
||||
PASS
|
||||
ok github.com/gtsteffaniak/filebrowser/settings 0.005s
|
||||
? github.com/gtsteffaniak/filebrowser/share [no test files]
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(hashCmd)
|
||||
}
|
||||
|
||||
var hashCmd = &cobra.Command{
|
||||
Use: "hash <password>",
|
||||
Short: "Hashes a password",
|
||||
Long: `Hashes a password using bcrypt algorithm.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
pwd, err := users.HashPwd(args[0])
|
||||
checkErr(err)
|
||||
fmt.Println(pwd)
|
||||
},
|
||||
}
|
||||
+43
-67
@@ -3,7 +3,7 @@ package cmd
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"io"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
@@ -17,22 +17,16 @@ import (
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
lumberjack "gopkg.in/natefinch/lumberjack.v2"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/auth"
|
||||
"github.com/gtsteffaniak/filebrowser/diskcache"
|
||||
"github.com/gtsteffaniak/filebrowser/files"
|
||||
fbhttp "github.com/gtsteffaniak/filebrowser/http"
|
||||
"github.com/gtsteffaniak/filebrowser/img"
|
||||
"github.com/gtsteffaniak/filebrowser/search"
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/storage"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile string
|
||||
)
|
||||
|
||||
type dirFS struct {
|
||||
http.Dir
|
||||
}
|
||||
@@ -54,7 +48,7 @@ func init() {
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "filebrowser",
|
||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||
serverConfig := settings.GlobalConfiguration.Server
|
||||
serverConfig := settings.Config.Server
|
||||
if !d.hadDB {
|
||||
quickSetup(d)
|
||||
}
|
||||
@@ -71,49 +65,49 @@ var rootCmd = &cobra.Command{
|
||||
fileCache = diskcache.New(afero.NewOsFs(), cacheDir)
|
||||
}
|
||||
// initialize indexing and schedule indexing ever n minutes (default 5)
|
||||
go search.InitializeIndex(serverConfig.IndexingInterval)
|
||||
go files.InitializeIndex(serverConfig.IndexingInterval, serverConfig.Indexing)
|
||||
_, err := os.Stat(serverConfig.Root)
|
||||
checkErr(err)
|
||||
checkErr(fmt.Sprint("cmd os.Stat ", serverConfig.Root), err)
|
||||
var listener net.Listener
|
||||
address := serverConfig.Address + ":" + strconv.Itoa(serverConfig.Port)
|
||||
switch {
|
||||
case serverConfig.Socket != "":
|
||||
listener, err = net.Listen("unix", serverConfig.Socket)
|
||||
checkErr(err)
|
||||
checkErr("net.Listen", err)
|
||||
socketPerm, err := cmd.Flags().GetUint32("socket-perm") //nolint:govet
|
||||
checkErr(err)
|
||||
checkErr("cmd.Flags().GetUint32", err)
|
||||
err = os.Chmod(serverConfig.Socket, os.FileMode(socketPerm))
|
||||
checkErr(err)
|
||||
checkErr("os.Chmod", err)
|
||||
case serverConfig.TLSKey != "" && serverConfig.TLSCert != "":
|
||||
cer, err := tls.LoadX509KeyPair(serverConfig.TLSCert, serverConfig.TLSKey) //nolint:govet
|
||||
checkErr(err)
|
||||
checkErr("tls.LoadX509KeyPair", err)
|
||||
listener, err = tls.Listen("tcp", address, &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
Certificates: []tls.Certificate{cer}},
|
||||
)
|
||||
checkErr(err)
|
||||
checkErr("tls.Listen", err)
|
||||
default:
|
||||
listener, err = net.Listen("tcp", address)
|
||||
checkErr(err)
|
||||
checkErr("net.Listen", err)
|
||||
}
|
||||
sigc := make(chan os.Signal, 1)
|
||||
signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
|
||||
go cleanupHandler(listener, sigc)
|
||||
assetsFs := dirFS{Dir: http.Dir("frontend/dist")}
|
||||
handler, err := fbhttp.NewHandler(imgSvc, fileCache, d.store, &serverConfig, assetsFs)
|
||||
checkErr(err)
|
||||
checkErr("fbhttp.NewHandler", err)
|
||||
defer listener.Close()
|
||||
log.Println("Listening on", listener.Addr().String())
|
||||
//nolint: gosec
|
||||
if err := http.Serve(listener, handler); err != nil {
|
||||
log.Fatal(err)
|
||||
log.Fatalf("Could not start server on port %d: %v", serverConfig.Port, err)
|
||||
}
|
||||
}, pythonConfig{allowNoDB: true}),
|
||||
}
|
||||
|
||||
func StartFilebrowser() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
log.Fatal("Error starting filebrowser:", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,56 +118,38 @@ func cleanupHandler(listener net.Listener, c chan os.Signal) { //nolint:interfac
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func getRunParams(st *storage.Storage) *settings.Server {
|
||||
server, err := st.Settings.GetServer()
|
||||
checkErr(err)
|
||||
return server
|
||||
}
|
||||
|
||||
func setupLog(logMethod string) {
|
||||
switch logMethod {
|
||||
case "stdout":
|
||||
log.SetOutput(os.Stdout)
|
||||
case "stderr":
|
||||
log.SetOutput(os.Stderr)
|
||||
case "":
|
||||
log.SetOutput(io.Discard)
|
||||
default:
|
||||
log.SetOutput(&lumberjack.Logger{
|
||||
Filename: logMethod,
|
||||
MaxSize: 100,
|
||||
MaxAge: 14,
|
||||
MaxBackups: 10,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func quickSetup(d pythonData) {
|
||||
settings.GlobalConfiguration.Key = generateKey()
|
||||
var err error
|
||||
if settings.GlobalConfiguration.Auth.Method == "noauth" {
|
||||
err = d.store.Auth.Save(&auth.NoAuth{})
|
||||
settings.Config.Auth.Key = generateKey()
|
||||
if settings.Config.Auth.Method == "noauth" {
|
||||
err := d.store.Auth.Save(&auth.NoAuth{})
|
||||
checkErr("d.store.Auth.Save", err)
|
||||
} else {
|
||||
settings.GlobalConfiguration.Auth.Method = "password"
|
||||
err = d.store.Auth.Save(&auth.JSONAuth{})
|
||||
settings.Config.Auth.Method = "password"
|
||||
err := d.store.Auth.Save(&auth.JSONAuth{})
|
||||
checkErr("d.store.Auth.Save", err)
|
||||
}
|
||||
err = d.store.Settings.Save(&settings.GlobalConfiguration)
|
||||
checkErr(err)
|
||||
err = d.store.Settings.SaveServer(&settings.GlobalConfiguration.Server)
|
||||
checkErr(err)
|
||||
username := settings.GlobalConfiguration.AdminUsername
|
||||
password := settings.GlobalConfiguration.AdminPassword
|
||||
if username == "" || password == "" {
|
||||
log.Fatal("username and password cannot be empty during quick setup")
|
||||
}
|
||||
user := &users.User{
|
||||
Username: username,
|
||||
Password: password,
|
||||
LockPassword: false,
|
||||
}
|
||||
settings.GlobalConfiguration.UserDefaults.Apply(user)
|
||||
err := d.store.Settings.Save(&settings.Config)
|
||||
checkErr("d.store.Settings.Save", err)
|
||||
err = d.store.Settings.SaveServer(&settings.Config.Server)
|
||||
checkErr("d.store.Settings.SaveServer", err)
|
||||
user := &users.User{}
|
||||
settings.Config.UserDefaults.Apply(user)
|
||||
user.Username = settings.Config.Auth.AdminUsername
|
||||
user.Password = settings.Config.Auth.AdminPassword
|
||||
user.Perm.Admin = true
|
||||
user.Scope = "./"
|
||||
user.DarkMode = true
|
||||
user.ViewMode = "normal"
|
||||
user.LockPassword = false
|
||||
user.Perm = users.Permissions{
|
||||
Create: true,
|
||||
Rename: true,
|
||||
Modify: true,
|
||||
Delete: true,
|
||||
Share: true,
|
||||
Download: true,
|
||||
Admin: true,
|
||||
}
|
||||
err = d.store.Users.Save(user)
|
||||
checkErr(err)
|
||||
checkErr("d.store.Users.Save", err)
|
||||
}
|
||||
@@ -42,23 +42,23 @@ including 'index_end'.`,
|
||||
},
|
||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||
i, err := strconv.Atoi(args[0])
|
||||
checkErr(err)
|
||||
checkErr("strconv.Atoi", err)
|
||||
f := i
|
||||
if len(args) == 2 { //nolint:gomnd
|
||||
f, err = strconv.Atoi(args[1])
|
||||
checkErr(err)
|
||||
checkErr("strconv.Atoi", err)
|
||||
}
|
||||
|
||||
user := func(u *users.User) {
|
||||
u.Rules = append(u.Rules[:i], u.Rules[f+1:]...)
|
||||
err := d.store.Users.Save(u)
|
||||
checkErr(err)
|
||||
checkErr("d.store.Users.Save", err)
|
||||
}
|
||||
|
||||
global := func(s *settings.Settings) {
|
||||
s.Rules = append(s.Rules[:i], s.Rules[f+1:]...)
|
||||
err := d.store.Settings.Save(s)
|
||||
checkErr(err)
|
||||
checkErr("d.store.Settings.Save", err)
|
||||
}
|
||||
|
||||
runRules(d.store, cmd, user, global)
|
||||
|
||||
@@ -33,7 +33,7 @@ func runRules(st *storage.Storage, cmd *cobra.Command, usersFn func(*users.User)
|
||||
id := getUserIdentifier(cmd.Flags())
|
||||
if id != nil {
|
||||
user, err := st.Users.Get("", id)
|
||||
checkErr(err)
|
||||
checkErr("st.Users.Get", err)
|
||||
|
||||
if usersFn != nil {
|
||||
usersFn(user)
|
||||
@@ -44,7 +44,7 @@ func runRules(st *storage.Storage, cmd *cobra.Command, usersFn func(*users.User)
|
||||
}
|
||||
|
||||
s, err := st.Settings.Get()
|
||||
checkErr(err)
|
||||
checkErr("st.Settings.Get", err)
|
||||
|
||||
if globalFn != nil {
|
||||
globalFn(s)
|
||||
|
||||
@@ -44,13 +44,13 @@ var rulesAddCmd = &cobra.Command{
|
||||
user := func(u *users.User) {
|
||||
u.Rules = append(u.Rules, rule)
|
||||
err := d.store.Users.Save(u)
|
||||
checkErr(err)
|
||||
checkErr("d.store.Users.Save", err)
|
||||
}
|
||||
|
||||
global := func(s *settings.Settings) {
|
||||
s.Rules = append(s.Rules, rule)
|
||||
err := d.store.Settings.Save(s)
|
||||
checkErr(err)
|
||||
checkErr("d.store.Settings.Save", err)
|
||||
}
|
||||
|
||||
runRules(d.store, cmd, user, global)
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
@@ -59,76 +56,3 @@ func parseUsernameOrID(arg string) (username string, id uint) {
|
||||
}
|
||||
return "", uint(id64)
|
||||
}
|
||||
|
||||
func addUserFlags(flags *pflag.FlagSet) {
|
||||
flags.Bool("perm.admin", false, "admin perm for users")
|
||||
flags.Bool("perm.execute", true, "execute perm for users")
|
||||
flags.Bool("perm.create", true, "create perm for users")
|
||||
flags.Bool("perm.rename", true, "rename perm for users")
|
||||
flags.Bool("perm.modify", true, "modify perm for users")
|
||||
flags.Bool("perm.delete", true, "delete perm for users")
|
||||
flags.Bool("perm.share", true, "share perm for users")
|
||||
flags.Bool("perm.download", true, "download perm for users")
|
||||
flags.String("sorting.by", "name", "sorting mode (name, size or modified)")
|
||||
flags.Bool("sorting.asc", false, "sorting by ascending order")
|
||||
flags.Bool("lockPassword", false, "lock password")
|
||||
flags.StringSlice("commands", nil, "a list of the commands a user can execute")
|
||||
flags.String("scope", ".", "scope for users")
|
||||
flags.String("locale", "en", "locale for users")
|
||||
flags.String("viewMode", string(users.ListViewMode), "view mode for users")
|
||||
flags.Bool("singleClick", false, "use single clicks only")
|
||||
}
|
||||
|
||||
func getViewMode(flags *pflag.FlagSet) string {
|
||||
viewMode := settings.GlobalConfiguration.UserDefaults.ViewMode
|
||||
if viewMode != users.ListViewMode && viewMode != users.MosaicViewMode {
|
||||
checkErr(errors.New("view mode must be \"" + string(users.ListViewMode) + "\" or \"" + string(users.MosaicViewMode) + "\""))
|
||||
}
|
||||
return viewMode
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all bool) {
|
||||
visit := func(flag *pflag.Flag) {
|
||||
switch flag.Name {
|
||||
case "scope":
|
||||
defaults.Scope = mustGetString(flags, flag.Name)
|
||||
case "locale":
|
||||
defaults.Locale = mustGetString(flags, flag.Name)
|
||||
case "viewMode":
|
||||
defaults.ViewMode = getViewMode(flags)
|
||||
case "singleClick":
|
||||
defaults.SingleClick = mustGetBool(flags, flag.Name)
|
||||
case "perm.admin":
|
||||
defaults.Perm.Admin = mustGetBool(flags, flag.Name)
|
||||
case "perm.execute":
|
||||
defaults.Perm.Execute = mustGetBool(flags, flag.Name)
|
||||
case "perm.create":
|
||||
defaults.Perm.Create = mustGetBool(flags, flag.Name)
|
||||
case "perm.rename":
|
||||
defaults.Perm.Rename = mustGetBool(flags, flag.Name)
|
||||
case "perm.modify":
|
||||
defaults.Perm.Modify = mustGetBool(flags, flag.Name)
|
||||
case "perm.delete":
|
||||
defaults.Perm.Delete = mustGetBool(flags, flag.Name)
|
||||
case "perm.share":
|
||||
defaults.Perm.Share = mustGetBool(flags, flag.Name)
|
||||
case "perm.download":
|
||||
defaults.Perm.Download = mustGetBool(flags, flag.Name)
|
||||
case "commands":
|
||||
commands, err := flags.GetStringSlice(flag.Name)
|
||||
checkErr(err)
|
||||
defaults.Commands = commands
|
||||
case "sorting.by":
|
||||
defaults.Sorting.By = mustGetString(flags, flag.Name)
|
||||
case "sorting.asc":
|
||||
defaults.Sorting.Asc = mustGetBool(flags, flag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if all {
|
||||
flags.VisitAll(visit)
|
||||
} else {
|
||||
flags.Visit(visit)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
func init() {
|
||||
usersCmd.AddCommand(usersAddCmd)
|
||||
addUserFlags(usersAddCmd.Flags())
|
||||
}
|
||||
|
||||
var usersAddCmd = &cobra.Command{
|
||||
@@ -17,34 +16,25 @@ var usersAddCmd = &cobra.Command{
|
||||
Long: `Create a new user and add it to the database.`,
|
||||
Args: cobra.ExactArgs(2), //nolint:gomnd
|
||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||
s, err := d.store.Settings.Get()
|
||||
checkErr(err)
|
||||
|
||||
password, err := users.HashPwd(args[1])
|
||||
checkErr(err)
|
||||
|
||||
user := &users.User{
|
||||
Username: args[0],
|
||||
Password: password,
|
||||
Password: args[1],
|
||||
LockPassword: mustGetBool(cmd.Flags(), "lockPassword"),
|
||||
}
|
||||
|
||||
s.UserDefaults.Apply(user)
|
||||
|
||||
servSettings, err := d.store.Settings.GetServer()
|
||||
checkErr(err)
|
||||
checkErr("d.store.Settings.GetServer()", err)
|
||||
// since getUserDefaults() polluted s.Defaults.Scope
|
||||
// which makes the Scope not the one saved in the db
|
||||
// we need the right s.Defaults.Scope here
|
||||
s2, err := d.store.Settings.Get()
|
||||
checkErr(err)
|
||||
checkErr("d.store.Settings.Get()", err)
|
||||
|
||||
userHome, err := s2.MakeUserDir(user.Username, user.Scope, servSettings.Root)
|
||||
checkErr(err)
|
||||
checkErr("s2.MakeUserDir", err)
|
||||
user.Scope = userHome
|
||||
|
||||
err = d.store.Users.Save(user)
|
||||
checkErr(err)
|
||||
checkErr("d.store.Users.Save", err)
|
||||
printUsers([]*users.User{user})
|
||||
}, pythonConfig{}),
|
||||
}
|
||||
@@ -16,9 +16,9 @@ path to the file where you want to write the users.`,
|
||||
Args: jsonYamlArg,
|
||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||
list, err := d.store.Users.Gets("")
|
||||
checkErr(err)
|
||||
checkErr("d.store.Users.Gets", err)
|
||||
|
||||
err = marshal(args[0], list)
|
||||
checkErr(err)
|
||||
checkErr("marshal", err)
|
||||
}, pythonConfig{}),
|
||||
}
|
||||
@@ -46,6 +46,6 @@ var findUsers = python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||
list, err = d.store.Users.Gets("")
|
||||
}
|
||||
|
||||
checkErr(err)
|
||||
checkErr("findUsers", err)
|
||||
printUsers(list)
|
||||
}, pythonConfig{})
|
||||
@@ -27,28 +27,28 @@ list or set it to 0.`,
|
||||
Args: jsonYamlArg,
|
||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||
fd, err := os.Open(args[0])
|
||||
checkErr(err)
|
||||
checkErr("os.Open", err)
|
||||
defer fd.Close()
|
||||
|
||||
list := []*users.User{}
|
||||
err = unmarshal(args[0], &list)
|
||||
checkErr(err)
|
||||
checkErr("unmarshal", err)
|
||||
|
||||
for _, user := range list {
|
||||
err = user.Clean("")
|
||||
checkErr(err)
|
||||
checkErr("Clean", err)
|
||||
}
|
||||
|
||||
if mustGetBool(cmd.Flags(), "replace") {
|
||||
oldUsers, err := d.store.Users.Gets("")
|
||||
checkErr(err)
|
||||
checkErr("d.store.Users.Gets", err)
|
||||
|
||||
err = marshal("users.backup.json", list)
|
||||
checkErr(err)
|
||||
checkErr("marshal users.backup.json", err)
|
||||
|
||||
for _, user := range oldUsers {
|
||||
err = d.store.Users.Delete(user.ID)
|
||||
checkErr(err)
|
||||
checkErr("d.store.Users.Delete", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,8 @@ list or set it to 0.`,
|
||||
// User exists in DB.
|
||||
if err == nil {
|
||||
if !overwrite {
|
||||
checkErr(errors.New("user " + strconv.Itoa(int(user.ID)) + " is already registred"))
|
||||
newErr := errors.New("user " + strconv.Itoa(int(user.ID)) + " is already registered")
|
||||
checkErr("", newErr)
|
||||
}
|
||||
|
||||
// If the usernames mismatch, check if there is another one in the DB
|
||||
@@ -68,7 +69,8 @@ list or set it to 0.`,
|
||||
// operation
|
||||
if user.Username != onDB.Username {
|
||||
if conflictuous, err := d.store.Users.Get("", user.Username); err == nil { //nolint:govet
|
||||
checkErr(usernameConflictError(user.Username, conflictuous.ID, user.ID))
|
||||
newErr := usernameConflictError(user.Username, conflictuous.ID, user.ID)
|
||||
checkErr("usernameConflictError", newErr)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -78,7 +80,7 @@ list or set it to 0.`,
|
||||
}
|
||||
|
||||
err = d.store.Users.Save(user)
|
||||
checkErr(err)
|
||||
checkErr("d.store.Users.Save", err)
|
||||
}
|
||||
}, pythonConfig{}),
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ var usersRmCmd = &cobra.Command{
|
||||
err = d.store.Users.Delete(id)
|
||||
}
|
||||
|
||||
checkErr(err)
|
||||
checkErr("usersRmCmd", err)
|
||||
fmt.Println("user deleted successfully")
|
||||
}, pythonConfig{}),
|
||||
}
|
||||
@@ -8,10 +8,6 @@ import (
|
||||
|
||||
func init() {
|
||||
usersCmd.AddCommand(usersUpdateCmd)
|
||||
|
||||
usersUpdateCmd.Flags().StringP("password", "p", "", "new password")
|
||||
usersUpdateCmd.Flags().StringP("username", "u", "", "new username")
|
||||
addUserFlags(usersUpdateCmd.Flags())
|
||||
}
|
||||
|
||||
var usersUpdateCmd = &cobra.Command{
|
||||
@@ -22,9 +18,6 @@ options you want to change.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: python(func(cmd *cobra.Command, args []string, d pythonData) {
|
||||
username, id := parseUsernameOrID(args[0])
|
||||
flags := cmd.Flags()
|
||||
password := mustGetString(flags, "password")
|
||||
newUsername := mustGetString(flags, "username")
|
||||
|
||||
var (
|
||||
err error
|
||||
@@ -36,27 +29,10 @@ options you want to change.`,
|
||||
} else {
|
||||
user, err = d.store.Users.Get("", username)
|
||||
}
|
||||
checkErr(err)
|
||||
user.Scope = user.Scope
|
||||
user.Locale = user.Locale
|
||||
user.ViewMode = user.ViewMode
|
||||
user.SingleClick = user.SingleClick
|
||||
user.Perm = user.Perm
|
||||
user.Commands = user.Commands
|
||||
user.Sorting = user.Sorting
|
||||
user.LockPassword = user.LockPassword
|
||||
|
||||
if newUsername != "" {
|
||||
user.Username = newUsername
|
||||
}
|
||||
|
||||
if password != "" {
|
||||
user.Password, err = users.HashPwd(password)
|
||||
checkErr(err)
|
||||
}
|
||||
checkErr("d.store.Users.Get", err)
|
||||
|
||||
err = d.store.Users.Update(user)
|
||||
checkErr(err)
|
||||
checkErr("d.store.Users.Update", err)
|
||||
printUsers([]*users.User{user})
|
||||
}, pythonConfig{}),
|
||||
}
|
||||
+12
-51
@@ -7,7 +7,6 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/asdine/storm/v3"
|
||||
"github.com/goccy/go-yaml"
|
||||
@@ -19,33 +18,33 @@ import (
|
||||
"github.com/gtsteffaniak/filebrowser/storage/bolt"
|
||||
)
|
||||
|
||||
func checkErr(err error) {
|
||||
func checkErr(source string, err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Fatalf("%s: %v", source, err)
|
||||
}
|
||||
}
|
||||
|
||||
func mustGetString(flags *pflag.FlagSet, flag string) string {
|
||||
s, err := flags.GetString(flag)
|
||||
checkErr(err)
|
||||
checkErr("mustGetString", err)
|
||||
return s
|
||||
}
|
||||
|
||||
func mustGetBool(flags *pflag.FlagSet, flag string) bool {
|
||||
b, err := flags.GetBool(flag)
|
||||
checkErr(err)
|
||||
checkErr("mustGetBool", err)
|
||||
return b
|
||||
}
|
||||
|
||||
func mustGetUint(flags *pflag.FlagSet, flag string) uint {
|
||||
b, err := flags.GetUint(flag)
|
||||
checkErr(err)
|
||||
checkErr("mustGetUint", err)
|
||||
return b
|
||||
}
|
||||
|
||||
func generateKey() []byte {
|
||||
k, err := settings.GenerateKey()
|
||||
checkErr(err)
|
||||
checkErr("generateKey", err)
|
||||
return k
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ func dbExists(path string) (bool, error) {
|
||||
func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
|
||||
return func(cmd *cobra.Command, args []string) {
|
||||
data := pythonData{hadDB: true}
|
||||
path := settings.GlobalConfiguration.Server.Database
|
||||
path := settings.Config.Server.Database
|
||||
exists, err := dbExists(path)
|
||||
|
||||
if err != nil {
|
||||
@@ -98,18 +97,19 @@ func python(fn pythonFunc, cfg pythonConfig) cobraFunc {
|
||||
|
||||
data.hadDB = exists
|
||||
db, err := storm.Open(path)
|
||||
checkErr(err)
|
||||
checkErr(fmt.Sprintf("storm.Open path %v", path), err)
|
||||
|
||||
defer db.Close()
|
||||
data.store, err = bolt.NewStorage(db)
|
||||
checkErr(err)
|
||||
checkErr("bolt.NewStorage", err)
|
||||
fn(cmd, args, data)
|
||||
}
|
||||
}
|
||||
|
||||
func marshal(filename string, data interface{}) error {
|
||||
fd, err := os.Create(filename)
|
||||
checkErr(err)
|
||||
|
||||
checkErr("os.Create", err)
|
||||
defer fd.Close()
|
||||
|
||||
switch ext := filepath.Ext(filename); ext {
|
||||
@@ -127,7 +127,7 @@ func marshal(filename string, data interface{}) error {
|
||||
|
||||
func unmarshal(filename string, data interface{}) error {
|
||||
fd, err := os.Open(filename)
|
||||
checkErr(err)
|
||||
checkErr("os.Open", err)
|
||||
defer fd.Close()
|
||||
|
||||
switch ext := filepath.Ext(filename); ext {
|
||||
@@ -152,42 +152,3 @@ func jsonYamlArg(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("invalid format: " + ext)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanUpInterfaceMap(in map[interface{}]interface{}) map[string]interface{} {
|
||||
result := make(map[string]interface{})
|
||||
for k, v := range in {
|
||||
result[fmt.Sprintf("%v", k)] = cleanUpMapValue(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func cleanUpInterfaceArray(in []interface{}) []interface{} {
|
||||
result := make([]interface{}, len(in))
|
||||
for i, v := range in {
|
||||
result[i] = cleanUpMapValue(v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func cleanUpMapValue(v interface{}) interface{} {
|
||||
switch v := v.(type) {
|
||||
case []interface{}:
|
||||
return cleanUpInterfaceArray(v)
|
||||
case map[interface{}]interface{}:
|
||||
return cleanUpInterfaceMap(v)
|
||||
default:
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// convertCmdStrToCmdArray checks if cmd string is blank (whitespace included)
|
||||
// then returns empty string array, else returns the splitted word array of cmd.
|
||||
// This is to ensure the result will never be []string{""}
|
||||
func convertCmdStrToCmdArray(cmd string) []string {
|
||||
var cmdArray []string
|
||||
trimmedCmdStr := strings.TrimSpace(cmd)
|
||||
if trimmedCmdStr != "" {
|
||||
cmdArray = strings.Split(trimmedCmdStr, " ")
|
||||
}
|
||||
return cmdArray
|
||||
}
|
||||
+13
-12
@@ -1,20 +1,21 @@
|
||||
server:
|
||||
port: 8080
|
||||
baseURL: "/"
|
||||
port: 80
|
||||
baseURL: "/"
|
||||
root: "/srv"
|
||||
auth:
|
||||
method: noauth
|
||||
signup: true
|
||||
method: password
|
||||
signup: false
|
||||
userDefaults:
|
||||
darkMode: true
|
||||
disableSettings: false
|
||||
scope: "."
|
||||
hideDotfiles: true
|
||||
singleClick: false
|
||||
permissions:
|
||||
admin: false
|
||||
create: true
|
||||
rename: true
|
||||
modify: true
|
||||
delete: true
|
||||
share: true
|
||||
download: true
|
||||
frontend:
|
||||
theme: dark
|
||||
create: false
|
||||
rename: false
|
||||
modify: false
|
||||
delete: false
|
||||
share: false
|
||||
download: false
|
||||
@@ -1,28 +1,44 @@
|
||||
package search
|
||||
package files
|
||||
|
||||
import (
|
||||
"mime"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var typeRegexp = regexp.MustCompile(`type:(\S+)`)
|
||||
|
||||
var AllFiletypeOptions = []string{
|
||||
"image",
|
||||
"audio",
|
||||
"archive",
|
||||
"video",
|
||||
"doc",
|
||||
"dir",
|
||||
"text",
|
||||
}
|
||||
var documentTypes = []string{
|
||||
".word",
|
||||
".pdf",
|
||||
".txt",
|
||||
".doc",
|
||||
".docx",
|
||||
}
|
||||
var textTypes = []string{
|
||||
".text",
|
||||
".sh",
|
||||
".yaml",
|
||||
".yml",
|
||||
".json",
|
||||
".env",
|
||||
}
|
||||
|
||||
var compressedFile = []string{
|
||||
".7z",
|
||||
".rar",
|
||||
".zip",
|
||||
".tar",
|
||||
".tar.gz",
|
||||
".tar.xz",
|
||||
".gz",
|
||||
".xz",
|
||||
}
|
||||
|
||||
type SearchOptions struct {
|
||||
@@ -120,3 +136,48 @@ func updateSize(given string) int {
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func IsMatchingType(extension string, matchType string) bool {
|
||||
mimetype := mime.TypeByExtension(extension)
|
||||
if strings.HasPrefix(mimetype, matchType) {
|
||||
return true
|
||||
}
|
||||
switch matchType {
|
||||
case "doc":
|
||||
return isDoc(extension)
|
||||
case "pdf":
|
||||
return extension == ".pdf"
|
||||
case "text":
|
||||
return isText(extension)
|
||||
case "archive":
|
||||
return isArchive(extension)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isText(extension string) bool {
|
||||
for _, typefile := range textTypes {
|
||||
if extension == typefile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isDoc(extension string) bool {
|
||||
for _, typefile := range documentTypes {
|
||||
if extension == typefile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isArchive(extension string) bool {
|
||||
for _, typefile := range compressedFile {
|
||||
if extension == typefile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
+262
-133
@@ -8,33 +8,41 @@ import (
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"io"
|
||||
"log"
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
filepath "path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/rules"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
var (
|
||||
bytesInMegabyte int64 = 1000000
|
||||
pathMutexes = make(map[string]*sync.Mutex)
|
||||
pathMutexesMu sync.Mutex // Mutex to protect the pathMutexes map
|
||||
)
|
||||
|
||||
// FileInfo describes a file.
|
||||
type FileInfo struct {
|
||||
*Listing
|
||||
Fs afero.Fs `json:"-"`
|
||||
Path string `json:"path"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
Extension string `json:"extension"`
|
||||
Extension string `json:"-"`
|
||||
ModTime time.Time `json:"modified"`
|
||||
Mode os.FileMode `json:"mode"`
|
||||
IsDir bool `json:"isDir"`
|
||||
IsSymlink bool `json:"isSymlink"`
|
||||
CacheTime time.Time `json:"-"`
|
||||
Mode os.FileMode `json:"-"`
|
||||
IsDir bool `json:"isDir,omitempty"`
|
||||
IsSymlink bool `json:"isSymlink,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Subtitles []string `json:"subtitles,omitempty"`
|
||||
Content string `json:"content,omitempty"`
|
||||
@@ -54,6 +62,22 @@ type FileOptions struct {
|
||||
Content bool
|
||||
}
|
||||
|
||||
// Sorting constants
|
||||
const (
|
||||
SortingByName = "name"
|
||||
SortingBySize = "size"
|
||||
SortingByModified = "modified"
|
||||
)
|
||||
|
||||
// Listing is a collection of files.
|
||||
type Listing struct {
|
||||
Items []*FileInfo `json:"items"`
|
||||
Path string `json:"path"`
|
||||
NumDirs int `json:"numDirs"`
|
||||
NumFiles int `json:"numFiles"`
|
||||
Sorting users.Sorting `json:"sorting"`
|
||||
}
|
||||
|
||||
// NewFileInfo creates a File object from a path and a given user. This File
|
||||
// object will be automatically filled depending on if it is a directory
|
||||
// or a file. If it's a video file, it will also detect any subtitles.
|
||||
@@ -61,37 +85,131 @@ func NewFileInfo(opts FileOptions) (*FileInfo, error) {
|
||||
if !opts.Checker.Check(opts.Path) {
|
||||
return nil, os.ErrPermission
|
||||
}
|
||||
|
||||
file, err := stat(opts)
|
||||
file, err := stat(opts.Path, opts) // Pass opts.Path here
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.Expand {
|
||||
if file.IsDir {
|
||||
if err := file.readListing(opts.Checker, opts.ReadHeader); err != nil { //nolint:govet
|
||||
if err := file.readListing(opts.Path, opts.Checker, opts.ReadHeader); err != nil { //nolint:govet
|
||||
return nil, err
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
err = file.detectType(opts.Modify, opts.Content, true)
|
||||
err = file.detectType(opts.Path, opts.Modify, opts.Content, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return file, err
|
||||
}
|
||||
|
||||
func stat(opts FileOptions) (*FileInfo, error) {
|
||||
var file *FileInfo
|
||||
func FileInfoFaster(opts FileOptions) (*FileInfo, error) {
|
||||
// Lock access for the specific path
|
||||
pathMutex := getMutex(opts.Path)
|
||||
pathMutex.Lock()
|
||||
defer pathMutex.Unlock()
|
||||
if !opts.Checker.Check(opts.Path) {
|
||||
return nil, os.ErrPermission
|
||||
}
|
||||
index := GetIndex(rootPath)
|
||||
trimmed := strings.TrimPrefix(opts.Path, "/")
|
||||
if trimmed == "" {
|
||||
trimmed = "/"
|
||||
}
|
||||
adjustedPath := makeIndexPath(trimmed, index.Root)
|
||||
var info FileInfo
|
||||
info, exists := index.GetMetadataInfo(adjustedPath)
|
||||
if exists && !opts.Content {
|
||||
// Check if the cache time is less than 1 second
|
||||
if time.Since(info.CacheTime) > time.Second {
|
||||
go RefreshFileInfo(opts)
|
||||
}
|
||||
// refresh cache after
|
||||
return &info, nil
|
||||
} else {
|
||||
// don't bother caching content
|
||||
if opts.Content {
|
||||
file, err := NewFileInfo(opts)
|
||||
return file, err
|
||||
}
|
||||
updated := RefreshFileInfo(opts)
|
||||
if !updated {
|
||||
file, err := NewFileInfo(opts)
|
||||
return file, err
|
||||
}
|
||||
info, exists = index.GetMetadataInfo(adjustedPath)
|
||||
if !exists || info.Name == "" {
|
||||
return &FileInfo{}, errors.ErrEmptyKey
|
||||
}
|
||||
return &info, nil
|
||||
}
|
||||
}
|
||||
|
||||
func RefreshFileInfo(opts FileOptions) bool {
|
||||
if !opts.Checker.Check(opts.Path) {
|
||||
return false
|
||||
}
|
||||
index := GetIndex(rootPath)
|
||||
trimmed := strings.TrimPrefix(opts.Path, "/")
|
||||
if trimmed == "" {
|
||||
trimmed = "/"
|
||||
}
|
||||
adjustedPath := makeIndexPath(trimmed, index.Root)
|
||||
file, err := stat(opts.Path, opts) // Pass opts.Path here
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_ = file.detectType(adjustedPath, true, opts.Content, opts.ReadHeader)
|
||||
if file.IsDir {
|
||||
err := file.readListing(opts.Path, opts.Checker, opts.ReadHeader)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
//_, exists := index.GetFileMetadata(adjustedPath)
|
||||
|
||||
return index.UpdateFileMetadata(adjustedPath, *file)
|
||||
} else {
|
||||
//_, exists := index.GetFileMetadata(adjustedPath)
|
||||
return index.UpdateFileMetadata(adjustedPath, *file)
|
||||
}
|
||||
}
|
||||
|
||||
func stat(path string, opts FileOptions) (*FileInfo, error) {
|
||||
var file *FileInfo
|
||||
if lstaterFs, ok := opts.Fs.(afero.Lstater); ok {
|
||||
info, _, err := lstaterFs.LstatIfPossible(opts.Path)
|
||||
info, _, err := lstaterFs.LstatIfPossible(path)
|
||||
if err == nil {
|
||||
file = &FileInfo{
|
||||
Fs: opts.Fs,
|
||||
Path: opts.Path,
|
||||
Name: info.Name(),
|
||||
ModTime: info.ModTime(),
|
||||
Mode: info.Mode(),
|
||||
Size: info.Size(),
|
||||
Extension: filepath.Ext(info.Name()),
|
||||
Token: opts.Token,
|
||||
}
|
||||
if info.IsDir() {
|
||||
file.IsDir = true
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 {
|
||||
file.IsSymlink = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if file == nil || file.IsSymlink {
|
||||
info, err := opts.Fs.Stat(opts.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if file != nil && file.IsSymlink {
|
||||
file.Size = info.Size()
|
||||
file.IsDir = info.IsDir()
|
||||
return file, nil
|
||||
}
|
||||
|
||||
file = &FileInfo{
|
||||
Fs: opts.Fs,
|
||||
Path: opts.Path,
|
||||
@@ -99,47 +217,12 @@ func stat(opts FileOptions) (*FileInfo, error) {
|
||||
ModTime: info.ModTime(),
|
||||
Mode: info.Mode(),
|
||||
IsDir: info.IsDir(),
|
||||
IsSymlink: IsSymlink(info.Mode()),
|
||||
Size: info.Size(),
|
||||
Extension: filepath.Ext(info.Name()),
|
||||
Token: opts.Token,
|
||||
}
|
||||
}
|
||||
|
||||
// regular file
|
||||
if file != nil && !file.IsSymlink {
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// fs doesn't support afero.Lstater interface or the file is a symlink
|
||||
info, err := opts.Fs.Stat(opts.Path)
|
||||
if err != nil {
|
||||
// can't follow symlink
|
||||
if file != nil && file.IsSymlink {
|
||||
return file, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set correct file size in case of symlink
|
||||
if file != nil && file.IsSymlink {
|
||||
file.Size = info.Size()
|
||||
file.IsDir = info.IsDir()
|
||||
return file, nil
|
||||
}
|
||||
|
||||
file = &FileInfo{
|
||||
Fs: opts.Fs,
|
||||
Path: opts.Path,
|
||||
Name: info.Name(),
|
||||
ModTime: info.ModTime(),
|
||||
Mode: info.Mode(),
|
||||
IsDir: info.IsDir(),
|
||||
Size: info.Size(),
|
||||
Extension: filepath.Ext(info.Name()),
|
||||
Token: opts.Token,
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
@@ -160,19 +243,15 @@ func (i *FileInfo) Checksum(algo string) error {
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
var h hash.Hash
|
||||
hashFuncs := map[string]hash.Hash{
|
||||
"md5": md5.New(),
|
||||
"sha1": sha1.New(),
|
||||
"sha256": sha256.New(),
|
||||
"sha512": sha512.New(),
|
||||
}
|
||||
|
||||
//nolint:gosec
|
||||
switch algo {
|
||||
case "md5":
|
||||
h = md5.New()
|
||||
case "sha1":
|
||||
h = sha1.New()
|
||||
case "sha256":
|
||||
h = sha256.New()
|
||||
case "sha512":
|
||||
h = sha512.New()
|
||||
default:
|
||||
h, ok := hashFuncs[algo]
|
||||
if !ok {
|
||||
return errors.ErrInvalidOption
|
||||
}
|
||||
|
||||
@@ -185,6 +264,7 @@ func (i *FileInfo) Checksum(algo string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RealPath gets the real path for the file, resolving symlinks if supported.
|
||||
func (i *FileInfo) RealPath() string {
|
||||
if realPathFs, ok := i.Fs.(interface {
|
||||
RealPath(name string) (fPath string, err error)
|
||||
@@ -198,72 +278,88 @@ func (i *FileInfo) RealPath() string {
|
||||
return i.Path
|
||||
}
|
||||
|
||||
// TODO: use constants
|
||||
//
|
||||
//nolint:goconst
|
||||
func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
||||
// addContent reads and sets content based on the file type.
|
||||
func (i *FileInfo) addContent(path string) error {
|
||||
if !i.IsDir {
|
||||
afs := &afero.Afero{Fs: i.Fs}
|
||||
content, err := afs.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stringContent := string(content)
|
||||
if !utf8.ValidString(stringContent) {
|
||||
return nil
|
||||
}
|
||||
if stringContent == "" {
|
||||
i.Content = "empty-file-x6OlSil"
|
||||
return nil
|
||||
}
|
||||
i.Content = stringContent
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// detectType detects the file type.
|
||||
func (i *FileInfo) detectType(path string, modify, saveContent, readHeader bool) error {
|
||||
if IsNamedPipe(i.Mode) {
|
||||
i.Type = "blob"
|
||||
if saveContent {
|
||||
return i.addContent(path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// failing to detect the type should not return error.
|
||||
// imagine the situation where a file in a dir with thousands
|
||||
// of files couldn't be opened: we'd have immediately
|
||||
// a 500 even though it doesn't matter. So we just log it.
|
||||
|
||||
mimetype := mime.TypeByExtension(i.Extension)
|
||||
|
||||
var buffer []byte
|
||||
if readHeader {
|
||||
buffer = i.readFirstBytes()
|
||||
|
||||
mimetype := mime.TypeByExtension(i.Extension)
|
||||
if mimetype == "" {
|
||||
mimetype = http.DetectContentType(buffer)
|
||||
http.DetectContentType(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(mimetype, "video"):
|
||||
i.Type = "video"
|
||||
i.detectSubtitles()
|
||||
return nil
|
||||
case strings.HasPrefix(mimetype, "audio"):
|
||||
i.Type = "audio"
|
||||
return nil
|
||||
case strings.HasPrefix(mimetype, "image"):
|
||||
i.Type = "image"
|
||||
return nil
|
||||
case strings.HasSuffix(mimetype, "pdf"):
|
||||
i.Type = "pdf"
|
||||
return nil
|
||||
case (strings.HasPrefix(mimetype, "text") || !isBinary(buffer)) && i.Size <= 10*1024*1024: // 10 MB
|
||||
i.Type = "text"
|
||||
|
||||
if !modify {
|
||||
i.Type = "textImmutable"
|
||||
ext := filepath.Ext(i.Name)
|
||||
for _, fileType := range AllFiletypeOptions {
|
||||
if IsMatchingType(ext, fileType) {
|
||||
i.Type = fileType
|
||||
}
|
||||
|
||||
if saveContent {
|
||||
afs := &afero.Afero{Fs: i.Fs}
|
||||
content, err := afs.ReadFile(i.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
switch i.Type {
|
||||
case "text":
|
||||
if !modify {
|
||||
i.Type = "textImmutable"
|
||||
}
|
||||
if saveContent {
|
||||
return i.addContent(path)
|
||||
}
|
||||
case "video":
|
||||
parentDir := strings.TrimRight(path, i.Name)
|
||||
i.detectSubtitles(parentDir)
|
||||
case "doc":
|
||||
if ext == ".pdf" {
|
||||
i.Type = "pdf"
|
||||
return nil
|
||||
}
|
||||
if saveContent {
|
||||
return i.addContent(path)
|
||||
}
|
||||
|
||||
i.Content = string(content)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
||||
if i.Type == "" {
|
||||
i.Type = "blob"
|
||||
if saveContent {
|
||||
return i.addContent(path)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// readFirstBytes reads the first bytes of the file.
|
||||
func (i *FileInfo) readFirstBytes() []byte {
|
||||
reader, err := i.Fs.Open(i.Path)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
i.Type = "blob"
|
||||
return nil
|
||||
}
|
||||
@@ -272,7 +368,6 @@ func (i *FileInfo) readFirstBytes() []byte {
|
||||
buffer := make([]byte, 512) //nolint:gomnd
|
||||
n, err := reader.Read(buffer)
|
||||
if err != nil && err != io.EOF {
|
||||
log.Print(err)
|
||||
i.Type = "blob"
|
||||
return nil
|
||||
}
|
||||
@@ -280,29 +375,43 @@ func (i *FileInfo) readFirstBytes() []byte {
|
||||
return buffer[:n]
|
||||
}
|
||||
|
||||
func (i *FileInfo) detectSubtitles() {
|
||||
// detectSubtitles detects subtitles for video files.
|
||||
func (i *FileInfo) detectSubtitles(parentDir string) {
|
||||
if i.Type != "video" {
|
||||
return
|
||||
}
|
||||
|
||||
i.Subtitles = []string{}
|
||||
ext := filepath.Ext(i.Path)
|
||||
ext := filepath.Ext(i.Name)
|
||||
dir, err := os.Open(parentDir)
|
||||
if err != nil {
|
||||
// Directory must have been deleted, remove it from the index
|
||||
return
|
||||
}
|
||||
// Read the directory contents
|
||||
files, err := dir.Readdir(-1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// detect multiple languages. Base*.vtt
|
||||
// TODO: give subtitles descriptive names (lang) and track attributes
|
||||
parentDir := strings.TrimRight(i.Path, i.Name)
|
||||
dir, err := afero.ReadDir(i.Fs, parentDir)
|
||||
if err == nil {
|
||||
base := strings.TrimSuffix(i.Name, ext)
|
||||
for _, f := range dir {
|
||||
if !f.IsDir() && strings.HasPrefix(f.Name(), base) && strings.HasSuffix(f.Name(), ".vtt") {
|
||||
i.Subtitles = append(i.Subtitles, path.Join(parentDir, f.Name()))
|
||||
base := strings.TrimSuffix(i.Name, ext)
|
||||
subtitleExts := []string{".vtt", ".txt", ".srt", ".lrc"}
|
||||
|
||||
for _, f := range files {
|
||||
if f.IsDir() || !strings.HasPrefix(f.Name(), base) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, subtitleExt := range subtitleExts {
|
||||
if strings.HasSuffix(f.Name(), subtitleExt) {
|
||||
i.Subtitles = append(i.Subtitles, filepath.Join(parentDir, f.Name()))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||
// readListing reads the contents of a directory and fills the listing.
|
||||
func (i *FileInfo) readListing(path string, checker rules.Checker, readHeader bool) error {
|
||||
afs := &afero.Afero{Fs: i.Fs}
|
||||
dir, err := afs.ReadDir(i.Path)
|
||||
if err != nil {
|
||||
@@ -311,13 +420,14 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||
|
||||
listing := &Listing{
|
||||
Items: []*FileInfo{},
|
||||
Path: i.Path,
|
||||
NumDirs: 0,
|
||||
NumFiles: 0,
|
||||
}
|
||||
|
||||
for _, f := range dir {
|
||||
name := f.Name()
|
||||
fPath := path.Join(i.Path, name)
|
||||
fPath := filepath.Join(i.Path, name)
|
||||
|
||||
if !checker.Check(fPath) {
|
||||
continue
|
||||
@@ -326,8 +436,6 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||
isSymlink, isInvalidLink := false, false
|
||||
if IsSymlink(f.Mode()) {
|
||||
isSymlink = true
|
||||
// It's a symbolic link. We try to follow it. If it doesn't work,
|
||||
// we stay with the link information instead of the target's.
|
||||
info, err := i.Fs.Stat(fPath)
|
||||
if err == nil {
|
||||
f = info
|
||||
@@ -337,15 +445,16 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||
}
|
||||
|
||||
file := &FileInfo{
|
||||
Fs: i.Fs,
|
||||
Name: name,
|
||||
Size: f.Size(),
|
||||
ModTime: f.ModTime(),
|
||||
Mode: f.Mode(),
|
||||
IsDir: f.IsDir(),
|
||||
IsSymlink: isSymlink,
|
||||
Extension: filepath.Ext(name),
|
||||
Path: fPath,
|
||||
Name: name,
|
||||
Size: f.Size(),
|
||||
ModTime: f.ModTime(),
|
||||
Mode: f.Mode(),
|
||||
}
|
||||
if f.IsDir() {
|
||||
file.IsDir = true
|
||||
}
|
||||
if isSymlink {
|
||||
file.IsSymlink = true
|
||||
}
|
||||
|
||||
if file.IsDir {
|
||||
@@ -356,7 +465,7 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||
if isInvalidLink {
|
||||
file.Type = "invalid_link"
|
||||
} else {
|
||||
err := file.detectType(true, false, readHeader)
|
||||
err := file.detectType(path, true, false, readHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -369,3 +478,23 @@ func (i *FileInfo) readListing(checker rules.Checker, readHeader bool) error {
|
||||
i.Listing = listing
|
||||
return nil
|
||||
}
|
||||
func IsNamedPipe(mode os.FileMode) bool {
|
||||
return mode&os.ModeNamedPipe != 0
|
||||
}
|
||||
|
||||
func IsSymlink(mode os.FileMode) bool {
|
||||
return mode&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
func getMutex(path string) *sync.Mutex {
|
||||
// Lock access to pathMutexes map
|
||||
pathMutexesMu.Lock()
|
||||
defer pathMutexesMu.Unlock()
|
||||
|
||||
// Create a mutex for the path if it doesn't exist
|
||||
if pathMutexes[path] == nil {
|
||||
pathMutexes[path] = &sync.Mutex{}
|
||||
}
|
||||
|
||||
return pathMutexes[path]
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
)
|
||||
|
||||
type Directory struct {
|
||||
Metadata map[string]FileInfo
|
||||
Files string
|
||||
}
|
||||
type File struct {
|
||||
Name string
|
||||
IsDir bool
|
||||
}
|
||||
|
||||
type Index struct {
|
||||
Root string
|
||||
Directories map[string]Directory
|
||||
NumDirs int
|
||||
NumFiles int
|
||||
inProgress bool
|
||||
quickList []File
|
||||
LastIndexed time.Time
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
var (
|
||||
rootPath string = "/srv"
|
||||
indexes []*Index
|
||||
indexesMutex sync.RWMutex
|
||||
)
|
||||
|
||||
func InitializeIndex(intervalMinutes uint32, schedule bool) {
|
||||
if schedule {
|
||||
go indexingScheduler(intervalMinutes)
|
||||
}
|
||||
}
|
||||
|
||||
func indexingScheduler(intervalMinutes uint32) {
|
||||
if settings.Config.Server.Root != "" {
|
||||
rootPath = settings.Config.Server.Root
|
||||
}
|
||||
si := GetIndex(rootPath)
|
||||
log.Printf("Indexing Files...")
|
||||
log.Printf("Configured to run every %v minutes", intervalMinutes)
|
||||
log.Printf("Indexing from root: %s", si.Root)
|
||||
for {
|
||||
startTime := time.Now()
|
||||
// Set the indexing flag to indicate that indexing is in progress
|
||||
si.resetCount()
|
||||
// Perform the indexing operation
|
||||
err := si.indexFiles(si.Root)
|
||||
si.quickList = []File{}
|
||||
// Reset the indexing flag to indicate that indexing has finished
|
||||
si.inProgress = false
|
||||
// Update the LastIndexed time
|
||||
si.LastIndexed = time.Now()
|
||||
if err != nil {
|
||||
log.Printf("Error during indexing: %v", err)
|
||||
}
|
||||
if si.NumFiles+si.NumDirs > 0 {
|
||||
timeIndexedInSeconds := int(time.Since(startTime).Seconds())
|
||||
log.Println("Successfully indexed files.")
|
||||
log.Printf("Time spent indexing: %v seconds\n", timeIndexedInSeconds)
|
||||
log.Printf("Files found: %v\n", si.NumFiles)
|
||||
log.Printf("Directories found: %v\n", si.NumDirs)
|
||||
}
|
||||
// Sleep for the specified interval
|
||||
time.Sleep(time.Duration(intervalMinutes) * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
// Define a function to recursively index files and directories
|
||||
func (si *Index) indexFiles(path string) error {
|
||||
// Check if the current directory has been modified since the last indexing
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
adjustedPath := makeIndexPath(path, si.Root)
|
||||
dir, err := os.Open(path)
|
||||
if err != nil {
|
||||
// Directory must have been deleted, remove it from the index
|
||||
si.RemoveDirectory(adjustedPath)
|
||||
}
|
||||
dirInfo, err := dir.Stat()
|
||||
if err != nil {
|
||||
dir.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// Compare the last modified time of the directory with the last indexed time
|
||||
lastIndexed := si.LastIndexed
|
||||
if dirInfo.ModTime().Before(lastIndexed) {
|
||||
dir.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read the directory contents
|
||||
files, err := dir.Readdir(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dir.Close()
|
||||
si.UpdateQuickList(files)
|
||||
si.InsertFiles(path)
|
||||
// done separately for memory efficiency on recursion
|
||||
si.InsertDirs(path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (si *Index) InsertFiles(path string) {
|
||||
adjustedPath := makeIndexPath(path, si.Root)
|
||||
subDirectory := Directory{}
|
||||
buffer := bytes.Buffer{}
|
||||
|
||||
for _, f := range si.GetQuickList() {
|
||||
if !f.IsDir {
|
||||
buffer.WriteString(f.Name + ";")
|
||||
si.UpdateCount("files")
|
||||
}
|
||||
}
|
||||
// Use GetMetadataInfo and SetFileMetadata for safer read and write operations
|
||||
subDirectory.Files = buffer.String()
|
||||
si.SetDirectoryInfo(adjustedPath, subDirectory)
|
||||
}
|
||||
|
||||
func (si *Index) InsertDirs(path string) {
|
||||
adjustedPath := makeIndexPath(path, si.Root)
|
||||
for _, f := range si.GetQuickList() {
|
||||
if f.IsDir {
|
||||
if _, exists := si.Directories[adjustedPath]; exists {
|
||||
si.UpdateCount("dirs")
|
||||
// Add or update the directory in the map
|
||||
if adjustedPath == "/" {
|
||||
si.SetDirectoryInfo("/"+f.Name, Directory{})
|
||||
} else {
|
||||
si.SetDirectoryInfo(adjustedPath+"/"+f.Name, Directory{})
|
||||
}
|
||||
}
|
||||
err := si.indexFiles(path + "/" + f.Name)
|
||||
if err != nil {
|
||||
if err.Error() == "invalid argument" {
|
||||
log.Printf("Could not index \"%v\": %v \n", path, "Permission Denied")
|
||||
} else {
|
||||
log.Printf("Could not index \"%v\": %v \n", path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeIndexPath(path string, root string) string {
|
||||
if path == root {
|
||||
return "/"
|
||||
}
|
||||
adjustedPath := strings.TrimPrefix(path, root+"/")
|
||||
adjustedPath = strings.TrimSuffix(adjustedPath, "/")
|
||||
if adjustedPath == "" {
|
||||
adjustedPath = "/"
|
||||
}
|
||||
return adjustedPath
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
)
|
||||
|
||||
func BenchmarkFillIndex(b *testing.B) {
|
||||
InitializeIndex(5, false)
|
||||
si := GetIndex(settings.Config.Server.Root)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
si.createMockData(50, 3) // 1000 dirs, 3 files per dir
|
||||
}
|
||||
}
|
||||
|
||||
func (si *Index) createMockData(numDirs, numFilesPerDir int) {
|
||||
for i := 0; i < numDirs; i++ {
|
||||
dirName := generateRandomPath(rand.Intn(3) + 1)
|
||||
files := []File{}
|
||||
// Append a new Directory to the slice
|
||||
for j := 0; j < numFilesPerDir; j++ {
|
||||
newFile := File{
|
||||
Name: "file-" + getRandomTerm() + getRandomExtension(),
|
||||
IsDir: false,
|
||||
}
|
||||
files = append(files, newFile)
|
||||
}
|
||||
si.UpdateQuickListForTests(files)
|
||||
si.InsertFiles(dirName)
|
||||
si.InsertDirs(dirName)
|
||||
}
|
||||
}
|
||||
|
||||
func generateRandomPath(levels int) string {
|
||||
rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
dirName := "srv"
|
||||
for i := 0; i < levels; i++ {
|
||||
dirName += "/" + getRandomTerm()
|
||||
}
|
||||
return dirName
|
||||
}
|
||||
|
||||
func getRandomTerm() string {
|
||||
wordbank := []string{
|
||||
"hi", "test", "other", "name",
|
||||
"cool", "things", "more", "items",
|
||||
}
|
||||
rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
index := rand.Intn(len(wordbank))
|
||||
return wordbank[index]
|
||||
}
|
||||
|
||||
func getRandomExtension() string {
|
||||
wordbank := []string{
|
||||
".txt", ".mp3", ".mov", ".doc",
|
||||
".mp4", ".bak", ".zip", ".jpg",
|
||||
}
|
||||
rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
index := rand.Intn(len(wordbank))
|
||||
return wordbank[index]
|
||||
}
|
||||
|
||||
func generateRandomSearchTerms(numTerms int) []string {
|
||||
// Generate random search terms
|
||||
searchTerms := make([]string, numTerms)
|
||||
for i := 0; i < numTerms; i++ {
|
||||
searchTerms[i] = getRandomTerm()
|
||||
}
|
||||
return searchTerms
|
||||
}
|
||||
|
||||
// JSONBytesEqual compares the JSON in two byte slices.
|
||||
func JSONBytesEqual(a, b []byte) (bool, error) {
|
||||
var j, j2 interface{}
|
||||
if err := json.Unmarshal(a, &j); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := json.Unmarshal(b, &j2); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return reflect.DeepEqual(j2, j), nil
|
||||
}
|
||||
|
||||
func TestGetIndex(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want *map[string][]string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GetIndex("root"); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GetIndex() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitializeIndex(t *testing.T) {
|
||||
type args struct {
|
||||
intervalMinutes uint32
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
InitializeIndex(tt.args.intervalMinutes, false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_indexingScheduler(t *testing.T) {
|
||||
type args struct {
|
||||
intervalMinutes uint32
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
indexingScheduler(tt.args.intervalMinutes)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/maruel/natural"
|
||||
)
|
||||
|
||||
// Listing is a collection of files.
|
||||
type Listing struct {
|
||||
Items []*FileInfo `json:"items"`
|
||||
NumDirs int `json:"numDirs"`
|
||||
NumFiles int `json:"numFiles"`
|
||||
Sorting Sorting `json:"sorting"`
|
||||
}
|
||||
|
||||
// ApplySort applies the sort order using .Order and .Sort
|
||||
//
|
||||
//nolint:goconst
|
||||
func (l Listing) ApplySort() {
|
||||
// Check '.Order' to know how to sort
|
||||
// TODO: use enum
|
||||
if !l.Sorting.Asc {
|
||||
switch l.Sorting.By {
|
||||
case "name":
|
||||
sort.Sort(sort.Reverse(byName(l)))
|
||||
case "size":
|
||||
sort.Sort(sort.Reverse(bySize(l)))
|
||||
case "modified":
|
||||
sort.Sort(sort.Reverse(byModified(l)))
|
||||
default:
|
||||
// If not one of the above, do nothing
|
||||
return
|
||||
}
|
||||
} else { // If we had more Orderings we could add them here
|
||||
switch l.Sorting.By {
|
||||
case "name":
|
||||
sort.Sort(byName(l))
|
||||
case "size":
|
||||
sort.Sort(bySize(l))
|
||||
case "modified":
|
||||
sort.Sort(byModified(l))
|
||||
default:
|
||||
sort.Sort(byName(l))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implement sorting for Listing
|
||||
type byName Listing
|
||||
type bySize Listing
|
||||
type byModified Listing
|
||||
|
||||
// By Name
|
||||
func (l byName) Len() int {
|
||||
return len(l.Items)
|
||||
}
|
||||
|
||||
func (l byName) Swap(i, j int) {
|
||||
l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
|
||||
}
|
||||
|
||||
// Treat upper and lower case equally
|
||||
func (l byName) Less(i, j int) bool {
|
||||
if l.Items[i].IsDir && !l.Items[j].IsDir {
|
||||
return l.Sorting.Asc
|
||||
}
|
||||
|
||||
if !l.Items[i].IsDir && l.Items[j].IsDir {
|
||||
return !l.Sorting.Asc
|
||||
}
|
||||
|
||||
return natural.Less(strings.ToLower(l.Items[j].Name), strings.ToLower(l.Items[i].Name))
|
||||
}
|
||||
|
||||
// By Size
|
||||
func (l bySize) Len() int {
|
||||
return len(l.Items)
|
||||
}
|
||||
|
||||
func (l bySize) Swap(i, j int) {
|
||||
l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
|
||||
}
|
||||
|
||||
const directoryOffset = -1 << 31 // = math.MinInt32
|
||||
func (l bySize) Less(i, j int) bool {
|
||||
iSize, jSize := l.Items[i].Size, l.Items[j].Size
|
||||
if l.Items[i].IsDir {
|
||||
iSize = directoryOffset + iSize
|
||||
}
|
||||
if l.Items[j].IsDir {
|
||||
jSize = directoryOffset + jSize
|
||||
}
|
||||
return iSize < jSize
|
||||
}
|
||||
|
||||
// By Modified
|
||||
func (l byModified) Len() int {
|
||||
return len(l.Items)
|
||||
}
|
||||
|
||||
func (l byModified) Swap(i, j int) {
|
||||
l.Items[i], l.Items[j] = l.Items[j], l.Items[i]
|
||||
}
|
||||
|
||||
func (l byModified) Less(i, j int) bool {
|
||||
iModified, jModified := l.Items[i].ModTime, l.Items[j].ModTime
|
||||
return iModified.Sub(jModified) < 0
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
sessionInProgress sync.Map
|
||||
maxSearchResults = 100
|
||||
)
|
||||
|
||||
func (si *Index) Search(search string, scope string, sourceSession string) ([]string, map[string]map[string]bool) {
|
||||
// Remove slashes
|
||||
scope = strings.TrimLeft(scope, "/")
|
||||
scope = strings.TrimRight(scope, "/")
|
||||
runningHash := generateRandomHash(4)
|
||||
sessionInProgress.Store(sourceSession, runningHash) // Store the value in the sync.Map
|
||||
searchOptions := ParseSearch(search)
|
||||
fileListTypes := make(map[string]map[string]bool)
|
||||
matching := []string{}
|
||||
count := 0
|
||||
for _, searchTerm := range searchOptions.Terms {
|
||||
if searchTerm == "" {
|
||||
continue
|
||||
}
|
||||
si.mu.Lock()
|
||||
defer si.mu.Unlock()
|
||||
for dirName, dir := range si.Directories {
|
||||
isDir := true
|
||||
files := strings.Split(dir.Files, ";")
|
||||
value, found := sessionInProgress.Load(sourceSession)
|
||||
if !found || value != runningHash {
|
||||
return []string{}, map[string]map[string]bool{}
|
||||
}
|
||||
if count > maxSearchResults {
|
||||
break
|
||||
}
|
||||
pathName := scopedPathNameFilter(dirName, scope, isDir)
|
||||
if pathName == "" {
|
||||
continue // path not matched
|
||||
}
|
||||
fileTypes := map[string]bool{}
|
||||
matches, fileType := containsSearchTerm(dirName, searchTerm, *searchOptions, isDir, fileTypes)
|
||||
if matches {
|
||||
fileListTypes[pathName] = fileType
|
||||
matching = append(matching, pathName)
|
||||
count++
|
||||
}
|
||||
isDir = false
|
||||
for _, file := range files {
|
||||
if file == "" {
|
||||
continue
|
||||
}
|
||||
value, found := sessionInProgress.Load(sourceSession)
|
||||
if !found || value != runningHash {
|
||||
return []string{}, map[string]map[string]bool{}
|
||||
}
|
||||
|
||||
if count > maxSearchResults {
|
||||
break
|
||||
}
|
||||
fullName := pathName + file
|
||||
fileTypes := map[string]bool{}
|
||||
|
||||
matches, fileType := containsSearchTerm(fullName, searchTerm, *searchOptions, isDir, fileTypes)
|
||||
if !matches {
|
||||
continue
|
||||
}
|
||||
fileListTypes[fullName] = fileType
|
||||
matching = append(matching, fullName)
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort the strings based on the number of elements after splitting by "/"
|
||||
sort.Slice(matching, func(i, j int) bool {
|
||||
parts1 := strings.Split(matching[i], "/")
|
||||
parts2 := strings.Split(matching[j], "/")
|
||||
return len(parts1) < len(parts2)
|
||||
})
|
||||
return matching, fileListTypes
|
||||
}
|
||||
|
||||
func scopedPathNameFilter(pathName string, scope string, isDir bool) string {
|
||||
pathName = strings.TrimLeft(pathName, "/")
|
||||
pathName = strings.TrimRight(pathName, "/")
|
||||
if strings.HasPrefix(pathName, scope) || scope == "" {
|
||||
pathName = strings.TrimPrefix(pathName, scope)
|
||||
pathName = strings.TrimLeft(pathName, "/")
|
||||
if isDir {
|
||||
pathName = pathName + "/"
|
||||
}
|
||||
} else {
|
||||
pathName = "" // return not matched
|
||||
}
|
||||
return pathName
|
||||
}
|
||||
|
||||
func containsSearchTerm(pathName string, searchTerm string, options SearchOptions, isDir bool, fileTypes map[string]bool) (bool, map[string]bool) {
|
||||
conditions := options.Conditions
|
||||
path := getLastPathComponent(pathName)
|
||||
// Convert to lowercase once
|
||||
if !conditions["exact"] {
|
||||
path = strings.ToLower(path)
|
||||
searchTerm = strings.ToLower(searchTerm)
|
||||
}
|
||||
if strings.Contains(path, searchTerm) {
|
||||
// Calculate fileSize only if needed
|
||||
var fileSize int64
|
||||
matchesAllConditions := true
|
||||
extension := filepath.Ext(path)
|
||||
for _, k := range AllFiletypeOptions {
|
||||
if IsMatchingType(extension, k) {
|
||||
fileTypes[k] = true
|
||||
}
|
||||
}
|
||||
fileTypes["dir"] = isDir
|
||||
for t, v := range conditions {
|
||||
if t == "exact" {
|
||||
continue
|
||||
}
|
||||
var matchesCondition bool
|
||||
switch t {
|
||||
case "larger":
|
||||
if fileSize == 0 {
|
||||
fileSize = getFileSize(pathName)
|
||||
}
|
||||
matchesCondition = fileSize > int64(options.LargerThan)*bytesInMegabyte
|
||||
case "smaller":
|
||||
if fileSize == 0 {
|
||||
fileSize = getFileSize(pathName)
|
||||
}
|
||||
matchesCondition = fileSize < int64(options.SmallerThan)*bytesInMegabyte
|
||||
default:
|
||||
matchesCondition = v == fileTypes[t]
|
||||
}
|
||||
if !matchesCondition {
|
||||
matchesAllConditions = false
|
||||
}
|
||||
}
|
||||
return matchesAllConditions, fileTypes
|
||||
}
|
||||
// Clear variables and return
|
||||
return false, map[string]bool{}
|
||||
}
|
||||
|
||||
func getFileSize(filepath string) int64 {
|
||||
fileInfo, err := os.Stat(rootPath + "/" + filepath)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return fileInfo.Size()
|
||||
}
|
||||
|
||||
func getLastPathComponent(path string) string {
|
||||
// Use filepath.Base to extract the last component of the path
|
||||
return filepath.Base(path)
|
||||
}
|
||||
|
||||
func generateRandomHash(length int) string {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
result := make([]byte, length)
|
||||
for i := range result {
|
||||
result[i] = charset[rand.Intn(len(charset))]
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func BenchmarkSearchAllIndexes(b *testing.B) {
|
||||
InitializeIndex(5, false)
|
||||
si := GetIndex(rootPath)
|
||||
|
||||
si.createMockData(50, 3) // 1000 dirs, 3 files per dir
|
||||
|
||||
// Generate 100 random search terms
|
||||
searchTerms := generateRandomSearchTerms(100)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Execute the SearchAllIndexes function
|
||||
for _, term := range searchTerms {
|
||||
si.Search(term, "/", "test")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop over test files and compare output
|
||||
func TestParseSearch(t *testing.T) {
|
||||
value := ParseSearch("my test search")
|
||||
want := &SearchOptions{
|
||||
Conditions: map[string]bool{
|
||||
"exact": false,
|
||||
},
|
||||
Terms: []string{"my test search"},
|
||||
}
|
||||
if !reflect.DeepEqual(value, want) {
|
||||
t.Fatalf("\n got: %+v\n want: %+v", value, want)
|
||||
}
|
||||
value = ParseSearch("case:exact my|test|search")
|
||||
want = &SearchOptions{
|
||||
Conditions: map[string]bool{
|
||||
"exact": true,
|
||||
},
|
||||
Terms: []string{"my", "test", "search"},
|
||||
}
|
||||
if !reflect.DeepEqual(value, want) {
|
||||
t.Fatalf("\n got: %+v\n want: %+v", value, want)
|
||||
}
|
||||
value = ParseSearch("type:largerThan=100 type:smallerThan=1000 test")
|
||||
want = &SearchOptions{
|
||||
Conditions: map[string]bool{
|
||||
"exact": false,
|
||||
"larger": true,
|
||||
},
|
||||
Terms: []string{"test"},
|
||||
LargerThan: 100,
|
||||
SmallerThan: 1000,
|
||||
}
|
||||
if !reflect.DeepEqual(value, want) {
|
||||
t.Fatalf("\n got: %+v\n want: %+v", value, want)
|
||||
}
|
||||
value = ParseSearch("type:audio thisfile")
|
||||
want = &SearchOptions{
|
||||
Conditions: map[string]bool{
|
||||
"exact": false,
|
||||
"audio": true,
|
||||
},
|
||||
Terms: []string{"thisfile"},
|
||||
}
|
||||
if !reflect.DeepEqual(value, want) {
|
||||
t.Fatalf("\n got: %+v\n want: %+v", value, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchWhileIndexing(t *testing.T) {
|
||||
InitializeIndex(5, false)
|
||||
si := GetIndex(rootPath)
|
||||
// Generate 100 random search terms
|
||||
// Generate 100 random search terms
|
||||
searchTerms := generateRandomSearchTerms(10)
|
||||
for i := 0; i < 5; i++ {
|
||||
// Execute the SearchAllIndexes function
|
||||
go si.createMockData(100, 100) // 1000 dirs, 3 files per dir
|
||||
for _, term := range searchTerms {
|
||||
go si.Search(term, "/", "test")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchIndexes(t *testing.T) {
|
||||
index := Index{
|
||||
Directories: map[string]Directory{
|
||||
"test": {
|
||||
Files: "audio1.wav;",
|
||||
},
|
||||
"test/path": {
|
||||
Files: "file.txt;",
|
||||
},
|
||||
"new": {},
|
||||
"new/test": {
|
||||
Files: "audio.wav;video.mp4;video.MP4;",
|
||||
},
|
||||
"new/test/path": {
|
||||
Files: "archive.zip;",
|
||||
},
|
||||
},
|
||||
}
|
||||
tests := []struct {
|
||||
search string
|
||||
scope string
|
||||
expectedResult []string
|
||||
expectedTypes map[string]map[string]bool
|
||||
}{
|
||||
{
|
||||
search: "audio",
|
||||
scope: "/new/",
|
||||
expectedResult: []string{"test/audio.wav"},
|
||||
expectedTypes: map[string]map[string]bool{
|
||||
"test/audio.wav": map[string]bool{"audio": true, "dir": false},
|
||||
},
|
||||
},
|
||||
{
|
||||
search: "test",
|
||||
scope: "/",
|
||||
expectedResult: []string{"test/", "new/test/"},
|
||||
expectedTypes: map[string]map[string]bool{
|
||||
"test/": map[string]bool{"dir": true},
|
||||
"new/test/": map[string]bool{"dir": true},
|
||||
},
|
||||
},
|
||||
{
|
||||
search: "archive",
|
||||
scope: "/",
|
||||
expectedResult: []string{"new/test/path/archive.zip"},
|
||||
expectedTypes: map[string]map[string]bool{
|
||||
"new/test/path/archive.zip": map[string]bool{"archive": true, "dir": false},
|
||||
},
|
||||
},
|
||||
{
|
||||
search: "video",
|
||||
scope: "/",
|
||||
expectedResult: []string{
|
||||
"new/test/video.mp4",
|
||||
"new/test/video.MP4",
|
||||
},
|
||||
expectedTypes: map[string]map[string]bool{
|
||||
"new/test/video.MP4": map[string]bool{"video": true, "dir": false},
|
||||
"new/test/video.mp4": map[string]bool{"video": true, "dir": false},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.search, func(t *testing.T) {
|
||||
actualResult, actualTypes := index.Search(tt.search, tt.scope, "")
|
||||
assert.Equal(t, tt.expectedResult, actualResult)
|
||||
if !reflect.DeepEqual(tt.expectedTypes, actualTypes) {
|
||||
t.Fatalf("\n got: %+v\n want: %+v", actualTypes, tt.expectedTypes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_scopedPathNameFilter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args struct {
|
||||
pathName string
|
||||
scope string
|
||||
isDir bool // Assuming isDir should be included in args
|
||||
}
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "scope test",
|
||||
args: struct {
|
||||
pathName string
|
||||
scope string
|
||||
isDir bool
|
||||
}{
|
||||
pathName: "/",
|
||||
scope: "/",
|
||||
isDir: false,
|
||||
},
|
||||
want: "", // Update this with the expected result
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := scopedPathNameFilter(tt.args.pathName, tt.args.scope, tt.args.isDir); got != tt.want {
|
||||
t.Errorf("scopedPathNameFilter() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isDoc(t *testing.T) {
|
||||
type args struct {
|
||||
extension string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := isDoc(tt.args.extension); got != tt.want {
|
||||
t.Errorf("isDoc() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getFileSize(t *testing.T) {
|
||||
type args struct {
|
||||
filepath string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want int64
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := getFileSize(tt.args.filepath); got != tt.want {
|
||||
t.Errorf("getFileSize() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isArchive(t *testing.T) {
|
||||
type args struct {
|
||||
extension string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := isArchive(tt.args.extension); got != tt.want {
|
||||
t.Errorf("isArchive() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getLastPathComponent(t *testing.T) {
|
||||
type args struct {
|
||||
path string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := getLastPathComponent(tt.args.path); got != tt.want {
|
||||
t.Errorf("getLastPathComponent() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_generateRandomHash(t *testing.T) {
|
||||
type args struct {
|
||||
length int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := generateRandomHash(tt.args.length); got != tt.want {
|
||||
t.Errorf("generateRandomHash() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package files
|
||||
|
||||
// Sorting contains a sorting order.
|
||||
type Sorting struct {
|
||||
By string `json:"by"`
|
||||
Asc bool `json:"asc"`
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
)
|
||||
|
||||
// GetFileMetadata retrieves the FileInfo from the specified directory in the index.
|
||||
func (si *Index) GetFileMetadata(adjustedPath string) (FileInfo, bool) {
|
||||
si.mu.RLock()
|
||||
dir, exists := si.Directories[adjustedPath]
|
||||
si.mu.RUnlock()
|
||||
if exists {
|
||||
// Initialize the Metadata map if it is nil
|
||||
if dir.Metadata == nil {
|
||||
dir.Metadata = make(map[string]FileInfo)
|
||||
si.SetDirectoryInfo(adjustedPath, dir)
|
||||
return FileInfo{}, false
|
||||
} else {
|
||||
return dir.Metadata[adjustedPath], true
|
||||
}
|
||||
}
|
||||
return FileInfo{}, false
|
||||
}
|
||||
|
||||
// UpdateFileMetadata updates the FileInfo for the specified directory in the index.
|
||||
func (si *Index) UpdateFileMetadata(adjustedPath string, info FileInfo) bool {
|
||||
si.mu.Lock()
|
||||
defer si.mu.Unlock()
|
||||
dir, exists := si.Directories[adjustedPath]
|
||||
if !exists {
|
||||
// Initialize the Metadata map if it is nil
|
||||
if dir.Metadata == nil {
|
||||
dir.Metadata = make(map[string]FileInfo)
|
||||
}
|
||||
si.Directories[adjustedPath] = dir
|
||||
// Release the read lock before calling SetFileMetadata
|
||||
}
|
||||
return si.SetFileMetadata(adjustedPath, info)
|
||||
}
|
||||
|
||||
// SetFileMetadata sets the FileInfo for the specified directory in the index.
|
||||
// internal use only
|
||||
func (si *Index) SetFileMetadata(adjustedPath string, info FileInfo) bool {
|
||||
|
||||
_, exists := si.Directories[adjustedPath]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
info.CacheTime = time.Now()
|
||||
si.Directories[adjustedPath].Metadata[adjustedPath] = info
|
||||
return true
|
||||
}
|
||||
|
||||
// GetMetadataInfo retrieves the FileInfo from the specified directory in the index.
|
||||
func (si *Index) GetMetadataInfo(adjustedPath string) (FileInfo, bool) {
|
||||
si.mu.RLock()
|
||||
dir, exists := si.Directories[adjustedPath]
|
||||
si.mu.RUnlock()
|
||||
if exists {
|
||||
// Initialize the Metadata map if it is nil
|
||||
if dir.Metadata == nil {
|
||||
dir.Metadata = make(map[string]FileInfo)
|
||||
si.SetDirectoryInfo(adjustedPath, dir)
|
||||
}
|
||||
info, metadataExists := dir.Metadata[adjustedPath]
|
||||
return info, metadataExists
|
||||
}
|
||||
return FileInfo{}, false
|
||||
}
|
||||
|
||||
// SetDirectoryInfo sets the directory information in the index.
|
||||
func (si *Index) SetDirectoryInfo(adjustedPath string, dir Directory) {
|
||||
si.mu.Lock()
|
||||
si.Directories[adjustedPath] = dir
|
||||
si.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetDirectoryInfo sets the directory information in the index.
|
||||
func (si *Index) GetDirectoryInfo(adjustedPath string) (Directory, bool) {
|
||||
si.mu.RLock()
|
||||
dir, exists := si.Directories[adjustedPath]
|
||||
si.mu.RUnlock()
|
||||
if exists {
|
||||
return dir, true
|
||||
}
|
||||
return Directory{}, false
|
||||
}
|
||||
|
||||
func (si *Index) RemoveDirectory(path string) {
|
||||
si.mu.Lock()
|
||||
defer si.mu.Unlock()
|
||||
delete(si.Directories, path)
|
||||
}
|
||||
|
||||
func (si *Index) UpdateCount(given string) {
|
||||
si.mu.Lock()
|
||||
defer si.mu.Unlock()
|
||||
if given == "files" {
|
||||
si.NumFiles++
|
||||
} else if given == "dirs" {
|
||||
si.NumDirs++
|
||||
} else {
|
||||
log.Println("could not update unknown type: ", given)
|
||||
}
|
||||
}
|
||||
|
||||
func (si *Index) resetCount() {
|
||||
si.mu.Lock()
|
||||
defer si.mu.Unlock()
|
||||
si.NumDirs = 0
|
||||
si.NumFiles = 0
|
||||
si.inProgress = true
|
||||
}
|
||||
|
||||
func GetIndex(root string) *Index {
|
||||
for _, index := range indexes {
|
||||
if index.Root == root {
|
||||
return index
|
||||
}
|
||||
}
|
||||
if settings.Config.Server.Root != "" {
|
||||
rootPath = settings.Config.Server.Root
|
||||
}
|
||||
newIndex := &Index{
|
||||
Root: rootPath,
|
||||
Directories: make(map[string]Directory), // Initialize the map
|
||||
NumDirs: 0,
|
||||
NumFiles: 0,
|
||||
inProgress: false,
|
||||
}
|
||||
indexesMutex.Lock()
|
||||
indexes = append(indexes, newIndex)
|
||||
indexesMutex.Unlock()
|
||||
return newIndex
|
||||
}
|
||||
|
||||
func (si *Index) UpdateQuickList(files []fs.FileInfo) {
|
||||
si.mu.Lock()
|
||||
defer si.mu.Unlock()
|
||||
si.quickList = []File{}
|
||||
for _, file := range files {
|
||||
newFile := File{
|
||||
Name: file.Name(),
|
||||
IsDir: file.IsDir(),
|
||||
}
|
||||
si.quickList = append(si.quickList, newFile)
|
||||
}
|
||||
}
|
||||
|
||||
func (si *Index) UpdateQuickListForTests(files []File) {
|
||||
si.mu.Lock()
|
||||
defer si.mu.Unlock()
|
||||
si.quickList = []File{}
|
||||
for _, file := range files {
|
||||
newFile := File{
|
||||
Name: file.Name,
|
||||
IsDir: file.IsDir,
|
||||
}
|
||||
si.quickList = append(si.quickList, newFile)
|
||||
}
|
||||
}
|
||||
|
||||
func (si *Index) GetQuickList() []File {
|
||||
si.mu.Lock()
|
||||
defer si.mu.Unlock()
|
||||
newQuickList := si.quickList
|
||||
return newQuickList
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"os"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func isBinary(content []byte) bool {
|
||||
maybeStr := string(content)
|
||||
runeCnt := utf8.RuneCount(content)
|
||||
runeIndex := 0
|
||||
gotRuneErrCnt := 0
|
||||
firstRuneErrIndex := -1
|
||||
|
||||
const (
|
||||
// 8 and below are control chars (e.g. backspace, null, eof, etc)
|
||||
maxControlCharsCode = 8
|
||||
// 0xFFFD(65533) is the "error" Rune or "Unicode replacement character"
|
||||
// see https://golang.org/pkg/unicode/utf8/#pkg-constants
|
||||
unicodeReplacementChar = 0xFFFD
|
||||
)
|
||||
|
||||
for _, b := range maybeStr {
|
||||
if b <= maxControlCharsCode {
|
||||
return true
|
||||
}
|
||||
|
||||
if b == unicodeReplacementChar {
|
||||
// if it is not the last (utf8.UTFMax - x) rune
|
||||
if runeCnt > utf8.UTFMax && runeIndex < runeCnt-utf8.UTFMax {
|
||||
return true
|
||||
}
|
||||
// else it is the last (utf8.UTFMax - x) rune
|
||||
// there maybe Vxxx, VVxx, VVVx, thus, we may got max 3 0xFFFD rune (assume V is the byte we got)
|
||||
// for Chinese, it can only be Vxx, VVx, we may got max 2 0xFFFD rune
|
||||
gotRuneErrCnt++
|
||||
|
||||
// mark the first time
|
||||
if firstRuneErrIndex == -1 {
|
||||
firstRuneErrIndex = runeIndex
|
||||
}
|
||||
}
|
||||
runeIndex++
|
||||
}
|
||||
|
||||
// if last (utf8.UTFMax - x ) rune has the "error" Rune, but not all
|
||||
if firstRuneErrIndex != -1 && gotRuneErrCnt != runeCnt-firstRuneErrIndex {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsNamedPipe(mode os.FileMode) bool {
|
||||
return mode&os.ModeNamedPipe != 0
|
||||
}
|
||||
|
||||
func IsSymlink(mode os.FileMode) bool {
|
||||
return mode&os.ModeSymlink != 0
|
||||
}
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
+30
-32
@@ -1,58 +1,56 @@
|
||||
module github.com/gtsteffaniak/filebrowser
|
||||
|
||||
go 1.21
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/asdine/storm/v3 v3.2.1
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/dsoprea/go-exif/v3 v3.0.1
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
|
||||
github.com/goccy/go-yaml v1.11.0
|
||||
github.com/goccy/go-yaml v1.11.3
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/google/go-cmp v0.5.9
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/maruel/natural v1.1.0
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/marusama/semaphore/v2 v2.5.0
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/shirou/gopsutil/v3 v3.23.8
|
||||
github.com/spf13/afero v1.9.5
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/shirou/gopsutil/v3 v3.24.5
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
|
||||
golang.org/x/crypto v0.12.0
|
||||
golang.org/x/image v0.12.0
|
||||
golang.org/x/text v0.13.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
golang.org/x/crypto v0.24.0
|
||||
golang.org/x/image v0.17.0
|
||||
golang.org/x/text v0.16.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.1 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
|
||||
github.com/fatih/color v1.10.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||
github.com/golang/snappy v0.0.2 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.11.4 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/nwaples/rardecode v1.1.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.2 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/ulikunitz/xz v0.5.9 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.4 // indirect
|
||||
golang.org/x/net v0.10.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.etcd.io/bbolt v1.3.10 // indirect
|
||||
golang.org/x/net v0.26.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
+69
-491
@@ -1,61 +1,13 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.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.44.3/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.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
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=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
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/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
||||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM=
|
||||
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM=
|
||||
github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
github.com/asdine/storm/v3 v3.2.1 h1:I5AqhkPK6nBZ/qJXySdI7ot5BlXSZ7qvDY1zAn5ZJac=
|
||||
github.com/asdine/storm/v3 v3.2.1/go.mod h1:LEpXwGt4pIqrE/XcTvCnZHT5MgZCV6Ub9q7yQzOFWr0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
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/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/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
@@ -80,122 +32,59 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LV
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003160719-7bc88537c05e/go.mod h1:VZ7cB0pTjm1ADBWhJUOHESu4ZYy9JN+ZPqjfiW09EPU=
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje0z+3UQ5YjYiSRRzVdtamFpvBQXKwMglWqw=
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54=
|
||||
github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
|
||||
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
|
||||
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
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/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I=
|
||||
github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U=
|
||||
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 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
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 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/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/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
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/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.11.4 h1:kz40R/YWls3iqT9zX9AHN3WoVsrAWVyui5sxuLqiXqU=
|
||||
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -203,415 +92,104 @@ 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/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ=
|
||||
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
|
||||
github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=
|
||||
github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=
|
||||
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.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
|
||||
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=
|
||||
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
|
||||
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shirou/gopsutil/v3 v3.23.8 h1:xnATPiybo6GgdRoC4YoGnxXZFRc3dqQTGi73oLvvBrE=
|
||||
github.com/shirou/gopsutil/v3 v3.23.8/go.mod h1:7hmCaBn+2ZwaZOr6jmPBZDfawwMGuo1id3C6aM8EDqQ=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
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.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
|
||||
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/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-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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
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/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ=
|
||||
golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
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-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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/image v0.17.0 h1:nTRVVdajgB8zCMZVsViyzhnMKPwYeroEERRC64JuLco=
|
||||
golang.org/x/image v0.17.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
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-20191105084925-a882066a44e0/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-20200320220750-118fecf932d8/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-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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/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-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/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-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/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-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-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.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.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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
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/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-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-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-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
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.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/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
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-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-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-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.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.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/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 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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=
|
||||
Executable
BIN
Binary file not shown.
+16
-43
@@ -2,6 +2,7 @@ package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -12,27 +13,12 @@ import (
|
||||
"github.com/golang-jwt/jwt/v4/request"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
const (
|
||||
TokenExpirationTime = time.Hour * 2
|
||||
)
|
||||
|
||||
type userInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Locale string `json:"locale"`
|
||||
ViewMode string `json:"viewMode"`
|
||||
SingleClick bool `json:"singleClick"`
|
||||
Perm users.Permissions `json:"perm"`
|
||||
Commands []string `json:"commands"`
|
||||
LockPassword bool `json:"lockPassword"`
|
||||
HideDotfiles bool `json:"hideDotfiles"`
|
||||
DateFormat bool `json:"dateFormat"`
|
||||
}
|
||||
|
||||
type authToken struct {
|
||||
User userInfo `json:"user"`
|
||||
User users.User `json:"user"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
@@ -66,7 +52,7 @@ func (e extractor) ExtractToken(r *http.Request) (string, error) {
|
||||
func withUser(fn handleFunc) handleFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
keyFunc := func(token *jwt.Token) (interface{}, error) {
|
||||
return d.settings.Key, nil
|
||||
return d.settings.Auth.Key, nil
|
||||
}
|
||||
|
||||
var tk authToken
|
||||
@@ -123,7 +109,7 @@ type signupBody struct {
|
||||
}
|
||||
|
||||
var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if !d.settings.Signup {
|
||||
if !settings.Config.Auth.Signup {
|
||||
return http.StatusMethodNotAllowed, nil
|
||||
}
|
||||
|
||||
@@ -143,17 +129,9 @@ var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int,
|
||||
|
||||
user := &users.User{
|
||||
Username: info.Username,
|
||||
Password: info.Password,
|
||||
}
|
||||
|
||||
d.settings.UserDefaults.Apply(user)
|
||||
|
||||
pwd, err := users.HashPwd(info.Password)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
user.Password = pwd
|
||||
|
||||
settings.Config.UserDefaults.Apply(user)
|
||||
userHome, err := d.settings.MakeUserDir(user.Username, user.Scope, d.server.Root)
|
||||
if err != nil {
|
||||
log.Printf("create user: failed to mkdir user home dir: [%s]", userHome)
|
||||
@@ -161,7 +139,7 @@ var signupHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int,
|
||||
}
|
||||
user.Scope = userHome
|
||||
log.Printf("new user: %s, home dir: [%s].", user.Username, userHome)
|
||||
|
||||
settings.Config.UserDefaults.Apply(user)
|
||||
err = d.store.Users.Save(user)
|
||||
if err == errors.ErrExist {
|
||||
return http.StatusConflict, err
|
||||
@@ -177,27 +155,22 @@ var renewHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data
|
||||
})
|
||||
|
||||
func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User) (int, error) {
|
||||
duration, err := time.ParseDuration(settings.Config.Auth.TokenExpirationTime)
|
||||
if err != nil {
|
||||
fmt.Println("Error parsing duration:", err)
|
||||
duration = time.Hour * 2
|
||||
}
|
||||
claims := &authToken{
|
||||
User: userInfo{
|
||||
ID: user.ID,
|
||||
Locale: user.Locale,
|
||||
ViewMode: user.ViewMode,
|
||||
SingleClick: user.SingleClick,
|
||||
Perm: user.Perm,
|
||||
LockPassword: user.LockPassword,
|
||||
Commands: user.Commands,
|
||||
HideDotfiles: user.HideDotfiles,
|
||||
DateFormat: user.DateFormat,
|
||||
},
|
||||
User: *user,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpirationTime)),
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(duration)),
|
||||
Issuer: "File Browser",
|
||||
},
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
signed, err := token.SignedString(d.settings.Key)
|
||||
signed, err := token.SignedString(d.settings.Auth.Key)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/tomasen/realip"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/rules"
|
||||
"github.com/gtsteffaniak/filebrowser/runner"
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/storage"
|
||||
@@ -27,9 +26,6 @@ type data struct {
|
||||
|
||||
// Check implements rules.Checker.
|
||||
func (d *data) Check(path string) bool {
|
||||
if d.user.HideDotfiles && rules.MatchHidden(path) {
|
||||
return false
|
||||
}
|
||||
|
||||
allow := true
|
||||
for _, rule := range d.settings.Rules {
|
||||
@@ -37,7 +33,6 @@ func (d *data) Check(path string) bool {
|
||||
allow = rule.Allow
|
||||
}
|
||||
}
|
||||
|
||||
for _, rule := range d.user.Rules {
|
||||
if rule.Matches(path) {
|
||||
allow = rule.Allow
|
||||
|
||||
@@ -69,6 +69,7 @@ func NewHandler(
|
||||
Handler(monkey(previewHandler(imgSvc, fileCache, server.EnableThumbnails, server.ResizePreview), "/api/preview")).Methods("GET")
|
||||
api.PathPrefix("/search").Handler(monkey(searchHandler, "/api/search")).Methods("GET")
|
||||
public := api.PathPrefix("/public").Subrouter()
|
||||
public.Handle("/publicUser", monkey(publicUserGetHandler, "")).Methods("GET")
|
||||
public.PathPrefix("/dl").Handler(monkey(publicDlHandler, "/api/public/dl/")).Methods("GET")
|
||||
public.PathPrefix("/share").Handler(monkey(publicShareHandler, "/api/public/share/")).Methods("GET")
|
||||
return stripPrefix(server.BaseURL, r), nil
|
||||
|
||||
@@ -45,7 +45,7 @@ func previewHandler(imgSvc ImgService, fileCache FileCache, enableThumbnails, re
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
file, err := files.FileInfoFaster(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: "/" + vars["path"],
|
||||
Modify: d.user.Perm.Modify,
|
||||
@@ -53,10 +53,10 @@ func previewHandler(imgSvc ImgService, fileCache FileCache, enableThumbnails, re
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
Checker: d,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
setContentDisposition(w, r, file)
|
||||
|
||||
switch file.Type {
|
||||
@@ -81,7 +81,6 @@ func handleImagePreview(
|
||||
(previewSize == PreviewSizeThumb && !enableThumbnails) {
|
||||
return rawFileHandler(w, r, file)
|
||||
}
|
||||
|
||||
format, err := imgSvc.FormatFromExtension(file.Extension)
|
||||
// Unsupported extensions directly return the raw data
|
||||
if err == img.ErrUnsupportedFormat || format == img.FormatGif {
|
||||
|
||||
+34
-56
@@ -12,32 +12,39 @@ import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/files"
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
"github.com/gtsteffaniak/filebrowser/share"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
var withHashFile = func(fn handleFunc) handleFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
id, ifPath := ifPathWithName(r)
|
||||
id, path := ifPathWithName(r)
|
||||
link, err := d.store.Share.GetByHash(id)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
status, err := authenticateShareRequest(r, link)
|
||||
if status != 0 || err != nil {
|
||||
return status, err
|
||||
if link.Hash != "" {
|
||||
status, err := authenticateShareRequest(r, link)
|
||||
if status != 0 || err != nil {
|
||||
return status, err
|
||||
}
|
||||
}
|
||||
|
||||
user, err := d.store.Users.Get(d.server.Root, link.UserID)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
d.user = &users.PublicUser
|
||||
if path == "/" {
|
||||
path = link.Path
|
||||
} else if strings.HasPrefix("/"+path, link.Path) {
|
||||
path = "/" + path
|
||||
} else {
|
||||
path = link.Path + "/" + path
|
||||
}
|
||||
|
||||
d.user = user
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: link.Path,
|
||||
sharePath := settings.Config.Server.Root + path
|
||||
lastComponent := filepath.Base(sharePath)
|
||||
basePath := filepath.Dir(sharePath)
|
||||
fsPath := afero.NewBasePathFs(afero.NewOsFs(), basePath)
|
||||
file, err := files.FileInfoFaster(files.FileOptions{
|
||||
Fs: fsPath,
|
||||
Path: lastComponent,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: false,
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
@@ -47,65 +54,37 @@ var withHashFile = func(fn handleFunc) handleFunc {
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
// share base path
|
||||
basePath := link.Path
|
||||
|
||||
// file relative path
|
||||
filePath := ""
|
||||
|
||||
if file.IsDir {
|
||||
basePath = filepath.Dir(basePath)
|
||||
filePath = ifPath
|
||||
}
|
||||
|
||||
// set fs root to the shared file/folder
|
||||
d.user.Fs = afero.NewBasePathFs(d.user.Fs, basePath)
|
||||
|
||||
file, err = files.NewFileInfo(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: filePath,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
Checker: d,
|
||||
Token: link.Token,
|
||||
})
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
d.raw = file
|
||||
return fn(w, r, d)
|
||||
}
|
||||
}
|
||||
|
||||
// ref to https://github.com/filebrowser/filebrowser/pull/727
|
||||
// `/api/public/dl/MEEuZK-v/file-name.txt` for old browsers to save file with correct name
|
||||
func ifPathWithName(r *http.Request) (id, filePath string) {
|
||||
pathElements := strings.Split(r.URL.Path, "/")
|
||||
// prevent maliciously constructed parameters like `/api/public/dl/XZzCDnK2_not_exists_hash_name`
|
||||
// len(pathElements) will be 1, and golang will panic `runtime error: index out of range`
|
||||
|
||||
switch len(pathElements) {
|
||||
case 1:
|
||||
return r.URL.Path, "/"
|
||||
default:
|
||||
return pathElements[0], path.Join("/", path.Join(pathElements[1:]...))
|
||||
id = pathElements[0]
|
||||
allButFirst := path.Join(pathElements[1:]...)
|
||||
if len(pathElements) == 1 {
|
||||
allButFirst = "/"
|
||||
}
|
||||
return id, allButFirst
|
||||
}
|
||||
|
||||
var publicShareHandler = withHashFile(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
file := d.raw.(*files.FileInfo)
|
||||
|
||||
file.Path = strings.TrimPrefix(file.Path, settings.Config.Server.Root)
|
||||
if file.IsDir {
|
||||
file.Listing.Sorting = files.Sorting{By: "name", Asc: false}
|
||||
file.Listing.ApplySort()
|
||||
return renderJSON(w, r, file)
|
||||
}
|
||||
|
||||
return renderJSON(w, r, file)
|
||||
})
|
||||
|
||||
var publicUserGetHandler = func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
// Call the actual handler logic here (e.g., renderJSON, etc.)
|
||||
// You may need to replace `fn` with the actual handler logic.
|
||||
return renderJSON(w, r, users.PublicUser)
|
||||
}
|
||||
|
||||
var publicDlHandler = withHashFile(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
file := d.raw.(*files.FileInfo)
|
||||
if !file.IsDir {
|
||||
@@ -138,7 +117,6 @@ func authenticateShareRequest(r *http.Request, l *share.Link) (int, error) {
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestPublicShareHandlerAuthentication(t *testing.T) {
|
||||
expectedStatusCode int
|
||||
}{
|
||||
"Public share, no auth required": {
|
||||
share: &share.Link{Hash: "h", UserID: 1},
|
||||
share: &share.Link{Hash: "h"},
|
||||
req: newHTTPRequest(t),
|
||||
expectedStatusCode: 200,
|
||||
},
|
||||
@@ -82,10 +82,11 @@ func TestPublicShareHandlerAuthentication(t *testing.T) {
|
||||
if err := storage.Share.Save(tc.share); err != nil {
|
||||
t.Fatalf("failed to save share: %v", err)
|
||||
}
|
||||
if err := storage.Users.Save(&users.User{Username: "username", Password: "pw"}); err != nil {
|
||||
t.Fatalf("failed to save user: %v", err)
|
||||
}
|
||||
if err := storage.Settings.Save(&settings.Settings{Key: []byte("key")}); err != nil {
|
||||
if err := storage.Settings.Save(&settings.Settings{
|
||||
Auth: settings.Auth{
|
||||
Key: []byte("key"),
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("failed to save settings: %v", err)
|
||||
}
|
||||
|
||||
@@ -96,7 +97,6 @@ func TestPublicShareHandlerAuthentication(t *testing.T) {
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
handler := handle(handler, "", storage, &settings.Server{})
|
||||
|
||||
handler.ServeHTTP(recorder, tc.req)
|
||||
result := recorder.Result()
|
||||
defer result.Body.Close()
|
||||
|
||||
+1
-3
@@ -81,7 +81,7 @@ var rawHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data)
|
||||
return http.StatusAccepted, nil
|
||||
}
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
file, err := files.FileInfoFaster(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
@@ -92,7 +92,6 @@ var rawHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
if files.IsNamedPipe(file.Mode) {
|
||||
setContentDisposition(w, r, file)
|
||||
return 0, nil
|
||||
@@ -109,7 +108,6 @@ func addFile(ar archiver.Writer, d *data, path, commonPath string) error {
|
||||
if !d.Check(path) {
|
||||
return nil
|
||||
}
|
||||
|
||||
info, err := d.user.Fs.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+10
-14
@@ -20,25 +20,22 @@ import (
|
||||
)
|
||||
|
||||
var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
file, err := files.FileInfoFaster(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
Checker: d,
|
||||
Content: true,
|
||||
Content: r.URL.Query().Get("content") == "true",
|
||||
})
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
if file.IsDir {
|
||||
file.Listing.Sorting = d.user.Sorting
|
||||
file.Listing.ApplySort()
|
||||
return renderJSON(w, r, file)
|
||||
}
|
||||
|
||||
if checksum := r.URL.Query().Get("checksum"); checksum != "" {
|
||||
err := file.Checksum(checksum)
|
||||
if err == errors.ErrInvalidOption {
|
||||
@@ -46,9 +43,6 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
|
||||
} else if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
// do not waste bandwidth if we just want the checksum
|
||||
file.Content = ""
|
||||
}
|
||||
|
||||
return renderJSON(w, r, file)
|
||||
@@ -60,7 +54,7 @@ func resourceDeleteHandler(fileCache FileCache) handleFunc {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
file, err := files.FileInfoFaster(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
@@ -102,7 +96,7 @@ func resourcePostHandler(fileCache FileCache) handleFunc {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
file, err := files.FileInfoFaster(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
@@ -173,7 +167,6 @@ var resourcePutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
|
||||
w.Header().Set("ETag", etag)
|
||||
return nil
|
||||
}, "save", r.URL.Path, "", d.user)
|
||||
|
||||
return errToStatus(err), err
|
||||
})
|
||||
|
||||
@@ -278,6 +271,10 @@ func writeFile(fs afero.Fs, dst string, in io.Reader) (os.FileInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//files.RefreshFileInfo(files.FileOptions{
|
||||
// Fs: info,
|
||||
//})
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -308,7 +305,7 @@ func patchAction(ctx context.Context, action, src, dst string, d *data, fileCach
|
||||
src = path.Clean("/" + src)
|
||||
dst = path.Clean("/" + dst)
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
file, err := files.FileInfoFaster(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: src,
|
||||
Modify: d.user.Perm.Modify,
|
||||
@@ -338,14 +335,13 @@ type DiskUsageResponse struct {
|
||||
}
|
||||
|
||||
var diskUsage = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
file, err := files.FileInfoFaster(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: false,
|
||||
ReadHeader: false,
|
||||
Checker: d,
|
||||
Content: false,
|
||||
})
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
|
||||
@@ -2,18 +2,22 @@ package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/search"
|
||||
"github.com/gtsteffaniak/filebrowser/files"
|
||||
"github.com/gtsteffaniak/filebrowser/settings"
|
||||
)
|
||||
|
||||
var searchHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
response := []map[string]interface{}{}
|
||||
query := r.URL.Query().Get("query")
|
||||
|
||||
// Retrieve the User-Agent and X-Auth headers from the request
|
||||
sessionId := r.Header.Get("SessionId")
|
||||
indexInfo, fileTypes := search.SearchAllIndexes(query, r.URL.Path, sessionId)
|
||||
for _, path := range indexInfo {
|
||||
userScope := r.Header.Get("UserScope")
|
||||
index := files.GetIndex(settings.Config.Server.Root)
|
||||
combinedScope := strings.TrimPrefix(userScope+r.URL.Path, ".")
|
||||
results, fileTypes := index.Search(query, combinedScope, sessionId)
|
||||
for _, path := range results {
|
||||
responseObj := map[string]interface{}{
|
||||
"path": path,
|
||||
"dir": true,
|
||||
|
||||
@@ -21,9 +21,9 @@ type settingsData struct {
|
||||
|
||||
var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
data := &settingsData{
|
||||
Signup: d.settings.Signup,
|
||||
CreateUserDir: d.settings.CreateUserDir,
|
||||
UserHomeBasePath: d.settings.UserHomeBasePath,
|
||||
Signup: settings.Config.Auth.Signup,
|
||||
CreateUserDir: settings.Config.Server.CreateUserDir,
|
||||
UserHomeBasePath: settings.Config.Server.UserHomeBasePath,
|
||||
Defaults: d.settings.UserDefaults,
|
||||
Rules: d.settings.Rules,
|
||||
Frontend: d.settings.Frontend,
|
||||
@@ -41,9 +41,8 @@ var settingsPutHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
d.settings.Signup = req.Signup
|
||||
d.settings.CreateUserDir = req.CreateUserDir
|
||||
d.settings.UserHomeBasePath = req.UserHomeBasePath
|
||||
d.settings.Server.CreateUserDir = req.CreateUserDir
|
||||
d.settings.Server.UserHomeBasePath = req.UserHomeBasePath
|
||||
d.settings.UserDefaults = req.Defaults
|
||||
d.settings.Rules = req.Rules
|
||||
d.settings.Frontend = req.Frontend
|
||||
|
||||
+11
-7
@@ -22,7 +22,6 @@ func withPermShare(fn handleFunc) handleFunc {
|
||||
if !d.user.Perm.Share {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
return fn(w, r, d)
|
||||
})
|
||||
}
|
||||
@@ -51,7 +50,6 @@ var shareListHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
return s[i].Expire < s[j].Expire
|
||||
})
|
||||
|
||||
return renderJSON(w, r, s)
|
||||
})
|
||||
|
||||
@@ -77,10 +75,15 @@ var shareDeleteHandler = withPermShare(func(w http.ResponseWriter, r *http.Reque
|
||||
}
|
||||
|
||||
err := d.store.Share.Delete(hash)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
return errToStatus(err), err
|
||||
})
|
||||
|
||||
var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
|
||||
var s *share.Link
|
||||
var body share.CreateBody
|
||||
if r.Body != nil {
|
||||
@@ -126,22 +129,22 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
|
||||
stringHash := ""
|
||||
var token string
|
||||
if len(hash) > 0 {
|
||||
tokenBuffer := make([]byte, 96) //nolint:gomnd
|
||||
tokenBuffer := make([]byte, 24) //nolint:gomnd
|
||||
if _, err := rand.Read(tokenBuffer); err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
token = base64.URLEncoding.EncodeToString(tokenBuffer)
|
||||
stringHash = string(hash)
|
||||
}
|
||||
|
||||
s = &share.Link{
|
||||
Path: r.URL.Path,
|
||||
Path: strings.TrimSuffix(r.URL.Path, "/"),
|
||||
Hash: str,
|
||||
Expire: expire,
|
||||
UserID: d.user.ID,
|
||||
PasswordHash: string(hash),
|
||||
PasswordHash: stringHash,
|
||||
Token: token,
|
||||
}
|
||||
|
||||
@@ -153,6 +156,7 @@ var sharePostHandler = withPermShare(func(w http.ResponseWriter, r *http.Request
|
||||
})
|
||||
|
||||
func getSharePasswordHash(body share.CreateBody) (data []byte, statuscode int, err error) {
|
||||
|
||||
if body.Password == "" {
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
||||
@@ -30,17 +30,17 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
|
||||
"Name": d.settings.Frontend.Name,
|
||||
"DisableExternal": d.settings.Frontend.DisableExternal,
|
||||
"DisableUsedPercentage": d.settings.Frontend.DisableUsedPercentage,
|
||||
"darkMode": settings.Config.UserDefaults.DarkMode,
|
||||
"Color": d.settings.Frontend.Color,
|
||||
"BaseURL": d.server.BaseURL,
|
||||
"Version": version.Version,
|
||||
"StaticURL": path.Join(d.server.BaseURL, "/static"),
|
||||
"Signup": d.settings.Signup,
|
||||
"Signup": settings.Config.Auth.Signup,
|
||||
"NoAuth": d.settings.Auth.Method == "noauth",
|
||||
"AuthMethod": d.settings.Auth.Method,
|
||||
"LoginPage": auther.LoginPage(),
|
||||
"CSS": false,
|
||||
"ReCaptcha": false,
|
||||
"Theme": d.settings.Frontend.Theme,
|
||||
"EnableThumbs": d.server.EnableThumbnails,
|
||||
"ResizePreview": d.server.ResizePreview,
|
||||
"EnableExec": d.server.EnableExec,
|
||||
@@ -64,9 +64,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
auther := raw.(*auth.JSONAuth)
|
||||
|
||||
if auther.ReCaptcha != nil {
|
||||
data["ReCaptcha"] = auther.ReCaptcha.Key != "" && auther.ReCaptcha.Secret != ""
|
||||
data["ReCaptchaHost"] = auther.ReCaptcha.Host
|
||||
@@ -104,7 +102,7 @@ func getStaticHandlers(store *storage.Storage, server *settings.Server, assetsFs
|
||||
}
|
||||
|
||||
w.Header().Set("x-xss-protection", "1; mode=block")
|
||||
return handleWithStaticData(w, r, d, assetsFs, "index.html", "text/html; charset=utf-8")
|
||||
return handleWithStaticData(w, r, d, assetsFs, "public/index.html", "text/html; charset=utf-8")
|
||||
}, "", store, server)
|
||||
|
||||
static = handle(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
|
||||
@@ -19,6 +19,11 @@ var (
|
||||
NonModifiableFieldsForNonAdmin = []string{"Username", "Scope", "LockPassword", "Perm", "Commands", "Rules"}
|
||||
)
|
||||
|
||||
// SortingSettings represents the sorting settings.
|
||||
type Sorting struct {
|
||||
By string `json:"by"`
|
||||
Asc bool `json:"asc"`
|
||||
}
|
||||
type modifyUserRequest struct {
|
||||
modifyRequest
|
||||
Data *users.User `json:"data"`
|
||||
@@ -57,7 +62,6 @@ func withSelfOrAdmin(fn handleFunc) handleFunc {
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
if d.user.ID != id && !d.user.Perm.Admin {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
@@ -124,11 +128,6 @@ var userPostHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *
|
||||
return http.StatusBadRequest, errors.ErrEmptyPassword
|
||||
}
|
||||
|
||||
req.Data.Password, err = users.HashPwd(req.Data.Password)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
userHome, err := d.settings.MakeUserDir(req.Data.Username, req.Data.Scope, d.server.Root)
|
||||
if err != nil {
|
||||
log.Printf("create user: failed to mkdir user home dir: [%s]", userHome)
|
||||
@@ -184,7 +183,6 @@ var userPutHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
|
||||
if !d.user.Perm.Admin && d.user.LockPassword {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
req.Data.Password, err = users.HashPwd(req.Data.Password)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
func renderJSON(w http.ResponseWriter, _ *http.Request, data interface{}) (int, error) {
|
||||
marsh, err := json.Marshal(data)
|
||||
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type Rule struct {
|
||||
Allow bool `json:"allow"`
|
||||
Path string `json:"path"`
|
||||
Regexp *Regexp `json:"regexp"`
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
// MatchHidden matches paths with a basename
|
||||
|
||||
@@ -101,18 +101,18 @@ func (r *Runner) exec(raw, evt, path, dst string, user *users.User) error {
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if !blocking {
|
||||
log.Printf("[INFO] Nonblocking Command: \"%s\"", strings.Join(command, " "))
|
||||
log.Printf("Nonblocking Command: \"%s\"", strings.Join(command, " "))
|
||||
defer func() {
|
||||
go func() {
|
||||
err := cmd.Wait()
|
||||
if err != nil {
|
||||
log.Printf("[INFO] Nonblocking Command \"%s\" failed: %s", strings.Join(command, " "), err)
|
||||
log.Printf("Nonblocking Command \"%s\" failed: %s", strings.Join(command, " "), err)
|
||||
}
|
||||
}()
|
||||
}()
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
log.Printf("[INFO] Blocking Command: \"%s\"", strings.Join(command, " "))
|
||||
log.Printf("Blocking Command: \"%s\"", strings.Join(command, " "))
|
||||
return cmd.Run()
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"mime"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
sessionInProgress sync.Map // Track session with requests in progress
|
||||
rootPath string = "/srv"
|
||||
indexes map[string][]string
|
||||
mutex sync.RWMutex
|
||||
lastIndexed time.Time
|
||||
)
|
||||
|
||||
func InitializeIndex(intervalMinutes uint32) {
|
||||
// Initialize the indexes map
|
||||
indexes = make(map[string][]string)
|
||||
indexes["dirs"] = []string{}
|
||||
indexes["files"] = []string{}
|
||||
var numFiles, numDirs int
|
||||
log.Println("Indexing files...")
|
||||
lastIndexedStart := time.Now()
|
||||
// Call the function to index files and directories
|
||||
totalNumFiles, totalNumDirs, err := indexFiles(rootPath, &numFiles, &numDirs)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
lastIndexed = lastIndexedStart
|
||||
go indexingScheduler(intervalMinutes)
|
||||
log.Println("Successfully indexed files.")
|
||||
log.Println("Files found :", totalNumFiles)
|
||||
log.Println("Directories found :", totalNumDirs)
|
||||
}
|
||||
|
||||
func indexingScheduler(intervalMinutes uint32) {
|
||||
log.Printf("Indexing scheduler will run every %v minutes", intervalMinutes)
|
||||
for {
|
||||
time.Sleep(time.Duration(intervalMinutes) * time.Minute)
|
||||
var numFiles, numDirs int
|
||||
lastIndexedStart := time.Now()
|
||||
totalNumFiles, totalNumDirs, err := indexFiles(rootPath, &numFiles, &numDirs)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
lastIndexed = lastIndexedStart
|
||||
if totalNumFiles+totalNumDirs > 0 {
|
||||
log.Println("re-indexing found changes and updated the index.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define a function to recursively index files and directories
|
||||
func indexFiles(path string, numFiles *int, numDirs *int) (int, int, error) {
|
||||
// Check if the current directory has been modified since last indexing
|
||||
dir, err := os.Open(path)
|
||||
if err != nil {
|
||||
// directory must have been deleted, remove from index
|
||||
delete(indexes, path)
|
||||
}
|
||||
defer dir.Close()
|
||||
dirInfo, err := dir.Stat()
|
||||
if err != nil {
|
||||
return *numFiles, *numDirs, err
|
||||
}
|
||||
// Compare the last modified time of the directory with the last indexed time
|
||||
if dirInfo.ModTime().Before(lastIndexed) {
|
||||
return *numFiles, *numDirs, nil
|
||||
}
|
||||
// Read the directory contents
|
||||
files, err := dir.Readdir(-1)
|
||||
if err != nil {
|
||||
return *numFiles, *numDirs, err
|
||||
}
|
||||
// Iterate over the files and directories
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
*numDirs++
|
||||
addToIndex(path, file.Name(), true)
|
||||
indexFiles(path+"/"+file.Name(), numFiles, numDirs) // recursive
|
||||
} else {
|
||||
*numFiles++
|
||||
addToIndex(path, file.Name(), false)
|
||||
}
|
||||
}
|
||||
return *numFiles, *numDirs, nil
|
||||
}
|
||||
|
||||
func addToIndex(path string, fileName string, isDir bool) {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
path = strings.TrimPrefix(path, rootPath+"/")
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
adjustedPath := path + "/" + fileName
|
||||
if path == rootPath {
|
||||
adjustedPath = fileName
|
||||
}
|
||||
if isDir {
|
||||
indexes["dirs"] = append(indexes["dirs"], adjustedPath)
|
||||
} else {
|
||||
indexes["files"] = append(indexes["files"], adjustedPath)
|
||||
}
|
||||
}
|
||||
|
||||
func SearchAllIndexes(search string, scope string, sourceSession string) ([]string, map[string]map[string]bool) {
|
||||
runningHash := generateRandomHash(4)
|
||||
sessionInProgress.Store(sourceSession, runningHash) // Store the value in the sync.Map
|
||||
|
||||
searchOptions := ParseSearch(search)
|
||||
mutex.RLock()
|
||||
defer mutex.RUnlock()
|
||||
fileListTypes := make(map[string]map[string]bool)
|
||||
var matching []string
|
||||
maximum := 100
|
||||
|
||||
for _, searchTerm := range searchOptions.Terms {
|
||||
if searchTerm == "" {
|
||||
continue
|
||||
}
|
||||
// Iterate over the indexes
|
||||
for _, i := range []string{"dirs", "files"} {
|
||||
isdir := i == "dirs"
|
||||
count := 0
|
||||
for _, path := range indexes[i] {
|
||||
value, found := sessionInProgress.Load(sourceSession)
|
||||
if !found || value != runningHash {
|
||||
return []string{}, map[string]map[string]bool{}
|
||||
}
|
||||
if count > maximum {
|
||||
break
|
||||
}
|
||||
pathName := scopedPathNameFilter(path, scope)
|
||||
if pathName == "" {
|
||||
continue
|
||||
}
|
||||
matches, fileType := containsSearchTerm(path, searchTerm, *searchOptions, isdir)
|
||||
if !matches {
|
||||
continue
|
||||
}
|
||||
if isdir {
|
||||
pathName = pathName + "/"
|
||||
}
|
||||
matching = append(matching, pathName)
|
||||
fileListTypes[pathName] = fileType
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort the strings based on the number of elements after splitting by "/"
|
||||
sort.Slice(matching, func(i, j int) bool {
|
||||
parts1 := strings.Split(matching[i], "/")
|
||||
parts2 := strings.Split(matching[j], "/")
|
||||
return len(parts1) < len(parts2)
|
||||
})
|
||||
return matching, fileListTypes
|
||||
}
|
||||
|
||||
func scopedPathNameFilter(pathName string, scope string) string {
|
||||
scope = strings.TrimPrefix(scope, "/")
|
||||
if strings.HasPrefix(pathName, scope) {
|
||||
pathName = strings.TrimPrefix(pathName, scope)
|
||||
} else {
|
||||
pathName = ""
|
||||
}
|
||||
return pathName
|
||||
}
|
||||
|
||||
func containsSearchTerm(pathName string, searchTerm string, options SearchOptions, isDir bool) (bool, map[string]bool) {
|
||||
conditions := options.Conditions
|
||||
path := getLastPathComponent(pathName)
|
||||
// Convert to lowercase once
|
||||
lowerSearchTerm := searchTerm
|
||||
if !conditions["exact"] {
|
||||
path = strings.ToLower(path)
|
||||
lowerSearchTerm = strings.ToLower(searchTerm)
|
||||
}
|
||||
if strings.Contains(path, lowerSearchTerm) {
|
||||
// Reuse the fileTypes map and clear its values
|
||||
fileTypes := map[string]bool{
|
||||
"audio": false,
|
||||
"image": false,
|
||||
"video": false,
|
||||
"doc": false,
|
||||
"archive": false,
|
||||
"dir": false,
|
||||
}
|
||||
// Calculate fileSize only if needed
|
||||
var fileSize int64
|
||||
if conditions["larger"] || conditions["smaller"] {
|
||||
fileSize = getFileSize(pathName)
|
||||
}
|
||||
matchesAllConditions := true
|
||||
extension := filepath.Ext(path)
|
||||
mimetype := mime.TypeByExtension(extension)
|
||||
fileTypes["audio"] = strings.HasPrefix(mimetype, "audio")
|
||||
fileTypes["image"] = strings.HasPrefix(mimetype, "image")
|
||||
fileTypes["video"] = strings.HasPrefix(mimetype, "video")
|
||||
fileTypes["doc"] = isDoc(extension)
|
||||
fileTypes["archive"] = isArchive(extension)
|
||||
fileTypes["dir"] = isDir
|
||||
for t, v := range conditions {
|
||||
if t == "exact" {
|
||||
continue
|
||||
}
|
||||
var matchesCondition bool
|
||||
switch t {
|
||||
case "larger":
|
||||
matchesCondition = fileSize > int64(options.LargerThan)*1000000
|
||||
case "smaller":
|
||||
matchesCondition = fileSize < int64(options.SmallerThan)*1000000
|
||||
default:
|
||||
matchesCondition = v == fileTypes[t]
|
||||
}
|
||||
if !matchesCondition {
|
||||
matchesAllConditions = false
|
||||
}
|
||||
}
|
||||
return matchesAllConditions, fileTypes
|
||||
}
|
||||
// Clear variables and return
|
||||
return false, map[string]bool{}
|
||||
}
|
||||
|
||||
func isDoc(extension string) bool {
|
||||
for _, typefile := range documentTypes {
|
||||
if extension == typefile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getFileSize(filepath string) int64 {
|
||||
fileInfo, err := os.Stat(rootPath + "/" + filepath)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return fileInfo.Size()
|
||||
}
|
||||
|
||||
func isArchive(extension string) bool {
|
||||
for _, typefile := range compressedFile {
|
||||
if extension == typefile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getLastPathComponent(path string) string {
|
||||
// Use filepath.Base to extract the last component of the path
|
||||
return filepath.Base(path)
|
||||
}
|
||||
|
||||
func generateRandomHash(length int) string {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||
rand.Seed(rand.Int63()) // Automatically seeded based on current time
|
||||
result := make([]byte, length)
|
||||
for i := range result {
|
||||
result[i] = charset[rand.Intn(len(charset))]
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func stringExistsInArray(target string, strings []string) bool {
|
||||
for _, s := range strings {
|
||||
if s == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// loop over test files and compare output
|
||||
func TestParseSearch(t *testing.T) {
|
||||
value := ParseSearch("my test search")
|
||||
want := &SearchOptions{
|
||||
Conditions: map[string]bool{
|
||||
"exact": false,
|
||||
},
|
||||
Terms: []string{"my test search"},
|
||||
}
|
||||
if !reflect.DeepEqual(value, want) {
|
||||
t.Fatalf("\n got: %+v\n want: %+v", value, want)
|
||||
}
|
||||
value = ParseSearch("case:exact my|test|search")
|
||||
want = &SearchOptions{
|
||||
Conditions: map[string]bool{
|
||||
"exact": true,
|
||||
},
|
||||
Terms: []string{"my", "test", "search"},
|
||||
}
|
||||
if !reflect.DeepEqual(value, want) {
|
||||
t.Fatalf("\n got: %+v\n want: %+v", value, want)
|
||||
}
|
||||
value = ParseSearch("type:largerThan=100 type:smallerThan=1000 test")
|
||||
want = &SearchOptions{
|
||||
Conditions: map[string]bool{
|
||||
"exact": false,
|
||||
"larger": true,
|
||||
},
|
||||
Terms: []string{"test"},
|
||||
LargerThan: 100,
|
||||
SmallerThan: 1000,
|
||||
}
|
||||
if !reflect.DeepEqual(value, want) {
|
||||
t.Fatalf("\n got: %+v\n want: %+v", value, want)
|
||||
}
|
||||
value = ParseSearch("type:audio thisfile")
|
||||
want = &SearchOptions{
|
||||
Conditions: map[string]bool{
|
||||
"exact": false,
|
||||
"audio": true,
|
||||
},
|
||||
Terms: []string{"thisfile"},
|
||||
}
|
||||
if !reflect.DeepEqual(value, want) {
|
||||
t.Fatalf("\n got: %+v\n want: %+v", value, want)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSearchAllIndexes(b *testing.B) {
|
||||
indexes = make(map[string][]string)
|
||||
|
||||
// Create mock data
|
||||
createMockData(50, 3) // 1000 dirs, 3 files per dir
|
||||
|
||||
// Generate 100 random search terms
|
||||
searchTerms := generateRandomSearchTerms(100)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Execute the SearchAllIndexes function
|
||||
for _, term := range searchTerms {
|
||||
SearchAllIndexes(term, "/", "test")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFillIndex(b *testing.B) {
|
||||
indexes = make(map[string][]string)
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
createMockData(50, 3) // 1000 dirs, 3 files per dir
|
||||
}
|
||||
}
|
||||
|
||||
func createMockData(numDirs, numFilesPerDir int) {
|
||||
for i := 0; i < numDirs; i++ {
|
||||
dirName := generateRandomPath(rand.Intn(3) + 1)
|
||||
addToIndex("/", dirName, true)
|
||||
for j := 0; j < numFilesPerDir; j++ {
|
||||
fileName := "file-" + getRandomTerm() + getRandomExtension()
|
||||
addToIndex("/"+dirName, fileName, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateRandomPath(levels int) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
dirName := "srv"
|
||||
for i := 0; i < levels; i++ {
|
||||
dirName += "/" + getRandomTerm()
|
||||
}
|
||||
return dirName
|
||||
}
|
||||
|
||||
func getRandomTerm() string {
|
||||
wordbank := []string{
|
||||
"hi", "test", "other", "name",
|
||||
"cool", "things", "more", "items",
|
||||
}
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
index := rand.Intn(len(wordbank))
|
||||
return wordbank[index]
|
||||
}
|
||||
|
||||
func getRandomExtension() string {
|
||||
wordbank := []string{
|
||||
".txt", ".mp3", ".mov", ".doc",
|
||||
".mp4", ".bak", ".zip", ".jpg",
|
||||
}
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
index := rand.Intn(len(wordbank))
|
||||
return wordbank[index]
|
||||
}
|
||||
|
||||
func generateRandomSearchTerms(numTerms int) []string {
|
||||
// Generate random search terms
|
||||
searchTerms := make([]string, numTerms)
|
||||
for i := 0; i < numTerms; i++ {
|
||||
searchTerms[i] = getRandomTerm()
|
||||
}
|
||||
return searchTerms
|
||||
}
|
||||
|
||||
// JSONBytesEqual compares the JSON in two byte slices.
|
||||
func JSONBytesEqual(a, b []byte) (bool, error) {
|
||||
var j, j2 interface{}
|
||||
if err := json.Unmarshal(a, &j); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := json.Unmarshal(b, &j2); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return reflect.DeepEqual(j2, j), nil
|
||||
}
|
||||
|
||||
func passedFunc(t *testing.T) {
|
||||
t.Logf("%s passed!", t.Name())
|
||||
}
|
||||
|
||||
func formatDuration(duration time.Duration) string {
|
||||
if duration >= time.Second {
|
||||
return fmt.Sprintf("%.2f seconds", duration.Seconds())
|
||||
} else if duration >= time.Millisecond {
|
||||
return fmt.Sprintf("%.2f ms", float64(duration.Milliseconds()))
|
||||
}
|
||||
return fmt.Sprintf("%.2f ns", float64(duration.Nanoseconds()))
|
||||
}
|
||||
|
||||
func formatMemory(bytes int64) string {
|
||||
sizes := []string{"B", "KB", "MB", "GB", "TB"}
|
||||
i := 0
|
||||
for bytes >= 1024 && i < len(sizes)-1 {
|
||||
bytes /= 1024
|
||||
i++
|
||||
}
|
||||
return fmt.Sprintf("%d %s", bytes, sizes[i])
|
||||
}
|
||||
+43
-20
@@ -3,22 +3,23 @@ package settings
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
var GlobalConfiguration Settings
|
||||
var Config Settings
|
||||
|
||||
func Initialize(configFile string) {
|
||||
yamlData := loadConfigFile(configFile)
|
||||
GlobalConfiguration = setDefaults()
|
||||
err := yaml.Unmarshal(yamlData, &GlobalConfiguration)
|
||||
Config = setDefaults()
|
||||
err := yaml.Unmarshal(yamlData, &Config)
|
||||
if err != nil {
|
||||
log.Fatalf("Error unmarshaling YAML data: %v", err)
|
||||
}
|
||||
GlobalConfiguration.UserDefaults.Perm = GlobalConfiguration.UserDefaults.Permissions
|
||||
GlobalConfiguration.Server.Root = "/srv" // hardcoded for now. TODO allow changing
|
||||
Config.UserDefaults.Perm = Config.UserDefaults.Permissions
|
||||
Config.Server.Root = strings.TrimSuffix(Config.Server.Root, "/")
|
||||
}
|
||||
|
||||
func loadConfigFile(configFile string) []byte {
|
||||
@@ -26,7 +27,7 @@ func loadConfigFile(configFile string) []byte {
|
||||
yamlFile, err := os.Open(configFile)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: opening config file\n %v\n WARNING: Using default config only\n If this was a mistake, please make sure the file exists and is accessible by the filebrowser binary.\n\n", err)
|
||||
setDefaults()
|
||||
Config = setDefaults()
|
||||
return []byte{}
|
||||
}
|
||||
defer yamlFile.Close()
|
||||
@@ -46,38 +47,60 @@ func loadConfigFile(configFile string) []byte {
|
||||
|
||||
func setDefaults() Settings {
|
||||
return Settings{
|
||||
Signup: true,
|
||||
AdminUsername: "admin",
|
||||
AdminPassword: "admin",
|
||||
Server: Server{
|
||||
EnableThumbnails: true,
|
||||
ResizePreview: false,
|
||||
EnableExec: false,
|
||||
IndexingInterval: 5,
|
||||
Port: 8080,
|
||||
Port: 80,
|
||||
NumImageProcessors: 4,
|
||||
BaseURL: "",
|
||||
Database: "database.db",
|
||||
Log: "stdout",
|
||||
Root: "/srv",
|
||||
Indexing: true,
|
||||
},
|
||||
Auth: Auth{
|
||||
Method: "password",
|
||||
TokenExpirationTime: "2h",
|
||||
AdminUsername: "admin",
|
||||
AdminPassword: "admin",
|
||||
Method: "password",
|
||||
Signup: false,
|
||||
Recaptcha: Recaptcha{
|
||||
Host: "",
|
||||
},
|
||||
},
|
||||
UserDefaults: UserDefaults{
|
||||
Scope: ".",
|
||||
LockPassword: false,
|
||||
HideDotfiles: true,
|
||||
Scope: ".",
|
||||
LockPassword: false,
|
||||
HideDotfiles: true,
|
||||
DarkMode: false,
|
||||
DisableSettings: false,
|
||||
Locale: "en",
|
||||
Permissions: users.Permissions{
|
||||
Create: true,
|
||||
Rename: true,
|
||||
Modify: true,
|
||||
Delete: true,
|
||||
Share: true,
|
||||
Download: true,
|
||||
Create: false,
|
||||
Rename: false,
|
||||
Modify: false,
|
||||
Delete: false,
|
||||
Share: false,
|
||||
Download: false,
|
||||
Admin: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Apply applies the default options to a user.
|
||||
func (d *UserDefaults) Apply(u *users.User) {
|
||||
u.DisableSettings = d.DisableSettings
|
||||
u.DarkMode = d.DarkMode
|
||||
u.Scope = d.Scope
|
||||
u.Locale = d.Locale
|
||||
u.ViewMode = d.ViewMode
|
||||
u.SingleClick = d.SingleClick
|
||||
u.Perm = d.Perm
|
||||
u.Sorting = d.Sorting
|
||||
u.Commands = d.Commands
|
||||
u.HideDotfiles = d.HideDotfiles
|
||||
u.DateFormat = d.DateFormat
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInitialize(t *testing.T) {
|
||||
type args struct {
|
||||
configFile string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
Initialize(tt.args.configFile)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_loadConfigFile(t *testing.T) {
|
||||
type args struct {
|
||||
configFile string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []byte
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := loadConfigFile(tt.args.configFile); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("loadConfigFile() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_setDefaults(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want Settings
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := setDefaults(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("setDefaults() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,13 @@ var (
|
||||
// MakeUserDir makes the user directory according to settings.
|
||||
func (s *Settings) MakeUserDir(username, userScope, serverRoot string) (string, error) {
|
||||
userScope = strings.TrimSpace(userScope)
|
||||
if userScope == "" && s.CreateUserDir {
|
||||
if userScope == "" && s.Server.CreateUserDir {
|
||||
username = cleanUsername(username)
|
||||
if username == "" || username == "-" || username == "." {
|
||||
log.Printf("create user: invalid user for home dir creation: [%s]", username)
|
||||
return "", errors.New("invalid user for home dir creation")
|
||||
}
|
||||
userScope = path.Join(s.UserHomeBasePath, username)
|
||||
userScope = path.Join(s.Server.UserHomeBasePath, username)
|
||||
}
|
||||
|
||||
userScope = path.Join("/", userScope)
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/rules"
|
||||
)
|
||||
|
||||
func TestSettings_MakeUserDir(t *testing.T) {
|
||||
type fields struct {
|
||||
Signup bool
|
||||
CreateUserDir bool
|
||||
UserHomeBasePath string
|
||||
Commands map[string][]string
|
||||
Shell []string
|
||||
AdminUsername string
|
||||
AdminPassword string
|
||||
Rules []rules.Rule
|
||||
Server Server
|
||||
Auth Auth
|
||||
Frontend Frontend
|
||||
Users []UserDefaults
|
||||
UserDefaults UserDefaults
|
||||
}
|
||||
type args struct {
|
||||
username string
|
||||
userScope string
|
||||
serverRoot string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &Settings{
|
||||
Commands: tt.fields.Commands,
|
||||
Shell: tt.fields.Shell,
|
||||
Rules: tt.fields.Rules,
|
||||
Server: tt.fields.Server,
|
||||
Auth: tt.fields.Auth,
|
||||
Frontend: tt.fields.Frontend,
|
||||
Users: tt.fields.Users,
|
||||
UserDefaults: tt.fields.UserDefaults,
|
||||
}
|
||||
got, err := s.MakeUserDir(tt.args.username, tt.args.userScope, tt.args.serverRoot)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Settings.MakeUserDir() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("Settings.MakeUserDir() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_cleanUsername(t *testing.T) {
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := cleanUsername(tt.args.s); got != tt.want {
|
||||
t.Errorf("cleanUsername() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -12,14 +12,14 @@ func TestConfigLoadChanged(t *testing.T) {
|
||||
yamlData := loadConfigFile("./testingConfig.yaml")
|
||||
// Marshal the YAML data to a more human-readable format
|
||||
newConfig := setDefaults()
|
||||
GlobalConfiguration := setDefaults()
|
||||
Config := setDefaults()
|
||||
|
||||
err := yaml.Unmarshal(yamlData, &newConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Error unmarshaling YAML data: %v", err)
|
||||
}
|
||||
// Use go-cmp to compare the two structs
|
||||
if diff := cmp.Diff(newConfig, GlobalConfiguration); diff == "" {
|
||||
if diff := cmp.Diff(newConfig, Config); diff == "" {
|
||||
t.Errorf("No change when there should have been (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
@@ -28,17 +28,27 @@ func TestConfigLoadSpecificValues(t *testing.T) {
|
||||
yamlData := loadConfigFile("./testingConfig.yaml")
|
||||
// Marshal the YAML data to a more human-readable format
|
||||
newConfig := setDefaults()
|
||||
GlobalConfiguration := setDefaults()
|
||||
Config := setDefaults()
|
||||
|
||||
err := yaml.Unmarshal(yamlData, &newConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Error unmarshaling YAML data: %v", err)
|
||||
}
|
||||
|
||||
if GlobalConfiguration.Auth.Method == newConfig.Auth.Method {
|
||||
log.Fatalf("Differences should have been found, but were not on Auth method")
|
||||
testCases := []struct {
|
||||
fieldName string
|
||||
globalVal interface{}
|
||||
newVal interface{}
|
||||
}{
|
||||
{"Auth.Method", Config.Auth.Method, newConfig.Auth.Method},
|
||||
{"Auth.Method", Config.Auth.Method, newConfig.Auth.Method},
|
||||
{"Frontend.disableExternal", Config.Frontend.DisableExternal, newConfig.Frontend.DisableExternal},
|
||||
{"UserDefaults.HideDotfiles", Config.UserDefaults.HideDotfiles, newConfig.UserDefaults.HideDotfiles},
|
||||
{"Server.Database", Config.Server.Database, newConfig.Server.Database},
|
||||
}
|
||||
if GlobalConfiguration.UserDefaults.HideDotfiles == newConfig.UserDefaults.HideDotfiles {
|
||||
log.Fatalf("Differences should have been found, but were not on Auth method")
|
||||
|
||||
for _, tc := range testCases {
|
||||
if tc.globalVal == tc.newVal {
|
||||
t.Errorf("Differences should have been found:\n\tConfig.%s: %v \n\tSetConfig: %v \n", tc.fieldName, tc.globalVal, tc.newVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package settings
|
||||
import (
|
||||
"github.com/gtsteffaniak/filebrowser/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/rules"
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
// StorageBackend is a settings storage backend.
|
||||
@@ -30,8 +29,8 @@ func (s *Storage) Get() (*Settings, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if set.UserHomeBasePath == "" {
|
||||
set.UserHomeBasePath = DefaultUsersHomeBasePath
|
||||
if set.Server.UserHomeBasePath == "" {
|
||||
set.Server.UserHomeBasePath = DefaultUsersHomeBasePath
|
||||
}
|
||||
return set, nil
|
||||
}
|
||||
@@ -46,7 +45,7 @@ var defaultEvents = []string{
|
||||
|
||||
// Save saves the settings for the current instance.
|
||||
func (s *Storage) Save(set *Settings) error {
|
||||
if len(set.Key) == 0 {
|
||||
if len(set.Auth.Key) == 0 {
|
||||
return errors.ErrEmptyKey
|
||||
}
|
||||
|
||||
@@ -59,7 +58,7 @@ func (s *Storage) Save(set *Settings) error {
|
||||
}
|
||||
|
||||
if set.UserDefaults.ViewMode == "" {
|
||||
set.UserDefaults.ViewMode = users.MosaicViewMode
|
||||
set.UserDefaults.ViewMode = "normal"
|
||||
}
|
||||
|
||||
if set.Rules == nil {
|
||||
|
||||
+31
-40
@@ -5,42 +5,28 @@ import (
|
||||
"github.com/gtsteffaniak/filebrowser/users"
|
||||
)
|
||||
|
||||
// Apply applies the default options to a user.
|
||||
func (d *UserDefaults) Apply(u *users.User) {
|
||||
u.Scope = d.Scope
|
||||
u.Locale = d.Locale
|
||||
u.ViewMode = d.ViewMode
|
||||
u.SingleClick = d.SingleClick
|
||||
u.Perm = d.Perm
|
||||
u.Sorting = d.Sorting
|
||||
u.Commands = d.Commands
|
||||
u.HideDotfiles = d.HideDotfiles
|
||||
u.DateFormat = d.DateFormat
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
Key []byte `json:"key"`
|
||||
Signup bool `json:"signup"`
|
||||
CreateUserDir bool `json:"createUserDir"`
|
||||
UserHomeBasePath string `json:"userHomeBasePath"`
|
||||
Commands map[string][]string `json:"commands"`
|
||||
Shell []string `json:"shell"`
|
||||
AdminUsername string `json:"adminUsername"`
|
||||
AdminPassword string `json:"adminPassword"`
|
||||
Rules []rules.Rule `json:"rules"`
|
||||
Server Server `json:"server"`
|
||||
Auth Auth `json:"auth"`
|
||||
Frontend Frontend `json:"frontend"`
|
||||
UserDefaults UserDefaults `json:"userDefaults"`
|
||||
Commands map[string][]string `json:"commands"`
|
||||
Shell []string `json:"shell"`
|
||||
Rules []rules.Rule `json:"rules"`
|
||||
Server Server `json:"server"`
|
||||
Auth Auth `json:"auth"`
|
||||
Frontend Frontend `json:"frontend"`
|
||||
Users []UserDefaults `json:"users,omitempty"`
|
||||
UserDefaults UserDefaults `json:"userDefaults"`
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
Recaptcha Recaptcha `json:"recaptcha"`
|
||||
Header string `json:"header"`
|
||||
Method string `json:"method"`
|
||||
Command string `json:"command"`
|
||||
Signup bool `json:"signup"`
|
||||
Shell string `json:"shell"`
|
||||
TokenExpirationTime string `json:"tokenExpirationTime"`
|
||||
Recaptcha Recaptcha `json:"recaptcha"`
|
||||
Header string `json:"header"`
|
||||
Method string `json:"method"`
|
||||
Command string `json:"command"`
|
||||
Signup bool `json:"signup"`
|
||||
Shell string `json:"shell"`
|
||||
AdminUsername string `json:"adminUsername"`
|
||||
AdminPassword string `json:"adminPassword"`
|
||||
Key []byte `json:"key"`
|
||||
}
|
||||
|
||||
type Recaptcha struct {
|
||||
@@ -66,6 +52,9 @@ type Server struct {
|
||||
Log string `json:"log"`
|
||||
Database string `json:"database"`
|
||||
Root string `json:"root"`
|
||||
UserHomeBasePath string `json:"userHomeBasePath"`
|
||||
CreateUserDir bool `json:"createUserDir"`
|
||||
Indexing bool `json:"indexing"`
|
||||
}
|
||||
|
||||
type Frontend struct {
|
||||
@@ -73,25 +62,27 @@ type Frontend struct {
|
||||
DisableExternal bool `json:"disableExternal"`
|
||||
DisableUsedPercentage bool `json:"disableUsedPercentage"`
|
||||
Files string `json:"files"`
|
||||
Theme string `json:"theme"`
|
||||
Color string `json:"color"`
|
||||
}
|
||||
|
||||
// UserDefaults is a type that holds the default values
|
||||
// for some fields on User.
|
||||
type UserDefaults struct {
|
||||
LockPassword bool `json:"lockPassword"`
|
||||
Scope string `json:"scope"`
|
||||
Locale string `json:"locale"`
|
||||
ViewMode string `json:"viewMode"`
|
||||
SingleClick bool `json:"singleClick"`
|
||||
Sorting struct {
|
||||
DarkMode bool `json:"darkMode"`
|
||||
LockPassword bool `json:"lockPassword"`
|
||||
DisableSettings bool `json:"disableSettings,omitempty"`
|
||||
Scope string `json:"scope"`
|
||||
Locale string `json:"locale"`
|
||||
ViewMode string `json:"viewMode"`
|
||||
SingleClick bool `json:"singleClick"`
|
||||
Rules []rules.Rule `json:"rules"`
|
||||
Sorting struct {
|
||||
By string `json:"by"`
|
||||
Asc bool `json:"asc"`
|
||||
} `json:"sorting"`
|
||||
Perm users.Permissions `json:"perm"`
|
||||
Permissions users.Permissions `json:"permissions"`
|
||||
Commands []string `json:"commands"`
|
||||
Commands []string `json:"commands,omitempty"`
|
||||
HideDotfiles bool `json:"hideDotfiles"`
|
||||
DateFormat bool `json:"dateFormat"`
|
||||
}
|
||||
@@ -7,11 +7,11 @@ server:
|
||||
enableThumbnails: false
|
||||
resizePreview: true
|
||||
typeDetectionByHeader: true
|
||||
port: 8080
|
||||
port: 80
|
||||
baseURL: "/"
|
||||
address: ""
|
||||
log: "stdout"
|
||||
database: "database.db"
|
||||
database: "mydb.db"
|
||||
root: "/srv"
|
||||
auth:
|
||||
recaptcha:
|
||||
@@ -25,15 +25,16 @@ auth:
|
||||
shell: ""
|
||||
frontend:
|
||||
name: ""
|
||||
disableExternal: false
|
||||
disableExternal: true
|
||||
disableUsedPercentage: true
|
||||
files: ""
|
||||
theme: ""
|
||||
color: ""
|
||||
userDefaults:
|
||||
scope: ""
|
||||
locale: ""
|
||||
viewMode: ""
|
||||
darkMode: true
|
||||
disableSettings: false
|
||||
singleClick: true
|
||||
sorting:
|
||||
by: ""
|
||||
@@ -47,6 +48,5 @@ userDefaults:
|
||||
delete: true
|
||||
share: true
|
||||
download: true
|
||||
commands: []
|
||||
hideDotfiles: false
|
||||
dateFormat: false
|
||||
@@ -20,7 +20,7 @@ func (s settingsBackend) Save(set *settings.Settings) error {
|
||||
|
||||
func (s settingsBackend) GetServer() (*settings.Server, error) {
|
||||
server := &settings.Server{
|
||||
Port: 8080,
|
||||
Port: 80,
|
||||
NumImageProcessors: 1,
|
||||
}
|
||||
return server, get(s.db, "server", server)
|
||||
|
||||
@@ -45,11 +45,9 @@ func (st usersBackend) Gets() ([]*users.User, error) {
|
||||
if err == storm.ErrNotFound {
|
||||
return nil, errors.ErrNotExist
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return allUsers, err
|
||||
}
|
||||
|
||||
return allUsers, err
|
||||
}
|
||||
|
||||
@@ -73,7 +71,12 @@ func (st usersBackend) Update(user *users.User, fields ...string) error {
|
||||
}
|
||||
|
||||
func (st usersBackend) Save(user *users.User) error {
|
||||
err := st.db.Save(user)
|
||||
pass, err := users.HashPwd(user.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user.Password = pass
|
||||
err = st.db.Save(user)
|
||||
if err == storm.ErrAlreadyExists {
|
||||
return errors.ErrExist
|
||||
}
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
module github.com/gtsteffaniak/filebrowser/tools
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/golangci/golangci-lint v1.51.1
|
||||
golang.org/x/tools v0.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
|
||||
4d63.com/gochecknoglobals v0.2.1 // indirect
|
||||
github.com/Abirdcfly/dupword v0.0.9 // indirect
|
||||
github.com/Antonboom/errname v0.1.7 // indirect
|
||||
github.com/Antonboom/nilnil v0.1.1 // indirect
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/OpenPeeDeeP/depguard v1.1.1 // indirect
|
||||
github.com/alexkohler/prealloc v1.0.0 // indirect
|
||||
github.com/alingse/asasalint v0.0.11 // indirect
|
||||
github.com/ashanbrown/forbidigo v1.3.0 // indirect
|
||||
github.com/ashanbrown/makezero v1.1.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bkielbasa/cyclop v1.2.0 // indirect
|
||||
github.com/blizzy78/varnamelen v0.8.0 // indirect
|
||||
github.com/bombsimon/wsl/v3 v3.3.0 // indirect
|
||||
github.com/breml/bidichk v0.2.3 // indirect
|
||||
github.com/breml/errchkjson v0.3.0 // indirect
|
||||
github.com/butuzov/ireturn v0.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/charithe/durationcheck v0.0.9 // indirect
|
||||
github.com/chavacava/garif v0.0.0-20221024190013-b3ef35877348 // indirect
|
||||
github.com/curioswitch/go-reassign v0.2.0 // indirect
|
||||
github.com/daixiang0/gci v0.9.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denis-tingaikin/go-header v0.4.3 // indirect
|
||||
github.com/esimonov/ifshort v1.0.4 // indirect
|
||||
github.com/ettle/strcase v0.1.1 // indirect
|
||||
github.com/fatih/color v1.14.1 // indirect
|
||||
github.com/fatih/structtag v1.2.0 // indirect
|
||||
github.com/firefart/nonamedreturns v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fzipp/gocyclo v0.6.0 // indirect
|
||||
github.com/go-critic/go-critic v0.6.5 // indirect
|
||||
github.com/go-toolsmith/astcast v1.0.0 // indirect
|
||||
github.com/go-toolsmith/astcopy v1.0.3 // indirect
|
||||
github.com/go-toolsmith/astequal v1.0.3 // indirect
|
||||
github.com/go-toolsmith/astfmt v1.0.0 // indirect
|
||||
github.com/go-toolsmith/astp v1.0.0 // indirect
|
||||
github.com/go-toolsmith/strparse v1.0.0 // indirect
|
||||
github.com/go-toolsmith/typep v1.0.2 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
|
||||
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect
|
||||
github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 // indirect
|
||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect
|
||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect
|
||||
github.com/golangci/misspell v0.4.0 // indirect
|
||||
github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect
|
||||
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 // indirect
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
|
||||
github.com/gostaticanalysis/comment v1.4.2 // indirect
|
||||
github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect
|
||||
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hexops/gotextdiff v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jgautheron/goconst v1.5.1 // indirect
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect
|
||||
github.com/julz/importas v0.1.0 // indirect
|
||||
github.com/junk1tm/musttag v0.4.4 // indirect
|
||||
github.com/kisielk/errcheck v1.6.3 // indirect
|
||||
github.com/kisielk/gotool v1.0.0 // indirect
|
||||
github.com/kkHAIKE/contextcheck v1.1.3 // indirect
|
||||
github.com/kulti/thelper v0.6.3 // indirect
|
||||
github.com/kunwardeep/paralleltest v1.0.6 // indirect
|
||||
github.com/kyoh86/exportloopref v0.1.11 // indirect
|
||||
github.com/ldez/gomoddirectives v0.2.3 // indirect
|
||||
github.com/ldez/tagliatelle v0.4.0 // indirect
|
||||
github.com/leonklingele/grouper v1.1.1 // indirect
|
||||
github.com/lufeee/execinquery v1.2.1 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/maratori/testableexamples v1.0.0 // indirect
|
||||
github.com/maratori/testpackage v1.1.0 // indirect
|
||||
github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mbilski/exhaustivestruct v1.2.0 // indirect
|
||||
github.com/mgechev/revive v1.2.5 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moricho/tparallel v0.2.1 // indirect
|
||||
github.com/nakabonne/nestif v0.3.1 // indirect
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
|
||||
github.com/nishanths/exhaustive v0.9.5 // indirect
|
||||
github.com/nishanths/predeclared v0.2.2 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.8.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/polyfloyd/go-errorlint v1.0.6 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/quasilyte/go-ruleguard v0.3.18 // indirect
|
||||
github.com/quasilyte/gogrep v0.0.0-20220828223005-86e4605de09f // indirect
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 // indirect
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
|
||||
github.com/ryancurrah/gomodguard v1.3.0 // indirect
|
||||
github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.21.1 // indirect
|
||||
github.com/securego/gosec/v2 v2.14.0 // indirect
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/sivchari/containedctx v1.0.2 // indirect
|
||||
github.com/sivchari/nosnakecase v1.7.0 // indirect
|
||||
github.com/sivchari/tenv v1.7.1 // indirect
|
||||
github.com/sonatard/noctx v0.0.1 // indirect
|
||||
github.com/sourcegraph/go-diff v0.7.0 // indirect
|
||||
github.com/spf13/afero v1.8.2 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/cobra v1.6.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.12.0 // indirect
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/stretchr/testify v1.8.1 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect
|
||||
github.com/tdakkota/asciicheck v0.1.1 // indirect
|
||||
github.com/tetafro/godot v1.4.11 // indirect
|
||||
github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e // indirect
|
||||
github.com/timonwong/loggercheck v0.9.3 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.8.0 // indirect
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
|
||||
github.com/ultraware/funlen v0.0.3 // indirect
|
||||
github.com/ultraware/whitespace v0.0.5 // indirect
|
||||
github.com/uudashr/gocognit v1.0.6 // indirect
|
||||
github.com/yagipy/maintidx v1.0.0 // indirect
|
||||
github.com/yeya24/promlinter v0.2.0 // indirect
|
||||
gitlab.com/bosi/decorder v0.2.3 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.17.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.6.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
honnef.co/go/tools v0.4.0 // indirect
|
||||
mvdan.cc/gofumpt v0.4.0 // indirect
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
|
||||
mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d // indirect
|
||||
)
|
||||
@@ -1,981 +0,0 @@
|
||||
4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA=
|
||||
4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs=
|
||||
4d63.com/gochecknoglobals v0.2.1 h1:1eiorGsgHOFOuoOiJDy2psSrQbRdIHrlge0IJIkUgDc=
|
||||
4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.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.44.3/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.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
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=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Abirdcfly/dupword v0.0.9 h1:MxprGjKq3yDBICXDgEEsyGirIXfMYXkLNT/agPsE1tk=
|
||||
github.com/Abirdcfly/dupword v0.0.9/go.mod h1:PzmHVLLZ27MvHSzV7eFmMXSFArWXZPZmfuuziuUrf2g=
|
||||
github.com/Antonboom/errname v0.1.7 h1:mBBDKvEYwPl4WFFNwec1CZO096G6vzK9vvDQzAwkako=
|
||||
github.com/Antonboom/errname v0.1.7/go.mod h1:g0ONh16msHIPgJSGsecu1G/dcF2hlYR/0SddnIAGavU=
|
||||
github.com/Antonboom/nilnil v0.1.1 h1:PHhrh5ANKFWRBh7TdYmyyq2gyT2lotnvFvvFbylF81Q=
|
||||
github.com/Antonboom/nilnil v0.1.1/go.mod h1:L1jBqoWM7AOeTD+tSquifKSesRHs4ZdaxvZR+xdJEaI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 h1:+r1rSv4gvYn0wmRjC8X7IAzX8QezqtFV9m0MUHFJgts=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0/go.mod h1:b3g59n2Y+T5xmcxJL+UEG2f8cQploZm1mR/v6BW0mU0=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/OpenPeeDeeP/depguard v1.1.1 h1:TSUznLjvp/4IUP+OQ0t/4jF4QUyxIcVX8YnghZdunyA=
|
||||
github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw=
|
||||
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
|
||||
github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
|
||||
github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I=
|
||||
github.com/ashanbrown/forbidigo v1.3.0 h1:VkYIwb/xxdireGAdJNZoo24O4lmnEWkactplBlWTShc=
|
||||
github.com/ashanbrown/forbidigo v1.3.0/go.mod h1:vVW7PEdqEFqapJe95xHkTfB1+XvZXBFg8t0sG2FIxmI=
|
||||
github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s=
|
||||
github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A=
|
||||
github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI=
|
||||
github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
|
||||
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
|
||||
github.com/bombsimon/wsl/v3 v3.3.0 h1:Mka/+kRLoQJq7g2rggtgQsjuI/K5Efd87WX96EWFxjM=
|
||||
github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
|
||||
github.com/breml/bidichk v0.2.3 h1:qe6ggxpTfA8E75hdjWPZ581sY3a2lnl0IRxLQFelECI=
|
||||
github.com/breml/bidichk v0.2.3/go.mod h1:8u2C6DnAy0g2cEq+k/A2+tr9O1s+vHGxWn0LTc70T2A=
|
||||
github.com/breml/errchkjson v0.3.0 h1:YdDqhfqMT+I1vIxPSas44P+9Z9HzJwCeAzjB8PxP1xw=
|
||||
github.com/breml/errchkjson v0.3.0/go.mod h1:9Cogkyv9gcT8HREpzi3TiqBxCqDzo8awa92zSDFcofU=
|
||||
github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY=
|
||||
github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charithe/durationcheck v0.0.9 h1:mPP4ucLrf/rKZiIG/a9IPXHGlh8p4CzgpyTy6EEutYk=
|
||||
github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg=
|
||||
github.com/chavacava/garif v0.0.0-20221024190013-b3ef35877348 h1:cy5GCEZLUCshCGCRRUjxHrDUqkB4l5cuUt3ShEckQEo=
|
||||
github.com/chavacava/garif v0.0.0-20221024190013-b3ef35877348/go.mod h1:f/miWtG3SSuTxKsNK3o58H1xl+XV6ZIfbC6p7lPPB8U=
|
||||
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/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/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cristalhq/acmd v0.8.1/go.mod h1:LG5oa43pE/BbxtfMoImHCQN++0Su7dzipdgBjMCBVDQ=
|
||||
github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo=
|
||||
github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
|
||||
github.com/daixiang0/gci v0.9.0 h1:t8XZ0vK6l0pwPoOmoGyqW2NwQlvbpAQNVvu/GRBgykM=
|
||||
github.com/daixiang0/gci v0.9.0/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c=
|
||||
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/denis-tingaikin/go-header v0.4.3 h1:tEaZKAlqql6SKCY++utLmkPLd6K8IBM20Ha7UVm+mtU=
|
||||
github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYBAC2Mra5RassOIQ2/c=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA=
|
||||
github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0=
|
||||
github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw=
|
||||
github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY=
|
||||
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
|
||||
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phmY9sfv40Y=
|
||||
github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/go-critic/go-critic v0.6.5 h1:fDaR/5GWURljXwF8Eh31T2GZNz9X4jeboS912mWF8Uo=
|
||||
github.com/go-critic/go-critic v0.6.5/go.mod h1:ezfP/Lh7MA6dBNn4c6ab5ALv3sKnZVLx37tr00uuaOY=
|
||||
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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
|
||||
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
||||
github.com/go-toolsmith/astcopy v1.0.2/go.mod h1:4TcEdbElGc9twQEYpVo/aieIXfHhiuLh4aLAck6dO7Y=
|
||||
github.com/go-toolsmith/astcopy v1.0.3 h1:r0bgSRlMOAgO+BdQnVAcpMSMkrQCnV6ZJmIkrJgcJj0=
|
||||
github.com/go-toolsmith/astcopy v1.0.3/go.mod h1:4TcEdbElGc9twQEYpVo/aieIXfHhiuLh4aLAck6dO7Y=
|
||||
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
||||
github.com/go-toolsmith/astequal v1.0.2/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4=
|
||||
github.com/go-toolsmith/astequal v1.0.3 h1:+LVdyRatFS+XO78SGV4I3TCEA0AC7fKEGma+fH+674o=
|
||||
github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4=
|
||||
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
|
||||
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
|
||||
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
|
||||
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
|
||||
github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5 h1:eD9POs68PHkwrx7hAB78z1cb6PfGq/jyWn3wJywsH1o=
|
||||
github.com/go-toolsmith/pkgload v1.0.2-0.20220101231613-e814995d17c5/go.mod h1:3NAwwmD4uY/yggRxoEjk/S00MIV3A+H7rrE3i87eYxM=
|
||||
github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
|
||||
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
|
||||
github.com/go-toolsmith/typep v1.0.2 h1:8xdsa1+FSIH/RhEkgnD1j2CJOy5mNllW1Q9tRiYwvlk=
|
||||
github.com/go-toolsmith/typep v1.0.2/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
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/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
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/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
|
||||
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
||||
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6J5HIP8ZtyMdiDscjMLfRBSPuzVVeo=
|
||||
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ=
|
||||
github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 h1:amWTbTGqOZ71ruzrdA+Nx5WA3tV1N0goTspwmKCQvBY=
|
||||
github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs=
|
||||
github.com/golangci/golangci-lint v1.51.1 h1:N5HD/x0ZrhJYsgKWyz7yJxxQ8JKR0Acc+FOP7QtGSAA=
|
||||
github.com/golangci/golangci-lint v1.51.1/go.mod h1:hnyNNO3fJ2Rjwo6HM+VXvcmLkKDOuBAnR9gVlS1mW1E=
|
||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
|
||||
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
|
||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
|
||||
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
|
||||
github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0=
|
||||
github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc=
|
||||
github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 h1:DIPQnGy2Gv2FSA4B/hh8Q7xx3B7AIDk3DAMeHclH1vQ=
|
||||
github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs=
|
||||
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
|
||||
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
|
||||
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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/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-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
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 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 h1:9alfqbrhuD+9fLZ4iaAVwhlp5PEhmnBt7yvK2Oy5C1U=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||
github.com/gostaticanalysis/analysisutil v0.1.0/go.mod h1:dMhHRU9KTiDcuLGdy87/2gTR8WruwYZrKdRq9m1O6uw=
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk=
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc=
|
||||
github.com/gostaticanalysis/comment v1.3.0/go.mod h1:xMicKDx7XRXYdVwY9f9wQpDJVnqWxw9wCauCMKp+IBI=
|
||||
github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado=
|
||||
github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q=
|
||||
github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.1.0 h1:6eUflI3DiGusXGK6X7cCcIgVCpZ2CiZ1Q7jl6ZxNV70=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak=
|
||||
github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk=
|
||||
github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A=
|
||||
github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
|
||||
github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
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/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+WcM=
|
||||
github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY=
|
||||
github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0=
|
||||
github.com/junk1tm/musttag v0.4.4 h1:VK4L7v7lvWAhKDDx0cUJgbb0UBNipYinv8pPeHJzH9Q=
|
||||
github.com/junk1tm/musttag v0.4.4/go.mod h1:XkcL/9O6RmD88JBXb+I15nYRl9W4ExhgQeCBEhfMC8U=
|
||||
github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM8=
|
||||
github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw=
|
||||
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkHAIKE/contextcheck v1.1.3 h1:l4pNvrb8JSwRd51ojtcOxOeHJzHek+MtOyXbaR0uvmw=
|
||||
github.com/kkHAIKE/contextcheck v1.1.3/go.mod h1:PG/cwd6c0705/LM0KTr1acO2gORUxkSVWyLJOFW5qoo=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs=
|
||||
github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
|
||||
github.com/kunwardeep/paralleltest v1.0.6 h1:FCKYMF1OF2+RveWlABsdnmsvJrei5aoyZoaGS+Ugg8g=
|
||||
github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes=
|
||||
github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ=
|
||||
github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA=
|
||||
github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA=
|
||||
github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0=
|
||||
github.com/ldez/tagliatelle v0.4.0 h1:sylp7d9kh6AdXN2DpVGHBRb5guTVAgOxqNGhbqc4b1c=
|
||||
github.com/ldez/tagliatelle v0.4.0/go.mod h1:mNtTfrHy2haaBAw+VT7IBV6VXBThS7TCreYWbBcJ87I=
|
||||
github.com/leonklingele/grouper v1.1.1 h1:suWXRU57D4/Enn6pXR0QVqqWWrnJ9Osrz+5rjt8ivzU=
|
||||
github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY=
|
||||
github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM=
|
||||
github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI=
|
||||
github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
|
||||
github.com/maratori/testpackage v1.1.0 h1:GJY4wlzQhuBusMF1oahQCBtUV/AQ/k69IZ68vxaac2Q=
|
||||
github.com/maratori/testpackage v1.1.0/go.mod h1:PeAhzU8qkCwdGEMTEupsHJNlQu2gZopMC6RjbhmHeDc=
|
||||
github.com/matoous/godox v0.0.0-20210227103229-6504466cf951 h1:pWxk9e//NbPwfxat7RXkts09K+dEBJWakUWwICVqYbA=
|
||||
github.com/matoous/godox v0.0.0-20210227103229-6504466cf951/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo=
|
||||
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
|
||||
github.com/mgechev/revive v1.2.5 h1:UF9AR8pOAuwNmhXj2odp4mxv9Nx2qUIwVz8ZsU+Mbec=
|
||||
github.com/mgechev/revive v1.2.5/go.mod h1:nFOXent79jMTISAfOAasKfy0Z2Ejq0WX7Qn/KAdYopI=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/moricho/tparallel v0.2.1 h1:95FytivzT6rYzdJLdtfn6m1bfFJylOJK41+lgv/EHf4=
|
||||
github.com/moricho/tparallel v0.2.1/go.mod h1:fXEIZxG2vdfl0ZF8b42f5a78EhjjD5mX8qUplsoSU4k=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
|
||||
github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nishanths/exhaustive v0.9.5 h1:TzssWan6orBiLYVqewCG8faud9qlFntJE30ACpzmGME=
|
||||
github.com/nishanths/exhaustive v0.9.5/go.mod h1:IbwrGdVMizvDcIxPYGVdQn5BqWJaOwpCvg4RGb8r/TA=
|
||||
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
|
||||
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
||||
github.com/nunnatsa/ginkgolinter v0.8.1 h1:/y4o/0hV+ruUHj4xXh89xlFjoaitnI4LnkpuYs02q1c=
|
||||
github.com/nunnatsa/ginkgolinter v0.8.1/go.mod h1:FYYLtszIdmzCH8XMaMPyxPVXZ7VCaIm55bA+gugx+14=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.3.1 h1:8SbseP7qM32WcvE6VaN6vfXxv698izmsJ1UQX9ve7T8=
|
||||
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
|
||||
github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k=
|
||||
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
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/polyfloyd/go-errorlint v1.0.6 h1:ZevdyEGxDoHAMQUVvdTT06hnYuKULe8TQkOmIYx6s1c=
|
||||
github.com/polyfloyd/go-errorlint v1.0.6/go.mod h1:NcnNncnm8fVV7vfQWiI4HZrzWFzGp24C262IQutNcMs=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30=
|
||||
github.com/quasilyte/go-ruleguard v0.3.18 h1:sd+abO1PEI9fkYennwzHn9kl3nqP6M5vE7FiOzZ+5CE=
|
||||
github.com/quasilyte/go-ruleguard v0.3.18/go.mod h1:lOIzcYlgxrQ2sGJ735EHXmf/e9MJ516j16K/Ifcttvs=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.21/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc=
|
||||
github.com/quasilyte/go-ruleguard/rules v0.0.0-20211022131956-028d6511ab71/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50=
|
||||
github.com/quasilyte/gogrep v0.0.0-20220828223005-86e4605de09f h1:6Gtn2i04RD0gVyYf2/IUMTIs+qYleBt4zxDqkLTcu4U=
|
||||
github.com/quasilyte/gogrep v0.0.0-20220828223005-86e4605de09f/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs=
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryancurrah/gomodguard v1.3.0 h1:q15RT/pd6UggBXVBuLps8BXRvl5GPBcwVA7BJHMLuTw=
|
||||
github.com/ryancurrah/gomodguard v1.3.0/go.mod h1:ggBxb3luypPEzqVtq33ee7YSN35V28XeGnid8dnni50=
|
||||
github.com/ryanrolds/sqlclosecheck v0.4.0 h1:i8SX60Rppc1wRuyQjMciLqIzV3xnoHB7/tXbr6RGYNI=
|
||||
github.com/ryanrolds/sqlclosecheck v0.4.0/go.mod h1:TBRRjzL31JONc9i4XMinicuo+s+E8yKZ5FN8X3G6CKQ=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.21.1 h1:GQGlReyL9Ek8DdJmwtwhHbhwHnuPfsKaprpjnrPcjxc=
|
||||
github.com/sashamelentyev/usestdlibvars v1.21.1/go.mod h1:MPI52Qq99iO9sFZZcKJ2y/bx6BNjs+/2bw3PCggIbew=
|
||||
github.com/securego/gosec/v2 v2.14.0 h1:U1hfs0oBackChXA72plCYVA4cOlQ4gO+209dHiSNZbI=
|
||||
github.com/securego/gosec/v2 v2.14.0/go.mod h1:Ff03zEi5NwSOfXj9nFpBfhbWTtROCkg9N+9goggrYn4=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
|
||||
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/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sivchari/containedctx v1.0.2 h1:0hLQKpgC53OVF1VT7CeoFHk9YKstur1XOgfYIc1yrHI=
|
||||
github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw=
|
||||
github.com/sivchari/nosnakecase v1.7.0 h1:7QkpWIRMe8x25gckkFd2A5Pi6Ymo0qgr4JrhGt95do8=
|
||||
github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY=
|
||||
github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak=
|
||||
github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg=
|
||||
github.com/sonatard/noctx v0.0.1 h1:VC1Qhl6Oxx9vvWo3UDgrGXYCeKCe3Wbw7qAWL6FrmTY=
|
||||
github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI=
|
||||
github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
|
||||
github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
|
||||
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
|
||||
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
|
||||
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8=
|
||||
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk=
|
||||
github.com/tdakkota/asciicheck v0.1.1 h1:PKzG7JUTUmVspQTDqtkX9eSiLGossXTybutHwTXuO0A=
|
||||
github.com/tdakkota/asciicheck v0.1.1/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
|
||||
github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
|
||||
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
|
||||
github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw=
|
||||
github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8=
|
||||
github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e h1:MV6KaVu/hzByHP0UvJ4HcMGE/8a6A4Rggc/0wx2AvJo=
|
||||
github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ=
|
||||
github.com/timonwong/loggercheck v0.9.3 h1:ecACo9fNiHxX4/Bc02rW2+kaJIAMAes7qJ7JKxt0EZI=
|
||||
github.com/timonwong/loggercheck v0.9.3/go.mod h1:wUqnk9yAOIKtGA39l1KLE9Iz0QiTocu/YZoOf+OzFdw=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.8.0 h1:qDzbir0xmoE+aNxGCPrn+rUSxAX+nG6vREgbbXAR81I=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.8.0/go.mod h1:ao7l5p0aOlUNJKI0qVwB4Yjlqutd0IvAB9Rdwyilxvg=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
|
||||
github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA=
|
||||
github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||
github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI=
|
||||
github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
|
||||
github.com/uudashr/gocognit v1.0.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842Y=
|
||||
github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY=
|
||||
github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM=
|
||||
github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk=
|
||||
github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o=
|
||||
github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA=
|
||||
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/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
gitlab.com/bosi/decorder v0.2.3 h1:gX4/RgK16ijY8V+BRQHAySfQAb354T7/xQpDB2n10P0=
|
||||
gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE=
|
||||
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.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
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/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220827204233-334a2380cb91/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a h1:Jw5wfR+h9mnIYH+OtGT2im5wV1YGGDora5vTv/aa5bE=
|
||||
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/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/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
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-20181114220301-adae6a3d119a/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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-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-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-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/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-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/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-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-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-20191001151750-bb3f8db39f24/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-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/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-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/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-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-20210225134936-a50acf3fe073/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-20210423185535-09eb48e85fd7/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-20210603081109-ebe580a85c40/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-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
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.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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190307163923-6a08e3108db3/go.mod h1:25r3+/G6/xytQM8iWZKq3Hn0kr0rgFKPUNVEL/dr3z4=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/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-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/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-20190916130336-e45ffcd953cc/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-20191108193012-7d206e10da11/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-20200117220505-0cba7a3a9ee9/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-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
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-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
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-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200831203904-5a2aa26beb65/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
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-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.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/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-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-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.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.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/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.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
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.5/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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=
|
||||
honnef.co/go/tools v0.4.0 h1:lyXVV1c8wUBJRKqI8JgIpT8TW1VDagfYYaxbKa/HoL8=
|
||||
honnef.co/go/tools v0.4.0/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA=
|
||||
mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM=
|
||||
mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||
mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d h1:3rvTIIM22r9pvXk+q3swxUQAQOxksVMGK7sml4nG57w=
|
||||
mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d/go.mod h1:IeHQjmn6TOD+e4Z3RFiZMMsLVL+A96Nvptar8Fj71is=
|
||||
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=
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@commitlint/cli": "^15.0.0",
|
||||
"@commitlint/config-conventional": "^15.0.0",
|
||||
"standard-version": "^9.3.2"
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//go:build tools
|
||||
// +build tools
|
||||
|
||||
package tools
|
||||
|
||||
// Manage tool dependencies via go.mod.
|
||||
//
|
||||
// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
|
||||
// https://github.com/golang/go/issues/25922
|
||||
//
|
||||
// nolint
|
||||
import (
|
||||
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
|
||||
_ "golang.org/x/tools/cmd/goimports"
|
||||
)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,10 +1,12 @@
|
||||
package users
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/rules"
|
||||
)
|
||||
|
||||
// StorageBackend is the interface to implement for a users storage.
|
||||
@@ -17,6 +19,7 @@ type StorageBackend interface {
|
||||
DeleteByUsername(string) error
|
||||
}
|
||||
|
||||
// Store is an interface for user storage.
|
||||
type Store interface {
|
||||
Get(baseScope string, id interface{}) (user *User, err error)
|
||||
Gets(baseScope string) ([]*User, error)
|
||||
@@ -24,6 +27,8 @@ type Store interface {
|
||||
Save(user *User) error
|
||||
Delete(id interface{}) error
|
||||
LastUpdate(id uint) int64
|
||||
AddRule(username string, rule rules.Rule) error
|
||||
DeleteRule(username string, ruleID string) error
|
||||
}
|
||||
|
||||
// Storage is a users storage.
|
||||
@@ -52,7 +57,7 @@ func (s *Storage) Get(baseScope string, id interface{}) (user *User, err error)
|
||||
if err := user.Clean(baseScope); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
return user, err
|
||||
}
|
||||
|
||||
// Gets gets a list of all users.
|
||||
@@ -61,7 +66,6 @@ func (s *Storage) Gets(baseScope string) ([]*User, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if err := user.Clean(baseScope); err != nil { //nolint:govet
|
||||
return nil, err
|
||||
@@ -73,7 +77,7 @@ func (s *Storage) Gets(baseScope string) ([]*User, error) {
|
||||
|
||||
// Update updates a user in the database.
|
||||
func (s *Storage) Update(user *User, fields ...string) error {
|
||||
err := user.Clean("", fields...)
|
||||
err := user.Clean("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -89,8 +93,51 @@ func (s *Storage) Update(user *User, fields ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRule adds a rule to the user's rules list and updates the user in the database.
|
||||
func (s *Storage) AddRule(userID string, rule rules.Rule) error {
|
||||
user, err := s.Get("", userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.Rules = append(user.Rules, rule)
|
||||
|
||||
err = s.Update(user, "Rules")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRule deletes a rule specified by ID from the user's rules list and updates the user in the database.
|
||||
func (s *Storage) DeleteRule(userID string, ruleID string) error {
|
||||
user, err := s.Get("", userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find and remove the rule with the specified ID
|
||||
var updatedRules []rules.Rule
|
||||
for _, r := range user.Rules {
|
||||
if r.Id != ruleID {
|
||||
updatedRules = append(updatedRules, r)
|
||||
}
|
||||
}
|
||||
|
||||
user.Rules = updatedRules
|
||||
|
||||
err = s.Update(user, "Rules")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save saves the user in a storage.
|
||||
func (s *Storage) Save(user *User) error {
|
||||
log.Println("Saving new user:", user.Username)
|
||||
if err := user.Clean(""); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+42
-65
@@ -6,16 +6,9 @@ import (
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gtsteffaniak/filebrowser/errors"
|
||||
"github.com/gtsteffaniak/filebrowser/files"
|
||||
"github.com/gtsteffaniak/filebrowser/rules"
|
||||
)
|
||||
|
||||
var (
|
||||
ListViewMode = "list"
|
||||
MosaicViewMode = "mosaic"
|
||||
)
|
||||
|
||||
type Permissions struct {
|
||||
Admin bool `json:"admin"`
|
||||
Execute bool `json:"execute"`
|
||||
@@ -27,23 +20,49 @@ type Permissions struct {
|
||||
Download bool `json:"download"`
|
||||
}
|
||||
|
||||
// SortingSettings represents the sorting settings.
|
||||
type Sorting struct {
|
||||
By string `json:"by"`
|
||||
Asc bool `json:"asc"`
|
||||
}
|
||||
|
||||
// User describes a user.
|
||||
type User struct {
|
||||
ID uint `storm:"id,increment" json:"id"`
|
||||
Username string `storm:"unique" json:"username"`
|
||||
Password string `json:"password"`
|
||||
Scope string `json:"scope"`
|
||||
Locale string `json:"locale"`
|
||||
LockPassword bool `json:"lockPassword"`
|
||||
ViewMode string `json:"viewMode"`
|
||||
SingleClick bool `json:"singleClick"`
|
||||
Perm Permissions `json:"perm"`
|
||||
Commands []string `json:"commands"`
|
||||
Sorting files.Sorting `json:"sorting"`
|
||||
Fs afero.Fs `json:"-" yaml:"-"`
|
||||
Rules []rules.Rule `json:"rules"`
|
||||
HideDotfiles bool `json:"hideDotfiles"`
|
||||
DateFormat bool `json:"dateFormat"`
|
||||
DarkMode bool `json:"darkMode"`
|
||||
DisableSettings bool `json:"disableSettings"`
|
||||
ID uint `storm:"id,increment" json:"id"`
|
||||
Username string `storm:"unique" json:"username"`
|
||||
Password string `json:"password"`
|
||||
Scope string `json:"scope"`
|
||||
Locale string `json:"locale"`
|
||||
LockPassword bool `json:"lockPassword"`
|
||||
ViewMode string `json:"viewMode"`
|
||||
SingleClick bool `json:"singleClick"`
|
||||
Perm Permissions `json:"perm"`
|
||||
Commands []string `json:"commands"`
|
||||
Sorting Sorting `json:"sorting"`
|
||||
Fs afero.Fs `json:"-" yaml:"-"`
|
||||
Rules []rules.Rule `json:"rules"`
|
||||
HideDotfiles bool `json:"hideDotfiles"`
|
||||
DateFormat bool `json:"dateFormat"`
|
||||
}
|
||||
|
||||
var PublicUser = User{
|
||||
Username: "publicUser", // temp user not registered
|
||||
Password: "publicUser", // temp user not registered
|
||||
Scope: "./",
|
||||
ViewMode: "normal",
|
||||
LockPassword: true,
|
||||
Fs: afero.NewMemMapFs(),
|
||||
Perm: Permissions{
|
||||
Create: false,
|
||||
Rename: false,
|
||||
Modify: false,
|
||||
Delete: false,
|
||||
Share: true,
|
||||
Download: true,
|
||||
Admin: false,
|
||||
},
|
||||
}
|
||||
|
||||
// GetRules implements rules.Provider.
|
||||
@@ -51,53 +70,11 @@ func (u *User) GetRules() []rules.Rule {
|
||||
return u.Rules
|
||||
}
|
||||
|
||||
var checkableFields = []string{
|
||||
"Username",
|
||||
"Password",
|
||||
"Scope",
|
||||
"ViewMode",
|
||||
"Commands",
|
||||
"Sorting",
|
||||
"Rules",
|
||||
}
|
||||
|
||||
// Clean cleans up a user and verifies if all its fields
|
||||
// are alright to be saved.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (u *User) Clean(baseScope string, fields ...string) error {
|
||||
if len(fields) == 0 {
|
||||
fields = checkableFields
|
||||
}
|
||||
|
||||
for _, field := range fields {
|
||||
switch field {
|
||||
case "Username":
|
||||
if u.Username == "" {
|
||||
return errors.ErrEmptyUsername
|
||||
}
|
||||
case "Password":
|
||||
if u.Password == "" {
|
||||
return errors.ErrEmptyPassword
|
||||
}
|
||||
case "ViewMode":
|
||||
if u.ViewMode == "" {
|
||||
u.ViewMode = ListViewMode
|
||||
}
|
||||
case "Commands":
|
||||
if u.Commands == nil {
|
||||
u.Commands = []string{}
|
||||
}
|
||||
case "Sorting":
|
||||
if u.Sorting.By == "" {
|
||||
u.Sorting.By = "name"
|
||||
}
|
||||
case "Rules":
|
||||
if u.Rules == nil {
|
||||
u.Rules = []rules.Rule{}
|
||||
}
|
||||
}
|
||||
}
|
||||
func (u *User) Clean(baseScope string) error {
|
||||
|
||||
if u.Fs == nil {
|
||||
scope := u.Scope
|
||||
|
||||
@@ -2,7 +2,7 @@ package version
|
||||
|
||||
var (
|
||||
// Version is the current File Browser version.
|
||||
Version = "(0.2.0)"
|
||||
Version = "(0.2.6)"
|
||||
// CommitSHA is the commmit sha.
|
||||
CommitSHA = "(unknown)"
|
||||
)
|
||||
+82
-64
@@ -8,7 +8,10 @@ Here is an expanded config file which includes all possible configurations:
|
||||
|
||||
```
|
||||
server:
|
||||
CreateUserDir: false
|
||||
UserHomeBasePath: ""
|
||||
indexingInterval: 5
|
||||
indexing: true
|
||||
numImageProcessors: 4
|
||||
socket: ""
|
||||
tlsKey: ""
|
||||
@@ -16,17 +19,20 @@ server:
|
||||
enableThumbnails: false
|
||||
resizePreview: true
|
||||
typeDetectionByHeader: true
|
||||
port: 8080
|
||||
port: 80
|
||||
baseURL: "/"
|
||||
address: ""
|
||||
log: "stdout"
|
||||
database: "/database/database.db"
|
||||
root: "/srv"
|
||||
auth:
|
||||
adminUsername: admin
|
||||
adminPassword: admin
|
||||
recaptcha:
|
||||
host: ""
|
||||
key: ""
|
||||
secret: ""
|
||||
tokenExpirationTime: 2h
|
||||
header: ""
|
||||
method: json
|
||||
command: ""
|
||||
@@ -37,11 +43,12 @@ frontend:
|
||||
disableExternal: false
|
||||
disableUsedPercentage: true
|
||||
files: ""
|
||||
theme: ""
|
||||
color: ""
|
||||
userDefaults:
|
||||
settingsAllowed: true
|
||||
darkMode: false
|
||||
scope: ""
|
||||
locale: ""
|
||||
locale: "en"
|
||||
viewMode: ""
|
||||
singleClick: true
|
||||
sorting:
|
||||
@@ -64,34 +71,37 @@ userDefaults:
|
||||
Here are the defaults if nothing is set:
|
||||
|
||||
```
|
||||
Signup: true
|
||||
AdminUsername: admin
|
||||
AdminPassword: admin
|
||||
Server:
|
||||
EnableThumbnails: true
|
||||
EnableExec: false
|
||||
IndexingInterval: 5
|
||||
Port: 8080
|
||||
NumImageProcessors: 4
|
||||
BaseURL: ""
|
||||
Database: database.db
|
||||
Log: stdout
|
||||
Root: /srv
|
||||
Auth:
|
||||
Method: password
|
||||
Recaptcha:
|
||||
Host: ""
|
||||
UserDefaults:
|
||||
Scope: "."
|
||||
LockPassword: false
|
||||
HideDotfiles: true
|
||||
Permissions:
|
||||
Create: true
|
||||
Rename: true
|
||||
Modify: true
|
||||
Delete: true
|
||||
Share: true
|
||||
Download: true
|
||||
server:
|
||||
enableThumbnails: true
|
||||
enableExec: false
|
||||
indexingInterval: 5
|
||||
port: 80
|
||||
numImageProcessors: 4
|
||||
baseURL: ""
|
||||
database: database.db
|
||||
log: stdout
|
||||
root: /srv
|
||||
auth:
|
||||
adminUsername: admin
|
||||
adminPassword: admin
|
||||
method: password
|
||||
recaptcha:
|
||||
host: ""
|
||||
userDefaults:
|
||||
settingsAllowed: true
|
||||
darkMode: false
|
||||
scope: ""
|
||||
locale: "en"
|
||||
scope: "."
|
||||
lockPassword: false
|
||||
hideDotfiles: true
|
||||
permissions:
|
||||
create: true
|
||||
rename: true
|
||||
modify: true
|
||||
delete: true
|
||||
share: true
|
||||
download: true
|
||||
```
|
||||
|
||||
## About each configuration
|
||||
@@ -100,17 +110,13 @@ UserDefaults:
|
||||
|
||||
## About each configuration
|
||||
|
||||
- `Signup`: This boolean value indicates whether user signup is enabled.
|
||||
|
||||
- `AdminUsername`: This is the username of the admin user.
|
||||
|
||||
- `AdminPassword`: This is the password of the admin user.
|
||||
|
||||
### Server configuration settings
|
||||
|
||||
- `indexingInterval`: This is the time in minutes the system waits before checking for filesystem changes (used in search only).
|
||||
- `indexingInterval`: This is the time in minutes the system waits before checking for filesystem changes. Default: `5`
|
||||
|
||||
- `numImageProcessors`: This is the number of image processors available.
|
||||
- `indexing`: This enables or disables indexing. (Note: search will not work without indexing) Default: `true`
|
||||
|
||||
- `numImageProcessors`: This is the number of image processors available. Default: `4`
|
||||
|
||||
- `socket`: This is the socket configuration.
|
||||
|
||||
@@ -118,23 +124,27 @@ UserDefaults:
|
||||
|
||||
- `tlsCert`: This is the TLS certificate configuration.
|
||||
|
||||
- `enableThumbnails`: This boolean value determines whether thumbnails are enabled.
|
||||
- `enableThumbnails`: This boolean value determines whether thumbnails are enabled on ui. Default: `true`
|
||||
|
||||
- `resizePreview`: This boolean value determines whether preview resizing is enabled.
|
||||
- `resizePreview`: This boolean value determines whether preview resizing is enabled. Default: `false`
|
||||
|
||||
- `typeDetectionByHeader`: This boolean value determines whether type detection is based on headers.
|
||||
|
||||
- `port`: This is the port number on which the server is running.
|
||||
- `port`: This is the port number on which the server is running. Default: `80`
|
||||
|
||||
- `baseURL`: This is the base URL for the server.
|
||||
- `baseURL`: This is the base URL for the server. Default: `""`
|
||||
|
||||
- `address`: This is the server address configuration.
|
||||
- `address`: This is the server address configuration. Default: `0.0.0.0`
|
||||
|
||||
- `log`: This specifies the log destination (e.g., "stdout" for standard output).
|
||||
- `log`: This specifies the log destination. Default: `stdout`
|
||||
|
||||
- `database`: This is the database file path + filename that will be created if it does not already exist. If it exists, it will use the existing file.
|
||||
- `database`: This is the database file path + filename that will be created if it does not already exist. If it exists, it will use the existing file. Default `database.db`
|
||||
|
||||
- `root`: This is the root directory path.
|
||||
- `root`: This is the root directory path. Default: `/srv`
|
||||
|
||||
- `CreateUserDir`: Boolean to create user directory on user creation. Default: `false`
|
||||
|
||||
- `UserHomeBasePath`: String to define user home directory base path. Default: `""`
|
||||
|
||||
### Auth configuration settings
|
||||
|
||||
@@ -149,17 +159,22 @@ UserDefaults:
|
||||
- `header`: This is the authentication header.
|
||||
|
||||
- `method`: This is the authentication method used (e.g., "json"). Possible values:
|
||||
- password - username and password
|
||||
- hook - hook authentication
|
||||
- proxy - proxy authentication
|
||||
- oath - oath authentication
|
||||
- `password` - username and password
|
||||
- `hook` - hook authentication
|
||||
- `proxy` - proxy authentication
|
||||
- `oath` - oath authentication
|
||||
- `noauth` - no authentication/login required.
|
||||
|
||||
- `command`: This is the authentication command.
|
||||
|
||||
- `signup`: This boolean value indicates whether user signup is enabled.
|
||||
- `command`: Deprecated: This is the authentication command.
|
||||
|
||||
- `shell`: This is the shell configuration.
|
||||
|
||||
|
||||
- `Signup`: This boolean value indicates whether user signup is enabled on the login page. NOTE: Be mindful of `userDefaults` settings if enabled. Default: `false`
|
||||
|
||||
- `AdminUsername`: This is the username of the admin user. Default: `admin`
|
||||
|
||||
- `AdminPassword`: This is the password of the admin user. Default: `admin`
|
||||
|
||||
### Frontend configuration settings
|
||||
|
||||
- `name`: This is the name of the frontend.
|
||||
@@ -173,22 +188,26 @@ UserDefaults:
|
||||
- `theme`: This is the theme configuration.
|
||||
|
||||
- `color`: This is the color configuration.
|
||||
|
||||
|
||||
### UserDefaults configuration settings
|
||||
|
||||
- `darkMode`: Determines whether dark mode is enabled for the user (`true` or `false`)
|
||||
|
||||
- `settingsAllowed`: Determines whether settings page is enabled for the user (`true` or `false`)
|
||||
|
||||
- `scope`: This is a scope of the permissions, "." or "./" means all directories, "./downloads" would mean only the downloads folder.
|
||||
|
||||
- `locale`: This is the locale configuration.
|
||||
- `locale`: String locale configuration. Default: `en`
|
||||
|
||||
- `viewMode`: This is the view mode configuration.
|
||||
- `viewMode`: This is the view mode configuration. Possible values: `normal`, `compact`, `list`, and `gallery`. default: `normal`
|
||||
|
||||
- `singleClick`: This boolean value determines whether single-click is enabled.
|
||||
- `singleClick`: This boolean value determines whether single-click is enabled. (`true` or `false`)
|
||||
|
||||
- `sorting`:
|
||||
|
||||
- `by`: This is the sorting method used (e.g., "asc").
|
||||
|
||||
- `asc`: This boolean value determines the sorting order.
|
||||
- `asc`: This boolean value determines the sorting order is ascending or descending. (`true` or `false`)
|
||||
|
||||
- `permissions`:
|
||||
|
||||
@@ -208,9 +227,8 @@ UserDefaults:
|
||||
|
||||
- `download`: This boolean value determines whether download permissions are granted.
|
||||
|
||||
- `commands`: This is a list of commands.
|
||||
- `commands`: Deprecated: This is a list of commands.
|
||||
|
||||
- `hideDotfiles`: This boolean value determines whether dotfiles are hidden.
|
||||
|
||||
- `dateFormat`: This boolean value determines whether date formatting is enabled.
|
||||
- `hideDotfiles`: This boolean value determines whether dotfiles are hidden. (`true` or `false`)
|
||||
|
||||
- `dateFormat`: This boolean value determines whether date formatting is enabled. (`true` or `false`)
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/eslint-config-typescript"
|
||||
],
|
||||
"rules": {
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/no-mutating-props": [
|
||||
"error",
|
||||
{
|
||||
"shallowOnly": true
|
||||
}
|
||||
]
|
||||
// no-undef is already included in
|
||||
// @vue/eslint-config-typescript
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: ["@vue/app"],
|
||||
};
|
||||
Generated
-7843
File diff suppressed because it is too large.
Load diff
+29
-39
@@ -1,54 +1,44 @@
|
||||
{
|
||||
"name": "filebrowser-frontend",
|
||||
"version": "2.0.0",
|
||||
"version": "3.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"npm": ">=7.0.0",
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"fix": "npx vue-cli-service lint",
|
||||
"watch": "vue-cli-service build --watch",
|
||||
"lint": "eslint --ext .vue,.js src/",
|
||||
"lint:fix": "eslint --ext .vue,.js --fix src/",
|
||||
"format": "prettier --write ."
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"watch": "vite build --watch",
|
||||
"typecheck": "vue-tsc -p ./tsconfig.json --noEmit",
|
||||
"lint": "npm run typecheck && eslint src/",
|
||||
"lint:fix": "eslint --fix src/",
|
||||
"format": "prettier --write .",
|
||||
"test": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.24.2",
|
||||
"clipboard": "^2.0.4",
|
||||
"css-vars-ponyfill": "^2.4.3",
|
||||
"file-loader": "^6.2.0",
|
||||
"js-base64": "^2.5.1",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"material-icons": "^1.10.5",
|
||||
"moment": "^2.29.4",
|
||||
"normalize.css": "^8.0.1",
|
||||
"noty": "^3.2.0-beta",
|
||||
"pretty-bytes": "^6.0.0",
|
||||
"qrcode.vue": "^1.7.0",
|
||||
"utif": "^3.1.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-async-computed": "^3.9.0",
|
||||
"vue-i18n": "^8.15.3",
|
||||
"vue-lazyload": "^1.3.3",
|
||||
"vue-router": "^3.1.3",
|
||||
"vue-simple-progress": "^1.1.1",
|
||||
"vuex": "^3.1.2",
|
||||
"vuex-router-sync": "^5.0.0",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
"qrcode.vue": "^3.4.1",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.10.2",
|
||||
"vue-lazyload": "^3.0.0",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-service": "^5.0.8",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"autoprefixer": {}
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie < 11"
|
||||
]
|
||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-vue": "^9.24.0",
|
||||
"vite": "^5.2.7",
|
||||
"vite-plugin-compression2": "^1.0.0",
|
||||
"vue-tsc": "^2.0.7"
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M3245 6989 c-522 -39 -1042 -197 -1480 -449 -849 -488 -1459 -1308
|
||||
-1673 -2250 -177 -776 -89 -1582 250 -2301 368 -778 1052 -1418 1857 -1739
|
||||
903 -359 1927 -325 2812 92 778 368 1418 1052 1739 1857 359 903 325 1927 -92
|
||||
2812 -296 627 -806 1175 -1423 1529 -587 338 -1308 500 -1990 449z m555 -580
|
||||
c519 -51 1018 -245 1446 -565 788 -588 1229 -1526 1174 -2496 -16 -277 -58
|
||||
-500 -145 -763 -144 -440 -378 -819 -710 -1150 -452 -452 -1005 -730 -1655
|
||||
-832 -91 -14 -175 -18 -405 -18 -304 0 -369 6 -595 51 -1105 223 -1999 1092
|
||||
-2259 2197 -52 221 -73 412 -73 667 0 397 64 732 204 1080 304 752 886 1334
|
||||
1638 1638 431 174 895 238 1380 191z"/>
|
||||
<path d="M2670 5215 c0 -13 -44 -15 -335 -15 -352 0 -383 -3 -399 -45 -3 -9
|
||||
-6 -758 -6 -1663 0 -1168 -3 -1643 -11 -1632 -8 11 -9 8 -4 -15 3 -16 17 -41
|
||||
31 -55 l24 -25 1530 0 1530 0 24 25 c14 14 26 36 27 50 1 14 1 711 1 1550 l-2
|
||||
1526 -228 142 -229 142 -136 0 -137 0 0 -600 0 -600 -705 0 -705 0 0 615 0
|
||||
615 -135 0 c-113 0 -135 -2 -135 -15z m-264 -190 c57 -29 89 -71 103 -137 35
|
||||
-154 -98 -282 -258 -247 -55 12 -122 62 -148 113 -36 69 -12 186 49 243 62 58
|
||||
170 70 254 28z m2316 -1702 c17 -15 18 -49 18 -670 l0 -653 -1245 0 -1245 0 0
|
||||
654 c0 582 2 656 16 670 14 14 139 16 1226 16 1113 0 1213 -1 1230 -17z
|
||||
m-2602 -1363 c40 -40 13 -100 -43 -100 -60 0 -88 59 -47 100 11 11 31 20 45
|
||||
20 14 0 34 -9 45 -20z m2840 0 c41 -41 11 -100 -52 -100 -35 0 -58 24 -58 60
|
||||
0 54 71 79 110 40z"/>
|
||||
<path d="M2431 3091 c-7 -13 -7 -23 2 -35 11 -15 97 -16 1067 -14 l1055 3 0
|
||||
30 0 30 -1057 3 c-1023 2 -1058 1 -1067 -17z"/>
|
||||
<path d="M2436 2675 c-19 -19 -11 -41 17 -49 41 -11 2067 -7 2088 4 23 13 25
|
||||
46 3 54 -9 3 -483 6 -1054 6 -919 0 -1040 -2 -1054 -15z"/>
|
||||
<path d="M2447 2273 c-14 -4 -17 -13 -15 -36 l3 -32 1049 -3 c767 -1 1052 1
|
||||
1062 9 20 16 17 47 -5 59 -20 10 -2055 13 -2094 3z"/>
|
||||
<path d="M3822 5027 c-21 -23 -22 -30 -22 -293 0 -258 1 -271 20 -292 27 -29
|
||||
56 -35 140 -30 56 3 75 8 93 26 22 22 22 26 22 298 l0 276 -24 19 c-19 16 -40
|
||||
19 -115 19 -84 0 -95 -2 -114 -23z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -1,147 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xml:space="preserve"
|
||||
width="560"
|
||||
height="560"
|
||||
version="1.1"
|
||||
style="clip-rule:evenodd;fill-rule:evenodd;image-rendering:optimizeQuality;shape-rendering:geometricPrecision;text-rendering:geometricPrecision"
|
||||
viewBox="0 0 560 560"
|
||||
id="svg44"
|
||||
sodipodi:docname="icon_raw.svg"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||
inkscape:export-filename="/home/umarcor/filebrowser/logo/icon_raw.svg.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"><metadata
|
||||
id="metadata48"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="711"
|
||||
id="namedview46"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.33714286"
|
||||
inkscape:cx="-172.33051"
|
||||
inkscape:cy="280"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="20"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg44" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
<![CDATA[
|
||||
.fil1 {fill:#FEFEFE}
|
||||
.fil6 {fill:#006498}
|
||||
.fil7 {fill:#0EA5EB}
|
||||
.fil8 {fill:#2979FF}
|
||||
.fil3 {fill:#2BBCFF}
|
||||
.fil0 {fill:#455A64}
|
||||
.fil4 {fill:#53C6FC}
|
||||
.fil5 {fill:#BDEAFF}
|
||||
.fil2 {fill:#332C2B;fill-opacity:0.149020}
|
||||
]]>
|
||||
</style>
|
||||
</defs>
|
||||
<g
|
||||
id="g85"
|
||||
transform="translate(-70,-70)"><path
|
||||
class="fil1"
|
||||
d="M 350,71 C 504,71 629,196 629,350 629,504 504,629 350,629 196,629 71,504 71,350 71,196 196,71 350,71 Z"
|
||||
id="path9"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#fefefe" /><path
|
||||
class="fil2"
|
||||
d="M 475,236 593,387 C 596,503 444,639 301,585 L 225,486 339,330 c 0,0 138,-95 136,-94 z"
|
||||
id="path11"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#332c2b;fill-opacity:0.14902003" /><path
|
||||
class="fil3"
|
||||
d="m 231,211 h 208 l 38,24 v 246 c 0,5 -3,8 -8,8 H 231 c -5,0 -8,-3 -8,-8 V 219 c 0,-5 3,-8 8,-8 z"
|
||||
id="path13"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#2bbcff" /><path
|
||||
class="fil4"
|
||||
d="m 231,211 h 208 l 38,24 v 2 L 440,214 H 231 c -4,0 -7,3 -7,7 v 263 c -1,-1 -1,-2 -1,-3 V 219 c 0,-5 3,-8 8,-8 z"
|
||||
id="path15"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#53c6fc" /><polygon
|
||||
class="fil5"
|
||||
points="305,212 418,212 418,310 305,310 "
|
||||
id="polygon17"
|
||||
style="fill:#bdeaff" /><path
|
||||
class="fil5"
|
||||
d="m 255,363 h 189 c 3,0 5,2 5,4 V 483 H 250 V 367 c 0,-2 2,-4 5,-4 z"
|
||||
id="path19"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#bdeaff" /><polygon
|
||||
class="fil6"
|
||||
points="250,470 449,470 449,483 250,483 "
|
||||
id="polygon21"
|
||||
style="fill:#006498" /><path
|
||||
class="fil6"
|
||||
d="m 380,226 h 10 c 3,0 6,2 6,5 v 40 c 0,3 -3,6 -6,6 h -10 c -3,0 -6,-3 -6,-6 v -40 c 0,-3 3,-5 6,-5 z"
|
||||
id="path23"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#006498" /><path
|
||||
class="fil1"
|
||||
d="m 254,226 c 10,0 17,7 17,17 0,9 -7,16 -17,16 -9,0 -17,-7 -17,-16 0,-10 8,-17 17,-17 z"
|
||||
id="path25"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#fefefe" /><path
|
||||
class="fil6"
|
||||
d="m 267,448 h 165 c 2,0 3,1 3,3 v 0 c 0,1 -1,3 -3,3 H 267 c -2,0 -3,-2 -3,-3 v 0 c 0,-2 1,-3 3,-3 z"
|
||||
id="path27"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#006498" /><path
|
||||
class="fil6"
|
||||
d="m 267,415 h 165 c 2,0 3,1 3,3 v 0 c 0,1 -1,2 -3,2 H 267 c -2,0 -3,-1 -3,-2 v 0 c 0,-2 1,-3 3,-3 z"
|
||||
id="path29"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#006498" /><path
|
||||
class="fil6"
|
||||
d="m 267,381 h 165 c 2,0 3,2 3,3 v 0 c 0,2 -1,3 -3,3 H 267 c -2,0 -3,-1 -3,-3 v 0 c 0,-1 1,-3 3,-3 z"
|
||||
id="path31"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#006498" /><path
|
||||
class="fil1"
|
||||
d="m 236,472 c 3,0 5,2 5,5 0,2 -2,4 -5,4 -3,0 -5,-2 -5,-4 0,-3 2,-5 5,-5 z"
|
||||
id="path33"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#fefefe" /><path
|
||||
class="fil1"
|
||||
d="m 463,472 c 3,0 5,2 5,5 0,2 -2,4 -5,4 -3,0 -5,-2 -5,-4 0,-3 2,-5 5,-5 z"
|
||||
id="path35"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#fefefe" /><polygon
|
||||
class="fil6"
|
||||
points="305,212 284,212 284,310 305,310 "
|
||||
id="polygon37"
|
||||
style="fill:#006498" /><path
|
||||
class="fil7"
|
||||
d="m 477,479 v 2 c 0,5 -3,8 -8,8 H 231 c -5,0 -8,-3 -8,-8 v -2 c 0,4 3,8 8,8 h 238 c 5,0 8,-4 8,-8 z"
|
||||
id="path39"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#0ea5eb" /><path
|
||||
class="fil8"
|
||||
d="M 350,70 C 505,70 630,195 630,350 630,505 505,630 350,630 195,630 70,505 70,350 70,195 195,70 350,70 Z m 0,46 C 479,116 584,221 584,350 584,479 479,584 350,584 221,584 116,479 116,350 116,221 221,116 350,116 Z"
|
||||
id="path41"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#2979ff" /></g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 5.4 KiB |
@@ -6,7 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
|
||||
[{[ if .ReCaptcha -]}]
|
||||
<script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit"></script>
|
||||
<script src="[{[ .ReCaptchaHost ]}]/recaptcha/api.js?render=explicit" data-vite-ignore></script>
|
||||
[{[ end ]}]
|
||||
|
||||
<title>[{[ if .Name -]}][{[ .Name ]}][{[ else ]}]File Browser[{[ end ]}]</title>
|
||||
@@ -120,17 +120,24 @@
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
<div id="loading">
|
||||
[{[ if .darkMode -]}]
|
||||
<div id="loading" class="dark-mode">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
</div>
|
||||
[{[ else ]}]
|
||||
<div id="loading">
|
||||
<div class="spinner">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
</div> [{[ end ]}]
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
|
||||
[{[ if .Theme -]}]
|
||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/themes/[{[ .Theme ]}].css" />
|
||||
[{[ end ]}]
|
||||
[{[ if .CSS -]}]
|
||||
<link rel="stylesheet" href="[{[ .StaticURL ]}]/custom.css" />
|
||||
[{[ end ]}]
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
:root {
|
||||
--background: #141D24;
|
||||
--surfacePrimary: #20292F;
|
||||
--surfaceSecondary: #3A4147;
|
||||
--divider: rgba(255, 255, 255, 0.12);
|
||||
--textPrimary: rgba(255, 255, 255, 0.87);
|
||||
--textSecondary: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
#loading {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
#login {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
header {
|
||||
background: var(--surfacePrimary);
|
||||
}
|
||||
|
||||
@supports (backdrop-filter: none) {
|
||||
header {
|
||||
background: transparent;
|
||||
backdrop-filter: blur(16px) invert(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
#search #input {
|
||||
background: var(--surfaceSecondary);
|
||||
border-color: var(--surfaceSecondary);
|
||||
}
|
||||
#search #input input::placeholder {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
#search.active #input {
|
||||
background: var(--surfacePrimary);
|
||||
border-color: white;
|
||||
}
|
||||
#search.active input {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
#search #result {
|
||||
background: var(--background);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
#search .boxes h3 {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
.action {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
.action:hover {
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
.action .counter {
|
||||
border-color: var(--surfacePrimary);
|
||||
}
|
||||
|
||||
nav > div {
|
||||
border-color: var(--divider);
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
border-color: var(--divider);
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
.breadcrumbs span {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
.breadcrumbs a:hover {
|
||||
background-color: rgba(255, 255, 255, .1);
|
||||
}
|
||||
|
||||
#listing .item {
|
||||
background: var(--surfacePrimary);
|
||||
color: var(--textPrimary);
|
||||
border-color: var(--divider) !important;
|
||||
}
|
||||
|
||||
#listing .item .modified {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
#listing h2,
|
||||
#listing.list .header span {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
#listing.list .header span {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
#listing.list .item.header {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
.message {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--surfacePrimary);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
.button--flat:hover {
|
||||
background: var(--surfaceSecondary);
|
||||
}
|
||||
|
||||
.dashboard #nav ul li {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
.dashboard #nav ul li:hover {
|
||||
background: var(--surfaceSecondary);
|
||||
}
|
||||
#result-list {
|
||||
background-color:#292929;
|
||||
}
|
||||
|
||||
.card h3,
|
||||
.dashboard #nav,
|
||||
.dashboard p label {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
.card#share input,
|
||||
.card#share select,
|
||||
.input {
|
||||
background: var(--surfaceSecondary);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
.input:hover,
|
||||
.input:focus {
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
.input--red {
|
||||
background: #73302D;
|
||||
}
|
||||
|
||||
.input--green {
|
||||
background: #147A41;
|
||||
}
|
||||
|
||||
.dashboard #nav .wrapper,
|
||||
.collapsible {
|
||||
border-color: var(--divider);
|
||||
}
|
||||
.collapsible > label * {
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
table th {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
.file-list li:hover {
|
||||
background: var(--surfaceSecondary);
|
||||
}
|
||||
.file-list li:before {
|
||||
color: var(--textSecondary);
|
||||
}
|
||||
|
||||
.shell {
|
||||
background: var(--surfacePrimary);
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
.shell__result {
|
||||
border-top: 1px solid var(--divider);
|
||||
}
|
||||
|
||||
#editor-container {
|
||||
background: var(--background);
|
||||
}
|
||||
|
||||
#editor-container .bar {
|
||||
background: var(--surfacePrimary);
|
||||
}
|
||||
nav {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
}
|
||||
|
||||
#file-selection {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
}
|
||||
#file-selection span {
|
||||
color: var(--textPrimary) !important;
|
||||
}
|
||||
#dropdown {
|
||||
background: var(--surfaceSecondary) !important;
|
||||
}
|
||||
|
||||
.share__box {
|
||||
background: var(--surfacePrimary) !important;
|
||||
color: var(--textPrimary);
|
||||
}
|
||||
|
||||
.share__box__element {
|
||||
border-top-color: var(--divider);
|
||||
}
|
||||
|
||||
.helpButton {
|
||||
background: var(--background);
|
||||
}
|
||||
.sizeInputWrapper {
|
||||
background: var(--background);
|
||||
color: white
|
||||
}
|
||||
.button-group button {
|
||||
background: var(--background);
|
||||
color: white
|
||||
}
|
||||
#result-desktop #result-list {
|
||||
background: #2a3137;
|
||||
max-height: unset;
|
||||
}
|
||||
+14
-9
@@ -3,22 +3,27 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_public_path__ = window.FileBrowser.StaticURL + "/";
|
||||
import { onMounted } from 'vue';
|
||||
import { mutations } from "@/store"; // Import your store's mutations
|
||||
|
||||
export default {
|
||||
name: "app",
|
||||
mounted() {
|
||||
const loading = document.getElementById("loading");
|
||||
loading.classList.add("done");
|
||||
|
||||
setTimeout(function () {
|
||||
loading.parentNode.removeChild(loading);
|
||||
}, 200);
|
||||
computed: {},
|
||||
setup() {
|
||||
onMounted(() => {
|
||||
mutations.setLoading(false); // Call your mutation or method to set loading to false
|
||||
// Query the loading element and remove it from the DOM
|
||||
const loadingDiv = document.getElementById('loading');
|
||||
if (loadingDiv) {
|
||||
loadingDiv.remove();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Always load styles.css */
|
||||
@import "./css/styles.css";
|
||||
@import "./css/dark.css";
|
||||
</style>
|
||||
@@ -1,13 +1,13 @@
|
||||
import { removePrefix } from "./utils";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import store from "@/store";
|
||||
import { state } from "@/store";
|
||||
|
||||
const ssl = window.location.protocol === "https:";
|
||||
const protocol = ssl ? "wss:" : "ws:";
|
||||
|
||||
export default function command(url, command, onmessage, onclose) {
|
||||
url = removePrefix(url);
|
||||
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${store.state.jwt}`;
|
||||
url = `${protocol}//${window.location.host}${baseURL}/api/command${url}?auth=${state.jwt}`;
|
||||
|
||||
let conn = new window.WebSocket(url);
|
||||
conn.onopen = () => conn.send(command);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { createURL, fetchURL, removePrefix } from "./utils";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import store from "@/store";
|
||||
import { state } from "@/store";
|
||||
|
||||
export async function fetch(url) {
|
||||
export async function fetch(url,content=false) {
|
||||
url = removePrefix(url);
|
||||
|
||||
const res = await fetchURL(`/api/resources${url}`, {});
|
||||
const res = await fetchURL(`/api/resources${url}?content=${content}`, {});
|
||||
|
||||
let data = await res.json();
|
||||
data.url = `/files${url}`;
|
||||
@@ -70,8 +70,8 @@ export function download(format, ...files) {
|
||||
url += `algo=${format}&`;
|
||||
}
|
||||
|
||||
if (store.state.jwt) {
|
||||
url += `auth=${store.state.jwt}&`;
|
||||
if (state.jwt) {
|
||||
url += `auth=${state.jwt}&`;
|
||||
}
|
||||
|
||||
window.open(url);
|
||||
@@ -95,7 +95,7 @@ export async function post(url, content = "", overwrite = false, onupload) {
|
||||
`${baseURL}/api/resources${url}?override=${overwrite}`,
|
||||
true
|
||||
);
|
||||
request.setRequestHeader("X-Auth", store.state.jwt);
|
||||
request.setRequestHeader("X-Auth", state.jwt);
|
||||
|
||||
if (typeof onupload === "function") {
|
||||
request.upload.onprogress = onupload;
|
||||
|
||||
+32
-14
@@ -1,20 +1,24 @@
|
||||
import { fetchURL, removePrefix, createURL } from "./utils";
|
||||
import { removePrefix, createURL } from "./utils";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
|
||||
export async function fetch(url, password = "") {
|
||||
export async function fetchPub(url, password = "") {
|
||||
url = removePrefix(url);
|
||||
|
||||
const res = await fetchURL(
|
||||
`/api/public/share${url}`,
|
||||
{
|
||||
headers: { "X-SHARE-PASSWORD": encodeURIComponent(password) },
|
||||
},
|
||||
false
|
||||
const res = await fetch(
|
||||
`/api/public/share${url}`,
|
||||
{
|
||||
headers: {
|
||||
"X-SHARE-PASSWORD": encodeURIComponent(password),
|
||||
},
|
||||
}
|
||||
);
|
||||
if (res.status != 200) {
|
||||
const error = new Error("000 No connection");
|
||||
error.status = res.status;
|
||||
throw error;
|
||||
}
|
||||
|
||||
let data = await res.json();
|
||||
data.url = `/share${url}`;
|
||||
|
||||
if (data.isDir) {
|
||||
if (!data.url.endsWith("/")) data.url += "/";
|
||||
data.items = data.items.map((item, index) => {
|
||||
@@ -34,12 +38,10 @@ export async function fetch(url, password = "") {
|
||||
|
||||
export function download(format, hash, token, ...files) {
|
||||
let url = `${baseURL}/api/public/dl/${hash}`;
|
||||
|
||||
if (files.length === 1) {
|
||||
url += encodeURIComponent(files[0]) + "?";
|
||||
} else {
|
||||
let arg = "";
|
||||
|
||||
for (let file of files) {
|
||||
arg += encodeURIComponent(file) + ",";
|
||||
}
|
||||
@@ -60,11 +62,27 @@ export function download(format, hash, token, ...files) {
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
export function getPublicUser() {
|
||||
return fetch("/api/public/publicUser")
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error fetching public user:", error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
export function getDownloadURL(share, inline = false) {
|
||||
const params = {
|
||||
...(inline && { inline: "true" }),
|
||||
...(share.token && { token: share.token }),
|
||||
};
|
||||
|
||||
return createURL("api/public/dl/" + share.hash + share.path, params, false);
|
||||
if (share.path == undefined) {
|
||||
share.path = ""
|
||||
}
|
||||
return createURL("api/public/dl/" + share.hash + "/"+share.path, params, false);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ export async function remove(hash) {
|
||||
export async function create(url, password = "", expires = "", unit = "hours") {
|
||||
url = removePrefix(url);
|
||||
url = `/api/share${url}`;
|
||||
expires = String(expires);
|
||||
if (expires !== "") {
|
||||
url += `?expires=${expires}&unit=${unit}`;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { fetchURL, fetchJSON } from "./utils";
|
||||
import { fetchURL, fetchJSON } from "@/api/utils";
|
||||
|
||||
export async function getAll() {
|
||||
return fetchJSON(`/api/users`, {});
|
||||
export async function getAllUsers() {
|
||||
return await fetchJSON(`/api/users`, {});
|
||||
}
|
||||
|
||||
export async function get(id) {
|
||||
|
||||
+11
-10
@@ -1,7 +1,8 @@
|
||||
import store from "@/store";
|
||||
import { state } from "@/store";
|
||||
import { renew, logout } from "@/utils/auth";
|
||||
import { baseURL } from "@/utils/constants";
|
||||
import { encodePath } from "@/utils/url";
|
||||
import { showError } from "@/notify";
|
||||
|
||||
export async function fetchURL(url, opts, auth = true) {
|
||||
opts = opts || {};
|
||||
@@ -13,21 +14,22 @@ export async function fetchURL(url, opts, auth = true) {
|
||||
try {
|
||||
res = await fetch(`${baseURL}${url}`, {
|
||||
headers: {
|
||||
"X-Auth": store.state.jwt,
|
||||
"sessionId": store.state.sessionId,
|
||||
"X-Auth": state.jwt,
|
||||
"sessionId": state.sessionId,
|
||||
"userScope": state.user.scope,
|
||||
...headers,
|
||||
},
|
||||
...rest,
|
||||
});
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
const error = new Error("000 No connection");
|
||||
error.status = 0;
|
||||
|
||||
error.status = res.status;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (auth && res.headers.get("X-Renew-Token") === "true") {
|
||||
await renew(store.state.jwt);
|
||||
await renew(state.jwt);
|
||||
}
|
||||
|
||||
if (res.status < 200 || res.status > 299) {
|
||||
@@ -46,17 +48,16 @@ export async function fetchURL(url, opts, auth = true) {
|
||||
|
||||
export async function fetchJSON(url, opts) {
|
||||
const res = await fetchURL(url, opts);
|
||||
|
||||
if (res.status === 200) {
|
||||
return res.json();
|
||||
} else {
|
||||
showError("unable to fetch : " + url + "status" + res.status);
|
||||
throw new Error(res.status);
|
||||
}
|
||||
}
|
||||
|
||||
export function removePrefix(url) {
|
||||
url = url.split("/").splice(2).join("/");
|
||||
|
||||
if (url === "") url = "/";
|
||||
if (url[0] !== "/") url = "/" + url;
|
||||
return url;
|
||||
@@ -70,7 +71,7 @@ export function createURL(endpoint, params = {}, auth = true) {
|
||||
const url = new URL(prefix + encodePath(endpoint), origin);
|
||||
|
||||
const searchParams = {
|
||||
...(auth && { auth: store.state.jwt }),
|
||||
...(auth && { auth: state.jwt }),
|
||||
...params,
|
||||
};
|
||||
|
||||
|
||||
@@ -10,21 +10,26 @@
|
||||
</component>
|
||||
|
||||
<span v-for="(link, index) in items" :key="index">
|
||||
<span class="chevron"
|
||||
><i class="material-icons">keyboard_arrow_right</i></span
|
||||
>
|
||||
<span class="chevron"><i class="material-icons">keyboard_arrow_right</i></span>
|
||||
<component :is="element" :to="link.url">{{ link.name }}</component>
|
||||
</span>
|
||||
<action style="display: contents" v-if="showShare" icon="share" show="share" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { state, mutations } from "@/store"; // Import mutations as well
|
||||
import Action from "@/components/header/Action.vue";
|
||||
|
||||
export default {
|
||||
name: "breadcrumbs",
|
||||
components: {
|
||||
Action,
|
||||
},
|
||||
props: ["base", "noLink"],
|
||||
computed: {
|
||||
items() {
|
||||
const relativePath = this.$route.path.replace(this.base, "");
|
||||
const relativePath = state.route.path.replace(this.base, "");
|
||||
let parts = relativePath.split("/");
|
||||
|
||||
if (parts[0] === "") {
|
||||
@@ -68,8 +73,19 @@ export default {
|
||||
|
||||
return "router-link";
|
||||
},
|
||||
showShare() {
|
||||
// Ensure user properties are accessed safely
|
||||
if (state.route.path.startsWith("/share")) {
|
||||
return false;
|
||||
}
|
||||
return state.user?.perm && state.user?.perm.share; // Access from state directly
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// Example of a method using mutations
|
||||
updateUserPermissions(newPerms) {
|
||||
mutations.updateUser({ perm: newPerms })
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<div class="button-group">
|
||||
<button v-for="(btn, index) in buttons" :key="index" :disabled="isDisabled" :class="{ active: activeButton === index && !isDisabled }"
|
||||
@click="setActiveButton(index, btn.label)">
|
||||
<button
|
||||
v-for="(btn, index) in buttons"
|
||||
:key="index"
|
||||
:disabled="isDisabled"
|
||||
:class="{ active: activeButton === index && !isDisabled }"
|
||||
@click="setActiveButton(index, btn.label)"
|
||||
>
|
||||
{{ btn.label }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -16,7 +21,7 @@ export default {
|
||||
},
|
||||
isDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
initialActive: {
|
||||
type: Number,
|
||||
@@ -31,15 +36,12 @@ export default {
|
||||
methods: {
|
||||
setActiveButton(index, label) {
|
||||
if (label == "Only Folders" && this.activeButton != index) {
|
||||
console.log("Only Folders && this.activeButton != index")
|
||||
this.$emit("disableAll");
|
||||
}
|
||||
if (label == "Only Folders" && this.activeButton == index) {
|
||||
console.log("Only Folders && this.activeButton == index")
|
||||
this.$emit("enableAll");
|
||||
}
|
||||
if (label == "Only Files" && this.activeButton != index) {
|
||||
console.log("Only Files && this.activeButton != index")
|
||||
this.$emit("enableAll");
|
||||
}
|
||||
// If the clicked button is already active, de-select it
|
||||
@@ -72,7 +74,7 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.button-group {
|
||||
margin:1em;
|
||||
margin: 1em;
|
||||
display: flex;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 1em;
|
||||
@@ -93,7 +95,7 @@ button {
|
||||
}
|
||||
|
||||
/* Remove the border from the last button */
|
||||
.button-group>button:last-child {
|
||||
.button-group > button:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
@@ -102,7 +104,7 @@ button:hover {
|
||||
}
|
||||
|
||||
button.active {
|
||||
background-color: var(--blue);
|
||||
background-color: var(--blue) !important;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,221 @@
|
||||
<!-- This component taken directly from vue-simple-progress
|
||||
since it didnt support Vue 3 but the component itself does
|
||||
https://raw.githubusercontent.com/dzwillia/vue-simple-progress/master/src/components/Progress.vue -->
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="vue-simple-progress-text"
|
||||
:style="text_style"
|
||||
v-if="text.length > 0 && textPosition == 'top'"
|
||||
>
|
||||
{{ text }}
|
||||
</div>
|
||||
<div class="vue-simple-progress" :style="progress_style">
|
||||
<div
|
||||
class="vue-simple-progress-text"
|
||||
:style="text_style"
|
||||
v-if="text.length > 0 && textPosition == 'middle'"
|
||||
>
|
||||
{{ text }}
|
||||
</div>
|
||||
<div
|
||||
style="position: relative; left: -9999px"
|
||||
:style="text_style"
|
||||
v-if="text.length > 0 && textPosition == 'inside'"
|
||||
>
|
||||
{{ text }}
|
||||
</div>
|
||||
<div class="vue-simple-progress-bar" :style="bar_style">
|
||||
<div :style="text_style" v-if="text.length > 0 && textPosition == 'inside'">
|
||||
{{ text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="vue-simple-progress-text"
|
||||
:style="text_style"
|
||||
v-if="text.length > 0 && textPosition == 'bottom'"
|
||||
>
|
||||
{{ text }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// We're leaving this untouched as you can read in the beginning
|
||||
var isNumber = function (n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "progress-bar",
|
||||
props: {
|
||||
val: {
|
||||
default: 0,
|
||||
},
|
||||
max: {
|
||||
default: 100,
|
||||
},
|
||||
size: {
|
||||
// either a number (pixel width/height) or 'tiny', 'small',
|
||||
// 'medium', 'large', 'huge', 'massive' for common sizes
|
||||
default: 3,
|
||||
},
|
||||
"bg-color": {
|
||||
type: String,
|
||||
default: "#eee",
|
||||
},
|
||||
"bar-color": {
|
||||
type: String,
|
||||
default: "#2196f3", // match .blue color to Material Design's 'Blue 500' color
|
||||
},
|
||||
"bar-transition": {
|
||||
type: String,
|
||||
default: "all 0.5s ease",
|
||||
},
|
||||
"bar-border-radius": {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
spacing: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
"text-align": {
|
||||
type: String,
|
||||
default: "center", // 'left', 'right'
|
||||
},
|
||||
"text-position": {
|
||||
type: String,
|
||||
default: "bottom", // 'bottom', 'top', 'middle', 'inside'
|
||||
},
|
||||
"font-size": {
|
||||
type: Number,
|
||||
default: 13,
|
||||
},
|
||||
"text-fg-color": {
|
||||
type: String,
|
||||
default: "#222",
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
pct() {
|
||||
var pct = (this.val / this.max) * 100;
|
||||
pct = pct.toFixed(2);
|
||||
return Math.min(pct, this.max);
|
||||
},
|
||||
size_px() {
|
||||
switch (this.size) {
|
||||
case "tiny":
|
||||
return 2;
|
||||
case "small":
|
||||
return 4;
|
||||
case "medium":
|
||||
return 8;
|
||||
case "large":
|
||||
return 12;
|
||||
case "big":
|
||||
return 16;
|
||||
case "huge":
|
||||
return 32;
|
||||
case "massive":
|
||||
return 64;
|
||||
}
|
||||
|
||||
return isNumber(this.size) ? this.size : 32;
|
||||
},
|
||||
text_padding() {
|
||||
switch (this.size) {
|
||||
case "tiny":
|
||||
case "small":
|
||||
case "medium":
|
||||
case "large":
|
||||
case "big":
|
||||
case "huge":
|
||||
case "massive":
|
||||
return Math.min(Math.max(Math.ceil(this.size_px / 8), 3), 12);
|
||||
}
|
||||
|
||||
return isNumber(this.spacing) ? this.spacing : 4;
|
||||
},
|
||||
text_font_size() {
|
||||
switch (this.size) {
|
||||
case "tiny":
|
||||
case "small":
|
||||
case "medium":
|
||||
case "large":
|
||||
case "big":
|
||||
case "huge":
|
||||
case "massive":
|
||||
return Math.min(Math.max(Math.ceil(this.size_px * 1.4), 11), 32);
|
||||
}
|
||||
|
||||
return isNumber(this.fontSize) ? this.fontSize : 13;
|
||||
},
|
||||
progress_style() {
|
||||
var style = {
|
||||
background: this.bgColor,
|
||||
};
|
||||
|
||||
if (this.textPosition == "middle" || this.textPosition == "inside") {
|
||||
style["position"] = "relative";
|
||||
style["min-height"] = this.size_px + "px";
|
||||
style["z-index"] = "-2";
|
||||
}
|
||||
|
||||
if (this.barBorderRadius > 0) {
|
||||
style["border-radius"] = this.barBorderRadius + "px";
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
bar_style() {
|
||||
var style = {
|
||||
background: this.barColor,
|
||||
width: this.pct + "%",
|
||||
height: this.size_px + "px",
|
||||
transition: this.barTransition,
|
||||
};
|
||||
|
||||
if (this.barBorderRadius > 0) {
|
||||
style["border-radius"] = this.barBorderRadius + "px";
|
||||
}
|
||||
|
||||
if (this.textPosition == "middle" || this.textPosition == "inside") {
|
||||
style["position"] = "absolute";
|
||||
style["top"] = "0";
|
||||
style["height"] = "100%";
|
||||
(style["min-height"] = this.size_px + "px"), (style["z-index"] = "-1");
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
text_style() {
|
||||
var style = {
|
||||
color: this.textFgColor,
|
||||
"font-size": this.text_font_size + "px",
|
||||
"text-align": this.textAlign,
|
||||
};
|
||||
|
||||
if (
|
||||
this.textPosition == "top" ||
|
||||
this.textPosition == "middle" ||
|
||||
this.textPosition == "inside"
|
||||
)
|
||||
style["padding-bottom"] = this.text_padding + "px";
|
||||
if (
|
||||
this.textPosition == "bottom" ||
|
||||
this.textPosition == "middle" ||
|
||||
this.textPosition == "inside"
|
||||
)
|
||||
style["padding-top"] = this.text_padding + "px";
|
||||
|
||||
return style;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
+357
-269
@@ -1,20 +1,45 @@
|
||||
<template>
|
||||
<div id="search" @click="open" v-bind:class="{ active, ongoing }">
|
||||
<div id="search" @click="open" :class="{ active, ongoing, 'dark-mode': isDarkMode }">
|
||||
<!-- Search input section -->
|
||||
<div id="input">
|
||||
<button v-if="active" class="action" @click="close" :aria-label="$t('buttons.close')" :title="$t('buttons.close')">
|
||||
<!-- Close button visible when search is active -->
|
||||
<button
|
||||
v-if="active"
|
||||
class="action"
|
||||
@click="close"
|
||||
:aria-label="$t('buttons.close')"
|
||||
:title="$t('buttons.close')"
|
||||
>
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
<!-- Search icon when search is not active -->
|
||||
<i v-else class="material-icons">search</i>
|
||||
<input class="main-input" type="text" @keyup.exact="keyup" @input="submit" ref="input" :autofocus="active"
|
||||
v-model.trim="value" :aria-label="$t('search.search')" :placeholder="$t('search.search')" />
|
||||
<!-- Input field for search -->
|
||||
<input
|
||||
class="main-input"
|
||||
type="text"
|
||||
@keyup.exact="keyup"
|
||||
@input="submit"
|
||||
ref="input"
|
||||
:autofocus="active"
|
||||
v-model.trim="value"
|
||||
:aria-label="$t('search.search')"
|
||||
:placeholder="$t('search.search')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Search results for mobile -->
|
||||
<div v-if="isMobile && active" id="result" :class="{ hidden: !active }" ref="result">
|
||||
<div id="result-list">
|
||||
<div class="button" style="width: 100%">
|
||||
Search Context: {{ getContext(this.$route.path) }}
|
||||
</div>
|
||||
<div class="button" style="width: 100%">Search Context: {{ getContext }}</div>
|
||||
<!-- List of search results -->
|
||||
<ul v-show="results.length > 0">
|
||||
<li v-for="(s, k) in results" :key="k" @click.stop.prevent="navigateTo(s.url)" style="cursor: pointer">
|
||||
<li
|
||||
v-for="(s, k) in results"
|
||||
:key="k"
|
||||
@click.stop.prevent="navigateTo(s.url)"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<router-link to="#" event="">
|
||||
<i v-if="s.dir" class="material-icons folder-icons"> folder </i>
|
||||
<i v-else-if="s.audio" class="material-icons audio-icons"> volume_up </i>
|
||||
@@ -23,49 +48,69 @@
|
||||
<i v-else-if="s.archive" class="material-icons archive-icons"> archive </i>
|
||||
<i v-else class="material-icons file-icons"> insert_drive_file </i>
|
||||
<span class="text-container">
|
||||
{{ basePath(s.path) }}<b>{{ baseName(s.path) }}</b>
|
||||
{{ basePath(s.path, s.dir) }}<b>{{ baseName(s.path) }}</b>
|
||||
</span>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Loading icon when search is ongoing -->
|
||||
<p v-show="isEmpty && isRunning" id="renew">
|
||||
<i class="material-icons spin">autorenew</i>
|
||||
</p>
|
||||
<!-- Message when no results are found -->
|
||||
<div v-show="isEmpty && !isRunning">
|
||||
<div class="searchPrompt" v-show="isEmpty && !isRunning">
|
||||
<p>{{ noneMessage }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="isEmpty">
|
||||
<button class="mobile-boxes" v-if="value.length === 0 && !showBoxes" @click="resetSearchFilters()">
|
||||
<div v-if="isEmpty">
|
||||
<!-- Reset filters button -->
|
||||
<button
|
||||
class="mobile-boxes"
|
||||
v-if="value.length === 0 && !showBoxes"
|
||||
@click="resetSearchFilters()"
|
||||
>
|
||||
Reset filters
|
||||
</button>
|
||||
<template v-if="value.length === 0 && showBoxes">
|
||||
<!-- Box types when no search input is present -->
|
||||
<div v-if="value.length === 0 && showBoxes">
|
||||
<div class="boxes">
|
||||
<h3>{{ $t("search.types") }}</h3>
|
||||
<div>
|
||||
<div class="mobile-boxes" tabindex="0" v-for="(v, k) in boxes" :key="k" role="button"
|
||||
@click="addToTypes('type:' + k)" :aria-label="v.label">
|
||||
<div
|
||||
class="mobile-boxes"
|
||||
tabindex="0"
|
||||
v-for="(v, k) in boxes"
|
||||
:key="k"
|
||||
role="button"
|
||||
@click="addToTypes('type:' + k)"
|
||||
:aria-label="v.label"
|
||||
>
|
||||
<i class="material-icons">{{ v.icon }}</i>
|
||||
<p>{{ v.label }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search results for desktop -->
|
||||
<div v-show="!isMobile && active" id="result-desktop" ref="result">
|
||||
<div class="searchContext">Search Context: {{ getContext(this.$route.path) }}</div>
|
||||
<div class="searchContext">Search Context: {{ getContext }}</div>
|
||||
<div id="result-list">
|
||||
<template>
|
||||
<div>
|
||||
<!-- Loading icon when search is ongoing -->
|
||||
<p v-show="isEmpty && isRunning" id="renew">
|
||||
<i class="material-icons spin">autorenew</i>
|
||||
</p>
|
||||
<!-- Message when no results are found -->
|
||||
<div class="searchPrompt" v-show="isEmpty && !isRunning">
|
||||
<p>{{ noneMessage }}</p>
|
||||
<div class="helpButton" @click="toggleHelp()">Help</div>
|
||||
</div>
|
||||
<!-- Help text section -->
|
||||
<div class="helpText" v-if="showHelp">
|
||||
<p>
|
||||
Search occurs on each character you type (3 character minimum for search
|
||||
@@ -89,27 +134,55 @@
|
||||
search times.
|
||||
</p>
|
||||
</div>
|
||||
<template>
|
||||
<ButtonGroup :buttons="folderSelect" @button-clicked="addToTypes" @remove-button-clicked="removeFromTypes"
|
||||
@disableAll="folderSelectClicked()" @enableAll="resetButtonGroups()" />
|
||||
<ButtonGroup :buttons="typeSelect" @button-clicked="addToTypes" @remove-button-clicked="removeFromTypes"
|
||||
:isDisabled="isTypeSelectDisabled" />
|
||||
<div>
|
||||
<!-- Button groups for filtering search results -->
|
||||
<ButtonGroup
|
||||
:buttons="folderSelect"
|
||||
@button-clicked="addToTypes"
|
||||
@remove-button-clicked="removeFromTypes"
|
||||
@disableAll="folderSelectClicked()"
|
||||
@enableAll="resetButtonGroups()"
|
||||
/>
|
||||
<ButtonGroup
|
||||
:buttons="typeSelect"
|
||||
@button-clicked="addToTypes"
|
||||
@remove-button-clicked="removeFromTypes"
|
||||
:isDisabled="isTypeSelectDisabled"
|
||||
/>
|
||||
<!-- Inputs for filtering by file size -->
|
||||
<div class="sizeConstraints">
|
||||
<div class="sizeInputWrapper">
|
||||
<p>Smaller Than:</p>
|
||||
<input class="sizeInput" v-model="smallerThan" type="number" min="0" placeholder="number" />
|
||||
<input
|
||||
class="sizeInput"
|
||||
v-model="smallerThan"
|
||||
type="number"
|
||||
min="0"
|
||||
placeholder="number"
|
||||
/>
|
||||
<p>MB</p>
|
||||
</div>
|
||||
<div class="sizeInputWrapper">
|
||||
<p>Larger Than:</p>
|
||||
<input class="sizeInput" v-model="largerThan" type="number" placeholder="number" />
|
||||
<input
|
||||
class="sizeInput"
|
||||
v-model="largerThan"
|
||||
type="number"
|
||||
placeholder="number"
|
||||
/>
|
||||
<p>MB</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<!-- List of search results -->
|
||||
<ul v-show="results.length > 0">
|
||||
<li v-for="(s, k) in results" :key="k" @click.stop.prevent="navigateTo(s.url)" style="cursor: pointer">
|
||||
<li
|
||||
v-for="(s, k) in results"
|
||||
:key="k"
|
||||
@click.stop.prevent="navigateTo(s.url)"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<router-link to="#" event="">
|
||||
<i v-if="s.dir" class="material-icons folder-icons"> folder </i>
|
||||
<i v-else-if="s.audio" class="material-icons audio-icons"> volume_up </i>
|
||||
@@ -118,7 +191,7 @@
|
||||
<i v-else-if="s.archive" class="material-icons archive-icons"> archive </i>
|
||||
<i v-else class="material-icons file-icons"> insert_drive_file </i>
|
||||
<span class="text-container">
|
||||
{{ basePath(s.path) }}<b>{{ baseName(s.path) }}</b>
|
||||
{{ basePath(s.path, s.dir) }}<b>{{ baseName(s.path) }}</b>
|
||||
</span>
|
||||
</router-link>
|
||||
</li>
|
||||
@@ -127,6 +200,246 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import ButtonGroup from "./ButtonGroup.vue";
|
||||
import { search } from "@/api";
|
||||
import { getters, mutations, state } from "@/store";
|
||||
import { showError } from "@/notify";
|
||||
|
||||
var boxes = {
|
||||
folder: { label: "folders", icon: "folder" },
|
||||
file: { label: "files", icon: "insert_drive_file" },
|
||||
archive: { label: "archives", icon: "archive" },
|
||||
image: { label: "images", icon: "photo" },
|
||||
audio: { label: "audio files", icon: "volume_up" },
|
||||
video: { label: "videos", icon: "movie" },
|
||||
doc: { label: "documents", icon: "picture_as_pdf" },
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ButtonGroup,
|
||||
},
|
||||
name: "search",
|
||||
data: function () {
|
||||
return {
|
||||
largerThan: "",
|
||||
smallerThan: "",
|
||||
noneMessage: "Start typing 3 or more characters to begin searching.",
|
||||
searchTypes: "",
|
||||
isTypeSelectDisabled: false,
|
||||
showHelp: false,
|
||||
folderSelect: [
|
||||
{ label: "Only Folders", value: "type:folder" },
|
||||
{ label: "Only Files", value: "type:file" },
|
||||
],
|
||||
typeSelect: [
|
||||
{ label: "Photos", value: "type:image" },
|
||||
{ label: "Audio", value: "type:audio" },
|
||||
{ label: "Videos", value: "type:video" },
|
||||
{ label: "Documents", value: "type:doc" },
|
||||
{ label: "Archives", value: "type:archive" },
|
||||
],
|
||||
value: "",
|
||||
width: window.innerWidth,
|
||||
ongoing: false,
|
||||
results: [],
|
||||
reload: false,
|
||||
scrollable: null,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
active(active) {
|
||||
const resultList = document.getElementById("result-list");
|
||||
if (!active) {
|
||||
resultList.classList.remove("active");
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
resultList.classList.add("active");
|
||||
}, 100);
|
||||
},
|
||||
currentPrompt(val, old) {
|
||||
this.active = val?.prompt === "search";
|
||||
if (old?.prompt === "search" && !this.active) {
|
||||
if (this.reload) {
|
||||
this.setReload(true);
|
||||
}
|
||||
|
||||
document.body.style.overflow = "auto";
|
||||
this.ongoing = false;
|
||||
this.results = [];
|
||||
this.value = "";
|
||||
this.active = false;
|
||||
this.$refs.input.blur();
|
||||
} else if (this.active) {
|
||||
this.reload = false;
|
||||
this.$refs.input.focus();
|
||||
document.body.style.overflow = "hidden";
|
||||
}
|
||||
},
|
||||
value() {
|
||||
if (this.results.length) {
|
||||
this.ongoing = false;
|
||||
this.results = [];
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
active() {
|
||||
return getters.currentPromptName() === "search";
|
||||
},
|
||||
showOverlay() {
|
||||
return getters.currentPrompt() !== null && getters.currentPromptName() !== "more";
|
||||
},
|
||||
isDarkMode() {
|
||||
return getters.isDarkMode();
|
||||
},
|
||||
showBoxes() {
|
||||
return this.searchTypes == "";
|
||||
},
|
||||
boxes() {
|
||||
return boxes;
|
||||
},
|
||||
isEmpty() {
|
||||
return this.results.length === 0;
|
||||
},
|
||||
text() {
|
||||
if (this.ongoing) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return this.value === ""
|
||||
? this.$t("search.typeToSearch")
|
||||
: this.$t("search.pressToSearch");
|
||||
},
|
||||
isMobile() {
|
||||
return this.width <= 800;
|
||||
},
|
||||
isRunning() {
|
||||
return this.ongoing;
|
||||
},
|
||||
searchHelp() {
|
||||
return this.showHelp;
|
||||
},
|
||||
getContext() {
|
||||
let path = state.route.path;
|
||||
path = path.slice(1);
|
||||
path = "./" + path.substring(path.indexOf("/") + 1);
|
||||
path = path.replace(/\/+$/, "") + "/";
|
||||
return path;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleResize() {
|
||||
this.width = window.innerWidth;
|
||||
},
|
||||
async navigateTo(url) {
|
||||
mutations.closeHovers();
|
||||
await this.$nextTick();
|
||||
setTimeout(() => this.$router.push(url), 10);
|
||||
},
|
||||
basePath(str, isDir) {
|
||||
let parts = str.replace(/(\/$|^\/)/, "").split("/");
|
||||
if (parts.length <= 1) {
|
||||
if (isDir) {
|
||||
return "/";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
parts.pop();
|
||||
parts = parts.join("/") + "/";
|
||||
if (isDir) {
|
||||
parts = "/" + parts; // fix weird rtl thing
|
||||
}
|
||||
return parts;
|
||||
},
|
||||
baseName(str) {
|
||||
let parts = str.replace(/(\/$|^\/)/, "").split("/");
|
||||
return parts.pop();
|
||||
},
|
||||
open() {
|
||||
mutations.showHover("search");
|
||||
},
|
||||
close(event) {
|
||||
event.stopPropagation();
|
||||
mutations.closeHovers();
|
||||
},
|
||||
keyup(event) {
|
||||
if (event.keyCode === 27) {
|
||||
this.close(event);
|
||||
return;
|
||||
}
|
||||
this.results.length === 0;
|
||||
},
|
||||
addToTypes(string) {
|
||||
if (this.searchTypes.includes(string)) {
|
||||
return true;
|
||||
}
|
||||
if (string == null || string == "") {
|
||||
return false;
|
||||
}
|
||||
this.searchTypes = this.searchTypes + string + " ";
|
||||
},
|
||||
resetSearchFilters() {
|
||||
this.searchTypes = "";
|
||||
},
|
||||
removeFromTypes(string) {
|
||||
if (string == null || string == "") {
|
||||
return false;
|
||||
}
|
||||
this.searchTypes = this.searchTypes.replace(string + " ", "");
|
||||
if (this.isMobile) {
|
||||
this.$refs.input.focus();
|
||||
}
|
||||
},
|
||||
folderSelectClicked() {
|
||||
this.isTypeSelectDisabled = true; // Disable the other ButtonGroup
|
||||
},
|
||||
resetButtonGroups() {
|
||||
this.isTypeSelectDisabled = false;
|
||||
},
|
||||
async submit(event) {
|
||||
this.showHelp = false;
|
||||
event.preventDefault();
|
||||
if (this.value === "" || this.value.length < 3) {
|
||||
this.ongoing = false;
|
||||
this.results = [];
|
||||
this.noneMessage = "Not enough characters to search (min 3)";
|
||||
return;
|
||||
}
|
||||
let searchTypesFull = this.searchTypes;
|
||||
if (this.largerThan != "") {
|
||||
searchTypesFull = searchTypesFull + "type:largerThan=" + this.largerThan + " ";
|
||||
}
|
||||
if (this.smallerThan != "") {
|
||||
searchTypesFull = searchTypesFull + "type:smallerThan=" + this.smallerThan + " ";
|
||||
}
|
||||
let path = state.route.path;
|
||||
this.ongoing = true;
|
||||
try {
|
||||
this.results = await search(path, searchTypesFull + this.value);
|
||||
} catch (error) {
|
||||
showError(error);
|
||||
}
|
||||
this.ongoing = false;
|
||||
if (this.results.length == 0) {
|
||||
this.noneMessage = "No results found in indexed search.";
|
||||
}
|
||||
},
|
||||
toggleHelp() {
|
||||
this.showHelp = !this.showHelp;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("resize", this.handleResize);
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("resize", this.handleResize);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.main-input {
|
||||
@@ -140,15 +453,17 @@
|
||||
color: white;
|
||||
border-left: 1px solid gray;
|
||||
border-right: 1px solid gray;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#result-desktop>#result-list {
|
||||
#result-desktop > #result-list {
|
||||
max-height: 80vh;
|
||||
width: 35em;
|
||||
overflow: scroll;
|
||||
padding-bottom: 1em;
|
||||
-webkit-transition: width 0.3s ease 0s;
|
||||
transition: width 0.3s ease 0s;
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
#result-desktop {
|
||||
@@ -172,13 +487,15 @@
|
||||
background-color: lightgray;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#search.active #result-desktop ul li a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: .3em 0;
|
||||
margin-right: .3em;
|
||||
padding: 0.3em 0;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
#search #result-list.active {
|
||||
@@ -201,9 +518,10 @@
|
||||
|
||||
/* Search */
|
||||
#search {
|
||||
z-index:3;
|
||||
background-color: unset !important;
|
||||
z-index: 3;
|
||||
position: fixed;
|
||||
top: .5em;
|
||||
top: 0.5em;
|
||||
min-width: 35em;
|
||||
left: 50%;
|
||||
-webkit-transform: translateX(-50%);
|
||||
@@ -255,7 +573,6 @@
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
|
||||
#search #result {
|
||||
padding-top: 1em;
|
||||
overflow: hidden;
|
||||
@@ -276,7 +593,7 @@ body.rtl #search #result {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
#search #result>div>*:first-child {
|
||||
#search #result > div > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -286,7 +603,7 @@ body.rtl #search #result {
|
||||
}
|
||||
|
||||
/* Search Results */
|
||||
body.rtl #search #result ul>* {
|
||||
body.rtl #search #result ul > * {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
@@ -314,7 +631,7 @@ body.rtl #search #result ul>* {
|
||||
}
|
||||
|
||||
#search.active #input {
|
||||
background-color: lightgray;
|
||||
background-color: var(--background);
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
border-bottom-style: none;
|
||||
@@ -411,7 +728,7 @@ body.rtl #search .boxes h3 {
|
||||
width: 5em;
|
||||
border-radius: 1em;
|
||||
padding: 1em;
|
||||
backdrop-filter: invert(.1);
|
||||
backdrop-filter: invert(0.1);
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
@@ -427,7 +744,7 @@ body.rtl #search .boxes h3 {
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
border: 1px solid #ccc
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.helpButton {
|
||||
@@ -448,232 +765,3 @@ body.rtl #search .boxes h3 {
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import ButtonGroup from "./ButtonGroup.vue";
|
||||
import { mapState, mapGetters, mapMutations } from "vuex";
|
||||
import { search } from "@/api";
|
||||
|
||||
var boxes = {
|
||||
folder: { label: "folders", icon: "folder" },
|
||||
file: { label: "files", icon: "insert_drive_file" },
|
||||
archive: { label: "archives", icon: "archive" },
|
||||
image: { label: "images", icon: "photo" },
|
||||
audio: { label: "audio files", icon: "volume_up" },
|
||||
video: { label: "videos", icon: "movie" },
|
||||
doc: { label: "documents", icon: "picture_as_pdf" },
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ButtonGroup,
|
||||
},
|
||||
name: "search",
|
||||
data: function () {
|
||||
return {
|
||||
largerThan: "",
|
||||
smallerThan: "",
|
||||
noneMessage: "Start typing 3 or more characters to begin searching.",
|
||||
searchTypes: "",
|
||||
isTypeSelectDisabled: false,
|
||||
showHelp: false,
|
||||
folderSelect: [
|
||||
{ label: "Only Folders", value: "type:folder" },
|
||||
{ label: "Only Files", value: "type:file" },
|
||||
],
|
||||
typeSelect: [
|
||||
{ label: "Photos", value: "type:image" },
|
||||
{ label: "Audio", value: "type:audio" },
|
||||
{ label: "Videos", value: "type:video" },
|
||||
{ label: "Documents", value: "type:docs" },
|
||||
{ label: "Archives", value: "type:archive" },
|
||||
],
|
||||
value: "",
|
||||
width: window.innerWidth,
|
||||
active: false,
|
||||
ongoing: false,
|
||||
results: [],
|
||||
reload: false,
|
||||
scrollable: null,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
active(active) {
|
||||
const resultList = document.getElementById("result-list");
|
||||
if (!active) {
|
||||
resultList.classList.remove("active");
|
||||
return
|
||||
}
|
||||
setTimeout(() => {
|
||||
resultList.classList.add("active");
|
||||
}, 100);
|
||||
|
||||
},
|
||||
show(val, old) {
|
||||
this.active = val === "search";
|
||||
if (old === "search" && !this.active) {
|
||||
if (this.reload) {
|
||||
this.setReload(true);
|
||||
}
|
||||
|
||||
document.body.style.overflow = "auto";
|
||||
this.ongoing = false;
|
||||
this.results = [];
|
||||
this.value = "";
|
||||
this.active = false;
|
||||
this.$refs.input.blur();
|
||||
} else if (this.active) {
|
||||
this.reload = false;
|
||||
this.$refs.input.focus();
|
||||
document.body.style.overflow = "hidden";
|
||||
}
|
||||
},
|
||||
value() {
|
||||
if (this.results.length) {
|
||||
this.ongoing = false;
|
||||
this.results = [];
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user", "show"]),
|
||||
...mapGetters(["isListing"]),
|
||||
showBoxes() {
|
||||
return this.searchTypes == "";
|
||||
},
|
||||
boxes() {
|
||||
return boxes;
|
||||
},
|
||||
isEmpty() {
|
||||
return this.results.length === 0;
|
||||
},
|
||||
text() {
|
||||
if (this.ongoing) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return this.value === ""
|
||||
? this.$t("search.typeToSearch")
|
||||
: this.$t("search.pressToSearch");
|
||||
},
|
||||
isMobile() {
|
||||
return this.width <= 800;
|
||||
},
|
||||
isRunning() {
|
||||
return this.ongoing;
|
||||
},
|
||||
searchHelp() {
|
||||
return this.showHelp;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener("resize", this.handleResize);
|
||||
this.handleResize(); // Call this once to set the initial width
|
||||
},
|
||||
methods: {
|
||||
handleResize() {
|
||||
this.width = window.innerWidth;
|
||||
},
|
||||
async navigateTo(url) {
|
||||
this.closeHovers();
|
||||
await this.$nextTick();
|
||||
setTimeout(() => this.$router.push(url), 0);
|
||||
},
|
||||
getContext(url) {
|
||||
url = url.slice(1);
|
||||
let path = "./" + url.substring(url.indexOf("/") + 1);
|
||||
return path.replace(/\/+$/, "") + "/";
|
||||
},
|
||||
basePath(str) {
|
||||
let parts = str.split("/");
|
||||
if (parts.length <= 2) {
|
||||
return "/";
|
||||
}
|
||||
parts.pop();
|
||||
parts = parts.join("/") + "/";
|
||||
if (str.endsWith("/")) {
|
||||
parts = "/" + parts;
|
||||
}
|
||||
return parts;
|
||||
},
|
||||
baseName(str) {
|
||||
let parts = str.replace(/(\/$|^\/)/, "").split("/");
|
||||
return parts.pop();
|
||||
},
|
||||
...mapMutations(["showHover", "closeHovers", "setReload"]),
|
||||
open() {
|
||||
this.showHover("search");
|
||||
},
|
||||
close(event) {
|
||||
event.stopPropagation();
|
||||
this.closeHovers();
|
||||
},
|
||||
keyup(event) {
|
||||
if (event.keyCode === 27) {
|
||||
this.close(event);
|
||||
return;
|
||||
}
|
||||
this.results.length === 0;
|
||||
},
|
||||
addToTypes(string) {
|
||||
if (this.searchTypes.includes(string)) {
|
||||
return true;
|
||||
}
|
||||
if (string == null || string == "") {
|
||||
return false;
|
||||
}
|
||||
this.searchTypes = this.searchTypes + string + " ";
|
||||
|
||||
},
|
||||
resetSearchFilters() {
|
||||
this.searchTypes = "";
|
||||
},
|
||||
removeFromTypes(string) {
|
||||
if (string == null || string == "") {
|
||||
return false;
|
||||
}
|
||||
this.searchTypes = this.searchTypes.replace(string + " ", "");
|
||||
if (this.isMobile) {
|
||||
this.$refs.input.focus();
|
||||
}
|
||||
},
|
||||
folderSelectClicked() {
|
||||
this.isTypeSelectDisabled = true; // Disable the other ButtonGroup
|
||||
},
|
||||
resetButtonGroups() {
|
||||
this.isTypeSelectDisabled = false;
|
||||
},
|
||||
async submit(event) {
|
||||
this.showHelp = false;
|
||||
event.preventDefault();
|
||||
if (this.value === "" || this.value.length < 3) {
|
||||
this.ongoing = false;
|
||||
this.results = [];
|
||||
this.noneMessage = "Not enough characters to search (min 3)";
|
||||
return;
|
||||
}
|
||||
let searchTypesFull = this.searchTypes;
|
||||
if (this.largerThan != "") {
|
||||
searchTypesFull = searchTypesFull + "type:largerThan=" + this.largerThan + " ";
|
||||
}
|
||||
if (this.smallerThan != "") {
|
||||
searchTypesFull = searchTypesFull + "type:smallerThan=" + this.smallerThan + " ";
|
||||
}
|
||||
let path = this.$route.path;
|
||||
this.ongoing = true;
|
||||
try {
|
||||
this.results = await search(path, searchTypesFull + this.value);
|
||||
} catch (error) {
|
||||
this.$showError(error);
|
||||
}
|
||||
this.ongoing = false;
|
||||
if (this.results.length == 0) {
|
||||
this.noneMessage = "No results found in indexed search.";
|
||||
}
|
||||
},
|
||||
toggleHelp() {
|
||||
this.showHelp = !this.showHelp;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -1,59 +1,112 @@
|
||||
<template>
|
||||
<nav :class="{ active }">
|
||||
<template v-if="isLogged">
|
||||
<button class="action" @click="toRoot" :aria-label="$t('sidebar.myFiles')" :title="$t('sidebar.myFiles')">
|
||||
<nav :class="{ active, 'dark-mode': isDarkMode }">
|
||||
<!-- Section for logged-in users -->
|
||||
<template v-if="isLoggedIn">
|
||||
<!-- My Files button -->
|
||||
<button
|
||||
class="action"
|
||||
@click="toRoot"
|
||||
:aria-label="$t('sidebar.myFiles')"
|
||||
:title="$t('sidebar.myFiles')"
|
||||
>
|
||||
<i class="material-icons">folder</i>
|
||||
<span>{{ $t("sidebar.myFiles") }}</span>
|
||||
</button>
|
||||
<div v-if="user.perm.create">
|
||||
<button @click="$store.commit('showHover', 'newDir')" class="action" :aria-label="$t('sidebar.newFolder')"
|
||||
:title="$t('sidebar.newFolder')">
|
||||
|
||||
<!-- Buttons visible if user has create permission -->
|
||||
<div v-if="user.perm?.create">
|
||||
<!-- New Folder button -->
|
||||
<button
|
||||
@click="showHover('newDir')"
|
||||
class="action"
|
||||
:aria-label="$t('sidebar.newFolder')"
|
||||
:title="$t('sidebar.newFolder')"
|
||||
>
|
||||
<i class="material-icons">create_new_folder</i>
|
||||
<span>{{ $t("sidebar.newFolder") }}</span>
|
||||
</button>
|
||||
<button @click="$store.commit('showHover', 'newFile')" class="action" :aria-label="$t('sidebar.newFile')"
|
||||
:title="$t('sidebar.newFile')">
|
||||
<!-- New File button -->
|
||||
<button
|
||||
@click="showHover('newFile')"
|
||||
class="action"
|
||||
:aria-label="$t('sidebar.newFile')"
|
||||
:title="$t('sidebar.newFile')"
|
||||
>
|
||||
<i class="material-icons">note_add</i>
|
||||
<span>{{ $t("sidebar.newFile") }}</span>
|
||||
</button>
|
||||
<button id="upload-button" @click="upload($event)" class="action" >
|
||||
<!-- Upload button -->
|
||||
<button id="upload-button" @click="upload($event)" class="action">
|
||||
<i class="material-icons">file_upload</i>
|
||||
<span>Upload file</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Settings and Logout buttons -->
|
||||
<div>
|
||||
<button class="action" @click="toSettings" :aria-label="$t('sidebar.settings')" :title="$t('sidebar.settings')">
|
||||
<!-- Settings button -->
|
||||
<button
|
||||
class="action"
|
||||
@click="toSettings"
|
||||
:aria-label="$t('sidebar.settings')"
|
||||
:title="$t('sidebar.settings')"
|
||||
>
|
||||
<i class="material-icons">settings_applications</i>
|
||||
<span>{{ $t("sidebar.settings") }}</span>
|
||||
</button>
|
||||
|
||||
<button v-if="canLogout" @click="logout" class="action" id="logout" :aria-label="$t('sidebar.logout')"
|
||||
:title="$t('sidebar.logout')">
|
||||
<!-- Logout button -->
|
||||
<button
|
||||
v-if="canLogout"
|
||||
@click="logout"
|
||||
class="action"
|
||||
id="logout"
|
||||
:aria-label="$t('sidebar.logout')"
|
||||
:title="$t('sidebar.logout')"
|
||||
>
|
||||
<i class="material-icons">exit_to_app</i>
|
||||
<span>{{ $t("sidebar.logout") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Section for non-logged-in users -->
|
||||
<template v-else>
|
||||
<router-link class="action" to="/login" :aria-label="$t('sidebar.login')" :title="$t('sidebar.login')">
|
||||
<!-- Login button -->
|
||||
<router-link
|
||||
class="action"
|
||||
to="/login"
|
||||
:aria-label="$t('sidebar.login')"
|
||||
:title="$t('sidebar.login')"
|
||||
>
|
||||
<i class="material-icons">exit_to_app</i>
|
||||
<span>{{ $t("sidebar.login") }}</span>
|
||||
</router-link>
|
||||
<router-link v-if="signup" class="action" to="/login" :aria-label="$t('sidebar.signup')"
|
||||
:title="$t('sidebar.signup')">
|
||||
<!-- Signup button, if signup is enabled -->
|
||||
<router-link
|
||||
v-if="signup"
|
||||
class="action"
|
||||
to="/login"
|
||||
:aria-label="$t('sidebar.signup')"
|
||||
:title="$t('sidebar.signup')"
|
||||
>
|
||||
<i class="material-icons">person_add</i>
|
||||
<span>{{ $t("sidebar.signup") }}</span>
|
||||
</router-link>
|
||||
</template>
|
||||
<div class="credits" v-if="$router.currentRoute.path.includes('/files/') && !disableUsedPercentage
|
||||
">
|
||||
|
||||
<!-- Credits and usage information section -->
|
||||
<div class="credits" v-if="isFiles && !disableUsedPercentage && usage">
|
||||
<progress-bar :val="usage.usedPercentage" size="medium"></progress-bar>
|
||||
<span style="text-align:center">{{ usage.usedPercentage }}%</span>
|
||||
<span style="text-align: center">{{ usage.usedPercentage }}%</span>
|
||||
<span>{{ usage.used }} of {{ usage.total }} used</span>
|
||||
<br>
|
||||
<br />
|
||||
<span v-if="disableExternal">File Browser</span>
|
||||
<span v-else>
|
||||
<a rel="noopener noreferrer" target="_blank" href="https://github.com/gtsteffaniak/filebrowser">
|
||||
<a
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
href="https://github.com/gtsteffaniak/filebrowser"
|
||||
>
|
||||
File Browser
|
||||
</a>
|
||||
</span>
|
||||
@@ -64,8 +117,8 @@
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import * as upload from "@/utils/upload";
|
||||
import * as auth from "@/utils/auth";
|
||||
import {
|
||||
@@ -77,78 +130,91 @@ import {
|
||||
loginPage,
|
||||
} from "@/utils/constants";
|
||||
import { files as api } from "@/api";
|
||||
import ProgressBar from "vue-simple-progress";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import ProgressBar from "@/components/ProgressBar.vue";
|
||||
import { getHumanReadableFilesize } from "@/utils/filesizes";
|
||||
import { state, getters, mutations } from "@/store"; // Import your custom store
|
||||
import { showError } from "@/notify";
|
||||
|
||||
export default {
|
||||
name: "sidebar",
|
||||
components: {
|
||||
ProgressBar,
|
||||
},
|
||||
mounted() {
|
||||
this.updateUsage();
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user"]),
|
||||
...mapGetters(["isLogged"]),
|
||||
isFiles() {
|
||||
return getters.isFiles();
|
||||
},
|
||||
user() {
|
||||
return state.user;
|
||||
},
|
||||
isDarkMode() {
|
||||
return getters.isDarkMode();
|
||||
},
|
||||
isLoggedIn() {
|
||||
return getters.isLoggedIn();
|
||||
},
|
||||
currentPrompt() {
|
||||
return getters.currentPrompt();
|
||||
},
|
||||
active() {
|
||||
return this.$store.state.show === "sidebar";
|
||||
return getters.currentPromptName() === "sidebar";
|
||||
},
|
||||
signup: () => signup,
|
||||
version: () => version,
|
||||
disableExternal: () => disableExternal,
|
||||
disableUsedPercentage: () => disableUsedPercentage,
|
||||
canLogout: () => !noAuth && loginPage,
|
||||
},
|
||||
asyncComputed: {
|
||||
usage: {
|
||||
async get() {
|
||||
let path = this.$route.path.endsWith("/")
|
||||
? this.$route.path
|
||||
: this.$route.path + "/";
|
||||
let usageStats = { used: 0, total: 0, usedPercentage: 0 };
|
||||
if (this.disableUsedPercentage) {
|
||||
return usageStats;
|
||||
}
|
||||
try {
|
||||
let usage = await api.usage(path);
|
||||
usageStats = {
|
||||
used: prettyBytes(usage.used / 1024, { binary: true }),
|
||||
total: prettyBytes(usage.total / 1024, { binary: true }),
|
||||
usedPercentage: Math.round((usage.used / usage.total) * 100),
|
||||
};
|
||||
} catch (error) {
|
||||
this.$showError(error);
|
||||
}
|
||||
return usageStats;
|
||||
},
|
||||
default: { used: "0 B", total: "0 B", usedPercentage: 0 },
|
||||
shouldUpdate() {
|
||||
return this.$router.currentRoute.path.includes("/files/");
|
||||
},
|
||||
},
|
||||
usage: () => state.usage,
|
||||
},
|
||||
methods: {
|
||||
toRoot() {
|
||||
this.$router.push({ path: "/files/" }, () => { });
|
||||
this.$store.commit("closeHovers");
|
||||
},
|
||||
toSettings() {
|
||||
this.$router.push({ path: "/settings" }, () => { });
|
||||
this.$store.commit("closeHovers");
|
||||
},
|
||||
help() {
|
||||
this.$store.commit("showHover", "help");
|
||||
},
|
||||
upload: function () {
|
||||
if (
|
||||
typeof window.DataTransferItem !== "undefined" &&
|
||||
typeof DataTransferItem.prototype.webkitGetAsEntry !== "undefined"
|
||||
) {
|
||||
this.$store.commit("showHover", "upload");
|
||||
} else {
|
||||
document.getElementById("upload-input").click();
|
||||
async updateUsage() {
|
||||
console.log("updating usage");
|
||||
|
||||
let path = getters.getRoutePath();
|
||||
let usageStats = { used: "0 B", total: "0 B", usedPercentage: 0 };
|
||||
if (this.disableUsedPercentage) {
|
||||
return usageStats;
|
||||
}
|
||||
try {
|
||||
let usage = await api.usage(path);
|
||||
usageStats = {
|
||||
used: getHumanReadableFilesize(usage.used / 1024),
|
||||
total: getHumanReadableFilesize(usage.total / 1024),
|
||||
usedPercentage: Math.round((usage.used / usage.total) * 100),
|
||||
};
|
||||
} catch (error) {
|
||||
showError("Error fetching usage:", error);
|
||||
}
|
||||
console.log(usageStats);
|
||||
mutations.setUsage(usageStats);
|
||||
},
|
||||
showHover(value) {
|
||||
return mutations.showHover(value);
|
||||
},
|
||||
// Navigate to the root files directory
|
||||
toRoot() {
|
||||
this.$router.push({ path: "/files/" }, () => {});
|
||||
mutations.closeHovers();
|
||||
},
|
||||
// Navigate to the settings page
|
||||
toSettings() {
|
||||
this.$router.push({ path: "/settings" }, () => {});
|
||||
mutations.closeHovers();
|
||||
},
|
||||
// Show the help overlay
|
||||
help() {
|
||||
mutations.showHover("help");
|
||||
},
|
||||
// Handle file upload
|
||||
upload(event) {
|
||||
return this.$upload(event);
|
||||
},
|
||||
// Handle files selected for upload
|
||||
uploadInput(event) {
|
||||
this.$store.commit("closeHovers");
|
||||
mutations.closeHovers();
|
||||
|
||||
let files = event.currentTarget.files;
|
||||
let folder_upload =
|
||||
@@ -161,17 +227,15 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
let path = this.$route.path.endsWith("/")
|
||||
? this.$route.path
|
||||
: this.$route.path + "/";
|
||||
let conflict = upload.checkConflict(files, this.req.items);
|
||||
let path = getters.getRoutePath();
|
||||
let conflict = upload.checkConflict(files, state.req.items);
|
||||
|
||||
if (conflict) {
|
||||
this.$store.commit("showHover", {
|
||||
prompt: "replace",
|
||||
mutations.showHover({
|
||||
name: "replace",
|
||||
confirm: (event) => {
|
||||
event.preventDefault();
|
||||
this.$store.commit("closeHovers");
|
||||
mutations.closeHovers();
|
||||
upload.handleFiles(files, path, true);
|
||||
},
|
||||
});
|
||||
@@ -181,6 +245,7 @@ export default {
|
||||
|
||||
upload.handleFiles(files, path);
|
||||
},
|
||||
// Logout the user
|
||||
logout: auth.logout,
|
||||
},
|
||||
};
|
||||
|
||||
Loaded 100 of 189 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user