14 Commits
dev ... v1.3.6

Author SHA1 Message Date
ssongliu
c2879f2a56 fix: 增加前端注入校验规则 (#1456) 2023-06-26 17:25:18 +08:00
zhengkunwang223
67221e7f70 feat: 应用商店增加分页 (#1447) 2023-06-25 22:24:39 +08:00
zhengkunwang223
c76cc07c1c fix: 解决 Docker 版本过低导致应用无法操作的问题 (#1446)
Refs https://github.com/1Panel-dev/1Panel/issues/1445
2023-06-25 22:24:33 +08:00
ssongliu
7a091a152e fix: 解决计划任务备份根目录下文件夹失败的问题 (#1444) 2023-06-25 22:24:29 +08:00
ssongliu
d2087641f7 fix: 解决终端连接注入漏洞问题 2023-06-25 22:24:22 +08:00
ssongliu
321ed00734 fix: 解决添加仓库注入漏洞问题 2023-06-25 22:24:19 +08:00
ssongliu
8ff160408f fix: 修改数据库无法正常访问情况下系统提示信息 (#1438) 2023-06-25 22:24:04 +08:00
ssongliu
662eae1ba4 fix: 解决容器镜像加速或仓库空行导致 Docker 无法正常启用的问题 (#1437) 2023-06-25 22:23:58 +08:00
ssongliu
f808e81958 fix: ssh 登录日志排序优化 (#1435) 2023-06-25 22:23:53 +08:00
wanghe-fit2cloud
f161bcbc31 fix: goreleaser 显示声明 CGO_ENABLED=0 2023-06-16 18:36:36 +08:00
wanghe-fit2cloud
73f789062b feat: 静态编译不依赖 glibc 2023-06-16 12:08:15 +08:00
ssongliu
1fdf4ffd2f fix: 构建移除对 cgo 的依赖 (#1386) 2023-06-16 12:08:06 +08:00
吴小白
6d58fe7a1e feat: 不使用 cgo (#1382) 2023-06-16 12:07:56 +08:00
ssongliu
c8bbfada8c fix: 解决容器、终端界面 ios 适配的问题 (#1385) 2023-06-15 19:32:03 +08:00
32 changed files with 316 additions and 181 deletions

View File

@@ -12,9 +12,7 @@ jobs:
build-linux-binary: build-linux-binary:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Install cross-compilers - name: Checkout Code
run: sudo apt-get update && sudo apt-get -y install gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi gcc-powerpc64le-linux-gnu gcc-s390x-linux-gnu
- name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
@@ -23,7 +21,7 @@ jobs:
- name: Build Web - name: Build Web
id: build_frontend id: build_frontend
run: | run: |
cd frontend && npm install && npm run build:dev cd frontend && npm install && npm run build:pro
env: env:
NODE_OPTIONS: --max-old-space-size=8192 NODE_OPTIONS: --max-old-space-size=8192
- name: Setup Go - name: Setup Go

View File

@@ -10,9 +10,7 @@ jobs:
create-release: create-release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Install cross-compilers - name: Checkout Code
run: sudo apt-get update && sudo apt-get -y install gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi gcc-powerpc64le-linux-gnu gcc-s390x-linux-gnu
- name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
@@ -20,7 +18,7 @@ jobs:
node-version: '18.14' node-version: '18.14'
- name: Build Web - name: Build Web
run: | run: |
cd frontend && npm install && npm run build:dev cd frontend && npm install && npm run build:pro
env: env:
NODE_OPTIONS: --max-old-space-size=8192 NODE_OPTIONS: --max-old-space-size=8192
- name: Setup Go - name: Setup Go
@@ -30,17 +28,33 @@ jobs:
- name: Build Release - name: Build Release
uses: goreleaser/goreleaser-action@v4 uses: goreleaser/goreleaser-action@v4
with: with:
distribution: goreleaser
version: latest
args: release --skip-publish --clean args: release --skip-publish --clean
- name: Upload Assets - name: Upload Assets
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
with: with:
draft: true draft: true
body: |
# 一、安装和升级
## 1.1 一键安装
```sh
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
```
## 1.2 在线升级
登录 1Panel Web 控制台,在页面右下角点击 **【检查更新】** 进行在线升级。
>更多信息请查阅在线文档https://1panel.cn/docs/
# 二、更新日志
files: | files: |
dist/*.tar.gz dist/*.tar.gz
dist/checksums.txt dist/checksums.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup OSSUTIL - name: Setup OSSUTIL
uses: yizhoumo/setup-ossutil@v1 uses: yizhoumo/setup-ossutil@v1
with: with:
@@ -49,4 +63,4 @@ jobs:
access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }} access-key-secret: ${{ secrets.OSS_ACCESS_KEY_SECRET }}
ossutil-version: '1.7.14' ossutil-version: '1.7.14'
- name: Upload Assets to OSS - name: Upload Assets to OSS
run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir run: ossutil cp -r dist/ oss://resource-fit2cloud-com/1panel/package/stable/${{ github.ref_name }}/release/ --include "*.tar.gz" --include "checksums.txt" --only-current-dir --force

1
.gitignore vendored
View File

@@ -30,5 +30,6 @@ dist/
1pctl 1pctl
1panel.service 1panel.service
install.sh install.sh
quick_start.sh
cmd/server/web/.DS_Store cmd/server/web/.DS_Store
cmd/server/.DS_Store cmd/server/.DS_Store

View File

@@ -16,18 +16,8 @@ builds:
- -trimpath - -trimpath
ldflags: ldflags:
- -w -s - -w -s
- --extldflags "-static -fpic"
tags:
- osusergo
env: env:
- CGO_ENABLED=1 - CGO_ENABLED=0
- >-
{{- if eq .Arch "amd64"}}CC=x86_64-linux-gnu-gcc{{- end }}
{{- if eq .Arch "arm64"}}CC=aarch64-linux-gnu-gcc{{- end }}
{{- if eq .Arch "arm"}}CC=arm-linux-gnueabi-gcc{{- end }}
{{- if eq .Arch "loong64"}}CC=loongarch64-linux-gnu-gcc{{- end }}
{{- if eq .Arch "ppc64le"}}CC=powerpc64le-linux-gnu-gcc{{- end }}
{{- if eq .Arch "s390x"}}CC=s390x-linux-gnu-gcc{{- end }}
goos: goos:
- linux - linux
goarm: goarm:

View File

@@ -1,34 +0,0 @@
FROM node:18.14 as build_web
ARG TARGETARCH
ARG NPM_REGISTRY="https://registry.npmmirror.com"
ENV NODE_OPTIONS="--max-old-space-size=4096"
WORKDIR /data
RUN set -ex \
&& npm config set registry ${NPM_REGISTRY}
ADD . /data
RUN set -ex \
&& cd /data/frontend \
&& npm install
RUN set -ex \
&& cd /data/frontend \
&& npm run build:dev
FROM golang:1.20
ARG TARGETARCH
ARG GOPROXY="https://goproxy.cn,direct"
COPY --from=build_web /data /data
WORKDIR /data
RUN set -ex \
&& go env -w GOPROXY=${GOPROXY} \
&& go install github.com/goreleaser/goreleaser@latest \
&& goreleaser build --single-target --snapshot --clean
CMD ["/bin/bash"]

View File

@@ -23,16 +23,16 @@ build_frontend:
build_backend_on_linux: build_backend_on_linux:
cd $(SERVER_PATH) \ cd $(SERVER_PATH) \
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -tags 'osusergo,netgo' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN) && GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
build_backend_on_darwin: build_backend_on_darwin:
cd $(SERVER_PATH) \ cd $(SERVER_PATH) \
&& CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++ $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-static -fpic"' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN) && GOOS=linux GOARCH=amd64 $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
build_backend_on_archlinux: build_backend_on_archlinux:
cd $(SERVER_PATH) \ cd $(SERVER_PATH) \
&& CGO_ENABLED=1 GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w --extldflags "-fpic"' -tags osusergo -o $(BUILD_PATH)/$(APP_NAME) $(MAIN) && GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOBUILD) -trimpath -ldflags '-s -w' -o $(BUILD_PATH)/$(APP_NAME) $(MAIN)
build_all: build_frontend build_backend_on_linux build_all: build_frontend build_backend_on_linux
build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin build_on_local: clean_assets build_frontend build_backend_on_darwin upx_bin

View File

@@ -163,11 +163,16 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
} }
defer wsConn.Close() defer wsConn.Close()
cmds := fmt.Sprintf("docker exec %s %s", containerID, command) cmds := []string{"exec", containerID, command}
if len(user) != 0 { if len(user) != 0 {
cmds = fmt.Sprintf("docker exec -u %s %s %s", user, containerID, command) cmds = []string{"exec", "-u", user, containerID, command}
} }
stdout, err := cmd.Exec(cmds) if cmd.CheckIllegal(user, containerID, command) {
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
return
}
}
stdout, err := cmd.ExecWithCheck("docker", cmds...)
if wshandleError(wsConn, errors.WithMessage(err, stdout)) { if wshandleError(wsConn, errors.WithMessage(err, stdout)) {
return return
} }

View File

@@ -197,6 +197,9 @@ func handleTar(sourceDir, targetDir, name, exclusionRules string) error {
if strings.Contains(sourceDir, "/") { if strings.Contains(sourceDir, "/") {
itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "") itemDir := strings.ReplaceAll(sourceDir[strings.LastIndex(sourceDir, "/"):], "/", "")
aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")] aheadDir := sourceDir[:strings.LastIndex(sourceDir, "/")]
if len(aheadDir) == 0 {
aheadDir = "/"
}
path += fmt.Sprintf("-C %s %s", aheadDir, itemDir) path += fmt.Sprintf("-C %s %s", aheadDir, itemDir)
} else { } else {
path = sourceDir path = sourceDir

View File

@@ -136,12 +136,14 @@ func (u *DockerService) UpdateConf(req dto.SettingUpdate) error {
switch req.Key { switch req.Key {
case "Registries": case "Registries":
req.Value = strings.TrimRight(req.Value, ",")
if len(req.Value) == 0 { if len(req.Value) == 0 {
delete(daemonMap, "insecure-registries") delete(daemonMap, "insecure-registries")
} else { } else {
daemonMap["insecure-registries"] = strings.Split(req.Value, ",") daemonMap["insecure-registries"] = strings.Split(req.Value, ",")
} }
case "Mirrors": case "Mirrors":
req.Value = strings.TrimRight(req.Value, ",")
if len(req.Value) == 0 { if len(req.Value) == 0 {
delete(daemonMap, "registry-mirrors") delete(daemonMap, "registry-mirrors")
} else { } else {

View File

@@ -3,12 +3,14 @@ package service
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"path" "path"
"strings" "strings"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/1Panel-dev/1Panel/backend/utils/cmd"
@@ -76,6 +78,9 @@ func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) {
} }
func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error { func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {
return buserr.New(constant.ErrRepoConn)
}
imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(req.Name)) imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(req.Name))
if imageRepo.ID != 0 { if imageRepo.ID != 0 {
return constant.ErrRecordExist return constant.ErrRecordExist
@@ -142,6 +147,9 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
if req.ID == 1 { if req.ID == 1 {
return errors.New("The default value cannot be deleted !") return errors.New("The default value cannot be deleted !")
} }
if cmd.CheckIllegal(req.Username, req.Password, req.DownloadUrl) {
return buserr.New(constant.ErrRepoConn)
}
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.ID)) repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.ID))
if err != nil { if err != nil {
return err return err
@@ -149,7 +157,7 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) { if repo.DownloadUrl != req.DownloadUrl || (!repo.Auth && req.Auth) {
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update") _ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
if repo.Auth { if repo.Auth {
_, _ = cmd.Execf("docker logout %s", repo.DownloadUrl) _, _ = cmd.ExecWithCheck("docker", "logout", repo.DownloadUrl)
} }
stdout, err := cmd.Exec("systemctl restart docker") stdout, err := cmd.Exec("systemctl restart docker")
if err != nil { if err != nil {
@@ -176,9 +184,9 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
} }
func (u *ImageRepoService) CheckConn(host, user, password string) error { func (u *ImageRepoService) CheckConn(host, user, password string) error {
stdout, err := cmd.Execf("docker login -u %s -p %s %s", user, password, host) stdout, err := cmd.ExecWithCheck("docker", "login", "-u", user, "-p", password, host)
if err != nil { if err != nil {
return errors.New(string(stdout)) return fmt.Errorf("stdout: %s, stderr: %v", stdout, err)
} }
if strings.Contains(string(stdout), "Login Succeeded") { if strings.Contains(string(stdout), "Login Succeeded") {
return nil return nil

View File

@@ -229,29 +229,29 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
if len(req.Info) != 0 { if len(req.Info) != 0 {
command = fmt.Sprintf(" | grep '%s'", req.Info) command = fmt.Sprintf(" | grep '%s'", req.Info)
} }
for i := 0; i < len(fileList); i++ { for i := 0; i < len(fileList); i++ {
if strings.HasPrefix(path.Base(fileList[i]), "secure") { withAppend := len(data.Logs) < req.Page*req.PageSize
commandItem := fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", fileList[i], command) if req.Status != constant.StatusSuccess {
dataItem := loadFailedSecureDatas(commandItem) if strings.HasPrefix(path.Base(fileList[i]), "secure") {
data.FailedCount += len(dataItem) commandItem := fmt.Sprintf("cat %s | grep -a 'Failed password for' | grep -v 'invalid' %s", fileList[i], command)
data.TotalCount += len(dataItem) dataItem, itemTotal := loadFailedSecureDatas(commandItem, withAppend)
if req.Status != constant.StatusSuccess { data.FailedCount += itemTotal
data.TotalCount += itemTotal
data.Logs = append(data.Logs, dataItem...)
}
if strings.HasPrefix(path.Base(fileList[i]), "auth.log") {
commandItem := fmt.Sprintf("cat %s | grep -a 'Connection closed by authenticating user' | grep -a 'preauth' %s", fileList[i], command)
dataItem, itemTotal := loadFailedAuthDatas(commandItem, withAppend)
data.FailedCount += itemTotal
data.TotalCount += itemTotal
data.Logs = append(data.Logs, dataItem...) data.Logs = append(data.Logs, dataItem...)
} }
} }
if strings.HasPrefix(path.Base(fileList[i]), "auth.log") {
commandItem := fmt.Sprintf("cat %s | grep -a 'Connection closed by authenticating user' | grep -a 'preauth' %s", fileList[i], command)
dataItem := loadFailedAuthDatas(commandItem)
data.FailedCount += len(dataItem)
data.TotalCount += len(dataItem)
if req.Status != constant.StatusSuccess {
data.Logs = append(data.Logs, dataItem...)
}
}
commandItem := fmt.Sprintf("cat %s | grep -a Accepted %s", fileList[i], command)
dataItem := loadSuccessDatas(commandItem)
data.TotalCount += len(dataItem)
if req.Status != constant.StatusFailed { if req.Status != constant.StatusFailed {
commandItem := fmt.Sprintf("cat %s | grep -a Accepted %s", fileList[i], command)
dataItem, itemTotal := loadSuccessDatas(commandItem, withAppend)
data.TotalCount += itemTotal
data.Logs = append(data.Logs, dataItem...) data.Logs = append(data.Logs, dataItem...)
} }
} }
@@ -279,7 +279,7 @@ func (u *SSHService) LoadLog(req dto.SearchSSHLog) (*dto.SSHLog, error) {
global.LOG.Errorf("load qqwry datas failed: %s", err) global.LOG.Errorf("load qqwry datas failed: %s", err)
} }
var itemLogs []dto.SSHHistory var itemLogs []dto.SSHHistory
for i := len(data.Logs) - 1; i >= 0; i-- { for i := 0; i < len(data.Logs); i++ {
data.Logs[i].Area = qqWry.Find(data.Logs[i].Address).Area data.Logs[i].Area = qqWry.Find(data.Logs[i].Address).Area
data.Logs[i].Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", timeNow.Year(), data.Logs[i].DateStr), nyc) data.Logs[i].Date, _ = time.ParseInLocation("2006 Jan 2 15:04:05", fmt.Sprintf("%d %s", timeNow.Year(), data.Logs[i].DateStr), nyc)
itemLogs = append(itemLogs, data.Logs[i]) itemLogs = append(itemLogs, data.Logs[i])
@@ -293,18 +293,17 @@ func sortFileList(fileNames []string) []string {
if len(fileNames) < 2 { if len(fileNames) < 2 {
return fileNames return fileNames
} }
var itemFile []string if strings.HasPrefix(path.Base(fileNames[0]), "secure") {
if strings.Contains(fileNames[0], "secure") { var itemFile []string
sort.Slice(fileNames, func(i, j int) bool { sort.Slice(fileNames, func(i, j int) bool {
return fileNames[i] < fileNames[j] return fileNames[i] > fileNames[j]
}) })
itemFile = append(itemFile, fileNames[1:]...) itemFile = append(itemFile, fileNames[len(fileNames)-1])
itemFile = append(itemFile, fileNames[0]) itemFile = append(itemFile, fileNames[:len(fileNames)-2]...)
return itemFile return itemFile
} }
sort.Slice(fileNames, func(i, j int) bool { sort.Slice(fileNames, func(i, j int) bool {
return fileNames[i] > fileNames[j] return fileNames[i] < fileNames[j]
}) })
return fileNames return fileNames
} }
@@ -339,82 +338,109 @@ func updateSSHConf(oldFiles []string, param string, value interface{}) []string
return newFiles return newFiles
} }
func loadSuccessDatas(command string) []dto.SSHHistory { func loadSuccessDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
var datas []dto.SSHHistory var (
datas []dto.SSHHistory
totalNum int
)
stdout2, err := cmd.Exec(command) stdout2, err := cmd.Exec(command)
if err == nil { if err == nil {
lines := strings.Split(string(stdout2), "\n") lines := strings.Split(string(stdout2), "\n")
for _, line := range lines { if len(lines) == 0 {
parts := strings.Fields(line) return datas, 0
}
for i := len(lines) - 1; i >= 0; i-- {
parts := strings.Fields(lines[i])
if len(parts) < 14 { if len(parts) < 14 {
continue continue
} }
historyItem := dto.SSHHistory{ totalNum++
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]), if withAppend {
AuthMode: parts[6], historyItem := dto.SSHHistory{
User: parts[8], DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
Address: parts[10], AuthMode: parts[6],
Port: parts[12], User: parts[8],
Status: constant.StatusSuccess, Address: parts[10],
Port: parts[12],
Status: constant.StatusSuccess,
}
datas = append(datas, historyItem)
} }
datas = append(datas, historyItem)
} }
} }
return datas return datas, totalNum
} }
func loadFailedAuthDatas(command string) []dto.SSHHistory { func loadFailedAuthDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
var datas []dto.SSHHistory var (
datas []dto.SSHHistory
totalNum int
)
stdout2, err := cmd.Exec(command) stdout2, err := cmd.Exec(command)
if err == nil { if err == nil {
lines := strings.Split(string(stdout2), "\n") lines := strings.Split(string(stdout2), "\n")
for _, line := range lines { if len(lines) == 0 {
parts := strings.Fields(line) return datas, 0
}
for i := len(lines) - 1; i >= 0; i-- {
parts := strings.Fields(lines[i])
if len(parts) < 14 { if len(parts) < 14 {
continue continue
} }
historyItem := dto.SSHHistory{ totalNum++
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]), if withAppend {
AuthMode: parts[8], historyItem := dto.SSHHistory{
User: parts[10], DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
Address: parts[11], AuthMode: parts[8],
Port: parts[13], User: parts[10],
Status: constant.StatusFailed, Address: parts[11],
Port: parts[13],
Status: constant.StatusFailed,
}
if strings.Contains(lines[i], ": ") {
historyItem.Message = strings.Split(lines[i], ": ")[1]
}
datas = append(datas, historyItem)
} }
if strings.Contains(line, ": ") {
historyItem.Message = strings.Split(line, ": ")[1]
}
datas = append(datas, historyItem)
} }
} }
return datas return datas, totalNum
} }
func loadFailedSecureDatas(command string) []dto.SSHHistory { func loadFailedSecureDatas(command string, withAppend bool) ([]dto.SSHHistory, int) {
var datas []dto.SSHHistory var (
datas []dto.SSHHistory
totalNum int
)
stdout2, err := cmd.Exec(command) stdout2, err := cmd.Exec(command)
if err == nil { if err == nil {
lines := strings.Split(string(stdout2), "\n") lines := strings.Split(string(stdout2), "\n")
for _, line := range lines { if len(lines) == 0 {
parts := strings.Fields(line) return datas, 0
}
for i := len(lines) - 1; i >= 0; i-- {
parts := strings.Fields(lines[i])
if len(parts) < 14 { if len(parts) < 14 {
continue continue
} }
historyItem := dto.SSHHistory{ totalNum++
DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]), if withAppend {
AuthMode: parts[6], historyItem := dto.SSHHistory{
User: parts[8], DateStr: fmt.Sprintf("%s %s %s", parts[0], parts[1], parts[2]),
Address: parts[10], AuthMode: parts[6],
Port: parts[12], User: parts[8],
Status: constant.StatusFailed, Address: parts[10],
Port: parts[12],
Status: constant.StatusFailed,
}
if strings.Contains(lines[i], ": ") {
historyItem.Message = strings.Split(lines[i], ": ")[1]
}
datas = append(datas, historyItem)
} }
if strings.Contains(line, ": ") {
historyItem.Message = strings.Split(line, ": ")[1]
}
datas = append(datas, historyItem)
} }
} }
return datas return datas, totalNum
} }
func handleGunzip(path string) error { func handleGunzip(path string) error {

View File

@@ -105,6 +105,7 @@ var (
ErrInUsed = "ErrInUsed" ErrInUsed = "ErrInUsed"
ErrObjectInUsed = "ErrObjectInUsed" ErrObjectInUsed = "ErrObjectInUsed"
ErrPortRules = "ErrPortRules" ErrPortRules = "ErrPortRules"
ErrRepoConn = "ErrRepoConn"
) )
// runtime // runtime

View File

@@ -78,6 +78,7 @@ ErrTypeOfRedis: "The recovery file type does not match the current persistence m
#container #container
ErrInUsed: "{{ .detail }} is in use and cannot be deleted" ErrInUsed: "{{ .detail }} is in use and cannot be deleted"
ErrObjectInUsed: "This object is in use and cannot be deleted" ErrObjectInUsed: "This object is in use and cannot be deleted"
ErrRepoConn: "The repository information contains illegal characters"
#runtime #runtime
ErrDirNotFound: "The build folder does not exist! Please check file integrity" ErrDirNotFound: "The build folder does not exist! Please check file integrity"

View File

@@ -78,6 +78,7 @@ ErrTypeOfRedis: "恢复文件类型与当前持久化方式不符,请修改后
#container #container
ErrInUsed: "{{ .detail }} 正被使用,无法删除" ErrInUsed: "{{ .detail }} 正被使用,无法删除"
ErrObjectInUsed: "该对象正被使用,无法删除" ErrObjectInUsed: "该对象正被使用,无法删除"
ErrRepoConn: "仓库信息中存在不合法的字符"
#runtime #runtime
ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!" ErrDirNotFound: "build 文件夹不存在!请检查文件完整性!"

View File

@@ -2,12 +2,13 @@ package db
import ( import (
"fmt" "fmt"
"gorm.io/gorm/logger"
"log" "log"
"os" "os"
"time" "time"
"gorm.io/driver/sqlite" "gorm.io/gorm/logger"
"github.com/glebarez/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"

View File

@@ -15,7 +15,7 @@ func BindDomain() gin.HandlerFunc {
settingRepo := repo.NewISettingRepo() settingRepo := repo.NewISettingRepo()
status, err := settingRepo.Get(settingRepo.WithByKey("BindDomain")) status, err := settingRepo.Get(settingRepo.WithByKey("BindDomain"))
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrDomain, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }
if len(status.Value) == 0 { if len(status.Value) == 0 {

View File

@@ -15,7 +15,7 @@ func WhiteAllow() gin.HandlerFunc {
settingRepo := repo.NewISettingRepo() settingRepo := repo.NewISettingRepo()
status, err := settingRepo.Get(settingRepo.WithByKey("AllowIPs")) status, err := settingRepo.Get(settingRepo.WithByKey("AllowIPs"))
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrIP, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }

View File

@@ -16,7 +16,7 @@ func PasswordExpired() gin.HandlerFunc {
settingRepo := repo.NewISettingRepo() settingRepo := repo.NewISettingRepo()
setting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays")) setting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err)
return return
} }
expiredDays, _ := strconv.Atoi(setting.Value) expiredDays, _ := strconv.Atoi(setting.Value)
@@ -27,7 +27,7 @@ func PasswordExpired() gin.HandlerFunc {
extime, err := settingRepo.Get(settingRepo.WithByKey("ExpirationTime")) extime, err := settingRepo.Get(settingRepo.WithByKey("ExpirationTime"))
if err != nil { if err != nil {
helper.ErrorWithDetail(c, constant.CodePasswordExpired, constant.ErrTypePasswordExpired, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err)
return return
} }
loc, _ := time.LoadLocation(common.LoadTimeZone()) loc, _ := time.LoadLocation(common.LoadTimeZone())

View File

@@ -5,6 +5,7 @@ import (
"context" "context"
"fmt" "fmt"
"os/exec" "os/exec"
"strings"
"time" "time"
"github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/buserr"
@@ -117,6 +118,43 @@ func Execf(cmdStr string, a ...interface{}) (string, error) {
return stdout.String(), nil return stdout.String(), nil
} }
func ExecWithCheck(name string, a ...string) (string, error) {
cmd := exec.Command(name, a...)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
errMsg := ""
if len(stderr.String()) != 0 {
errMsg = fmt.Sprintf("stderr: %s", stderr.String())
}
if len(stdout.String()) != 0 {
if len(errMsg) != 0 {
errMsg = fmt.Sprintf("%s; stdout: %s", errMsg, stdout.String())
} else {
errMsg = fmt.Sprintf("stdout: %s", stdout.String())
}
}
return errMsg, err
}
return stdout.String(), nil
}
func CheckIllegal(args ...string) bool {
if args == nil {
return false
}
for _, arg := range args {
if strings.Contains(arg, "&") || strings.Contains(arg, "|") || strings.Contains(arg, ";") ||
strings.Contains(arg, "$") || strings.Contains(arg, "'") || strings.Contains(arg, "`") ||
strings.Contains(arg, "(") || strings.Contains(arg, ")") || strings.Contains(arg, "\"") {
return true
}
}
return false
}
func HasNoPasswordSudo() bool { func HasNoPasswordSudo() bool {
cmd2 := exec.Command("sudo", "-n", "ls") cmd2 := exec.Command("sudo", "-n", "ls")
err2 := cmd2.Run() err2 := cmd2.Run()

View File

@@ -5,31 +5,31 @@ import (
) )
func Up(filePath string) (string, error) { func Up(filePath string) (string, error) {
stdout, err := cmd.Execf("docker compose -f %s up -d", filePath) stdout, err := cmd.Execf("docker-compose -f %s up -d", filePath)
return stdout, err return stdout, err
} }
func Down(filePath string) (string, error) { func Down(filePath string) (string, error) {
stdout, err := cmd.Execf("docker compose -f %s down --remove-orphans", filePath) stdout, err := cmd.Execf("docker-compose -f %s down --remove-orphans", filePath)
return stdout, err return stdout, err
} }
func Start(filePath string) (string, error) { func Start(filePath string) (string, error) {
stdout, err := cmd.Execf("docker compose -f %s start", filePath) stdout, err := cmd.Execf("docker-compose -f %s start", filePath)
return stdout, err return stdout, err
} }
func Stop(filePath string) (string, error) { func Stop(filePath string) (string, error) {
stdout, err := cmd.Execf("docker compose -f %s stop", filePath) stdout, err := cmd.Execf("docker-compose -f %s stop", filePath)
return stdout, err return stdout, err
} }
func Restart(filePath string) (string, error) { func Restart(filePath string) (string, error) {
stdout, err := cmd.Execf("docker compose -f %s restart", filePath) stdout, err := cmd.Execf("docker-compose -f %s restart", filePath)
return stdout, err return stdout, err
} }
func Operate(filePath, operation string) (string, error) { func Operate(filePath, operation string) (string, error) {
stdout, err := cmd.Execf("docker compose -f %s %s", filePath, operation) stdout, err := cmd.Execf("docker-compose -f %s %s", filePath, operation)
return stdout, err return stdout, err
} }

View File

@@ -7,8 +7,8 @@ import (
"github.com/1Panel-dev/1Panel/backend/server" "github.com/1Panel-dev/1Panel/backend/server"
cmdUtils "github.com/1Panel-dev/1Panel/backend/utils/cmd" cmdUtils "github.com/1Panel-dev/1Panel/backend/utils/cmd"
"github.com/glebarez/sqlite"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gorm.io/driver/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
) )

View File

@@ -18,13 +18,13 @@
:stroke-width="12" :stroke-width="12"
:percentage="uploadPrecent" :percentage="uploadPrecent"
></el-progress> ></el-progress>
<div v-if="type === 'mysql'" class="el-upload__tip"> <div v-if="type === 'mysql'" style="width: 80%" class="el-upload__tip">
<span class="input-help">{{ $t('database.supportUpType') }}</span> <span class="input-help">{{ $t('database.supportUpType') }}</span>
<span class="input-help"> <span class="input-help">
{{ $t('database.zipFormat') }} {{ $t('database.zipFormat') }}
</span> </span>
</div> </div>
<div v-else class="el-upload__tip"> <div v-else style="width: 80%" class="el-upload__tip">
<span class="input-help">{{ $t('website.supportUpType') }}</span> <span class="input-help">{{ $t('website.supportUpType') }}</span>
<span class="input-help"> <span class="input-help">
{{ $t('website.zipFormat', [type + '.json']) }} {{ $t('website.zipFormat', [type + '.json']) }}

View File

@@ -30,6 +30,27 @@ const checkHost = (rule: any, value: any, callback: any) => {
} }
}; };
const checkIllegal = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.requiredInput')));
return;
}
if (
value.indexOf('&') !== -1 ||
value.indexOf('|') !== -1 ||
value.indexOf(';') !== -1 ||
value.indexOf('$') !== -1 ||
value.indexOf("'") !== -1 ||
value.indexOf('`') !== -1 ||
value.indexOf('(') !== -1 ||
value.indexOf(')') !== -1
) {
callback(new Error(i18n.global.t('commons.rule.illegalInput')));
} else {
callback();
}
};
const complexityPassword = (rule: any, value: any, callback: any) => { const complexityPassword = (rule: any, value: any, callback: any) => {
if (value === '' || typeof value === 'undefined' || value == null) { if (value === '' || typeof value === 'undefined' || value == null) {
callback(new Error(i18n.global.t('commons.rule.complexityPassword'))); callback(new Error(i18n.global.t('commons.rule.complexityPassword')));
@@ -333,6 +354,7 @@ interface CommonRule {
integerNumber: FormItemRule; integerNumber: FormItemRule;
ip: FormItemRule; ip: FormItemRule;
host: FormItemRule; host: FormItemRule;
illegal: FormItemRule;
port: FormItemRule; port: FormItemRule;
domain: FormItemRule; domain: FormItemRule;
databaseName: FormItemRule; databaseName: FormItemRule;
@@ -440,6 +462,11 @@ export const Rules: CommonRule = {
required: true, required: true,
trigger: 'blur', trigger: 'blur',
}, },
illegal: {
validator: checkIllegal,
required: true,
trigger: 'blur',
},
port: { port: {
required: true, required: true,
trigger: 'blur', trigger: 'blur',

View File

@@ -135,6 +135,7 @@ const message = {
rePassword: 'The passwords are inconsistent. Please check and re-enter the password', rePassword: 'The passwords are inconsistent. Please check and re-enter the password',
requiredInput: 'Please enter the required fields', requiredInput: 'Please enter the required fields',
requiredSelect: 'Please select the required fields', requiredSelect: 'Please select the required fields',
illegalInput: 'There are illegal characters in the input box.',
commonName: 'Support English, Chinese, numbers, .-, and _ length 1-30', commonName: 'Support English, Chinese, numbers, .-, and _ length 1-30',
userName: 'Support English, Chinese, numbers and _ length 3-30', userName: 'Support English, Chinese, numbers and _ length 3-30',
simpleName: 'Support English, numbers and _ length 1-30', simpleName: 'Support English, numbers and _ length 1-30',

View File

@@ -138,6 +138,7 @@ const message = {
rePassword: '密码不一致请检查后重新输入', rePassword: '密码不一致请检查后重新输入',
requiredInput: '请填写必填项', requiredInput: '请填写必填项',
requiredSelect: '请选择必选项', requiredSelect: '请选择必选项',
illegalInput: '输入框中存在不合法字符',
commonName: '支持英文中文数字.-和_,长度1-30', commonName: '支持英文中文数字.-和_,长度1-30',
userName: '支持英文中文数字和_,长度3-30', userName: '支持英文中文数字和_,长度3-30',
simpleName: '支持英文数字_,长度1-30', simpleName: '支持英文数字_,长度1-30',

View File

@@ -99,6 +99,15 @@
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<div class="page-button">
<fu-table-pagination
v-model:current-page="paginationConfig.currentPage"
v-model:page-size="paginationConfig.pageSize"
v-bind="paginationConfig"
@change="search(req)"
:layout="'total, sizes, prev, pager, next, jumper'"
/>
</div>
</template> </template>
</LayoutContent> </LayoutContent>
<Detail v-if="showDetail" :id="appId"></Detail> <Detail v-if="showDetail" :id="appId"></Detail>
@@ -116,6 +125,12 @@ import { useI18n } from 'vue-i18n';
const language = useI18n().locale.value; const language = useI18n().locale.value;
const paginationConfig = reactive({
currentPage: 1,
pageSize: 50,
total: 0,
});
const req = reactive({ const req = reactive({
name: '', name: '',
tags: [], tags: [],
@@ -138,9 +153,12 @@ const getColor = (index: number) => {
const search = async (req: App.AppReq) => { const search = async (req: App.AppReq) => {
loading.value = true; loading.value = true;
req.pageSize = paginationConfig.pageSize;
req.page = paginationConfig.currentPage;
await SearchApp(req) await SearchApp(req)
.then((res) => { .then((res) => {
apps.value = res.data.items; apps.value = res.data.items;
paginationConfig.total = res.data.total;
}) })
.finally(() => { .finally(() => {
loading.value = false; loading.value = false;
@@ -260,4 +278,10 @@ onMounted(() => {
border: none; border: none;
} }
} }
.page-button {
float: right;
margin-bottom: 10px;
margin-top: 10px;
}
</style> </style>

View File

@@ -8,7 +8,7 @@
> >
<template #header> <template #header>
<DrawerHeader :header="$t('commons.button.log')" :resource="logSearch.container" :back="handleClose"> <DrawerHeader :header="$t('commons.button.log')" :resource="logSearch.container" :back="handleClose">
<template #extra> <template #extra v-if="!mobile">
<el-button @click="toggleFullscreen" class="fullScreen" icon="FullScreen" plain></el-button> <el-button @click="toggleFullscreen" class="fullScreen" icon="FullScreen" plain></el-button>
</template> </template>
</DrawerHeader> </DrawerHeader>
@@ -56,7 +56,7 @@
import { cleanContainerLog, logContainer } from '@/api/modules/container'; import { cleanContainerLog, logContainer } from '@/api/modules/container';
import i18n from '@/lang'; import i18n from '@/lang';
import { dateFormatForName } from '@/utils/util'; import { dateFormatForName } from '@/utils/util';
import { nextTick, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue'; import { computed, nextTick, onBeforeUnmount, reactive, ref, shallowRef, watch } from 'vue';
import { Codemirror } from 'vue-codemirror'; import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript'; import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark'; import { oneDark } from '@codemirror/theme-one-dark';
@@ -67,8 +67,10 @@ import screenfull from 'screenfull';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
const extensions = [javascript(), oneDark]; const extensions = [javascript(), oneDark];
const logVisiable = ref(false); const logVisiable = ref(false);
const mobile = computed(() => {
return globalStore.isMobile();
});
const logInfo = ref(); const logInfo = ref();
const view = shallowRef(); const view = shallowRef();
@@ -110,16 +112,13 @@ function toggleFullscreen() {
screenfull.toggle(); screenfull.toggle();
} }
} }
screenfull.on('change', () => {
globalStore.isFullScreen = screenfull.isFullscreen;
});
const handleClose = async () => { const handleClose = async () => {
logVisiable.value = false; logVisiable.value = false;
clearInterval(Number(timer)); clearInterval(Number(timer));
timer = null; timer = null;
}; };
watch(logVisiable, (val) => { watch(logVisiable, (val) => {
if (screenfull.isEnabled && !val) screenfull.exit(); if (screenfull.isEnabled && !val && !mobile.value) screenfull.exit();
}); });
const searchLogs = async () => { const searchLogs = async () => {
const res = await logContainer(logSearch); const res = await logContainer(logSearch);
@@ -172,6 +171,12 @@ const acceptParams = (props: DialogProps): void => {
searchLogs(); searchLogs();
} }
}, 1000 * 5); }, 1000 * 5);
if (!mobile.value) {
screenfull.on('change', () => {
globalStore.isFullScreen = screenfull.isFullscreen;
});
}
}; };
onBeforeUnmount(() => { onBeforeUnmount(() => {

View File

@@ -111,10 +111,10 @@ const handleClose = () => {
}; };
const rules = reactive({ const rules = reactive({
name: [Rules.requiredInput, Rules.name], name: [Rules.requiredInput, Rules.name],
downloadUrl: [Rules.requiredInput], downloadUrl: [Rules.illegal],
protocol: [Rules.requiredSelect], protocol: [Rules.requiredSelect],
username: [Rules.requiredInput], username: [Rules.illegal],
password: [Rules.requiredInput], password: [Rules.illegal],
auth: [Rules.requiredSelect], auth: [Rules.requiredSelect],
}); });

View File

@@ -19,10 +19,10 @@
<el-option :label="$t('commons.status.success')" value="Success"></el-option> <el-option :label="$t('commons.status.success')" value="Success"></el-option>
<el-option :label="$t('commons.status.failed')" value="Failed"></el-option> <el-option :label="$t('commons.status.failed')" value="Failed"></el-option>
</el-select> </el-select>
<el-tag type="success" size="large" style="margin-left: 15px"> <el-tag v-if="searchStatus === 'All'" type="success" size="large" style="margin-left: 15px">
{{ $t('commons.status.success') }} {{ successfulCount }} {{ $t('commons.status.success') }} {{ successfulCount }}
</el-tag> </el-tag>
<el-tag type="danger" size="large" style="margin-left: 5px"> <el-tag v-if="searchStatus === 'All'" type="danger" size="large" style="margin-left: 5px">
{{ $t('commons.status.failed') }} {{ faliedCount }} {{ $t('commons.status.failed') }} {{ faliedCount }}
</el-tag> </el-tag>
</el-col> </el-col>

View File

@@ -123,14 +123,14 @@
></el-empty> ></el-empty>
</div> </div>
</el-tabs> </el-tabs>
<el-button @click="toggleFullscreen" class="fullScreen" icon="FullScreen"></el-button> <el-button @click="toggleFullscreen" v-if="!mobile" class="fullScreen" icon="FullScreen"></el-button>
<HostDialog ref="dialogRef" @on-conn-terminal="onConnTerminal" @load-host-tree="loadHostTree" /> <HostDialog ref="dialogRef" @on-conn-terminal="onConnTerminal" @load-host-tree="loadHostTree" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, getCurrentInstance, watch, nextTick } from 'vue'; import { ref, getCurrentInstance, watch, nextTick, computed } from 'vue';
import Terminal from '@/components/terminal/index.vue'; import Terminal from '@/components/terminal/index.vue';
import HostDialog from '@/views/host/terminal/terminal/host-create.vue'; import HostDialog from '@/views/host/terminal/terminal/host-create.vue';
import type Node from 'element-plus/es/components/tree/src/model/node'; import type Node from 'element-plus/es/components/tree/src/model/node';
@@ -145,15 +145,15 @@ import { GlobalStore } from '@/store';
const dialogRef = ref(); const dialogRef = ref();
const ctx = getCurrentInstance() as any; const ctx = getCurrentInstance() as any;
const globalStore = GlobalStore(); const globalStore = GlobalStore();
const mobile = computed(() => {
return globalStore.isMobile();
});
function toggleFullscreen() { function toggleFullscreen() {
if (screenfull.isEnabled) { if (screenfull.isEnabled) {
screenfull.toggle(); screenfull.toggle();
} }
} }
screenfull.on('change', () => {
globalStore.isFullScreen = screenfull.isFullscreen;
});
const localHostID = ref(); const localHostID = ref();
@@ -209,6 +209,12 @@ const acceptParams = async () => {
} }
} }
} }
if (!mobile.value) {
screenfull.on('change', () => {
globalStore.isFullScreen = screenfull.isFullscreen;
});
}
}; };
const cleanTimer = () => { const cleanTimer = () => {
clearInterval(Number(timer)); clearInterval(Number(timer));

11
go.mod
View File

@@ -17,6 +17,7 @@ require (
github.com/gin-contrib/gzip v0.0.6 github.com/gin-contrib/gzip v0.0.6
github.com/gin-contrib/i18n v0.0.1 github.com/gin-contrib/i18n v0.0.1
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/glebarez/sqlite v1.8.0
github.com/go-acme/lego/v4 v4.9.0 github.com/go-acme/lego/v4 v4.9.0
github.com/go-gormigrate/gormigrate/v2 v2.0.2 github.com/go-gormigrate/gormigrate/v2 v2.0.2
github.com/go-playground/validator/v10 v10.14.0 github.com/go-playground/validator/v10 v10.14.0
@@ -55,8 +56,7 @@ require (
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/sqlite v1.4.4 gorm.io/gorm v1.24.6
gorm.io/gorm v1.24.5
) )
require ( require (
@@ -112,6 +112,7 @@ require (
github.com/fvbommel/sortorder v1.0.2 // indirect github.com/fvbommel/sortorder v1.0.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
@@ -166,7 +167,6 @@ require (
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/mattn/go-sqlite3 v1.14.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/miekg/dns v1.1.50 // indirect github.com/miekg/dns v1.1.50 // indirect
@@ -200,6 +200,7 @@ require (
github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
github.com/rs/xid v1.4.0 // indirect github.com/rs/xid v1.4.0 // indirect
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect
@@ -258,6 +259,10 @@ require (
k8s.io/client-go v0.24.1 // indirect k8s.io/client-go v0.24.1 // indirect
k8s.io/klog/v2 v2.80.1 // indirect k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect
modernc.org/libc v1.22.3 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/sqlite v1.21.1 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect
) )

27
go.sum
View File

@@ -276,6 +276,10 @@ github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjX
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/glebarez/go-sqlite v1.21.1 h1:7MZyUPh2XTrHS7xNEHQbrhfMZuPSzhkm2A1qgg0y5NY=
github.com/glebarez/go-sqlite v1.21.1/go.mod h1:ISs8MF6yk5cL4n/43rSOmVMGJJjHYr7L2MbZZ5Q4E2E=
github.com/glebarez/sqlite v1.8.0 h1:02X12E2I/4C1n+v90yTqrjRa8yuo7c3KeHI3FRznCvc=
github.com/glebarez/sqlite v1.8.0/go.mod h1:bpET16h1za2KOOMb8+jCp6UBP/iahDpfPQqSaYLTLx8=
github.com/go-acme/lego/v4 v4.9.0 h1:8Hjj44IqRS7cigshMyFQ+0pIZvwgkG/+9A0UnNh7G8A= github.com/go-acme/lego/v4 v4.9.0 h1:8Hjj44IqRS7cigshMyFQ+0pIZvwgkG/+9A0UnNh7G8A=
github.com/go-acme/lego/v4 v4.9.0/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4= github.com/go-acme/lego/v4 v4.9.0/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -431,6 +435,7 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
@@ -504,7 +509,6 @@ github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLl
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -592,9 +596,7 @@ github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
@@ -748,6 +750,9 @@ github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdk
github.com/qiniu/go-sdk/v7 v7.14.0 h1:6icihMTKHoKMmeU1mqtIoHUv7c1LrLjYm8wTQaYDqmw= github.com/qiniu/go-sdk/v7 v7.14.0 h1:6icihMTKHoKMmeU1mqtIoHUv7c1LrLjYm8wTQaYDqmw=
github.com/qiniu/go-sdk/v7 v7.14.0/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w= github.com/qiniu/go-sdk/v7 v7.14.0/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w=
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
@@ -1339,12 +1344,10 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8= gorm.io/driver/mysql v1.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8=
gorm.io/driver/postgres v1.3.7 h1:FKF6sIMDHDEvvMF/XJvbnCl0nu6KSKUaPXevJ4r+VYQ= gorm.io/driver/postgres v1.3.7 h1:FKF6sIMDHDEvvMF/XJvbnCl0nu6KSKUaPXevJ4r+VYQ=
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc= gorm.io/driver/sqlite v1.3.2 h1:nWTy4cE52K6nnMhv23wLmur9Y3qWbZvOBz+V4PrGAxg=
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/driver/sqlserver v1.3.2 h1:yYt8f/xdAKLY7lCCyXxIUEgZ/WsURos3dHrx8MKFGAk= gorm.io/driver/sqlserver v1.3.2 h1:yYt8f/xdAKLY7lCCyXxIUEgZ/WsURos3dHrx8MKFGAk=
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE= gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1368,6 +1371,14 @@ k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2R
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
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.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.21.1 h1:GyDFqNnESLOhwwDRaHGdp2jKLDzpyT/rNLglX3ZkMSU=
modernc.org/sqlite v1.21.1/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=