mirror of
https://github.com/IceWhaleTech/CasaOS.git
synced 2025-12-23 13:04:42 +00:00
Compare commits
67 Commits
v0.4.1-alp
...
v0.4.3-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c7c8dc1ec | ||
|
|
60a141fe25 | ||
|
|
02e712f649 | ||
|
|
7022cf5d29 | ||
|
|
6cf46ce50c | ||
|
|
202c1de570 | ||
|
|
858ab5b124 | ||
|
|
e319975a60 | ||
|
|
5c2c3b5e98 | ||
|
|
a0dc58264a | ||
|
|
717b47ca2c | ||
|
|
f530f69ba5 | ||
|
|
87b190a84b | ||
|
|
10191a1be3 | ||
|
|
59f2ccbeb3 | ||
|
|
076b7198b2 | ||
|
|
eb483a4c5d | ||
|
|
60e81bc781 | ||
|
|
dc8ee89c85 | ||
|
|
c995750312 | ||
|
|
de6ed093a2 | ||
|
|
449e1515d9 | ||
|
|
e6ddb0d849 | ||
|
|
c19e32c6e9 | ||
|
|
94755e221a | ||
|
|
86a3692dad | ||
|
|
45a5567978 | ||
|
|
3190421fd9 | ||
|
|
a7126cac63 | ||
|
|
151aa037bb | ||
|
|
08ed9933ee | ||
|
|
8425011e73 | ||
|
|
96ff550d61 | ||
|
|
85a803d352 | ||
|
|
5def3e5856 | ||
|
|
c768260b1b | ||
|
|
46e146e633 | ||
|
|
1a2f917b30 | ||
|
|
86adfbaef8 | ||
|
|
143af78745 | ||
|
|
7b07f22685 | ||
|
|
c8b6a1c228 | ||
|
|
827fba2164 | ||
|
|
6217009caf | ||
|
|
fbfcc2c43a | ||
|
|
b331c484f5 | ||
|
|
a1fbbf9584 | ||
|
|
3611ec7d09 | ||
|
|
7dedbafc8a | ||
|
|
b7634402bd | ||
|
|
8e025a9836 | ||
|
|
8bfc8178cf | ||
|
|
8343f52137 | ||
|
|
7501833cf9 | ||
|
|
4c917a33a4 | ||
|
|
173997c44b | ||
|
|
76bc6e68ae | ||
|
|
d0fc2cc8cb | ||
|
|
32980f4033 | ||
|
|
28d3ca0ca6 | ||
|
|
3d31ad6689 | ||
|
|
0bb138e39e | ||
|
|
e8db1767e5 | ||
|
|
6b01263252 | ||
|
|
b2a4fafdb4 | ||
|
|
fcfb48d88e | ||
|
|
87d8be8c61 |
53
.github/ISSUE_TEMPLATE/app_request.yml
vendored
53
.github/ISSUE_TEMPLATE/app_request.yml
vendored
@@ -1,53 +0,0 @@
|
|||||||
name: "App Request"
|
|
||||||
description: "Request to add an app to the app store."
|
|
||||||
title: "[App Request] AppName"
|
|
||||||
labels: ["App Request"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
### ❤ Thanks for taking the time to fill out this app request!
|
|
||||||
> Before proceeding, please make sure that this app is not in App Store and no one has [requested](https://github.com/IceWhaleTech/CasaOS/labels/App%20Request) the same app before.
|
|
||||||
> If you have already requested the app, please ask your friends to help add a 👍 to this issue. Then be patient and wait for the developers to work on it.
|
|
||||||
> If you have any questions, please ask them on [Discord](https://discord.gg/knqAbbBbeX) or [Github Discussions](https://github.com/IceWhaleTech/CasaOS/discussions).
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: app-info
|
|
||||||
attributes:
|
|
||||||
label: "App Information"
|
|
||||||
description: "The formal information of this app, as detailed as possible."
|
|
||||||
value: |
|
|
||||||
- Name: <!-- eg. Nextcloud -->
|
|
||||||
- Short Description: <!-- eg. Personal cloud and file sharing solution -->
|
|
||||||
- Official Website: <!-- If available. eg. https://nextcloud.com -->
|
|
||||||
- GitHub Repository: <!-- If available. eg. https://github.com/nextcloud/server -->
|
|
||||||
- Docker Image: <!-- If available. eg. nextcloud/server:latest, ghcr.io/nextcloud/server:latest -->
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: why
|
|
||||||
attributes:
|
|
||||||
label: "Why do you want this app?"
|
|
||||||
description: "Detailed notes can help developers and others understand the importance of this app."
|
|
||||||
placeholder: |
|
|
||||||
As a [what role], it helps me solve [what problem], and especially [what function] is great!
|
|
||||||
or
|
|
||||||
It solves [what problem] and especially [what feature] works well, which is hard to do with other app.
|
|
||||||
or
|
|
||||||
This is the app that [some device/service] must use and will not work without it.
|
|
||||||
or
|
|
||||||
others
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: additional-info
|
|
||||||
attributes:
|
|
||||||
label: "Additional information?"
|
|
||||||
description: "Anything else you want to share with the developers and others?"
|
|
||||||
placeholder: |
|
|
||||||
Example:
|
|
||||||
- Noteworthy matters.
|
|
||||||
- Recommended Docker image.
|
|
||||||
- Validated Docker deployment instructions.
|
|
||||||
- Notable Docker setup details.
|
|
||||||
- Recommended config files, user data, accessible directory settings.
|
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
|
- name: App Request
|
||||||
|
url: https://github.com/IceWhaleTech/CasaOS-AppStore/issues/new?assignees=&labels=App+Request&template=app_request.yml&title=%5BApp+Request%5D+AppName
|
||||||
|
about: Request to add an app to the app store.
|
||||||
- name: Feature/Enhancement Ideas
|
- name: Feature/Enhancement Ideas
|
||||||
url: https://github.com/IceWhaleTech/CasaOS/discussions/164
|
url: https://github.com/IceWhaleTech/CasaOS/discussions/164
|
||||||
about: Have an idea for a new feature/enhancement?
|
about: Have an idea for a new feature/enhancement?
|
||||||
@@ -8,4 +11,4 @@ contact_links:
|
|||||||
about: Ask questions, propose ideas, or discuss anything related to CasaOS
|
about: Ask questions, propose ideas, or discuss anything related to CasaOS
|
||||||
- name: Discord
|
- name: Discord
|
||||||
url: https://discord.gg/knqAbbBbeX
|
url: https://discord.gg/knqAbbBbeX
|
||||||
about: Get help or share great ideas on Discord!
|
about: Get help or share great ideas on Discord!
|
||||||
|
|||||||
4
.github/workflows/casa.yml
vendored
4
.github/workflows/casa.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
|||||||
# env:
|
# env:
|
||||||
# GITHUB_TOKEN: ${{ github.token }}
|
# GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
submodules: true
|
submodules: true
|
||||||
@@ -73,7 +73,7 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
- name: Use Node.js
|
- name: Use Node.js
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '14'
|
||||||
|
|
||||||
|
|||||||
26
.github/workflows/codecov.yml
vendored
Normal file
26
.github/workflows/codecov.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: Collect Code Coverage
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: "1.20"
|
||||||
|
- name: Run coverage
|
||||||
|
run: go test -race -failfast -coverprofile=coverage.txt -covermode=atomic -v ./...
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v3
|
||||||
4
.github/workflows/demo.yml
vendored
4
.github/workflows/demo.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
steps:
|
steps:
|
||||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Configure AWS credentials from Test account
|
- name: Configure AWS credentials from Test account
|
||||||
uses: aws-actions/configure-aws-credentials@v1
|
uses: aws-actions/configure-aws-credentials@v1
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get old instance and snapshot name, create new instance name
|
- name: Get old instance and snapshot name, create new instance name
|
||||||
run: |
|
run: |
|
||||||
echo "OLD_INSTANCE_SNAPSHOT_NAME=$(aws lightsail get-instance-snapshots | grep '"name": "casaos-0.3.6-1666150291' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
|
echo "OLD_INSTANCE_SNAPSHOT_NAME=$(aws lightsail get-instance-snapshots | grep '"name": "updateto_to_0.4.1-1676285322' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
|
||||||
echo "OLD_INSTANCE_NAME=$(aws lightsail get-instances | grep '"name": "CasaOS-Demo-[0-9]' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
|
echo "OLD_INSTANCE_NAME=$(aws lightsail get-instances | grep '"name": "CasaOS-Demo-[0-9]' | sed 's/ //g' | sed 's/"//g' | sed 's/,//g' | sed 's/name://g')" >> $GITHUB_ENV
|
||||||
echo "NEW_INSTANCE_NAME=CasaOS-Demo-$(date +%s)" >> $GITHUB_ENV
|
echo "NEW_INSTANCE_NAME=CasaOS-Demo-$(date +%s)" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
|||||||
84
.github/workflows/push_test_server.yml
vendored
Normal file
84
.github/workflows/push_test_server.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
name: Auto Publish Website
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
jobs:
|
||||||
|
goreleaser:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: isntall git
|
||||||
|
run: sudo apt install --yes git
|
||||||
|
- name: git global
|
||||||
|
run: sudo git config --global --add safe.directory '*'
|
||||||
|
- name: set version
|
||||||
|
run: sudo git tag v99.99.99-alpha
|
||||||
|
|
||||||
|
- name: Fetch all tags
|
||||||
|
run: sudo git fetch --force --tags
|
||||||
|
|
||||||
|
- name: Get version
|
||||||
|
id: get_version
|
||||||
|
# run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
||||||
|
run: echo "VERSION=$(git describe --abbrev=0 --tags | awk -F- '{print $1}')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: show version
|
||||||
|
id: show_version
|
||||||
|
# run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
||||||
|
run: echo ${{env.VERSION}}
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: 'stable'
|
||||||
|
|
||||||
|
- name: Run GoReleaser
|
||||||
|
uses: goreleaser/goreleaser-action@v4
|
||||||
|
with:
|
||||||
|
# either 'goreleaser' (default) or 'goreleaser-pro'
|
||||||
|
distribution: goreleaser
|
||||||
|
version: latest
|
||||||
|
args: release --rm-dist --snapshot
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution
|
||||||
|
# GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
|
||||||
|
- name: remove migration file
|
||||||
|
run: sudo find . -type f \( -name '*migration*' \) -delete
|
||||||
|
|
||||||
|
- name: install sshpass
|
||||||
|
run: sudo apt install sshpass --yes
|
||||||
|
|
||||||
|
- name: ZeroTier
|
||||||
|
uses: zerotier/github-action@v1.0.1
|
||||||
|
with:
|
||||||
|
network_id: ${{ secrets.ZEROTIER_NETWORK_ID }}
|
||||||
|
auth_token: ${{ secrets.ZEROTIER_CENTRAL_TOKEN }}
|
||||||
|
|
||||||
|
- name: ping host
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
count=10
|
||||||
|
while ! ping -c 1 10.147.18.11 ; do
|
||||||
|
echo "waiting..." ;
|
||||||
|
sleep 1 ;
|
||||||
|
let count=count-1
|
||||||
|
done
|
||||||
|
echo "ping success"
|
||||||
|
|
||||||
|
- name: copy tar to target host
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sshpass -p "${{ secrets.ssh_password }}" scp -r -o StrictHostKeyChecking=no -P 22 ./dist/*.gz root@10.147.18.11:/var/www/download
|
||||||
|
echo "ping success"
|
||||||
|
- name: send message
|
||||||
|
run: |
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"msg_type":"text","content":{"text":"CasaOS updated"}}' ${{ secrets.SSH_ROBOT_URL }}
|
||||||
30
.github/workflows/release.yml
vendored
30
.github/workflows/release.yml
vendored
@@ -12,34 +12,29 @@ jobs:
|
|||||||
goreleaser:
|
goreleaser:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
-
|
- name: Install dependencies for cross-compiling
|
||||||
name: Install dependencies for cross-compiling
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt-get --no-install-recommends --yes install \
|
sudo apt-get --no-install-recommends --yes install \
|
||||||
upx libc6-dev-amd64-cross \
|
upx libc6-dev-amd64-cross \
|
||||||
gcc-aarch64-linux-gnu libc6-dev-arm64-cross \
|
gcc-aarch64-linux-gnu libc6-dev-arm64-cross \
|
||||||
gcc-arm-linux-gnueabihf libc6-dev-armhf-cross
|
gcc-arm-linux-gnueabihf libc6-dev-armhf-cross
|
||||||
-
|
- name: Checkout
|
||||||
name: Checkout
|
uses: actions/checkout@v3
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
-
|
- name: Fetch all tags
|
||||||
name: Fetch all tags
|
|
||||||
run: git fetch --force --tags
|
run: git fetch --force --tags
|
||||||
|
|
||||||
- name: Get version
|
- name: Get version
|
||||||
id: get_version
|
id: get_version
|
||||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
|
||||||
-
|
- name: Set up Go
|
||||||
name: Set up Go
|
uses: actions/setup-go@v4
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
with:
|
||||||
go-version: 1.19
|
go-version: "1.20"
|
||||||
-
|
- name: Run GoReleaser
|
||||||
name: Run GoReleaser
|
uses: goreleaser/goreleaser-action@v4
|
||||||
uses: goreleaser/goreleaser-action@v2
|
|
||||||
with:
|
with:
|
||||||
# either 'goreleaser' (default) or 'goreleaser-pro'
|
# either 'goreleaser' (default) or 'goreleaser-pro'
|
||||||
distribution: goreleaser
|
distribution: goreleaser
|
||||||
@@ -49,7 +44,7 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
# Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution
|
# Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution
|
||||||
# GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
# GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
|
||||||
- name: Upload to oss
|
- name: Upload to oss
|
||||||
id: upload_to_oss
|
id: upload_to_oss
|
||||||
uses: tvrcgo/upload-to-oss@master
|
uses: tvrcgo/upload-to-oss@master
|
||||||
@@ -66,6 +61,3 @@ jobs:
|
|||||||
dist/linux-arm-7-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-arm-7-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz
|
dist/linux-arm-7-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-arm-7-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz
|
||||||
dist/linux-arm64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-arm64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz
|
dist/linux-arm64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-arm64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz
|
||||||
dist/linux-amd64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-amd64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz
|
dist/linux-amd64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz:/IceWhaleTech/CasaOS/releases/download/${{ steps.get_version.outputs.VERSION }}/linux-amd64-casaos-migration-tool-${{ steps.get_version.outputs.VERSION }}.tar.gz
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ before:
|
|||||||
- go generate
|
- go generate
|
||||||
- go run github.com/google/go-licenses@latest check . --disallowed_types=restricted
|
- go run github.com/google/go-licenses@latest check . --disallowed_types=restricted
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
- go test -v ./...
|
- go test -race -v ./...
|
||||||
|
|
||||||
builds:
|
builds:
|
||||||
- id: casaos-amd64
|
- id: casaos-amd64
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ before:
|
|||||||
- go generate
|
- go generate
|
||||||
- go run github.com/google/go-licenses@latest check . --disallowed_types=restricted
|
- go run github.com/google/go-licenses@latest check . --disallowed_types=restricted
|
||||||
- go mod tidy
|
- go mod tidy
|
||||||
- go test -v ./...
|
- go test -race -v ./...
|
||||||
|
|
||||||
builds:
|
builds:
|
||||||
- id: casaos-amd64
|
- id: casaos-amd64
|
||||||
|
|||||||
55
CHANGELOG.md
55
CHANGELOG.md
@@ -16,17 +16,62 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
## [0.4.3]
|
||||||
## [0.4.1-alpha] - 2023-1-8
|
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- [Disj] Added disk merging feature in storage management (beta) that allows for multiple disks to be merged into a single storage space
|
|
||||||
|
- [Disk] Now usb also supports merging to
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- [File] Solve the installation dependency problem, make the installation more smoothly
|
||||||
|
- [File] Change the default permissions of the sharing folder
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- [System] Fixed not see wlan iface ([#909](https://github.com/IceWhaleTech/CasaOS/issues/909))
|
||||||
|
- [System] Terminal font issue fix ([#929](https://github.com/IceWhaleTech/CasaOS/issues/929))
|
||||||
|
- [File] Fixed the problem of not being able to launch after mounting
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
|
||||||
|
## [0.4.2]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- [App] Increase the display of progress during the installation process
|
||||||
|
- [App] Label whether the current app supports x86 or Pi devices
|
||||||
|
- [App] Support single app version upgrade
|
||||||
|
- [File] Support mounting of Google Drive and Dropbox cloud drives
|
||||||
|
- [System] Support Mint Linux
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- [File] Optimize the download speed of a single file
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- [Share] Fix the samba permission issue
|
||||||
|
- [Disk] Fix the problem of disk mount point plus 1 after upgrade ([#770](https://github.com/IceWhaleTech/CasaOS/issues/770))
|
||||||
|
- [File] Fix the problem of file permission change caused by modifying files in casaos ([#829](https://github.com/IceWhaleTech/CasaOS/issues/829))
|
||||||
|
- [Share] Fix the problem of files being deleted due to samba uninstallation failure ([#843](https://github.com/IceWhaleTech/CasaOS/issues/843))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [0.4.1] - 2023-1-19
|
||||||
|
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- [Disk] Added disk merging feature in storage management (beta) that allows for multiple disks to be merged into a single storage space
|
||||||
- [System] Added option for startpage.com search engine
|
- [System] Added option for startpage.com search engine
|
||||||
- [APP] Added app cloning feature in the app's context menu.
|
- [APP] Added app cloning feature in the app's context menu.
|
||||||
-
|
|
||||||
### Changed
|
### Changed
|
||||||
- [APP] Improved app installation process, including display of the installation process, checks for successful installation, and prompts
|
- [APP] Improved app installation process, including display of the installation process, checks for successful installation, and prompts
|
||||||
|
- [System] Binary sizes are 40%~60% smaller (thanks to upx)
|
||||||
|
- [App] Optimization of install and update for certain country.
|
||||||
|
- [All] Lots of bug fixes
|
||||||
|
|
||||||
## [0.4.0] - 2022-12-13
|
## [0.4.0] - 2022-12-13
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
wiki@casaos.io.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
@@ -28,6 +28,9 @@
|
|||||||
<a href="https://github.com/IceWhaleTech/CasaOS/issues" target="_blank">
|
<a href="https://github.com/IceWhaleTech/CasaOS/issues" target="_blank">
|
||||||
<img alt="CasaOS Issues" src="https://img.shields.io/github/issues/IceWhaleTech/CasaOS?color=162453&style=flat-square&label=Issues" />
|
<img alt="CasaOS Issues" src="https://img.shields.io/github/issues/IceWhaleTech/CasaOS?color=162453&style=flat-square&label=Issues" />
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://codecov.io/gh/IceWhaleTech/CasaOS" >
|
||||||
|
<img src="https://codecov.io/gh/IceWhaleTech/CasaOS/branch/main/graph/badge.svg?token=l9uMKGlkxM"/>
|
||||||
|
</a>
|
||||||
<a href="https://github.com/IceWhaleTech/CasaOS/stargazers" target="_blank">
|
<a href="https://github.com/IceWhaleTech/CasaOS/stargazers" target="_blank">
|
||||||
<img alt="CasaOS Stargazers" src="https://img.shields.io/github/stars/IceWhaleTech/CasaOS?color=162453&style=flat-square&label=Stars" />
|
<img alt="CasaOS Stargazers" src="https://img.shields.io/github/stars/IceWhaleTech/CasaOS?color=162453&style=flat-square&label=Stars" />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
9
SECURITY.md
Normal file
9
SECURITY.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
CasaOS is currently under active development. Support is limited before CasaOS reaches v1.0.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you see any vulnerabiility, email us at wiki@casaos.io
|
||||||
116
api/casaos/openapi.yaml
Normal file
116
api/casaos/openapi.yaml
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
|
||||||
|
info:
|
||||||
|
title: CasaOS API
|
||||||
|
version: v2
|
||||||
|
description: |
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/IceWhaleTech/logo/main/casaos/casaos_banner_dark_night_800px.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/IceWhaleTech/logo/main/casaos/casaos_banner_twilight_blue_800px.png">
|
||||||
|
<img alt="CasaOS" src="https://raw.githubusercontent.com/IceWhaleTech/logo/main/casaos/casaos_banner_twilight_blue_800px.png">
|
||||||
|
</picture>
|
||||||
|
|
||||||
|
CasaOS API provides miscellaneous methods for different scenarios.
|
||||||
|
|
||||||
|
For issues and discussions, please visit the [GitHub repository](https://github.com/IceWhaleTech/CasaOS) or join [our Discord](https://discord.gg/knqAbbBbeX).
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: /v2/casaos
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- name: Health methods
|
||||||
|
description: |-
|
||||||
|
(TODO)
|
||||||
|
- name: File methods
|
||||||
|
description: |-
|
||||||
|
(TODO)
|
||||||
|
|
||||||
|
x-tagGroups:
|
||||||
|
- name: Methods
|
||||||
|
tags:
|
||||||
|
- Health methods
|
||||||
|
|
||||||
|
security:
|
||||||
|
- access_token: []
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/health/services:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Health methods
|
||||||
|
summary: Get service status
|
||||||
|
description: |-
|
||||||
|
Get running status of each `casaos-*` service.
|
||||||
|
operationId: getHealthServices
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
$ref: "#/components/responses/GetHealthServicesOK"
|
||||||
|
"500":
|
||||||
|
$ref: "#/components/responses/ResponseInternalServerError"
|
||||||
|
/file/test:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- File methods
|
||||||
|
summary: Test file methods
|
||||||
|
description: |-
|
||||||
|
Test file methods.
|
||||||
|
operationId: getFileTest
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
$ref: "#/components/responses/ResponseOK"
|
||||||
|
"500":
|
||||||
|
$ref: "#/components/responses/ResponseInternalServerError"
|
||||||
|
components:
|
||||||
|
securitySchemes:
|
||||||
|
access_token:
|
||||||
|
type: apiKey
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
|
||||||
|
responses:
|
||||||
|
ResponseOK:
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BaseResponse"
|
||||||
|
|
||||||
|
ResponseInternalServerError:
|
||||||
|
description: Internal Server Error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BaseResponse"
|
||||||
|
|
||||||
|
GetHealthServicesOK:
|
||||||
|
description: OK
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: "#/components/schemas/BaseResponse"
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: "#/components/schemas/HealthServices"
|
||||||
|
|
||||||
|
schemas:
|
||||||
|
BaseResponse:
|
||||||
|
properties:
|
||||||
|
message:
|
||||||
|
readOnly: true
|
||||||
|
description: message returned by server side if there is any
|
||||||
|
type: string
|
||||||
|
example: ""
|
||||||
|
|
||||||
|
HealthServices:
|
||||||
|
properties:
|
||||||
|
running:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: "casaos-gateway.service"
|
||||||
|
not_running:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: "casaos.service"
|
||||||
24
api/index.html
Normal file
24
api/index.html
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>CasaOS | Developers</title>
|
||||||
|
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<redoc spec-url='casaos/openapi.yaml' expandResponses='all' jsonSampleExpandLevel='all'></redoc>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
LEGACY_WITHOUT_VERSION ${DOWNLOAD_DOMAIN}IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz
|
LEGACY_WITHOUT_VERSION ${DOWNLOAD_DOMAIN}IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz
|
||||||
v0.3.5 ${DOWNLOAD_DOMAIN}IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz
|
v0.3.5 ${DOWNLOAD_DOMAIN}IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz
|
||||||
v0.3.5.1 ${DOWNLOAD_DOMAIN}IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz
|
v0.3.5.1 ${DOWNLOAD_DOMAIN}IceWhaleTech/CasaOS/releases/download/v0.3.6/linux-${ARCH}-casaos-migration-tool-v0.3.6.tar.gz
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ __get_setup_script_directory_by_os_release() {
|
|||||||
} || {
|
} || {
|
||||||
pushd "${ID}" >/dev/null
|
pushd "${ID}" >/dev/null
|
||||||
} || {
|
} || {
|
||||||
pushd "${ID_LIKE}" >/dev/null
|
[[ -n ${ID_LIKE} ]] && for ID in ${ID_LIKE}; do
|
||||||
|
pushd "${ID}" >/dev/null && break
|
||||||
|
done
|
||||||
} || {
|
} || {
|
||||||
echo "Unsupported OS: ${ID} ${VERSION_CODENAME} (${ID_LIKE})"
|
echo "Unsupported OS: ${ID} ${VERSION_CODENAME} (${ID_LIKE})"
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
After=casaos-gateway.service
|
After=casaos-message-bus.service
|
||||||
|
After=rclone.service
|
||||||
ConditionFileNotEmpty=/etc/casaos/casaos.conf
|
ConditionFileNotEmpty=/etc/casaos/casaos.conf
|
||||||
Description=CasaOS Main Service
|
Description=CasaOS Main Service
|
||||||
|
|
||||||
|
|||||||
12
build/sysroot/usr/lib/systemd/system/rclone.service
Normal file
12
build/sysroot/usr/lib/systemd/system/rclone.service
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=rclone
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStartPre=/usr/bin/mkdir -p /var/run/rclone
|
||||||
|
ExecStartPre=/usr/bin/rm -f /var/run/rclone/rclone.sock
|
||||||
|
ExecStart=/usr/bin/rclone rcd --rc-addr unix:///var/run/rclone/rclone.sock --rc-no-auth --rc-allow-origin "*"
|
||||||
|
Restart=always
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -26,7 +26,9 @@ __get_setup_script_directory_by_os_release() {
|
|||||||
} || {
|
} || {
|
||||||
pushd "${ID}" &>/dev/null
|
pushd "${ID}" &>/dev/null
|
||||||
} || {
|
} || {
|
||||||
pushd "${ID_LIKE}" &>/dev/null
|
[[ -n ${ID_LIKE} ]] && for ID in ${ID_LIKE}; do
|
||||||
|
pushd "${ID}" >/dev/null && break
|
||||||
|
done
|
||||||
} || {
|
} || {
|
||||||
echo "Unsupported OS: ${ID} ${VERSION_CODENAME} (${ID_LIKE})"
|
echo "Unsupported OS: ${ID} ${VERSION_CODENAME} (${ID_LIKE})"
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ GetNetCard() {
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if [ -d "/sys/devices/virtual/net" ] && [ -d "/sys/class/net" ]; then
|
if [ -d "/sys/devices/virtual/net" ] && [ -d "/sys/class/net" ]; then
|
||||||
ls /sys/class/net/ | grep -v "$(ls /sys/devices/virtual/net/)"
|
ls /sys/class/net/ | grep -v "$(ls /sys/devices/virtual/net/)" -w
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -66,18 +66,6 @@ GetLocalJoinNetworks() {
|
|||||||
zerotier-cli listnetworks -j
|
zerotier-cli listnetworks -j
|
||||||
}
|
}
|
||||||
|
|
||||||
#移除挂载点,删除已挂在的文件夹
|
|
||||||
UMountPorintAndRemoveDir() {
|
|
||||||
DEVICE=$1
|
|
||||||
MOUNT_POINT=$(mount | grep ${DEVICE} | awk '{ print $3 }')
|
|
||||||
if [[ -z ${MOUNT_POINT} ]]; then
|
|
||||||
${log} "Warning: ${DEVICE} is not mounted"
|
|
||||||
else
|
|
||||||
umount -lf ${DEVICE}
|
|
||||||
/bin/rmdir "${MOUNT_POINT}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
#格式化fat32磁盘
|
#格式化fat32磁盘
|
||||||
#param 需要格式化的目录 /dev/sda1
|
#param 需要格式化的目录 /dev/sda1
|
||||||
#param 格式
|
#param 格式
|
||||||
@@ -133,11 +121,7 @@ GetPlugInDisk() {
|
|||||||
fdisk -l | grep 'Disk' | grep 'sd' | awk -F , '{print substr($1,11,3)}'
|
fdisk -l | grep 'Disk' | grep 'sd' | awk -F , '{print substr($1,11,3)}'
|
||||||
}
|
}
|
||||||
|
|
||||||
#获取磁盘状态
|
|
||||||
#param 磁盘路径
|
|
||||||
GetDiskHealthState() {
|
|
||||||
smartctl -H $1 | grep "SMART Health Status" | awk -F ":" '{print$2}'
|
|
||||||
}
|
|
||||||
|
|
||||||
#获取磁盘字节数量和扇区数量
|
#获取磁盘字节数量和扇区数量
|
||||||
#param 磁盘路径 /dev/sda
|
#param 磁盘路径 /dev/sda
|
||||||
@@ -370,19 +354,6 @@ MountCIFS(){
|
|||||||
$sudo_cmd mount -t cifs -o username=$1,password=$6,port=$4 //$2/$3 $5
|
$sudo_cmd mount -t cifs -o username=$1,password=$6,port=$4 //$2/$3 $5
|
||||||
}
|
}
|
||||||
|
|
||||||
# $1:service name
|
|
||||||
CheckServiceStatus(){
|
|
||||||
rs="`systemctl status $1 |grep -E 'Active|PID'`"
|
|
||||||
#echo "$rs"
|
|
||||||
run="`echo "$rs" |grep -B 2 'running'`"
|
|
||||||
fai="`echo "$rs" |grep -E -B 2 'failed|inactive|dead'`"
|
|
||||||
if [ "$run" == "" ]
|
|
||||||
then
|
|
||||||
echo "failed"
|
|
||||||
else
|
|
||||||
echo "running"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
UDEVILUmount(){
|
UDEVILUmount(){
|
||||||
$sudo_cmd udevil umount -f $1
|
$sudo_cmd udevil umount -f $1
|
||||||
}
|
}
|
||||||
@@ -17,10 +17,10 @@ import (
|
|||||||
|
|
||||||
interfaces "github.com/IceWhaleTech/CasaOS-Common"
|
interfaces "github.com/IceWhaleTech/CasaOS-Common"
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/utils/systemctl"
|
"github.com/IceWhaleTech/CasaOS-Common/utils/systemctl"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/common"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/config"
|
"github.com/IceWhaleTech/CasaOS/pkg/config"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/sqlite"
|
"github.com/IceWhaleTech/CasaOS/pkg/sqlite"
|
||||||
"github.com/IceWhaleTech/CasaOS/service"
|
"github.com/IceWhaleTech/CasaOS/service"
|
||||||
"github.com/IceWhaleTech/CasaOS/types"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ func init() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *versionFlag {
|
if *versionFlag {
|
||||||
fmt.Println("v" + types.CURRENTVERSION)
|
fmt.Println("v" + common.VERSION)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func init() {
|
|||||||
sqliteDB = sqlite.GetDb(dbFlag)
|
sqliteDB = sqlite.GetDb(dbFlag)
|
||||||
// gredis.GetRedisConn(config.RedisInfo),
|
// gredis.GetRedisConn(config.RedisInfo),
|
||||||
|
|
||||||
service.MyService = service.NewService(sqliteDB, "", nil)
|
service.MyService = service.NewService(sqliteDB, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -1,46 +1,13 @@
|
|||||||
/*
|
|
||||||
* @Author: LinkLeong link@icewhale.org
|
|
||||||
* @Date: 2022-08-24 17:36:00
|
|
||||||
* @LastEditors: LinkLeong
|
|
||||||
* @LastEditTime: 2022-09-05 11:24:27
|
|
||||||
* @FilePath: /CasaOS/cmd/migration-tool/migration-034-035.go
|
|
||||||
* @Description:
|
|
||||||
* @Website: https://www.casaos.io
|
|
||||||
* Copyright (c) 2022 by icewhale, All Rights Reserved.
|
|
||||||
*/
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
interfaces "github.com/IceWhaleTech/CasaOS-Common"
|
interfaces "github.com/IceWhaleTech/CasaOS-Common"
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/utils/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type migrationTool struct{}
|
type migrationTool struct{}
|
||||||
|
|
||||||
func (u *migrationTool) IsMigrationNeeded() (bool, error) {
|
func (u *migrationTool) IsMigrationNeeded() (bool, error) {
|
||||||
majorVersion, minorVersion, patchVersion, err := version.DetectLegacyVersion()
|
return false, nil
|
||||||
if err != nil {
|
|
||||||
if err == version.ErrLegacyVersionNotFound {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if majorVersion > 0 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if minorVersion > 3 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if minorVersion == 3 && patchVersion > 5 {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Info("Migration is needed for a CasaOS version 0.3.5 and older...")
|
|
||||||
return true, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *migrationTool) PreMigrate() error {
|
func (u *migrationTool) PreMigrate() error {
|
||||||
|
|||||||
210
codegen/casaos_api.go
Normal file
210
codegen/casaos_api.go
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
// Package codegen provides primitives to interact with the openapi HTTP API.
|
||||||
|
//
|
||||||
|
// Code generated by github.com/deepmap/oapi-codegen version v1.12.4 DO NOT EDIT.
|
||||||
|
package codegen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/getkin/kin-openapi/openapi3"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Access_tokenScopes = "access_token.Scopes"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseResponse defines model for BaseResponse.
|
||||||
|
type BaseResponse struct {
|
||||||
|
// Message message returned by server side if there is any
|
||||||
|
Message *string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthServices defines model for HealthServices.
|
||||||
|
type HealthServices struct {
|
||||||
|
NotRunning *[]string `json:"not_running,omitempty"`
|
||||||
|
Running *[]string `json:"running,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHealthServicesOK defines model for GetHealthServicesOK.
|
||||||
|
type GetHealthServicesOK struct {
|
||||||
|
Data *HealthServices `json:"data,omitempty"`
|
||||||
|
|
||||||
|
// Message message returned by server side if there is any
|
||||||
|
Message *string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseInternalServerError defines model for ResponseInternalServerError.
|
||||||
|
type ResponseInternalServerError = BaseResponse
|
||||||
|
|
||||||
|
// ResponseOK defines model for ResponseOK.
|
||||||
|
type ResponseOK = BaseResponse
|
||||||
|
|
||||||
|
// ServerInterface represents all server handlers.
|
||||||
|
type ServerInterface interface {
|
||||||
|
// Test file methods
|
||||||
|
// (GET /file/test)
|
||||||
|
GetFileTest(ctx echo.Context) error
|
||||||
|
// Get service status
|
||||||
|
// (GET /health/services)
|
||||||
|
GetHealthServices(ctx echo.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerInterfaceWrapper converts echo contexts to parameters.
|
||||||
|
type ServerInterfaceWrapper struct {
|
||||||
|
Handler ServerInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileTest converts echo context to params.
|
||||||
|
func (w *ServerInterfaceWrapper) GetFileTest(ctx echo.Context) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx.Set(Access_tokenScopes, []string{""})
|
||||||
|
|
||||||
|
// Invoke the callback with all the unmarshalled arguments
|
||||||
|
err = w.Handler.GetFileTest(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHealthServices converts echo context to params.
|
||||||
|
func (w *ServerInterfaceWrapper) GetHealthServices(ctx echo.Context) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx.Set(Access_tokenScopes, []string{""})
|
||||||
|
|
||||||
|
// Invoke the callback with all the unmarshalled arguments
|
||||||
|
err = w.Handler.GetHealthServices(ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a simple interface which specifies echo.Route addition functions which
|
||||||
|
// are present on both echo.Echo and echo.Group, since we want to allow using
|
||||||
|
// either of them for path registration
|
||||||
|
type EchoRouter interface {
|
||||||
|
CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||||
|
DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||||
|
GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||||
|
HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||||
|
OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||||
|
PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||||
|
POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||||
|
PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||||
|
TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterHandlers adds each server route to the EchoRouter.
|
||||||
|
func RegisterHandlers(router EchoRouter, si ServerInterface) {
|
||||||
|
RegisterHandlersWithBaseURL(router, si, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers handlers, and prepends BaseURL to the paths, so that the paths
|
||||||
|
// can be served under a prefix.
|
||||||
|
func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) {
|
||||||
|
|
||||||
|
wrapper := ServerInterfaceWrapper{
|
||||||
|
Handler: si,
|
||||||
|
}
|
||||||
|
|
||||||
|
router.GET(baseURL+"/file/test", wrapper.GetFileTest)
|
||||||
|
router.GET(baseURL+"/health/services", wrapper.GetHealthServices)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base64 encoded, gzipped, json marshaled Swagger object
|
||||||
|
var swaggerSpec = []string{
|
||||||
|
|
||||||
|
"H4sIAAAAAAAC/7xW70/jRhD9V1bTfoDKxBGoUmXpPnC9wiFUpSpIrUSi3GY9sfewd92ZcSBF/t+rXZtL",
|
||||||
|
"SCiC649PiffHe2/e7szsAxhfN96hE4bsAQi58Y4xfpyjfERdSXmFtLIGeXIZho13gk7CX900lTVarHfp",
|
||||||
|
"Z/YujLEpsdZxtqomS8huHuBbwiVk8E26YUv7dZy+14y/DrTQJQ/QkG+QxPYici0R7CWIpyqh67pZ13UJ",
|
||||||
|
"5MiGbBPkQQaTS+gSeKS6cILkdBV2If1E5OlNwb0+pH0lj9yqJ1c9+5a4Nxr9T7QEV7pkAIuOP9mR7Z5H",
|
||||||
|
"jcy6iBNPgYYJRSgtOczVYq24j49tjsoulZRIqCwr7daQAN7ruqkQMoAECHU+cdUaMqEWE5B1E2ZYyLqi",
|
||||||
|
"F75zzHvSnJc5tc6FDdkDWME6jm94jGbtecQ9BOyxfBnQRHodvl+Dd1RowTu9fj1uDIfRtGRlfRWs7yPQ",
|
||||||
|
"xiDzXPwtxiO2wdgSdY4ECThdB4zTVkpP9s94GzZcurGXuO6dsm7p909o2o7HJ6axRlrC+IFTp5RS/QT7",
|
||||||
|
"lgyqGnOr303hoCFcIvGR8ZWno3hBMFO5ptvDKSgmwyjvplCKNJylKem7UWGlbBctIw13d2R8nV4Y/K3U",
|
||||||
|
"FV6jKdPKFz6ttXVpb97wM19o55DmAX7ubFHK/IfxuLkfNa6YwteKrQLQf6hW7mykmC+qFl8WbOtC6SpI",
|
||||||
|
"+FGznlz1ov5/Rb2adOcWTF2vSp3+cqEa8iubI6vassGq0g59y6pGKX3OaulJ5Xa5REInig06TdbzKKCc",
|
||||||
|
"eVKWucWQ47nKLZuW2XrHiWoq1IxqZdlKKAXq5tzKx3ahCBvPVjytZwePbvRO7IffyzxUntRnb5268S2p",
|
||||||
|
"D5aNp3yzO+8HRkWR3ro/TheL9wv8/XA0jeliJebuJmBIYIXEfZKsjkO6+gadbixkcDIaj04ggUZLGXM0",
|
||||||
|
"XdoKU0GOhblA2U+0a2RRYdmjZyOIkBRT9iKHLPTWMxtiYonFb6vtHo/Hf1fUv6xLtzpFl8D3b9nyXOeL",
|
||||||
|
"9aita03r5/QH23TBkN3A2fbwLOxLy1iXU94qzM/aco6ihnqqWLS0rPxSoTal+jRU0u8+qQHmWct2OsDX",
|
||||||
|
"GPfco+bfdzCEOgQyhLplYc+/beJWN4jvpad94GbWzcKCQMZxvqUKMkhXx0P2Q1gwwO+6fnA9+TA53LSP",
|
||||||
|
"Hfbw4np5w5MTD0T3R6KLc/Jt0/MN637euyt7gc66vwIAAP//o5zNVnEKAAA=",
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSwagger returns the content of the embedded swagger specification file
|
||||||
|
// or error if failed to decode
|
||||||
|
func decodeSpec() ([]byte, error) {
|
||||||
|
zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error base64 decoding spec: %s", err)
|
||||||
|
}
|
||||||
|
zr, err := gzip.NewReader(bytes.NewReader(zipped))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error decompressing spec: %s", err)
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, err = buf.ReadFrom(zr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error decompressing spec: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawSpec = decodeSpecCached()
|
||||||
|
|
||||||
|
// a naive cached of a decoded swagger spec
|
||||||
|
func decodeSpecCached() func() ([]byte, error) {
|
||||||
|
data, err := decodeSpec()
|
||||||
|
return func() ([]byte, error) {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
|
||||||
|
func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
|
||||||
|
var res = make(map[string]func() ([]byte, error))
|
||||||
|
if len(pathToFile) > 0 {
|
||||||
|
res[pathToFile] = rawSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSwagger returns the Swagger specification corresponding to the generated code
|
||||||
|
// in this file. The external references of Swagger specification are resolved.
|
||||||
|
// The logic of resolving external references is tightly connected to "import-mapping" feature.
|
||||||
|
// Externally referenced files must be embedded in the corresponding golang packages.
|
||||||
|
// Urls can be supported but this task was out of the scope.
|
||||||
|
func GetSwagger() (swagger *openapi3.T, err error) {
|
||||||
|
var resolvePath = PathToRawSpec("")
|
||||||
|
|
||||||
|
loader := openapi3.NewLoader()
|
||||||
|
loader.IsExternalRefsAllowed = true
|
||||||
|
loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
|
||||||
|
var pathToFile = url.String()
|
||||||
|
pathToFile = path.Clean(pathToFile)
|
||||||
|
getSpec, ok := resolvePath[pathToFile]
|
||||||
|
if !ok {
|
||||||
|
err1 := fmt.Errorf("path not found: %s", pathToFile)
|
||||||
|
return nil, err1
|
||||||
|
}
|
||||||
|
return getSpec()
|
||||||
|
}
|
||||||
|
var specData []byte
|
||||||
|
specData, err = rawSpec()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
swagger, err = loader.LoadFromData(specData)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
2254
codegen/message_bus/api.go
Normal file
2254
codegen/message_bus/api.go
Normal file
File diff suppressed because it is too large
Load Diff
7
common/constants.go
Normal file
7
common/constants.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
const (
|
||||||
|
SERVICENAME = "casaos"
|
||||||
|
VERSION = "0.4.2"
|
||||||
|
BODY = " "
|
||||||
|
)
|
||||||
23
common/message.go
Normal file
23
common/message.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/codegen/message_bus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// devtype -> action -> event
|
||||||
|
EventTypes map[string]map[string]message_bus.EventType
|
||||||
|
|
||||||
|
PropertyNameLookupMaps = map[string]map[string]string{
|
||||||
|
"system": {
|
||||||
|
fmt.Sprintf("%s:%s", SERVICENAME, "utilization"): "ID_BUS",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionPastTense = map[string]string{
|
||||||
|
"add": "added",
|
||||||
|
"remove": "removed",
|
||||||
|
}
|
||||||
|
)
|
||||||
12
drivers/all.go
Normal file
12
drivers/all.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package drivers
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/IceWhaleTech/CasaOS/drivers/dropbox"
|
||||||
|
_ "github.com/IceWhaleTech/CasaOS/drivers/google_drive"
|
||||||
|
)
|
||||||
|
|
||||||
|
// All do nothing,just for import
|
||||||
|
// same as _ import
|
||||||
|
func All() {
|
||||||
|
|
||||||
|
}
|
||||||
30
drivers/base/client.go
Normal file
30
drivers/base/client.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var NoRedirectClient *resty.Client
|
||||||
|
var RestyClient = NewRestyClient()
|
||||||
|
var HttpClient = &http.Client{}
|
||||||
|
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||||
|
var DefaultTimeout = time.Second * 30
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
NoRedirectClient = resty.New().SetRedirectPolicy(
|
||||||
|
resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
NoRedirectClient.SetHeader("user-agent", UserAgent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRestyClient() *resty.Client {
|
||||||
|
return resty.New().
|
||||||
|
SetHeader("user-agent", UserAgent).
|
||||||
|
SetRetryCount(3).
|
||||||
|
SetTimeout(DefaultTimeout)
|
||||||
|
}
|
||||||
12
drivers/base/types.go
Normal file
12
drivers/base/types.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import "github.com/go-resty/resty/v2"
|
||||||
|
|
||||||
|
type Json map[string]interface{}
|
||||||
|
|
||||||
|
type TokenResp struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReqCallback func(req *resty.Request)
|
||||||
100
drivers/dropbox/drive.go
Normal file
100
drivers/dropbox/drive.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package dropbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/driver"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/pkg/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Dropbox struct {
|
||||||
|
model.StorageA
|
||||||
|
Addition
|
||||||
|
AccessToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) GetAddition() driver.Additional {
|
||||||
|
return &d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) Init(ctx context.Context) error {
|
||||||
|
if len(d.RefreshToken) == 0 {
|
||||||
|
d.getRefreshToken()
|
||||||
|
}
|
||||||
|
return d.refreshToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) Drop(ctx context.Context) error {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
files, err := d.getFiles(dir.GetID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
|
||||||
|
return fileToObj(src), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
url := "https://content.dropboxapi.com/2/files/download"
|
||||||
|
link := model.Link{
|
||||||
|
URL: url,
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Header: http.Header{
|
||||||
|
"Authorization": []string{"Bearer " + d.AccessToken},
|
||||||
|
"Dropbox-API-Arg": []string{`{"path": "` + file.GetPath() + `"}`},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return &link, nil
|
||||||
|
}
|
||||||
|
func (d *Dropbox) GetUserInfo(ctx context.Context) (string, error) {
|
||||||
|
url := "https://api.dropboxapi.com/2/users/get_current_account"
|
||||||
|
user := UserInfo{}
|
||||||
|
resp, err := d.request(url, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetHeader("Content-Type", "")
|
||||||
|
}, &user)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
logger.Info("resp", zap.Any("resp", string(resp)))
|
||||||
|
return user.Email, nil
|
||||||
|
}
|
||||||
|
func (d *Dropbox) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
return errors.New("not support")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dropbox) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*Dropbox)(nil)
|
||||||
33
drivers/dropbox/meta.go
Normal file
33
drivers/dropbox/meta.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package dropbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/driver"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ICONURL = "./img/driver/Dropbox.svg"
|
||||||
|
const APPKEY = "tciqajyazzdygt9"
|
||||||
|
const APPSECRET = "e7gtmv441cwdf0n"
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
driver.RootID
|
||||||
|
RefreshToken string `json:"refresh_token" required:"true" omit:"true"`
|
||||||
|
AppKey string `json:"app_key" type:"string" default:"tciqajyazzdygt9" omit:"true"`
|
||||||
|
AppSecret string `json:"app_secret" type:"string" default:"e7gtmv441cwdf0n" omit:"true"`
|
||||||
|
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" omit:"true"`
|
||||||
|
AuthUrl string `json:"auth_url" type:"string" default:"https://www.dropbox.com/oauth2/authorize?client_id=tciqajyazzdygt9&redirect_uri=https://cloudoauth.files.casaos.app&response_type=code&token_access_type=offline&state=${HOST}%2Fv1%2Frecover%2FDropbox&&force_reapprove=true&force_reauthentication=true"`
|
||||||
|
Icon string `json:"icon" type:"string" default:"./img/driver/Dropbox.svg"`
|
||||||
|
Code string `json:"code" type:"string" help:"code from auth_url" omit:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "Dropbox",
|
||||||
|
OnlyProxy: true,
|
||||||
|
DefaultRoot: "root",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(func() driver.Driver {
|
||||||
|
return &Dropbox{}
|
||||||
|
})
|
||||||
|
}
|
||||||
88
drivers/dropbox/types.go
Normal file
88
drivers/dropbox/types.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package dropbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserInfo struct {
|
||||||
|
AccountID string `json:"account_id"`
|
||||||
|
Name struct {
|
||||||
|
GivenName string `json:"given_name"`
|
||||||
|
Surname string `json:"surname"`
|
||||||
|
FamiliarName string `json:"familiar_name"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
AbbreviatedName string `json:"abbreviated_name"`
|
||||||
|
} `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
EmailVerified bool `json:"email_verified"`
|
||||||
|
Disabled bool `json:"disabled"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
Locale string `json:"locale"`
|
||||||
|
ReferralLink string `json:"referral_link"`
|
||||||
|
IsPaired bool `json:"is_paired"`
|
||||||
|
AccountType struct {
|
||||||
|
Tag string `json:".tag"`
|
||||||
|
} `json:"account_type"`
|
||||||
|
RootInfo struct {
|
||||||
|
Tag string `json:".tag"`
|
||||||
|
RootNamespaceID string `json:"root_namespace_id"`
|
||||||
|
HomeNamespaceID string `json:"home_namespace_id"`
|
||||||
|
} `json:"root_info"`
|
||||||
|
}
|
||||||
|
type TokenError struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
ErrorDescription string `json:"error_description"`
|
||||||
|
}
|
||||||
|
type File struct {
|
||||||
|
Tag string `json:".tag"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
PathLower string `json:"path_lower"`
|
||||||
|
PathDisplay string `json:"path_display"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
ClientModified time.Time `json:"client_modified,omitempty"`
|
||||||
|
ServerModified time.Time `json:"server_modified,omitempty"`
|
||||||
|
Rev string `json:"rev,omitempty"`
|
||||||
|
Size int `json:"size,omitempty"`
|
||||||
|
IsDownloadable bool `json:"is_downloadable,omitempty"`
|
||||||
|
ContentHash string `json:"content_hash,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Files struct {
|
||||||
|
Files []File `json:"entries"`
|
||||||
|
Cursor string `json:"cursor"`
|
||||||
|
HasMore bool `json:"has_more"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Error struct {
|
||||||
|
Errors []struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
LocationType string `json:"location_type"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
}
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileToObj(f File) *model.ObjThumb {
|
||||||
|
logger.Info("dropbox file", zap.Any("file", f))
|
||||||
|
obj := &model.ObjThumb{
|
||||||
|
Object: model.Object{
|
||||||
|
ID: f.ID,
|
||||||
|
Name: f.Name,
|
||||||
|
Size: int64(f.Size),
|
||||||
|
Modified: f.ClientModified,
|
||||||
|
IsFolder: f.Tag == "folder",
|
||||||
|
Path: f.PathDisplay,
|
||||||
|
},
|
||||||
|
Thumbnail: model.Thumbnail{},
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
102
drivers/dropbox/util.go
Normal file
102
drivers/dropbox/util.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package dropbox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/drivers/base"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Dropbox) getRefreshToken() error {
|
||||||
|
url := "https://api.dropbox.com/oauth2/token"
|
||||||
|
var resp base.TokenResp
|
||||||
|
var e TokenError
|
||||||
|
|
||||||
|
res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetFormData(map[string]string{
|
||||||
|
"code": d.Code,
|
||||||
|
"grant_type": "authorization_code",
|
||||||
|
"redirect_uri": "https://cloudoauth.files.casaos.app",
|
||||||
|
}).SetBasicAuth(d.Addition.AppKey, d.Addition.AppSecret).SetHeader("Content-Type", "application/x-www-form-urlencoded").Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Info("get refresh token", zap.String("res", res.String()))
|
||||||
|
if e.Error != "" {
|
||||||
|
return fmt.Errorf(e.Error)
|
||||||
|
}
|
||||||
|
d.RefreshToken = resp.RefreshToken
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
func (d *Dropbox) refreshToken() error {
|
||||||
|
url := "https://api.dropbox.com/oauth2/token"
|
||||||
|
var resp base.TokenResp
|
||||||
|
var e TokenError
|
||||||
|
|
||||||
|
res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetFormData(map[string]string{
|
||||||
|
"refresh_token": d.RefreshToken,
|
||||||
|
"grant_type": "refresh_token",
|
||||||
|
}).SetBasicAuth(d.Addition.AppKey, d.Addition.AppSecret).SetHeader("Content-Type", "application/x-www-form-urlencoded").Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Info("get refresh token", zap.String("res", res.String()))
|
||||||
|
if e.Error != "" {
|
||||||
|
return fmt.Errorf(e.Error)
|
||||||
|
}
|
||||||
|
d.AccessToken = resp.AccessToken
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
func (d *Dropbox) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||||
|
req := base.RestyClient.R()
|
||||||
|
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
||||||
|
req.SetHeader("Content-Type", "application/json")
|
||||||
|
if callback != nil {
|
||||||
|
callback(req)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
req.SetResult(resp)
|
||||||
|
}
|
||||||
|
var e Error
|
||||||
|
req.SetError(&e)
|
||||||
|
res, err := req.Execute(method, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if e.Error.Code != 0 {
|
||||||
|
if e.Error.Code == 401 {
|
||||||
|
err = d.refreshToken()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.request(url, method, callback, resp)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
|
||||||
|
}
|
||||||
|
return res.Body(), nil
|
||||||
|
}
|
||||||
|
func (d *Dropbox) getFiles(path string) ([]File, error) {
|
||||||
|
|
||||||
|
res := make([]File, 0)
|
||||||
|
var resp Files
|
||||||
|
body := base.Json{
|
||||||
|
"limit": 2000,
|
||||||
|
"path": path,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := d.request("https://api.dropboxapi.com/2/files/list_folder", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(body)
|
||||||
|
}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = append(res, resp.Files...)
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
183
drivers/google_drive/drive.go
Normal file
183
drivers/google_drive/drive.go
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
package google_drive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/drivers/base"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/driver"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/pkg/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GoogleDrive struct {
|
||||||
|
model.StorageA
|
||||||
|
Addition
|
||||||
|
AccessToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) GetAddition() driver.Additional {
|
||||||
|
return &d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) Init(ctx context.Context) error {
|
||||||
|
if d.ChunkSize == 0 {
|
||||||
|
d.ChunkSize = 5
|
||||||
|
}
|
||||||
|
if len(d.RefreshToken) == 0 {
|
||||||
|
d.getRefreshToken()
|
||||||
|
}
|
||||||
|
return d.refreshToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) Drop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
files, err := d.getFiles(dir.GetID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
|
||||||
|
return fileToObj(src), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.GetID())
|
||||||
|
_, err := d.request(url, http.MethodGet, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
link := model.Link{
|
||||||
|
Method: http.MethodGet,
|
||||||
|
URL: url + "&alt=media",
|
||||||
|
Header: http.Header{
|
||||||
|
"Authorization": []string{"Bearer " + d.AccessToken},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return &link, nil
|
||||||
|
}
|
||||||
|
func (d *GoogleDrive) GetUserInfo(ctx context.Context) (string, error) {
|
||||||
|
url := "https://content.googleapis.com/drive/v3/about?fields=user"
|
||||||
|
user := UserInfo{}
|
||||||
|
resp, err := d.request(url, http.MethodGet, nil, &user)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
logger.Info("resp", zap.Any("resp", resp))
|
||||||
|
return user.User.EmailAddress, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
data := base.Json{
|
||||||
|
"name": dirName,
|
||||||
|
"parents": []string{parentDir.GetID()},
|
||||||
|
"mimeType": "application/vnd.google-apps.folder",
|
||||||
|
}
|
||||||
|
_, err := d.request("https://www.googleapis.com/drive/v3/files", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(data)
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
query := map[string]string{
|
||||||
|
"addParents": dstDir.GetID(),
|
||||||
|
"removeParents": "root",
|
||||||
|
}
|
||||||
|
url := "https://www.googleapis.com/drive/v3/files/" + srcObj.GetID()
|
||||||
|
_, err := d.request(url, http.MethodPatch, func(req *resty.Request) {
|
||||||
|
req.SetQueryParams(query)
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
data := base.Json{
|
||||||
|
"name": newName,
|
||||||
|
}
|
||||||
|
url := "https://www.googleapis.com/drive/v3/files/" + srcObj.GetID()
|
||||||
|
_, err := d.request(url, http.MethodPatch, func(req *resty.Request) {
|
||||||
|
req.SetBody(data)
|
||||||
|
}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
return errors.New("not support")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
url := "https://www.googleapis.com/drive/v3/files/" + obj.GetID()
|
||||||
|
_, err := d.request(url, http.MethodDelete, nil, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
obj := stream.GetOld()
|
||||||
|
var (
|
||||||
|
e Error
|
||||||
|
url string
|
||||||
|
data base.Json
|
||||||
|
res *resty.Response
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if obj != nil {
|
||||||
|
url = fmt.Sprintf("https://www.googleapis.com/upload/drive/v3/files/%s?uploadType=resumable&supportsAllDrives=true", obj.GetID())
|
||||||
|
data = base.Json{}
|
||||||
|
} else {
|
||||||
|
data = base.Json{
|
||||||
|
"name": stream.GetName(),
|
||||||
|
"parents": []string{dstDir.GetID()},
|
||||||
|
}
|
||||||
|
url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
|
||||||
|
}
|
||||||
|
req := base.NoRedirectClient.R().
|
||||||
|
SetHeaders(map[string]string{
|
||||||
|
"Authorization": "Bearer " + d.AccessToken,
|
||||||
|
"X-Upload-Content-Type": stream.GetMimetype(),
|
||||||
|
"X-Upload-Content-Length": strconv.FormatInt(stream.GetSize(), 10),
|
||||||
|
}).
|
||||||
|
SetError(&e).SetBody(data).SetContext(ctx)
|
||||||
|
if obj != nil {
|
||||||
|
res, err = req.Patch(url)
|
||||||
|
} else {
|
||||||
|
res, err = req.Post(url)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Error.Code != 0 {
|
||||||
|
if e.Error.Code == 401 {
|
||||||
|
err = d.refreshToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.Put(ctx, dstDir, stream, up)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
|
||||||
|
}
|
||||||
|
putUrl := res.Header().Get("location")
|
||||||
|
if stream.GetSize() < d.ChunkSize*1024*1024 {
|
||||||
|
_, err = d.request(putUrl, http.MethodPut, func(req *resty.Request) {
|
||||||
|
req.SetHeader("Content-Length", strconv.FormatInt(stream.GetSize(), 10)).SetBody(stream.GetReadCloser())
|
||||||
|
}, nil)
|
||||||
|
} else {
|
||||||
|
err = d.chunkUpload(ctx, stream, putUrl)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*GoogleDrive)(nil)
|
||||||
35
drivers/google_drive/meta.go
Normal file
35
drivers/google_drive/meta.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package google_drive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/driver"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ICONURL = "./img/driver/GoogleDrive.svg"
|
||||||
|
const CLIENTID = "921743327851-urr4f7jjfp4ts639evqb3i4m4qb4u4cc.apps.googleusercontent.com"
|
||||||
|
const CLIENTSECRET = "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"
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
driver.RootID
|
||||||
|
RefreshToken string `json:"refresh_token" required:"true" omit:"true"`
|
||||||
|
OrderBy string `json:"order_by" type:"string" help:"such as: folder,name,modifiedTime" omit:"true"`
|
||||||
|
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" omit:"true"`
|
||||||
|
ClientID string `json:"client_id" required:"true" default:"921743327851-urr4f7jjfp4ts639evqb3i4m4qb4u4cc.apps.googleusercontent.com" omit:"true"`
|
||||||
|
ClientSecret string `json:"client_secret" required:"true" default:"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" omit:"true"`
|
||||||
|
ChunkSize int64 `json:"chunk_size" type:"number" help:"chunk size while uploading (unit: MB)" omit:"true"`
|
||||||
|
AuthUrl string `json:"auth_url" type:"string" default:"https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?response_type=code&client_id=921743327851-urr4f7jjfp4ts639evqb3i4m4qb4u4cc.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fcloudoauth.files.casaos.app&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&access_type=offline&approval_prompt=force&state=${HOST}%2Fv1%2Frecover%2FGoogleDrive&service=lso&o2v=1&flowName=GeneralOAuthFlow"`
|
||||||
|
Icon string `json:"icon" type:"string" default:"./img/driver/GoogleDrive.svg"`
|
||||||
|
Code string `json:"code" type:"string" help:"code from auth_url" omit:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "GoogleDrive",
|
||||||
|
OnlyProxy: true,
|
||||||
|
DefaultRoot: "root",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(func() driver.Driver {
|
||||||
|
return &GoogleDrive{}
|
||||||
|
})
|
||||||
|
}
|
||||||
77
drivers/google_drive/types.go
Normal file
77
drivers/google_drive/types.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package google_drive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserInfo struct {
|
||||||
|
User struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
DisplayName string `json:"displayName"`
|
||||||
|
PhotoLink string `json:"photoLink"`
|
||||||
|
Me bool `json:"me"`
|
||||||
|
PermissionID string `json:"permissionId"`
|
||||||
|
EmailAddress string `json:"emailAddress"`
|
||||||
|
} `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenError struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
ErrorDescription string `json:"error_description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Files struct {
|
||||||
|
NextPageToken string `json:"nextPageToken"`
|
||||||
|
Files []File `json:"files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
MimeType string `json:"mimeType"`
|
||||||
|
ModifiedTime time.Time `json:"modifiedTime"`
|
||||||
|
Size string `json:"size"`
|
||||||
|
ThumbnailLink string `json:"thumbnailLink"`
|
||||||
|
ShortcutDetails struct {
|
||||||
|
TargetId string `json:"targetId"`
|
||||||
|
TargetMimeType string `json:"targetMimeType"`
|
||||||
|
} `json:"shortcutDetails"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileToObj(f File) *model.ObjThumb {
|
||||||
|
log.Debugf("google file: %+v", f)
|
||||||
|
size, _ := strconv.ParseInt(f.Size, 10, 64)
|
||||||
|
obj := &model.ObjThumb{
|
||||||
|
Object: model.Object{
|
||||||
|
ID: f.Id,
|
||||||
|
Name: f.Name,
|
||||||
|
Size: size,
|
||||||
|
Modified: f.ModifiedTime,
|
||||||
|
IsFolder: f.MimeType == "application/vnd.google-apps.folder",
|
||||||
|
},
|
||||||
|
Thumbnail: model.Thumbnail{},
|
||||||
|
}
|
||||||
|
if f.MimeType == "application/vnd.google-apps.shortcut" {
|
||||||
|
obj.ID = f.ShortcutDetails.TargetId
|
||||||
|
obj.IsFolder = f.ShortcutDetails.TargetMimeType == "application/vnd.google-apps.folder"
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Error struct {
|
||||||
|
Errors []struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
LocationType string `json:"location_type"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
}
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"error"`
|
||||||
|
}
|
||||||
152
drivers/google_drive/util.go
Normal file
152
drivers/google_drive/util.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
package google_drive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/drivers/base"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/pkg/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// do others that not defined in Driver interface
|
||||||
|
|
||||||
|
func (d *GoogleDrive) getRefreshToken() error {
|
||||||
|
url := "https://www.googleapis.com/oauth2/v4/token"
|
||||||
|
var resp base.TokenResp
|
||||||
|
var e TokenError
|
||||||
|
res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetFormData(map[string]string{
|
||||||
|
"client_id": d.ClientID,
|
||||||
|
"client_secret": d.ClientSecret,
|
||||||
|
"code": d.Code,
|
||||||
|
"grant_type": "authorization_code",
|
||||||
|
"redirect_uri": "https://cloudoauth.files.casaos.app",
|
||||||
|
}).Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Info("get refresh token", zap.String("res", res.String()))
|
||||||
|
if e.Error != "" {
|
||||||
|
return fmt.Errorf(e.Error)
|
||||||
|
}
|
||||||
|
d.RefreshToken = resp.RefreshToken
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) refreshToken() error {
|
||||||
|
url := "https://www.googleapis.com/oauth2/v4/token"
|
||||||
|
var resp base.TokenResp
|
||||||
|
var e TokenError
|
||||||
|
res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).
|
||||||
|
SetFormData(map[string]string{
|
||||||
|
"client_id": d.ClientID,
|
||||||
|
"client_secret": d.ClientSecret,
|
||||||
|
"refresh_token": d.RefreshToken,
|
||||||
|
"grant_type": "refresh_token",
|
||||||
|
}).Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debug(res.String())
|
||||||
|
if e.Error != "" {
|
||||||
|
return fmt.Errorf(e.Error)
|
||||||
|
}
|
||||||
|
d.AccessToken = resp.AccessToken
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||||
|
req := base.RestyClient.R()
|
||||||
|
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
||||||
|
req.SetQueryParam("includeItemsFromAllDrives", "true")
|
||||||
|
req.SetQueryParam("supportsAllDrives", "true")
|
||||||
|
if callback != nil {
|
||||||
|
callback(req)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
req.SetResult(resp)
|
||||||
|
}
|
||||||
|
var e Error
|
||||||
|
req.SetError(&e)
|
||||||
|
res, err := req.Execute(method, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if e.Error.Code != 0 {
|
||||||
|
if e.Error.Code == 401 {
|
||||||
|
err = d.refreshToken()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.request(url, method, callback, resp)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
|
||||||
|
}
|
||||||
|
return res.Body(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) getFiles(id string) ([]File, error) {
|
||||||
|
pageToken := "first"
|
||||||
|
res := make([]File, 0)
|
||||||
|
for pageToken != "" {
|
||||||
|
if pageToken == "first" {
|
||||||
|
pageToken = ""
|
||||||
|
}
|
||||||
|
var resp Files
|
||||||
|
orderBy := "folder,name,modifiedTime desc"
|
||||||
|
if d.OrderBy != "" {
|
||||||
|
orderBy = d.OrderBy + " " + d.OrderDirection
|
||||||
|
}
|
||||||
|
query := map[string]string{
|
||||||
|
"orderBy": orderBy,
|
||||||
|
"fields": "files(id,name,mimeType,size,modifiedTime,thumbnailLink,shortcutDetails),nextPageToken",
|
||||||
|
"pageSize": "1000",
|
||||||
|
"q": fmt.Sprintf("'%s' in parents and trashed = false", id),
|
||||||
|
//"includeItemsFromAllDrives": "true",
|
||||||
|
//"supportsAllDrives": "true",
|
||||||
|
"pageToken": pageToken,
|
||||||
|
}
|
||||||
|
_, err := d.request("https://www.googleapis.com/drive/v3/files", http.MethodGet, func(req *resty.Request) {
|
||||||
|
req.SetQueryParams(query)
|
||||||
|
}, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pageToken = resp.NextPageToken
|
||||||
|
res = append(res, resp.Files...)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *GoogleDrive) chunkUpload(ctx context.Context, stream model.FileStreamer, url string) error {
|
||||||
|
var defaultChunkSize = d.ChunkSize * 1024 * 1024
|
||||||
|
var finish int64 = 0
|
||||||
|
for finish < stream.GetSize() {
|
||||||
|
if utils.IsCanceled(ctx) {
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
chunkSize := stream.GetSize() - finish
|
||||||
|
if chunkSize > defaultChunkSize {
|
||||||
|
chunkSize = defaultChunkSize
|
||||||
|
}
|
||||||
|
_, err := d.request(url, http.MethodPut, func(req *resty.Request) {
|
||||||
|
req.SetHeaders(map[string]string{
|
||||||
|
"Content-Length": strconv.FormatInt(chunkSize, 10),
|
||||||
|
"Content-Range": fmt.Sprintf("bytes %d-%d/%d", finish, finish+chunkSize-1, stream.GetSize()),
|
||||||
|
}).SetBody(io.LimitReader(stream.GetReadCloser(), chunkSize)).SetContext(ctx)
|
||||||
|
}, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
finish += chunkSize
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
125
go.mod
125
go.mod
@@ -1,36 +1,133 @@
|
|||||||
module github.com/IceWhaleTech/CasaOS
|
module github.com/IceWhaleTech/CasaOS
|
||||||
|
|
||||||
go 1.16
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d
|
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d
|
||||||
github.com/IceWhaleTech/CasaOS-Common v0.4.1-alpha3
|
github.com/IceWhaleTech/CasaOS-Common v0.4.2-alpha3
|
||||||
github.com/benbjohnson/clock v1.3.0 // indirect
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||||
|
github.com/deckarep/golang-set/v2 v2.3.0
|
||||||
|
github.com/deepmap/oapi-codegen v1.12.4
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/dsoprea/go-exif/v3 v3.0.0-20221012082141-d21ac8e2de85
|
github.com/dsoprea/go-exif/v3 v3.0.0-20221012082141-d21ac8e2de85
|
||||||
|
github.com/getkin/kin-openapi v0.115.0
|
||||||
github.com/gin-contrib/gzip v0.0.6
|
github.com/gin-contrib/gzip v0.0.6
|
||||||
github.com/gin-gonic/gin v1.8.2
|
github.com/gin-gonic/gin v1.9.0
|
||||||
github.com/glebarez/sqlite v1.6.0
|
github.com/glebarez/sqlite v1.7.0
|
||||||
github.com/go-ini/ini v1.67.0
|
github.com/go-ini/ini v1.67.0
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0
|
||||||
github.com/golang/mock v1.6.0
|
github.com/golang/mock v1.6.0
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
|
||||||
github.com/gomodule/redigo v1.8.9
|
github.com/gomodule/redigo v1.8.9
|
||||||
github.com/google/go-github/v36 v36.0.0
|
github.com/google/go-github/v36 v36.0.0
|
||||||
github.com/googollee/go-socket.io v1.6.2
|
github.com/googollee/go-socket.io v1.7.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
|
github.com/h2non/filetype v1.1.3
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0
|
github.com/hirochachacha/go-smb2 v1.1.0
|
||||||
github.com/klauspost/compress v1.15.13 // indirect
|
github.com/json-iterator/go v1.1.12
|
||||||
|
github.com/labstack/echo/v4 v4.10.2
|
||||||
|
github.com/maruel/natural v1.1.0
|
||||||
github.com/mholt/archiver/v3 v3.5.1
|
github.com/mholt/archiver/v3 v3.5.1
|
||||||
|
github.com/mileusna/useragent v1.2.1
|
||||||
|
github.com/moby/sys/mount v0.3.3
|
||||||
|
github.com/moby/sys/mountinfo v0.6.2
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/robfig/cron v1.2.0
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/shirou/gopsutil/v3 v3.22.11
|
github.com/shirou/gopsutil/v3 v3.23.2
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/tidwall/gjson v1.14.4
|
github.com/tidwall/gjson v1.14.4
|
||||||
|
go.uber.org/goleak v1.2.1
|
||||||
go.uber.org/zap v1.24.0
|
go.uber.org/zap v1.24.0
|
||||||
golang.org/x/crypto v0.4.0
|
golang.org/x/crypto v0.7.0
|
||||||
golang.org/x/oauth2 v0.3.0
|
golang.org/x/oauth2 v0.6.0
|
||||||
gorm.io/gorm v1.24.2
|
golang.org/x/sync v0.1.0
|
||||||
|
gorm.io/gorm v1.24.6
|
||||||
gotest.tools v2.2.0+incompatible
|
gotest.tools v2.2.0+incompatible
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||||
|
github.com/benbjohnson/clock v1.3.0 // indirect
|
||||||
|
github.com/bytedance/sonic v1.8.5 // indirect
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0 // 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/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/geoffgarside/ber v1.1.0 // indirect
|
||||||
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
|
github.com/glebarez/go-sqlite v1.21.0 // indirect
|
||||||
|
github.com/go-errors/errors v1.4.2 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||||
|
github.com/go-openapi/swag v0.22.3 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
||||||
|
github.com/goccy/go-json v0.10.1 // indirect
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
|
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||||
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
|
github.com/invopop/yaml v0.2.0 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.16.3 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||||
|
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||||
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
|
github.com/labstack/gommon v0.4.0 // indirect
|
||||||
|
github.com/leodido/go-urn v1.2.2 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||||
|
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
|
||||||
|
github.com/perimeterx/marshmallow v1.1.4 // indirect
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
|
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
go.uber.org/atomic v1.10.0 // indirect
|
||||||
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
|
golang.org/x/image v0.6.0 // indirect
|
||||||
|
golang.org/x/net v0.8.0 // indirect
|
||||||
|
golang.org/x/sys v0.6.0 // indirect
|
||||||
|
golang.org/x/text v0.8.0 // indirect
|
||||||
|
golang.org/x/time v0.3.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/protobuf v1.30.0 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
modernc.org/libc v1.22.3 // indirect
|
||||||
|
modernc.org/mathutil v1.5.0 // indirect
|
||||||
|
modernc.org/memory v1.5.0 // indirect
|
||||||
|
modernc.org/sqlite v1.21.0 // indirect
|
||||||
|
)
|
||||||
|
|||||||
341
go.sum
341
go.sum
@@ -1,26 +1,34 @@
|
|||||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
|
||||||
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
|
|
||||||
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
|
||||||
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d h1:62lEBImTxZ83pgzywgDNIrPPuQ+j4ep9QjqrWBn1hrU=
|
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d h1:62lEBImTxZ83pgzywgDNIrPPuQ+j4ep9QjqrWBn1hrU=
|
||||||
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d/go.mod h1:lW9x+yEjqKdPbE3+cf2fGPJXCw/hChX3Omi9QHTLFsQ=
|
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d/go.mod h1:lW9x+yEjqKdPbE3+cf2fGPJXCw/hChX3Omi9QHTLFsQ=
|
||||||
github.com/IceWhaleTech/CasaOS-Common v0.4.1-alpha3 h1:jQfIty6u06fPJCutpS+97qr8uho3RpQX+B/CwHPCv/Q=
|
github.com/IceWhaleTech/CasaOS-Common v0.4.2-alpha3 h1:WJUYo+hJpLmza7mQngoJVeUJOfnrZevNrX5wzTuOJo0=
|
||||||
github.com/IceWhaleTech/CasaOS-Common v0.4.1-alpha3/go.mod h1:xcemiRsXcs1zrmQxYMyExDjZ7UHYwkJqYE71IDIV0xA=
|
github.com/IceWhaleTech/CasaOS-Common v0.4.2-alpha3/go.mod h1:xcemiRsXcs1zrmQxYMyExDjZ7UHYwkJqYE71IDIV0xA=
|
||||||
github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
|
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||||
|
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||||
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/bytedance/sonic v1.8.5 h1:kjX0/vo5acEQ/sinD/18SkA/lDDUk23F0RcaHvI7omc=
|
||||||
|
github.com/bytedance/sonic v1.8.5/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g=
|
||||||
|
github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
|
github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s=
|
||||||
|
github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
||||||
@@ -37,27 +45,29 @@ github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2
|
|||||||
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
||||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg=
|
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg=
|
||||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf h1:/w4QxepU4AHh3AuO6/g8y/YIIHH5+aKP3Bj8sg5cqhU=
|
|
||||||
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
|
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
|
||||||
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
|
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
|
||||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LVjRU0RNUuMDqkPTxcALio0LWPFPXxxFCvVGVAwEpFc=
|
github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LVjRU0RNUuMDqkPTxcALio0LWPFPXxxFCvVGVAwEpFc=
|
||||||
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-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 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/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
||||||
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
|
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
|
||||||
|
github.com/getkin/kin-openapi v0.115.0 h1:c8WHRLVY3G8m9jQTy0/DnIuljgRwTCB5twZytQS4JyU=
|
||||||
|
github.com/getkin/kin-openapi v0.115.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
|
||||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||||
github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY=
|
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
|
||||||
github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398=
|
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
|
||||||
github.com/glebarez/go-sqlite v1.20.0 h1:6D9uRXq3Kd+W7At+hOU2eIAeahv6qcYfO8jzmvb4Dr8=
|
github.com/glebarez/go-sqlite v1.21.0 h1:b8MHPtBagkSD2gntImZPsG3o3QEXgMDxguW/GLUonHQ=
|
||||||
github.com/glebarez/go-sqlite v1.20.0/go.mod h1:uTnJoqtwMQjlULmljLT73Cg7HB+2X6evsBHODyyq1ak=
|
github.com/glebarez/go-sqlite v1.21.0/go.mod h1:GodsA6yGSa3eKbvpr7dS+JaqazzVfMcjIXvx6KHhW/c=
|
||||||
github.com/glebarez/sqlite v1.6.0 h1:ZpvDLv4zBi2cuuQPitRiVz/5Uh6sXa5d8eBu0xNTpAo=
|
github.com/glebarez/sqlite v1.7.0 h1:A7Xj/KN2Lvie4Z4rrgQHY8MsbebX3NyWsL3n2i82MVI=
|
||||||
github.com/glebarez/sqlite v1.6.0/go.mod h1:6D6zPU/HTrFlYmVDKqBJlmQvma90P6r7sRRdkUUZOYk=
|
github.com/glebarez/sqlite v1.7.0/go.mod h1:PkeevrRlF/1BhQBCnzcMWzgrIk7IOop+qS2jUYLfHhk=
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
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.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.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||||
@@ -67,24 +77,40 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
|||||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||||
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||||
|
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
|
||||||
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||||
|
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||||
|
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||||
|
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
github.com/goccy/go-json v0.10.1 h1:lEs5Ob+oOG/Ze199njvzHbhn6p9T+h64F5hRj69iTTo=
|
||||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
|
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
|
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
|
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-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-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 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
||||||
@@ -94,109 +120,148 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev
|
|||||||
github.com/golang/protobuf v1.3.1/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.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
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.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||||
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
|
||||||
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
|
||||||
github.com/google/go-cmp v0.5.3/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.5/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.6/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.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
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/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-github/v36 v36.0.0 h1:ndCzM616/oijwufI7nBRa+5eZHLldT+4yIB68ib5ogs=
|
github.com/google/go-github/v36 v36.0.0 h1:ndCzM616/oijwufI7nBRa+5eZHLldT+4yIB68ib5ogs=
|
||||||
github.com/google/go-github/v36 v36.0.0/go.mod h1:LFlKC047IOqiglRGNqNb9s/iAPTnnjtlshm+bxp+kwk=
|
github.com/google/go-github/v36 v36.0.0/go.mod h1:LFlKC047IOqiglRGNqNb9s/iAPTnnjtlshm+bxp+kwk=
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googollee/go-socket.io v1.6.2 h1:olKLLHJtHz1IkL/OrTyNriZZvVQYEORNkJAqsOwPask=
|
github.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8=
|
||||||
github.com/googollee/go-socket.io v1.6.2/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg=
|
github.com/googollee/go-socket.io v1.7.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg=
|
||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||||
|
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
|
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
|
||||||
|
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
|
||||||
|
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
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/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0=
|
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
|
||||||
github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
|
||||||
|
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||||
|
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||||
|
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
|
||||||
|
github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de h1:V53FWzU6KAZVi1tPp5UIsMoUWJ2/PNwYIDXnu7QuBCE=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20230110061619-bbe2e5e100de/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
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/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||||
|
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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
|
||||||
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
|
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/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||||
|
github.com/mileusna/useragent v1.2.1 h1:p3RJWhi3LfuI6BHdddojREyK3p6qX67vIfOVMnUIVr0=
|
||||||
|
github.com/mileusna/useragent v1.2.1/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=
|
||||||
|
github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs=
|
||||||
|
github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwKdqp5K0=
|
||||||
|
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
|
||||||
|
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||||
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||||
|
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
|
||||||
|
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||||
github.com/pierrec/lz4/v4 v4.1.2 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=
|
github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw=
|
||||||
|
github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
|
||||||
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||||
|
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/shirou/gopsutil/v3 v3.22.11 h1:kxsPKS+Eeo+VnEQ2XCaGJepeP6KY53QoRTETx3+1ndM=
|
github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU=
|
||||||
github.com/shirou/gopsutil/v3 v3.22.11/go.mod h1:xl0EeL4vXJ+hQMAGN8B9VFpxukEMA0XdevQOe5MZ1oY=
|
github.com/shirou/gopsutil/v3 v3.23.2/go.mod h1:gv0aQw33GLo3pG8SiWKiQrbDzbRY1K80RyZJ7V4Th1M=
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
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/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
@@ -206,57 +271,65 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/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.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/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||||
|
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
|
||||||
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
|
||||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||||
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
|
||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
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.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
|
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
|
||||||
|
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
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/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||||
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.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
|
||||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
|
||||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
|
||||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/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-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
|
||||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/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.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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
@@ -265,31 +338,28 @@ golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/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-20200501053045-e0ff5e5a1de5/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-20200513185701-a91f0712d120/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
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.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
|
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
|
||||||
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
|
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-20201020160332-67f06af15bc9/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-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.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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -297,36 +367,38 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/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-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-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-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-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.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.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 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
|
golang.org/x/sys v0.6.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-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
golang.org/x/text v0.7.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.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||||
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||||
|
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/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-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
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-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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -337,21 +409,18 @@ google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID
|
|||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
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.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.30.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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.7/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.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 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
@@ -360,40 +429,16 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/gorm v1.24.2 h1:9wR6CFD+G8nOusLdvkZelOEhpJVwwHzpQOUM+REd6U0=
|
gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
|
||||||
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
|
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
|
||||||
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
|
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
|
||||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
|
||||||
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
|
||||||
modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
|
|
||||||
modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
|
|
||||||
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
|
||||||
modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI=
|
|
||||||
modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0=
|
|
||||||
modernc.org/ccgo/v3 v3.16.13-0.20221017192402-261537637ce8/go.mod h1:fUB3Vn0nVPReA+7IG7yZDfjv1TMWjhQP8gCxrFAtL5g=
|
|
||||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
|
||||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
|
||||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
|
||||||
modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA=
|
|
||||||
modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0=
|
|
||||||
modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
|
|
||||||
modernc.org/libc v1.20.3/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
|
|
||||||
modernc.org/libc v1.21.4/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
|
|
||||||
modernc.org/libc v1.21.5 h1:xBkU9fnHV+hvZuPSRszN0AXDG4M7nwPLwTWwkYcvLCI=
|
|
||||||
modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
|
|
||||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||||
modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||||
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
|
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||||
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
modernc.org/sqlite v1.21.0 h1:4aP4MdUf15i3R3M2mx6Q90WHKz3nZLoz96zlB6tNdow=
|
||||||
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
modernc.org/sqlite v1.21.0/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
|
||||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
modernc.org/sqlite v1.20.0 h1:80zmD3BGkm8BZ5fUi/4lwJQHiO3GXgIUvZRXpoIfROY=
|
|
||||||
modernc.org/sqlite v1.20.0/go.mod h1:EsYz8rfOvLCiYTy5ZFsOYzoCcRMu98YYkwAcCw5YIYw=
|
|
||||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
|
||||||
modernc.org/tcl v1.15.0/go.mod h1:xRoGotBZ6dU+Zo2tca+2EqVEeMmOUBzHnhIwq4YrVnE=
|
|
||||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
|
||||||
modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ=
|
|
||||||
|
|||||||
43
internal/conf/config.go
Normal file
43
internal/conf/config.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
Type string `json:"type" env:"DB_TYPE"`
|
||||||
|
Host string `json:"host" env:"DB_HOST"`
|
||||||
|
Port int `json:"port" env:"DB_PORT"`
|
||||||
|
User string `json:"user" env:"DB_USER"`
|
||||||
|
Password string `json:"password" env:"DB_PASS"`
|
||||||
|
Name string `json:"name" env:"DB_NAME"`
|
||||||
|
DBFile string `json:"db_file" env:"DB_FILE"`
|
||||||
|
TablePrefix string `json:"table_prefix" env:"DB_TABLE_PREFIX"`
|
||||||
|
SSLMode string `json:"ssl_mode" env:"DB_SSL_MODE"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scheme struct {
|
||||||
|
Https bool `json:"https" env:"HTTPS"`
|
||||||
|
CertFile string `json:"cert_file" env:"CERT_FILE"`
|
||||||
|
KeyFile string `json:"key_file" env:"KEY_FILE"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogConfig struct {
|
||||||
|
Enable bool `json:"enable" env:"LOG_ENABLE"`
|
||||||
|
Name string `json:"name" env:"LOG_NAME"`
|
||||||
|
MaxSize int `json:"max_size" env:"MAX_SIZE"`
|
||||||
|
MaxBackups int `json:"max_backups" env:"MAX_BACKUPS"`
|
||||||
|
MaxAge int `json:"max_age" env:"MAX_AGE"`
|
||||||
|
Compress bool `json:"compress" env:"COMPRESS"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Force bool `json:"force" env:"FORCE"`
|
||||||
|
Address string `json:"address" env:"ADDR"`
|
||||||
|
Port int `json:"port" env:"PORT"`
|
||||||
|
SiteURL string `json:"site_url" env:"SITE_URL"`
|
||||||
|
Cdn string `json:"cdn" env:"CDN"`
|
||||||
|
JwtSecret string `json:"jwt_secret" env:"JWT_SECRET"`
|
||||||
|
TokenExpiresIn int `json:"token_expires_in" env:"TOKEN_EXPIRES_IN"`
|
||||||
|
Database Database `json:"database"`
|
||||||
|
Scheme Scheme `json:"scheme"`
|
||||||
|
TempDir string `json:"temp_dir" env:"TEMP_DIR"`
|
||||||
|
BleveDir string `json:"bleve_dir" env:"BLEVE_DIR"`
|
||||||
|
Log LogConfig `json:"log"`
|
||||||
|
}
|
||||||
72
internal/conf/const.go
Normal file
72
internal/conf/const.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
const (
|
||||||
|
TypeString = "string"
|
||||||
|
TypeSelect = "select"
|
||||||
|
TypeBool = "bool"
|
||||||
|
TypeText = "text"
|
||||||
|
TypeNumber = "number"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// site
|
||||||
|
VERSION = "version"
|
||||||
|
ApiUrl = "api_url"
|
||||||
|
BasePath = "base_path"
|
||||||
|
SiteTitle = "site_title"
|
||||||
|
Announcement = "announcement"
|
||||||
|
AllowIndexed = "allow_indexed"
|
||||||
|
|
||||||
|
Logo = "logo"
|
||||||
|
Favicon = "favicon"
|
||||||
|
MainColor = "main_color"
|
||||||
|
|
||||||
|
// preview
|
||||||
|
TextTypes = "text_types"
|
||||||
|
AudioTypes = "audio_types"
|
||||||
|
VideoTypes = "video_types"
|
||||||
|
ImageTypes = "image_types"
|
||||||
|
ProxyTypes = "proxy_types"
|
||||||
|
ProxyIgnoreHeaders = "proxy_ignore_headers"
|
||||||
|
AudioAutoplay = "audio_autoplay"
|
||||||
|
VideoAutoplay = "video_autoplay"
|
||||||
|
|
||||||
|
// global
|
||||||
|
HideFiles = "hide_files"
|
||||||
|
CustomizeHead = "customize_head"
|
||||||
|
CustomizeBody = "customize_body"
|
||||||
|
LinkExpiration = "link_expiration"
|
||||||
|
SignAll = "sign_all"
|
||||||
|
PrivacyRegs = "privacy_regs"
|
||||||
|
OcrApi = "ocr_api"
|
||||||
|
FilenameCharMapping = "filename_char_mapping"
|
||||||
|
|
||||||
|
// index
|
||||||
|
SearchIndex = "search_index"
|
||||||
|
AutoUpdateIndex = "auto_update_index"
|
||||||
|
IndexPaths = "index_paths"
|
||||||
|
IgnorePaths = "ignore_paths"
|
||||||
|
|
||||||
|
// aria2
|
||||||
|
Aria2Uri = "aria2_uri"
|
||||||
|
Aria2Secret = "aria2_secret"
|
||||||
|
|
||||||
|
// single
|
||||||
|
Token = "token"
|
||||||
|
IndexProgress = "index_progress"
|
||||||
|
|
||||||
|
//Github
|
||||||
|
GithubClientId = "github_client_id"
|
||||||
|
GithubClientSecrets = "github_client_secrets"
|
||||||
|
GithubLoginEnabled = "github_login_enabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UNKNOWN = iota
|
||||||
|
FOLDER
|
||||||
|
//OFFICE
|
||||||
|
VIDEO
|
||||||
|
AUDIO
|
||||||
|
TEXT
|
||||||
|
IMAGE
|
||||||
|
)
|
||||||
30
internal/conf/var.go
Normal file
30
internal/conf/var.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
var (
|
||||||
|
BuiltAt string
|
||||||
|
GoVersion string
|
||||||
|
GitAuthor string
|
||||||
|
GitCommit string
|
||||||
|
Version string = "dev"
|
||||||
|
WebVersion string
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Conf *Config
|
||||||
|
)
|
||||||
|
|
||||||
|
var SlicesMap = make(map[string][]string)
|
||||||
|
var FilenameCharMap = make(map[string]string)
|
||||||
|
var PrivacyReg []*regexp.Regexp
|
||||||
|
|
||||||
|
var (
|
||||||
|
// StoragesLoaded loaded success if empty
|
||||||
|
StoragesLoaded = false
|
||||||
|
)
|
||||||
|
var (
|
||||||
|
RawIndexHtml string
|
||||||
|
ManageHtml string
|
||||||
|
IndexHtml string
|
||||||
|
)
|
||||||
25
internal/driver/config.go
Normal file
25
internal/driver/config.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* @Author: a624669980@163.com a624669980@163.com
|
||||||
|
* @Date: 2022-12-13 11:05:05
|
||||||
|
* @LastEditors: a624669980@163.com a624669980@163.com
|
||||||
|
* @LastEditTime: 2022-12-13 11:05:13
|
||||||
|
* @FilePath: /drive/internal/driver/config.go
|
||||||
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||||
|
*/
|
||||||
|
package driver
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
LocalSort bool `json:"local_sort"`
|
||||||
|
OnlyLocal bool `json:"only_local"`
|
||||||
|
OnlyProxy bool `json:"only_proxy"`
|
||||||
|
NoCache bool `json:"no_cache"`
|
||||||
|
NoUpload bool `json:"no_upload"`
|
||||||
|
NeedMs bool `json:"need_ms"` // if need get message from user, such as validate code
|
||||||
|
DefaultRoot string `json:"default_root"`
|
||||||
|
CheckStatus bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) MustProxy() bool {
|
||||||
|
return c.OnlyProxy || c.OnlyLocal
|
||||||
|
}
|
||||||
131
internal/driver/driver.go
Normal file
131
internal/driver/driver.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Driver interface {
|
||||||
|
Meta
|
||||||
|
Reader
|
||||||
|
User
|
||||||
|
//Writer
|
||||||
|
//Other
|
||||||
|
}
|
||||||
|
|
||||||
|
type Meta interface {
|
||||||
|
Config() Config
|
||||||
|
// GetStorage just get raw storage, no need to implement, because model.Storage have implemented
|
||||||
|
GetStorage() *model.StorageA
|
||||||
|
SetStorage(model.StorageA)
|
||||||
|
// GetAddition Additional is used for unmarshal of JSON, so need return pointer
|
||||||
|
GetAddition() Additional
|
||||||
|
// Init If already initialized, drop first
|
||||||
|
Init(ctx context.Context) error
|
||||||
|
Drop(ctx context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Other interface {
|
||||||
|
Other(ctx context.Context, args model.OtherArgs) (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Reader interface {
|
||||||
|
// List files in the path
|
||||||
|
// if identify files by path, need to set ID with path,like path.Join(dir.GetID(), obj.GetName())
|
||||||
|
// if identify files by id, need to set ID with corresponding id
|
||||||
|
List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error)
|
||||||
|
// Link get url/filepath/reader of file
|
||||||
|
Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error)
|
||||||
|
}
|
||||||
|
type User interface {
|
||||||
|
// GetRoot get root directory of user
|
||||||
|
GetUserInfo(ctx context.Context) (string, error)
|
||||||
|
}
|
||||||
|
type Getter interface {
|
||||||
|
GetRoot(ctx context.Context) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//type Writer interface {
|
||||||
|
// Mkdir
|
||||||
|
// Move
|
||||||
|
// Rename
|
||||||
|
// Copy
|
||||||
|
// Remove
|
||||||
|
// Put
|
||||||
|
//}
|
||||||
|
|
||||||
|
type Mkdir interface {
|
||||||
|
MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Move interface {
|
||||||
|
Move(ctx context.Context, srcObj, dstDir model.Obj) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Rename interface {
|
||||||
|
Rename(ctx context.Context, srcObj model.Obj, newName string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Copy interface {
|
||||||
|
Copy(ctx context.Context, srcObj, dstDir model.Obj) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Remove interface {
|
||||||
|
Remove(ctx context.Context, obj model.Obj) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Put interface {
|
||||||
|
Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up UpdateProgress) error
|
||||||
|
}
|
||||||
|
|
||||||
|
//type WriteResult interface {
|
||||||
|
// MkdirResult
|
||||||
|
// MoveResult
|
||||||
|
// RenameResult
|
||||||
|
// CopyResult
|
||||||
|
// PutResult
|
||||||
|
// Remove
|
||||||
|
//}
|
||||||
|
|
||||||
|
type MkdirResult interface {
|
||||||
|
MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MoveResult interface {
|
||||||
|
Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RenameResult interface {
|
||||||
|
Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CopyResult interface {
|
||||||
|
Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PutResult interface {
|
||||||
|
Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up UpdateProgress) (model.Obj, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateProgress func(percentage int)
|
||||||
|
|
||||||
|
type Progress struct {
|
||||||
|
Total int64
|
||||||
|
Done int64
|
||||||
|
up UpdateProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Progress) Write(b []byte) (n int, err error) {
|
||||||
|
n = len(b)
|
||||||
|
p.Done += int64(n)
|
||||||
|
p.up(int(float64(p.Done) / float64(p.Total) * 100))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProgress(total int64, up UpdateProgress) *Progress {
|
||||||
|
return &Progress{
|
||||||
|
Total: total,
|
||||||
|
up: up,
|
||||||
|
}
|
||||||
|
}
|
||||||
56
internal/driver/item.go
Normal file
56
internal/driver/item.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* @Author: a624669980@163.com a624669980@163.com
|
||||||
|
* @Date: 2022-12-13 11:05:47
|
||||||
|
* @LastEditors: a624669980@163.com a624669980@163.com
|
||||||
|
* @LastEditTime: 2022-12-13 11:05:54
|
||||||
|
* @FilePath: /drive/internal/driver/item.go
|
||||||
|
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
||||||
|
*/
|
||||||
|
package driver
|
||||||
|
|
||||||
|
type Additional interface{}
|
||||||
|
|
||||||
|
type Select string
|
||||||
|
|
||||||
|
type Item struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Default string `json:"default"`
|
||||||
|
Options string `json:"options"`
|
||||||
|
Required bool `json:"required"`
|
||||||
|
Help string `json:"help"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Info struct {
|
||||||
|
Common []Item `json:"common"`
|
||||||
|
Additional []Item `json:"additional"`
|
||||||
|
Config Config `json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type IRootPath interface {
|
||||||
|
GetRootPath() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type IRootId interface {
|
||||||
|
GetRootId() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RootPath struct {
|
||||||
|
RootFolderPath string `json:"root_folder_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RootID struct {
|
||||||
|
RootFolderID string `json:"root_folder_id" omit:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RootPath) GetRootPath() string {
|
||||||
|
return r.RootFolderPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RootPath) SetRootPath(path string) {
|
||||||
|
r.RootFolderPath = path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RootID) GetRootId() string {
|
||||||
|
return r.RootFolderID
|
||||||
|
}
|
||||||
6
internal/op/const.go
Normal file
6
internal/op/const.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package op
|
||||||
|
|
||||||
|
const (
|
||||||
|
WORK = "work"
|
||||||
|
RootName = "root"
|
||||||
|
)
|
||||||
173
internal/op/driver.go
Normal file
173
internal/op/driver.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
package op
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/conf"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/driver"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type New func() driver.Driver
|
||||||
|
|
||||||
|
var driverNewMap = map[string]New{}
|
||||||
|
var driverInfoMap = map[string][]driver.Item{} //driver.Info{}
|
||||||
|
|
||||||
|
func RegisterDriver(driver New) {
|
||||||
|
// log.Infof("register driver: [%s]", config.Name)
|
||||||
|
tempDriver := driver()
|
||||||
|
tempConfig := tempDriver.Config()
|
||||||
|
registerDriverItems(tempConfig, tempDriver.GetAddition())
|
||||||
|
driverNewMap[tempConfig.Name] = driver
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDriverNew(name string) (New, error) {
|
||||||
|
n, ok := driverNewMap[name]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("no driver named: %s", name)
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDriverNames() []string {
|
||||||
|
var driverNames []string
|
||||||
|
for k := range driverInfoMap {
|
||||||
|
driverNames = append(driverNames, k)
|
||||||
|
}
|
||||||
|
return driverNames
|
||||||
|
}
|
||||||
|
|
||||||
|
// func GetDriverInfoMap() map[string]driver.Info {
|
||||||
|
// return driverInfoMap
|
||||||
|
// }
|
||||||
|
func GetDriverInfoMap() map[string][]driver.Item {
|
||||||
|
return driverInfoMap
|
||||||
|
}
|
||||||
|
func registerDriverItems(config driver.Config, addition driver.Additional) {
|
||||||
|
// log.Debugf("addition of %s: %+v", config.Name, addition)
|
||||||
|
tAddition := reflect.TypeOf(addition)
|
||||||
|
for tAddition.Kind() == reflect.Pointer {
|
||||||
|
tAddition = tAddition.Elem()
|
||||||
|
}
|
||||||
|
//mainItems := getMainItems(config)
|
||||||
|
additionalItems := getAdditionalItems(tAddition, config.DefaultRoot)
|
||||||
|
driverInfoMap[config.Name] = additionalItems
|
||||||
|
// driver.Info{
|
||||||
|
// Common: mainItems,
|
||||||
|
// Additional: additionalItems,
|
||||||
|
// Config: config,
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMainItems(config driver.Config) []driver.Item {
|
||||||
|
items := []driver.Item{{
|
||||||
|
Name: "mount_path",
|
||||||
|
Type: conf.TypeString,
|
||||||
|
Required: true,
|
||||||
|
Help: "",
|
||||||
|
}, {
|
||||||
|
Name: "order",
|
||||||
|
Type: conf.TypeNumber,
|
||||||
|
Help: "use to sort",
|
||||||
|
}, {
|
||||||
|
Name: "remark",
|
||||||
|
Type: conf.TypeText,
|
||||||
|
}}
|
||||||
|
if !config.NoCache {
|
||||||
|
items = append(items, driver.Item{
|
||||||
|
Name: "cache_expiration",
|
||||||
|
Type: conf.TypeNumber,
|
||||||
|
Default: "30",
|
||||||
|
Required: true,
|
||||||
|
Help: "The cache expiration time for this storage",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if !config.OnlyProxy && !config.OnlyLocal {
|
||||||
|
items = append(items, []driver.Item{{
|
||||||
|
Name: "web_proxy",
|
||||||
|
Type: conf.TypeBool,
|
||||||
|
}, {
|
||||||
|
Name: "webdav_policy",
|
||||||
|
Type: conf.TypeSelect,
|
||||||
|
Options: "302_redirect,use_proxy_url,native_proxy",
|
||||||
|
Default: "302_redirect",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
}...)
|
||||||
|
} else {
|
||||||
|
items = append(items, driver.Item{
|
||||||
|
Name: "webdav_policy",
|
||||||
|
Type: conf.TypeSelect,
|
||||||
|
Default: "native_proxy",
|
||||||
|
Options: "use_proxy_url,native_proxy",
|
||||||
|
Required: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
items = append(items, driver.Item{
|
||||||
|
Name: "down_proxy_url",
|
||||||
|
Type: conf.TypeText,
|
||||||
|
})
|
||||||
|
if config.LocalSort {
|
||||||
|
items = append(items, []driver.Item{{
|
||||||
|
Name: "order_by",
|
||||||
|
Type: conf.TypeSelect,
|
||||||
|
Options: "name,size,modified",
|
||||||
|
}, {
|
||||||
|
Name: "order_direction",
|
||||||
|
Type: conf.TypeSelect,
|
||||||
|
Options: "asc,desc",
|
||||||
|
}}...)
|
||||||
|
}
|
||||||
|
items = append(items, driver.Item{
|
||||||
|
Name: "extract_folder",
|
||||||
|
Type: conf.TypeSelect,
|
||||||
|
Options: "front,back",
|
||||||
|
})
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAdditionalItems(t reflect.Type, defaultRoot string) []driver.Item {
|
||||||
|
var items []driver.Item
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.Type.Kind() == reflect.Struct {
|
||||||
|
items = append(items, getAdditionalItems(field.Type, defaultRoot)...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tag := field.Tag
|
||||||
|
ignore, ok1 := tag.Lookup("ignore")
|
||||||
|
name, ok2 := tag.Lookup("json")
|
||||||
|
if (ok1 && ignore == "true") || !ok2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tag.Get("omit") == "true" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item := driver.Item{
|
||||||
|
Name: name,
|
||||||
|
Type: strings.ToLower(field.Type.Name()),
|
||||||
|
Default: tag.Get("default"),
|
||||||
|
Options: tag.Get("options"),
|
||||||
|
Required: tag.Get("required") == "true",
|
||||||
|
Help: tag.Get("help"),
|
||||||
|
}
|
||||||
|
if tag.Get("type") != "" {
|
||||||
|
item.Type = tag.Get("type")
|
||||||
|
}
|
||||||
|
if item.Name == "root_folder_id" || item.Name == "root_folder_path" {
|
||||||
|
if item.Default == "" {
|
||||||
|
item.Default = defaultRoot
|
||||||
|
}
|
||||||
|
item.Required = item.Default != ""
|
||||||
|
}
|
||||||
|
// set default type to string
|
||||||
|
if item.Type == "" {
|
||||||
|
item.Type = "string"
|
||||||
|
}
|
||||||
|
items = append(items, item)
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
109
internal/op/hook.go
Normal file
109
internal/op/hook.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package op
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/conf"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/driver"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Obj
|
||||||
|
type ObjsUpdateHook = func(parent string, objs []model.Obj)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ObjsUpdateHooks = make([]ObjsUpdateHook, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterObjsUpdateHook(hook ObjsUpdateHook) {
|
||||||
|
ObjsUpdateHooks = append(ObjsUpdateHooks, hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleObjsUpdateHook(parent string, objs []model.Obj) {
|
||||||
|
for _, hook := range ObjsUpdateHooks {
|
||||||
|
hook(parent, objs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting
|
||||||
|
type SettingItemHook func(item *model.SettingItem) error
|
||||||
|
|
||||||
|
var settingItemHooks = map[string]SettingItemHook{
|
||||||
|
conf.VideoTypes: func(item *model.SettingItem) error {
|
||||||
|
conf.SlicesMap[conf.VideoTypes] = strings.Split(item.Value, ",")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
conf.AudioTypes: func(item *model.SettingItem) error {
|
||||||
|
conf.SlicesMap[conf.AudioTypes] = strings.Split(item.Value, ",")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
conf.ImageTypes: func(item *model.SettingItem) error {
|
||||||
|
conf.SlicesMap[conf.ImageTypes] = strings.Split(item.Value, ",")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
conf.TextTypes: func(item *model.SettingItem) error {
|
||||||
|
conf.SlicesMap[conf.TextTypes] = strings.Split(item.Value, ",")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
conf.ProxyTypes: func(item *model.SettingItem) error {
|
||||||
|
conf.SlicesMap[conf.ProxyTypes] = strings.Split(item.Value, ",")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
conf.ProxyIgnoreHeaders: func(item *model.SettingItem) error {
|
||||||
|
conf.SlicesMap[conf.ProxyIgnoreHeaders] = strings.Split(item.Value, ",")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
conf.PrivacyRegs: func(item *model.SettingItem) error {
|
||||||
|
regStrs := strings.Split(item.Value, "\n")
|
||||||
|
regs := make([]*regexp.Regexp, 0, len(regStrs))
|
||||||
|
for _, regStr := range regStrs {
|
||||||
|
reg, err := regexp.Compile(regStr)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
regs = append(regs, reg)
|
||||||
|
}
|
||||||
|
conf.PrivacyReg = regs
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
conf.FilenameCharMapping: func(item *model.SettingItem) error {
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
err := json.UnmarshalFromString(item.Value, &conf.FilenameCharMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Info("filename char mapping", zap.Any("FilenameCharMap", conf.FilenameCharMap))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterSettingItemHook(key string, hook SettingItemHook) {
|
||||||
|
settingItemHooks[key] = hook
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleSettingItemHook(item *model.SettingItem) (hasHook bool, err error) {
|
||||||
|
if hook, ok := settingItemHooks[item.Key]; ok {
|
||||||
|
return true, hook(item)
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage
|
||||||
|
type StorageHook func(typ string, storage driver.Driver)
|
||||||
|
|
||||||
|
var storageHooks = make([]StorageHook, 0)
|
||||||
|
|
||||||
|
func CallStorageHooks(typ string, storage driver.Driver) {
|
||||||
|
for _, hook := range storageHooks {
|
||||||
|
hook(typ, storage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterStorageHook(hook StorageHook) {
|
||||||
|
storageHooks = append(storageHooks, hook)
|
||||||
|
}
|
||||||
36
internal/sign/sign.go
Normal file
36
internal/sign/sign.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/pkg/sign"
|
||||||
|
)
|
||||||
|
|
||||||
|
var once sync.Once
|
||||||
|
var instance sign.Sign
|
||||||
|
|
||||||
|
func Sign(data string) string {
|
||||||
|
|
||||||
|
return NotExpired(data)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDuration(data string, d time.Duration) string {
|
||||||
|
once.Do(Instance)
|
||||||
|
return instance.Sign(data, time.Now().Add(d).Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NotExpired(data string) string {
|
||||||
|
once.Do(Instance)
|
||||||
|
return instance.Sign(data, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Verify(data string, sign string) error {
|
||||||
|
once.Do(Instance)
|
||||||
|
return instance.Verify(data, sign)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Instance() {
|
||||||
|
instance = sign.NewHMACSign([]byte("token"))
|
||||||
|
}
|
||||||
122
main.go
122
main.go
@@ -1,6 +1,10 @@
|
|||||||
|
//go:generate bash -c "mkdir -p codegen && go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 -generate types,server,spec -package codegen api/casaos/openapi.yaml > codegen/casaos_api.go"
|
||||||
|
//go:generate bash -c "mkdir -p codegen/message_bus && go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 -generate types,client -package message_bus https://raw.githubusercontent.com/IceWhaleTech/CasaOS-MessageBus/main/api/message_bus/openapi.yaml > codegen/message_bus/api.go"
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@@ -11,6 +15,11 @@ import (
|
|||||||
"github.com/IceWhaleTech/CasaOS-Common/model"
|
"github.com/IceWhaleTech/CasaOS-Common/model"
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/utils/constants"
|
"github.com/IceWhaleTech/CasaOS-Common/utils/constants"
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
|
||||||
|
util_http "github.com/IceWhaleTech/CasaOS-Common/utils/http"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/codegen/message_bus"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/common"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/cache"
|
"github.com/IceWhaleTech/CasaOS/pkg/cache"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/config"
|
"github.com/IceWhaleTech/CasaOS/pkg/config"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/sqlite"
|
"github.com/IceWhaleTech/CasaOS/pkg/sqlite"
|
||||||
@@ -18,12 +27,10 @@ import (
|
|||||||
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
|
||||||
"github.com/IceWhaleTech/CasaOS/route"
|
"github.com/IceWhaleTech/CasaOS/route"
|
||||||
"github.com/IceWhaleTech/CasaOS/service"
|
"github.com/IceWhaleTech/CasaOS/service"
|
||||||
"github.com/IceWhaleTech/CasaOS/types"
|
|
||||||
"github.com/coreos/go-systemd/daemon"
|
"github.com/coreos/go-systemd/daemon"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/robfig/cron"
|
"github.com/robfig/cron/v3"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,6 +42,12 @@ var (
|
|||||||
commit = "private build"
|
commit = "private build"
|
||||||
date = "private build"
|
date = "private build"
|
||||||
|
|
||||||
|
//go:embed api/index.html
|
||||||
|
_docHTML string
|
||||||
|
|
||||||
|
//go:embed api/casaos/openapi.yaml
|
||||||
|
_docYAML string
|
||||||
|
|
||||||
configFlag = flag.String("c", "", "config address")
|
configFlag = flag.String("c", "", "config address")
|
||||||
dbFlag = flag.String("db", "", "db path")
|
dbFlag = flag.String("db", "", "db path")
|
||||||
versionFlag = flag.Bool("v", false, "version")
|
versionFlag = flag.Bool("v", false, "version")
|
||||||
@@ -43,7 +56,7 @@ var (
|
|||||||
func init() {
|
func init() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if *versionFlag {
|
if *versionFlag {
|
||||||
fmt.Println("v" + types.CURRENTVERSION)
|
fmt.Println("v" + common.VERSION)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,13 +73,17 @@ func init() {
|
|||||||
sqliteDB = sqlite.GetDb(*dbFlag)
|
sqliteDB = sqlite.GetDb(*dbFlag)
|
||||||
// gredis.GetRedisConn(config.RedisInfo),
|
// gredis.GetRedisConn(config.RedisInfo),
|
||||||
|
|
||||||
service.MyService = service.NewService(sqliteDB, config.CommonInfo.RuntimePath, route.SocketIo())
|
service.MyService = service.NewService(sqliteDB, config.CommonInfo.RuntimePath)
|
||||||
|
|
||||||
service.Cache = cache.Init()
|
service.Cache = cache.Init()
|
||||||
|
|
||||||
service.GetCPUThermalZone()
|
service.GetCPUThermalZone()
|
||||||
|
|
||||||
route.InitFunction()
|
route.InitFunction()
|
||||||
|
|
||||||
|
///
|
||||||
|
// service.MountLists = make(map[string]*mountlib.MountPoint)
|
||||||
|
// configfile.Install()
|
||||||
}
|
}
|
||||||
|
|
||||||
// @title casaOS API
|
// @title casaOS API
|
||||||
@@ -84,43 +101,55 @@ func main() {
|
|||||||
if *versionFlag {
|
if *versionFlag {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// model.Setup()
|
|
||||||
// gredis.Setup()
|
|
||||||
|
|
||||||
r := route.InitRouter()
|
v1Router := route.InitV1Router()
|
||||||
defer service.SocketServer.Close()
|
|
||||||
r.GET("/v1/socketio/*any", gin.WrapH(service.SocketServer))
|
|
||||||
r.POST("/v1/socketio/*any", gin.WrapH(service.SocketServer))
|
|
||||||
|
|
||||||
// service.SyncTask(sqliteDB)
|
v2Router := route.InitV2Router()
|
||||||
cron2 := cron.New()
|
v2DocRouter := route.InitV2DocRouter(_docHTML, _docYAML)
|
||||||
// every day execution
|
v3file := route.InitFile()
|
||||||
|
v4dir := route.InitDir()
|
||||||
err := cron2.AddFunc("0/5 * * * * *", func() {
|
mux := &util_http.HandlerMultiplexer{
|
||||||
if service.ClientCount > 0 {
|
HandlerMap: map[string]http.Handler{
|
||||||
// route.SendNetINfoBySocket()
|
"v1": v1Router,
|
||||||
// route.SendCPUBySocket()
|
"v2": v2Router,
|
||||||
// route.SendMemBySocket()
|
"doc": v2DocRouter,
|
||||||
// route.SendDiskBySocket()
|
"v3": v3file,
|
||||||
// route.SendUSBBySocket()
|
"v4": v4dir,
|
||||||
route.SendAllHardwareStatusBySocket()
|
},
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
}
|
||||||
cron2.Start()
|
|
||||||
|
|
||||||
defer cron2.Stop()
|
crontab := cron.New(cron.WithSeconds())
|
||||||
|
if _, err := crontab.AddFunc("@every 5s", route.SendAllHardwareStatusBySocket); err != nil {
|
||||||
|
logger.Error("add crontab error", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
crontab.Start()
|
||||||
|
defer crontab.Stop()
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", net.JoinHostPort(LOCALHOST, "0"))
|
listener, err := net.Listen("tcp", net.JoinHostPort(LOCALHOST, "0"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
routers := []string{"sys", "port", "file", "folder", "batch", "image", "samba", "notify", "socketio"}
|
routers := []string{
|
||||||
for _, v := range routers {
|
"/v1/sys",
|
||||||
|
"/v1/port",
|
||||||
|
"/v1/file",
|
||||||
|
"/v1/folder",
|
||||||
|
"/v1/batch",
|
||||||
|
"/v1/image",
|
||||||
|
"/v1/samba",
|
||||||
|
"/v1/notify",
|
||||||
|
"/v1/driver",
|
||||||
|
"/v1/cloud",
|
||||||
|
"/v1/recover",
|
||||||
|
"/v1/other",
|
||||||
|
route.V2APIPath,
|
||||||
|
route.V2DocPath,
|
||||||
|
route.V3FilePath,
|
||||||
|
}
|
||||||
|
for _, apiPath := range routers {
|
||||||
err = service.MyService.Gateway().CreateRoute(&model.Route{
|
err = service.MyService.Gateway().CreateRoute(&model.Route{
|
||||||
Path: "/v1/" + v,
|
Path: apiPath,
|
||||||
Target: "http://" + listener.Addr().String(),
|
Target: "http://" + listener.Addr().String(),
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -129,6 +158,25 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var events []message_bus.EventType
|
||||||
|
events = append(events, message_bus.EventType{Name: "casaos:system:utilization", SourceID: common.SERVICENAME, PropertyTypeList: []message_bus.PropertyType{}})
|
||||||
|
events = append(events, message_bus.EventType{Name: "casaos:file:recover", SourceID: common.SERVICENAME, PropertyTypeList: []message_bus.PropertyType{}})
|
||||||
|
events = append(events, message_bus.EventType{Name: "casaos:file:operate", SourceID: common.SERVICENAME, PropertyTypeList: []message_bus.PropertyType{}})
|
||||||
|
// register at message bus
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
response, err := service.MyService.MessageBus().RegisterEventTypesWithResponse(context.Background(), events)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error when trying to register one or more event types - some event type will not be discoverable", zap.Error(err))
|
||||||
|
}
|
||||||
|
if response != nil && response.StatusCode() != http.StatusOK {
|
||||||
|
logger.Error("error when trying to register one or more event types - some event type will not be discoverable", zap.String("status", response.Status()), zap.String("body", string(response.Body)))
|
||||||
|
}
|
||||||
|
if response.StatusCode() == http.StatusOK {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Second * 2)
|
time.Sleep(time.Second * 2)
|
||||||
// v0.3.6
|
// v0.3.6
|
||||||
@@ -162,14 +210,20 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
logger.Info("This process is not running as a systemd service.")
|
logger.Info("This process is not running as a systemd service.")
|
||||||
}
|
}
|
||||||
|
// http.HandleFunc("/v1/file/test", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
// //http.ServeFile(w, r, r.URL.Path[1:])
|
||||||
|
// http.ServeFile(w, r, "/DATA/test.img")
|
||||||
|
// })
|
||||||
|
// go http.ListenAndServe(":8081", nil)
|
||||||
|
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
Handler: r,
|
Handler: mux,
|
||||||
ReadHeaderTimeout: 5 * time.Second, // fix G112: Potential slowloris attack (see https://github.com/securego/gosec)
|
ReadHeaderTimeout: 5 * time.Second, // fix G112: Potential slowloris attack (see https://github.com/securego/gosec)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("CasaOS main service is listening...", zap.Any("address", listener.Addr().String()))
|
logger.Info("CasaOS main service is listening...", zap.Any("address", listener.Addr().String()))
|
||||||
|
// defer service.MyService.Storage().UnmountAllStorage()
|
||||||
err = s.Serve(listener) // not using http.serve() to fix G114: Use of net/http serve function that has no support for setting timeouts (see https://github.com/securego/gosec)
|
err = s.Serve(listener) // not using http.serve() to fix G114: Use of net/http serve function that has no support for setting timeouts (see https://github.com/securego/gosec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
39
model/args.go
Normal file
39
model/args.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListArgs struct {
|
||||||
|
ReqPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinkArgs struct {
|
||||||
|
IP string
|
||||||
|
Header http.Header
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Link struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Header http.Header `json:"header"` // needed header
|
||||||
|
Data io.ReadCloser // return file reader directly
|
||||||
|
Status int // status maybe 200 or 206, etc
|
||||||
|
FilePath *string // local file, return the filepath
|
||||||
|
Expiration *time.Duration // url expiration time
|
||||||
|
Method string `json:"method"` // http method
|
||||||
|
}
|
||||||
|
|
||||||
|
type OtherArgs struct {
|
||||||
|
Obj Obj
|
||||||
|
Method string
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FsOtherArgs struct {
|
||||||
|
Path string `json:"path" form:"path"`
|
||||||
|
Method string `json:"method" form:"method"`
|
||||||
|
Data interface{} `json:"data" form:"data"`
|
||||||
|
}
|
||||||
6
model/common.go
Normal file
6
model/common.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type PageResp struct {
|
||||||
|
Content interface{} `json:"content"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
186
model/obj.go
Normal file
186
model/obj.go
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
mapset "github.com/deckarep/golang-set/v2"
|
||||||
|
|
||||||
|
"github.com/maruel/natural"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UnwrapObj interface {
|
||||||
|
Unwrap() Obj
|
||||||
|
}
|
||||||
|
|
||||||
|
type Obj interface {
|
||||||
|
GetSize() int64
|
||||||
|
GetName() string
|
||||||
|
ModTime() time.Time
|
||||||
|
IsDir() bool
|
||||||
|
|
||||||
|
// The internal information of the driver.
|
||||||
|
// If you want to use it, please understand what it means
|
||||||
|
GetID() string
|
||||||
|
GetPath() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileStreamer interface {
|
||||||
|
io.ReadCloser
|
||||||
|
Obj
|
||||||
|
GetMimetype() string
|
||||||
|
SetReadCloser(io.ReadCloser)
|
||||||
|
NeedStore() bool
|
||||||
|
GetReadCloser() io.ReadCloser
|
||||||
|
GetOld() Obj
|
||||||
|
}
|
||||||
|
|
||||||
|
type URL interface {
|
||||||
|
URL() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Thumb interface {
|
||||||
|
Thumb() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetPath interface {
|
||||||
|
SetPath(path string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SortFiles(objs []Obj, orderBy, orderDirection string) {
|
||||||
|
if orderBy == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort.Slice(objs, func(i, j int) bool {
|
||||||
|
switch orderBy {
|
||||||
|
case "name":
|
||||||
|
{
|
||||||
|
c := natural.Less(objs[i].GetName(), objs[j].GetName())
|
||||||
|
if orderDirection == "desc" {
|
||||||
|
return !c
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
case "size":
|
||||||
|
{
|
||||||
|
if orderDirection == "desc" {
|
||||||
|
return objs[i].GetSize() >= objs[j].GetSize()
|
||||||
|
}
|
||||||
|
return objs[i].GetSize() <= objs[j].GetSize()
|
||||||
|
}
|
||||||
|
case "modified":
|
||||||
|
if orderDirection == "desc" {
|
||||||
|
return objs[i].ModTime().After(objs[j].ModTime())
|
||||||
|
}
|
||||||
|
return objs[i].ModTime().Before(objs[j].ModTime())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExtractFolder(objs []Obj, extractFolder string) {
|
||||||
|
if extractFolder == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
front := extractFolder == "front"
|
||||||
|
sort.SliceStable(objs, func(i, j int) bool {
|
||||||
|
if objs[i].IsDir() || objs[j].IsDir() {
|
||||||
|
if !objs[i].IsDir() {
|
||||||
|
return !front
|
||||||
|
}
|
||||||
|
if !objs[j].IsDir() {
|
||||||
|
return front
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap
|
||||||
|
func WrapObjName(objs Obj) Obj {
|
||||||
|
return &ObjWrapName{Obj: objs}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WrapObjsName(objs []Obj) {
|
||||||
|
for i := 0; i < len(objs); i++ {
|
||||||
|
objs[i] = &ObjWrapName{Obj: objs[i]}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnwrapObjs(obj Obj) Obj {
|
||||||
|
if unwrap, ok := obj.(UnwrapObj); ok {
|
||||||
|
obj = unwrap.Unwrap()
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetThumb(obj Obj) (thumb string, ok bool) {
|
||||||
|
if obj, ok := obj.(Thumb); ok {
|
||||||
|
return obj.Thumb(), true
|
||||||
|
}
|
||||||
|
if unwrap, ok := obj.(UnwrapObj); ok {
|
||||||
|
return GetThumb(unwrap.Unwrap())
|
||||||
|
}
|
||||||
|
return thumb, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUrl(obj Obj) (url string, ok bool) {
|
||||||
|
if obj, ok := obj.(URL); ok {
|
||||||
|
return obj.URL(), true
|
||||||
|
}
|
||||||
|
if unwrap, ok := obj.(UnwrapObj); ok {
|
||||||
|
return GetUrl(unwrap.Unwrap())
|
||||||
|
}
|
||||||
|
return url, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge
|
||||||
|
func NewObjMerge() *ObjMerge {
|
||||||
|
return &ObjMerge{
|
||||||
|
set: mapset.NewSet[string](),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObjMerge struct {
|
||||||
|
regs []*regexp.Regexp
|
||||||
|
set mapset.Set[string]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (om *ObjMerge) Merge(objs []Obj, objs_ ...Obj) []Obj {
|
||||||
|
newObjs := make([]Obj, 0, len(objs)+len(objs_))
|
||||||
|
newObjs = om.insertObjs(om.insertObjs(newObjs, objs...), objs_...)
|
||||||
|
return newObjs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (om *ObjMerge) insertObjs(objs []Obj, objs_ ...Obj) []Obj {
|
||||||
|
for _, obj := range objs_ {
|
||||||
|
if om.clickObj(obj) {
|
||||||
|
objs = append(objs, obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return objs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (om *ObjMerge) clickObj(obj Obj) bool {
|
||||||
|
for _, reg := range om.regs {
|
||||||
|
if reg.MatchString(obj.GetName()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return om.set.Add(obj.GetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (om *ObjMerge) InitHideReg(hides string) {
|
||||||
|
rs := strings.Split(hides, "\n")
|
||||||
|
om.regs = make([]*regexp.Regexp, 0, len(rs))
|
||||||
|
for _, r := range rs {
|
||||||
|
om.regs = append(om.regs, regexp.MustCompile(r))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (om *ObjMerge) Reset() {
|
||||||
|
om.set.Clear()
|
||||||
|
}
|
||||||
90
model/object.go
Normal file
90
model/object.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ObjWrapName struct {
|
||||||
|
Name string
|
||||||
|
Obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ObjWrapName) Unwrap() Obj {
|
||||||
|
return o.Obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *ObjWrapName) GetName() string {
|
||||||
|
if o.Name == "" {
|
||||||
|
o.Name = o.Obj.GetName()
|
||||||
|
}
|
||||||
|
return o.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type Object struct {
|
||||||
|
ID string
|
||||||
|
Path string
|
||||||
|
Name string
|
||||||
|
Size int64
|
||||||
|
Modified time.Time
|
||||||
|
IsFolder bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) GetName() string {
|
||||||
|
return o.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) GetSize() int64 {
|
||||||
|
return o.Size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) ModTime() time.Time {
|
||||||
|
return o.Modified
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) IsDir() bool {
|
||||||
|
return o.IsFolder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) GetID() string {
|
||||||
|
return o.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) GetPath() string {
|
||||||
|
return o.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) SetPath(id string) {
|
||||||
|
o.Path = id
|
||||||
|
}
|
||||||
|
|
||||||
|
type Thumbnail struct {
|
||||||
|
Thumbnail string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Url struct {
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w Url) URL() string {
|
||||||
|
return w.Url
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Thumbnail) Thumb() string {
|
||||||
|
return t.Thumbnail
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObjThumb struct {
|
||||||
|
Object
|
||||||
|
Thumbnail
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObjectURL struct {
|
||||||
|
Object
|
||||||
|
Url
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObjThumbURL struct {
|
||||||
|
Object
|
||||||
|
Thumbnail
|
||||||
|
Url
|
||||||
|
}
|
||||||
23
model/req.go
Normal file
23
model/req.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type PageReq struct {
|
||||||
|
Index int `json:"page" form:"index"`
|
||||||
|
Size int `json:"size" form:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaxUint = ^uint(0)
|
||||||
|
const MinUint = 0
|
||||||
|
const MaxInt = int(MaxUint >> 1)
|
||||||
|
const MinInt = -MaxInt - 1
|
||||||
|
|
||||||
|
func (p *PageReq) Validate() {
|
||||||
|
if p.Index < 1 {
|
||||||
|
p.Index = 1
|
||||||
|
}
|
||||||
|
if p.Size < 1 {
|
||||||
|
p.Size = 100000
|
||||||
|
}
|
||||||
|
// if p.PerPage < 1 {
|
||||||
|
// p.PerPage = MaxInt
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type SearchFileInfo struct {
|
type SearchEngine struct {
|
||||||
Path string `json:"path"`
|
Name string `json:"name"`
|
||||||
Name string `json:"name"`
|
Icon string `json:"icon"`
|
||||||
Type int `json:"type"`
|
SearchUrl string `json:"search_url"`
|
||||||
|
RecoUrl string `json:"reco_url"`
|
||||||
|
Data []string `json:"data"`
|
||||||
}
|
}
|
||||||
|
|||||||
33
model/setting.go
Normal file
33
model/setting.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
const (
|
||||||
|
SINGLE = iota
|
||||||
|
SITE
|
||||||
|
STYLE
|
||||||
|
PREVIEW
|
||||||
|
GLOBAL
|
||||||
|
ARIA2
|
||||||
|
INDEX
|
||||||
|
GITHUB
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PUBLIC = iota
|
||||||
|
PRIVATE
|
||||||
|
READONLY
|
||||||
|
DEPRECATED
|
||||||
|
)
|
||||||
|
|
||||||
|
type SettingItem struct {
|
||||||
|
Key string `json:"key" gorm:"primaryKey" binding:"required"` // unique key
|
||||||
|
Value string `json:"value"` // value
|
||||||
|
Help string `json:"help"` // help message
|
||||||
|
Type string `json:"type"` // string, number, bool, select
|
||||||
|
Options string `json:"options"` // values for select
|
||||||
|
Group int `json:"group"` // use to group setting in frontend
|
||||||
|
Flag int `json:"flag"` // 0 = public, 1 = private, 2 = readonly, 3 = deprecated, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SettingItem) IsDeprecated() bool {
|
||||||
|
return s.Flag == DEPRECATED
|
||||||
|
}
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
//
|
|
||||||
type SmartctlA struct {
|
|
||||||
Smartctl struct {
|
|
||||||
Version []int `json:"version"`
|
|
||||||
SvnRevision string `json:"svn_revision"`
|
|
||||||
PlatformInfo string `json:"platform_info"`
|
|
||||||
BuildInfo string `json:"build_info"`
|
|
||||||
Argv []string `json:"argv"`
|
|
||||||
ExitStatus int `json:"exit_status"`
|
|
||||||
} `json:"smartctl"`
|
|
||||||
Device struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
InfoName string `json:"info_name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Protocol string `json:"protocol"`
|
|
||||||
} `json:"device"`
|
|
||||||
ModelName string `json:"model_name"`
|
|
||||||
SerialNumber string `json:"serial_number"`
|
|
||||||
FirmwareVersion string `json:"firmware_version"`
|
|
||||||
UserCapacity struct {
|
|
||||||
Blocks int `json:"blocks"`
|
|
||||||
Bytes int64 `json:"bytes"`
|
|
||||||
} `json:"user_capacity"`
|
|
||||||
SmartStatus struct {
|
|
||||||
Passed bool `json:"passed"`
|
|
||||||
} `json:"smart_status"`
|
|
||||||
AtaSmartData struct {
|
|
||||||
OfflineDataCollection struct {
|
|
||||||
Status struct {
|
|
||||||
Value int `json:"value"`
|
|
||||||
String string `json:"string"`
|
|
||||||
} `json:"status"`
|
|
||||||
CompletionSeconds int `json:"completion_seconds"`
|
|
||||||
} `json:"offline_data_collection"`
|
|
||||||
SelfTest struct {
|
|
||||||
Status struct {
|
|
||||||
Value int `json:"value"`
|
|
||||||
String string `json:"string"`
|
|
||||||
Passed bool `json:"passed"`
|
|
||||||
} `json:"status"`
|
|
||||||
PollingMinutes struct {
|
|
||||||
Short int `json:"short"`
|
|
||||||
Extended int `json:"extended"`
|
|
||||||
Conveyance int `json:"conveyance"`
|
|
||||||
} `json:"polling_minutes"`
|
|
||||||
} `json:"self_test"`
|
|
||||||
Capabilities struct {
|
|
||||||
Values []int `json:"values"`
|
|
||||||
ExecOfflineImmediateSupported bool `json:"exec_offline_immediate_supported"`
|
|
||||||
OfflineIsAbortedUponNewCmd bool `json:"offline_is_aborted_upon_new_cmd"`
|
|
||||||
OfflineSurfaceScanSupported bool `json:"offline_surface_scan_supported"`
|
|
||||||
SelfTestsSupported bool `json:"self_tests_supported"`
|
|
||||||
ConveyanceSelfTestSupported bool `json:"conveyance_self_test_supported"`
|
|
||||||
SelectiveSelfTestSupported bool `json:"selective_self_test_supported"`
|
|
||||||
AttributeAutosaveEnabled bool `json:"attribute_autosave_enabled"`
|
|
||||||
ErrorLoggingSupported bool `json:"error_logging_supported"`
|
|
||||||
GpLoggingSupported bool `json:"gp_logging_supported"`
|
|
||||||
} `json:"capabilities"`
|
|
||||||
} `json:"ata_smart_data"`
|
|
||||||
PowerOnTime struct {
|
|
||||||
Hours int `json:"hours"`
|
|
||||||
} `json:"power_on_time"`
|
|
||||||
PowerCycleCount int `json:"power_cycle_count"`
|
|
||||||
Temperature struct {
|
|
||||||
Current int `json:"current"`
|
|
||||||
} `json:"temperature"`
|
|
||||||
}
|
|
||||||
54
model/storage.go
Normal file
54
model/storage.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type StorageA struct {
|
||||||
|
ID uint `json:"id" gorm:"primaryKey"` // unique key
|
||||||
|
MountPath string `json:"mount_path" gorm:"unique" binding:"required"` // must be standardized
|
||||||
|
Order int `json:"order"` // use to sort
|
||||||
|
Driver string `json:"driver"` // driver used
|
||||||
|
CacheExpiration int `json:"cache_expiration"` // cache expire time
|
||||||
|
Status string `json:"status"`
|
||||||
|
Addition string `json:"addition" gorm:"type:text"` // Additional information, defined in the corresponding driver
|
||||||
|
Remark string `json:"remark"`
|
||||||
|
Modified time.Time `json:"modified"`
|
||||||
|
Disabled bool `json:"disabled"` // if disabled
|
||||||
|
Sort
|
||||||
|
Proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
type Sort struct {
|
||||||
|
OrderBy string `json:"order_by"`
|
||||||
|
OrderDirection string `json:"order_direction"`
|
||||||
|
ExtractFolder string `json:"extract_folder"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Proxy struct {
|
||||||
|
WebProxy bool `json:"web_proxy"`
|
||||||
|
WebdavPolicy string `json:"webdav_policy"`
|
||||||
|
DownProxyUrl string `json:"down_proxy_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageA) GetStorage() *StorageA {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageA) SetStorage(storage StorageA) {
|
||||||
|
*s = storage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StorageA) SetStatus(status string) {
|
||||||
|
s.Status = status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Proxy) Webdav302() bool {
|
||||||
|
return p.WebdavPolicy == "302_redirect"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Proxy) WebdavProxy() bool {
|
||||||
|
return p.WebdavPolicy == "use_proxy_url"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Proxy) WebdavNative() bool {
|
||||||
|
return !p.Webdav302() && !p.WebdavProxy()
|
||||||
|
}
|
||||||
33
model/stream.go
Normal file
33
model/stream.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FileStream struct {
|
||||||
|
Obj
|
||||||
|
io.ReadCloser
|
||||||
|
Mimetype string
|
||||||
|
WebPutAsTask bool
|
||||||
|
Old Obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileStream) GetMimetype() string {
|
||||||
|
return f.Mimetype
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileStream) NeedStore() bool {
|
||||||
|
return f.WebPutAsTask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileStream) GetReadCloser() io.ReadCloser {
|
||||||
|
return f.ReadCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileStream) SetReadCloser(rc io.ReadCloser) {
|
||||||
|
f.ReadCloser = rc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileStream) GetOld() Obj {
|
||||||
|
return f.Old
|
||||||
|
}
|
||||||
12
pkg/fs/fs.go
Normal file
12
pkg/fs/fs.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package fs
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// CheckClose is a utility function used to check the return from
|
||||||
|
// Close in a defer statement.
|
||||||
|
func CheckClose(c io.Closer, err *error) {
|
||||||
|
cerr := c.Close()
|
||||||
|
if *err == nil {
|
||||||
|
*err = cerr
|
||||||
|
}
|
||||||
|
}
|
||||||
412
pkg/generic_sync/generic_sync.go
Normal file
412
pkg/generic_sync/generic_sync.go
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package generic_sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MapOf is like a Go map[interface{}]interface{} but is safe for concurrent use
|
||||||
|
// by multiple goroutines without additional locking or coordination.
|
||||||
|
// Loads, stores, and deletes run in amortized constant time.
|
||||||
|
//
|
||||||
|
// The MapOf type is specialized. Most code should use a plain Go map instead,
|
||||||
|
// with separate locking or coordination, for better type safety and to make it
|
||||||
|
// easier to maintain other invariants along with the map content.
|
||||||
|
//
|
||||||
|
// The MapOf type is optimized for two common use cases: (1) when the entry for a given
|
||||||
|
// key is only ever written once but read many times, as in caches that only grow,
|
||||||
|
// or (2) when multiple goroutines read, write, and overwrite entries for disjoint
|
||||||
|
// sets of keys. In these two cases, use of a MapOf may significantly reduce lock
|
||||||
|
// contention compared to a Go map paired with a separate Mutex or RWMutex.
|
||||||
|
//
|
||||||
|
// The zero MapOf is empty and ready for use. A MapOf must not be copied after first use.
|
||||||
|
type MapOf[K comparable, V any] struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
|
// read contains the portion of the map's contents that are safe for
|
||||||
|
// concurrent access (with or without mu held).
|
||||||
|
//
|
||||||
|
// The read field itself is always safe to load, but must only be stored with
|
||||||
|
// mu held.
|
||||||
|
//
|
||||||
|
// Entries stored in read may be updated concurrently without mu, but updating
|
||||||
|
// a previously-expunged entry requires that the entry be copied to the dirty
|
||||||
|
// map and unexpunged with mu held.
|
||||||
|
read atomic.Value // readOnly
|
||||||
|
|
||||||
|
// dirty contains the portion of the map's contents that require mu to be
|
||||||
|
// held. To ensure that the dirty map can be promoted to the read map quickly,
|
||||||
|
// it also includes all of the non-expunged entries in the read map.
|
||||||
|
//
|
||||||
|
// Expunged entries are not stored in the dirty map. An expunged entry in the
|
||||||
|
// clean map must be unexpunged and added to the dirty map before a new value
|
||||||
|
// can be stored to it.
|
||||||
|
//
|
||||||
|
// If the dirty map is nil, the next write to the map will initialize it by
|
||||||
|
// making a shallow copy of the clean map, omitting stale entries.
|
||||||
|
dirty map[K]*entry[V]
|
||||||
|
|
||||||
|
// misses counts the number of loads since the read map was last updated that
|
||||||
|
// needed to lock mu to determine whether the key was present.
|
||||||
|
//
|
||||||
|
// Once enough misses have occurred to cover the cost of copying the dirty
|
||||||
|
// map, the dirty map will be promoted to the read map (in the unamended
|
||||||
|
// state) and the next store to the map will make a new dirty copy.
|
||||||
|
misses int
|
||||||
|
}
|
||||||
|
|
||||||
|
// readOnly is an immutable struct stored atomically in the MapOf.read field.
|
||||||
|
type readOnly[K comparable, V any] struct {
|
||||||
|
m map[K]*entry[V]
|
||||||
|
amended bool // true if the dirty map contains some key not in m.
|
||||||
|
}
|
||||||
|
|
||||||
|
// expunged is an arbitrary pointer that marks entries which have been deleted
|
||||||
|
// from the dirty map.
|
||||||
|
var expunged = unsafe.Pointer(new(interface{}))
|
||||||
|
|
||||||
|
// An entry is a slot in the map corresponding to a particular key.
|
||||||
|
type entry[V any] struct {
|
||||||
|
// p points to the interface{} value stored for the entry.
|
||||||
|
//
|
||||||
|
// If p == nil, the entry has been deleted and m.dirty == nil.
|
||||||
|
//
|
||||||
|
// If p == expunged, the entry has been deleted, m.dirty != nil, and the entry
|
||||||
|
// is missing from m.dirty.
|
||||||
|
//
|
||||||
|
// Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty
|
||||||
|
// != nil, in m.dirty[key].
|
||||||
|
//
|
||||||
|
// An entry can be deleted by atomic replacement with nil: when m.dirty is
|
||||||
|
// next created, it will atomically replace nil with expunged and leave
|
||||||
|
// m.dirty[key] unset.
|
||||||
|
//
|
||||||
|
// An entry's associated value can be updated by atomic replacement, provided
|
||||||
|
// p != expunged. If p == expunged, an entry's associated value can be updated
|
||||||
|
// only after first setting m.dirty[key] = e so that lookups using the dirty
|
||||||
|
// map find the entry.
|
||||||
|
p unsafe.Pointer // *interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEntry[V any](i V) *entry[V] {
|
||||||
|
return &entry[V]{p: unsafe.Pointer(&i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load returns the value stored in the map for a key, or nil if no
|
||||||
|
// value is present.
|
||||||
|
// The ok result indicates whether value was found in the map.
|
||||||
|
func (m *MapOf[K, V]) Load(key K) (value V, ok bool) {
|
||||||
|
read, _ := m.read.Load().(readOnly[K, V])
|
||||||
|
e, ok := read.m[key]
|
||||||
|
if !ok && read.amended {
|
||||||
|
m.mu.Lock()
|
||||||
|
// Avoid reporting a spurious miss if m.dirty got promoted while we were
|
||||||
|
// blocked on m.mu. (If further loads of the same key will not miss, it's
|
||||||
|
// not worth copying the dirty map for this key.)
|
||||||
|
read, _ = m.read.Load().(readOnly[K, V])
|
||||||
|
e, ok = read.m[key]
|
||||||
|
if !ok && read.amended {
|
||||||
|
e, ok = m.dirty[key]
|
||||||
|
// Regardless of whether the entry was present, record a miss: this key
|
||||||
|
// will take the slow path until the dirty map is promoted to the read
|
||||||
|
// map.
|
||||||
|
m.missLocked()
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return value, false
|
||||||
|
}
|
||||||
|
return e.load()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapOf[K, V]) Has(key K) bool {
|
||||||
|
_, ok := m.Load(key)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *entry[V]) load() (value V, ok bool) {
|
||||||
|
p := atomic.LoadPointer(&e.p)
|
||||||
|
if p == nil || p == expunged {
|
||||||
|
return value, false
|
||||||
|
}
|
||||||
|
return *(*V)(p), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store sets the value for a key.
|
||||||
|
func (m *MapOf[K, V]) Store(key K, value V) {
|
||||||
|
read, _ := m.read.Load().(readOnly[K, V])
|
||||||
|
if e, ok := read.m[key]; ok && e.tryStore(&value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
read, _ = m.read.Load().(readOnly[K, V])
|
||||||
|
if e, ok := read.m[key]; ok {
|
||||||
|
if e.unexpungeLocked() {
|
||||||
|
// The entry was previously expunged, which implies that there is a
|
||||||
|
// non-nil dirty map and this entry is not in it.
|
||||||
|
m.dirty[key] = e
|
||||||
|
}
|
||||||
|
e.storeLocked(&value)
|
||||||
|
} else if e, ok := m.dirty[key]; ok {
|
||||||
|
e.storeLocked(&value)
|
||||||
|
} else {
|
||||||
|
if !read.amended {
|
||||||
|
// We're adding the first new key to the dirty map.
|
||||||
|
// Make sure it is allocated and mark the read-only map as incomplete.
|
||||||
|
m.dirtyLocked()
|
||||||
|
m.read.Store(readOnly[K, V]{m: read.m, amended: true})
|
||||||
|
}
|
||||||
|
m.dirty[key] = newEntry(value)
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryStore stores a value if the entry has not been expunged.
|
||||||
|
//
|
||||||
|
// If the entry is expunged, tryStore returns false and leaves the entry
|
||||||
|
// unchanged.
|
||||||
|
func (e *entry[V]) tryStore(i *V) bool {
|
||||||
|
for {
|
||||||
|
p := atomic.LoadPointer(&e.p)
|
||||||
|
if p == expunged {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unexpungeLocked ensures that the entry is not marked as expunged.
|
||||||
|
//
|
||||||
|
// If the entry was previously expunged, it must be added to the dirty map
|
||||||
|
// before m.mu is unlocked.
|
||||||
|
func (e *entry[V]) unexpungeLocked() (wasExpunged bool) {
|
||||||
|
return atomic.CompareAndSwapPointer(&e.p, expunged, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// storeLocked unconditionally stores a value to the entry.
|
||||||
|
//
|
||||||
|
// The entry must be known not to be expunged.
|
||||||
|
func (e *entry[V]) storeLocked(i *V) {
|
||||||
|
atomic.StorePointer(&e.p, unsafe.Pointer(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadOrStore returns the existing value for the key if present.
|
||||||
|
// Otherwise, it stores and returns the given value.
|
||||||
|
// The loaded result is true if the value was loaded, false if stored.
|
||||||
|
func (m *MapOf[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
|
||||||
|
// Avoid locking if it's a clean hit.
|
||||||
|
read, _ := m.read.Load().(readOnly[K, V])
|
||||||
|
if e, ok := read.m[key]; ok {
|
||||||
|
actual, loaded, ok := e.tryLoadOrStore(value)
|
||||||
|
if ok {
|
||||||
|
return actual, loaded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
read, _ = m.read.Load().(readOnly[K, V])
|
||||||
|
if e, ok := read.m[key]; ok {
|
||||||
|
if e.unexpungeLocked() {
|
||||||
|
m.dirty[key] = e
|
||||||
|
}
|
||||||
|
actual, loaded, _ = e.tryLoadOrStore(value)
|
||||||
|
} else if e, ok := m.dirty[key]; ok {
|
||||||
|
actual, loaded, _ = e.tryLoadOrStore(value)
|
||||||
|
m.missLocked()
|
||||||
|
} else {
|
||||||
|
if !read.amended {
|
||||||
|
// We're adding the first new key to the dirty map.
|
||||||
|
// Make sure it is allocated and mark the read-only map as incomplete.
|
||||||
|
m.dirtyLocked()
|
||||||
|
m.read.Store(readOnly[K, V]{m: read.m, amended: true})
|
||||||
|
}
|
||||||
|
m.dirty[key] = newEntry(value)
|
||||||
|
actual, loaded = value, false
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
return actual, loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryLoadOrStore atomically loads or stores a value if the entry is not
|
||||||
|
// expunged.
|
||||||
|
//
|
||||||
|
// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and
|
||||||
|
// returns with ok==false.
|
||||||
|
func (e *entry[V]) tryLoadOrStore(i V) (actual V, loaded, ok bool) {
|
||||||
|
p := atomic.LoadPointer(&e.p)
|
||||||
|
if p == expunged {
|
||||||
|
return actual, false, false
|
||||||
|
}
|
||||||
|
if p != nil {
|
||||||
|
return *(*V)(p), true, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the interface after the first load to make this method more amenable
|
||||||
|
// to escape analysis: if we hit the "load" path or the entry is expunged, we
|
||||||
|
// shouldn'V bother heap-allocating.
|
||||||
|
ic := i
|
||||||
|
for {
|
||||||
|
if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) {
|
||||||
|
return i, false, true
|
||||||
|
}
|
||||||
|
p = atomic.LoadPointer(&e.p)
|
||||||
|
if p == expunged {
|
||||||
|
return actual, false, false
|
||||||
|
}
|
||||||
|
if p != nil {
|
||||||
|
return *(*V)(p), true, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the value for a key.
|
||||||
|
func (m *MapOf[K, V]) Delete(key K) {
|
||||||
|
read, _ := m.read.Load().(readOnly[K, V])
|
||||||
|
e, ok := read.m[key]
|
||||||
|
if !ok && read.amended {
|
||||||
|
m.mu.Lock()
|
||||||
|
read, _ = m.read.Load().(readOnly[K, V])
|
||||||
|
e, ok = read.m[key]
|
||||||
|
if !ok && read.amended {
|
||||||
|
delete(m.dirty, key)
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
e.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *entry[V]) delete() (hadValue bool) {
|
||||||
|
for {
|
||||||
|
p := atomic.LoadPointer(&e.p)
|
||||||
|
if p == nil || p == expunged {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if atomic.CompareAndSwapPointer(&e.p, p, nil) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range calls f sequentially for each key and value present in the map.
|
||||||
|
// If f returns false, range stops the iteration.
|
||||||
|
//
|
||||||
|
// Range does not necessarily correspond to any consistent snapshot of the MapOf's
|
||||||
|
// contents: no key will be visited more than once, but if the value for any key
|
||||||
|
// is stored or deleted concurrently, Range may reflect any mapping for that key
|
||||||
|
// from any point during the Range call.
|
||||||
|
//
|
||||||
|
// Range may be O(N) with the number of elements in the map even if f returns
|
||||||
|
// false after a constant number of calls.
|
||||||
|
func (m *MapOf[K, V]) Range(f func(key K, value V) bool) {
|
||||||
|
// We need to be able to iterate over all of the keys that were already
|
||||||
|
// present at the start of the call to Range.
|
||||||
|
// If read.amended is false, then read.m satisfies that property without
|
||||||
|
// requiring us to hold m.mu for a long time.
|
||||||
|
read, _ := m.read.Load().(readOnly[K, V])
|
||||||
|
if read.amended {
|
||||||
|
// m.dirty contains keys not in read.m. Fortunately, Range is already O(N)
|
||||||
|
// (assuming the caller does not break out early), so a call to Range
|
||||||
|
// amortizes an entire copy of the map: we can promote the dirty copy
|
||||||
|
// immediately!
|
||||||
|
m.mu.Lock()
|
||||||
|
read, _ = m.read.Load().(readOnly[K, V])
|
||||||
|
if read.amended {
|
||||||
|
read = readOnly[K, V]{m: m.dirty}
|
||||||
|
m.read.Store(read)
|
||||||
|
m.dirty = nil
|
||||||
|
m.misses = 0
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, e := range read.m {
|
||||||
|
v, ok := e.load()
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !f(k, v) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns a slice of the values in the map.
|
||||||
|
func (m *MapOf[K, V]) Values() []V {
|
||||||
|
var values []V
|
||||||
|
m.Range(func(key K, value V) bool {
|
||||||
|
values = append(values, value)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapOf[K, V]) Count() int {
|
||||||
|
return len(m.dirty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapOf[K, V]) Empty() bool {
|
||||||
|
return m.Count() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapOf[K, V]) ToMap() map[K]V {
|
||||||
|
ans := make(map[K]V)
|
||||||
|
m.Range(func(key K, value V) bool {
|
||||||
|
ans[key] = value
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapOf[K, V]) Clear() {
|
||||||
|
m.Range(func(key K, value V) bool {
|
||||||
|
m.Delete(key)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapOf[K, V]) missLocked() {
|
||||||
|
m.misses++
|
||||||
|
if m.misses < len(m.dirty) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.read.Store(readOnly[K, V]{m: m.dirty})
|
||||||
|
m.dirty = nil
|
||||||
|
m.misses = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MapOf[K, V]) dirtyLocked() {
|
||||||
|
if m.dirty != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
read, _ := m.read.Load().(readOnly[K, V])
|
||||||
|
m.dirty = make(map[K]*entry[V], len(read.m))
|
||||||
|
for k, e := range read.m {
|
||||||
|
if !e.tryExpungeLocked() {
|
||||||
|
m.dirty[k] = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *entry[V]) tryExpungeLocked() (isExpunged bool) {
|
||||||
|
p := atomic.LoadPointer(&e.p)
|
||||||
|
for p == nil {
|
||||||
|
if atomic.CompareAndSwapPointer(&e.p, nil, expunged) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
p = atomic.LoadPointer(&e.p)
|
||||||
|
}
|
||||||
|
return p == expunged
|
||||||
|
}
|
||||||
52
pkg/sign/hmac.go
Normal file
52
pkg/sign/hmac.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HMACSign struct {
|
||||||
|
SecretKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HMACSign) Sign(data string, expire int64) string {
|
||||||
|
h := hmac.New(sha256.New, s.SecretKey)
|
||||||
|
expireTimeStamp := strconv.FormatInt(expire, 10)
|
||||||
|
_, err := io.WriteString(h, data+":"+expireTimeStamp)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64.URLEncoding.EncodeToString(h.Sum(nil)) + ":" + expireTimeStamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s HMACSign) Verify(data, sign string) error {
|
||||||
|
signSlice := strings.Split(sign, ":")
|
||||||
|
// check whether contains expire time
|
||||||
|
if signSlice[len(signSlice)-1] == "" {
|
||||||
|
return ErrExpireMissing
|
||||||
|
}
|
||||||
|
// check whether expire time is expired
|
||||||
|
expires, err := strconv.ParseInt(signSlice[len(signSlice)-1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return ErrExpireInvalid
|
||||||
|
}
|
||||||
|
// if expire time is expired, return error
|
||||||
|
if expires < time.Now().Unix() && expires != 0 {
|
||||||
|
return ErrSignExpired
|
||||||
|
}
|
||||||
|
// verify sign
|
||||||
|
if s.Sign(data, expires) != sign {
|
||||||
|
return ErrSignInvalid
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHMACSign(secret []byte) Sign {
|
||||||
|
return HMACSign{SecretKey: secret}
|
||||||
|
}
|
||||||
15
pkg/sign/sign.go
Normal file
15
pkg/sign/sign.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package sign
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
type Sign interface {
|
||||||
|
Sign(data string, expire int64) string
|
||||||
|
Verify(data, sign string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrSignExpired = errors.New("sign expired")
|
||||||
|
ErrSignInvalid = errors.New("sign invalid")
|
||||||
|
ErrExpireInvalid = errors.New("expire invalid")
|
||||||
|
ErrExpireMissing = errors.New("expire missing")
|
||||||
|
)
|
||||||
212
pkg/singleflight/singleflight.go
Normal file
212
pkg/singleflight/singleflight.go
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package singleflight provides a duplicate function call suppression
|
||||||
|
// mechanism.
|
||||||
|
package singleflight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// errGoexit indicates the runtime.Goexit was called in
|
||||||
|
// the user given function.
|
||||||
|
var errGoexit = errors.New("runtime.Goexit was called")
|
||||||
|
|
||||||
|
// A panicError is an arbitrary value recovered from a panic
|
||||||
|
// with the stack trace during the execution of given function.
|
||||||
|
type panicError struct {
|
||||||
|
value any
|
||||||
|
stack []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements error interface.
|
||||||
|
func (p *panicError) Error() string {
|
||||||
|
return fmt.Sprintf("%v\n\n%s", p.value, p.stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPanicError(v any) error {
|
||||||
|
stack := debug.Stack()
|
||||||
|
|
||||||
|
// The first line of the stack trace is of the form "goroutine N [status]:"
|
||||||
|
// but by the time the panic reaches Do the goroutine may no longer exist
|
||||||
|
// and its status will have changed. Trim out the misleading line.
|
||||||
|
if line := bytes.IndexByte(stack[:], '\n'); line >= 0 {
|
||||||
|
stack = stack[line+1:]
|
||||||
|
}
|
||||||
|
return &panicError{value: v, stack: stack}
|
||||||
|
}
|
||||||
|
|
||||||
|
// call is an in-flight or completed singleflight.Do call
|
||||||
|
type call[T any] struct {
|
||||||
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
// These fields are written once before the WaitGroup is done
|
||||||
|
// and are only read after the WaitGroup is done.
|
||||||
|
val T
|
||||||
|
err error
|
||||||
|
|
||||||
|
// forgotten indicates whether Forget was called with this call's key
|
||||||
|
// while the call was still in flight.
|
||||||
|
forgotten bool
|
||||||
|
|
||||||
|
// These fields are read and written with the singleflight
|
||||||
|
// mutex held before the WaitGroup is done, and are read but
|
||||||
|
// not written after the WaitGroup is done.
|
||||||
|
dups int
|
||||||
|
chans []chan<- Result[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group represents a class of work and forms a namespace in
|
||||||
|
// which units of work can be executed with duplicate suppression.
|
||||||
|
type Group[T any] struct {
|
||||||
|
mu sync.Mutex // protects m
|
||||||
|
m map[string]*call[T] // lazily initialized
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result holds the results of Do, so they can be passed
|
||||||
|
// on a channel.
|
||||||
|
type Result[T any] struct {
|
||||||
|
Val T
|
||||||
|
Err error
|
||||||
|
Shared bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do executes and returns the results of the given function, making
|
||||||
|
// sure that only one execution is in-flight for a given key at a
|
||||||
|
// time. If a duplicate comes in, the duplicate caller waits for the
|
||||||
|
// original to complete and receives the same results.
|
||||||
|
// The return value shared indicates whether v was given to multiple callers.
|
||||||
|
func (g *Group[T]) Do(key string, fn func() (T, error)) (v T, err error, shared bool) {
|
||||||
|
g.mu.Lock()
|
||||||
|
if g.m == nil {
|
||||||
|
g.m = make(map[string]*call[T])
|
||||||
|
}
|
||||||
|
if c, ok := g.m[key]; ok {
|
||||||
|
c.dups++
|
||||||
|
g.mu.Unlock()
|
||||||
|
c.wg.Wait()
|
||||||
|
|
||||||
|
if e, ok := c.err.(*panicError); ok {
|
||||||
|
panic(e)
|
||||||
|
} else if c.err == errGoexit {
|
||||||
|
runtime.Goexit()
|
||||||
|
}
|
||||||
|
return c.val, c.err, true
|
||||||
|
}
|
||||||
|
c := new(call[T])
|
||||||
|
c.wg.Add(1)
|
||||||
|
g.m[key] = c
|
||||||
|
g.mu.Unlock()
|
||||||
|
|
||||||
|
g.doCall(c, key, fn)
|
||||||
|
return c.val, c.err, c.dups > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoChan is like Do but returns a channel that will receive the
|
||||||
|
// results when they are ready.
|
||||||
|
//
|
||||||
|
// The returned channel will not be closed.
|
||||||
|
func (g *Group[T]) DoChan(key string, fn func() (T, error)) <-chan Result[T] {
|
||||||
|
ch := make(chan Result[T], 1)
|
||||||
|
g.mu.Lock()
|
||||||
|
if g.m == nil {
|
||||||
|
g.m = make(map[string]*call[T])
|
||||||
|
}
|
||||||
|
if c, ok := g.m[key]; ok {
|
||||||
|
c.dups++
|
||||||
|
c.chans = append(c.chans, ch)
|
||||||
|
g.mu.Unlock()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
c := &call[T]{chans: []chan<- Result[T]{ch}}
|
||||||
|
c.wg.Add(1)
|
||||||
|
g.m[key] = c
|
||||||
|
g.mu.Unlock()
|
||||||
|
|
||||||
|
go g.doCall(c, key, fn)
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// doCall handles the single call for a key.
|
||||||
|
func (g *Group[T]) doCall(c *call[T], key string, fn func() (T, error)) {
|
||||||
|
normalReturn := false
|
||||||
|
recovered := false
|
||||||
|
|
||||||
|
// use double-defer to distinguish panic from runtime.Goexit,
|
||||||
|
// more details see https://golang.org/cl/134395
|
||||||
|
defer func() {
|
||||||
|
// the given function invoked runtime.Goexit
|
||||||
|
if !normalReturn && !recovered {
|
||||||
|
c.err = errGoexit
|
||||||
|
}
|
||||||
|
|
||||||
|
c.wg.Done()
|
||||||
|
g.mu.Lock()
|
||||||
|
defer g.mu.Unlock()
|
||||||
|
if !c.forgotten {
|
||||||
|
delete(g.m, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if e, ok := c.err.(*panicError); ok {
|
||||||
|
// In order to prevent the waiting channels from being blocked forever,
|
||||||
|
// needs to ensure that this panic cannot be recovered.
|
||||||
|
if len(c.chans) > 0 {
|
||||||
|
go panic(e)
|
||||||
|
select {} // Keep this goroutine around so that it will appear in the crash dump.
|
||||||
|
} else {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
} else if c.err == errGoexit {
|
||||||
|
// Already in the process of goexit, no need to call again
|
||||||
|
} else {
|
||||||
|
// Normal return
|
||||||
|
for _, ch := range c.chans {
|
||||||
|
ch <- Result[T]{c.val, c.err, c.dups > 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
func() {
|
||||||
|
defer func() {
|
||||||
|
if !normalReturn {
|
||||||
|
// Ideally, we would wait to take a stack trace until we've determined
|
||||||
|
// whether this is a panic or a runtime.Goexit.
|
||||||
|
//
|
||||||
|
// Unfortunately, the only way we can distinguish the two is to see
|
||||||
|
// whether the recover stopped the goroutine from terminating, and by
|
||||||
|
// the time we know that, the part of the stack trace relevant to the
|
||||||
|
// panic has been discarded.
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
c.err = newPanicError(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.val, c.err = fn()
|
||||||
|
normalReturn = true
|
||||||
|
}()
|
||||||
|
|
||||||
|
if !normalReturn {
|
||||||
|
recovered = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forget tells the singleflight to forget about a key. Future calls
|
||||||
|
// to Do for this key will call the function rather than waiting for
|
||||||
|
// an earlier call to complete.
|
||||||
|
func (g *Group[T]) Forget(key string) {
|
||||||
|
g.mu.Lock()
|
||||||
|
if c, ok := g.m[key]; ok {
|
||||||
|
c.forgotten = true
|
||||||
|
}
|
||||||
|
delete(g.m, key)
|
||||||
|
g.mu.Unlock()
|
||||||
|
}
|
||||||
@@ -11,13 +11,12 @@
|
|||||||
package sqlite
|
package sqlite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
|
||||||
model2 "github.com/IceWhaleTech/CasaOS/service/model"
|
model2 "github.com/IceWhaleTech/CasaOS/service/model"
|
||||||
"github.com/glebarez/sqlite"
|
"github.com/glebarez/sqlite"
|
||||||
"go.uber.org/zap"
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,23 +31,24 @@ func GetDb(dbPath string) *gorm.DB {
|
|||||||
// db, err := gorm.Open(mysql2.Open(dsn), &gorm.Config{})
|
// db, err := gorm.Open(mysql2.Open(dsn), &gorm.Config{})
|
||||||
file.IsNotExistMkDir(dbPath)
|
file.IsNotExistMkDir(dbPath)
|
||||||
db, err := gorm.Open(sqlite.Open(dbPath+"/casaOS.db"), &gorm.Config{})
|
db, err := gorm.Open(sqlite.Open(dbPath+"/casaOS.db"), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
panic("sqlite connect error")
|
||||||
|
}
|
||||||
|
|
||||||
c, _ := db.DB()
|
c, _ := db.DB()
|
||||||
c.SetMaxIdleConns(10)
|
c.SetMaxIdleConns(10)
|
||||||
c.SetMaxOpenConns(1)
|
c.SetMaxOpenConns(1)
|
||||||
c.SetConnMaxIdleTime(time.Second * 1000)
|
c.SetConnMaxIdleTime(time.Second * 1000)
|
||||||
if err != nil {
|
|
||||||
logger.Error("sqlite connect error", zap.Any("db connect error", err))
|
|
||||||
panic("sqlite connect error")
|
|
||||||
}
|
|
||||||
gdb = db
|
gdb = db
|
||||||
|
|
||||||
err = db.AutoMigrate(&model2.AppNotify{}, model2.SharesDBModel{}, model2.ConnectionsDBModel{})
|
err = db.AutoMigrate(&model2.AppNotify{}, model2.SharesDBModel{}, model2.ConnectionsDBModel{}, model2.PeerDriveDBModel{})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
db.Exec("DROP TABLE IF EXISTS o_application")
|
db.Exec("DROP TABLE IF EXISTS o_application")
|
||||||
db.Exec("DROP TABLE IF EXISTS o_friend")
|
db.Exec("DROP TABLE IF EXISTS o_friend")
|
||||||
db.Exec("DROP TABLE IF EXISTS o_person_download")
|
db.Exec("DROP TABLE IF EXISTS o_person_download")
|
||||||
db.Exec("DROP TABLE IF EXISTS o_person_down_record")
|
db.Exec("DROP TABLE IF EXISTS o_person_down_record")
|
||||||
if err != nil {
|
|
||||||
logger.Error("check or create db error", zap.Any("error", err))
|
|
||||||
}
|
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|||||||
18
pkg/utils/balance.go
Normal file
18
pkg/utils/balance.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
var balance = ".balance"
|
||||||
|
|
||||||
|
func IsBalance(str string) bool {
|
||||||
|
return strings.Contains(str, balance)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetActualMountPath remove balance suffix
|
||||||
|
func GetActualMountPath(virtualPath string) string {
|
||||||
|
bIndex := strings.LastIndex(virtualPath, ".balance")
|
||||||
|
if bIndex != -1 {
|
||||||
|
virtualPath = virtualPath[:bIndex]
|
||||||
|
}
|
||||||
|
return virtualPath
|
||||||
|
}
|
||||||
5
pkg/utils/bool.go
Normal file
5
pkg/utils/bool.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
func IsBool(bs ...bool) bool {
|
||||||
|
return len(bs) > 0 && bs[0]
|
||||||
|
}
|
||||||
@@ -2,28 +2,30 @@ package command
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func OnlyExec(cmdStr string) {
|
func OnlyExec(cmdStr string) {
|
||||||
cmd := exec.Command("/bin/bash", "-c", cmdStr)
|
cmd := exec.Command("/bin/bash", "-c", cmdStr)
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer stdout.Close()
|
defer stdout.Close()
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cmd.Wait()
|
|
||||||
return
|
if err := cmd.Wait(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecResultStrArray(cmdStr string) []string {
|
func ExecResultStrArray(cmdStr string) []string {
|
||||||
@@ -51,7 +53,11 @@ func ExecResultStrArray(cmdStr string) []string {
|
|||||||
}
|
}
|
||||||
networklist = append(networklist, string(output))
|
networklist = append(networklist, string(output))
|
||||||
}
|
}
|
||||||
cmd.Wait()
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
return networklist
|
return networklist
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,11 +76,15 @@ func ExecResultStr(cmdStr string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
str, err := ioutil.ReadAll(stdout)
|
str, err := ioutil.ReadAll(stdout)
|
||||||
cmd.Wait()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
return string(str)
|
return string(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,23 +108,6 @@ func ExecLSBLKByPath(path string) []byte {
|
|||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
// exec smart
|
|
||||||
func ExecSmartCTLByPath(path string) []byte {
|
|
||||||
timeout := 3
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
output, err := exec.CommandContext(ctx, "smartctl", "-a", path, "-j").Output()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("smartctl", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return output
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExecEnabledSMART(path string) {
|
|
||||||
exec.Command("smartctl", "-s on", path).Output()
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExecuteScripts(scriptDirectory string) {
|
func ExecuteScripts(scriptDirectory string) {
|
||||||
if _, err := os.Stat(scriptDirectory); os.IsNotExist(err) {
|
if _, err := os.Stat(scriptDirectory); os.IsNotExist(err) {
|
||||||
fmt.Printf("No post-start scripts at %s\n", scriptDirectory)
|
fmt.Printf("No post-start scripts at %s\n", scriptDirectory)
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
package command
|
package command_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
|
||||||
|
"go.uber.org/goleak"
|
||||||
"gotest.tools/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExecuteScripts(t *testing.T) {
|
func TestExecuteScripts(t *testing.T) {
|
||||||
|
goleak.VerifyNone(t)
|
||||||
|
|
||||||
// make a temp directory
|
// make a temp directory
|
||||||
tmpDir, err := os.MkdirTemp("", "casaos-test-*")
|
tmpDir, err := os.MkdirTemp("", "casaos-test-*")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
ExecuteScripts(tmpDir)
|
command.ExecuteScripts(tmpDir)
|
||||||
|
|
||||||
// create a sample script under tmpDir
|
// create a sample script under tmpDir
|
||||||
script := tmpDir + "/test.sh"
|
script := tmpDir + "/test.sh"
|
||||||
@@ -25,5 +29,5 @@ func TestExecuteScripts(t *testing.T) {
|
|||||||
_, err = f.WriteString("#!/bin/bash\necho 123")
|
_, err = f.WriteString("#!/bin/bash\necho 123")
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
ExecuteScripts(tmpDir)
|
command.ExecuteScripts(tmpDir)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,11 +46,12 @@ const (
|
|||||||
ERROR_APP_NAME_EXIST = 50004
|
ERROR_APP_NAME_EXIST = 50004
|
||||||
|
|
||||||
//file
|
//file
|
||||||
FILE_DOES_NOT_EXIST = 60001
|
FILE_DOES_NOT_EXIST = 60001
|
||||||
FILE_READ_ERROR = 60002
|
FILE_READ_ERROR = 60002
|
||||||
FILE_DELETE_ERROR = 60003
|
FILE_DELETE_ERROR = 60003
|
||||||
DIR_NOT_EXISTS = 60004
|
DIR_NOT_EXISTS = 60004
|
||||||
SOURCE_DES_SAME = 60005
|
SOURCE_DES_SAME = 60005
|
||||||
|
MOUNTED_DIRECTIORIES = 60006
|
||||||
|
|
||||||
//share
|
//share
|
||||||
SHARE_ALREADY_EXISTS = 70001
|
SHARE_ALREADY_EXISTS = 70001
|
||||||
@@ -109,8 +110,9 @@ var MsgFlags = map[int]string{
|
|||||||
|
|
||||||
DIR_NOT_EXISTS: "Directory does not exist",
|
DIR_NOT_EXISTS: "Directory does not exist",
|
||||||
|
|
||||||
FILE_READ_ERROR: "File read error",
|
FILE_READ_ERROR: "File read error",
|
||||||
FILE_DELETE_ERROR: "Delete error",
|
FILE_DELETE_ERROR: "Delete error",
|
||||||
|
MOUNTED_DIRECTIORIES: "The directory is mounted, please unmount it first.",
|
||||||
|
|
||||||
COMMAND_ERROR_INVALID_OPERATION: "invalid operation",
|
COMMAND_ERROR_INVALID_OPERATION: "invalid operation",
|
||||||
}
|
}
|
||||||
|
|||||||
14
pkg/utils/ctx.go
Normal file
14
pkg/utils/ctx.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IsCanceled(ctx context.Context) bool {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package file
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -577,3 +578,165 @@ func ReadLine(lineNumber int, path string) string {
|
|||||||
defer file.Close()
|
defer file.Close()
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NameAccumulation(name string, dir string) string {
|
||||||
|
path := filepath.Join(dir, name)
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
base := name
|
||||||
|
strings.Split(base, "_")
|
||||||
|
index := strings.LastIndex(base, "_")
|
||||||
|
if index < 0 {
|
||||||
|
index = len(base)
|
||||||
|
}
|
||||||
|
for i := 1; ; i++ {
|
||||||
|
newPath := filepath.Join(dir, fmt.Sprintf("%s_%d", base[:index], i))
|
||||||
|
if _, err := os.Stat(newPath); os.IsNotExist(err) {
|
||||||
|
return fmt.Sprintf("%s_%d", base[:index], i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseFileHeader(h []byte, boundary []byte) (map[string]string, bool) {
|
||||||
|
arr := bytes.Split(h, boundary)
|
||||||
|
//var out_header FileHeader
|
||||||
|
//out_header.ContentLength = -1
|
||||||
|
const (
|
||||||
|
CONTENT_DISPOSITION = "Content-Disposition: "
|
||||||
|
NAME = "name=\""
|
||||||
|
FILENAME = "filename=\""
|
||||||
|
CONTENT_TYPE = "Content-Type: "
|
||||||
|
CONTENT_LENGTH = "Content-Length: "
|
||||||
|
)
|
||||||
|
result := make(map[string]string)
|
||||||
|
for _, item := range arr {
|
||||||
|
|
||||||
|
tarr := bytes.Split(item, []byte(";"))
|
||||||
|
if len(tarr) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tbyte := tarr[1]
|
||||||
|
fmt.Println(string(tbyte))
|
||||||
|
tbyte = bytes.ReplaceAll(tbyte, []byte("\r\n--"), []byte(""))
|
||||||
|
tbyte = bytes.ReplaceAll(tbyte, []byte("name=\""), []byte(""))
|
||||||
|
tempArr := bytes.Split(tbyte, []byte("\"\r\n\r\n"))
|
||||||
|
if len(tempArr) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bytes.HasPrefix(item, []byte("name="))
|
||||||
|
result[strings.TrimSpace(string(tempArr[0]))] = strings.TrimSpace(string(tempArr[1]))
|
||||||
|
}
|
||||||
|
// for _, item := range arr {
|
||||||
|
// if bytes.HasPrefix(item, []byte(CONTENT_DISPOSITION)) {
|
||||||
|
// l := len(CONTENT_DISPOSITION)
|
||||||
|
// arr1 := bytes.Split(item[l:], []byte("; "))
|
||||||
|
// out_header.ContentDisposition = string(arr1[0])
|
||||||
|
// if bytes.HasPrefix(arr1[1], []byte(NAME)) {
|
||||||
|
// out_header.Name = string(arr1[1][len(NAME) : len(arr1[1])-1])
|
||||||
|
// }
|
||||||
|
// l = len(arr1[2])
|
||||||
|
// if bytes.HasPrefix(arr1[2], []byte(FILENAME)) && arr1[2][l-1] == 0x22 {
|
||||||
|
// out_header.FileName = string(arr1[2][len(FILENAME) : l-1])
|
||||||
|
// }
|
||||||
|
// } else if bytes.HasPrefix(item, []byte(CONTENT_TYPE)) {
|
||||||
|
// l := len(CONTENT_TYPE)
|
||||||
|
// out_header.ContentType = string(item[l:])
|
||||||
|
// } else if bytes.HasPrefix(item, []byte(CONTENT_LENGTH)) {
|
||||||
|
// l := len(CONTENT_LENGTH)
|
||||||
|
// s := string(item[l:])
|
||||||
|
// content_length, err := strconv.ParseInt(s, 10, 64)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Printf("content length error:%s", string(item))
|
||||||
|
// return out_header, false
|
||||||
|
// } else {
|
||||||
|
// out_header.ContentLength = content_length
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// log.Printf("unknown:%s\n", string(item))
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//fmt.Println(result)
|
||||||
|
// if len(out_header.FileName) == 0 {
|
||||||
|
// return out_header, false
|
||||||
|
// }
|
||||||
|
return result, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadToBoundary(boundary []byte, stream io.ReadCloser, target io.WriteCloser) ([]byte, bool, error) {
|
||||||
|
read_data := make([]byte, 1024*8)
|
||||||
|
read_data_len := 0
|
||||||
|
buf := make([]byte, 1024*4)
|
||||||
|
b_len := len(boundary)
|
||||||
|
reach_end := false
|
||||||
|
for !reach_end {
|
||||||
|
read_len, err := stream.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF && read_len <= 0 {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
reach_end = true
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(read_data[read_data_len:], buf[:read_len])
|
||||||
|
read_data_len += read_len
|
||||||
|
if read_data_len < b_len+4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
loc := bytes.Index(read_data[:read_data_len], boundary)
|
||||||
|
if loc >= 0 {
|
||||||
|
|
||||||
|
target.Write(read_data[:loc-4])
|
||||||
|
return read_data[loc:read_data_len], reach_end, nil
|
||||||
|
}
|
||||||
|
target.Write(read_data[:read_data_len-b_len-4])
|
||||||
|
copy(read_data[0:], read_data[read_data_len-b_len-4:])
|
||||||
|
read_data_len = b_len + 4
|
||||||
|
}
|
||||||
|
target.Write(read_data[:read_data_len])
|
||||||
|
return nil, reach_end, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseFromHead(read_data []byte, read_total int, boundary []byte, stream io.ReadCloser) (map[string]string, []byte, error) {
|
||||||
|
|
||||||
|
buf := make([]byte, 1024*8)
|
||||||
|
found_boundary := false
|
||||||
|
boundary_loc := -1
|
||||||
|
|
||||||
|
for {
|
||||||
|
read_len, err := stream.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if read_total+read_len > cap(read_data) {
|
||||||
|
return nil, nil, fmt.Errorf("not found boundary")
|
||||||
|
}
|
||||||
|
copy(read_data[read_total:], buf[:read_len])
|
||||||
|
read_total += read_len
|
||||||
|
if !found_boundary {
|
||||||
|
boundary_loc = bytes.LastIndex(read_data[:read_total], boundary)
|
||||||
|
if boundary_loc == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
found_boundary = true
|
||||||
|
}
|
||||||
|
start_loc := boundary_loc + len(boundary)
|
||||||
|
fmt.Println(string(read_data))
|
||||||
|
file_head_loc := bytes.Index(read_data[start_loc:read_total], []byte("\r\n\r\n"))
|
||||||
|
if file_head_loc == -1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
file_head_loc += start_loc
|
||||||
|
ret := false
|
||||||
|
headMap, ret := ParseFileHeader(read_data, boundary)
|
||||||
|
if !ret {
|
||||||
|
return headMap, nil, fmt.Errorf("ParseFileHeader fail:%s", string(read_data[start_loc:file_head_loc]))
|
||||||
|
}
|
||||||
|
return headMap, read_data[file_head_loc+4 : read_total], nil
|
||||||
|
}
|
||||||
|
return nil, nil, fmt.Errorf("reach to sream EOF")
|
||||||
|
}
|
||||||
|
|||||||
16
pkg/utils/file/file_test.go
Normal file
16
pkg/utils/file/file_test.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.uber.org/goleak"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNameAccumulation(t *testing.T) {
|
||||||
|
goleak.VerifyNone(t)
|
||||||
|
|
||||||
|
fmt.Println("aaa")
|
||||||
|
a := NameAccumulation("/mnt/test_1_1", "/")
|
||||||
|
fmt.Println(a)
|
||||||
|
}
|
||||||
171
pkg/utils/httper/drive.go
Normal file
171
pkg/utils/httper/drive.go
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
package httper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MountList struct {
|
||||||
|
MountPoints []MountPoints `json:"mountPoints"`
|
||||||
|
}
|
||||||
|
type MountPoints struct {
|
||||||
|
MountPoint string `json:"MountPoint"`
|
||||||
|
Fs string `json:"Fs"`
|
||||||
|
Icon string `json:"Icon"`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
}
|
||||||
|
type MountPoint struct {
|
||||||
|
MountPoint string `json:"mount_point"`
|
||||||
|
Fs string `json:"fs"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
type MountResult struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
Input struct {
|
||||||
|
Fs string `json:"fs"`
|
||||||
|
MountPoint string `json:"mountPoint"`
|
||||||
|
} `json:"input"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemotesResult struct {
|
||||||
|
Remotes []string `json:"remotes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||||
|
var DefaultTimeout = time.Second * 30
|
||||||
|
|
||||||
|
func NewRestyClient() *resty.Client {
|
||||||
|
|
||||||
|
unixSocket := "/var/run/rclone/rclone.sock"
|
||||||
|
|
||||||
|
transport := http.Transport{
|
||||||
|
Dial: func(_, _ string) (net.Conn, error) {
|
||||||
|
return net.Dial("unix", unixSocket)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
|
||||||
|
client.SetTransport(&transport).SetBaseURL("http://localhost")
|
||||||
|
client.SetRetryCount(3).SetRetryWaitTime(5*time.Second).SetTimeout(DefaultTimeout).SetHeader("User-Agent", UserAgent)
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMountList() (MountList, error) {
|
||||||
|
var result MountList
|
||||||
|
res, err := NewRestyClient().R().Post("/mount/listmounts")
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
return result, fmt.Errorf("get mount list failed")
|
||||||
|
}
|
||||||
|
json.Unmarshal(res.Body(), &result)
|
||||||
|
for i := 0; i < len(result.MountPoints); i++ {
|
||||||
|
result.MountPoints[i].Fs = result.MountPoints[i].Fs[:len(result.MountPoints[i].Fs)-1]
|
||||||
|
}
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mount(mountPoint string, fs string) error {
|
||||||
|
res, err := NewRestyClient().R().SetFormData(map[string]string{
|
||||||
|
"mountPoint": mountPoint,
|
||||||
|
"fs": fs,
|
||||||
|
"mountOpt": `{"AllowOther": true}`,
|
||||||
|
"vfsOpt": `{"CacheMode": 3}`,
|
||||||
|
}).Post("/mount/mount")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
return fmt.Errorf("mount failed")
|
||||||
|
}
|
||||||
|
logger.Info("mount then", zap.Any("res", res.Body()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func Unmount(mountPoint string) error {
|
||||||
|
res, err := NewRestyClient().R().SetFormData(map[string]string{
|
||||||
|
"mountPoint": mountPoint,
|
||||||
|
}).Post("/mount/unmount")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("when unmount", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
logger.Error("then unmount failed", zap.Any("res", res.Body()))
|
||||||
|
return fmt.Errorf("unmount failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("unmount then", zap.Any("res", res.Body()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateConfig(data map[string]string, name, t string) error {
|
||||||
|
data["config_is_local"] = "false"
|
||||||
|
dataStr, _ := json.Marshal(data)
|
||||||
|
res, err := NewRestyClient().R().SetFormData(map[string]string{
|
||||||
|
"name": name,
|
||||||
|
"parameters": string(dataStr),
|
||||||
|
"type": t,
|
||||||
|
}).Post("/config/create")
|
||||||
|
logger.Info("when create config then", zap.Any("res", res.Body()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
return fmt.Errorf("create config failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetConfigByName(name string) (map[string]string, error) {
|
||||||
|
|
||||||
|
res, err := NewRestyClient().R().SetFormData(map[string]string{
|
||||||
|
"name": name,
|
||||||
|
}).Post("/config/get")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
return nil, fmt.Errorf("create config failed")
|
||||||
|
}
|
||||||
|
var result map[string]string
|
||||||
|
json.Unmarshal(res.Body(), &result)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
func GetAllConfigName() (RemotesResult, error) {
|
||||||
|
var result RemotesResult
|
||||||
|
res, err := NewRestyClient().R().SetFormData(map[string]string{}).Post("/config/listremotes")
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
return result, fmt.Errorf("get config failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
json.Unmarshal(res.Body(), &result)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
func DeleteConfigByName(name string) error {
|
||||||
|
res, err := NewRestyClient().R().SetFormData(map[string]string{
|
||||||
|
"name": name,
|
||||||
|
}).Post("/config/delete")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
return fmt.Errorf("delete config failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -10,21 +10,22 @@ import (
|
|||||||
func IsIPv4(address string) bool {
|
func IsIPv4(address string) bool {
|
||||||
return strings.Count(address, ":") < 2
|
return strings.Count(address, ":") < 2
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsIPv6(address string) bool {
|
func IsIPv6(address string) bool {
|
||||||
return strings.Count(address, ":") >= 2
|
return strings.Count(address, ":") >= 2
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取外网ip
|
// 获取外网ip
|
||||||
func GetExternalIPV4() string {
|
func GetExternalIPV4() string {
|
||||||
return httper2.Get("https://api.ipify.org", nil)
|
return httper2.Get("https://api.ipify.org", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取外网ip
|
// 获取外网ip
|
||||||
func GetExternalIPV6() string {
|
func GetExternalIPV6() string {
|
||||||
return httper2.Get("https://api6.ipify.org", nil)
|
return httper2.Get("https://api6.ipify.org", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取本地ip
|
// 获取本地ip
|
||||||
func GetLoclIp() string {
|
func GetLoclIp() string {
|
||||||
addrs, err := net.InterfaceAddrs()
|
addrs, err := net.InterfaceAddrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -35,11 +36,11 @@ func GetLoclIp() string {
|
|||||||
if ipnet.IP.To4() != nil {
|
if ipnet.IP.To4() != nil {
|
||||||
return ipnet.IP.String()
|
return ipnet.IP.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "127.0.0.1"
|
return "127.0.0.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDeviceAllIP(port string) []string {
|
func GetDeviceAllIP(port string) []string {
|
||||||
var address []string
|
var address []string
|
||||||
addrs, err := net.InterfaceAddrs()
|
addrs, err := net.InterfaceAddrs()
|
||||||
@@ -60,7 +61,6 @@ func HasLocalIP(ip net.IP) bool {
|
|||||||
if ip.IsLoopback() {
|
if ip.IsLoopback() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
ip.String()
|
|
||||||
|
|
||||||
ip4 := ip.To4()
|
ip4 := ip.To4()
|
||||||
if ip4 == nil {
|
if ip4 == nil {
|
||||||
|
|||||||
@@ -4,23 +4,28 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"go.uber.org/goleak"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetExternalIPV4(t *testing.T) {
|
func TestGetExternalIPV4(t *testing.T) {
|
||||||
|
goleak.VerifyNone(t)
|
||||||
|
|
||||||
ipv4 := make(chan string)
|
ipv4 := make(chan string)
|
||||||
go func() { ipv4 <- GetExternalIPV4() }()
|
go func() { ipv4 <- GetExternalIPV4() }()
|
||||||
fmt.Println(<-ipv4)
|
fmt.Println(<-ipv4)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetExternalIPV6(t *testing.T) {
|
func TestGetExternalIPV6(t *testing.T) {
|
||||||
ipv6 := make(chan string)
|
ipv6 := make(chan string)
|
||||||
go func() { ipv6 <- GetExternalIPV6() }()
|
go func() { ipv6 <- GetExternalIPV6() }()
|
||||||
fmt.Println(<-ipv6)
|
fmt.Println(<-ipv6)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetLoclIp(t *testing.T) {
|
func TestGetLoclIp(t *testing.T) {
|
||||||
fmt.Println(GetLoclIp())
|
fmt.Println(GetLoclIp())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHasLocalIP(t *testing.T) {
|
func TestHasLocalIP(t *testing.T) {
|
||||||
fmt.Println("dddd")
|
fmt.Println("dddd")
|
||||||
fmt.Println(HasLocalIP(net.ParseIP("192.168.2.10")))
|
fmt.Println(HasLocalIP(net.ParseIP("192.168.2.10")))
|
||||||
|
|||||||
81
pkg/utils/path.go
Normal file
81
pkg/utils/path.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/url"
|
||||||
|
stdpath "path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FixAndCleanPath
|
||||||
|
// The upper layer of the root directory is still the root directory.
|
||||||
|
// So ".." And "." will be cleared
|
||||||
|
// for example
|
||||||
|
// 1. ".." or "." => "/"
|
||||||
|
// 2. "../..." or "./..." => "/..."
|
||||||
|
// 3. "../.x." or "./.x." => "/.x."
|
||||||
|
// 4. "x//\\y" = > "/z/x"
|
||||||
|
func FixAndCleanPath(path string) string {
|
||||||
|
path = strings.ReplaceAll(path, "\\", "/")
|
||||||
|
if !strings.HasPrefix(path, "/") {
|
||||||
|
path = "/" + path
|
||||||
|
}
|
||||||
|
return stdpath.Clean(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathAddSeparatorSuffix Add path '/' suffix
|
||||||
|
// for example /root => /root/
|
||||||
|
func PathAddSeparatorSuffix(path string) string {
|
||||||
|
if !strings.HasSuffix(path, "/") {
|
||||||
|
path = path + "/"
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathEqual judge path is equal
|
||||||
|
func PathEqual(path1, path2 string) bool {
|
||||||
|
return FixAndCleanPath(path1) == FixAndCleanPath(path2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSubPath(path string, subPath string) bool {
|
||||||
|
path, subPath = FixAndCleanPath(path), FixAndCleanPath(subPath)
|
||||||
|
return path == subPath || strings.HasPrefix(subPath, PathAddSeparatorSuffix(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ext(path string) string {
|
||||||
|
ext := stdpath.Ext(path)
|
||||||
|
if strings.HasPrefix(ext, ".") {
|
||||||
|
return ext[1:]
|
||||||
|
}
|
||||||
|
return ext
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodePath(path string, all ...bool) string {
|
||||||
|
seg := strings.Split(path, "/")
|
||||||
|
toReplace := []struct {
|
||||||
|
Src string
|
||||||
|
Dst string
|
||||||
|
}{
|
||||||
|
{Src: "%", Dst: "%25"},
|
||||||
|
{"%", "%25"},
|
||||||
|
{"?", "%3F"},
|
||||||
|
{"#", "%23"},
|
||||||
|
}
|
||||||
|
for i := range seg {
|
||||||
|
if len(all) > 0 && all[0] {
|
||||||
|
seg[i] = url.PathEscape(seg[i])
|
||||||
|
} else {
|
||||||
|
for j := range toReplace {
|
||||||
|
seg[i] = strings.ReplaceAll(seg[i], toReplace[j].Src, toReplace[j].Dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(seg, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
func JoinBasePath(basePath, reqPath string) (string, error) {
|
||||||
|
if strings.HasSuffix(reqPath, "..") || strings.Contains(reqPath, "../") {
|
||||||
|
return "", errors.New("access using relative path is not allowed")
|
||||||
|
}
|
||||||
|
return stdpath.Join(FixAndCleanPath(basePath), FixAndCleanPath(reqPath)), nil
|
||||||
|
}
|
||||||
46
pkg/utils/slice.go
Normal file
46
pkg/utils/slice.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
// SliceEqual check if two slices are equal
|
||||||
|
func SliceEqual[T comparable](a, b []T) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, v := range a {
|
||||||
|
if v != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceContains check if slice contains element
|
||||||
|
func SliceContains[T comparable](arr []T, v T) bool {
|
||||||
|
for _, vv := range arr {
|
||||||
|
if vv == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// SliceConvert convert slice to another type slice
|
||||||
|
func SliceConvert[S any, D any](srcS []S, convert func(src S) (D, error)) ([]D, error) {
|
||||||
|
var res []D
|
||||||
|
for i := range srcS {
|
||||||
|
dst, err := convert(srcS[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = append(res, dst)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustSliceConvert[S any, D any](srcS []S, convert func(src S) D) []D {
|
||||||
|
var res []D
|
||||||
|
for i := range srcS {
|
||||||
|
dst := convert(srcS[i])
|
||||||
|
res = append(res, dst)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
37
pkg/utils/time.go
Normal file
37
pkg/utils/time.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MustParseCNTime(str string) time.Time {
|
||||||
|
lastOpTime, _ := time.ParseInLocation("2006-01-02 15:04:05 -07", str+" +08", time.Local)
|
||||||
|
return lastOpTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDebounce(interval time.Duration) func(f func()) {
|
||||||
|
var timer *time.Timer
|
||||||
|
var lock sync.Mutex
|
||||||
|
return func(f func()) {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
if timer != nil {
|
||||||
|
timer.Stop()
|
||||||
|
}
|
||||||
|
timer = time.AfterFunc(interval, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDebounce2(interval time.Duration, f func()) func() {
|
||||||
|
var timer *time.Timer
|
||||||
|
var lock sync.Mutex
|
||||||
|
return func() {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
if timer == nil {
|
||||||
|
timer = time.AfterFunc(interval, f)
|
||||||
|
}
|
||||||
|
(*time.Timer)(timer).Reset(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,15 +14,15 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/common"
|
||||||
"github.com/IceWhaleTech/CasaOS/model"
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
"github.com/IceWhaleTech/CasaOS/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsNeedUpdate(version model.Version) (bool, model.Version) {
|
func IsNeedUpdate(version model.Version) (bool, model.Version) {
|
||||||
|
|
||||||
v1 := strings.Split(version.Version, ".")
|
v1 := strings.Split(version.Version, ".")
|
||||||
|
|
||||||
v2 := strings.Split(types.CURRENTVERSION, ".")
|
v2 := strings.Split(common.VERSION, ".")
|
||||||
|
|
||||||
for len(v1) < len(v2) {
|
for len(v1) < len(v2) {
|
||||||
v1 = append(v1, "0")
|
v1 = append(v1, "0")
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
//go:build darwin
|
|
||||||
// +build darwin
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @Author: LinkLeong link@icewhale.org
|
|
||||||
* @Date: 2022-08-12 14:22:28
|
|
||||||
* @LastEditors: LinkLeong
|
|
||||||
* @LastEditTime: 2022-09-05 16:27:55
|
|
||||||
* @FilePath: /CasaOS/route/darwin.go
|
|
||||||
* @Description:
|
|
||||||
* @Website: https://www.casaos.io
|
|
||||||
* Copyright (c) 2022 by icewhale, All Rights Reserved.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package route
|
|
||||||
|
|
||||||
func MonitoryUSB() {
|
|
||||||
|
|
||||||
}
|
|
||||||
func SendAllHardwareStatusBySocket() {
|
|
||||||
|
|
||||||
}
|
|
||||||
func SendUSBBySocket() {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -18,13 +18,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/common"
|
||||||
"github.com/IceWhaleTech/CasaOS/model"
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/config"
|
"github.com/IceWhaleTech/CasaOS/pkg/config"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/samba"
|
"github.com/IceWhaleTech/CasaOS/pkg/samba"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/utils/encryption"
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/encryption"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
|
||||||
"github.com/IceWhaleTech/CasaOS/service"
|
"github.com/IceWhaleTech/CasaOS/service"
|
||||||
"github.com/IceWhaleTech/CasaOS/types"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ func InitInfo() {
|
|||||||
logger.Error("GetMacAddress", zap.String("error", err.Error()))
|
logger.Error("GetMacAddress", zap.String("error", err.Error()))
|
||||||
}
|
}
|
||||||
mb.Hash = encryption.GetMD5ByStr(mac)
|
mb.Hash = encryption.GetMD5ByStr(mac)
|
||||||
mb.Version = types.CURRENTVERSION
|
mb.Version = common.VERSION
|
||||||
os.Remove(config.AppInfo.DBPath + "/baseinfo.conf")
|
os.Remove(config.AppInfo.DBPath + "/baseinfo.conf")
|
||||||
by, err := json.Marshal(mb)
|
by, err := json.Marshal(mb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -73,7 +73,11 @@ func InitNetworkMount() {
|
|||||||
}
|
}
|
||||||
baseHostPath := "/mnt/" + connection.Host
|
baseHostPath := "/mnt/" + connection.Host
|
||||||
|
|
||||||
mountPointList := service.MyService.System().GetDirPath(baseHostPath)
|
mountPointList, err := service.MyService.System().GetDirPath(baseHostPath)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("get mount point err", zap.Any("err", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
for _, v := range mountPointList {
|
for _, v := range mountPointList {
|
||||||
service.MyService.Connections().UnmountSmaba(v.Path)
|
service.MyService.Connections().UnmountSmaba(v.Path)
|
||||||
}
|
}
|
||||||
@@ -88,5 +92,6 @@ func InitNetworkMount() {
|
|||||||
}
|
}
|
||||||
connection.Directories = strings.Join(directories, ",")
|
connection.Directories = strings.Join(directories, ",")
|
||||||
service.MyService.Connections().UpdateConnection(&connection)
|
service.MyService.Connections().UpdateConnection(&connection)
|
||||||
|
service.MyService.Storage().CheckAndMountAll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,10 +66,11 @@ func SendAllHardwareStatusBySocket() {
|
|||||||
|
|
||||||
body["sys_net"] = newNet
|
body["sys_net"] = newNet
|
||||||
systemTempMap := service.MyService.Notify().GetSystemTempMap()
|
systemTempMap := service.MyService.Notify().GetSystemTempMap()
|
||||||
for k, v := range systemTempMap {
|
systemTempMap.Range(func(key, value interface{}) bool {
|
||||||
body[k] = v
|
body[key.(string)] = value
|
||||||
}
|
return true
|
||||||
service.MyService.Notify().SendNotify("sys_hardware_status", body)
|
})
|
||||||
|
service.MyService.Notify().SendNotify("casaos:system:utilization", body)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func MonitoryUSB() {
|
// func MonitoryUSB() {
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* @Author: LinkLeong link@icewhale.com
|
|
||||||
* @Date: 2022-05-23 17:18:56
|
|
||||||
* @LastEditors: LinkLeong
|
|
||||||
* @LastEditTime: 2022-06-09 21:48:10
|
|
||||||
* @FilePath: /CasaOS/route/socket.go
|
|
||||||
* @Description:
|
|
||||||
* @Website: https://www.casaos.io
|
|
||||||
* Copyright (c) 2022 by icewhale, All Rights Reserved.
|
|
||||||
*/
|
|
||||||
package route
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
|
||||||
"github.com/IceWhaleTech/CasaOS/service"
|
|
||||||
socketio "github.com/googollee/go-socket.io"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SocketIo() *socketio.Server {
|
|
||||||
server := socketio.NewServer(nil)
|
|
||||||
server.OnConnect("/", func(s socketio.Conn) error {
|
|
||||||
s.SetContext("")
|
|
||||||
logger.Info("connected", zap.Any("id", s.ID()))
|
|
||||||
s.Join("public")
|
|
||||||
service.ClientCount += 1
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
server.OnEvent("/", "notice", func(s socketio.Conn, msg string) {
|
|
||||||
logger.Info("notice", zap.Any("msg", msg))
|
|
||||||
s.Emit("reply", "have "+msg)
|
|
||||||
})
|
|
||||||
|
|
||||||
// server.OnEvent("/chat", "msg", func(s socketio.Conn, msg string) string {
|
|
||||||
// s.SetContext(msg)
|
|
||||||
// return "recv " + msg
|
|
||||||
// })
|
|
||||||
|
|
||||||
// server.OnEvent("/", "bye", func(s socketio.Conn) string {
|
|
||||||
// last := s.Context().(string)
|
|
||||||
// s.Emit("bye", last)
|
|
||||||
// s.Close()
|
|
||||||
// return last
|
|
||||||
// })
|
|
||||||
|
|
||||||
server.OnError("/", func(s socketio.Conn, e error) {
|
|
||||||
logger.Error("meet error", zap.Any("error", e))
|
|
||||||
})
|
|
||||||
|
|
||||||
server.OnDisconnect("/", func(s socketio.Conn, reason string) {
|
|
||||||
service.ClientCount -= 1
|
|
||||||
logger.Info("closed", zap.Any("reason", reason))
|
|
||||||
})
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := server.Serve(); err != nil {
|
|
||||||
logger.Error("error when trying to listen socketio ", zap.Any("error", err))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return server
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitRouter() *gin.Engine {
|
func InitV1Router() *gin.Engine {
|
||||||
ginMode := gin.ReleaseMode
|
ginMode := gin.ReleaseMode
|
||||||
if config.ServerInfo.RunMode != "" {
|
if config.ServerInfo.RunMode != "" {
|
||||||
ginMode = config.ServerInfo.RunMode
|
ginMode = config.ServerInfo.RunMode
|
||||||
@@ -30,29 +30,13 @@ func InitRouter() *gin.Engine {
|
|||||||
r.Use(middleware.WriteLog())
|
r.Use(middleware.WriteLog())
|
||||||
}
|
}
|
||||||
|
|
||||||
// r.StaticFS("/ui", http.FS(web.Static))
|
|
||||||
// r.GET("/", WebUIHome)
|
|
||||||
// r.StaticFS("/assets", http.Dir("./static/assets"))
|
|
||||||
// r.StaticFile("/favicon.ico", "./static/favicon.ico")
|
|
||||||
//r.GET("/", func(c *gin.Context) {
|
|
||||||
// c.Redirect(http.StatusMovedPermanently, "ui/")
|
|
||||||
//})
|
|
||||||
|
|
||||||
// r.POST("/v1/users/register", v1.PostUserRegister)
|
|
||||||
// r.POST("/v1/users/login", v1.PostUserLogin)
|
|
||||||
// r.GET("/v1/users/name", v1.GetUserAllUsername) //all/name
|
|
||||||
// r.POST("/v1/users/refresh", v1.PostUserRefreshToken)
|
|
||||||
// // No short-term modifications
|
|
||||||
// r.GET("/v1/users/image", v1.GetUserImage)
|
|
||||||
|
|
||||||
// r.GET("/v1/users/status", v1.GetUserStatus) //init/check
|
|
||||||
// r.GET("/v1/guide/check", v1.GetGuideCheck) // /v1/sys/guide_check
|
|
||||||
r.GET("/v1/sys/debug", v1.GetSystemConfigDebug) // //debug
|
r.GET("/v1/sys/debug", v1.GetSystemConfigDebug) // //debug
|
||||||
|
|
||||||
r.GET("/v1/sys/version/check", v1.GetSystemCheckVersion)
|
r.GET("/v1/sys/version/check", v1.GetSystemCheckVersion)
|
||||||
r.GET("/ping", func(ctx *gin.Context) {
|
r.GET("/ping", func(ctx *gin.Context) {
|
||||||
ctx.String(200, "pong")
|
ctx.String(200, "pong")
|
||||||
})
|
})
|
||||||
|
r.GET("/v1/recover/:type", v1.GetRecoverStorage)
|
||||||
v1Group := r.Group("/v1")
|
v1Group := r.Group("/v1")
|
||||||
|
|
||||||
v1Group.Use(jwt.ExceptLocalhost())
|
v1Group.Use(jwt.ExceptLocalhost())
|
||||||
@@ -108,10 +92,25 @@ func InitRouter() *gin.Engine {
|
|||||||
v1FileGroup.GET("/content", v1.GetFilerContent) // file/read
|
v1FileGroup.GET("/content", v1.GetFilerContent) // file/read
|
||||||
|
|
||||||
// File uploads need to be handled separately, and will not be modified here
|
// File uploads need to be handled separately, and will not be modified here
|
||||||
|
//v1FileGroup.POST("/upload", v1.PostFileUpload)
|
||||||
v1FileGroup.POST("/upload", v1.PostFileUpload)
|
v1FileGroup.POST("/upload", v1.PostFileUpload)
|
||||||
v1FileGroup.GET("/upload", v1.GetFileUpload)
|
v1FileGroup.GET("/upload", v1.GetFileUpload)
|
||||||
// v1FileGroup.GET("/download", v1.UserFileDownloadCommonService)
|
// v1FileGroup.GET("/download", v1.UserFileDownloadCommonService)
|
||||||
|
v1FileGroup.GET("/ws", v1.ConnectWebSocket)
|
||||||
|
v1FileGroup.GET("/peers", v1.GetPeers)
|
||||||
}
|
}
|
||||||
|
v1CloudGroup := v1Group.Group("/cloud")
|
||||||
|
v1CloudGroup.Use()
|
||||||
|
{
|
||||||
|
v1CloudGroup.GET("", v1.ListStorages)
|
||||||
|
v1CloudGroup.DELETE("", v1.UmountStorage)
|
||||||
|
}
|
||||||
|
v1DriverGroup := v1Group.Group("/driver")
|
||||||
|
v1DriverGroup.Use()
|
||||||
|
{
|
||||||
|
v1DriverGroup.GET("", v1.ListDriverInfo)
|
||||||
|
}
|
||||||
|
|
||||||
v1FolderGroup := v1Group.Group("/folder")
|
v1FolderGroup := v1Group.Group("/folder")
|
||||||
v1FolderGroup.Use()
|
v1FolderGroup.Use()
|
||||||
{
|
{
|
||||||
@@ -119,6 +118,7 @@ func InitRouter() *gin.Engine {
|
|||||||
v1FolderGroup.GET("", v1.DirPath) ///file/dirpath
|
v1FolderGroup.GET("", v1.DirPath) ///file/dirpath
|
||||||
v1FolderGroup.POST("", v1.MkdirAll) ///file/mkdir
|
v1FolderGroup.POST("", v1.MkdirAll) ///file/mkdir
|
||||||
v1FolderGroup.GET("/size", v1.GetSize)
|
v1FolderGroup.GET("/size", v1.GetSize)
|
||||||
|
v1FolderGroup.GET("/count", v1.GetFileCount)
|
||||||
}
|
}
|
||||||
v1BatchGroup := v1Group.Group("/batch")
|
v1BatchGroup := v1Group.Group("/batch")
|
||||||
v1BatchGroup.Use()
|
v1BatchGroup.Use()
|
||||||
@@ -159,9 +159,15 @@ func InitRouter() *gin.Engine {
|
|||||||
v1NotifyGroup.POST("/:path", v1.PostNotifyMessage)
|
v1NotifyGroup.POST("/:path", v1.PostNotifyMessage)
|
||||||
// merge to system
|
// merge to system
|
||||||
v1NotifyGroup.POST("/system_status", v1.PostSystemStatusNotify)
|
v1NotifyGroup.POST("/system_status", v1.PostSystemStatusNotify)
|
||||||
v1NotifyGroup.POST("/install_app", v1.PostInstallAppNotify)
|
}
|
||||||
v1NotifyGroup.POST("/uninstall_app", v1.PostUninstallAppNotify)
|
|
||||||
|
v1OtherGroup := v1Group.Group("/other")
|
||||||
|
v1OtherGroup.Use()
|
||||||
|
{
|
||||||
|
v1OtherGroup.GET("/search", v1.GetSearchResult)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
101
route/v1/cloud.go
Normal file
101
route/v1/cloud.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/drivers/dropbox"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/drivers/google_drive"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/httper"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListStorages(c *gin.Context) {
|
||||||
|
// var req model.PageReq
|
||||||
|
// if err := c.ShouldBind(&req); err != nil {
|
||||||
|
// c.JSON(common_err.SUCCESS, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: err.Error()})
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// req.Validate()
|
||||||
|
|
||||||
|
//logger.Info("ListStorages", zap.Any("req", req))
|
||||||
|
//storages, total, err := service.MyService.Storage().GetStorages(req.Page, req.PerPage)
|
||||||
|
// if err != nil {
|
||||||
|
// c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: model.PageResp{
|
||||||
|
// Content: storages,
|
||||||
|
// Total: total,
|
||||||
|
// }})
|
||||||
|
r, err := service.MyService.Storage().GetStorages()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(r.MountPoints); i++ {
|
||||||
|
dataMap, err := service.MyService.Storage().GetConfigByName(r.MountPoints[i].Fs)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("GetConfigByName", zap.Any("err", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dataMap["type"] == "drive" {
|
||||||
|
r.MountPoints[i].Icon = google_drive.ICONURL
|
||||||
|
}
|
||||||
|
if dataMap["type"] == "dropbox" {
|
||||||
|
r.MountPoints[i].Icon = dropbox.ICONURL
|
||||||
|
}
|
||||||
|
r.MountPoints[i].Name = dataMap["username"]
|
||||||
|
}
|
||||||
|
list := []httper.MountPoint{}
|
||||||
|
|
||||||
|
for _, v := range r.MountPoints {
|
||||||
|
list = append(list, httper.MountPoint{
|
||||||
|
Fs: v.Fs,
|
||||||
|
Icon: v.Icon,
|
||||||
|
MountPoint: v.MountPoint,
|
||||||
|
Name: v.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: list})
|
||||||
|
}
|
||||||
|
|
||||||
|
func UmountStorage(c *gin.Context) {
|
||||||
|
json := make(map[string]string)
|
||||||
|
c.ShouldBind(&json)
|
||||||
|
mountPoint := json["mount_point"]
|
||||||
|
if mountPoint == "" {
|
||||||
|
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: "mount_point is empty"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := service.MyService.Storage().UnmountStorage(mountPoint)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
service.MyService.Storage().DeleteConfigByName(strings.ReplaceAll(mountPoint, "/mnt/", ""))
|
||||||
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: "success"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStorage(c *gin.Context) {
|
||||||
|
|
||||||
|
// idStr := c.Query("id")
|
||||||
|
// id, err := strconv.Atoi(idStr)
|
||||||
|
// if err != nil {
|
||||||
|
// c.JSON(common_err.SUCCESS, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: err.Error()})
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// storage, err := service.MyService.Storage().GetStorageById(uint(id))
|
||||||
|
// if err != nil {
|
||||||
|
// c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: storage})
|
||||||
|
}
|
||||||
12
route/v1/driver.go
Normal file
12
route/v1/driver.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/model"
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/common_err"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/internal/op"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListDriverInfo(c *gin.Context) {
|
||||||
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: op.GetDriverInfoMap()})
|
||||||
|
}
|
||||||
524
route/v1/file.go
524
route/v1/file.go
@@ -1,6 +1,7 @@
|
|||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -14,15 +15,63 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
"github.com/IceWhaleTech/CasaOS/model"
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
|
||||||
"github.com/IceWhaleTech/CasaOS/service"
|
"github.com/IceWhaleTech/CasaOS/service"
|
||||||
|
model2 "github.com/IceWhaleTech/CasaOS/service/model"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
uuid "github.com/satori/go.uuid"
|
uuid "github.com/satori/go.uuid"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/h2non/filetype"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListReq struct {
|
||||||
|
model.PageReq
|
||||||
|
Path string `json:"path" form:"path"`
|
||||||
|
//Refresh bool `json:"refresh"`
|
||||||
|
}
|
||||||
|
type ObjResp struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
IsDir bool `json:"is_dir"`
|
||||||
|
Modified time.Time `json:"modified"`
|
||||||
|
Sign string `json:"sign"`
|
||||||
|
Thumb string `json:"thumb"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Date time.Time `json:"date"`
|
||||||
|
Extensions map[string]interface{} `json:"extensions"`
|
||||||
|
}
|
||||||
|
type FsListResp struct {
|
||||||
|
Content []ObjResp `json:"content"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Readme string `json:"readme,omitempty"`
|
||||||
|
Write bool `json:"write,omitempty"`
|
||||||
|
Provider string `json:"provider,omitempty"`
|
||||||
|
Index int `json:"index"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// 升级成 WebSocket 协议
|
||||||
|
upgraderFile = websocket.Upgrader{
|
||||||
|
// 允许CORS跨域请求
|
||||||
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
conn *websocket.Conn
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
// @Summary 读取文件
|
// @Summary 读取文件
|
||||||
@@ -190,6 +239,36 @@ func GetDownloadSingleFile(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
fileName := path.Base(filePath)
|
||||||
|
// c.Header("Content-Disposition", "inline")
|
||||||
|
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
|
||||||
|
|
||||||
|
fi, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only have to pass the file header = first 261 bytes
|
||||||
|
buffer := make([]byte, 261)
|
||||||
|
|
||||||
|
_, _ = fi.Read(buffer)
|
||||||
|
|
||||||
|
kind, _ := filetype.Match(buffer)
|
||||||
|
if kind != filetype.Unknown {
|
||||||
|
c.Header("Content-Type", kind.MIME.Value)
|
||||||
|
}
|
||||||
|
node, err := os.Stat(filePath)
|
||||||
|
// Set the Last-Modified header to the timestamp
|
||||||
|
c.Header("Last-Modified", node.ModTime().UTC().Format(http.TimeFormat))
|
||||||
|
|
||||||
|
knownSize := node.Size() >= 0
|
||||||
|
if knownSize {
|
||||||
|
c.Header("Content-Length", strconv.FormatInt(node.Size(), 10))
|
||||||
|
}
|
||||||
|
http.ServeContent(c.Writer, c.Request, fileName, node.ModTime(), fi)
|
||||||
|
//http.ServeFile(c.Writer, c.Request, filePath)
|
||||||
|
defer fi.Close()
|
||||||
|
return
|
||||||
fileTmp, err := os.Open(filePath)
|
fileTmp, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(common_err.SERVICE_ERROR, model.Result{
|
c.JSON(common_err.SERVICE_ERROR, model.Result{
|
||||||
@@ -200,9 +279,6 @@ func GetDownloadSingleFile(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
defer fileTmp.Close()
|
defer fileTmp.Close()
|
||||||
|
|
||||||
fileName := path.Base(filePath)
|
|
||||||
// c.Header("Content-Disposition", "inline")
|
|
||||||
c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
|
|
||||||
c.File(filePath)
|
c.File(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,22 +291,49 @@ func GetDownloadSingleFile(c *gin.Context) {
|
|||||||
// @Success 200 {string} string "ok"
|
// @Success 200 {string} string "ok"
|
||||||
// @Router /file/dirpath [get]
|
// @Router /file/dirpath [get]
|
||||||
func DirPath(c *gin.Context) {
|
func DirPath(c *gin.Context) {
|
||||||
path := c.DefaultQuery("path", "")
|
var req ListReq
|
||||||
info := service.MyService.System().GetDirPath(path)
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Validate()
|
||||||
|
info, err := service.MyService.System().GetDirPath(req.Path)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
shares := service.MyService.Shares().GetSharesList()
|
shares := service.MyService.Shares().GetSharesList()
|
||||||
sharesMap := make(map[string]string)
|
sharesMap := make(map[string]string)
|
||||||
for _, v := range shares {
|
for _, v := range shares {
|
||||||
sharesMap[v.Path] = fmt.Sprint(v.ID)
|
sharesMap[v.Path] = fmt.Sprint(v.ID)
|
||||||
}
|
}
|
||||||
|
// if len(info) <= (req.Page-1)*req.Size {
|
||||||
for i := 0; i < len(info); i++ {
|
// c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: "page out of range"})
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
forEnd := req.Index * req.Size
|
||||||
|
if forEnd > len(info) {
|
||||||
|
forEnd = len(info)
|
||||||
|
}
|
||||||
|
for i := (req.Index - 1) * req.Size; i < forEnd; i++ {
|
||||||
if v, ok := sharesMap[info[i].Path]; ok {
|
if v, ok := sharesMap[info[i].Path]; ok {
|
||||||
ex := make(map[string]interface{})
|
ex := make(map[string]interface{})
|
||||||
|
|
||||||
shareEx := make(map[string]string)
|
shareEx := make(map[string]string)
|
||||||
shareEx["shared"] = "true"
|
shareEx["shared"] = "true"
|
||||||
shareEx["id"] = v
|
shareEx["id"] = v
|
||||||
ex["share"] = shareEx
|
ex["share"] = shareEx
|
||||||
|
ex["mounted"] = false
|
||||||
|
info[i].Extensions = ex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(req.Path, "/mnt") || strings.HasPrefix(req.Path, "/media") {
|
||||||
|
for i := (req.Index - 1) * req.Size; i < forEnd; i++ {
|
||||||
|
ex := info[i].Extensions
|
||||||
|
if ex == nil {
|
||||||
|
ex = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
mounted := service.IsMounted(info[i].Path)
|
||||||
|
ex["mounted"] = mounted
|
||||||
info[i].Extensions = ex
|
info[i].Extensions = ex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,17 +353,34 @@ func DirPath(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pathList := []model.Path{}
|
pathList := []ObjResp{}
|
||||||
for i := 0; i < len(info); i++ {
|
for i := (req.Index - 1) * req.Size; i < forEnd; i++ {
|
||||||
if info[i].Name == ".temp" && info[i].IsDir {
|
if info[i].Name == ".temp" && info[i].IsDir {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := fileQueue[info[i].Path]; !ok {
|
if _, ok := fileQueue[info[i].Path]; !ok {
|
||||||
pathList = append(pathList, info[i])
|
t := ObjResp{}
|
||||||
|
t.IsDir = info[i].IsDir
|
||||||
|
t.Name = info[i].Name
|
||||||
|
t.Modified = info[i].Date
|
||||||
|
t.Date = info[i].Date
|
||||||
|
t.Size = info[i].Size
|
||||||
|
t.Path = info[i].Path
|
||||||
|
t.Extensions = info[i].Extensions
|
||||||
|
pathList = append(pathList, t)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
flist := FsListResp{
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: pathList})
|
Content: pathList,
|
||||||
|
Total: int64(len(info)),
|
||||||
|
// Readme: "",
|
||||||
|
// Write: true,
|
||||||
|
// Provider: "local",
|
||||||
|
Index: req.Index,
|
||||||
|
Size: req.Size,
|
||||||
|
}
|
||||||
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: flist})
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary rename file or dir
|
// @Summary rename file or dir
|
||||||
@@ -281,6 +401,12 @@ func RenamePath(c *gin.Context) {
|
|||||||
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
|
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
mounted := service.IsMounted(op)
|
||||||
|
if mounted {
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.MOUNTED_DIRECTIORIES, Message: common_err.GetMsg(common_err.MOUNTED_DIRECTIORIES), Data: common_err.GetMsg(common_err.MOUNTED_DIRECTIORIES)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
success, err := service.MyService.System().RenameFile(op, np)
|
success, err := service.MyService.System().RenameFile(op, np)
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: success, Message: common_err.GetMsg(success), Data: err})
|
c.JSON(common_err.SUCCESS, model.Result{Success: success, Message: common_err.GetMsg(success), Data: err})
|
||||||
}
|
}
|
||||||
@@ -477,6 +603,71 @@ func PostFileUpload(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PostFileOctet(c *gin.Context) {
|
||||||
|
|
||||||
|
content_length := c.Request.ContentLength
|
||||||
|
if content_length <= 0 || content_length > 1024*1024*1024*2*1024 {
|
||||||
|
log.Printf("content_length error\n")
|
||||||
|
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: "content_length error"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
content_type_, has_key := c.Request.Header["Content-Type"]
|
||||||
|
if !has_key {
|
||||||
|
log.Printf("Content-Type error\n")
|
||||||
|
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: "Content-Type error"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(content_type_) != 1 {
|
||||||
|
log.Printf("Content-Type count error\n")
|
||||||
|
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: "Content-Type count error"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
content_type := content_type_[0]
|
||||||
|
const BOUNDARY string = "; boundary="
|
||||||
|
loc := strings.Index(content_type, BOUNDARY)
|
||||||
|
if loc == -1 {
|
||||||
|
log.Printf("Content-Type error, no boundary\n")
|
||||||
|
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.CLIENT_ERROR, Message: common_err.GetMsg(common_err.CLIENT_ERROR), Data: "Content-Type error, no boundary"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
boundary := []byte(content_type[(loc + len(BOUNDARY)):])
|
||||||
|
log.Printf("[%s]\n\n", boundary)
|
||||||
|
read_data := make([]byte, 1024*24)
|
||||||
|
var read_total int = 0
|
||||||
|
for {
|
||||||
|
file_header, file_data, err := file.ParseFromHead(read_data, read_total, append(boundary, []byte("\r\n")...), c.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("file :%s\n", file_header)
|
||||||
|
//
|
||||||
|
//os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o644)
|
||||||
|
f, err := os.OpenFile(file_header["path"]+"/"+file_header["filename"], os.O_WRONLY|os.O_CREATE, 0o644)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("create file fail:%v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.Write(file_data)
|
||||||
|
file_data = nil
|
||||||
|
|
||||||
|
temp_data, reach_end, err := file.ReadToBoundary(boundary, c.Request.Body, f)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if reach_end {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
copy(read_data[0:], temp_data)
|
||||||
|
read_total = len(temp_data)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary copy or move file
|
// @Summary copy or move file
|
||||||
// @Produce application/json
|
// @Produce application/json
|
||||||
// @Accept application/json
|
// @Accept application/json
|
||||||
@@ -507,6 +698,13 @@ func PostOperateFileOrDir(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
list.Item[i].Size = size
|
list.Item[i].Size = size
|
||||||
total += size
|
total += size
|
||||||
|
if list.Type == "move" {
|
||||||
|
mounted := service.IsMounted(list.Item[i].From)
|
||||||
|
if mounted {
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.MOUNTED_DIRECTIORIES, Message: common_err.GetMsg(common_err.MOUNTED_DIRECTIORIES), Data: common_err.GetMsg(common_err.MOUNTED_DIRECTIORIES)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list.TotalSize = total
|
list.TotalSize = total
|
||||||
@@ -544,6 +742,13 @@ func DeleteFile(c *gin.Context) {
|
|||||||
// path := c.Query("path")
|
// path := c.Query("path")
|
||||||
|
|
||||||
// paths := strings.Split(path, ",")
|
// paths := strings.Split(path, ",")
|
||||||
|
for _, v := range paths {
|
||||||
|
mounted := service.IsMounted(v)
|
||||||
|
if mounted {
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.MOUNTED_DIRECTIORIES, Message: common_err.GetMsg(common_err.MOUNTED_DIRECTIORIES), Data: common_err.GetMsg(common_err.MOUNTED_DIRECTIORIES)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, v := range paths {
|
for _, v := range paths {
|
||||||
err := os.RemoveAll(v)
|
err := os.RemoveAll(v)
|
||||||
@@ -576,12 +781,18 @@ func PutFileContent(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// err := os.Remove(path)
|
// err := os.Remove(path)
|
||||||
err := os.RemoveAll(fi.FilePath)
|
f, err := os.Stat(fi.FilePath)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_ALREADY_EXISTS, Message: common_err.GetMsg(common_err.FILE_ALREADY_EXISTS)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fm := f.Mode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_DELETE_ERROR, Message: common_err.GetMsg(common_err.FILE_DELETE_ERROR), Data: err})
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_DELETE_ERROR, Message: common_err.GetMsg(common_err.FILE_DELETE_ERROR), Data: err})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = file.CreateFileAndWriteContent(fi.FilePath, fi.FileContent)
|
os.OpenFile(fi.FilePath, os.O_CREATE, fm)
|
||||||
|
err = file.WriteToFullPath([]byte(fi.FileContent), fi.FilePath, fm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
return
|
return
|
||||||
@@ -660,3 +871,286 @@ func GetSize(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: size})
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: size})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFileCount(c *gin.Context) {
|
||||||
|
json := make(map[string]string)
|
||||||
|
c.ShouldBind(&json)
|
||||||
|
path := json["path"]
|
||||||
|
list, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: len(list)})
|
||||||
|
}
|
||||||
|
|
||||||
|
type CenterHandler struct {
|
||||||
|
// 广播通道,有数据则循环每个用户广播出去
|
||||||
|
broadcast chan []byte
|
||||||
|
// 注册通道,有用户进来 则推到用户集合map中
|
||||||
|
register chan *Client
|
||||||
|
// 注销通道,有用户关闭连接 则将该用户剔出集合map中
|
||||||
|
unregister chan *Client
|
||||||
|
// 用户集合,每个用户本身也在跑两个协程,监听用户的读、写的状态
|
||||||
|
clients map[string]*Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
handler *CenterHandler
|
||||||
|
conn *websocket.Conn
|
||||||
|
// 每个用户自己的循环跑起来的状态监控
|
||||||
|
send chan []byte
|
||||||
|
ID string `json:"id"`
|
||||||
|
IP string `json:"ip"`
|
||||||
|
Name service.Name `json:"name"`
|
||||||
|
RtcSupported bool `json:"rtcSupported"`
|
||||||
|
TimerId int `json:"timerId"`
|
||||||
|
LastBeat time.Time `json:"lastBeat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PeerModel struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name service.Name `json:"name"`
|
||||||
|
RtcSupported bool `json:"rtcSupported"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConnectWebSocket(c *gin.Context) {
|
||||||
|
peerId := c.Query("peer")
|
||||||
|
writer := c.Writer
|
||||||
|
request := c.Request
|
||||||
|
key := uuid.NewV4().String()
|
||||||
|
//peerModel := service.MyService.Peer().GetPeerByUserAgent(c.Request.UserAgent())
|
||||||
|
peerModel := model2.PeerDriveDBModel{}
|
||||||
|
name := service.GetName(request)
|
||||||
|
if conn, err = upgraderFile.Upgrade(writer, request, writer.Header()); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := &Client{handler: &handler, conn: conn, send: make(chan []byte, 256), ID: service.GetPeerId(request, key), IP: service.GetIP(request), Name: name, RtcSupported: true, TimerId: 0, LastBeat: time.Now()}
|
||||||
|
if peerId != "" || len(peerModel.ID) > 0 {
|
||||||
|
if len(peerModel.ID) == 0 {
|
||||||
|
peerModel = service.MyService.Peer().GetPeerByID(peerId)
|
||||||
|
}
|
||||||
|
if len(peerModel.ID) > 0 {
|
||||||
|
key = peerId
|
||||||
|
client.ID = peerModel.ID
|
||||||
|
client.Name = service.GetNameByDB(peerModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var list = service.MyService.Peer().GetPeers()
|
||||||
|
if len(peerModel.ID) == 0 {
|
||||||
|
peerModel.ID = key
|
||||||
|
peerModel.DisplayName = name.DisplayName
|
||||||
|
peerModel.DeviceName = name.DeviceName
|
||||||
|
peerModel.Model = name.Model
|
||||||
|
peerModel.OS = name.OS
|
||||||
|
peerModel.Browser = name.Browser
|
||||||
|
peerModel.UserAgent = c.Request.UserAgent()
|
||||||
|
peerModel.IP = client.IP
|
||||||
|
service.MyService.Peer().CreatePeer(&peerModel)
|
||||||
|
list = append(list, peerModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie := http.Cookie{
|
||||||
|
Name: "peerid",
|
||||||
|
Value: key,
|
||||||
|
Path: "/",
|
||||||
|
}
|
||||||
|
http.SetCookie(writer, &cookie)
|
||||||
|
if len(list) > 10 {
|
||||||
|
kickoutList := []Client{}
|
||||||
|
count := len(list) - 10
|
||||||
|
for i := len(list) - 1; count > 0 && i > -1; i-- {
|
||||||
|
if _, ok := handler.clients[list[i].ID]; !ok {
|
||||||
|
count--
|
||||||
|
kickoutList = append(kickoutList, Client{ID: list[i].ID, Name: service.GetNameByDB(list[i]), IP: list[i].IP})
|
||||||
|
service.MyService.Peer().DeletePeer(list[i].ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if len(kickoutList) > 0 {
|
||||||
|
// other := make(map[string]interface{})
|
||||||
|
// other["type"] = "kickout"
|
||||||
|
// other["peers"] = kickoutList
|
||||||
|
// otherBy, err := json.Marshal(other)
|
||||||
|
// fmt.Println(err)
|
||||||
|
// client.handler.broadcast <- otherBy
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
list = service.MyService.Peer().GetPeers()
|
||||||
|
if len(list) > 10 {
|
||||||
|
fmt.Println("解决完后依然有溢出", list)
|
||||||
|
}
|
||||||
|
currentPeer := PeerModel{ID: client.ID, Name: client.Name, RtcSupported: client.RtcSupported}
|
||||||
|
pmsg := make(map[string]interface{})
|
||||||
|
pmsg["type"] = "peer-joined"
|
||||||
|
pmsg["peer"] = currentPeer
|
||||||
|
pby, err := json.Marshal(pmsg)
|
||||||
|
fmt.Println(err)
|
||||||
|
for _, v := range handler.clients {
|
||||||
|
v.send <- pby
|
||||||
|
}
|
||||||
|
//client.handler.broadcast <- pby
|
||||||
|
clients := []PeerModel{}
|
||||||
|
for _, v := range client.handler.clients {
|
||||||
|
if _, ok := handler.clients[v.ID]; ok {
|
||||||
|
clients = append(clients, PeerModel{ID: v.ID, Name: v.Name, RtcSupported: v.RtcSupported})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
other := make(map[string]interface{})
|
||||||
|
other["type"] = "peers"
|
||||||
|
other["peers"] = clients
|
||||||
|
otherBy, err := json.Marshal(other)
|
||||||
|
fmt.Println(err)
|
||||||
|
client.send <- otherBy
|
||||||
|
|
||||||
|
// 推给监控中心注册到用户集合中
|
||||||
|
handler.register <- client
|
||||||
|
|
||||||
|
client.send <- []byte(`{"type":"ping"}`)
|
||||||
|
|
||||||
|
data := make(map[string]string)
|
||||||
|
data["displayName"] = client.Name.DisplayName
|
||||||
|
data["deviceName"] = client.Name.DeviceName
|
||||||
|
data["id"] = client.ID
|
||||||
|
msg := make(map[string]interface{})
|
||||||
|
msg["type"] = "display-name"
|
||||||
|
msg["message"] = data
|
||||||
|
by, _ := json.Marshal(msg)
|
||||||
|
client.send <- by
|
||||||
|
|
||||||
|
// 每个 client 都挂起 2 个新的协程,监控读、写状态
|
||||||
|
go client.writePump()
|
||||||
|
go client.readPump()
|
||||||
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = CenterHandler{broadcast: make(chan []byte),
|
||||||
|
register: make(chan *Client),
|
||||||
|
unregister: make(chan *Client),
|
||||||
|
clients: make(map[string]*Client)}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// 起个协程跑起来,监听注册、注销、消息 3 个 channel
|
||||||
|
go handler.monitoring()
|
||||||
|
|
||||||
|
crontab := cron.New(cron.WithSeconds()) //精确到秒
|
||||||
|
//定义定时器调用的任务函数
|
||||||
|
|
||||||
|
task := func() {
|
||||||
|
handler.broadcast <- []byte(`{"type":"ping"}`)
|
||||||
|
}
|
||||||
|
//定时任务
|
||||||
|
spec := "*/30 * * * * ?" //cron表达式,每五秒一次
|
||||||
|
// 添加定时任务,
|
||||||
|
crontab.AddFunc(spec, task)
|
||||||
|
// 启动定时器
|
||||||
|
crontab.Start()
|
||||||
|
}
|
||||||
|
func (c *Client) writePump() {
|
||||||
|
defer func() {
|
||||||
|
c.handler.unregister <- c
|
||||||
|
|
||||||
|
c.conn.Close()
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
// 广播推过来的新消息,马上通过websocket推给自己
|
||||||
|
message, _ := <-c.send
|
||||||
|
fmt.Println("推送消息", string(message), "1")
|
||||||
|
if err := c.conn.WriteMessage(websocket.TextMessage, message); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读,监听客户端是否有推送内容过来服务端
|
||||||
|
func (c *Client) readPump() {
|
||||||
|
defer func() {
|
||||||
|
c.handler.unregister <- c
|
||||||
|
c.conn.Close()
|
||||||
|
}()
|
||||||
|
for {
|
||||||
|
// 循环监听是否该用户是否要发言
|
||||||
|
_, message, err := c.conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
// 异常关闭的处理
|
||||||
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
||||||
|
log.Printf("error: %v", err)
|
||||||
|
}
|
||||||
|
c.handler.broadcast <- []byte(`{"type":"peer-left","peerId":"` + c.ID + `"}`)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// 要的话,推给广播中心,广播中心再推给每个用户
|
||||||
|
|
||||||
|
t := gjson.GetBytes(message, "type")
|
||||||
|
if t.String() == "disconnect" {
|
||||||
|
c.handler.unregister <- c
|
||||||
|
c.conn.Close()
|
||||||
|
// clients := []Client{}
|
||||||
|
// list := service.MyService.Peer().GetPeers()
|
||||||
|
// for _, v := range list {
|
||||||
|
// if _, ok := handler.clients[v.ID]; ok {
|
||||||
|
// clients = append(clients, *handler.clients[v.ID])
|
||||||
|
// } else {
|
||||||
|
// clients = append(clients, Client{ID: v.ID, Name: service.GetNameByDB(v), IP: v.IP, Offline: true})
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// other := make(map[string]interface{})
|
||||||
|
// other["type"] = "peers"
|
||||||
|
// other["peers"] = clients
|
||||||
|
// otherBy, err := json.Marshal(other)
|
||||||
|
// fmt.Println(err)
|
||||||
|
c.handler.broadcast <- []byte(`{"type":"peer-left","peerId":"` + c.ID + `"}`)
|
||||||
|
//c.handler.broadcast <- otherBy
|
||||||
|
break
|
||||||
|
} else if t.String() == "pong" {
|
||||||
|
c.LastBeat = time.Now()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
to := gjson.GetBytes(message, "to")
|
||||||
|
|
||||||
|
if len(to.String()) > 0 {
|
||||||
|
toC := c.handler.clients[to.String()]
|
||||||
|
if toC == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data := map[string]interface{}{}
|
||||||
|
json.Unmarshal(message, &data)
|
||||||
|
data["sender"] = c.ID
|
||||||
|
delete(data, "to")
|
||||||
|
message, err = json.Marshal(data)
|
||||||
|
toC.send <- message
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c.handler.broadcast <- message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (ch *CenterHandler) monitoring() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
// 注册,新用户连接过来会推进注册通道,这里接收推进来的用户指针
|
||||||
|
case client := <-ch.register:
|
||||||
|
ch.clients[client.ID] = client
|
||||||
|
// 注销,关闭连接或连接异常会将用户推出群聊
|
||||||
|
case client := <-ch.unregister:
|
||||||
|
delete(ch.clients, client.ID)
|
||||||
|
// 消息,监听到有新消息到来
|
||||||
|
case message := <-ch.broadcast:
|
||||||
|
println("消息来了,message:" + string(message))
|
||||||
|
// 推送给每个用户的通道,每个用户都有跑协程起了writePump的监听
|
||||||
|
for _, client := range ch.clients {
|
||||||
|
client.send <- message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func GetPeers(c *gin.Context) {
|
||||||
|
peers := service.MyService.Peer().GetPeers()
|
||||||
|
for i := 0; i < len(peers); i++ {
|
||||||
|
if _, ok := handler.clients[peers[i].ID]; ok {
|
||||||
|
peers[i].Online = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: peers})
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package v1
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/model/notify"
|
|
||||||
"github.com/IceWhaleTech/CasaOS/model"
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
||||||
"github.com/IceWhaleTech/CasaOS/service"
|
"github.com/IceWhaleTech/CasaOS/service"
|
||||||
@@ -11,14 +10,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func PostNotifyMessage(c *gin.Context) {
|
func PostNotifyMessage(c *gin.Context) {
|
||||||
path := c.Param("path")
|
name := c.Param("name")
|
||||||
message := make(map[string]interface{})
|
message := make(map[string]interface{})
|
||||||
if err := c.ShouldBind(&message); err != nil {
|
if err := c.ShouldBind(&message); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: err.Error()})
|
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
service.MyService.Notify().SendNotify(path, message)
|
service.MyService.Notify().SendNotify(name, message)
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,25 +31,3 @@ func PostSystemStatusNotify(c *gin.Context) {
|
|||||||
service.MyService.Notify().SettingSystemTempData(message)
|
service.MyService.Notify().SettingSystemTempData(message)
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostInstallAppNotify(c *gin.Context) {
|
|
||||||
app := notify.Application{}
|
|
||||||
if err := c.ShouldBind(&app); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
service.MyService.Notify().SendInstallAppBySocket(app)
|
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
|
||||||
}
|
|
||||||
|
|
||||||
func PostUninstallAppNotify(c *gin.Context) {
|
|
||||||
app := notify.Application{}
|
|
||||||
if err := c.ShouldBind(&app); err != nil {
|
|
||||||
c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
service.MyService.Notify().SendUninstallAppBySocket(app)
|
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
|
|
||||||
}
|
|
||||||
|
|||||||
30
route/v1/other.go
Normal file
30
route/v1/other.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSearchResult(c *gin.Context) {
|
||||||
|
json := make(map[string]string)
|
||||||
|
c.ShouldBind(&json)
|
||||||
|
url := json["url"]
|
||||||
|
|
||||||
|
if url == "" {
|
||||||
|
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS), Data: "key is empty"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//data, err := service.MyService.Other().Search(key)
|
||||||
|
data, err := service.MyService.Other().AgentSearch(url)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
|
||||||
|
}
|
||||||
205
route/v1/recover.go
Normal file
205
route/v1/recover.go
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/drivers/dropbox"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/drivers/google_drive"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetRecoverStorage(c *gin.Context) {
|
||||||
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
|
t := c.Param("type")
|
||||||
|
currentTime := time.Now().UTC()
|
||||||
|
currentDate := time.Now().UTC().Format("2006-01-02")
|
||||||
|
notify := make(map[string]interface{})
|
||||||
|
if t == "GoogleDrive" {
|
||||||
|
add := google_drive.Addition{}
|
||||||
|
add.Code = c.Query("code")
|
||||||
|
if len(add.Code) == 0 {
|
||||||
|
c.String(200, `<p>Code cannot be empty</p><script>window.close()</script>`)
|
||||||
|
notify["status"] = "fail"
|
||||||
|
notify["message"] = "Code cannot be empty"
|
||||||
|
logger.Error("Then code is empty: ", zap.String("code", add.Code), zap.Any("name", "google_drive"))
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
add.RootFolderID = "root"
|
||||||
|
add.ClientID = google_drive.CLIENTID
|
||||||
|
add.ClientSecret = google_drive.CLIENTSECRET
|
||||||
|
|
||||||
|
var google_drive google_drive.GoogleDrive
|
||||||
|
google_drive.Addition = add
|
||||||
|
err := google_drive.Init(c)
|
||||||
|
if err != nil {
|
||||||
|
c.String(200, `<p>Initialization failure:`+err.Error()+`</p><script>window.close()</script>`)
|
||||||
|
notify["status"] = "fail"
|
||||||
|
notify["message"] = "Initialization failure"
|
||||||
|
logger.Error("Then init error: ", zap.Error(err), zap.Any("name", "google_drive"))
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
username, err := google_drive.GetUserInfo(c)
|
||||||
|
if err != nil {
|
||||||
|
c.String(200, `<p>Failed to get user information:`+err.Error()+`</p><script>window.close()</script>`)
|
||||||
|
notify["status"] = "fail"
|
||||||
|
notify["message"] = "Failed to get user information"
|
||||||
|
logger.Error("Then get user info error: ", zap.Error(err), zap.Any("name", "google_drive"))
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dmap := make(map[string]string)
|
||||||
|
dmap["username"] = username
|
||||||
|
configs, err := service.MyService.Storage().GetConfig()
|
||||||
|
if err != nil {
|
||||||
|
c.String(200, `<p>Failed to get rclone config:`+err.Error()+`</p><script>window.close()</script>`)
|
||||||
|
notify["status"] = "fail"
|
||||||
|
notify["message"] = "Failed to get rclone config"
|
||||||
|
logger.Error("Then get config error: ", zap.Error(err), zap.Any("name", "google_drive"))
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, v := range configs.Remotes {
|
||||||
|
cf, err := service.MyService.Storage().GetConfigByName(v)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("then get config by name error: ", zap.Error(err), zap.Any("name", v))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cf["type"] == "drive" && cf["username"] == dmap["username"] {
|
||||||
|
c.String(200, `<p>The same configuration has been added</p><script>window.close()</script>`)
|
||||||
|
err := service.MyService.Storage().CheckAndMountByName(v)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("check and mount by name error: ", zap.Error(err), zap.Any("name", cf["username"]))
|
||||||
|
}
|
||||||
|
notify["status"] = "warn"
|
||||||
|
notify["message"] = "The same configuration has been added"
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(username) > 0 {
|
||||||
|
a := strings.Split(username, "@")
|
||||||
|
username = a[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
//username = fileutil.NameAccumulation(username, "/mnt")
|
||||||
|
username += "_google_drive_" + strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
|
|
||||||
|
dmap["client_id"] = add.ClientID
|
||||||
|
dmap["client_secret"] = add.ClientSecret
|
||||||
|
dmap["scope"] = "drive"
|
||||||
|
dmap["mount_point"] = "/mnt/" + username
|
||||||
|
dmap["token"] = `{"access_token":"` + google_drive.AccessToken + `","token_type":"Bearer","refresh_token":"` + google_drive.RefreshToken + `","expiry":"` + currentDate + `T` + currentTime.Add(time.Hour*1).Add(time.Minute*50).Format("15:04:05") + `Z"}`
|
||||||
|
service.MyService.Storage().CreateConfig(dmap, username, "drive")
|
||||||
|
service.MyService.Storage().MountStorage("/mnt/"+username, username+":")
|
||||||
|
notify := make(map[string]interface{})
|
||||||
|
notify["status"] = "success"
|
||||||
|
notify["message"] = "Success"
|
||||||
|
notify["driver"] = "GoogleDrive"
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
} else if t == "Dropbox" {
|
||||||
|
add := dropbox.Addition{}
|
||||||
|
add.Code = c.Query("code")
|
||||||
|
if len(add.Code) == 0 {
|
||||||
|
c.String(200, `<p>Code cannot be empty</p><script>window.close()</script>`)
|
||||||
|
notify["status"] = "fail"
|
||||||
|
notify["message"] = "Code cannot be empty"
|
||||||
|
logger.Error("Then code is empty error: ", zap.String("code", add.Code), zap.Any("name", "dropbox"))
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
add.RootFolderID = ""
|
||||||
|
add.AppKey = dropbox.APPKEY
|
||||||
|
add.AppSecret = dropbox.APPSECRET
|
||||||
|
var dropbox dropbox.Dropbox
|
||||||
|
dropbox.Addition = add
|
||||||
|
err := dropbox.Init(c)
|
||||||
|
if err != nil {
|
||||||
|
c.String(200, `<p>Initialization failure:`+err.Error()+`</p><script>window.close()</script>`)
|
||||||
|
notify["status"] = "fail"
|
||||||
|
notify["message"] = "Initialization failure"
|
||||||
|
logger.Error("Then init error: ", zap.Error(err), zap.Any("name", "dropbox"))
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
username, err := dropbox.GetUserInfo(c)
|
||||||
|
if err != nil {
|
||||||
|
c.String(200, `<p>Failed to get user information:`+err.Error()+`</p><script>window.close()</script>`)
|
||||||
|
notify["status"] = "fail"
|
||||||
|
notify["message"] = "Failed to get user information"
|
||||||
|
logger.Error("Then get user information: ", zap.Error(err), zap.Any("name", "dropbox"))
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dmap := make(map[string]string)
|
||||||
|
dmap["username"] = username
|
||||||
|
|
||||||
|
configs, err := service.MyService.Storage().GetConfig()
|
||||||
|
if err != nil {
|
||||||
|
c.String(200, `<p>Failed to get rclone config:`+err.Error()+`</p><script>window.close()</script>`)
|
||||||
|
notify["status"] = "fail"
|
||||||
|
notify["message"] = "Failed to get rclone config"
|
||||||
|
logger.Error("Then get config error: ", zap.Error(err), zap.Any("name", "dropbox"))
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, v := range configs.Remotes {
|
||||||
|
cf, err := service.MyService.Storage().GetConfigByName(v)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("then get config by name error: ", zap.Error(err), zap.Any("name", v))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cf["type"] == "dropbox" && cf["username"] == dmap["username"] {
|
||||||
|
c.String(200, `<p>The same configuration has been added</p><script>window.close()</script>`)
|
||||||
|
err := service.MyService.Storage().CheckAndMountByName(v)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("check and mount by name error: ", zap.Error(err), zap.Any("name", cf["username"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
notify["status"] = "warn"
|
||||||
|
notify["message"] = "The same configuration has been added"
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(username) > 0 {
|
||||||
|
a := strings.Split(username, "@")
|
||||||
|
username = a[0]
|
||||||
|
}
|
||||||
|
username += "_dropbox_" + strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
|
|
||||||
|
dmap["client_id"] = add.AppKey
|
||||||
|
dmap["client_secret"] = add.AppSecret
|
||||||
|
dmap["token"] = `{"access_token":"` + dropbox.AccessToken + `","token_type":"bearer","refresh_token":"` + dropbox.Addition.RefreshToken + `","expiry":"` + currentDate + `T` + currentTime.Add(time.Hour*3).Add(time.Minute*50).Format("15:04:05") + `.780385354Z"}`
|
||||||
|
dmap["mount_point"] = "/mnt/" + username
|
||||||
|
// data.SetValue(username, "type", "dropbox")
|
||||||
|
// data.SetValue(username, "client_id", add.AppKey)
|
||||||
|
// data.SetValue(username, "client_secret", add.AppSecret)
|
||||||
|
// data.SetValue(username, "mount_point", "/mnt/"+username)
|
||||||
|
|
||||||
|
// data.SetValue(username, "token", `{"access_token":"`+dropbox.AccessToken+`","token_type":"bearer","refresh_token":"`+dropbox.Addition.RefreshToken+`","expiry":"`+currentDate+`T`+currentTime.Add(time.Hour*3).Format("15:04:05")+`.780385354Z"}`)
|
||||||
|
// e = data.Save()
|
||||||
|
// if e != nil {
|
||||||
|
// c.String(200, `<p>保存配置失败:`+e.Error()+`</p>`)
|
||||||
|
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
service.MyService.Storage().CreateConfig(dmap, username, "dropbox")
|
||||||
|
service.MyService.Storage().MountStorage("/mnt/"+username, username+":")
|
||||||
|
|
||||||
|
notify["status"] = "success"
|
||||||
|
notify["message"] = "Success"
|
||||||
|
notify["driver"] = "Dropbox"
|
||||||
|
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.String(200, `<p>Just close the page</p><script>window.close()</script>`)
|
||||||
|
}
|
||||||
@@ -12,11 +12,17 @@ package v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/systemctl"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/IceWhaleTech/CasaOS/model"
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/samba"
|
"github.com/IceWhaleTech/CasaOS/pkg/samba"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
||||||
@@ -30,12 +36,14 @@ import (
|
|||||||
// service
|
// service
|
||||||
|
|
||||||
func GetSambaStatus(c *gin.Context) {
|
func GetSambaStatus(c *gin.Context) {
|
||||||
status := service.MyService.System().IsServiceRunning("smbd")
|
if status, err := systemctl.IsServiceRunning("smbd"); err != nil || !status {
|
||||||
|
c.JSON(http.StatusInternalServerError, model.Result{
|
||||||
if !status {
|
Success: common_err.SERVICE_NOT_RUNNING,
|
||||||
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_NOT_RUNNING, Message: common_err.GetMsg(common_err.SERVICE_NOT_RUNNING)})
|
Message: common_err.GetMsg(common_err.SERVICE_NOT_RUNNING),
|
||||||
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
needInit := true
|
needInit := true
|
||||||
if file.Exists("/etc/samba/smb.conf") {
|
if file.Exists("/etc/samba/smb.conf") {
|
||||||
str := file.ReadLine(1, "/etc/samba/smb.conf")
|
str := file.ReadLine(1, "/etc/samba/smb.conf")
|
||||||
@@ -87,12 +95,13 @@ func PostSambaSharesCreate(c *gin.Context) {
|
|||||||
shareDBModel.Anonymous = true
|
shareDBModel.Anonymous = true
|
||||||
shareDBModel.Path = v.Path
|
shareDBModel.Path = v.Path
|
||||||
shareDBModel.Name = filepath.Base(v.Path)
|
shareDBModel.Name = filepath.Base(v.Path)
|
||||||
os.Chmod(v.Path, 0777)
|
os.Chmod(v.Path, 0o777)
|
||||||
service.MyService.Shares().CreateShare(shareDBModel)
|
service.MyService.Shares().CreateShare(shareDBModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: shares})
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: shares})
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteSambaShares(c *gin.Context) {
|
func DeleteSambaShares(c *gin.Context) {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
if id == "" {
|
if id == "" {
|
||||||
@@ -103,8 +112,7 @@ func DeleteSambaShares(c *gin.Context) {
|
|||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: id})
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: id})
|
||||||
}
|
}
|
||||||
|
|
||||||
//client
|
// client
|
||||||
|
|
||||||
func GetSambaConnectionsList(c *gin.Context) {
|
func GetSambaConnectionsList(c *gin.Context) {
|
||||||
connections := service.MyService.Connections().GetConnectionsList()
|
connections := service.MyService.Connections().GetConnectionsList()
|
||||||
connectionList := []model.Connections{}
|
connectionList := []model.Connections{}
|
||||||
@@ -191,11 +199,28 @@ func DeleteSambaConnections(c *gin.Context) {
|
|||||||
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.Record_NOT_EXIST, Message: common_err.GetMsg(common_err.Record_NOT_EXIST)})
|
c.JSON(common_err.CLIENT_ERROR, model.Result{Success: common_err.Record_NOT_EXIST, Message: common_err.GetMsg(common_err.Record_NOT_EXIST)})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mountPointList := service.MyService.System().GetDirPath(connection.MountPoint)
|
mountPointList, err := samba.GetSambaSharesList(connection.Host, connection.Port, connection.Username, connection.Password)
|
||||||
for _, v := range mountPointList {
|
//mountPointList, err := service.MyService.System().GetDirPath(connection.MountPoint)
|
||||||
service.MyService.Connections().UnmountSmaba(v.Path)
|
if err != nil {
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
baseHostPath := "/mnt/" + connection.Host
|
||||||
|
for _, v := range mountPointList {
|
||||||
|
if service.IsMounted(baseHostPath + "/" + v) {
|
||||||
|
err := service.MyService.Connections().UnmountSmaba(baseHostPath + "/" + v)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("unmount smaba error", zap.Error(err), zap.Any("path", baseHostPath+"/"+v))
|
||||||
|
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
dir, _ := ioutil.ReadDir(connection.MountPoint)
|
||||||
|
if len(dir) == 0 {
|
||||||
|
os.RemoveAll(connection.MountPoint)
|
||||||
}
|
}
|
||||||
os.RemoveAll(connection.MountPoint)
|
|
||||||
service.MyService.Connections().DeleteConnection(id)
|
service.MyService.Connections().DeleteConnection(id)
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: id})
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: id})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,16 +8,17 @@
|
|||||||
* @Website: https://www.casaos.io
|
* @Website: https://www.casaos.io
|
||||||
* Copyright (c) 2022 by icewhale, All Rights Reserved.
|
* Copyright (c) 2022 by icewhale, All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
package v1
|
package v1_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
v1 "github.com/IceWhaleTech/CasaOS/route/v1"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/stretchr/testify/assert"
|
"gotest.tools/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
|
func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
|
||||||
@@ -62,7 +63,7 @@ func TestGetSambaSharesList(t *testing.T) {
|
|||||||
|
|
||||||
requestUrl := "/v1/samba/shares"
|
requestUrl := "/v1/samba/shares"
|
||||||
httpRequest, _ := http.NewRequest("GET", requestUrl, nil)
|
httpRequest, _ := http.NewRequest("GET", requestUrl, nil)
|
||||||
GetSambaSharesList(con)
|
v1.GetSambaSharesList(con)
|
||||||
ginEngine.ServeHTTP(response, httpRequest)
|
ginEngine.ServeHTTP(response, httpRequest)
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -14,6 +15,7 @@ import (
|
|||||||
|
|
||||||
http2 "github.com/IceWhaleTech/CasaOS-Common/utils/http"
|
http2 "github.com/IceWhaleTech/CasaOS-Common/utils/http"
|
||||||
"github.com/IceWhaleTech/CasaOS-Common/utils/port"
|
"github.com/IceWhaleTech/CasaOS-Common/utils/port"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/common"
|
||||||
"github.com/IceWhaleTech/CasaOS/model"
|
"github.com/IceWhaleTech/CasaOS/model"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/config"
|
"github.com/IceWhaleTech/CasaOS/pkg/config"
|
||||||
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
|
||||||
@@ -46,7 +48,7 @@ func GetSystemCheckVersion(c *gin.Context) {
|
|||||||
data := make(map[string]interface{}, 3)
|
data := make(map[string]interface{}, 3)
|
||||||
data["need_update"] = need
|
data["need_update"] = need
|
||||||
data["version"] = version
|
data["version"] = version
|
||||||
data["current_version"] = types.CURRENTVERSION
|
data["current_version"] = common.VERSION
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +94,7 @@ func GetSystemConfigDebug(c *gin.Context) {
|
|||||||
- Remote Version: %s
|
- Remote Version: %s
|
||||||
- Browser: $Browser$
|
- Browser: $Browser$
|
||||||
- Version: $Version$
|
- Version: $Version$
|
||||||
`, sys.OS, types.CURRENTVERSION, disk.Total>>20, disk.Used>>20, array, version.Version)
|
`, sys.OS, common.VERSION, disk.Total>>20, disk.Used>>20, array, version.Version)
|
||||||
|
|
||||||
// array = append(array, fmt.Sprintf("disk,total:%v,used:%v,UsedPercent:%v", disk.Total>>20, disk.Used>>20, disk.UsedPercent))
|
// array = append(array, fmt.Sprintf("disk,total:%v,used:%v,UsedPercent:%v", disk.Total>>20, disk.Used>>20, disk.UsedPercent))
|
||||||
|
|
||||||
@@ -175,12 +177,17 @@ func PostKillCasaOS(c *gin.Context) {
|
|||||||
func GetSystemHardwareInfo(c *gin.Context) {
|
func GetSystemHardwareInfo(c *gin.Context) {
|
||||||
data := make(map[string]string, 1)
|
data := make(map[string]string, 1)
|
||||||
data["drive_model"] = service.MyService.System().GetDeviceTree()
|
data["drive_model"] = service.MyService.System().GetDeviceTree()
|
||||||
c.JSON(common_err.SUCCESS,
|
data["arch"] = runtime.GOARCH
|
||||||
model.Result{
|
|
||||||
Success: common_err.SUCCESS,
|
if cpu := service.MyService.System().GetCpuInfo(); len(cpu) > 0 {
|
||||||
Message: common_err.GetMsg(common_err.SUCCESS),
|
|
||||||
Data: data,
|
c.JSON(common_err.SUCCESS,
|
||||||
})
|
model.Result{
|
||||||
|
Success: common_err.SUCCESS,
|
||||||
|
Message: common_err.GetMsg(common_err.SUCCESS),
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary system utilization
|
// @Summary system utilization
|
||||||
@@ -229,9 +236,11 @@ func GetSystemUtilization(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data["net"] = newNet
|
data["net"] = newNet
|
||||||
for k, v := range service.MyService.Notify().GetSystemTempMap() {
|
systemMap := service.MyService.Notify().GetSystemTempMap()
|
||||||
data[k] = v
|
systemMap.Range(func(key, value interface{}) bool {
|
||||||
}
|
data[key.(string)] = value
|
||||||
|
return true
|
||||||
|
})
|
||||||
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
|
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: data})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
222
route/v2.go
Normal file
222
route/v2.go
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
package route
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/codegen"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/common_err"
|
||||||
|
"github.com/IceWhaleTech/CasaOS-Common/utils/jwt"
|
||||||
|
v2Route "github.com/IceWhaleTech/CasaOS/route/v2"
|
||||||
|
"github.com/deepmap/oapi-codegen/pkg/middleware"
|
||||||
|
"github.com/getkin/kin-openapi/openapi3"
|
||||||
|
"github.com/getkin/kin-openapi/openapi3filter"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
echo_middleware "github.com/labstack/echo/v4/middleware"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_swagger *openapi3.T
|
||||||
|
|
||||||
|
V2APIPath string
|
||||||
|
V2DocPath string
|
||||||
|
V3FilePath string
|
||||||
|
V4DirPath string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
swagger, err := codegen.GetSwagger()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_swagger = swagger
|
||||||
|
|
||||||
|
u, err := url.Parse(_swagger.Servers[0].URL)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
V2APIPath = strings.TrimRight(u.Path, "/")
|
||||||
|
V2DocPath = "/doc" + V2APIPath
|
||||||
|
V3FilePath = "/v3/file"
|
||||||
|
V4DirPath = "/v4/dir"
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitV2Router() http.Handler {
|
||||||
|
appManagement := v2Route.NewCasaOS()
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
|
||||||
|
e.Use((echo_middleware.CORSWithConfig(echo_middleware.CORSConfig{
|
||||||
|
AllowOrigins: []string{"*"},
|
||||||
|
AllowMethods: []string{echo.POST, echo.GET, echo.OPTIONS, echo.PUT, echo.DELETE},
|
||||||
|
AllowHeaders: []string{echo.HeaderAuthorization, echo.HeaderContentLength, echo.HeaderXCSRFToken, echo.HeaderContentType, echo.HeaderAccessControlAllowOrigin, echo.HeaderAccessControlAllowHeaders, echo.HeaderAccessControlAllowMethods, echo.HeaderConnection, echo.HeaderOrigin, echo.HeaderXRequestedWith},
|
||||||
|
ExposeHeaders: []string{echo.HeaderContentLength, echo.HeaderAccessControlAllowOrigin, echo.HeaderAccessControlAllowHeaders},
|
||||||
|
MaxAge: 172800,
|
||||||
|
AllowCredentials: true,
|
||||||
|
})))
|
||||||
|
|
||||||
|
e.Use(echo_middleware.Gzip())
|
||||||
|
|
||||||
|
e.Use(echo_middleware.Logger())
|
||||||
|
|
||||||
|
e.Use(echo_middleware.JWTWithConfig(echo_middleware.JWTConfig{
|
||||||
|
Skipper: func(c echo.Context) bool {
|
||||||
|
return c.RealIP() == "::1" || c.RealIP() == "127.0.0.1"
|
||||||
|
//return true
|
||||||
|
},
|
||||||
|
ParseTokenFunc: func(token string, c echo.Context) (interface{}, error) {
|
||||||
|
claims, code := jwt.Validate(token)
|
||||||
|
if code != common_err.SUCCESS {
|
||||||
|
return nil, echo.ErrUnauthorized
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Request().Header.Set("user_id", strconv.Itoa(claims.ID))
|
||||||
|
|
||||||
|
return claims, nil
|
||||||
|
},
|
||||||
|
TokenLookupFuncs: []echo_middleware.ValuesExtractor{
|
||||||
|
func(c echo.Context) ([]string, error) {
|
||||||
|
return []string{c.Request().Header.Get(echo.HeaderAuthorization)}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
// e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
// return func(c echo.Context) error {
|
||||||
|
// switch c.Request().Header.Get(echo.HeaderContentType) {
|
||||||
|
// case common.MIMEApplicationYAML: // in case request contains a compose content in YAML
|
||||||
|
// return middleware.OapiRequestValidatorWithOptions(_swagger, &middleware.Options{
|
||||||
|
// Options: openapi3filter.Options{
|
||||||
|
// AuthenticationFunc: openapi3filter.NoopAuthenticationFunc,
|
||||||
|
// // ExcludeRequestBody: true,
|
||||||
|
// // ExcludeResponseBody: true,
|
||||||
|
// },
|
||||||
|
// })(next)(c)
|
||||||
|
|
||||||
|
// default:
|
||||||
|
// return middleware.OapiRequestValidatorWithOptions(_swagger, &middleware.Options{
|
||||||
|
// Options: openapi3filter.Options{
|
||||||
|
// AuthenticationFunc: openapi3filter.NoopAuthenticationFunc,
|
||||||
|
// },
|
||||||
|
// })(next)(c)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
e.Use(middleware.OapiRequestValidatorWithOptions(_swagger, &middleware.Options{
|
||||||
|
Options: openapi3filter.Options{AuthenticationFunc: openapi3filter.NoopAuthenticationFunc},
|
||||||
|
}))
|
||||||
|
|
||||||
|
codegen.RegisterHandlersWithBaseURL(e, appManagement, V2APIPath)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitV2DocRouter(docHTML string, docYAML string) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path == V2DocPath {
|
||||||
|
if _, err := w.Write([]byte(docHTML)); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.URL.Path == V2DocPath+"/openapi.yaml" {
|
||||||
|
if _, err := w.Write([]byte(docYAML)); err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
func InitFile() http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
filePath := r.URL.Query().Get("path")
|
||||||
|
fileName := path.Base(filePath)
|
||||||
|
w.Header().Add("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(fileName))
|
||||||
|
http.ServeFile(w, r, filePath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitDir() http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t := r.URL.Query().Get("format")
|
||||||
|
files := r.URL.Query().Get("files")
|
||||||
|
|
||||||
|
if len(files) == 0 {
|
||||||
|
// w.JSON(common_err.CLIENT_ERROR, model.Result{
|
||||||
|
// Success: common_err.INVALID_PARAMS,
|
||||||
|
// Message: common_err.GetMsg(common_err.INVALID_PARAMS),
|
||||||
|
// })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list := strings.Split(files, ",")
|
||||||
|
for _, v := range list {
|
||||||
|
if !file.Exists(v) {
|
||||||
|
// c.JSON(common_err.SERVICE_ERROR, model.Result{
|
||||||
|
// Success: common_err.FILE_DOES_NOT_EXIST,
|
||||||
|
// Message: common_err.GetMsg(common_err.FILE_DOES_NOT_EXIST),
|
||||||
|
// })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/octet-stream")
|
||||||
|
w.Header().Add("Content-Transfer-Encoding", "binary")
|
||||||
|
w.Header().Add("Cache-Control", "no-cache")
|
||||||
|
// handles only single files not folders and multiple files
|
||||||
|
// if len(list) == 1 {
|
||||||
|
|
||||||
|
//filePath := list[0]
|
||||||
|
// info, err := os.Stat(filePath)
|
||||||
|
// if err != nil {
|
||||||
|
|
||||||
|
// w.JSON(http.StatusOK, model.Result{
|
||||||
|
// Success: common_err.FILE_DOES_NOT_EXIST,
|
||||||
|
// Message: common_err.GetMsg(common_err.FILE_DOES_NOT_EXIST),
|
||||||
|
// })
|
||||||
|
//return
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
extension, ar, err := file.GetCompressionAlgorithm(t)
|
||||||
|
if err != nil {
|
||||||
|
// w.JSON(common_err.CLIENT_ERROR, model.Result{
|
||||||
|
// Success: common_err.INVALID_PARAMS,
|
||||||
|
// Message: common_err.GetMsg(common_err.INVALID_PARAMS),
|
||||||
|
// })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ar.Create(w)
|
||||||
|
if err != nil {
|
||||||
|
// c.JSON(common_err.SERVICE_ERROR, model.Result{
|
||||||
|
// Success: common_err.SERVICE_ERROR,
|
||||||
|
// Message: common_err.GetMsg(common_err.SERVICE_ERROR),
|
||||||
|
// Data: err.Error(),
|
||||||
|
// })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer ar.Close()
|
||||||
|
commonDir := file.CommonPrefix(filepath.Separator, list...)
|
||||||
|
|
||||||
|
currentPath := filepath.Base(commonDir)
|
||||||
|
|
||||||
|
name := "_" + currentPath
|
||||||
|
name += extension
|
||||||
|
w.Header().Add("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))
|
||||||
|
for _, fname := range list {
|
||||||
|
err = file.AddFile(ar, fname, commonDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to archive %s: %v", fname, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
17
route/v2/file.go
Normal file
17
route/v2/file.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Path: route/v2/file.go
|
||||||
|
|
||||||
|
func (s *CasaOS) GetFileTest(ctx echo.Context) error {
|
||||||
|
|
||||||
|
//http.ServeFile(w, r, r.URL.Path[1:])
|
||||||
|
http.ServeFile(ctx.Response().Writer, ctx.Request(), "/DATA/test.img")
|
||||||
|
|
||||||
|
return ctx.String(200, "pong")
|
||||||
|
}
|
||||||
26
route/v2/health.go
Normal file
26
route/v2/health.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/IceWhaleTech/CasaOS/codegen"
|
||||||
|
"github.com/IceWhaleTech/CasaOS/service"
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *CasaOS) GetHealthServices(ctx echo.Context) error {
|
||||||
|
services, err := service.MyService.Health().Services()
|
||||||
|
if err != nil {
|
||||||
|
message := err.Error()
|
||||||
|
return ctx.JSON(http.StatusInternalServerError, codegen.ResponseInternalServerError{
|
||||||
|
Message: &message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.JSON(http.StatusOK, codegen.GetHealthServicesOK{
|
||||||
|
Data: &codegen.HealthServices{
|
||||||
|
Running: services[true],
|
||||||
|
NotRunning: services[false],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
11
route/v2/route.go
Normal file
11
route/v2/route.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/IceWhaleTech/CasaOS/codegen"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CasaOS struct{}
|
||||||
|
|
||||||
|
func NewCasaOS() codegen.ServerInterface {
|
||||||
|
return &CasaOS{}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user