Compare commits

...
Author SHA1 Message Date
seaheart 23d2d40613 fix: space dir download logic 2025-09-14 23:10:53 +08:00
lishuang f873a4aeb9 feat: upgrade to 4.1.2 2025-02-24 23:01:44 +08:00
eyeblue a49495268f Merge pull request #273 from eyebluecn/fix-batch-download
fix: space files from different space member download batch error
2025-02-24 22:58:15 +08:00
seaheart 389b1bdd2e fix: batch download 2025-02-23 17:14:55 +08:00
eyeblue 09c903ec2d Merge pull request #265 from eyebluecn/fix-space-matter-preview
fix: space matter preview token logic
2024-10-06 21:54:56 +08:00
yinyuchen b4c0455f70 fix: space matter preview token logic 2024-10-06 20:51:06 +08:00
lishuang 6f59fe2b37 feat: update golang version in docker. 2024-07-28 01:17:10 +08:00
lishuang 365853fade feat: upgrade 4.1.0 2024-07-27 23:54:12 +08:00
lishuang 837ce742e0 feat: add changelog 2024-07-27 23:50:43 +08:00
yinyuchen 11bf29fad7 feat: upgrade frontend static 2024-07-27 23:48:19 +08:00
lishuang 350e898bda fix: sort bug. 2024-07-27 22:48:24 +08:00
lishuang 3c29788980 feat: add default sort for matter search. 2024-07-27 21:43:36 +08:00
lishuang 09ff58362b feat: add the api search api. 2024-07-27 17:51:05 +08:00
lishuang 7cb6c148dd fix: soft delete bug. 2024-07-27 17:21:30 +08:00
lishuang e8cca6ea15 feat: update the go mod. 2024-07-25 19:44:30 +08:00
lishuang a067234c64 feat: finish api for search dfs. 2024-07-21 23:15:32 +08:00
lishuang b74463d9e5 feat: search dfs. 2024-07-20 16:09:26 +08:00
lishuang 28f85b4d45 fix: fix issue #232 2024-07-20 14:56:01 +08:00
lishuang e8567524ac fix: fix issue #222 2024-07-20 14:43:14 +08:00
lishuang c0dcee45d4 fix: bug for /api/user/edit 2024-07-20 13:00:54 +08:00
lishuang d51343ba8d feat: refine api for /api/space/member/create 2024-07-20 12:47:30 +08:00
yinyuchen 777a1961d0 update dingding qrcode 2024-06-26 22:54:54 +08:00
lishuang 706cb057bc feat: upgrade to tank-4.0.4 2024-05-05 20:39:43 +08:00
eyeblue 31c0356acc Merge pull request #231 from kuww/master
完善预览视频无法播放的问题
2024-05-05 19:42:31 +08:00
yinyuchen 47de9769d0 fix: share overview problem 2024-04-29 23:00:34 +08:00
lishuang ebb7ad0207 feat: upgrade to 4.0.3 2024-03-26 23:38:59 +08:00
yinyuchen 750c0983f1 chore: upgrade version 2024-03-26 23:28:29 +08:00
yinyuchen f005466293 fix: frontend dir problem 2024-03-26 23:24:07 +08:00
seaheart aacf9e05b9 add static 2023-11-12 15:39:04 +08:00
lishuang 62fba81406 feat: use go 1.18 in docker. 2023-11-10 00:24:36 +08:00
lishuang c644757135 feat: release 4.0.2 2023-11-10 00:06:02 +08:00
lishuang 53a0dcce2f feat: add the privacy false default for dir. 2023-11-09 23:48:41 +08:00
seaheart b1a948dd2a change static file 2023-11-09 23:47:41 +08:00
kuww e19fdb2ad2 完善预览视频无法播放的问题 2023-11-02 16:46:12 +08:00
lishuang b9e2f2cf48 feat: upgrade t tank-4.0.1 2023-09-09 11:56:05 +08:00
lishuang 2de7e6b2d0 feat: fix the scan bug. 2023-09-09 11:47:48 +08:00
eyeblue a2716ed8b8 Merge pull request #224 from gmugu/master
Fix "Scan Disk" abnormality
2023-09-09 11:43:39 +08:00
lishuang 73d60a0907 feat: update the dingding image. 2023-09-07 00:41:25 +08:00
lishuang 15c26a6e1f feat: update the dingding image. 2023-09-07 00:40:55 +08:00
lishuang 09ce9b811e feat: update the dingding image. 2023-09-07 00:12:06 +08:00
MuGu 05737663a2 Fix the user_uuid error in scanning disk local space. 2023-09-02 17:17:26 +08:00
MuGu 9820a1e1f3 1、Fix "Scan Disk" user_uuid error that causes WebDAV unable to display scan list.
2、Fix the problem of abnormality when scanning non-existent folders.
2023-09-01 18:46:20 +08:00
lishuang f4965c8256 feat: add the shared space feature. 2023-08-13 16:14:56 +08:00
lishuang 4892083ee9 feat: upgrade the doc img. 2023-08-13 16:13:47 +08:00
47 changed files with 500 additions and 206 deletions
+2
View File
@@ -8,3 +8,5 @@ tmp
tank
tank.exe
# mac
.DS_Store
+53
View File
@@ -51,6 +51,59 @@ c. 在github上发布新版本。
go get xxx/xxx
go mod tidy
tank-4.1.1 2024-10-6
1. 修复已知问题
tank-4.1.0 2024-7-27
1. 删除用户后,空间一并删除。 #222
2. 增加批量绑定空间成员功能。 #227
3. 共享空间增加回收站逻辑。 #232
4. 修复用户修改空间单位问题。 #233 #253
5. 增强搜索功能,会进行dfs深度优先搜索。 #239
1. When delete user, it's space will be deleted #222
2. Batch creating space member #227
3. Add feature of recycle bin for space #232
4. Fix issue of changing unit #233 #253
5. Enhance the search ability, using dfs #239
tank-4.0.4 2024-4-29
1. 修复已知问题
tank-4.0.3 2024-3-26
1. 修复已知问题
tank-4.0.2 2023-11-10
1. 修复BUG https://github.com/eyebluecn/tank/issues/229
2. 新增了分享提取码快速提取
3. 分享文件默认按照创建时间倒序排列。
1. Fix BUG https://github.com/eyebluecn/tank/issues/229
2. Fast share using code.
3. Share list order by create time desc.
tank-4.0.1 2023-09-09
1.修复一些文件扫描的bug https://github.com/eyebluecn/tank/issues/221 https://github.com/eyebluecn/tank/issues/220
1.Fix scan bug. https://github.com/eyebluecn/tank/issues/221 https://github.com/eyebluecn/tank/issues/220
tank-4.0.0 2023-08-13
1.增加空间概念,可以多人共同维护一个共享空间中的文件 #201
2.新增粘贴上传文件功能 #188
3.文件大小单位选择体验优化 #179
4.支持使用url的方式抓取文件
5.一些UI优化
1.Add space, supports multi users maintaining a shared space. #201
2.Add the feature of uploading by pasting files. #188
3.Human readable file size unit. #179
4.Add the feature of uploading by url.
6.Some user experience enhancement.
tank-4.0.0.beta1 2023-07-22
1. Shared space
+2 -2
View File
@@ -1,5 +1,5 @@
# use golang 1.17
FROM golang:1.17
# use golang 1.22.5
FROM golang:1.22.5
# maintainer. Author's name and email.
MAINTAINER eyeblue "eyebluecn@126.com"
+2 -1
View File
@@ -2,12 +2,13 @@
[English Version](https://tank-doc.eyeblue.cn/en)
# 蓝眼云盘(4.0.0)
# 蓝眼云盘(4.1.3)
[在线Demo](https://tanker.eyeblue.cn) (体验账号: demo 密码:123456)
蓝眼云盘是蓝眼开源系列代表作品之一,致力于打造精致,优雅,简约的云盘。核心功能如下:
1. 文件列表
2. 文件/文件夹上传 + 拖拽上传
3. 共享空间
3. 文件分享
4. 文件监控统计
5. 回收站
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 46 KiB

+19 -19
View File
@@ -1,27 +1,27 @@
{
"files": {
"main.css": "/static/css/main.884ffe67.css",
"main.js": "/static/js/main.e19bf6b0.js",
"main.css": "/static/css/main.8f121d4f.css",
"main.js": "/static/js/main.e27b1761.js",
"static/media/logo.png": "/static/media/logo.847e54ef7fb4b744fad4.png",
"static/media/empty.svg": "/static/media/empty.1ac6f6fb3e1d8c985f32df632ded4fe1.svg",
"static/media/psd.svg": "/static/media/psd.f2767f53369b5e75d7708a857d8381f4.svg",
"static/media/xls.svg": "/static/media/xls.9f55e13113424a3cc3316acbf072f072.svg",
"static/media/ppt.svg": "/static/media/ppt.8696321d8f3893a15681473769c8283c.svg",
"static/media/doc.svg": "/static/media/doc.017d14ee10ef3d2427494d1b0e52b03e.svg",
"static/media/pdf.svg": "/static/media/pdf.67d0061b2cef7912ed36b895cb8a328a.svg",
"static/media/video.svg": "/static/media/video.09bf3324c0e699b3611bf6bb0fc1b50e.svg",
"static/media/file.svg": "/static/media/file.b6be968e09dc04a45ec49a0dbcef53da.svg",
"static/media/text.svg": "/static/media/text.fa66aadee4b7dd9d51f4d1c863e5f75e.svg",
"static/media/audio.svg": "/static/media/audio.59dab6a9512494e5a34c8a424ab68f39.svg",
"static/media/image.svg": "/static/media/image.ee35022875b37a7962afa08bce8bc1fd.svg",
"static/media/folder.svg": "/static/media/folder.b4ce8a2960e8da846081c692d3065dff.svg",
"static/media/archive.svg": "/static/media/archive.f58837716d1fc6f2e61dc1618dad27a6.svg",
"static/media/empty.svg": "/static/media/empty.087b383305fa7993cbf2fd9ccfd2cdaf.svg",
"static/media/psd.svg": "/static/media/psd.d0769f3468941f83b2cd1491e6ac7d69.svg",
"static/media/xls.svg": "/static/media/xls.7a882f471ff07b1015705abf4945d6a3.svg",
"static/media/ppt.svg": "/static/media/ppt.b987e0e201d3a6ebb0f83c5444521626.svg",
"static/media/doc.svg": "/static/media/doc.bc5d5331ff681915068a120f067a9dec.svg",
"static/media/pdf.svg": "/static/media/pdf.c92b8a844f37db16d4a72bf62a73138c.svg",
"static/media/video.svg": "/static/media/video.a1b07956cad84a3efc508d024286b30f.svg",
"static/media/file.svg": "/static/media/file.9f6f3fda1dd60a03fe595532e845852c.svg",
"static/media/text.svg": "/static/media/text.8aeb382b647dad745f0e9c2f11c349d2.svg",
"static/media/audio.svg": "/static/media/audio.0d111f7f828e8ea78c83b47d23e771a2.svg",
"static/media/image.svg": "/static/media/image.333c8c7c7df48b383c852de5ce99c33c.svg",
"static/media/folder.svg": "/static/media/folder.fce5913565dc18a92c7bbbe19ba6c4d0.svg",
"static/media/archive.svg": "/static/media/archive.982c48675c38139800a2f45d7c86a661.svg",
"index.html": "/index.html",
"main.884ffe67.css.map": "/static/css/main.884ffe67.css.map",
"main.e19bf6b0.js.map": "/static/js/main.e19bf6b0.js.map"
"main.8f121d4f.css.map": "/static/css/main.8f121d4f.css.map",
"main.e27b1761.js.map": "/static/js/main.e27b1761.js.map"
},
"entrypoints": [
"static/css/main.884ffe67.css",
"static/js/main.e19bf6b0.js"
"static/css/main.8f121d4f.css",
"static/js/main.e27b1761.js"
]
}
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="eyeblue tank"/><title>EyeblueTank</title><script defer="defer" src="/static/js/main.e19bf6b0.js"></script><link href="/static/css/main.884ffe67.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run EyeblueTank.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="eyeblue tank"/><title>EyeblueTank</title><script defer="defer" src="/static/js/main.e27b1761.js"></script><link href="/static/css/main.8f121d4f.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run EyeblueTank.</noscript><div id="root"></div></body></html>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 855 B

After

Width:  |  Height:  |  Size: 855 B

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 949 B

After

Width:  |  Height:  |  Size: 949 B

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

+1 -1
View File
@@ -11,7 +11,7 @@
@REM prepare the variables.
@REM version name
SET VERSION_NAME=tank-4.0.0
SET VERSION_NAME=tank-4.1.3
ECHO VERSION_NAME: %VERSION_NAME%
@REM golang proxy
SET GOPROXY=https://goproxy.cn
+1 -1
View File
@@ -10,7 +10,7 @@ set -e
#prepare the variables.
# version name
VERSION_NAME=tank-4.0.0
VERSION_NAME=tank-4.1.3
echo "VERSION_NAME: ${VERSION_NAME}"
# golang proxy
GOPROXY=https://goproxy.cn
+3 -3
View File
@@ -11,10 +11,10 @@ const (
DEFAULT_SERVER_PORT = 6010
//db table's prefix. tank40_ means current version is tank:4.0.x
TABLE_PREFIX = "tank40_"
//db table's prefix. tank41_ means current version is tank:4.1.x
TABLE_PREFIX = "tank41_"
VERSION = "4.0.0"
VERSION = "4.1.2"
)
type Config interface {
+4 -2
View File
@@ -99,8 +99,10 @@ func (this *AlienService) ValidMatter(
}
tokenUser := this.userDao.CheckByUuid(downloadToken.UserUuid)
if matter.UserUuid != tokenUser.Uuid {
panic(result.UNAUTHORIZED)
if matter.SpaceUuid != tokenUser.SpaceUuid {
//whether user has the space's read auth.
this.spaceService.CheckReadableByUuid(request, tokenUser, matter.SpaceUuid)
}
//TODO: expire the download token. If download by chunk, do this later.
+52 -1
View File
@@ -6,6 +6,7 @@ import (
"github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util"
"net/http"
"sort"
"strings"
)
@@ -77,6 +78,7 @@ func (this *MatterController) RegisterRoutes() map[string]func(writer http.Respo
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
routeMap["/api/matter/detail"] = this.Wrap(this.Detail, USER_ROLE_USER)
routeMap["/api/matter/page"] = this.Wrap(this.Page, USER_ROLE_USER)
routeMap["/api/matter/search"] = this.Wrap(this.Search, USER_ROLE_USER)
routeMap["/api/matter/create/directory"] = this.Wrap(this.CreateDirectory, USER_ROLE_USER)
routeMap["/api/matter/upload"] = this.Wrap(this.Upload, USER_ROLE_USER)
@@ -174,6 +176,55 @@ func (this *MatterController) Page(writer http.ResponseWriter, request *http.Req
return this.Success(pager)
}
// DFS search.
func (this *MatterController) Search(writer http.ResponseWriter, request *http.Request) *result.WebResult {
limit := util.ExtractRequestOptionalInt(request, "limit", 200)
puuid := util.ExtractRequestString(request, "puuid")
keyword := util.ExtractRequestString(request, "keyword")
deletedStr := util.ExtractRequestOptionalString(request, "deleted", "")
var deleted = false
if deletedStr == TRUE {
deleted = true
}
user := this.checkUser(request)
spaceUuid := util.ExtractRequestOptionalString(request, "spaceUuid", user.SpaceUuid)
this.spaceService.CheckReadableByUuid(request, user, spaceUuid)
matters := this.matterService.DfsSearch(
request,
limit,
puuid,
keyword,
spaceUuid,
deleted,
)
//sort.
//从小到大自定义排序
sort.Slice(matters, func(i, j int) bool {
matter1 := matters[i]
matter2 := matters[j]
if matter1.Dir {
return true
} else if matter2.Dir {
return false
} else {
cmp := strings.Compare(matter1.Name, matter2.Name)
if cmp < 0 {
return true
} else if cmp == 0 {
return true
} else {
return false
}
}
})
return this.Success(matters)
}
func (this *MatterController) CreateDirectory(writer http.ResponseWriter, request *http.Request) *result.WebResult {
puuid := util.ExtractRequestString(request, "puuid")
@@ -301,7 +352,7 @@ func (this *MatterController) SoftDeleteBatch(writer http.ResponseWriter, reques
continue
}
if matter.UserUuid != user.Uuid {
if matter.SpaceUuid != spaceUuid {
panic(result.UNAUTHORIZED)
}
+68 -6
View File
@@ -1,15 +1,16 @@
package rest
import (
"math"
"os"
"time"
"github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/builder"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util"
"github.com/eyebluecn/tank/code/tool/uuid"
"gorm.io/gorm"
"math"
"os"
"time"
)
type MatterDao struct {
@@ -205,6 +206,38 @@ func (this *MatterDao) CountBySpaceUuidAndPuuidAndDirAndName(spaceUuid string, p
return int(count)
}
func (this *MatterDao) FindBySpaceUuidAndPuuidAndDirAndName(spaceUuid string, puuid string, dir bool, name string) *Matter {
var matter = &Matter{}
var wp = &builder.WherePair{}
if puuid != "" {
wp = wp.And(&builder.WherePair{Query: "puuid = ?", Args: []interface{}{puuid}})
}
if spaceUuid != "" {
wp = wp.And(&builder.WherePair{Query: "space_uuid = ?", Args: []interface{}{spaceUuid}})
}
if name != "" {
wp = wp.And(&builder.WherePair{Query: "name = ?", Args: []interface{}{name}})
}
wp = wp.And(&builder.WherePair{Query: "dir = ?", Args: []interface{}{dir}})
db := core.CONTEXT.GetDB().Where(wp.Query, wp.Args...).First(matter)
if db.Error != nil {
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
return nil
} else {
this.PanicError(db.Error)
}
}
return matter
}
func (this *MatterDao) FindByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir string, name string) *Matter {
var matter = &Matter{}
@@ -279,6 +312,32 @@ func (this *MatterDao) FindBySpaceNameAndPuuidAndDirAndName(spaceName string, pu
return matter
}
func (this *MatterDao) FindByPuuidAndSpaceUuid(puuid string, spaceUuid string, sortArray []builder.OrderPair) []*Matter {
var matters []*Matter
var wp = &builder.WherePair{}
wp = wp.And(&builder.WherePair{Query: "puuid = ? AND space_uuid = ?", Args: []interface{}{puuid, spaceUuid}})
if sortArray == nil {
sortArray = []builder.OrderPair{
{
Key: "dir",
Value: DIRECTION_DESC,
},
{
Key: "create_time",
Value: DIRECTION_DESC,
},
}
}
db := core.CONTEXT.GetDB().Model(&Matter{}).Where(wp.Query, wp.Args...).Order(this.GetSortString(sortArray)).Find(&matters)
this.PanicError(db.Error)
return matters
}
func (this *MatterDao) FindByPuuidAndUserUuid(puuid string, userUuid string, sortArray []builder.OrderPair) []*Matter {
return this.FindByPuuidAndUserUuidAndDeleted(puuid, userUuid, "", sortArray)
}
@@ -322,6 +381,8 @@ func (this *MatterDao) FindByUuids(uuids []string, sortArray []builder.OrderPair
return matters
}
// pagination is 0 base.
func (this *MatterDao) PlainPage(
page int,
pageSize int,
@@ -518,9 +579,10 @@ func (this *MatterDao) Delete(matter *Matter) {
//delete from db.
db := core.CONTEXT.GetDB().Delete(&matter)
this.PanicError(db.Error)
//delete dir from disk.
util.DeleteEmptyDir(matter.AbsolutePath())
if util.PathExists(matter.AbsolutePath()) {
//delete dir from disk.
util.DeleteEmptyDir(matter.AbsolutePath())
}
} else {
+79 -16
View File
@@ -3,12 +3,6 @@ package rest
import (
"archive/zip"
"fmt"
"github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/builder"
"github.com/eyebluecn/tank/code/tool/download"
"github.com/eyebluecn/tank/code/tool/i18n"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util"
"io"
"io/ioutil"
"mime/multipart"
@@ -19,6 +13,13 @@ import (
"regexp"
"strings"
"time"
"github.com/eyebluecn/tank/code/core"
"github.com/eyebluecn/tank/code/tool/builder"
"github.com/eyebluecn/tank/code/tool/download"
"github.com/eyebluecn/tank/code/tool/i18n"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util"
)
/**
@@ -137,6 +138,62 @@ func (this *MatterService) Page(
return pager
}
// search files by dfs.
func (this *MatterService) DfsSearch(
request *http.Request,
limit int,
puuid string,
keyword string,
spaceUuid string,
deleted bool,
) []*Matter {
deletedStr := FALSE
if deleted {
deletedStr = FALSE
}
//find all matters including dir and files.
_, matters := this.matterDao.PlainPage(0, limit, puuid, "", spaceUuid, keyword, "", deletedStr, nil, nil, nil)
if len(matters) >= limit {
return matters
}
var resultList = make([]*Matter, 0)
for _, matter := range matters {
if !matter.Dir {
resultList = append(resultList, matter)
}
}
//dfs from puuid.
_, dirMatters := this.matterDao.PlainPage(0, 1000, puuid, "", spaceUuid, "", TRUE, "", nil, nil, nil)
for _, dirMatter := range dirMatters {
//add dir if match.
if strings.Contains(dirMatter.Name, keyword) {
if deleted == dirMatter.Deleted {
resultList = append(resultList, dirMatter)
}
}
remainLimit := limit - len(resultList)
subMatters := this.DfsSearch(request, remainLimit, dirMatter.Uuid, keyword, spaceUuid, deleted)
for _, subMatter := range subMatters {
resultList = append(resultList, subMatter)
}
//enough then return
if len(resultList) >= limit {
return resultList
}
}
//not enough ,return.
return resultList
}
// Download. Support chunk download.
func (this *MatterService) DownloadFile(
writer http.ResponseWriter,
@@ -157,12 +214,12 @@ func (this *MatterService) DownloadZip(
if matters == nil || len(matters) == 0 {
panic(result.BadRequest("matters cannot be nil."))
}
userUuid := matters[0].UserUuid
spaceUuid := matters[0].SpaceUuid
puuid := matters[0].Puuid
for _, m := range matters {
if m.UserUuid != userUuid {
panic(result.BadRequest("userUuid not same"))
if m.SpaceUuid != spaceUuid {
panic(result.BadRequest("spaceUuid not same"))
} else if m.Puuid != puuid {
panic(result.BadRequest("puuid not same"))
}
@@ -229,13 +286,13 @@ func (this *MatterService) zipMatters(request *http.Request, matters []*Matter,
if matters == nil || len(matters) == 0 {
panic(result.BadRequest("matters cannot be nil."))
}
userUuid := matters[0].UserUuid
spaceUuid := matters[0].SpaceUuid
puuid := matters[0].Puuid
baseDirPath := util.GetDirOfPath(matters[0].AbsolutePath()) + "/"
for _, m := range matters {
if m.UserUuid != userUuid {
panic(result.BadRequest("userUuid not same"))
if m.SpaceUuid != spaceUuid {
panic(result.BadRequest("spaceUuid not same"))
} else if m.Puuid != puuid {
panic(result.BadRequest("puuid not same"))
}
@@ -449,9 +506,14 @@ func (this *MatterService) Upload(request *http.Request, file io.Reader, fileHea
dirAbsolutePath := dirMatter.AbsolutePath()
count := this.matterDao.CountBySpaceUuidAndPuuidAndDirAndName(space.Uuid, dirMatter.Uuid, false, filename)
if count > 0 {
panic(result.BadRequestI18n(request, i18n.MatterExist, filename))
dbMatter := this.matterDao.FindBySpaceUuidAndPuuidAndDirAndName(space.Uuid, dirMatter.Uuid, false, filename)
if dbMatter != nil {
if dbMatter.Deleted {
panic(result.BadRequestI18n(request, i18n.MatterRecycleBinExist, filename))
} else {
panic(result.BadRequestI18n(request, i18n.MatterExist, filename))
}
}
fileAbsolutePath := dirAbsolutePath + "/" + filename
@@ -700,6 +762,7 @@ func (this *MatterService) createDirectory(request *http.Request, dirMatter *Mat
Dir: true,
Name: name,
Path: relativePath,
Privacy: false,
VisitTime: time.Now(),
}
@@ -1198,7 +1261,7 @@ func (this *MatterService) WrapChildrenDetail(request *http.Request, matter *Mat
if matter.Dir {
children := this.matterDao.FindByPuuidAndUserUuid(matter.Uuid, matter.UserUuid, nil)
children := this.matterDao.FindByPuuidAndSpaceUuid(matter.Uuid, matter.SpaceUuid, nil)
matter.Children = children
for _, child := range matter.Children {
+7
View File
@@ -183,6 +183,13 @@ func (this *SpaceDao) PageHandle(fun func(space *Space)) {
}
}
func (this *SpaceDao) DeleteByUserUuid(userUuid string) {
db := core.CONTEXT.GetDB().Where("user_uuid = ?", userUuid).Delete(Space{})
this.PanicError(db.Error)
}
func (this *SpaceDao) Delete(space *Space) {
db := core.CONTEXT.GetDB().Delete(&space)
+21 -7
View File
@@ -7,6 +7,7 @@ import (
"github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util"
"net/http"
"strings"
)
type SpaceMemberController struct {
@@ -73,13 +74,20 @@ func (this *SpaceMemberController) RegisterRoutes() map[string]func(writer http.
func (this *SpaceMemberController) Create(writer http.ResponseWriter, request *http.Request) *result.WebResult {
spaceUuid := util.ExtractRequestString(request, "spaceUuid")
userUuid := util.ExtractRequestString(request, "userUuid")
userUuidsStr := util.ExtractRequestString(request, "userUuids")
spaceRole := util.ExtractRequestString(request, "role")
if spaceRole != SPACE_MEMBER_ROLE_READ_ONLY && spaceRole != SPACE_MEMBER_ROLE_READ_WRITE && spaceRole != SPACE_MEMBER_ROLE_ADMIN {
panic("spaceRole is not correct")
}
//validate userUuids
if userUuidsStr == "" {
panic("userUuids is required")
}
userUuids := strings.Split(userUuidsStr, ",")
// check operator's permission
currentUser := this.checkUser(request)
canManage := this.spaceMemberService.canManage(currentUser, spaceUuid)
if !canManage {
@@ -87,18 +95,24 @@ func (this *SpaceMemberController) Create(writer http.ResponseWriter, request *h
}
//check whether exists.
spaceMember := this.spaceMemberDao.FindBySpaceUuidAndUserUuid(spaceUuid, userUuid)
if spaceMember != nil {
panic(result.BadRequestI18n(request, i18n.SpaceMemberExist))
for _, userUuid := range userUuids {
spaceMember := this.spaceMemberDao.FindBySpaceUuidAndUserUuid(spaceUuid, userUuid)
user := this.userDao.CheckByUuid(userUuid)
if spaceMember != nil {
panic(result.BadRequestI18n(request, i18n.SpaceMemberExist, user.Username))
}
}
//check whether space exists.
space := this.spaceDao.CheckByUuid(spaceUuid)
user := this.userDao.CheckByUuid(userUuid)
spaceMember = this.spaceMemberService.CreateMember(space, user, spaceRole)
//check whether exists.
for _, userUuid := range userUuids {
user := this.userDao.CheckByUuid(userUuid)
this.spaceMemberService.CreateMember(space, user, spaceRole)
}
return this.Success(spaceMember)
return this.Success("OK")
}
func (this *SpaceMemberController) Edit(writer http.ResponseWriter, request *http.Request) *result.WebResult {
+14 -10
View File
@@ -132,19 +132,19 @@ func (this *TaskService) doScanTask() {
this.logger.Info("scan spaceName = %s", space.Name)
//find admin as operator.
adminUser := this.userDao.FindAnAdmin()
this.matterService.DeleteByPhysics(request, adminUser, space)
this.matterService.ScanPhysics(request, adminUser, space)
//find user by space
user := this.userDao.FindByUuid(space.UserUuid)
if user == nil {
user = this.userDao.FindAnAdmin()
}
this.matterService.DeleteByPhysics(request, user, space)
this.matterService.ScanPhysics(request, user, space)
})
})
} else if scanConfig.Scope == SCAN_SCOPE_CUSTOM {
//scan custom user's folder.
//find admin as operator.
adminUser := this.userDao.FindAnAdmin()
for _, spaceName := range scanConfig.SpaceNames {
space := this.spaceDao.FindByName(spaceName)
@@ -154,9 +154,13 @@ func (this *TaskService) doScanTask() {
this.logger.Info("scan custom user folder. spaceName = %s", spaceName)
core.RunWithRecovery(func() {
this.matterService.DeleteByPhysics(request, adminUser, space)
this.matterService.ScanPhysics(request, adminUser, space)
//find user by space
user := this.userDao.FindByUuid(space.UserUuid)
if user == nil {
user = this.userDao.FindAnAdmin()
}
this.matterService.DeleteByPhysics(request, user, space)
this.matterService.ScanPhysics(request, user, space)
})
+35 -5
View File
@@ -64,6 +64,7 @@ func (this *UserController) RegisterRoutes() map[string]func(writer http.Respons
routeMap["/api/user/change/password"] = this.Wrap(this.ChangePassword, USER_ROLE_USER)
routeMap["/api/user/reset/password"] = this.Wrap(this.ResetPassword, USER_ROLE_ADMINISTRATOR)
routeMap["/api/user/page"] = this.Wrap(this.Page, USER_ROLE_ADMINISTRATOR)
routeMap["/api/user/search"] = this.Wrap(this.Search, USER_ROLE_USER)
routeMap["/api/user/toggle/status"] = this.Wrap(this.ToggleStatus, USER_ROLE_ADMINISTRATOR)
routeMap["/api/user/transfiguration"] = this.Wrap(this.Transfiguration, USER_ROLE_ADMINISTRATOR)
routeMap["/api/user/scan"] = this.Wrap(this.Scan, USER_ROLE_ADMINISTRATOR)
@@ -240,19 +241,31 @@ func (this *UserController) Edit(writer http.ResponseWriter, request *http.Reque
sizeLimit := util.ExtractRequestInt64(request, "sizeLimit")
totalSizeLimit := util.ExtractRequestInt64(request, "totalSizeLimit")
user := this.checkUser(request)
operator := this.checkUser(request)
currentUser := this.userDao.CheckByUuid(uuid)
currentUser.AvatarUrl = avatarUrl
if user.Role == USER_ROLE_ADMINISTRATOR {
//only admin can edit user's sizeLimit
if operator.Role == USER_ROLE_ADMINISTRATOR {
//only admin can edit user's role and sizeLimit
if role == USER_ROLE_USER || role == USER_ROLE_ADMINISTRATOR {
currentUser.Role = role
}
} else if user.Uuid != uuid {
} else if operator.Uuid == uuid {
//cannot edit sizeLimit, totalSizeLimit
space := this.spaceDao.CheckByUuid(currentUser.SpaceUuid)
if space.SizeLimit != sizeLimit {
this.logger.Error(" %s try to modify sizeLimit from %d to %d.", operator.Uuid, space.SizeLimit, sizeLimit)
panic(result.BadRequestI18n(request, i18n.PermissionDenied))
}
if space.TotalSizeLimit != totalSizeLimit {
this.logger.Error(" %s try to modify TotalSizeLimit from %d to %d.", operator.Uuid, space.TotalSizeLimit, totalSizeLimit)
panic(result.BadRequestI18n(request, i18n.PermissionDenied))
}
} else {
panic(result.UNAUTHORIZED)
}
@@ -260,7 +273,7 @@ func (this *UserController) Edit(writer http.ResponseWriter, request *http.Reque
currentUser = this.userDao.Save(currentUser)
//edit user's private space info.
space := this.spaceService.Edit(request, user, currentUser.SpaceUuid, sizeLimit, totalSizeLimit)
space := this.spaceService.Edit(request, operator, currentUser.SpaceUuid, sizeLimit, totalSizeLimit)
//remove cache user.
this.userService.RemoveCacheUserByUuid(currentUser.Uuid)
@@ -373,6 +386,23 @@ func (this *UserController) Page(writer http.ResponseWriter, request *http.Reque
return this.Success(pager)
}
func (this *UserController) Search(writer http.ResponseWriter, request *http.Request) *result.WebResult {
keyword := request.FormValue("keyword")
pager := this.userDao.Page(0, 10, keyword, "", nil)
var resultList []*User = make([]*User, 0)
for _, u := range pager.Data.([]*User) {
resultList = append(resultList, &User{
Uuid: u.Uuid,
Username: u.Username,
})
}
return this.Success(resultList)
}
func (this *UserController) ToggleStatus(writer http.ResponseWriter, request *http.Request) *result.WebResult {
uuid := request.FormValue("uuid")
+20
View File
@@ -25,6 +25,8 @@ type UserService struct {
matterDao *MatterDao
matterService *MatterService
imageCacheDao *ImageCacheDao
spaceDao *SpaceDao
spaceMemberDao *SpaceMemberDao
shareDao *ShareDao
shareService *ShareService
downloadTokenDao *DownloadTokenDao
@@ -65,6 +67,15 @@ func (this *UserService) Init() {
this.imageCacheDao = b
}
b = core.CONTEXT.GetBean(this.spaceDao)
if b, ok := b.(*SpaceDao); ok {
this.spaceDao = b
}
b = core.CONTEXT.GetBean(this.spaceMemberDao)
if b, ok := b.(*SpaceMemberDao); ok {
this.spaceMemberDao = b
}
b = core.CONTEXT.GetBean(this.shareService)
if b, ok := b.(*ShareService); ok {
this.shareService = b
@@ -300,6 +311,15 @@ func (this *UserService) DeleteUser(request *http.Request, currentUser *User) {
this.logger.Info("delete matters")
this.matterDao.DeleteByUserUuid(currentUser.Uuid)
//delete space members
space := this.spaceDao.CheckByUuid(currentUser.SpaceUuid)
this.logger.Info("delete space members")
this.spaceMemberDao.DeleteBySpaceUuid(space.Uuid)
//delete spaces
this.logger.Info("delete spaces")
this.spaceDao.DeleteByUserUuid(currentUser.Uuid)
//delete this user
this.logger.Info("delete this user.")
this.userDao.Delete(currentUser)
+1 -1
View File
@@ -10,7 +10,7 @@ import (
func TestHello(t *testing.T) {
split := strings.Split("good", "/")
split := strings.Split("", "")
fmt.Printf("%v", split)
var i int
+5 -2
View File
@@ -3,8 +3,6 @@ package download
import (
"errors"
"fmt"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util"
"io"
"mime/multipart"
"net/http"
@@ -14,6 +12,9 @@ import (
"strconv"
"strings"
"time"
"github.com/eyebluecn/tank/code/tool/result"
"github.com/eyebluecn/tank/code/tool/util"
)
// HttpRange specifies the byte range to be sent to the client.
@@ -313,6 +314,8 @@ func DownloadFile(
sendSize = ra.length
code = http.StatusPartialContent
writer.Header().Set("Content-Range", ra.contentRange(size))
// 把文件放进去
io.CopyN(writer, diskFile, int64(ra.length))
case len(ranges) > 1:
sendSize = RangesMIMESize(ranges, ctype, size)
code = http.StatusPartialContent
+2 -1
View File
@@ -34,6 +34,7 @@ var (
UserDisabled = &Item{English: `user has been disabled`, Chinese: `用户已经被禁用了`}
MatterDestinationMustDirectory = &Item{English: `destination must be directory'`, Chinese: `目标对象只能是文件夹。`}
MatterExist = &Item{English: `"%s" already exists, invalid operation`, Chinese: `"%s" 已经存在了,操作无效`}
MatterRecycleBinExist = &Item{English: `"%s" already exists in recycle bin, invalid operation`, Chinese: `"%s" 已经存在于回收站,请彻底删除后再操作`}
MatterDepthExceedLimit = &Item{English: `directory's depth exceed the limit %d > %d`, Chinese: `文件加层数超过限制 %d > %d `}
MatterNameLengthExceedLimit = &Item{English: `filename's length exceed the limit %d > %d`, Chinese: `文件名称长度超过限制 %d > %d `}
MatterSelectNumExceedLimit = &Item{English: `selected files' num exceed the limit %d > %d`, Chinese: `选择的文件数量超出限制了 %d > %d `}
@@ -50,7 +51,7 @@ var (
SpaceNameError = &Item{English: `space's name can only be letters, numbers or _`, Chinese: `空间名称必填,且只能包含中文,字母,数字和'_'`}
SpaceNameExist = &Item{English: `space's name "%s" exists`, Chinese: `空间名称"%s"已被占用,请使用其他名字`}
SpaceExclusive = &Item{English: `user can only own ONE space`, Chinese: `一个用户只能拥有一个私有空间`}
SpaceMemberExist = &Item{English: `space member exists`, Chinese: `该用于已经是空间的成员`}
SpaceMemberExist = &Item{English: `space member %s exists`, Chinese: `用户 %s 已经是空间的成员`}
PermissionDenied = &Item{English: `permission denied.`, Chinese: `没有操作权限`}
)
+28 -6
View File
@@ -1,14 +1,36 @@
module github.com/eyebluecn/tank
go 1.14
go 1.22.5
require (
github.com/disintegration/imaging v1.6.2
github.com/glebarez/sqlite v1.8.0
github.com/glebarez/sqlite v1.11.0
github.com/json-iterator/go v1.1.12
github.com/robfig/cron/v3 v3.0.1
golang.org/x/crypto v0.1.0
golang.org/x/text v0.4.0
gorm.io/driver/mysql v1.3.2
gorm.io/gorm v1.25.1
golang.org/x/crypto v0.25.0
golang.org/x/text v0.16.0
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.11
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/glebarez/go-sqlite v1.22.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.22.0 // indirect
modernc.org/libc v1.55.4 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/sqlite v1.31.1 // indirect
)
+72 -113
View File
@@ -1,48 +1,41 @@
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=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
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/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
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/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-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
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/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
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/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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
@@ -50,93 +43,59 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
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-20220722155255-886fb9371eb4/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-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-20210615035016-665e8c7367d1/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I=
gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64=
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
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.2/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
modernc.org/libc v1.21.4/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/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
modernc.org/ccgo/v4 v4.20.4 h1:3pPOlMcblnu5CBU3w1BFtepwBnLezGjPYTH8xBeYZM8=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v2 v2.4.3 h1:Ik4ZcMbC7aY4ZDPUhzXVXi7GMub9QcXLTfXn3mWpNw8=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
modernc.org/libc v1.55.4 h1:iWzZ96v1Iut2Sm4lEp0yzFde20M9zpcI0wY3DFcb+8g=
modernc.org/libc v1.55.4/go.mod h1:GPuVtbWvXUo590z/xfVIQcOqnugb1WovSfMJWMe0ZfA=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.21.1 h1:GyDFqNnESLOhwwDRaHGdp2jKLDzpyT/rNLglX3ZkMSU=
modernc.org/sqlite v1.21.1/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.15.1/go.mod h1:aEjeGJX2gz1oWKOLDVZ2tnEWLUrIn8H+GFu+akoDhqs=
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/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.30.2 h1:IPVVkhLu5mMVnS1dQgh3h0SAACRWcVk7aoLP9Us3UCk=
modernc.org/sqlite v1.30.2/go.mod h1:DUmsiWQDaAvU4abhc/N+djlom/L2o8f7gZ95RCvyoLU=
modernc.org/sqlite v1.31.1 h1:XVU0VyzxrYHlBhIs1DiEgSl0ZtdnPtbLVy8hSkzxGrs=
modernc.org/sqlite v1.31.1/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=