Compare commits

...

36 Commits

Author SHA1 Message Date
link
60a141fe25 Test init log (#974) 2023-03-23 12:09:46 +08:00
link
02e712f649 Test init log (#972) 2023-03-22 16:06:48 +08:00
link
7022cf5d29 Add test log init (#971) 2023-03-22 14:07:01 +08:00
Tiger Wang
6cf46ce50c update github workflow with newer version of different actions (#970) 2023-03-21 22:59:31 -04:00
link
202c1de570 Update file count (#969) 2023-03-21 14:25:01 +08:00
link
858ab5b124 Update file count (#968) 2023-03-21 10:12:03 +08:00
link
e319975a60 Update file count (#966) 2023-03-20 19:47:13 +08:00
link
5c2c3b5e98 Update file count (#965) 2023-03-20 17:02:42 +08:00
link
a0dc58264a Update interface result state (#964) 2023-03-20 15:24:45 +08:00
Tiger Wang
717b47ca2c clean up some unnecessary dependencies, logics, and linter warnings (#963)
Signed-off-by: Tiger Wang <tigerwang@outlook.com>
2023-03-18 20:55:51 -04:00
LinkLeong
f530f69ba5 Update migration.list 2023-03-17 08:10:01 +00:00
LinkLeong
87b190a84b Remove fs.go 2023-03-17 08:00:06 +00:00
link
10191a1be3 Test rclone (#961) 2023-03-17 15:37:28 +08:00
Tiger Wang
59f2ccbeb3 move app request to CasaOS-AppStore (#958)
Signed-off-by: Tiger Wang <tigerwang@outlook.com>
2023-03-15 21:02:34 -04:00
link
076b7198b2 Special treatment for arm equipment 2023-03-15 06:20:58 +00:00
link
eb483a4c5d Change log (#955) 2023-03-15 10:46:12 +08:00
link
60e81bc781 Change changelog (#953) 2023-03-14 14:02:44 +08:00
link
dc8ee89c85 Statistics on the number of files added (#951) 2023-03-13 18:30:22 +08:00
link
c995750312 Socket service (#949) 2023-03-13 09:42:18 +08:00
Tiger Wang
de6ed093a2 Create SECURITY.md
Signed-off-by: Tiger Wang <tigerwang@outlook.com>
2023-03-08 20:07:56 -05:00
link
449e1515d9 Rclone update (#935) 2023-03-06 16:10:19 +08:00
raller1028
e6ddb0d849 Update push_test_server.yml
Signed-off-by: raller1028 <57336867+raller1028@users.noreply.github.com>
2023-03-02 15:46:34 +08:00
link
c19e32c6e9 Modify user rights (#922) 2023-02-24 14:35:38 +08:00
link
94755e221a Add judgment on mount points (#921) 2023-02-24 14:20:30 +08:00
link
86a3692dad Add proxy search interface (#917) 2023-02-23 10:38:48 +08:00
raller1028
45a5567978 Update push_test_server.yml
Signed-off-by: raller1028 <57336867+raller1028@users.noreply.github.com>
2023-02-21 15:45:43 +08:00
link
3190421fd9 Start sequence (#904)
Signed-off-by: link <a624669980@163.com>
2023-02-17 19:31:22 +08:00
link
a7126cac63 Rotational training registration to message-bus (#903) 2023-02-17 18:40:19 +08:00
raller1028
151aa037bb Update push_test_server.yml
Signed-off-by: raller1028 <57336867+raller1028@users.noreply.github.com>
2023-02-17 11:46:30 +08:00
raller1028
08ed9933ee Update push_test_server.yml
Signed-off-by: raller1028 <57336867+raller1028@users.noreply.github.com>
2023-02-16 18:32:47 +08:00
raller1028
8425011e73 Update push_test_server.yml
Signed-off-by: raller1028 <57336867+raller1028@users.noreply.github.com>
2023-02-16 18:20:40 +08:00
link
96ff550d61 Fix permission change cannot be unmount (#902) 2023-02-16 16:55:51 +08:00
link
85a803d352 Update service (#900) 2023-02-16 14:23:49 +08:00
link
5def3e5856 Add error log (#899) 2023-02-16 09:08:35 +08:00
Tiger Wang
c768260b1b add CODE_OF_CONDUCT.md (#898)
Signed-off-by: Tiger Wang <tigerwang@outlook.com>
2023-02-15 16:14:47 -05:00
link
46e146e633 Fix the download file name error problem (#896) 2023-02-15 17:31:20 +08:00
58 changed files with 1493 additions and 2209 deletions

View File

@@ -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.

View File

@@ -1,5 +1,8 @@
blank_issues_enabled: false
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
url: https://github.com/IceWhaleTech/CasaOS/discussions/164
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
- name: Discord
url: https://discord.gg/knqAbbBbeX
about: Get help or share great ideas on Discord!
about: Get help or share great ideas on Discord!

View File

@@ -40,7 +40,7 @@ jobs:
# env:
# GITHUB_TOKEN: ${{ github.token }}
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
@@ -73,7 +73,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: '14'

View File

@@ -13,10 +13,14 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
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
uses: codecov/codecov-action@v3

View File

@@ -22,7 +22,7 @@ jobs:
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# 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
uses: aws-actions/configure-aws-credentials@v1

View File

@@ -11,48 +11,74 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v2
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: Fetch all tags
- 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@v2
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.19
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
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
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 ${{ secrets.ssh_port }} ./dist/*.gz root@${{ secrets.ssh_ip }}:/var/www/download
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"}}' https://open.feishu.cn/open-apis/bot/v2/hook/eb8f45c7-9636-4b64-84f2-a66d9aeb9d30
curl -X POST -H "Content-Type: application/json" -d '{"msg_type":"text","content":{"text":"CasaOS updated"}}' ${{ secrets.SSH_ROBOT_URL }}

View File

@@ -12,34 +12,29 @@ jobs:
goreleaser:
runs-on: ubuntu-22.04
steps:
-
name: Install dependencies for cross-compiling
- name: Install dependencies for cross-compiling
run: |
sudo apt update
sudo apt-get --no-install-recommends --yes install \
upx libc6-dev-amd64-cross \
gcc-aarch64-linux-gnu libc6-dev-arm64-cross \
gcc-arm-linux-gnueabihf libc6-dev-armhf-cross
-
name: Checkout
uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
-
name: Fetch all tags
- name: Fetch all tags
run: git fetch --force --tags
- name: Get version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
-
name: Set up Go
uses: actions/setup-go@v2
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.19
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
go-version: "1.20"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
@@ -49,7 +44,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Your GoReleaser Pro key, if you are using the 'goreleaser-pro' distribution
# GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
- name: Upload to oss
id: upload_to_oss
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-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

View File

@@ -16,6 +16,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Security
## [0.4.3]
### Added
- [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]

128
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
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.

9
SECURITY.md Normal file
View 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

View File

@@ -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
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

View File

@@ -2,8 +2,9 @@
Description=rclone
[Service]
ExecStartPre=/usr/bin/rm -f /tmp/rclone.sock
ExecStart=/usr/bin/rclone rcd --rc-addr unix:///tmp/rclone.sock --rc-no-auth
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

View File

@@ -25,7 +25,7 @@ GetNetCard() {
fi
else
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
}

View File

@@ -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
import (
interfaces "github.com/IceWhaleTech/CasaOS-Common"
"github.com/IceWhaleTech/CasaOS-Common/utils/version"
)
type migrationTool struct{}
func (u *migrationTool) IsMigrationNeeded() (bool, error) {
majorVersion, minorVersion, patchVersion, err := version.DetectLegacyVersion()
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
return false, nil
}
func (u *migrationTool) PreMigrate() error {

View File

@@ -14,7 +14,7 @@ import (
)
type Dropbox struct {
model.Storage
model.StorageA
Addition
AccessToken string
}

View File

@@ -17,7 +17,7 @@ import (
)
type GoogleDrive struct {
model.Storage
model.StorageA
Addition
AccessToken string
}

121
go.mod
View File

@@ -1,130 +1,133 @@
module github.com/IceWhaleTech/CasaOS
go 1.19
go 1.20
require (
github.com/Curtis-Milo/nat-type-identifier-go v0.0.0-20220215191915-18d42168c63d
github.com/IceWhaleTech/CasaOS-Common v0.4.2-alpha3
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
github.com/deckarep/golang-set/v2 v2.1.0
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/dsoprea/go-exif/v3 v3.0.0-20221012082141-d21ac8e2de85
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd
github.com/getkin/kin-openapi v0.113.0
github.com/getkin/kin-openapi v0.115.0
github.com/gin-contrib/gzip v0.0.6
github.com/gin-gonic/gin v1.8.2
github.com/glebarez/sqlite v1.6.0
github.com/gin-gonic/gin v1.9.0
github.com/glebarez/sqlite v1.7.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/gomodule/redigo v1.8.9
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/h2non/filetype v1.1.3
github.com/hirochachacha/go-smb2 v1.1.0
github.com/json-iterator/go v1.1.12
github.com/labstack/echo/v4 v4.10.0
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/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/pkg/errors v0.9.1
github.com/robfig/cron v1.2.0
github.com/robfig/cron/v3 v3.0.1
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/sirupsen/logrus v1.9.0
github.com/stretchr/testify v1.8.1
github.com/tidwall/gjson v1.14.4
go.uber.org/goleak v1.2.1
go.uber.org/zap v1.24.0
golang.org/x/crypto v0.5.0
golang.org/x/oauth2 v0.3.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gorm.io/gorm v1.24.3
golang.org/x/crypto v0.7.0
golang.org/x/oauth2 v0.6.0
golang.org/x/sync v0.1.0
gorm.io/gorm v1.24.6
gotest.tools v2.2.0+incompatible
)
require (
github.com/andybalholm/brotli v1.0.1 // indirect
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/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // 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.20.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.5 // indirect
github.com/go-openapi/swag v0.21.1 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/godbus/dbus/v5 v5.0.4 // indirect
github.com/gofrs/uuid v4.0.0+incompatible // 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.4.2 // 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.2 // 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.0.0 // 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.1.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.15.13 // 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.1 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // 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.16 // indirect
github.com/mattn/go-sqlite3 v1.14.15 // 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.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // 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.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // 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.0 // 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/ugorji/go/codec v1.2.7 // indirect
github.com/ulikunitz/xz v0.5.9 // 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.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
golang.org/x/net v0.5.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
golang.org/x/time v0.2.0 // 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.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // 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.21.5 // indirect
modernc.org/libc v1.22.3 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.4.0 // indirect
modernc.org/sqlite v1.20.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/sqlite v1.21.0 // indirect
)

291
go.sum
View File

@@ -1,31 +1,32 @@
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
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/IceWhaleTech/CasaOS-Common v0.4.2-alpha3 h1:WJUYo+hJpLmza7mQngoJVeUJOfnrZevNrX5wzTuOJo0=
github.com/IceWhaleTech/CasaOS-Common v0.4.2-alpha3/go.mod h1:xcemiRsXcs1zrmQxYMyExDjZ7UHYwkJqYE71IDIV0xA=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a h1:RenIAa2q4H8UcS/cqmwdT1WCWIAH5aumP8m8RpbqVsE=
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a/go.mod h1:sSBbaOg90XwWKtpT56kVujF0bIeVITnPlssLclogS04=
github.com/andybalholm/brotli v1.0.1 h1:KqhlKozYbRtJvsPrrEeXcO+N2l6NYT5A2QAFmSULpEc=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.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/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
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/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.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
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=
@@ -50,22 +51,23 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003142440-7a1927d49d9d/go.mod h1:LV
github.com/dsoprea/go-utility/v2 v2.0.0-20221003160719-7bc88537c05e/go.mod h1:VZ7cB0pTjm1ADBWhJUOHESu4ZYy9JN+ZPqjfiW09EPU=
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje0z+3UQ5YjYiSRRzVdtamFpvBQXKwMglWqw=
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8=
github.com/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/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
github.com/getkin/kin-openapi v0.113.0 h1:t9aNS/q5Agr7a55Jp1AuZ3sR2WzHESv3Dd2ys4UphsM=
github.com/getkin/kin-openapi v0.113.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
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/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/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.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY=
github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398=
github.com/glebarez/go-sqlite v1.20.0 h1:6D9uRXq3Kd+W7At+hOU2eIAeahv6qcYfO8jzmvb4Dr8=
github.com/glebarez/go-sqlite v1.20.0/go.mod h1:uTnJoqtwMQjlULmljLT73Cg7HB+2X6evsBHODyyq1ak=
github.com/glebarez/sqlite v1.6.0 h1:ZpvDLv4zBi2cuuQPitRiVz/5Uh6sXa5d8eBu0xNTpAo=
github.com/glebarez/sqlite v1.6.0/go.mod h1:6D6zPU/HTrFlYmVDKqBJlmQvma90P6r7sRRdkUUZOYk=
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
github.com/glebarez/go-sqlite v1.21.0 h1:b8MHPtBagkSD2gntImZPsG3o3QEXgMDxguW/GLUonHQ=
github.com/glebarez/go-sqlite v1.21.0/go.mod h1:GodsA6yGSa3eKbvpr7dS+JaqazzVfMcjIXvx6KHhW/c=
github.com/glebarez/sqlite v1.7.0 h1:A7Xj/KN2Lvie4Z4rrgQHY8MsbebX3NyWsL3n2i82MVI=
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.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
@@ -75,35 +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-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-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
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.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
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/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/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.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.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
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.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
github.com/goccy/go-json v0.10.1 h1:lEs5Ob+oOG/Ze199njvzHbhn6p9T+h64F5hRj69iTTo=
github.com/goccy/go-json v0.10.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
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.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
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.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
@@ -113,29 +120,30 @@ 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.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.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
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.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/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-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.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/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/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.6.2/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg=
github.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8=
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=
@@ -145,14 +153,13 @@ 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/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 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
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.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/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/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -160,33 +167,37 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
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/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
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.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0=
github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
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/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/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.10.0 h1:5CiyngihEO4HXsz3vVsJn7f8xAlWwRr3aY6Ih280ZKA=
github.com/labstack/echo/v4 v4.10.0/go.mod h1:S/T/5fy/GigaXnHTkh0ZGe4LpkkQysvRjFMSUTkDRNQ=
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 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
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-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.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
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=
@@ -195,12 +206,13 @@ github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb
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.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
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=
@@ -212,36 +224,41 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
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/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.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
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 h1:qvY3YFXRQE/XB8MlLzJH7mSzBs74eA2gg52YTk6jUPM=
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/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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/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/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/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
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.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.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/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shirou/gopsutil/v3 v3.22.11 h1:kxsPKS+Eeo+VnEQ2XCaGJepeP6KY53QoRTETx3+1ndM=
github.com/shirou/gopsutil/v3 v3.22.11/go.mod h1:xl0EeL4vXJ+hQMAGN8B9VFpxukEMA0XdevQOe5MZ1oY=
github.com/shirou/gopsutil/v3 v3.23.2 h1:PAWSuiAszn7IhPMBtXsbSCafej7PqUOvY6YywlQUExU=
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/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
@@ -254,24 +271,30 @@ 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.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/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/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
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.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/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
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/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/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.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.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I=
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=
@@ -279,29 +302,34 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
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/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.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/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
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-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-20210711020723-a769d52b0f97/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.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4=
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.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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
@@ -310,26 +338,26 @@ 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-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-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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
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.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw=
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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/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-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-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-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -341,31 +369,36 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/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-20211103235746-7861aae1554b/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-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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
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.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.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.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE=
golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.7.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-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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -376,18 +409,15 @@ 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/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.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
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 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -395,45 +425,20 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.3 h1:WL2ifUmzR/SLp85CSURAfybcHnGZ+yLSGSxgYXlFBHg=
gorm.io/gorm v1.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
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/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
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/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
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.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
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=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.21.0 h1:4aP4MdUf15i3R3M2mx6Q90WHKz3nZLoz96zlB6tNdow=
modernc.org/sqlite v1.21.0/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@@ -17,8 +17,8 @@ type Driver interface {
type Meta interface {
Config() Config
// GetStorage just get raw storage, no need to implement, because model.Storage have implemented
GetStorage() *model.Storage
SetStorage(model.Storage)
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

View File

@@ -1,545 +0,0 @@
package op
import (
"context"
"os"
stdpath "path"
"time"
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/generic_sync"
"github.com/IceWhaleTech/CasaOS/pkg/singleflight"
"github.com/IceWhaleTech/CasaOS/pkg/utils"
"github.com/Xhofe/go-cache"
"github.com/pkg/errors"
pkgerr "github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
// In order to facilitate adding some other things before and after file op
var listCache = cache.NewMemCache(cache.WithShards[[]model.Obj](64))
var listG singleflight.Group[[]model.Obj]
func updateCacheObj(storage driver.Driver, path string, oldObj model.Obj, newObj model.Obj) {
key := Key(storage, path)
objs, ok := listCache.Get(key)
if ok {
for i, obj := range objs {
if obj.GetName() == oldObj.GetName() {
objs[i] = newObj
break
}
}
listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
}
}
func delCacheObj(storage driver.Driver, path string, obj model.Obj) {
key := Key(storage, path)
objs, ok := listCache.Get(key)
if ok {
for i, oldObj := range objs {
if oldObj.GetName() == obj.GetName() {
objs = append(objs[:i], objs[i+1:]...)
break
}
}
listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
}
}
var addSortDebounceMap generic_sync.MapOf[string, func(func())]
func addCacheObj(storage driver.Driver, path string, newObj model.Obj) {
key := Key(storage, path)
objs, ok := listCache.Get(key)
if ok {
for i, obj := range objs {
if obj.GetName() == newObj.GetName() {
objs[i] = newObj
return
}
}
// Simple separation of files and folders
if len(objs) > 0 && objs[len(objs)-1].IsDir() == newObj.IsDir() {
objs = append(objs, newObj)
} else {
objs = append([]model.Obj{newObj}, objs...)
}
if storage.Config().LocalSort {
debounce, _ := addSortDebounceMap.LoadOrStore(key, utils.NewDebounce(time.Minute))
log.Debug("addCacheObj: wait start sort")
debounce(func() {
log.Debug("addCacheObj: start sort")
model.SortFiles(objs, storage.GetStorage().OrderBy, storage.GetStorage().OrderDirection)
addSortDebounceMap.Delete(key)
})
}
listCache.Set(key, objs, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
}
}
func ClearCache(storage driver.Driver, path string) {
listCache.Del(Key(storage, path))
}
func Key(storage driver.Driver, path string) string {
return stdpath.Join(storage.GetStorage().MountPath, utils.FixAndCleanPath(path))
}
// List files in storage, not contains virtual file
func List(ctx context.Context, storage driver.Driver, path string, args model.ListArgs, refresh ...bool) ([]model.Obj, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
}
path = utils.FixAndCleanPath(path)
log.Debugf("op.List %s", path)
key := Key(storage, path)
if !utils.IsBool(refresh...) {
if files, ok := listCache.Get(key); ok {
log.Debugf("use cache when list %s", path)
return files, nil
}
}
dir, err := GetUnwrap(ctx, storage, path)
if err != nil {
return nil, errors.WithMessage(err, "failed get dir")
}
log.Debugf("list dir: %+v", dir)
if !dir.IsDir() {
return nil, errors.WithStack(errors.New("not a folder"))
}
objs, err, _ := listG.Do(key, func() ([]model.Obj, error) {
files, err := storage.List(ctx, dir, args)
if err != nil {
return nil, errors.Wrapf(err, "failed to list objs")
}
// set path
for _, f := range files {
if s, ok := f.(model.SetPath); ok && f.GetPath() == "" && dir.GetPath() != "" {
s.SetPath(stdpath.Join(dir.GetPath(), f.GetName()))
}
}
// warp obj name
model.WrapObjsName(files)
// call hooks
go func(reqPath string, files []model.Obj) {
for _, hook := range ObjsUpdateHooks {
hook(args.ReqPath, files)
}
}(args.ReqPath, files)
// sort objs
if storage.Config().LocalSort {
model.SortFiles(files, storage.GetStorage().OrderBy, storage.GetStorage().OrderDirection)
}
model.ExtractFolder(files, storage.GetStorage().ExtractFolder)
if !storage.Config().NoCache {
if len(files) > 0 {
log.Debugf("set cache: %s => %+v", key, files)
listCache.Set(key, files, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
} else {
log.Debugf("del cache: %s", key)
listCache.Del(key)
}
}
return files, nil
})
return objs, err
}
// Get object from list of files
func Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, error) {
path = utils.FixAndCleanPath(path)
log.Debugf("op.Get %s", path)
// is root folder
if utils.PathEqual(path, "/") {
var rootObj model.Obj
switch r := storage.GetAddition().(type) {
case driver.IRootId:
rootObj = &model.Object{
ID: r.GetRootId(),
Name: RootName,
Size: 0,
Modified: storage.GetStorage().Modified,
IsFolder: true,
Path: path,
}
case driver.IRootPath:
rootObj = &model.Object{
Path: r.GetRootPath(),
Name: RootName,
Size: 0,
Modified: storage.GetStorage().Modified,
IsFolder: true,
}
default:
if storage, ok := storage.(driver.Getter); ok {
obj, err := storage.GetRoot(ctx)
if err != nil {
return nil, errors.WithMessage(err, "failed get root obj")
}
rootObj = obj
}
}
if rootObj == nil {
return nil, errors.Errorf("please implement IRootPath or IRootId or Getter method")
}
return &model.ObjWrapName{
Name: RootName,
Obj: rootObj,
}, nil
}
// not root folder
dir, name := stdpath.Split(path)
files, err := List(ctx, storage, dir, model.ListArgs{})
if err != nil {
return nil, errors.WithMessage(err, "failed get parent list")
}
for _, f := range files {
// TODO maybe copy obj here
if f.GetName() == name {
return f, nil
}
}
log.Debugf("cant find obj with name: %s", name)
return nil, errors.WithStack(errors.New("object not found"))
}
func GetUnwrap(ctx context.Context, storage driver.Driver, path string) (model.Obj, error) {
obj, err := Get(ctx, storage, path)
if err != nil {
return nil, err
}
return model.UnwrapObjs(obj), err
}
var linkCache = cache.NewMemCache(cache.WithShards[*model.Link](16))
var linkG singleflight.Group[*model.Link]
// Link get link, if is an url. should have an expiry time
func Link(ctx context.Context, storage driver.Driver, path string, args model.LinkArgs) (*model.Link, model.Obj, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return nil, nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
}
file, err := GetUnwrap(ctx, storage, path)
if err != nil {
return nil, nil, errors.WithMessage(err, "failed to get file")
}
if file.IsDir() {
return nil, nil, errors.WithStack(errors.New("not a file"))
}
key := Key(storage, path) + ":" + args.IP
if link, ok := linkCache.Get(key); ok {
return link, file, nil
}
fn := func() (*model.Link, error) {
link, err := storage.Link(ctx, file, args)
if err != nil {
return nil, errors.Wrapf(err, "failed get link")
}
if link.Expiration != nil {
linkCache.Set(key, link, cache.WithEx[*model.Link](*link.Expiration))
}
return link, nil
}
link, err, _ := linkG.Do(key, fn)
return link, file, err
}
// Other api
func Other(ctx context.Context, storage driver.Driver, args model.FsOtherArgs) (interface{}, error) {
obj, err := GetUnwrap(ctx, storage, args.Path)
if err != nil {
return nil, errors.WithMessagef(err, "failed to get obj")
}
if o, ok := storage.(driver.Other); ok {
return o.Other(ctx, model.OtherArgs{
Obj: obj,
Method: args.Method,
Data: args.Data,
})
} else {
return nil, errors.New("not implement")
}
}
var mkdirG singleflight.Group[interface{}]
func MakeDir(ctx context.Context, storage driver.Driver, path string, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
}
path = utils.FixAndCleanPath(path)
key := Key(storage, path)
_, err, _ := mkdirG.Do(key, func() (interface{}, error) {
// check if dir exists
f, err := GetUnwrap(ctx, storage, path)
if err != nil {
if errors.Is(pkgerr.Cause(err), errors.New("object not found")) {
parentPath, dirName := stdpath.Split(path)
err = MakeDir(ctx, storage, parentPath)
if err != nil {
return nil, errors.WithMessagef(err, "failed to make parent dir [%s]", parentPath)
}
parentDir, err := GetUnwrap(ctx, storage, parentPath)
// this should not happen
if err != nil {
return nil, errors.WithMessagef(err, "failed to get parent dir [%s]", parentPath)
}
switch s := storage.(type) {
case driver.MkdirResult:
var newObj model.Obj
newObj, err = s.MakeDir(ctx, parentDir, dirName)
if err == nil {
if newObj != nil {
addCacheObj(storage, parentPath, model.WrapObjName(newObj))
} else if !utils.IsBool(lazyCache...) {
ClearCache(storage, parentPath)
}
}
case driver.Mkdir:
err = s.MakeDir(ctx, parentDir, dirName)
if err == nil && !utils.IsBool(lazyCache...) {
ClearCache(storage, parentPath)
}
default:
return nil, errors.New("not implement")
}
return nil, errors.WithStack(err)
}
return nil, errors.WithMessage(err, "failed to check if dir exists")
}
// dir exists
if f.IsDir() {
return nil, nil
}
// dir to make is a file
return nil, errors.New("file exists")
})
return err
}
func Move(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
}
srcPath = utils.FixAndCleanPath(srcPath)
dstDirPath = utils.FixAndCleanPath(dstDirPath)
srcRawObj, err := Get(ctx, storage, srcPath)
if err != nil {
return errors.WithMessage(err, "failed to get src object")
}
srcObj := model.UnwrapObjs(srcRawObj)
dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
if err != nil {
return errors.WithMessage(err, "failed to get dst dir")
}
srcDirPath := stdpath.Dir(srcPath)
switch s := storage.(type) {
case driver.MoveResult:
var newObj model.Obj
newObj, err = s.Move(ctx, srcObj, dstDir)
if err == nil {
delCacheObj(storage, srcDirPath, srcRawObj)
if newObj != nil {
addCacheObj(storage, dstDirPath, model.WrapObjName(newObj))
} else if !utils.IsBool(lazyCache...) {
ClearCache(storage, dstDirPath)
}
}
case driver.Move:
err = s.Move(ctx, srcObj, dstDir)
if err == nil {
delCacheObj(storage, srcDirPath, srcRawObj)
if !utils.IsBool(lazyCache...) {
ClearCache(storage, dstDirPath)
}
}
default:
return errors.New("not implement")
}
return errors.WithStack(err)
}
func Rename(ctx context.Context, storage driver.Driver, srcPath, dstName string, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
}
srcPath = utils.FixAndCleanPath(srcPath)
srcRawObj, err := Get(ctx, storage, srcPath)
if err != nil {
return errors.WithMessage(err, "failed to get src object")
}
srcObj := model.UnwrapObjs(srcRawObj)
srcDirPath := stdpath.Dir(srcPath)
switch s := storage.(type) {
case driver.RenameResult:
var newObj model.Obj
newObj, err = s.Rename(ctx, srcObj, dstName)
if err == nil {
if newObj != nil {
updateCacheObj(storage, srcDirPath, srcRawObj, model.WrapObjName(newObj))
} else if !utils.IsBool(lazyCache...) {
ClearCache(storage, srcDirPath)
}
}
case driver.Rename:
err = s.Rename(ctx, srcObj, dstName)
if err == nil && !utils.IsBool(lazyCache...) {
ClearCache(storage, srcDirPath)
}
default:
return errors.New("not implement")
}
return errors.WithStack(err)
}
// Copy Just copy file[s] in a storage
func Copy(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
}
srcPath = utils.FixAndCleanPath(srcPath)
dstDirPath = utils.FixAndCleanPath(dstDirPath)
srcObj, err := GetUnwrap(ctx, storage, srcPath)
if err != nil {
return errors.WithMessage(err, "failed to get src object")
}
dstDir, err := GetUnwrap(ctx, storage, dstDirPath)
if err != nil {
return errors.WithMessage(err, "failed to get dst dir")
}
switch s := storage.(type) {
case driver.CopyResult:
var newObj model.Obj
newObj, err = s.Copy(ctx, srcObj, dstDir)
if err == nil {
if newObj != nil {
addCacheObj(storage, dstDirPath, model.WrapObjName(newObj))
} else if !utils.IsBool(lazyCache...) {
ClearCache(storage, dstDirPath)
}
}
case driver.Copy:
err = s.Copy(ctx, srcObj, dstDir)
if err == nil && !utils.IsBool(lazyCache...) {
ClearCache(storage, dstDirPath)
}
default:
return errors.New("not implement")
}
return errors.WithStack(err)
}
func Remove(ctx context.Context, storage driver.Driver, path string) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
}
path = utils.FixAndCleanPath(path)
rawObj, err := Get(ctx, storage, path)
if err != nil {
// if object not found, it's ok
if errors.Is(pkgerr.Cause(err), errors.New("object not found")) {
return nil
}
return errors.WithMessage(err, "failed to get object")
}
dirPath := stdpath.Dir(path)
switch s := storage.(type) {
case driver.Remove:
err = s.Remove(ctx, model.UnwrapObjs(rawObj))
if err == nil {
delCacheObj(storage, dirPath, rawObj)
}
default:
return errors.New("not implement")
}
return errors.WithStack(err)
}
func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *model.FileStream, up driver.UpdateProgress, lazyCache ...bool) error {
if storage.Config().CheckStatus && storage.GetStorage().Status != WORK {
return errors.Errorf("storage not init: %s", storage.GetStorage().Status)
}
defer func() {
if f, ok := file.GetReadCloser().(*os.File); ok {
err := os.RemoveAll(f.Name())
if err != nil {
log.Errorf("failed to remove file [%s]", f.Name())
}
}
}()
defer func() {
if err := file.Close(); err != nil {
log.Errorf("failed to close file streamer, %v", err)
}
}()
// if file exist and size = 0, delete it
dstDirPath = utils.FixAndCleanPath(dstDirPath)
dstPath := stdpath.Join(dstDirPath, file.GetName())
fi, err := GetUnwrap(ctx, storage, dstPath)
if err == nil {
if fi.GetSize() == 0 {
err = Remove(ctx, storage, dstPath)
if err != nil {
return errors.WithMessagef(err, "failed remove file that exist and have size 0")
}
} else {
file.Old = fi
}
}
err = MakeDir(ctx, storage, dstDirPath)
if err != nil {
return errors.WithMessagef(err, "failed to make dir [%s]", dstDirPath)
}
parentDir, err := GetUnwrap(ctx, storage, dstDirPath)
// this should not happen
if err != nil {
return errors.WithMessagef(err, "failed to get dir [%s]", dstDirPath)
}
// if up is nil, set a default to prevent panic
if up == nil {
up = func(p int) {}
}
switch s := storage.(type) {
case driver.PutResult:
var newObj model.Obj
newObj, err = s.Put(ctx, parentDir, file, up)
if err == nil {
if newObj != nil {
addCacheObj(storage, dstDirPath, model.WrapObjName(newObj))
} else if !utils.IsBool(lazyCache...) {
ClearCache(storage, dstDirPath)
}
}
case driver.Put:
err = s.Put(ctx, parentDir, file, up)
if err == nil && !utils.IsBool(lazyCache...) {
ClearCache(storage, dstDirPath)
}
default:
return errors.New("not implement")
}
log.Debugf("put file [%s] done", file.GetName())
//if err == nil {
// //clear cache
// key := stdpath.Join(storage.GetStorage().MountPath, dstDirPath)
// listCache.Del(key)
//}
return errors.WithStack(err)
}

48
main.go
View File

@@ -30,8 +30,7 @@ import (
"github.com/coreos/go-systemd/daemon"
"go.uber.org/zap"
_ "github.com/IceWhaleTech/CasaOS/drivers"
"github.com/robfig/cron"
"github.com/robfig/cron/v3"
"gorm.io/gorm"
)
@@ -79,8 +78,13 @@ func init() {
service.Cache = cache.Init()
service.GetCPUThermalZone()
service.MyService.Storages().InitStorages()
route.InitFunction()
///
// service.MountLists = make(map[string]*mountlib.MountPoint)
// configfile.Install()
service.MyService.Storage().CheckAndMountAll()
}
// @title casaOS API
@@ -115,18 +119,13 @@ func main() {
},
}
cron2 := cron.New()
// every day execution
err := cron2.AddFunc("0/5 * * * * *", func() {
route.SendAllHardwareStatusBySocket()
})
if err != nil {
fmt.Println(err)
crontab := cron.New(cron.WithSeconds())
if _, err := crontab.AddFunc("@every 5s", route.SendAllHardwareStatusBySocket); err != nil {
logger.Error("add crontab error", zap.Error(err))
}
cron2.Start()
defer cron2.Stop()
crontab.Start()
defer crontab.Stop()
listener, err := net.Listen("tcp", net.JoinHostPort(LOCALHOST, "0"))
if err != nil {
@@ -144,6 +143,7 @@ func main() {
"/v1/driver",
"/v1/cloud",
"/v1/recover",
"/v1/other",
route.V2APIPath,
route.V2DocPath,
route.V3FilePath,
@@ -164,14 +164,18 @@ func main() {
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
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)))
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() {
@@ -220,7 +224,7 @@ func main() {
}
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)
if err != nil {
panic(err)

View File

@@ -1,7 +1,9 @@
package model
type SearchFileInfo struct {
Path string `json:"path"`
Name string `json:"name"`
Type int `json:"type"`
type SearchEngine struct {
Name string `json:"name"`
Icon string `json:"icon"`
SearchUrl string `json:"search_url"`
RecoUrl string `json:"reco_url"`
Data []string `json:"data"`
}

View File

@@ -2,7 +2,7 @@ package model
import "time"
type Storage struct {
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
@@ -29,15 +29,15 @@ type Proxy struct {
DownProxyUrl string `json:"down_proxy_url"`
}
func (s *Storage) GetStorage() *Storage {
func (s *StorageA) GetStorage() *StorageA {
return s
}
func (s *Storage) SetStorage(storage Storage) {
func (s *StorageA) SetStorage(storage StorageA) {
*s = storage
}
func (s *Storage) SetStatus(status string) {
func (s *StorageA) SetStatus(status string) {
s.Status = status
}

View File

@@ -11,14 +11,12 @@
package sqlite
import (
"fmt"
"time"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/glebarez/sqlite"
"go.uber.org/zap"
"gorm.io/gorm"
)
@@ -33,23 +31,24 @@ func GetDb(dbPath string) *gorm.DB {
// db, err := gorm.Open(mysql2.Open(dsn), &gorm.Config{})
file.IsNotExistMkDir(dbPath)
db, err := gorm.Open(sqlite.Open(dbPath+"/casaOS.db"), &gorm.Config{})
if err != nil {
panic("sqlite connect error")
}
c, _ := db.DB()
c.SetMaxIdleConns(10)
c.SetMaxOpenConns(1)
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
err = db.AutoMigrate(&model2.AppNotify{}, model2.SharesDBModel{}, model2.ConnectionsDBModel{}, model.Storage{})
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_friend")
db.Exec("DROP TABLE IF EXISTS o_person_download")
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
}

View File

@@ -14,14 +14,18 @@ func OnlyExec(cmdStr string) {
cmd := exec.Command("/bin/bash", "-c", cmdStr)
stdout, err := cmd.StdoutPipe()
if err != nil {
fmt.Println(err)
return
}
defer stdout.Close()
if err := cmd.Start(); err != nil {
fmt.Println(err)
return
}
cmd.Wait()
return
if err := cmd.Wait(); err != nil {
fmt.Println(err)
}
}
func ExecResultStrArray(cmdStr string) []string {
@@ -49,7 +53,11 @@ func ExecResultStrArray(cmdStr string) []string {
}
networklist = append(networklist, string(output))
}
cmd.Wait()
if err := cmd.Wait(); err != nil {
fmt.Println(err)
}
return networklist
}
@@ -68,11 +76,15 @@ func ExecResultStr(cmdStr string) string {
return ""
}
str, err := ioutil.ReadAll(stdout)
cmd.Wait()
if err != nil {
fmt.Println(err)
return ""
}
if err := cmd.Wait(); err != nil {
fmt.Println(err)
}
return string(str)
}

View File

@@ -1,19 +1,23 @@
package command
package command_test
import (
"os"
"testing"
"github.com/IceWhaleTech/CasaOS/pkg/utils/command"
"go.uber.org/goleak"
"gotest.tools/assert"
)
func TestExecuteScripts(t *testing.T) {
goleak.VerifyNone(t)
// make a temp directory
tmpDir, err := os.MkdirTemp("", "casaos-test-*")
assert.NilError(t, err)
defer os.RemoveAll(tmpDir)
ExecuteScripts(tmpDir)
command.ExecuteScripts(tmpDir)
// create a sample script under tmpDir
script := tmpDir + "/test.sh"
@@ -25,5 +29,5 @@ func TestExecuteScripts(t *testing.T) {
_, err = f.WriteString("#!/bin/bash\necho 123")
assert.NilError(t, err)
ExecuteScripts(tmpDir)
command.ExecuteScripts(tmpDir)
}

View File

@@ -2,6 +2,7 @@ package file
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
@@ -596,3 +597,146 @@ func NameAccumulation(name string, dir string) string {
}
}
}
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")
}

View File

@@ -3,9 +3,13 @@ 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)

View File

@@ -13,12 +13,13 @@ import (
)
type MountList struct {
MountPoints []struct {
MountPoint string `json:"MountPoint"`
Fs string `json:"Fs"`
Icon string `json:"Icon"`
Name string `json:"Name"`
} `json:"mountPoints"`
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"`
@@ -45,7 +46,7 @@ var DefaultTimeout = time.Second * 30
func NewRestyClient() *resty.Client {
unixSocket := "/tmp/rclone.sock"
unixSocket := "/var/run/rclone/rclone.sock"
transport := http.Transport{
Dial: func(_, _ string) (net.Conn, error) {
@@ -75,11 +76,13 @@ func GetMountList() (MountList, error) {
}
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
@@ -102,6 +105,7 @@ func Unmount(mountPoint string) error {
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
}

View File

@@ -10,21 +10,22 @@ import (
func IsIPv4(address string) bool {
return strings.Count(address, ":") < 2
}
func IsIPv6(address string) bool {
return strings.Count(address, ":") >= 2
}
//获取外网ip
// 获取外网ip
func GetExternalIPV4() string {
return httper2.Get("https://api.ipify.org", nil)
}
//获取外网ip
// 获取外网ip
func GetExternalIPV6() string {
return httper2.Get("https://api6.ipify.org", nil)
}
//获取本地ip
// 获取本地ip
func GetLoclIp() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
@@ -35,11 +36,11 @@ func GetLoclIp() string {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return "127.0.0.1"
}
func GetDeviceAllIP(port string) []string {
var address []string
addrs, err := net.InterfaceAddrs()
@@ -60,7 +61,6 @@ func HasLocalIP(ip net.IP) bool {
if ip.IsLoopback() {
return true
}
ip.String()
ip4 := ip.To4()
if ip4 == nil {

View File

@@ -4,23 +4,28 @@ import (
"fmt"
"net"
"testing"
"go.uber.org/goleak"
)
func TestGetExternalIPV4(t *testing.T) {
goleak.VerifyNone(t)
ipv4 := make(chan string)
go func() { ipv4 <- GetExternalIPV4() }()
fmt.Println(<-ipv4)
}
func TestGetExternalIPV6(t *testing.T) {
ipv6 := make(chan string)
go func() { ipv6 <- GetExternalIPV6() }()
fmt.Println(<-ipv6)
}
func TestGetLoclIp(t *testing.T) {
fmt.Println(GetLoclIp())
}
func TestHasLocalIP(t *testing.T) {
fmt.Println("dddd")
fmt.Println(HasLocalIP(net.ParseIP("192.168.2.10")))

View File

@@ -93,9 +93,4 @@ func InitNetworkMount() {
connection.Directories = strings.Join(directories, ",")
service.MyService.Connections().UpdateConnection(&connection)
}
err := service.MyService.Storage().CheckAndMountAll()
if err != nil {
logger.Error("mount storage err", zap.Any("err", err))
}
}

View File

@@ -92,22 +92,25 @@ func InitV1Router() *gin.Engine {
v1FileGroup.GET("/content", v1.GetFilerContent) // file/read
// File uploads need to be handled separately, and will not be modified here
//v1FileGroup.POST("/upload", v1.PostFileUpload)
v1FileGroup.POST("/upload", v1.PostFileUpload)
v1FileGroup.GET("/upload", v1.GetFileUpload)
// 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.DeleteStorage)
v1CloudGroup.DELETE("", v1.UmountStorage)
}
v1DriverGroup := v1Group.Group("/driver")
v1DriverGroup.Use()
{
v1DriverGroup.GET("", v1.ListDriverInfo)
}
v1FolderGroup := v1Group.Group("/folder")
v1FolderGroup.Use()
{
@@ -115,6 +118,7 @@ func InitV1Router() *gin.Engine {
v1FolderGroup.GET("", v1.DirPath) ///file/dirpath
v1FolderGroup.POST("", v1.MkdirAll) ///file/mkdir
v1FolderGroup.GET("/size", v1.GetSize)
v1FolderGroup.GET("/count", v1.GetFileCount)
}
v1BatchGroup := v1Group.Group("/batch")
v1BatchGroup.Use()
@@ -156,6 +160,13 @@ func InitV1Router() *gin.Engine {
// merge to system
v1NotifyGroup.POST("/system_status", v1.PostSystemStatusNotify)
}
v1OtherGroup := v1Group.Group("/other")
v1OtherGroup.Use()
{
v1OtherGroup.GET("/search", v1.GetSearchResult)
}
}
return r

View File

@@ -1,7 +1,6 @@
package v1
import (
"strconv"
"strings"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
@@ -68,20 +67,7 @@ func ListStorages(c *gin.Context) {
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: list})
}
func UpdateStorage(c *gin.Context) {
var req model.Storage
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
}
if err := service.MyService.Storages().UpdateStorage(c, req); 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()})
} else {
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: "success"})
}
}
func DeleteStorage(c *gin.Context) {
func UmountStorage(c *gin.Context) {
json := make(map[string]string)
c.ShouldBind(&json)
mountPoint := json["mount_point"]
@@ -98,34 +84,6 @@ func DeleteStorage(c *gin.Context) {
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: "success"})
}
func DisableStorage(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
}
if err := service.MyService.Storages().DisableStorage(c, uint(id)); 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: "success"})
}
func EnableStorage(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
}
if err := service.MyService.Storages().EnableStorage(c, uint(id)); 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: "success"})
}
func GetStorage(c *gin.Context) {
// idStr := c.Query("id")

View File

@@ -1,9 +1,9 @@
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/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/gin-gonic/gin"
)

View File

@@ -1,7 +1,7 @@
package v1
import (
"errors"
"encoding/json"
"fmt"
"io"
"io/ioutil"
@@ -15,18 +15,19 @@ import (
"strconv"
"strings"
"sync"
"time"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/internal/conf"
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/gorilla/websocket"
"github.com/robfig/cron/v3"
"github.com/tidwall/gjson"
"github.com/IceWhaleTech/CasaOS/pkg/utils"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
"github.com/IceWhaleTech/CasaOS/service"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/IceWhaleTech/CasaOS/internal/sign"
"github.com/gin-gonic/gin"
uuid "github.com/satori/go.uuid"
"go.uber.org/zap"
@@ -34,6 +35,45 @@ import (
"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 读取文件
// @Produce application/json
// @Accept application/json
@@ -563,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)})
}
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
// @Produce application/json
// @Accept application/json
@@ -766,168 +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})
}
func Proxy(c *gin.Context) {
rawPath := c.Query("path")
filename := filepath.Base(rawPath)
storage, err := service.MyService.FsService().GetStorage(rawPath)
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(500, 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
}
if canProxy(storage, filename) {
downProxyUrl := storage.GetStorage().DownProxyUrl
if downProxyUrl != "" {
_, ok := c.GetQuery("d")
if !ok {
URL := fmt.Sprintf("%s%s?sign=%s",
strings.Split(downProxyUrl, "\n")[0],
utils.EncodePath(rawPath, true),
sign.Sign(rawPath))
c.Redirect(302, URL)
return
}
}
link, file, err := service.MyService.FsService().Link(c, rawPath, model.LinkArgs{
Header: c.Request.Header,
Type: c.Query("type"),
})
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.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: len(list)})
}
return
}
err = CommonProxy(c.Writer, c.Request, link, file)
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
}
} else {
c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: "proxy not allowed"})
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)
}
// TODO need optimize
// when should be proxy?
// 1. config.MustProxy()
// 2. storage.WebProxy
// 3. proxy_types
func shouldProxy(storage driver.Driver, filename string) bool {
if storage.Config().MustProxy() || storage.GetStorage().WebProxy {
return true
cookie := http.Cookie{
Name: "peerid",
Value: key,
Path: "/",
}
if utils.SliceContains(conf.SlicesMap[conf.ProxyTypes], utils.Ext(filename)) {
return true
}
return false
}
// TODO need optimize
// when can be proxy?
// 1. text file
// 2. config.MustProxy()
// 3. storage.WebProxy
// 4. proxy_types
// solution: text_file + shouldProxy()
func canProxy(storage driver.Driver, filename string) bool {
if storage.Config().MustProxy() || storage.GetStorage().WebProxy || storage.GetStorage().WebdavProxy() {
return true
}
if utils.SliceContains(conf.SlicesMap[conf.ProxyTypes], utils.Ext(filename)) {
return true
}
if utils.SliceContains(conf.SlicesMap[conf.TextTypes], utils.Ext(filename)) {
return true
}
return false
}
var HttpClient = &http.Client{}
func CommonProxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.Obj) error {
// read data with native
var err error
if link.Data != nil {
defer func() {
_ = link.Data.Close()
}()
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, file.GetName(), url.QueryEscape(file.GetName())))
w.Header().Set("Content-Length", strconv.FormatInt(file.GetSize(), 10))
if link.Header != nil {
// TODO clean header with blacklist or whitelist
link.Header.Del("set-cookie")
for h, val := range link.Header {
w.Header()[h] = val
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 link.Status == 0 {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(link.Status)
}
_, err = io.Copy(w, link.Data)
if err != nil {
return err
}
return nil
// 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
// }
}
// local file
if link.FilePath != nil && *link.FilePath != "" {
f, err := os.Open(*link.FilePath)
if err != nil {
return err
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})
}
defer func() {
_ = f.Close()
}()
fileStat, err := os.Stat(*link.FilePath)
if err != nil {
return err
}
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
}
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, file.GetName(), url.QueryEscape(file.GetName())))
http.ServeContent(w, r, file.GetName(), fileStat.ModTime(), f)
return nil
} else {
req, err := http.NewRequest(link.Method, link.URL, nil)
}
}
// 读,监听客户端是否有推送内容过来服务端
func (c *Client) readPump() {
defer func() {
c.handler.unregister <- c
c.conn.Close()
}()
for {
// 循环监听是否该用户是否要发言
_, message, err := c.conn.ReadMessage()
if err != nil {
return err
// 异常关闭的处理
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
log.Printf("error: %v", err)
}
c.handler.broadcast <- []byte(`{"type":"peer-left","peerId":"` + c.ID + `"}`)
break
}
for h, val := range r.Header {
if utils.SliceContains(conf.SlicesMap[conf.ProxyIgnoreHeaders], strings.ToLower(h)) {
// 要的话,推给广播中心,广播中心再推给每个用户
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
}
req.Header[h] = val
data := map[string]interface{}{}
json.Unmarshal(message, &data)
data["sender"] = c.ID
delete(data, "to")
message, err = json.Marshal(data)
toC.send <- message
continue
}
for h, val := range link.Header {
req.Header[h] = val
}
res, err := HttpClient.Do(req)
if err != nil {
return err
}
defer func() {
_ = res.Body.Close()
}()
logger.Info("proxy status", zap.Any("status", res.StatusCode))
// TODO clean header with blacklist or whitelist
res.Header.Del("set-cookie")
for h, v := range res.Header {
w.Header()[h] = v
}
w.WriteHeader(res.StatusCode)
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, file.GetName(), url.QueryEscape(file.GetName())))
if res.StatusCode >= 400 {
all, _ := ioutil.ReadAll(res.Body)
msg := string(all)
logger.Info("msg", zap.Any("msg", msg))
return errors.New(msg)
}
_, err = io.Copy(w, res.Body)
if err != nil {
return err
}
return nil
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})
}

View File

@@ -1,97 +0,0 @@
package v1
import (
"path/filepath"
"time"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
"github.com/IceWhaleTech/CasaOS/service"
"github.com/gin-gonic/gin"
)
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"`
}
func FsList(c *gin.Context) {
var req ListReq
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()
objs, err := service.MyService.FsService().FList(c, req.Path, false)
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
}
total, objs := pagination(objs, &req.PageReq)
provider := "unknown"
storage, err := service.MyService.FsService().GetStorage(req.Path)
if err == nil {
provider = storage.GetStorage().Driver
}
c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS), Data: FsListResp{
Content: toObjsResp(objs, req.Path, false),
Total: int64(total),
Readme: "",
Write: false,
Provider: provider,
}})
}
func pagination(objs []model.Obj, req *model.PageReq) (int, []model.Obj) {
pageIndex, pageSize := req.Index, req.Size
total := len(objs)
start := (pageIndex - 1) * pageSize
if start > total {
return total, []model.Obj{}
}
end := start + pageSize
if end > total {
end = total
}
return total, objs[start:end]
}
func toObjsResp(objs []model.Obj, parent string, encrypt bool) []ObjResp {
var resp []ObjResp
for _, obj := range objs {
thumb, _ := model.GetThumb(obj)
resp = append(resp, ObjResp{
Name: obj.GetName(),
Size: obj.GetSize(),
IsDir: obj.IsDir(),
Modified: obj.ModTime(),
Sign: "",
Path: filepath.Join(parent, obj.GetName()),
Thumb: thumb,
Type: 0,
})
}
return resp
}

30
route/v1/other.go Normal file
View 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})
}

View File

@@ -26,6 +26,7 @@ func GetRecoverStorage(c *gin.Context) {
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
}
@@ -41,6 +42,7 @@ func GetRecoverStorage(c *gin.Context) {
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
}
@@ -50,6 +52,7 @@ func GetRecoverStorage(c *gin.Context) {
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
}
@@ -57,9 +60,10 @@ func GetRecoverStorage(c *gin.Context) {
dmap["username"] = username
configs, err := service.MyService.Storage().GetConfig()
if err != nil {
c.String(200, `<p>Failed to get user information:`+err.Error()+`</p><script>window.close()</script>`)
c.String(200, `<p>Failed to get rclone config:`+err.Error()+`</p><script>window.close()</script>`)
notify["status"] = "fail"
notify["message"] = "Failed to get user information"
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
}
@@ -99,6 +103,7 @@ func GetRecoverStorage(c *gin.Context) {
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{}
@@ -107,6 +112,7 @@ func GetRecoverStorage(c *gin.Context) {
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
}
@@ -120,6 +126,7 @@ func GetRecoverStorage(c *gin.Context) {
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
}
@@ -128,6 +135,7 @@ func GetRecoverStorage(c *gin.Context) {
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
}
@@ -136,9 +144,10 @@ func GetRecoverStorage(c *gin.Context) {
configs, err := service.MyService.Storage().GetConfig()
if err != nil {
c.String(200, `<p>Failed to get user information:`+err.Error()+`</p><script>window.close()</script>`)
c.String(200, `<p>Failed to get rclone config:`+err.Error()+`</p><script>window.close()</script>`)
notify["status"] = "fail"
notify["message"] = "Failed to get user information"
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
}
@@ -188,6 +197,7 @@ func GetRecoverStorage(c *gin.Context) {
notify["status"] = "success"
notify["message"] = "Success"
notify["driver"] = "Dropbox"
service.MyService.Notify().SendNotify("casaos:file:recover", notify)
}

View File

@@ -199,18 +199,23 @@ 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)})
return
}
mountPointList, err := service.MyService.System().GetDirPath(connection.MountPoint)
mountPointList, err := samba.GetSambaSharesList(connection.Host, connection.Port, connection.Username, connection.Password)
//mountPointList, err := service.MyService.System().GetDirPath(connection.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
}
baseHostPath := "/mnt/" + connection.Host
for _, v := range mountPointList {
err := service.MyService.Connections().UnmountSmaba(v.Path)
if err != nil {
logger.Error("unmount smaba error", zap.Error(err), zap.Any("path", v.Path))
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
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 {

View File

@@ -8,16 +8,17 @@
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package v1
package v1_test
import (
"net/http"
"net/http/httptest"
"testing"
v1 "github.com/IceWhaleTech/CasaOS/route/v1"
"github.com/gin-gonic/gin"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"gotest.tools/assert"
)
func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
@@ -62,7 +63,7 @@ func TestGetSambaSharesList(t *testing.T) {
requestUrl := "/v1/samba/shares"
httpRequest, _ := http.NewRequest("GET", requestUrl, nil)
GetSambaSharesList(con)
v1.GetSambaSharesList(con)
ginEngine.ServeHTTP(response, httpRequest)
return response
}

View File

@@ -178,12 +178,16 @@ func GetSystemHardwareInfo(c *gin.Context) {
data := make(map[string]string, 1)
data["drive_model"] = service.MyService.System().GetDeviceTree()
data["arch"] = runtime.GOARCH
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
Data: data,
})
if cpu := service.MyService.System().GetCpuInfo(); len(cpu) > 0 {
c.JSON(common_err.SUCCESS,
model.Result{
Success: common_err.SUCCESS,
Message: common_err.GetMsg(common_err.SUCCESS),
Data: data,
})
}
}
// @Summary system utilization

View File

@@ -4,6 +4,7 @@ import (
"log"
"net/http"
"net/url"
"path"
"path/filepath"
"strconv"
"strings"
@@ -138,8 +139,10 @@ func InitV2DocRouter(docHTML string, docYAML string) http.Handler {
}
func InitFile() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := r.URL.Query().Get("path")
http.ServeFile(w, r, path)
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)
})
}

View File

@@ -1,9 +0,0 @@
package service
import (
"testing"
)
func TestGetCasaOSCount(t *testing.T) {
}

View File

@@ -1,154 +0,0 @@
package service
import (
"context"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/model"
log "github.com/dsoprea/go-logging"
"go.uber.org/zap"
)
type FsService interface {
FList(ctx context.Context, path string, refresh ...bool) ([]model.Obj, error)
GetStorage(path string) (driver.Driver, error)
Link(ctx context.Context, path string, args model.LinkArgs) (*model.Link, model.Obj, error)
}
type fsService struct {
}
// the param named path of functions in this package is a mount path
// So, the purpose of this package is to convert mount path to actual path
// then pass the actual path to the op package
func (f *fsService) FList(ctx context.Context, path string, refresh ...bool) ([]model.Obj, error) {
res, err := MyService.FsListService().FsList(ctx, path, refresh...)
if err != nil {
logger.Info("failed list", zap.Any("path", path), zap.Any("err", err))
return nil, err
}
return res, nil
}
// func (f *fsService) Get(ctx context.Context, path string) (model.Obj, error) {
// res, err := get(ctx, path)
// if err != nil {
// log.Errorf("failed get %s: %+v", path, err)
// return nil, err
// }
// return res, nil
// }
func (f *fsService) Link(ctx context.Context, path string, args model.LinkArgs) (*model.Link, model.Obj, error) {
res, file, err := MyService.FsLinkService().Link(ctx, path, args)
if err != nil {
log.Errorf("failed link %s: %+v", path, err)
return nil, nil, err
}
return res, file, nil
}
// func (f *fsService) MakeDir(ctx context.Context, path string, lazyCache ...bool) error {
// err := makeDir(ctx, path, lazyCache...)
// if err != nil {
// log.Errorf("failed make dir %s: %+v", path, err)
// }
// return err
// }
// func (f *fsService) Move(ctx context.Context, srcPath, dstDirPath string, lazyCache ...bool) error {
// err := move(ctx, srcPath, dstDirPath, lazyCache...)
// if err != nil {
// log.Errorf("failed move %s to %s: %+v", srcPath, dstDirPath, err)
// }
// return err
// }
// func (f *fsService) Copy(ctx context.Context, srcObjPath, dstDirPath string, lazyCache ...bool) (bool, error) {
// res, err := _copy(ctx, srcObjPath, dstDirPath, lazyCache...)
// if err != nil {
// log.Errorf("failed copy %s to %s: %+v", srcObjPath, dstDirPath, err)
// }
// return res, err
// }
// func (f *fsService) Rename(ctx context.Context, srcPath, dstName string, lazyCache ...bool) error {
// err := rename(ctx, srcPath, dstName, lazyCache...)
// if err != nil {
// log.Errorf("failed rename %s to %s: %+v", srcPath, dstName, err)
// }
// return err
// }
// func (f *fsService) Remove(ctx context.Context, path string) error {
// err := remove(ctx, path)
// if err != nil {
// log.Errorf("failed remove %s: %+v", path, err)
// }
// return err
// }
// func PutDirectly(ctx context.Context, dstDirPath string, file *model.FileStream, lazyCache ...bool) error {
// err := putDirectly(ctx, dstDirPath, file, lazyCache...)
// if err != nil {
// log.Errorf("failed put %s: %+v", dstDirPath, err)
// }
// return err
// }
// func (f *fsService) PutAsTask(dstDirPath string, file *model.FileStream) error {
// err := putAsTask(dstDirPath, file)
// if err != nil {
// log.Errorf("failed put %s: %+v", dstDirPath, err)
// }
// return err
// }
func (f *fsService) GetStorage(path string) (driver.Driver, error) {
storageDriver, _, err := MyService.StoragePath().GetStorageAndActualPath(path)
if err != nil {
return nil, err
}
return storageDriver, nil
}
// func (f *fsService) Other(ctx context.Context, args model.FsOtherArgs) (interface{}, error) {
// res, err := other(ctx, args)
// if err != nil {
// log.Errorf("failed remove %s: %+v", args.Path, err)
// }
// return res, err
// }
// func get(ctx context.Context, path string) (model.Obj, error) {
// path = utils.FixAndCleanPath(path)
// // maybe a virtual file
// if path != "/" {
// virtualFiles := op.GetStorageVirtualFilesByPath(stdpath.Dir(path))
// for _, f := range virtualFiles {
// if f.GetName() == stdpath.Base(path) {
// return f, nil
// }
// }
// }
// storage, actualPath, err := op.GetStorageAndActualPath(path)
// if err != nil {
// // if there are no storage prefix with path, maybe root folder
// if path == "/" {
// return &model.Object{
// Name: "root",
// Size: 0,
// Modified: time.Time{},
// IsFolder: true,
// }, nil
// }
// return nil, errors.WithMessage(err, "failed get storage")
// }
// return op.Get(ctx, storage, actualPath)
// }
func NewFsService() FsService {
return &fsService{}
}

View File

@@ -1,27 +0,0 @@
package service
import (
"context"
"github.com/IceWhaleTech/CasaOS/internal/op"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/pkg/errors"
)
type FsLinkService interface {
Link(ctx context.Context, path string, args model.LinkArgs) (*model.Link, model.Obj, error)
}
type fsLinkService struct {
}
func (f *fsLinkService) Link(ctx context.Context, path string, args model.LinkArgs) (*model.Link, model.Obj, error) {
storage, actualPath, err := MyService.StoragePath().GetStorageAndActualPath(path)
if err != nil {
return nil, nil, errors.WithMessage(err, "failed get storage")
}
return op.Link(ctx, storage, actualPath, args)
}
func NewFsLinkService() FsLinkService {
return &fsLinkService{}
}

View File

@@ -1,198 +0,0 @@
package service
import (
"context"
stdpath "path"
"time"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/internal/op"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/singleflight"
"github.com/IceWhaleTech/CasaOS/pkg/utils"
"github.com/Xhofe/go-cache"
log "github.com/dsoprea/go-logging"
"github.com/pkg/errors"
"go.uber.org/zap"
)
type FsListService interface {
FsList(ctx context.Context, path string, refresh ...bool) ([]model.Obj, error)
Key(storage driver.Driver, path string) string
Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, error)
GetUnwrap(ctx context.Context, storage driver.Driver, path string) (model.Obj, error)
List(ctx context.Context, storage driver.Driver, path string, args model.ListArgs, refresh ...bool) ([]model.Obj, error)
}
type fsListService struct {
}
var listCache = cache.NewMemCache(cache.WithShards[[]model.Obj](64))
var listG singleflight.Group[[]model.Obj]
// List files
func (fl *fsListService) FsList(ctx context.Context, path string, refresh ...bool) ([]model.Obj, error) {
virtualFiles := MyService.Storages().GetStorageVirtualFilesByPath(path)
storage, actualPath, err := MyService.StoragePath().GetStorageAndActualPath(path)
if err != nil && len(virtualFiles) == 0 {
return nil, errors.WithMessage(err, "failed get storage")
}
var _objs []model.Obj
if storage != nil {
_objs, err = fl.List(ctx, storage, actualPath, model.ListArgs{
ReqPath: path,
}, refresh...)
if err != nil {
log.Errorf("%+v", err)
if len(virtualFiles) == 0 {
return nil, errors.WithMessage(err, "failed get objs")
}
}
}
om := model.NewObjMerge()
objs := om.Merge(virtualFiles, _objs...)
return objs, nil
}
func (fl *fsListService) Key(storage driver.Driver, path string) string {
return stdpath.Join(storage.GetStorage().MountPath, utils.FixAndCleanPath(path))
}
// Get object from list of files
func (fl *fsListService) Get(ctx context.Context, storage driver.Driver, path string) (model.Obj, error) {
path = utils.FixAndCleanPath(path)
logger.Info("get", zap.String("path", path))
// is root folder
if utils.PathEqual(path, "/") {
var rootObj model.Obj
switch r := storage.GetAddition().(type) {
case driver.IRootId:
rootObj = &model.Object{
ID: r.GetRootId(),
Name: op.RootName,
Size: 0,
Modified: storage.GetStorage().Modified,
IsFolder: true,
}
case driver.IRootPath:
rootObj = &model.Object{
Path: r.GetRootPath(),
Name: op.RootName,
Size: 0,
Modified: storage.GetStorage().Modified,
IsFolder: true,
}
default:
if storage, ok := storage.(driver.Getter); ok {
obj, err := storage.GetRoot(ctx)
if err != nil {
return nil, errors.WithMessage(err, "failed get root obj")
}
rootObj = obj
}
}
if rootObj == nil {
return nil, errors.Errorf("please implement IRootPath or IRootId or Getter method")
}
return &model.ObjWrapName{
Name: op.RootName,
Obj: rootObj,
}, nil
}
// not root folder
dir, name := stdpath.Split(path)
files, err := fl.List(ctx, storage, dir, model.ListArgs{})
if err != nil {
return nil, errors.WithMessage(err, "failed get parent list")
}
for _, f := range files {
// TODO maybe copy obj here
if f.GetName() == name {
return f, nil
}
}
logger.Info("cant find obj with name", zap.Any("name", name))
return nil, errors.WithStack(errors.New("object not found"))
}
func (fl *fsListService) GetUnwrap(ctx context.Context, storage driver.Driver, path string) (model.Obj, error) {
obj, err := fl.Get(ctx, storage, path)
if err != nil {
return nil, err
}
return model.UnwrapObjs(obj), err
}
// List files in storage, not contains virtual file
func (fl *fsListService) List(ctx context.Context, storage driver.Driver, path string, args model.ListArgs, refresh ...bool) ([]model.Obj, error) {
if storage.Config().CheckStatus && storage.GetStorage().Status != op.WORK {
return nil, errors.Errorf("storage not init: %s", storage.GetStorage().Status)
}
path = utils.FixAndCleanPath(path)
logger.Info("op.List", zap.Any("path", path))
key := fl.Key(storage, path)
if !utils.IsBool(refresh...) {
if files, ok := listCache.Get(key); ok {
logger.Info("op.List", zap.Any("use cache", path))
return files, nil
}
}
dir, err := fl.GetUnwrap(ctx, storage, path)
if err != nil {
return nil, errors.WithMessage(err, "failed get dir")
}
logger.Info("op.List", zap.Any("dir", dir))
if !dir.IsDir() {
return nil, errors.WithStack(errors.New("not a folder"))
}
objs, err, _ := listG.Do(key, func() ([]model.Obj, error) {
files, err := storage.List(ctx, dir, args)
if err != nil {
return nil, errors.Wrapf(err, "failed to list objs")
}
// set path
for _, f := range files {
if s, ok := f.(model.SetPath); ok && f.GetPath() == "" && dir.GetPath() != "" {
s.SetPath(stdpath.Join(dir.GetPath(), f.GetName()))
}
}
// warp obj name
model.WrapObjsName(files)
// call hooks
go func(reqPath string, files []model.Obj) {
for _, hook := range op.ObjsUpdateHooks {
hook(args.ReqPath, files)
}
}(args.ReqPath, files)
// sort objs
if storage.Config().LocalSort {
model.SortFiles(files, storage.GetStorage().OrderBy, storage.GetStorage().OrderDirection)
}
model.ExtractFolder(files, storage.GetStorage().ExtractFolder)
if !storage.Config().NoCache {
if len(files) > 0 {
logger.Info("set cache", zap.Any("key", key), zap.Any("files", files))
listCache.Set(key, files, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
} else {
logger.Info("del cache", zap.Any("key", key))
listCache.Del(key)
}
}
return files, nil
})
return objs, err
}
func NewFsListService() FsListService {
return &fsListService{}
}

16
service/model/o_drive.go Normal file
View File

@@ -0,0 +1,16 @@
package model
type PeerDriveDBModel struct {
ID string `gorm:"column:id;primary_key" json:"id"`
Updated int64 `gorm:"autoUpdateTime"`
Created int64 `gorm:"autoCreateTime"`
UserAgent string `json:"user_agent"`
DisplayName string `json:"display_name"`
DeviceName string `json:"device_name"`
Model string `json:"model"`
IP string `json:"ip"`
OS string `json:"os"`
Browser string `json:"browser"`
Online bool `gorm:"-" json:"online"`
}

116
service/other.go Normal file
View File

@@ -0,0 +1,116 @@
package service
import (
"net/url"
"strings"
"sync"
"time"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/go-resty/resty/v2"
"github.com/tidwall/gjson"
"go.uber.org/zap"
)
type OtherService interface {
Search(key string) ([]model.SearchEngine, error)
AgentSearch(url string) ([]byte, error)
}
type otherService struct{}
func (s *otherService) Search(key string) ([]model.SearchEngine, error) {
engines := []model.SearchEngine{}
engines = append(engines, model.SearchEngine{
Name: "bing",
Icon: "https://files.codelife.cc/itab/search/bing.svg",
SearchUrl: "https://www.bing.com/search?q=",
RecoUrl: "https://www.bing.com/osjson.aspx?query=", // + keyword
}, model.SearchEngine{
Name: "google",
Icon: "https://files.codelife.cc/itab/search/google.svg",
SearchUrl: "https://www.google.com/search?q=",
RecoUrl: "https://www.google.com/complete/search?client=gws-wiz&xssi=t&hl=en-US&authuser=0&dpr=1&q=", // + keyword
}, model.SearchEngine{
Name: "baidu",
Icon: "https://files.codelife.cc/itab/search/baidu.svg",
SearchUrl: "https://www.baidu.com/s?wd=",
RecoUrl: "https://www.baidu.com/sugrec?json=1&prod=pc&wd=", // + keyword
}, model.SearchEngine{
Name: "duckduckgo",
Icon: "https://files.codelife.cc/itab/search/duckduckgo.svg",
SearchUrl: "https://duckduckgo.com/?q=",
RecoUrl: "https://duckduckgo.com/ac/?type=list&q=", // + keyword
}, model.SearchEngine{
Name: "startpage",
Icon: "https://www.startpage.com/sp/cdn/favicons/apple-touch-icon-60x60--default.png",
SearchUrl: "https://www.startpage.com/do/search?q=",
RecoUrl: "https://www.startpage.com/suggestions?segment=startpage.udog&lui=english&q=", // + keyword
})
client := resty.New()
client.SetTimeout(3 * time.Second) // 设置全局超时时间
var wg sync.WaitGroup
for i := 0; i < len(engines); i++ {
wg.Add(1)
go func(i int, k string) {
name := engines[i].Name
url := engines[i].RecoUrl + url.QueryEscape(k)
defer wg.Done()
resp, err := client.R().Get(url)
if err != nil {
logger.Error("Then get search result error: %v", zap.Error(err), zap.String("name", name), zap.String("url", url))
return
}
res := []string{}
if name == "bing" {
r := gjson.Get(resp.String(), "1")
for _, v := range r.Array() {
res = append(res, v.String())
}
} else if name == "google" {
r := gjson.Get(strings.Replace(resp.String(), ")]}'", "", 1), "0.#.0")
for _, v := range r.Array() {
res = append(res, strings.ReplaceAll(strings.ReplaceAll(v.String(), "<b>", " "), "</b>", ""))
}
} else if name == "baidu" {
r := gjson.Get(resp.String(), "g.#.q")
for _, v := range r.Array() {
res = append(res, v.String())
}
} else if name == "duckduckgo" {
r := gjson.Get(resp.String(), "1")
for _, v := range r.Array() {
res = append(res, v.String())
}
} else if name == "startpage" {
r := gjson.Get(resp.String(), "suggestions.#.text")
for _, v := range r.Array() {
res = append(res, v.String())
}
}
engines[i].Data = res
}(i, key)
}
wg.Wait()
return engines, nil
}
func (s *otherService) AgentSearch(url string) ([]byte, error) {
client := resty.New()
client.SetTimeout(3 * time.Second) // 设置全局超时时间
resp, err := client.R().Get(url)
if err != nil {
logger.Error("Then get search result error: %v", zap.Error(err), zap.String("url", url))
return nil, err
}
return resp.Body(), nil
}
func NewOtherService() OtherService {
return &otherService{}
}

17
service/other_test.go Normal file
View File

@@ -0,0 +1,17 @@
package service
import (
"testing"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"go.uber.org/goleak"
)
func TestSearch(t *testing.T) {
logger.LogInitConsoleOnly()
goleak.VerifyNone(t)
if d, e := NewOtherService().Search("test"); e != nil || d == nil {
t.Error("then test search error", e)
}
}

59
service/peer.go Normal file
View File

@@ -0,0 +1,59 @@
/*
* @Author: LinkLeong link@icewhale.org
* @Date: 2022-07-26 18:13:22
* @LastEditors: LinkLeong
* @LastEditTime: 2022-08-04 20:10:31
* @FilePath: /CasaOS/service/connections.go
* @Description:
* @Website: https://www.casaos.io
* Copyright (c) 2022 by icewhale, All Rights Reserved.
*/
package service
import (
"github.com/IceWhaleTech/CasaOS/service/model"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"gorm.io/gorm"
)
type PeerService interface {
GetPeerByUserAgent(ua string) (m model2.PeerDriveDBModel)
GetPeerByID(id string) (m model2.PeerDriveDBModel)
GetPeers() (peers []model2.PeerDriveDBModel)
CreatePeer(m *model2.PeerDriveDBModel)
DeletePeer(id string)
GetPeerByName(name string) (m model2.PeerDriveDBModel)
}
type peerStruct struct {
db *gorm.DB
}
func (s *peerStruct) GetPeerByName(name string) (m model2.PeerDriveDBModel) {
s.db.Where("display_name = ?", name).First(&m)
return
}
func (s *peerStruct) GetPeerByUserAgent(ua string) (m model2.PeerDriveDBModel) {
s.db.Where("user_agent = ?", ua).First(&m)
return
}
func (s *peerStruct) GetPeerByID(id string) (m model2.PeerDriveDBModel) {
s.db.Where("id = ?", id).First(&m)
return
}
func (s *peerStruct) GetPeers() (peers []model2.PeerDriveDBModel) {
s.db.Order("updated desc").Find(&peers)
return
}
func (s *peerStruct) CreatePeer(m *model2.PeerDriveDBModel) {
s.db.Create(m)
}
func (s *peerStruct) DeletePeer(id string) {
s.db.Where("id= ?", id).Delete(&model.PeerDriveDBModel{})
}
func NewPeerService(db *gorm.DB) PeerService {
return &peerStruct{db: db}
}

View File

@@ -40,12 +40,9 @@ type Repository interface {
Shares() SharesService
System() SystemService
Storage() StorageService
Storages() StoragesService
StoragePath() StoragePathService
FsListService() FsListService
FsLinkService() FsLinkService
FsService() FsService
MessageBus() *message_bus.ClientWithResponses
Peer() PeerService
Other() OtherService
}
func NewService(db *gorm.DB, RuntimePath string) Repository {
@@ -55,64 +52,48 @@ func NewService(db *gorm.DB, RuntimePath string) Repository {
}
return &store{
casa: NewCasaService(),
connections: NewConnectionsService(db),
gateway: gatewayManagement,
notify: NewNotifyService(db),
rely: NewRelyService(db),
system: NewSystemService(),
health: NewHealthService(),
shares: NewSharesService(db),
storage: NewStorageService(),
storages: NewStoragesService(),
storage_path: NewStoragePathService(),
fs_list: NewFsListService(),
fs_link: NewFsLinkService(),
fs: NewFsService(),
casa: NewCasaService(),
connections: NewConnectionsService(db),
gateway: gatewayManagement,
notify: NewNotifyService(db),
rely: NewRelyService(db),
system: NewSystemService(),
health: NewHealthService(),
shares: NewSharesService(db),
storage: NewStorageService(),
other: NewOtherService(),
peer: NewPeerService(db),
}
}
type store struct {
db *gorm.DB
casa CasaService
notify NotifyServer
rely RelyService
system SystemService
shares SharesService
connections ConnectionsService
gateway external.ManagementService
storage StorageService
storages StoragesService
storage_path StoragePathService
fs_list FsListService
fs_link FsLinkService
fs FsService
health HealthService
}
func (c *store) FsLinkService() FsLinkService {
return c.fs_link
}
func (c *store) FsService() FsService {
return c.fs
}
func (c *store) FsListService() FsListService {
return c.fs_list
}
func (c *store) StoragePath() StoragePathService {
return c.storage_path
}
func (c *store) Storages() StoragesService {
return c.storages
peer PeerService
db *gorm.DB
casa CasaService
notify NotifyServer
rely RelyService
system SystemService
shares SharesService
connections ConnectionsService
gateway external.ManagementService
storage StorageService
health HealthService
other OtherService
}
func (c *store) Storage() StorageService {
return c.storage
}
func (c *store) Peer() PeerService {
return c.peer
}
func (c *store) Other() OtherService {
return c.other
}
func (c *store) Gateway() external.ManagementService {
return c.gateway
}

View File

@@ -82,7 +82,7 @@ read only = No
guest ok = Yes
create mask = 0777
directory mask = 0777
guest account = root
force user = root
`
}

102
service/socket.go Normal file
View File

@@ -0,0 +1,102 @@
package service
import (
"net/http"
"strconv"
"strings"
model2 "github.com/IceWhaleTech/CasaOS/service/model"
"github.com/mileusna/useragent"
)
type Name struct {
Model string `json:"model"`
OS string `json:"os"`
Browser string `json:"browser"`
DeviceName string `json:"deviceName"`
DisplayName string `json:"displayName"`
}
func GetPeerId(request *http.Request, id string) string {
cookiePree, err := request.Cookie("peerid")
if err != nil {
return id
}
if len(cookiePree.Value) > 0 {
return cookiePree.Value
}
return id
}
func GetIP(request *http.Request) string {
ip := ""
if len(request.Header.Get("x-forwarded-for")) > 0 {
ip = strings.Split(request.Header.Get("x-forwarded-for"), ",")[0]
} else {
ip = request.RemoteAddr
}
if ip == "::1" || ip == "::ffff:127.0.0.1" {
ip = "127.0.0.1"
}
return ip
}
func GetName(request *http.Request) Name {
us := useragent.Parse(request.Header.Get("user-agent"))
device := ""
if len(us.Device) > 0 {
device += us.Device
} else {
device += us.Name
}
display := ""
if len(us.Device) > 0 {
display = us.Device + " " + us.Name
} else {
display = us.OS + " " + us.Name
}
model := "desktop"
if us.Mobile {
model = "mobile"
}
if us.Tablet {
model = "tablet"
}
peer := MyService.Peer().GetPeerByName(display)
if len(peer.ID) > 0 {
for i := 0; true; i++ {
peer = MyService.Peer().GetPeerByName(display + "_" + strconv.Itoa(i+1))
if len(peer.ID) == 0 {
display = display + "_" + strconv.Itoa(i+1)
break
}
}
}
return Name{
Model: model,
OS: us.OS,
Browser: us.Name,
DeviceName: device,
DisplayName: display,
}
}
func GetNameByDB(m model2.PeerDriveDBModel) Name {
device := ""
if len(m.DeviceName) > 0 {
device += m.DeviceName
} else {
device += m.Browser
}
return Name{
Model: m.Model,
OS: m.OS,
Browser: m.Browser,
DeviceName: device,
DisplayName: m.DisplayName,
}
}

View File

@@ -1,73 +0,0 @@
package service
import (
"fmt"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/pkg/errors"
"gorm.io/gorm"
)
type StorageOldService interface {
CreateStorage(storage *model.Storage) error
UpdateStorage(storage *model.Storage) error
DeleteStorageById(id uint) error
GetStorages(pageIndex, pageSize int) ([]model.Storage, int64, error)
GetStorageById(id uint) (*model.Storage, error)
GetEnabledStorages() ([]model.Storage, error)
}
type storageOldStruct struct {
db *gorm.DB
}
// CreateStorage just insert storage to database
func (s *storageOldStruct) CreateStorage(storage *model.Storage) error {
return errors.WithStack(s.db.Create(storage).Error)
}
// UpdateStorage just update storage in database
func (s *storageOldStruct) UpdateStorage(storage *model.Storage) error {
return errors.WithStack(s.db.Save(storage).Error)
}
// DeleteStorageById just delete storage from database by id
func (s *storageOldStruct) DeleteStorageById(id uint) error {
return errors.WithStack(s.db.Delete(&model.Storage{}, id).Error)
}
// GetStorages Get all storages from database order by index
func (s *storageOldStruct) GetStorages(pageIndex, pageSize int) ([]model.Storage, int64, error) {
storageDB := s.db.Model(&model.Storage{})
var count int64
if err := storageDB.Count(&count).Error; err != nil {
return nil, 0, errors.Wrapf(err, "failed get storages count")
}
var storages []model.Storage
if err := storageDB.Order("`order`").Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&storages).Error; err != nil {
return nil, 0, errors.WithStack(err)
}
return storages, count, nil
}
// GetStorageById Get Storage by id, used to update storage usually
func (s *storageOldStruct) GetStorageById(id uint) (*model.Storage, error) {
var storage model.Storage
storage.ID = id
if err := s.db.First(&storage).Error; err != nil {
return nil, errors.WithStack(err)
}
return &storage, nil
}
func (s *storageOldStruct) GetEnabledStorages() ([]model.Storage, error) {
var storages []model.Storage
if err := s.db.Where(fmt.Sprintf("%s = ?", "disabled"), false).Find(&storages).Error; err != nil {
return nil, errors.WithStack(err)
}
return storages, nil
}
func NewStorageOldService(db *gorm.DB) StorageOldService {
return &storageOldStruct{db: db}
}

View File

@@ -1,34 +0,0 @@
package service
import (
"strings"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/pkg/utils"
"github.com/pkg/errors"
"go.uber.org/zap"
)
type StoragePathService interface {
GetStorageAndActualPath(rawPath string) (storage driver.Driver, actualPath string, err error)
}
type storagePathStruct struct {
}
func (s *storagePathStruct) GetStorageAndActualPath(rawPath string) (storage driver.Driver, actualPath string, err error) {
rawPath = utils.FixAndCleanPath(rawPath)
storage = MyService.Storages().GetBalancedStorage(rawPath)
if storage == nil {
err = errors.Errorf("can't find storage with rawPath: %s", rawPath)
return
}
logger.Info("use storage", zap.Any("storage mount path", storage.GetStorage().MountPath))
mountPath := utils.GetActualMountPath(storage.GetStorage().MountPath)
actualPath = utils.FixAndCleanPath(strings.TrimPrefix(rawPath, mountPath))
return
}
func NewStoragePathService() StoragePathService {
return &storagePathStruct{}
}

View File

@@ -1,398 +0,0 @@
package service
import (
"context"
"sort"
"strings"
"time"
"github.com/IceWhaleTech/CasaOS-Common/utils/logger"
"github.com/IceWhaleTech/CasaOS/pkg/utils"
jsoniter "github.com/json-iterator/go"
"go.uber.org/zap"
"github.com/IceWhaleTech/CasaOS/pkg/generic_sync"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/internal/op"
mapset "github.com/deckarep/golang-set/v2"
"github.com/pkg/errors"
)
type StoragesService interface {
HasStorage(mountPath string) bool
CreateStorage(ctx context.Context, storage model.Storage) (uint, error)
LoadStorage(ctx context.Context, storage model.Storage) error
EnableStorage(ctx context.Context, id uint) error
DisableStorage(ctx context.Context, id uint) error
UpdateStorage(ctx context.Context, storage model.Storage) error
DeleteStorageById(ctx context.Context, id uint) error
MustSaveDriverStorage(driver driver.Driver) error
GetStorageVirtualFilesByPath(prefix string) []model.Obj
initStorage(ctx context.Context, storage model.Storage, storageDriver driver.Driver, setMountPath func(d driver.Driver, ctx context.Context) string) (err error)
InitStorages()
GetBalancedStorage(path string) driver.Driver
}
type storagesStruct struct {
}
// Although the driver type is stored,
// there is a storage in each driver,
// so it should actually be a storage, just wrapped by the driver
var storagesMap generic_sync.MapOf[string, driver.Driver]
func GetAllStorages() []driver.Driver {
return storagesMap.Values()
}
func (s *storagesStruct) HasStorage(mountPath string) bool {
return storagesMap.Has(utils.FixAndCleanPath(mountPath))
}
func GetStorageByMountPath(mountPath string) (driver.Driver, error) {
mountPath = utils.FixAndCleanPath(mountPath)
storageDriver, ok := storagesMap.Load(mountPath)
if !ok {
return nil, errors.Errorf("no mount path for an storage is: %s", mountPath)
}
return storageDriver, nil
}
// CreateStorage Save the storage to database so storage can get an id
// then instantiate corresponding driver and save it in memory
func (s *storagesStruct) CreateStorage(ctx context.Context, storage model.Storage) (uint, error) {
storage.Modified = time.Now()
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
var err error
// check driver first
driverName := storage.Driver
driverNew, err := op.GetDriverNew(driverName)
if err != nil {
return 0, errors.WithMessage(err, "failed get driver new")
}
storageDriver := driverNew()
// // insert storage to database
// err = MyService.Storage().CreateStorage(&storage)
// if err != nil {
// return storage.ID, errors.WithMessage(err, "failed create storage in database")
// }
// already has an id
err = s.initStorage(ctx, storage, storageDriver, func(d driver.Driver, ctx context.Context) string {
u, _ := d.GetUserInfo(ctx)
if len(u) > 0 {
a := strings.Split(u, "@")
u = a[0]
}
return u
})
if err != nil {
s.DeleteStorageById(ctx, storage.ID)
return storage.ID, errors.Wrap(err, "failed init storage")
}
go op.CallStorageHooks("add", storageDriver)
logger.Error("storage created", zap.Any("storage", storageDriver))
return storage.ID, nil
}
// LoadStorage load exist storage in db to memory
func (s *storagesStruct) LoadStorage(ctx context.Context, storage model.Storage) error {
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
// check driver first
driverName := storage.Driver
driverNew, err := op.GetDriverNew(driverName)
if err != nil {
return errors.WithMessage(err, "failed get driver new")
}
storageDriver := driverNew()
err = s.initStorage(ctx, storage, storageDriver, nil)
go op.CallStorageHooks("add", storageDriver)
logger.Info("storage created", zap.Any("storage", storageDriver))
return err
}
// initStorage initialize the driver and store to storagesMap
func (s *storagesStruct) initStorage(ctx context.Context, storage model.Storage, storageDriver driver.Driver, setMountPath func(d driver.Driver, ctx context.Context) string) (err error) {
storageDriver.SetStorage(storage)
driverStorage := storageDriver.GetStorage()
// Unmarshal Addition
var json = jsoniter.ConfigCompatibleWithStandardLibrary
err = json.UnmarshalFromString(driverStorage.Addition, storageDriver.GetAddition())
if err == nil {
err = storageDriver.Init(ctx)
}
if setMountPath != nil {
driverStorage.MountPath += "_" + setMountPath(storageDriver, ctx)
}
if s.HasStorage(driverStorage.MountPath) {
return errors.New("mount path already exists")
}
storageDriver.SetStorage(*driverStorage)
storagesMap.Store(driverStorage.MountPath, storageDriver)
if err != nil {
driverStorage.SetStatus(err.Error())
err = errors.Wrap(err, "failed init storage")
} else {
driverStorage.SetStatus(op.WORK)
}
err = s.MustSaveDriverStorage(storageDriver)
return err
}
func (s *storagesStruct) EnableStorage(ctx context.Context, id uint) error {
// storage, err := MyService.Storage().GetStorageById(id)
// if err != nil {
// return errors.WithMessage(err, "failed get storage")
// }
// if !storage.Disabled {
// return errors.Errorf("this storage have enabled")
// }
// storage.Disabled = false
// err = MyService.Storage().UpdateStorage(storage)
// if err != nil {
// return errors.WithMessage(err, "failed update storage in db")
// }
// err = s.LoadStorage(ctx, *storage)
// if err != nil {
// return errors.WithMessage(err, "failed load storage")
// }
return nil
}
func (s *storagesStruct) DisableStorage(ctx context.Context, id uint) error {
// storage, err := MyService.Storage().GetStorageById(id)
// if err != nil {
// return errors.WithMessage(err, "failed get storage")
// }
// if storage.Disabled {
// return errors.Errorf("this storage have disabled")
// }
// storageDriver, err := GetStorageByMountPath(storage.MountPath)
// if err != nil {
// return errors.WithMessage(err, "failed get storage driver")
// }
// // drop the storage in the driver
// if err := storageDriver.Drop(ctx); err != nil {
// return errors.Wrap(err, "failed drop storage")
// }
// // delete the storage in the memory
// storage.Disabled = true
// err = MyService.Storage().UpdateStorage(storage)
// if err != nil {
// return errors.WithMessage(err, "failed update storage in db")
// }
// storagesMap.Delete(storage.MountPath)
// go op.CallStorageHooks("del", storageDriver)
return nil
}
// UpdateStorage update storage
// get old storage first
// drop the storage then reinitialize
func (s *storagesStruct) UpdateStorage(ctx context.Context, storage model.Storage) error {
// oldStorage, err := MyService.Storage().GetStorageById(storage.ID)
// if err != nil {
// return errors.WithMessage(err, "failed get old storage")
// }
// if oldStorage.Driver != storage.Driver {
// return errors.Errorf("driver cannot be changed")
// }
// storage.Modified = time.Now()
// storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
// err = MyService.Storage().UpdateStorage(&storage)
// if err != nil {
// return errors.WithMessage(err, "failed update storage in database")
// }
// if storage.Disabled {
// return nil
// }
// storageDriver, err := GetStorageByMountPath(oldStorage.MountPath)
// if oldStorage.MountPath != storage.MountPath {
// // mount path renamed, need to drop the storage
// storagesMap.Delete(oldStorage.MountPath)
// }
// if err != nil {
// return errors.WithMessage(err, "failed get storage driver")
// }
// err = storageDriver.Drop(ctx)
// if err != nil {
// return errors.Wrapf(err, "failed drop storage")
// }
// err = s.initStorage(ctx, storage, storageDriver, nil)
// go op.CallStorageHooks("update", storageDriver)
// logger.Info("storage updated", zap.Any("storage", storageDriver))
//return err
return nil
}
func (s *storagesStruct) DeleteStorageById(ctx context.Context, id uint) error {
// storage, err := MyService.Storage().GetStorageById(id)
// if err != nil {
// return errors.WithMessage(err, "failed get storage")
// }
// if !storage.Disabled {
// storageDriver, err := GetStorageByMountPath(storage.MountPath)
// if err == nil {
// // drop the storage in the driver
// if err := storageDriver.Drop(ctx); err != nil {
// return errors.Wrapf(err, "failed drop storage")
// }
// // delete the storage in the memory
// storagesMap.Delete(storage.MountPath)
// }
// go op.CallStorageHooks("del", storageDriver)
// }
// // delete the storage in the database
// if err := MyService.Storage().DeleteStorageById(id); err != nil {
// return errors.WithMessage(err, "failed delete storage in database")
// }
return nil
}
// MustSaveDriverStorage call from specific driver
func (s *storagesStruct) MustSaveDriverStorage(driver driver.Driver) error {
err := saveDriverStorage(driver)
if err != nil {
logger.Error("failed save driver storage", zap.Any("err", err))
}
return err
}
func saveDriverStorage(driver driver.Driver) error {
// storage := driver.GetStorage()
// addition := driver.GetAddition()
// var json = jsoniter.ConfigCompatibleWithStandardLibrary
// str, err := json.MarshalToString(addition)
// if err != nil {
// return errors.Wrap(err, "error while marshal addition")
// }
// storage.Addition = str
// err = MyService.Storage().UpdateStorage(storage)
// if err != nil {
// return errors.WithMessage(err, "failed update storage in database")
// }
return nil
}
// getStoragesByPath get storage by longest match path, contains balance storage.
// for example, there is /a/b,/a/c,/a/d/e,/a/d/e.balance
// getStoragesByPath(/a/d/e/f) => /a/d/e,/a/d/e.balance
func getStoragesByPath(path string) []driver.Driver {
storages := make([]driver.Driver, 0)
curSlashCount := 0
storagesMap.Range(func(mountPath string, value driver.Driver) bool {
mountPath = utils.GetActualMountPath(mountPath)
// is this path
if utils.IsSubPath(mountPath, path) {
slashCount := strings.Count(utils.PathAddSeparatorSuffix(mountPath), "/")
// not the longest match
if slashCount > curSlashCount {
storages = storages[:0]
curSlashCount = slashCount
}
if slashCount == curSlashCount {
storages = append(storages, value)
}
}
return true
})
// make sure the order is the same for same input
sort.Slice(storages, func(i, j int) bool {
return storages[i].GetStorage().MountPath < storages[j].GetStorage().MountPath
})
return storages
}
// GetStorageVirtualFilesByPath Obtain the virtual file generated by the storage according to the path
// for example, there are: /a/b,/a/c,/a/d/e,/a/b.balance1,/av
// GetStorageVirtualFilesByPath(/a) => b,c,d
func (s *storagesStruct) GetStorageVirtualFilesByPath(prefix string) []model.Obj {
files := make([]model.Obj, 0)
storages := storagesMap.Values()
sort.Slice(storages, func(i, j int) bool {
if storages[i].GetStorage().Order == storages[j].GetStorage().Order {
return storages[i].GetStorage().MountPath < storages[j].GetStorage().MountPath
}
return storages[i].GetStorage().Order < storages[j].GetStorage().Order
})
prefix = utils.FixAndCleanPath(prefix)
set := mapset.NewSet[string]()
for _, v := range storages {
mountPath := utils.GetActualMountPath(v.GetStorage().MountPath)
// Exclude prefix itself and non prefix
if len(prefix) >= len(mountPath) || !utils.IsSubPath(prefix, mountPath) {
continue
}
name := strings.SplitN(strings.TrimPrefix(mountPath[len(prefix):], "/"), "/", 2)[0]
if set.Add(name) {
files = append(files, &model.Object{
Name: name,
Size: 0,
Modified: v.GetStorage().Modified,
IsFolder: true,
})
}
}
return files
}
var balanceMap generic_sync.MapOf[string, int]
// GetBalancedStorage get storage by path
func (s *storagesStruct) GetBalancedStorage(path string) driver.Driver {
path = utils.FixAndCleanPath(path)
storages := getStoragesByPath(path)
storageNum := len(storages)
switch storageNum {
case 0:
return nil
case 1:
return storages[0]
default:
virtualPath := utils.GetActualMountPath(storages[0].GetStorage().MountPath)
i, _ := balanceMap.LoadOrStore(virtualPath, 0)
i = (i + 1) % storageNum
balanceMap.Store(virtualPath, i)
return storages[i]
}
}
func (s *storagesStruct) InitStorages() {
// storages, err := MyService.Storage().GetEnabledStorages()
// if err != nil {
// logger.Error("failed get enabled storages", zap.Any("err", err))
// }
// go func(storages []model.Storage) {
// for i := range storages {
// err := s.LoadStorage(context.Background(), storages[i])
// if err != nil {
// logger.Error("failed get enabled storages", zap.Any("err", err))
// } else {
// logger.Info("success load storage", zap.String("mount_path", storages[i].MountPath))
// }
// }
// conf.StoragesLoaded = true
// }(storages)
}
func NewStoragesService() StoragesService {
return &storagesStruct{}
}

View File

@@ -169,7 +169,7 @@ func (c *systemService) GetDirPath(path string) ([]model.Path, error) {
}
ls, err := ioutil.ReadDir(path)
ls, err := os.ReadDir(path)
if err != nil {
logger.Error("when read dir", zap.Error(err))
return []model.Path{}, err
@@ -182,7 +182,12 @@ func (c *systemService) GetDirPath(path string) ([]model.Path, error) {
if err != nil {
link = filePath
}
temp := model.Path{Name: l.Name(), Path: filePath, IsDir: l.IsDir(), Date: l.ModTime(), Size: l.Size()}
tempFile, err := l.Info()
if err != nil {
logger.Error("when read dir", zap.Error(err))
return []model.Path{}, err
}
temp := model.Path{Name: l.Name(), Path: filePath, IsDir: l.IsDir(), Date: tempFile.ModTime(), Size: tempFile.Size()}
if filePath != link {
file, _ := os.Stat(link)
temp.IsDir = file.IsDir()