From cf0c32b3b61850e0c607946fcc00a15aca0f5d7f Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 26 Mar 2026 11:00:01 +0000 Subject: [PATCH 1/3] tidy up exports cmds Signed-off-by: Ben --- .gitignore | 5 +- cmd/exportguild/main.go | 162 +++++--- cmd/exportuser/main.go | 376 +++--------------- cmd/exportuser/userdata.go | 2 - go.mod | 50 +-- go.sum | 135 ++++--- internal/util/util.go | 50 +++ pkg/config/config.go | 3 +- .../export/user}/cachedata.go | 10 +- pkg/export/user/must.go | 7 + pkg/export/user/userdata.go | 248 ++++++++++++ pkg/s3client/errors.go | 1 + 12 files changed, 594 insertions(+), 455 deletions(-) delete mode 100644 cmd/exportuser/userdata.go create mode 100644 internal/util/util.go rename {cmd/exportuser => pkg/export/user}/cachedata.go (63%) create mode 100644 pkg/export/user/must.go create mode 100644 pkg/export/user/userdata.go diff --git a/.gitignore b/.gitignore index 5e7de82..cad17ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .idea *.iml .env -export/ -export_user/ +exports/ /new -/done.json \ No newline at end of file +/done.json diff --git a/cmd/exportguild/main.go b/cmd/exportguild/main.go index d1e9982..9ec9709 100644 --- a/cmd/exportguild/main.go +++ b/cmd/exportguild/main.go @@ -9,14 +9,16 @@ import ( "os" "strconv" "strings" + "sync/atomic" + "github.com/TicketsBot-cloud/common/encryption" "github.com/TicketsBot-cloud/gdl/objects/channel/message" + "github.com/TicketsBot-cloud/logarchiver/internal/util" "github.com/TicketsBot-cloud/logarchiver/pkg/config" "github.com/TicketsBot-cloud/logarchiver/pkg/model" v1 "github.com/TicketsBot-cloud/logarchiver/pkg/model/v1" - v22 "github.com/TicketsBot-cloud/logarchiver/pkg/model/v2" + v2 "github.com/TicketsBot-cloud/logarchiver/pkg/model/v2" "github.com/TicketsBot-cloud/logarchiver/pkg/s3client" - "github.com/TicketsBot/common/encryption" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "golang.org/x/sync/errgroup" @@ -37,7 +39,15 @@ func main() { flag.Parse() conf := config.Parse[config.CliConfig]() - // create minio client + fmt.Printf("Exporting guild data for %d\n", *guildId) + + exportDir := fmt.Sprintf("exports/guild/%d", *guildId) + + if err := os.MkdirAll(exportDir, 0700); err != nil { + panic(fmt.Sprintf("failed to create export directory: %v", err)) + } + + fmt.Println("[1/3] Connecting to S3...") m, err := minio.New(conf.Endpoint, &minio.Options{ Creds: credentials.NewStaticV4(conf.AccessKey, conf.SecretKey, ""), Secure: conf.Secure, @@ -48,23 +58,24 @@ func main() { client := s3client.NewS3Client(m, conf.Bucket) - // likely to be file exists - _ = os.Mkdir(fmt.Sprintf("export/%d", *guildId), 0) - + fmt.Println("[2/3] Exporting transcripts...") if ticketId != nil && *ticketId > 0 { - export(*ticketId, client) + exportTicket(*ticketId, client, exportDir) + printProgress(1, 1, 0, 0) } else { keys, err := client.GetAllKeysForGuild(context.Background(), *guildId) if err != nil { panic(err) } + total := int64(len(keys)) + var processed, skipped, failed atomic.Int64 + keyCh := make(chan string) go func() { for _, key := range keys { keyCh <- key } - close(keyCh) }() @@ -73,14 +84,34 @@ func main() { group.Go(func() error { for key := range keyCh { id := key[strings.LastIndex(key, "/")+1:] + if id == "" { + skipped.Add(1) + processed.Add(1) + printProgress(int(processed.Load()), int(total), int(skipped.Load()), int(failed.Load())) + continue + } + parsed, err := strconv.Atoi(id) - must(err) + if err != nil { + skipped.Add(1) + processed.Add(1) + printProgress(int(processed.Load()), int(total), int(skipped.Load()), int(failed.Load())) + continue + } if after != nil && *after > 0 && parsed < *after { + skipped.Add(1) + processed.Add(1) + printProgress(int(processed.Load()), int(total), int(skipped.Load()), int(failed.Load())) continue } - export(parsed, client) + if ok := exportTicket(parsed, client, exportDir); !ok { + failed.Add(1) + } + + processed.Add(1) + printProgress(int(processed.Load()), int(total), int(skipped.Load()), int(failed.Load())) } return nil @@ -91,83 +122,110 @@ func main() { panic(err) } } + + fmt.Println("[3/3] Creating zip archive...") + zipPath := fmt.Sprintf("exports/guild/%d.zip", *guildId) + if err := util.ZipFiles(exportDir, zipPath); err != nil { + panic(fmt.Sprintf("could not zip files: %v", err)) + } + + os.RemoveAll(exportDir) + fmt.Printf("Done! Export saved to %s\n", zipPath) } -func export(id int, client *s3client.S3Client) { - data, err := client.GetTicket(context.Background(), *guildId, id) - must(err) +func printProgress(current, total, skipped, failed int) { + const barWidth = 40 + filled := barWidth * current / total + bar := make([]byte, barWidth) + for i := range bar { + if i < filled { + bar[i] = '#' + } else { + bar[i] = '-' + } + } + fmt.Printf("\r[%s] %d/%d transcripts (skipped: %d, failed: %d)", bar, current, total, skipped, failed) + if current == total { + fmt.Println() + } +} - data, err = encryption.Decompress(data) - must(err) +// exportTicket exports a single ticket and returns true on success. +func exportTicket(id int, client *s3client.S3Client, exportDir string) bool { + data, err := client.GetTicket(context.Background(), *guildId, id) + if err != nil { + return false + } data, err = encryption.Decrypt([]byte(*key), data) - must(err) + if err != nil { + return false + } if *convert || (userWhitelist != nil && *userWhitelist > 0) { - var transcript v22.Transcript + var transcript v2.Transcript version := model.GetVersion(data) switch version { case model.V1: var messages []message.Message if err := json.Unmarshal(data, &messages); err != nil { - panic(err) + return false } transcript = v1.ConvertToV2(messages) case model.V2: if err := json.Unmarshal(data, &transcript); err != nil { - panic(err) + return false } default: - panic(fmt.Sprintf("Unknown version %d", version)) - } - - data, err = json.Marshal(transcript) - must(err) - } - - if userWhitelist != nil && *userWhitelist > 0 { - var transcript v22.Transcript - if err := json.Unmarshal(data, &transcript); err != nil { - panic(err) + return false } - transcript.Entities.Channels = nil - transcript.Entities.Roles = nil + if userWhitelist != nil && *userWhitelist > 0 { + transcript.Entities.Channels = nil + transcript.Entities.Roles = nil - user, ok := transcript.Entities.Users[*userWhitelist] - if !ok { - transcript.Entities.Users = nil - } else { - transcript.Entities.Users = map[uint64]v22.User{ - user.Id: user, + user, ok := transcript.Entities.Users[*userWhitelist] + if !ok { + transcript.Entities.Users = nil + } else { + transcript.Entities.Users = map[uint64]v2.User{ + user.Id: user, + } } - } - var messages []v22.Message - for _, message := range transcript.Messages { - if message.AuthorId == *userWhitelist { - messages = append(messages, message) + var messages []v2.Message + for _, message := range transcript.Messages { + if message.AuthorId == *userWhitelist { + messages = append(messages, message) + } } - } - transcript.Messages = messages + transcript.Messages = messages + } data, err = json.Marshal(transcript) - must(err) + if err != nil { + return false + } } var encoded bytes.Buffer - must(json.Indent(&encoded, data, "", " ")) + if err := json.Indent(&encoded, data, "", " "); err != nil { + return false + } - f, err := os.Create(fmt.Sprintf("export/%d/%d.json", *guildId, id)) - must(err) + f, err := os.Create(fmt.Sprintf("%s/%d.json", exportDir, id)) + if err != nil { + return false + } - _, err = encoded.WriteTo(f) - must(err) + if _, err := encoded.WriteTo(f); err != nil { + return false + } - fmt.Printf("exported %d\n", id) + return true } func must(err error) { diff --git a/cmd/exportuser/main.go b/cmd/exportuser/main.go index 1f74977..93131c1 100644 --- a/cmd/exportuser/main.go +++ b/cmd/exportuser/main.go @@ -7,22 +7,21 @@ import ( "flag" "fmt" "os" - "strconv" - "time" + "github.com/TicketsBot-cloud/common/encryption" + "github.com/TicketsBot-cloud/database" "github.com/TicketsBot-cloud/gdl/cache" "github.com/TicketsBot-cloud/gdl/objects/channel/message" + "github.com/TicketsBot-cloud/logarchiver/internal/util" "github.com/TicketsBot-cloud/logarchiver/pkg/config" + "github.com/TicketsBot-cloud/logarchiver/pkg/export/user" "github.com/TicketsBot-cloud/logarchiver/pkg/model" v1 "github.com/TicketsBot-cloud/logarchiver/pkg/model/v1" v2 "github.com/TicketsBot-cloud/logarchiver/pkg/model/v2" "github.com/TicketsBot-cloud/logarchiver/pkg/s3client" - "github.com/TicketsBot/common/encryption" - "github.com/TicketsBot/database" "github.com/jackc/pgx/v4/pgxpool" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" - "go.uber.org/zap" ) var ( @@ -34,13 +33,17 @@ var ( func main() { flag.Parse() - conf := config.Parse() + conf := config.Parse[config.CliConfig]() - // likely to be file exists - _ = os.Mkdir(fmt.Sprintf("export_user/%d", *userId), 0) + exportDir := fmt.Sprintf("exports/user/%d", *userId) - fmt.Println("Connecting to database...") + if err := os.MkdirAll(exportDir, 0700); err != nil { + panic(fmt.Sprintf("failed to create export directory: %v", err)) + } + + fmt.Printf("Exporting user data for %d\n", *userId) + fmt.Println("[1/5] Connecting to database...") var db *database.Database { pool, err := pgxpool.Connect(context.Background(), *dbUri) @@ -49,8 +52,7 @@ func main() { db = database.NewDatabase(pool) } - fmt.Println("Connecting to cache...") - + fmt.Println("[2/5] Connecting to cache...") var c cache.PgCache { pool, err := pgxpool.Connect(context.Background(), *cacheUri) @@ -62,26 +64,27 @@ func main() { }) } - fmt.Println("Connected to cache") - - // Get + write user data + fmt.Println("[3/5] Exporting database and cache data...") { - userData := getUserData(db, *userId) + userData := user.GetUserData(db, *userId) encoded, err := json.MarshalIndent(userData, "", " ") must(err) - writeFile(fmt.Sprintf("export_user/%d/database.json", *userId), encoded) + writeFile(fmt.Sprintf("%s/database.json", exportDir), encoded) + fmt.Println(" - Wrote database.json") } { - cacheData := getCacheData(&c, *userId) + cacheData := user.GetCacheData(&c, *userId) encoded, err := json.MarshalIndent(cacheData, "", " ") must(err) - writeFile(fmt.Sprintf("export_user/%d/cache.json", *userId), encoded) + writeFile(fmt.Sprintf("%s/cache.json", exportDir), encoded) + fmt.Println(" - Wrote cache.json") } + fmt.Println("[4/5] Exporting transcripts...") transcriptIds := make(map[uint64][]int) { @@ -122,11 +125,35 @@ func main() { } } - getTranscripts(conf, transcriptIds) + getTranscripts(conf, exportDir, transcriptIds) + + fmt.Println("[5/5] Creating zip archive...") + if err := util.ZipFiles(exportDir, fmt.Sprintf("exports/user/%d.zip", *userId)); err != nil { + panic(fmt.Sprintf("could not zip files: %v", err)) + } + + os.RemoveAll(exportDir) + fmt.Printf("Done! Export saved to exports/user/%d.zip\n", *userId) } -func getTranscripts(conf config.Config, tickets map[uint64][]int) { - // create minio client +func printProgress(current, total, skipped, failed int) { + const barWidth = 40 + filled := barWidth * current / total + bar := make([]byte, barWidth) + for i := range bar { + if i < filled { + bar[i] = '#' + } else { + bar[i] = '-' + } + } + fmt.Printf("\r[%s] %d/%d transcripts (skipped: %d, failed: %d)", bar, current, total, skipped, failed) + if current == total { + fmt.Println() + } +} + +func getTranscripts(conf config.CliConfig, exportDir string, tickets map[uint64][]int) { m, err := minio.New(conf.Endpoint, &minio.Options{ Creds: credentials.NewStaticV4(conf.AccessKey, conf.SecretKey, ""), Secure: conf.Secure, @@ -137,34 +164,32 @@ func getTranscripts(conf config.Config, tickets map[uint64][]int) { client := s3client.NewS3Client(m, conf.Bucket) - logger, err := zap.NewDevelopment() - if err != nil { - panic(err) - } + _ = os.MkdirAll(fmt.Sprintf("%s/transcripts", exportDir), 0700) - _ = os.Mkdir(fmt.Sprintf("export_user/%d/transcripts", *userId), 0) + var total int + for _, ticketIds := range tickets { + total += len(ticketIds) + } + var processed, skipped, failed int for guildId, ticketIds := range tickets { for _, ticketId := range ticketIds { + processed++ + printProgress(processed, total, skipped, failed) + data, err := client.GetTicket(context.Background(), guildId, ticketId) if err != nil { - if errors.Is(err, s3client.ErrTicketNotFound) { - logger.Info("ticket not found", zap.Uint64("guildId", guildId), zap.Int("ticketId", ticketId)) + if errors.Is(err, s3client.ErrTicketNotFound) || err.Error() == "The specified key does not exist." { + skipped++ continue } else { panic(err) } } - data, err = encryption.Decompress(data) - if err != nil { - logger.Error("failed to decompress", zap.Error(err), zap.Uint64("guildId", guildId), zap.Int("ticketId", ticketId)) - continue - } - data, err = encryption.Decrypt([]byte(*key), data) if err != nil { - logger.Error("failed to decrypt", zap.Error(err), zap.Uint64("guildId", guildId), zap.Int("ticketId", ticketId)) + failed++ continue } @@ -212,10 +237,8 @@ func getTranscripts(conf config.Config, tickets map[uint64][]int) { encoded, err := json.MarshalIndent(transcript, "", " ") must(err) - fileName := fmt.Sprintf("export_user/%d/transcripts/%d-%d.json", *userId, guildId, ticketId) + fileName := fmt.Sprintf("%s/transcripts/%d-%d.json", exportDir, guildId, ticketId) must(os.WriteFile(fileName, encoded, 0644)) - - fmt.Printf("exported %d/%d\n", guildId, ticketId) } } } @@ -233,280 +256,3 @@ func must(err error) { panic(err) } } - -// user data - -// The worst code you have ever seen -func getUserData(db *database.Database, userId uint64) map[string]interface{} { - data := make(map[string]interface{}) - - data["blacklisted_guilds"] = getBlacklistedGuilds(db, userId) - data["close_requests"] = getCloseRequests(db, userId) - data["response_times"] = getResponseTimes(db, userId) - data["participated_tickets"] = getParticipatedTickets(db, userId) - data["permissions"] = getPermissions(db, userId) - data["team_permissions"] = getTeamPermissions(db, userId) - data["claimed_tickets"] = getClaimedTickets(db, userId) - data["member_of_tickets"] = getTicketsMember(db, userId) - data["tickets"] = getTickets(db, userId) - data["premium_activated_for"] = getPremiumActivatedFor(db, userId) - - guilds, err := db.UserGuilds.Get(userId) - must(err) - data["guilds"] = guilds - - voteTime, err := db.Votes.Get(userId) - must(err) - if voteTime.IsZero() { - data["last_vote_time"] = nil - } else { - data["last_vote_time"] = voteTime - } - - whitelabel, err := db.Whitelabel.GetByUserId(userId) - must(err) - if whitelabel.UserId == 0 { - data["whitelabel"] = nil - } else { - data["whitelabel"] = whitelabel - } - - whitelabelExpiry, err := db.WhitelabelUsers.GetExpiry(userId) - must(err) - if whitelabelExpiry.IsZero() { - data["whitelabel_expiry"] = nil - } else { - data["whitelabel_expiry"] = whitelabelExpiry - } - - return data -} - -func getBlacklistedGuilds(db *database.Database, userId uint64) (guilds []uint64) { - rows, err := db.Blacklist.Query(context.Background(), "SELECT guild_id FROM blacklist WHERE user_id = $1;", userId) - must(err) - - for rows.Next() { - var guildId uint64 - must(rows.Scan(&guildId)) - - guilds = append(guilds, guildId) - } - - return -} - -func getCloseRequests(db *database.Database, userId uint64) (requests []database.CloseRequest) { - query := ` -SELECT "guild_id", "ticket_id", "user_id", "close_at", "close_reason" -FROM close_request -WHERE "user_id" = $1;` - - rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) - - for rows.Next() { - var request database.CloseRequest - must(rows.Scan(&request.GuildId, &request.TicketId, &request.UserId, &request.CloseAt, &request.Reason)) - requests = append(requests, request) - } - - return -} - -func getResponseTimes(db *database.Database, userId uint64) (times []interface{}) { - query := ` -SELECT "guild_id", "ticket_id", "user_id", "response_time" -FROM first_response_time -WHERE "user_id" = $1;` - - rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) - - for rows.Next() { - var guildId, userId uint64 - var ticketId int - var responseTime time.Duration - - must(rows.Scan(&guildId, &ticketId, &userId, &responseTime)) - times = append(times, map[string]interface{}{ - "guild_id": guildId, - "ticket_id": ticketId, - "user_id": userId, - "response_time": responseTime, - }) - } - - return -} - -func getParticipatedTickets(db *database.Database, userId uint64) (tickets []string) { - query := ` -SELECT "guild_id", "ticket_id" -FROM participant -WHERE "user_id" = $1;` - - rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) - - for rows.Next() { - var guildId uint64 - var ticketId int - - must(rows.Scan(&guildId, &ticketId)) - tickets = append(tickets, fmt.Sprintf("%d/%d", guildId, ticketId)) - } - - return -} - -func getPermissions(db *database.Database, userId uint64) map[uint64]string { - query := ` -SELECT "guild_id", "support", "admin" -FROM permissions -WHERE "user_id" = $1;` - - rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) - - data := make(map[uint64]string) - for rows.Next() { - var guildId uint64 - var isSupport, isAdmin bool - - must(rows.Scan(&guildId, &isSupport, &isAdmin)) - - if isAdmin { - data[guildId] = "admin" - } else if isSupport { - data[guildId] = "support" - } else { - data[guildId] = "none" - } - } - - return data -} - -func getTeamPermissions(db *database.Database, userId uint64) (teams []int) { - query := ` -SELECT "team_id" -FROM support_team_members -WHERE "user_id" = $1;` - - rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) - - for rows.Next() { - var teamId int - must(rows.Scan(&teamId)) - teams = append(teams, teamId) - } - - return -} - -func getClaimedTickets(db *database.Database, userId uint64) (tickets []string) { - query := ` -SELECT "guild_id", "ticket_id" -FROM ticket_claims -WHERE "user_id" = $1;` - - rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) - - for rows.Next() { - var guildId uint64 - var ticketId int - - must(rows.Scan(&guildId, &ticketId)) - tickets = append(tickets, fmt.Sprintf("%d/%d", guildId, ticketId)) - } - - return -} - -func getTicketsMember(db *database.Database, userId uint64) (tickets []string) { - query := ` -SELECT "guild_id", "ticket_id" -FROM ticket_members -WHERE "user_id" = $1;` - - rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) - - for rows.Next() { - var guildId uint64 - var ticketId int - - must(rows.Scan(&guildId, &ticketId)) - tickets = append(tickets, fmt.Sprintf("%d/%d", guildId, ticketId)) - } - - return -} - -func getTickets(db *database.Database, userId uint64) (tickets []database.Ticket) { - query := ` -SELECT id, guild_id, channel_id, user_id, open, open_time, welcome_message_id, panel_id, has_transcript -FROM tickets -WHERE "user_id" = $1;` - - rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) - - for rows.Next() { - var ticket database.Ticket - must(rows.Scan(&ticket.Id, &ticket.GuildId, &ticket.ChannelId, &ticket.UserId, &ticket.Open, &ticket.OpenTime, &ticket.WelcomeMessageId, &ticket.PanelId, &ticket.HasTranscript)) - tickets = append(tickets, ticket) - } - - return -} - -func getPremiumActivatedFor(db *database.Database, userId uint64) (guilds []uint64) { - query := ` -SELECT guild_id -FROM used_keys -WHERE "activated_by" = $1;` - - rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) - - for rows.Next() { - var guildId uint64 - must(rows.Scan(&guildId)) - guilds = append(guilds, guildId) - } - - return -} - -// cache data -func getCacheData(cache *cache.PgCache, userId uint64) map[string]interface{} { - data := make(map[string]interface{}) - - user, ok := cache.GetUser(userId) - if ok { - data["user"] = user - } else { - data["user"] = nil - } - - rows, err := cache.Query(context.Background(), `SELECT guild_id, data FROM members WHERE "user_id" = $1;`, userId) - must(err) - - memberData := make(map[string]interface{}) - for rows.Next() { - var guildId uint64 - var raw string - - must(rows.Scan(&guildId, &raw)) - - memberData[strconv.FormatUint(guildId, 10)] = json.RawMessage([]byte(raw)) - } - - data["member_data"] = memberData - - return data -} diff --git a/cmd/exportuser/userdata.go b/cmd/exportuser/userdata.go deleted file mode 100644 index c9ecbf5..0000000 --- a/cmd/exportuser/userdata.go +++ /dev/null @@ -1,2 +0,0 @@ -package main - diff --git a/go.mod b/go.mod index e5656e6..66ec611 100644 --- a/go.mod +++ b/go.mod @@ -1,26 +1,26 @@ module github.com/TicketsBot-cloud/logarchiver -go 1.22.0 - -toolchain go1.24.2 +go 1.25.0 // replace github.com/TicketsBot-cloud/gdl => ../gdl require ( - github.com/TicketsBot-cloud/gdl v0.0.0-20250509054940-2045fbe19c06 - github.com/TicketsBot/common v0.0.0-20230723121853-8d873b27086e - github.com/TicketsBot/database v0.0.0-20220217133004-d190910ad66f + github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e + github.com/TicketsBot-cloud/database v0.0.0-20260308193919-30a698fefa8b + github.com/TicketsBot-cloud/gdl v0.0.0-20260306134952-cccb0116fef6 + github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1 github.com/caarlos0/env v3.5.0+incompatible github.com/getsentry/sentry-go v0.21.0 github.com/gin-contrib/zap v0.1.0 github.com/gin-gonic/gin v1.8.1 github.com/google/uuid v1.6.0 github.com/jackc/pgx/v4 v4.18.3 - github.com/jackc/pgx/v5 v5.6.0 + github.com/jackc/pgx/v5 v5.9.1 github.com/joho/godotenv v1.5.1 - github.com/minio/minio-go/v7 v7.0.73 - go.uber.org/zap v1.23.0 - golang.org/x/sync v0.8.0 + github.com/klauspost/compress v1.18.5 + github.com/minio/minio-go/v7 v7.0.99 + go.uber.org/zap v1.27.1 + golang.org/x/sync v0.20.0 ) require ( @@ -31,38 +31,40 @@ require ( github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.11.1 // indirect - github.com/goccy/go-json v0.10.3 // indirect - github.com/gofrs/uuid v4.0.0+incompatible // indirect + github.com/goccy/go-json v0.10.6 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.14.3 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgproto3/v2 v2.3.3 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgtype v1.14.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgtype v1.14.4 // indirect github.com/jackc/pgx v3.6.2+incompatible // indirect github.com/jackc/puddle v1.3.0 // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect - github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/klauspost/crc32 v1.3.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-isatty v0.0.17 // indirect + github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/philhofer/fwd v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/rs/xid v1.5.0 // indirect + github.com/rs/xid v1.6.0 // indirect + github.com/tinylib/msgp v1.6.3 // indirect github.com/ugorji/go/codec v1.2.7 // indirect go.opentelemetry.io/otel v1.10.0 // indirect go.opentelemetry.io/otel/trace v1.10.0 // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect google.golang.org/protobuf v1.29.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index b79acb0..16820d1 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,13 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/TicketsBot-cloud/gdl v0.0.0-20250509054940-2045fbe19c06 h1:PzziB2S58d9agJtpaPVrYMTuBiJICr2QIGQoqL6l3z0= -github.com/TicketsBot-cloud/gdl v0.0.0-20250509054940-2045fbe19c06/go.mod h1:CdwBR2egPtxUXjD2CgC9ZwfuB8dz9HPePM8nuG6dt7Y= -github.com/TicketsBot/common v0.0.0-20230723121853-8d873b27086e h1:K7c0kRXxaW14dl9G9rQ78eGP+Lu+F7qPxTud/j68SQQ= -github.com/TicketsBot/common v0.0.0-20230723121853-8d873b27086e/go.mod h1:rZuaTbjajlxscl628aBgUGiUZcOVLvF6pfsPL+2JOac= -github.com/TicketsBot/database v0.0.0-20220217133004-d190910ad66f h1:kBIHaAxyIGqxgwz/ZRggNGjyIdZ3ctWZqjJ9Svrn4L4= -github.com/TicketsBot/database v0.0.0-20220217133004-d190910ad66f/go.mod h1:72oWvH/Gq1iKeXCZhVRZn1JFbNVC5iAgERZWTrEarEo= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e h1:nFKV7yEm8MWbCP7dtsJ88+agcxDUD0YKIotVHMVvytw= +github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e/go.mod h1:tGrTHFz09OM3eDWF+62hIi9ELpT4igCFi868FKSvKBg= +github.com/TicketsBot-cloud/database v0.0.0-20260308193919-30a698fefa8b h1:bHkfJWo8T/9TiHuYHxaOz8GAILIiKPugC1k3CzdOq/A= +github.com/TicketsBot-cloud/database v0.0.0-20260308193919-30a698fefa8b/go.mod h1:HQXAgmNSm7/FmBYwcsa6qpZqMrDhbLoEl+AyqFQ+RwY= +github.com/TicketsBot-cloud/gdl v0.0.0-20260306134952-cccb0116fef6 h1:ucG0xLPt7xixW7/LvL0hXDBDouDRS1Nf+77qP8iJ/X0= +github.com/TicketsBot-cloud/gdl v0.0.0-20260306134952-cccb0116fef6/go.mod h1:CdwBR2egPtxUXjD2CgC9ZwfuB8dz9HPePM8nuG6dt7Y= +github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1 h1:FqC1KGOsmB+ikvbmDkyNQU6bGUWyfYq8Ip9r4KxTveY= +github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1/go.mod h1:N7zwetwx8B3RK/ZajWwMroJSyv2ZJ+bIOZWv/z8DhaM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= @@ -51,10 +52,8 @@ github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJ github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= -github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -70,11 +69,9 @@ github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9 github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= @@ -93,51 +90,51 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= -github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= -github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.14.4 h1:fKuNiCumbKTAIxQwXfB/nsrnkEI6bPJrrSiMKgbJ2j8= +github.com/jackc/pgtype v1.14.4/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o= github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= -github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= -github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= -github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/pgx/v5 v5.9.1 h1:uwrxJXBnx76nyISkhr33kQLlUqjv7et7b9FjCen/tdc= +github.com/jackc/pgx/v5 v5.9.1/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM= +github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -157,20 +154,19 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 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/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= +github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.73 h1:qr2vi96Qm7kZ4v7LLebjte+MQh621fFWnv93p12htEo= -github.com/minio/minio-go/v7 v7.0.73/go.mod h1:qydcVzV8Hqtj1VtEocfxbmVFa2siu6HGa+LDEPogjD8= +github.com/minio/minio-go/v7 v7.0.99 h1:2vH/byrwUkIpFQFOilvTfaUpvAX3fEFhEzO+DR3DlCE= +github.com/minio/minio-go/v7 v7.0.99/go.mod h1:EtGNKtlX20iL2yaYnxEigaIvj0G0GwSDnifnG8ClIdw= 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= @@ -179,6 +175,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -192,8 +190,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -206,6 +204,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -214,12 +213,16 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tinylib/msgp v1.6.3 h1:bCSxiTz386UTgyT1i0MSCvdbWjVW+8sG3PjkGsZQt4s= +github.com/tinylib/msgp v1.6.3/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.opentelemetry.io/otel v1.10.0 h1:Y7DTJMR6zs1xkS/upamJYk0SxxN4C9AqRd77jmZnyY4= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= @@ -229,38 +232,46 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -268,12 +279,18 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -281,7 +298,6 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -292,20 +308,31 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -316,6 +343,8 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/util/util.go b/internal/util/util.go new file mode 100644 index 0000000..d3520d7 --- /dev/null +++ b/internal/util/util.go @@ -0,0 +1,50 @@ +package util + +import ( + "archive/zip" + "io" + "os" + "path/filepath" +) + +func ZipFiles(source, target string) error { + zipFile, err := os.Create(target) + if err != nil { + return err + } + + defer zipFile.Close() + + zipWriter := zip.NewWriter(zipFile) + defer zipWriter.Close() + + err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + relPath, err := filepath.Rel(source, path) + if err != nil { + return err + } + + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + w, err := zipWriter.Create(relPath) + if err != nil { + return err + } + _, err = io.Copy(w, file) + return err + }) + + return err +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 7c59216..f81aad0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,9 +1,10 @@ package config import ( + "reflect" + "github.com/caarlos0/env" "github.com/google/uuid" - "reflect" ) type Config struct { diff --git a/cmd/exportuser/cachedata.go b/pkg/export/user/cachedata.go similarity index 63% rename from cmd/exportuser/cachedata.go rename to pkg/export/user/cachedata.go index 7f3c041..065010f 100644 --- a/cmd/exportuser/cachedata.go +++ b/pkg/export/user/cachedata.go @@ -1,4 +1,4 @@ -package main +package user import ( "context" @@ -8,17 +8,17 @@ import ( "github.com/TicketsBot-cloud/gdl/cache" ) -func getCacheData(cache *cache.PgCache, userId uint64) map[string]interface{} { +func GetCacheData(c *cache.PgCache, userId uint64) map[string]interface{} { data := make(map[string]interface{}) - user, ok := cache.GetUser(userId) - if ok { + user, err := c.GetUser(context.Background(), userId) + if err == nil { data["user"] = user } else { data["user"] = nil } - rows, err := cache.Query(context.Background(), `SELECT guild_id, data FROM members WHERE "user_id" = $1;`, userId) + rows, err := c.Query(context.Background(), `SELECT guild_id, data FROM members WHERE "user_id" = $1;`, userId) must(err) memberData := make(map[string]interface{}) diff --git a/pkg/export/user/must.go b/pkg/export/user/must.go new file mode 100644 index 0000000..f71b56e --- /dev/null +++ b/pkg/export/user/must.go @@ -0,0 +1,7 @@ +package user + +func must(err error) { + if err != nil { + panic(err) + } +} diff --git a/pkg/export/user/userdata.go b/pkg/export/user/userdata.go new file mode 100644 index 0000000..2836728 --- /dev/null +++ b/pkg/export/user/userdata.go @@ -0,0 +1,248 @@ +package user + +import ( + "context" + "fmt" + "time" + + "github.com/TicketsBot-cloud/database" +) + +// The worst code you have ever seen +func GetUserData(db *database.Database, userId uint64) map[string]interface{} { + ctx := context.Background() + data := make(map[string]interface{}) + + data["blacklisted_guilds"] = getBlacklistedGuilds(db, userId) + data["close_requests"] = getCloseRequests(db, userId) + data["response_times"] = getResponseTimes(db, userId) + data["participated_tickets"] = getParticipatedTickets(db, userId) + data["permissions"] = getPermissions(db, userId) + data["team_permissions"] = getTeamPermissions(db, userId) + data["claimed_tickets"] = getClaimedTickets(db, userId) + data["member_of_tickets"] = getTicketsMember(db, userId) + data["tickets"] = getTickets(db, userId) + data["premium_activated_for"] = getPremiumActivatedFor(db, userId) + + guilds, err := db.UserGuilds.Get(ctx, userId) + must(err) + data["guilds"] = guilds + + whitelabel, err := db.Whitelabel.GetByUserId(ctx, userId) + must(err) + if whitelabel.UserId == 0 { + data["whitelabel"] = nil + } else { + data["whitelabel"] = whitelabel + } + + whitelabelExpiry, err := db.WhitelabelUsers.GetExpiry(ctx, userId) + must(err) + if whitelabelExpiry.IsZero() { + data["whitelabel_expiry"] = nil + } else { + data["whitelabel_expiry"] = whitelabelExpiry + } + + return data +} + +func getBlacklistedGuilds(db *database.Database, userId uint64) (guilds []uint64) { + rows, err := db.Blacklist.Query(context.Background(), "SELECT guild_id FROM blacklist WHERE user_id = $1;", userId) + must(err) + + for rows.Next() { + var guildId uint64 + must(rows.Scan(&guildId)) + + guilds = append(guilds, guildId) + } + + return +} + +func getCloseRequests(db *database.Database, userId uint64) (requests []database.CloseRequest) { + query := ` +SELECT "guild_id", "ticket_id", "user_id", "close_at", "close_reason" +FROM close_request +WHERE "user_id" = $1;` + + rows, err := db.Blacklist.Query(context.Background(), query, userId) + must(err) + + for rows.Next() { + var request database.CloseRequest + must(rows.Scan(&request.GuildId, &request.TicketId, &request.UserId, &request.CloseAt, &request.Reason)) + requests = append(requests, request) + } + + return +} + +func getResponseTimes(db *database.Database, userId uint64) (times []interface{}) { + query := ` +SELECT "guild_id", "ticket_id", "user_id", "response_time" +FROM first_response_time +WHERE "user_id" = $1;` + + rows, err := db.Blacklist.Query(context.Background(), query, userId) + must(err) + + for rows.Next() { + var guildId, userId uint64 + var ticketId int + var responseTime time.Duration + + must(rows.Scan(&guildId, &ticketId, &userId, &responseTime)) + times = append(times, map[string]interface{}{ + "guild_id": guildId, + "ticket_id": ticketId, + "user_id": userId, + "response_time": responseTime, + }) + } + + return +} + +func getParticipatedTickets(db *database.Database, userId uint64) (tickets []string) { + query := ` +SELECT "guild_id", "ticket_id" +FROM participant +WHERE "user_id" = $1;` + + rows, err := db.Blacklist.Query(context.Background(), query, userId) + must(err) + + for rows.Next() { + var guildId uint64 + var ticketId int + + must(rows.Scan(&guildId, &ticketId)) + tickets = append(tickets, fmt.Sprintf("%d/%d", guildId, ticketId)) + } + + return +} + +func getPermissions(db *database.Database, userId uint64) map[uint64]string { + query := ` +SELECT "guild_id", "support", "admin" +FROM permissions +WHERE "user_id" = $1;` + + rows, err := db.Blacklist.Query(context.Background(), query, userId) + must(err) + + data := make(map[uint64]string) + for rows.Next() { + var guildId uint64 + var isSupport, isAdmin bool + + must(rows.Scan(&guildId, &isSupport, &isAdmin)) + + if isAdmin { + data[guildId] = "admin" + } else if isSupport { + data[guildId] = "support" + } else { + data[guildId] = "none" + } + } + + return data +} + +func getTeamPermissions(db *database.Database, userId uint64) (teams []int) { + query := ` +SELECT "team_id" +FROM support_team_members +WHERE "user_id" = $1;` + + rows, err := db.Blacklist.Query(context.Background(), query, userId) + must(err) + + for rows.Next() { + var teamId int + must(rows.Scan(&teamId)) + teams = append(teams, teamId) + } + + return +} + +func getClaimedTickets(db *database.Database, userId uint64) (tickets []string) { + query := ` +SELECT "guild_id", "ticket_id" +FROM ticket_claims +WHERE "user_id" = $1;` + + rows, err := db.Blacklist.Query(context.Background(), query, userId) + must(err) + + for rows.Next() { + var guildId uint64 + var ticketId int + + must(rows.Scan(&guildId, &ticketId)) + tickets = append(tickets, fmt.Sprintf("%d/%d", guildId, ticketId)) + } + + return +} + +func getTicketsMember(db *database.Database, userId uint64) (tickets []string) { + query := ` +SELECT "guild_id", "ticket_id" +FROM ticket_members +WHERE "user_id" = $1;` + + rows, err := db.Blacklist.Query(context.Background(), query, userId) + must(err) + + for rows.Next() { + var guildId uint64 + var ticketId int + + must(rows.Scan(&guildId, &ticketId)) + tickets = append(tickets, fmt.Sprintf("%d/%d", guildId, ticketId)) + } + + return +} + +func getTickets(db *database.Database, userId uint64) (tickets []database.Ticket) { + query := ` +SELECT id, guild_id, channel_id, user_id, open, open_time, welcome_message_id, panel_id, has_transcript +FROM tickets +WHERE "user_id" = $1;` + + rows, err := db.Blacklist.Query(context.Background(), query, userId) + must(err) + + for rows.Next() { + var ticket database.Ticket + must(rows.Scan(&ticket.Id, &ticket.GuildId, &ticket.ChannelId, &ticket.UserId, &ticket.Open, &ticket.OpenTime, &ticket.WelcomeMessageId, &ticket.PanelId, &ticket.HasTranscript)) + tickets = append(tickets, ticket) + } + + return +} + +func getPremiumActivatedFor(db *database.Database, userId uint64) (guilds []uint64) { + query := ` +SELECT guild_id +FROM used_keys +WHERE "activated_by" = $1;` + + rows, err := db.Blacklist.Query(context.Background(), query, userId) + must(err) + + for rows.Next() { + var guildId uint64 + must(rows.Scan(&guildId)) + guilds = append(guilds, guildId) + } + + return +} diff --git a/pkg/s3client/errors.go b/pkg/s3client/errors.go index 66abf2b..48de3f4 100644 --- a/pkg/s3client/errors.go +++ b/pkg/s3client/errors.go @@ -3,3 +3,4 @@ package s3client import "errors" var ErrTicketNotFound = errors.New("object not found") +var ErrTicketNotFound2 = errors.New("The specified key does not exist") From 45877e640f9f3d8eb22b13ca8b0ebc39082f6b5d Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 26 Mar 2026 17:59:11 +0000 Subject: [PATCH 2/3] bump Signed-off-by: Ben --- cmd/exportguild/main.go | 125 +++++++++++++------- cmd/exportuser/main.go | 7 +- pkg/export/user/cachedata.go | 13 ++- pkg/export/user/must.go | 7 -- pkg/export/user/userdata.go | 215 ++++++++++++++++++++++++++--------- pkg/s3client/client.go | 8 +- 6 files changed, 261 insertions(+), 114 deletions(-) delete mode 100644 pkg/export/user/must.go diff --git a/cmd/exportguild/main.go b/cmd/exportguild/main.go index 9ec9709..1450ede 100644 --- a/cmd/exportguild/main.go +++ b/cmd/exportguild/main.go @@ -7,9 +7,8 @@ import ( "flag" "fmt" "os" - "strconv" - "strings" "sync/atomic" + "time" "github.com/TicketsBot-cloud/common/encryption" "github.com/TicketsBot-cloud/gdl/objects/channel/message" @@ -19,6 +18,7 @@ import ( v1 "github.com/TicketsBot-cloud/logarchiver/pkg/model/v1" v2 "github.com/TicketsBot-cloud/logarchiver/pkg/model/v2" "github.com/TicketsBot-cloud/logarchiver/pkg/s3client" + "github.com/jackc/pgx/v4/pgxpool" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "golang.org/x/sync/errgroup" @@ -29,16 +29,23 @@ const workers = 15 var ( guildId = flag.Uint64("guildid", 0, "guild id to export") key = flag.String("key", "", "aes key") + dbUri = flag.String("dburi", "", "database uri") ticketId = flag.Int("ticketid", 0, "set to export a single ticket") convert = flag.Bool("convert", false, "convert to v2 if necessary") userWhitelist = flag.Uint64("userwhitelist", 0, "only export tickets from this user") after = flag.Int("after", 0, "export ticket IDs above this value (inclusive)") + dateFromStr = flag.String("date-from", "", "only export tickets created from this date (YYYY-MM-DD, inclusive)") + dateToStr = flag.String("date-to", "", "only export tickets created up to this date (YYYY-MM-DD, inclusive)") ) func main() { flag.Parse() conf := config.Parse[config.CliConfig]() + if *dbUri == "" { + panic("-dburi is required") + } + fmt.Printf("Exporting guild data for %d\n", *guildId) exportDir := fmt.Sprintf("exports/guild/%d", *guildId) @@ -63,50 +70,32 @@ func main() { exportTicket(*ticketId, client, exportDir) printProgress(1, 1, 0, 0) } else { - keys, err := client.GetAllKeysForGuild(context.Background(), *guildId) - if err != nil { - panic(err) - } + ticketIds := getTicketIds(*guildId, *dateFromStr, *dateToStr) + fmt.Printf(" Found %d tickets\n", len(ticketIds)) - total := int64(len(keys)) + total := int64(len(ticketIds)) var processed, skipped, failed atomic.Int64 - keyCh := make(chan string) + idCh := make(chan int) go func() { - for _, key := range keys { - keyCh <- key + for _, id := range ticketIds { + idCh <- id } - close(keyCh) + close(idCh) }() group, _ := errgroup.WithContext(context.Background()) for i := 0; i < workers; i++ { group.Go(func() error { - for key := range keyCh { - id := key[strings.LastIndex(key, "/")+1:] - if id == "" { + for id := range idCh { + if after != nil && *after > 0 && id < *after { skipped.Add(1) processed.Add(1) printProgress(int(processed.Load()), int(total), int(skipped.Load()), int(failed.Load())) continue } - parsed, err := strconv.Atoi(id) - if err != nil { - skipped.Add(1) - processed.Add(1) - printProgress(int(processed.Load()), int(total), int(skipped.Load()), int(failed.Load())) - continue - } - - if after != nil && *after > 0 && parsed < *after { - skipped.Add(1) - processed.Add(1) - printProgress(int(processed.Load()), int(total), int(skipped.Load()), int(failed.Load())) - continue - } - - if ok := exportTicket(parsed, client, exportDir); !ok { + if result := exportTicket(id, client, exportDir); result < 0 { failed.Add(1) } @@ -133,6 +122,58 @@ func main() { fmt.Printf("Done! Export saved to %s\n", zipPath) } +func getTicketIds(guildId uint64, fromStr, toStr string) []int { + pool, err := pgxpool.Connect(context.Background(), *dbUri) + if err != nil { + panic(fmt.Sprintf("failed to connect to database: %v", err)) + } + defer pool.Close() + + query := `SELECT id FROM tickets WHERE guild_id = $1 AND has_transcript = 't' AND open = 'f'` + args := []interface{}{guildId} + argIdx := 2 + + if fromStr != "" { + dateFrom, err := time.Parse("2006-01-02", fromStr) + if err != nil { + panic(fmt.Sprintf("invalid -date-from: %v", err)) + } + query += fmt.Sprintf(` AND open_time >= $%d`, argIdx) + args = append(args, dateFrom) + argIdx++ + } + + if toStr != "" { + dateTo, err := time.Parse("2006-01-02", toStr) + if err != nil { + panic(fmt.Sprintf("invalid -date-to: %v", err)) + } + // End of day so the date is inclusive + dateTo = dateTo.Add(24*time.Hour - time.Nanosecond) + query += fmt.Sprintf(` AND open_time <= $%d`, argIdx) + args = append(args, dateTo) + } + + query += ` ORDER BY id;` + + rows, err := pool.Query(context.Background(), query, args...) + if err != nil { + panic(fmt.Sprintf("failed to query tickets: %v", err)) + } + defer rows.Close() + + var ids []int + for rows.Next() { + var id int + if err := rows.Scan(&id); err != nil { + panic(fmt.Sprintf("failed to scan ticket id: %v", err)) + } + ids = append(ids, id) + } + + return ids +} + func printProgress(current, total, skipped, failed int) { const barWidth = 40 filled := barWidth * current / total @@ -150,16 +191,16 @@ func printProgress(current, total, skipped, failed int) { } } -// exportTicket exports a single ticket and returns true on success. -func exportTicket(id int, client *s3client.S3Client, exportDir string) bool { +// exportTicket exports a single ticket. Returns 1 for success, -1 for failure. +func exportTicket(id int, client *s3client.S3Client, exportDir string) int { data, err := client.GetTicket(context.Background(), *guildId, id) if err != nil { - return false + return -1 } data, err = encryption.Decrypt([]byte(*key), data) if err != nil { - return false + return -1 } if *convert || (userWhitelist != nil && *userWhitelist > 0) { @@ -170,16 +211,16 @@ func exportTicket(id int, client *s3client.S3Client, exportDir string) bool { case model.V1: var messages []message.Message if err := json.Unmarshal(data, &messages); err != nil { - return false + return -1 } transcript = v1.ConvertToV2(messages) case model.V2: if err := json.Unmarshal(data, &transcript); err != nil { - return false + return -1 } default: - return false + return -1 } if userWhitelist != nil && *userWhitelist > 0 { @@ -207,25 +248,25 @@ func exportTicket(id int, client *s3client.S3Client, exportDir string) bool { data, err = json.Marshal(transcript) if err != nil { - return false + return -1 } } var encoded bytes.Buffer if err := json.Indent(&encoded, data, "", " "); err != nil { - return false + return -1 } f, err := os.Create(fmt.Sprintf("%s/%d.json", exportDir, id)) if err != nil { - return false + return -1 } if _, err := encoded.WriteTo(f); err != nil { - return false + return -1 } - return true + return 1 } func must(err error) { diff --git a/cmd/exportuser/main.go b/cmd/exportuser/main.go index 93131c1..5d5cb4d 100644 --- a/cmd/exportuser/main.go +++ b/cmd/exportuser/main.go @@ -66,7 +66,9 @@ func main() { fmt.Println("[3/5] Exporting database and cache data...") { - userData := user.GetUserData(db, *userId) + userData, err := user.GetUserData(db, *userId) + must(err) + encoded, err := json.MarshalIndent(userData, "", " ") must(err) @@ -75,7 +77,8 @@ func main() { } { - cacheData := user.GetCacheData(&c, *userId) + cacheData, err := user.GetCacheData(&c, *userId) + must(err) encoded, err := json.MarshalIndent(cacheData, "", " ") must(err) diff --git a/pkg/export/user/cachedata.go b/pkg/export/user/cachedata.go index 065010f..5c288ab 100644 --- a/pkg/export/user/cachedata.go +++ b/pkg/export/user/cachedata.go @@ -3,12 +3,13 @@ package user import ( "context" "encoding/json" + "fmt" "strconv" "github.com/TicketsBot-cloud/gdl/cache" ) -func GetCacheData(c *cache.PgCache, userId uint64) map[string]interface{} { +func GetCacheData(c *cache.PgCache, userId uint64) (map[string]interface{}, error) { data := make(map[string]interface{}) user, err := c.GetUser(context.Background(), userId) @@ -19,19 +20,23 @@ func GetCacheData(c *cache.PgCache, userId uint64) map[string]interface{} { } rows, err := c.Query(context.Background(), `SELECT guild_id, data FROM members WHERE "user_id" = $1;`, userId) - must(err) + if err != nil { + return nil, fmt.Errorf("failed to query member data: %w", err) + } memberData := make(map[string]interface{}) for rows.Next() { var guildId uint64 var raw string - must(rows.Scan(&guildId, &raw)) + if err := rows.Scan(&guildId, &raw); err != nil { + return nil, fmt.Errorf("failed to scan member row: %w", err) + } memberData[strconv.FormatUint(guildId, 10)] = json.RawMessage([]byte(raw)) } data["member_data"] = memberData - return data + return data, nil } diff --git a/pkg/export/user/must.go b/pkg/export/user/must.go deleted file mode 100644 index f71b56e..0000000 --- a/pkg/export/user/must.go +++ /dev/null @@ -1,7 +0,0 @@ -package user - -func must(err error) { - if err != nil { - panic(err) - } -} diff --git a/pkg/export/user/userdata.go b/pkg/export/user/userdata.go index 2836728..9e12cba 100644 --- a/pkg/export/user/userdata.go +++ b/pkg/export/user/userdata.go @@ -8,28 +8,80 @@ import ( "github.com/TicketsBot-cloud/database" ) -// The worst code you have ever seen -func GetUserData(db *database.Database, userId uint64) map[string]interface{} { +func GetUserData(db *database.Database, userId uint64) (map[string]interface{}, error) { ctx := context.Background() data := make(map[string]interface{}) - data["blacklisted_guilds"] = getBlacklistedGuilds(db, userId) - data["close_requests"] = getCloseRequests(db, userId) - data["response_times"] = getResponseTimes(db, userId) - data["participated_tickets"] = getParticipatedTickets(db, userId) - data["permissions"] = getPermissions(db, userId) - data["team_permissions"] = getTeamPermissions(db, userId) - data["claimed_tickets"] = getClaimedTickets(db, userId) - data["member_of_tickets"] = getTicketsMember(db, userId) - data["tickets"] = getTickets(db, userId) - data["premium_activated_for"] = getPremiumActivatedFor(db, userId) + blacklistedGuilds, err := getBlacklistedGuilds(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get blacklisted guilds: %w", err) + } + data["blacklisted_guilds"] = blacklistedGuilds + + closeRequests, err := getCloseRequests(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get close requests: %w", err) + } + data["close_requests"] = closeRequests + + responseTimes, err := getResponseTimes(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get response times: %w", err) + } + data["response_times"] = responseTimes + + participatedTickets, err := getParticipatedTickets(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get participated tickets: %w", err) + } + data["participated_tickets"] = participatedTickets + + permissions, err := getPermissions(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get permissions: %w", err) + } + data["permissions"] = permissions + + teamPermissions, err := getTeamPermissions(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get team permissions: %w", err) + } + data["team_permissions"] = teamPermissions + + claimedTickets, err := getClaimedTickets(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get claimed tickets: %w", err) + } + data["claimed_tickets"] = claimedTickets + + ticketsMember, err := getTicketsMember(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get tickets member: %w", err) + } + data["member_of_tickets"] = ticketsMember + + tickets, err := getTickets(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get tickets: %w", err) + } + data["tickets"] = tickets + + premiumActivatedFor, err := getPremiumActivatedFor(db, userId) + if err != nil { + return nil, fmt.Errorf("failed to get premium activated for: %w", err) + } + data["premium_activated_for"] = premiumActivatedFor guilds, err := db.UserGuilds.Get(ctx, userId) - must(err) + if err != nil { + return nil, fmt.Errorf("failed to get user guilds: %w", err) + } data["guilds"] = guilds whitelabel, err := db.Whitelabel.GetByUserId(ctx, userId) - must(err) + if err != nil { + return nil, fmt.Errorf("failed to get whitelabel: %w", err) + } if whitelabel.UserId == 0 { data["whitelabel"] = nil } else { @@ -37,63 +89,80 @@ func GetUserData(db *database.Database, userId uint64) map[string]interface{} { } whitelabelExpiry, err := db.WhitelabelUsers.GetExpiry(ctx, userId) - must(err) + if err != nil { + return nil, fmt.Errorf("failed to get whitelabel expiry: %w", err) + } if whitelabelExpiry.IsZero() { data["whitelabel_expiry"] = nil } else { data["whitelabel_expiry"] = whitelabelExpiry } - return data + return data, nil } -func getBlacklistedGuilds(db *database.Database, userId uint64) (guilds []uint64) { +func getBlacklistedGuilds(db *database.Database, userId uint64) ([]uint64, error) { rows, err := db.Blacklist.Query(context.Background(), "SELECT guild_id FROM blacklist WHERE user_id = $1;", userId) - must(err) + if err != nil { + return nil, err + } + var guilds []uint64 for rows.Next() { var guildId uint64 - must(rows.Scan(&guildId)) + if err := rows.Scan(&guildId); err != nil { + return nil, err + } guilds = append(guilds, guildId) } - return + return guilds, nil } -func getCloseRequests(db *database.Database, userId uint64) (requests []database.CloseRequest) { +func getCloseRequests(db *database.Database, userId uint64) ([]database.CloseRequest, error) { query := ` SELECT "guild_id", "ticket_id", "user_id", "close_at", "close_reason" FROM close_request WHERE "user_id" = $1;` rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) + if err != nil { + return nil, err + } + var requests []database.CloseRequest for rows.Next() { var request database.CloseRequest - must(rows.Scan(&request.GuildId, &request.TicketId, &request.UserId, &request.CloseAt, &request.Reason)) + if err := rows.Scan(&request.GuildId, &request.TicketId, &request.UserId, &request.CloseAt, &request.Reason); err != nil { + return nil, err + } requests = append(requests, request) } - return + return requests, nil } -func getResponseTimes(db *database.Database, userId uint64) (times []interface{}) { +func getResponseTimes(db *database.Database, userId uint64) ([]interface{}, error) { query := ` SELECT "guild_id", "ticket_id", "user_id", "response_time" FROM first_response_time WHERE "user_id" = $1;` rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) + if err != nil { + return nil, err + } + var times []interface{} for rows.Next() { var guildId, userId uint64 var ticketId int var responseTime time.Duration - must(rows.Scan(&guildId, &ticketId, &userId, &responseTime)) + if err := rows.Scan(&guildId, &ticketId, &userId, &responseTime); err != nil { + return nil, err + } times = append(times, map[string]interface{}{ "guild_id": guildId, "ticket_id": ticketId, @@ -102,44 +171,53 @@ WHERE "user_id" = $1;` }) } - return + return times, nil } -func getParticipatedTickets(db *database.Database, userId uint64) (tickets []string) { +func getParticipatedTickets(db *database.Database, userId uint64) ([]string, error) { query := ` SELECT "guild_id", "ticket_id" FROM participant WHERE "user_id" = $1;` rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) + if err != nil { + return nil, err + } + var tickets []string for rows.Next() { var guildId uint64 var ticketId int - must(rows.Scan(&guildId, &ticketId)) + if err := rows.Scan(&guildId, &ticketId); err != nil { + return nil, err + } tickets = append(tickets, fmt.Sprintf("%d/%d", guildId, ticketId)) } - return + return tickets, nil } -func getPermissions(db *database.Database, userId uint64) map[uint64]string { +func getPermissions(db *database.Database, userId uint64) (map[uint64]string, error) { query := ` SELECT "guild_id", "support", "admin" FROM permissions WHERE "user_id" = $1;` rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) + if err != nil { + return nil, err + } data := make(map[uint64]string) for rows.Next() { var guildId uint64 var isSupport, isAdmin bool - must(rows.Scan(&guildId, &isSupport, &isAdmin)) + if err := rows.Scan(&guildId, &isSupport, &isAdmin); err != nil { + return nil, err + } if isAdmin { data[guildId] = "admin" @@ -150,99 +228,124 @@ WHERE "user_id" = $1;` } } - return data + return data, nil } -func getTeamPermissions(db *database.Database, userId uint64) (teams []int) { +func getTeamPermissions(db *database.Database, userId uint64) ([]int, error) { query := ` SELECT "team_id" FROM support_team_members WHERE "user_id" = $1;` rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) + if err != nil { + return nil, err + } + var teams []int for rows.Next() { var teamId int - must(rows.Scan(&teamId)) + if err := rows.Scan(&teamId); err != nil { + return nil, err + } teams = append(teams, teamId) } - return + return teams, nil } -func getClaimedTickets(db *database.Database, userId uint64) (tickets []string) { +func getClaimedTickets(db *database.Database, userId uint64) ([]string, error) { query := ` SELECT "guild_id", "ticket_id" FROM ticket_claims WHERE "user_id" = $1;` rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) + if err != nil { + return nil, err + } + var tickets []string for rows.Next() { var guildId uint64 var ticketId int - must(rows.Scan(&guildId, &ticketId)) + if err := rows.Scan(&guildId, &ticketId); err != nil { + return nil, err + } tickets = append(tickets, fmt.Sprintf("%d/%d", guildId, ticketId)) } - return + return tickets, nil } -func getTicketsMember(db *database.Database, userId uint64) (tickets []string) { +func getTicketsMember(db *database.Database, userId uint64) ([]string, error) { query := ` SELECT "guild_id", "ticket_id" FROM ticket_members WHERE "user_id" = $1;` rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) + if err != nil { + return nil, err + } + var tickets []string for rows.Next() { var guildId uint64 var ticketId int - must(rows.Scan(&guildId, &ticketId)) + if err := rows.Scan(&guildId, &ticketId); err != nil { + return nil, err + } tickets = append(tickets, fmt.Sprintf("%d/%d", guildId, ticketId)) } - return + return tickets, nil } -func getTickets(db *database.Database, userId uint64) (tickets []database.Ticket) { +func getTickets(db *database.Database, userId uint64) ([]database.Ticket, error) { query := ` SELECT id, guild_id, channel_id, user_id, open, open_time, welcome_message_id, panel_id, has_transcript FROM tickets WHERE "user_id" = $1;` rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) + if err != nil { + return nil, err + } + var tickets []database.Ticket for rows.Next() { var ticket database.Ticket - must(rows.Scan(&ticket.Id, &ticket.GuildId, &ticket.ChannelId, &ticket.UserId, &ticket.Open, &ticket.OpenTime, &ticket.WelcomeMessageId, &ticket.PanelId, &ticket.HasTranscript)) + if err := rows.Scan(&ticket.Id, &ticket.GuildId, &ticket.ChannelId, &ticket.UserId, &ticket.Open, &ticket.OpenTime, &ticket.WelcomeMessageId, &ticket.PanelId, &ticket.HasTranscript); err != nil { + return nil, err + } tickets = append(tickets, ticket) } - return + return tickets, nil } -func getPremiumActivatedFor(db *database.Database, userId uint64) (guilds []uint64) { +func getPremiumActivatedFor(db *database.Database, userId uint64) ([]uint64, error) { query := ` SELECT guild_id FROM used_keys WHERE "activated_by" = $1;` rows, err := db.Blacklist.Query(context.Background(), query, userId) - must(err) + if err != nil { + return nil, err + } + var guilds []uint64 for rows.Next() { var guildId uint64 - must(rows.Scan(&guildId)) + if err := rows.Scan(&guildId); err != nil { + return nil, err + } guilds = append(guilds, guildId) } - return + return guilds, nil } diff --git a/pkg/s3client/client.go b/pkg/s3client/client.go index bbb4ce2..f42b3f9 100644 --- a/pkg/s3client/client.go +++ b/pkg/s3client/client.go @@ -84,13 +84,15 @@ func (c *S3Client) DeleteTicket(ctx context.Context, guildId uint64, ticketId in func (c *S3Client) GetAllKeysForGuild(ctx context.Context, guildId uint64) ([]string, error) { prefix := fmt.Sprintf("%d/", guildId) opts := minio.ListObjectsOptions{ - WithMetadata: true, - Prefix: prefix, - Recursive: true, + Prefix: prefix, + Recursive: true, } keys := make([]string, 0) for obj := range c.client.ListObjects(ctx, c.bucketName, opts) { + if obj.Err != nil { + return nil, obj.Err + } keys = append(keys, obj.Key) } From 82c191da77736cb4a36ed9177a3611739c71fe86 Mon Sep 17 00:00:00 2001 From: biast12 <53872542+biast12@users.noreply.github.com> Date: Sun, 21 Jun 2026 23:48:58 +0200 Subject: [PATCH 3/3] Bump gdl --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 66ec611..77973cd 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ go 1.25.0 require ( github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e github.com/TicketsBot-cloud/database v0.0.0-20260308193919-30a698fefa8b - github.com/TicketsBot-cloud/gdl v0.0.0-20260306134952-cccb0116fef6 + github.com/TicketsBot-cloud/gdl v0.0.0-20260612070331-a3947b410d3e github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1 github.com/caarlos0/env v3.5.0+incompatible github.com/getsentry/sentry-go v0.21.0 diff --git a/go.sum b/go.sum index 16820d1..712d052 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e h1:nFKV7yE github.com/TicketsBot-cloud/common v0.0.0-20260210203202-54154661338e/go.mod h1:tGrTHFz09OM3eDWF+62hIi9ELpT4igCFi868FKSvKBg= github.com/TicketsBot-cloud/database v0.0.0-20260308193919-30a698fefa8b h1:bHkfJWo8T/9TiHuYHxaOz8GAILIiKPugC1k3CzdOq/A= github.com/TicketsBot-cloud/database v0.0.0-20260308193919-30a698fefa8b/go.mod h1:HQXAgmNSm7/FmBYwcsa6qpZqMrDhbLoEl+AyqFQ+RwY= -github.com/TicketsBot-cloud/gdl v0.0.0-20260306134952-cccb0116fef6 h1:ucG0xLPt7xixW7/LvL0hXDBDouDRS1Nf+77qP8iJ/X0= -github.com/TicketsBot-cloud/gdl v0.0.0-20260306134952-cccb0116fef6/go.mod h1:CdwBR2egPtxUXjD2CgC9ZwfuB8dz9HPePM8nuG6dt7Y= +github.com/TicketsBot-cloud/gdl v0.0.0-20260612070331-a3947b410d3e h1:qCibZQmO2rrBFrvn6+oAchPEQ7k/0Izr3ruL5P9aVZU= +github.com/TicketsBot-cloud/gdl v0.0.0-20260612070331-a3947b410d3e/go.mod h1:CdwBR2egPtxUXjD2CgC9ZwfuB8dz9HPePM8nuG6dt7Y= github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1 h1:FqC1KGOsmB+ikvbmDkyNQU6bGUWyfYq8Ip9r4KxTveY= github.com/TicketsBot/common v0.0.0-20241117150316-ff54c97b45c1/go.mod h1:N7zwetwx8B3RK/ZajWwMroJSyv2ZJ+bIOZWv/z8DhaM= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=