- commit
- 62eeceb
- parent
- 5fec9cc
- author
- Antonio Mika
- date
- 2024-10-08 01:54:03 +0000 UTC
Merge pull request #145 from picosh/am/lib-refactor Refactor
+6,
-5
1@@ -18,6 +18,7 @@ import (
2 "github.com/picosh/pico/db"
3 "github.com/picosh/pico/db/postgres"
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 type Client struct {
9@@ -647,13 +648,13 @@ type AuthCfg struct {
10 }
11
12 func StartApiServer() {
13- debug := shared.GetEnv("AUTH_DEBUG", "0")
14+ debug := utils.GetEnv("AUTH_DEBUG", "0")
15 cfg := &AuthCfg{
16- DbURL: shared.GetEnv("DATABASE_URL", ""),
17+ DbURL: utils.GetEnv("DATABASE_URL", ""),
18 Debug: debug == "1",
19- Issuer: shared.GetEnv("AUTH_ISSUER", "pico.sh"),
20- Domain: shared.GetEnv("AUTH_DOMAIN", "http://0.0.0.0:3000"),
21- Port: shared.GetEnv("AUTH_WEB_PORT", "3000"),
22+ Issuer: utils.GetEnv("AUTH_ISSUER", "pico.sh"),
23+ Domain: utils.GetEnv("AUTH_DOMAIN", "http://0.0.0.0:3000"),
24+ Port: utils.GetEnv("AUTH_WEB_PORT", "3000"),
25 }
26
27 logger := shared.CreateLogger("auth")
+2,
-2
1@@ -6,7 +6,7 @@ import (
2
3 "github.com/picosh/pico/db"
4 "github.com/picosh/pico/db/postgres"
5- "github.com/picosh/pico/shared"
6+ "github.com/picosh/utils"
7 )
8
9 func main() {
10@@ -23,7 +23,7 @@ func main() {
11 // By: "post_id",
12 By: "user_id",
13 Interval: "day",
14- Origin: shared.StartOfMonth(),
15+ Origin: utils.StartOfMonth(),
16 // Where: "AND (post_id IS NOT NULL OR (post_id IS NULL AND project_id IS NULL))",
17 },
18 )
1@@ -10,6 +10,7 @@ import (
2 "github.com/picosh/pico/pgs"
3 "github.com/picosh/pico/shared"
4 "github.com/picosh/pico/shared/storage"
5+ "github.com/picosh/utils"
6 )
7
8 func bail(err error) {
9@@ -27,7 +28,7 @@ type RmProject struct {
10 // have a corresponding project inside our database.
11 func main() {
12 // to actually commit changes, set to true
13- writeEnv := shared.GetEnv("WRITE", "0")
14+ writeEnv := utils.GetEnv("WRITE", "0")
15 write := false
16 if writeEnv == "1" {
17 write = true
18@@ -102,7 +103,7 @@ func main() {
19 }
20 }
21
22- session := &shared.CmdSessionLogger{
23+ session := &utils.CmdSessionLogger{
24 Log: logger,
25 }
26
+2,
-1
1@@ -6,6 +6,7 @@ import (
2
3 "github.com/picosh/pico/db/postgres"
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 func main() {
9@@ -24,7 +25,7 @@ func main() {
10 empty := 0
11 diff := 0
12 for _, post := range posts {
13- nextShasum := shared.Shasum([]byte(post.Text))
14+ nextShasum := utils.Shasum([]byte(post.Text))
15 if post.Shasum == "" {
16 empty += 1
17 } else if post.Shasum != nextShasum {
+2,
-2
1@@ -14,7 +14,7 @@ import (
2
3 _ "github.com/lib/pq"
4 "github.com/picosh/pico/db"
5- "github.com/picosh/pico/shared"
6+ "github.com/picosh/utils"
7 )
8
9 var PAGER_SIZE = 15
10@@ -1552,7 +1552,7 @@ func (me *PsqlDB) FindFeedItemsByPostID(postID string) ([]*db.FeedItem, error) {
11 }
12
13 func (me *PsqlDB) InsertProject(userID, name, projectDir string) (string, error) {
14- if !shared.IsValidSubdomain(name) {
15+ if !utils.IsValidSubdomain(name) {
16 return "", fmt.Errorf("'%s' is not a valid project name, must match /^[a-z0-9-]+$/", name)
17 }
18
+1,
-1
1@@ -96,7 +96,7 @@ services:
2 env_file:
3 - .env.prod
4 environment:
5- APP_DOMAIN: ${PUBSUB_DOMAIN:-send.pico.sh}
6+ APP_DOMAIN: ${PUBSUB_DOMAIN:-pipe.pico.sh}
7 APP_EMAIL: ${PUBSUB_EMAIL:-hello@pico.sh}
8 volumes:
9 - ${PUBSUB_CADDYFILE}:/etc/caddy/Caddyfile
+11,
-10
1@@ -2,19 +2,20 @@ package feeds
2
3 import (
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 func NewConfigSite() *shared.ConfigSite {
9- debug := shared.GetEnv("FEEDS_DEBUG", "0")
10- domain := shared.GetEnv("FEEDS_DOMAIN", "feeds.pico.sh")
11- port := shared.GetEnv("FEEDS_WEB_PORT", "3000")
12- protocol := shared.GetEnv("FEEDS_PROTOCOL", "https")
13- storageDir := shared.GetEnv("IMGS_STORAGE_DIR", ".storage")
14- minioURL := shared.GetEnv("MINIO_URL", "")
15- minioUser := shared.GetEnv("MINIO_ROOT_USER", "")
16- minioPass := shared.GetEnv("MINIO_ROOT_PASSWORD", "")
17- dbURL := shared.GetEnv("DATABASE_URL", "")
18- sendgridKey := shared.GetEnv("SENDGRID_API_KEY", "")
19+ debug := utils.GetEnv("FEEDS_DEBUG", "0")
20+ domain := utils.GetEnv("FEEDS_DOMAIN", "feeds.pico.sh")
21+ port := utils.GetEnv("FEEDS_WEB_PORT", "3000")
22+ protocol := utils.GetEnv("FEEDS_PROTOCOL", "https")
23+ storageDir := utils.GetEnv("IMGS_STORAGE_DIR", ".storage")
24+ minioURL := utils.GetEnv("MINIO_URL", "")
25+ minioUser := utils.GetEnv("MINIO_ROOT_USER", "")
26+ minioPass := utils.GetEnv("MINIO_ROOT_PASSWORD", "")
27+ dbURL := utils.GetEnv("DATABASE_URL", "")
28+ sendgridKey := utils.GetEnv("SENDGRID_API_KEY", "")
29
30 return &shared.ConfigSite{
31 Debug: debug == "1",
+4,
-3
1@@ -11,6 +11,7 @@ import (
2 "github.com/picosh/pico/db"
3 "github.com/picosh/pico/filehandlers"
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 type FeedHooks struct {
9@@ -19,7 +20,7 @@ type FeedHooks struct {
10 }
11
12 func (p *FeedHooks) FileValidate(s ssh.Session, data *filehandlers.PostMetaData) (bool, error) {
13- if !shared.IsTextFile(string(data.Text)) {
14+ if !utils.IsTextFile(string(data.Text)) {
15 err := fmt.Errorf(
16 "WARNING: (%s) invalid file must be plain text (utf-8), skipping",
17 data.Filename,
18@@ -27,7 +28,7 @@ func (p *FeedHooks) FileValidate(s ssh.Session, data *filehandlers.PostMetaData)
19 return false, err
20 }
21
22- if !shared.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
23+ if !utils.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
24 extStr := strings.Join(p.Cfg.AllowedExt, ",")
25 err := fmt.Errorf(
26 "WARNING: (%s) invalid file, format must be (%s), skipping",
27@@ -44,7 +45,7 @@ func (p *FeedHooks) FileMeta(s ssh.Session, data *filehandlers.PostMetaData) err
28 parsedText := shared.ListParseText(string(data.Text))
29
30 if parsedText.Title == "" {
31- data.Title = shared.ToUpper(data.Slug)
32+ data.Title = utils.ToUpper(data.Slug)
33 } else {
34 data.Title = parsedText.Title
35 }
+8,
-8
1@@ -14,16 +14,16 @@ import (
2 "github.com/picosh/pico/db/postgres"
3 "github.com/picosh/pico/filehandlers"
4 "github.com/picosh/pico/filehandlers/util"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/shared/storage"
7 wsh "github.com/picosh/pico/wish"
8+ "github.com/picosh/send/auth"
9 "github.com/picosh/send/list"
10 "github.com/picosh/send/pipe"
11+ wishrsync "github.com/picosh/send/protocols/rsync"
12+ "github.com/picosh/send/protocols/scp"
13+ "github.com/picosh/send/protocols/sftp"
14 "github.com/picosh/send/proxy"
15- "github.com/picosh/send/send/auth"
16- wishrsync "github.com/picosh/send/send/rsync"
17- "github.com/picosh/send/send/scp"
18- "github.com/picosh/send/send/sftp"
19+ "github.com/picosh/utils"
20 )
21
22 func createRouter(handler *filehandlers.FileHandlerRouter) proxy.Router {
23@@ -52,9 +52,9 @@ func withProxy(handler *filehandlers.FileHandlerRouter, otherMiddleware ...wish.
24 }
25
26 func StartSshServer() {
27- host := shared.GetEnv("LISTS_HOST", "0.0.0.0")
28- port := shared.GetEnv("LISTS_SSH_PORT", "2222")
29- promPort := shared.GetEnv("LISTS_PROM_PORT", "9222")
30+ host := utils.GetEnv("LISTS_HOST", "0.0.0.0")
31+ port := utils.GetEnv("LISTS_SSH_PORT", "2222")
32+ promPort := utils.GetEnv("LISTS_PROM_PORT", "9222")
33 cfg := NewConfigSite()
34 logger := cfg.Logger
35 dbh := postgres.NewDB(cfg.DbURL, cfg.Logger)
+14,
-13
1@@ -22,7 +22,8 @@ import (
2 "github.com/picosh/pico/shared/storage"
3 "github.com/picosh/pobj"
4 sst "github.com/picosh/pobj/storage"
5- "github.com/picosh/send/send/utils"
6+ sendutils "github.com/picosh/send/utils"
7+ "github.com/picosh/utils"
8 ignore "github.com/sabhiram/go-gitignore"
9 )
10
11@@ -91,7 +92,7 @@ func shouldIgnoreFile(fp, ignoreStr string) bool {
12 }
13
14 type FileData struct {
15- *utils.FileEntry
16+ *sendutils.FileEntry
17 User *db.User
18 Bucket sst.Bucket
19 Project *db.Project
20@@ -116,13 +117,13 @@ func (h *UploadAssetHandler) GetLogger() *slog.Logger {
21 return h.Cfg.Logger
22 }
23
24-func (h *UploadAssetHandler) Read(s ssh.Session, entry *utils.FileEntry) (os.FileInfo, utils.ReaderAtCloser, error) {
25+func (h *UploadAssetHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
26 user, err := futil.GetUser(s.Context())
27 if err != nil {
28 return nil, nil, err
29 }
30
31- fileInfo := &utils.VirtualFile{
32+ fileInfo := &sendutils.VirtualFile{
33 FName: filepath.Base(entry.Filepath),
34 FIsDir: false,
35 FSize: entry.Size,
36@@ -170,7 +171,7 @@ func (h *UploadAssetHandler) List(s ssh.Session, fpath string, isDir bool, recur
37 name = "/"
38 }
39
40- info := &utils.VirtualFile{
41+ info := &sendutils.VirtualFile{
42 FName: name,
43 FIsDir: true,
44 }
45@@ -243,7 +244,7 @@ func (h *UploadAssetHandler) findDenylist(bucket sst.Bucket, project *db.Project
46 return str, nil
47 }
48
49-func (h *UploadAssetHandler) Write(s ssh.Session, entry *utils.FileEntry) (string, error) {
50+func (h *UploadAssetHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (string, error) {
51 user, err := futil.GetUser(s.Context())
52 if err != nil {
53 h.Cfg.Logger.Error("user not found in ctx", "err", err.Error())
54@@ -365,7 +366,7 @@ func (h *UploadAssetHandler) Write(s ssh.Session, entry *utils.FileEntry) (strin
55 )
56
57 fsize, err := h.writeAsset(
58- shared.NewMaxBytesReader(data.Reader, int64(sizeRemaining)),
59+ utils.NewMaxBytesReader(data.Reader, int64(sizeRemaining)),
60 data,
61 )
62 if err != nil {
63@@ -373,9 +374,9 @@ func (h *UploadAssetHandler) Write(s ssh.Session, entry *utils.FileEntry) (strin
64 cerr := fmt.Errorf(
65 "%s: storage size %.2fmb, storage max %.2fmb, file max %.2fmb",
66 err,
67- shared.BytesToMB(int(curStorageSize)),
68- shared.BytesToMB(int(storageMax)),
69- shared.BytesToMB(int(fileMax)),
70+ utils.BytesToMB(int(curStorageSize)),
71+ utils.BytesToMB(int(storageMax)),
72+ utils.BytesToMB(int(fileMax)),
73 )
74 return "", cerr
75 }
76@@ -393,15 +394,15 @@ func (h *UploadAssetHandler) Write(s ssh.Session, entry *utils.FileEntry) (strin
77 str := fmt.Sprintf(
78 "%s (space: %.2f/%.2fGB, %.2f%%)",
79 url,
80- shared.BytesToGB(int(nextStorageSize)),
81- shared.BytesToGB(maxSize),
82+ utils.BytesToGB(int(nextStorageSize)),
83+ utils.BytesToGB(maxSize),
84 (float32(nextStorageSize)/float32(maxSize))*100,
85 )
86
87 return str, nil
88 }
89
90-func (h *UploadAssetHandler) Delete(s ssh.Session, entry *utils.FileEntry) error {
91+func (h *UploadAssetHandler) Delete(s ssh.Session, entry *sendutils.FileEntry) error {
92 user, err := futil.GetUser(s.Context())
93 if err != nil {
94 h.Cfg.Logger.Error("user not found in ctx", "err", err.Error())
+11,
-10
1@@ -18,7 +18,8 @@ import (
2 "github.com/picosh/pico/shared"
3 "github.com/picosh/pico/shared/storage"
4 "github.com/picosh/pobj"
5- "github.com/picosh/send/send/utils"
6+ sendutils "github.com/picosh/send/utils"
7+ "github.com/picosh/utils"
8 )
9
10 var Space = "imgs"
11@@ -29,7 +30,7 @@ type PostMetaData struct {
12 Cur *db.Post
13 Tags []string
14 User *db.User
15- *utils.FileEntry
16+ *sendutils.FileEntry
17 FeatureFlag *db.FeatureFlag
18 }
19
20@@ -47,7 +48,7 @@ func NewUploadImgHandler(dbpool db.DB, cfg *shared.ConfigSite, storage storage.S
21 }
22 }
23
24-func (h *UploadImgHandler) Read(s ssh.Session, entry *utils.FileEntry) (os.FileInfo, utils.ReaderAtCloser, error) {
25+func (h *UploadImgHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
26 user, err := util.GetUser(s.Context())
27 if err != nil {
28 return nil, nil, err
29@@ -64,7 +65,7 @@ func (h *UploadImgHandler) Read(s ssh.Session, entry *utils.FileEntry) (os.FileI
30 return nil, nil, err
31 }
32
33- fileInfo := &utils.VirtualFile{
34+ fileInfo := &sendutils.VirtualFile{
35 FName: post.Filename,
36 FIsDir: false,
37 FSize: int64(post.FileSize),
38@@ -86,7 +87,7 @@ func (h *UploadImgHandler) Read(s ssh.Session, entry *utils.FileEntry) (os.FileI
39 return fileInfo, reader, nil
40 }
41
42-func (h *UploadImgHandler) Write(s ssh.Session, entry *utils.FileEntry) (string, error) {
43+func (h *UploadImgHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (string, error) {
44 logger := h.Cfg.Logger
45 user, err := util.GetUser(s.Context())
46 if err != nil {
47@@ -123,8 +124,8 @@ func (h *UploadImgHandler) Write(s ssh.Session, entry *utils.FileEntry) (string,
48
49 now := time.Now()
50 fileSize := binary.Size(text)
51- shasum := shared.Shasum(text)
52- slug := shared.SanitizeFileExt(filename)
53+ shasum := utils.Shasum(text)
54+ slug := utils.SanitizeFileExt(filename)
55
56 nextPost := db.Post{
57 Filename: filename,
58@@ -184,14 +185,14 @@ func (h *UploadImgHandler) Write(s ssh.Session, entry *utils.FileEntry) (string,
59 str := fmt.Sprintf(
60 "%s (space: %.2f/%.2fGB, %.2f%%)",
61 url,
62- shared.BytesToGB(totalFileSize),
63- shared.BytesToGB(maxSize),
64+ utils.BytesToGB(totalFileSize),
65+ utils.BytesToGB(maxSize),
66 (float32(totalFileSize)/float32(maxSize))*100,
67 )
68 return str, nil
69 }
70
71-func (h *UploadImgHandler) Delete(s ssh.Session, entry *utils.FileEntry) error {
72+func (h *UploadImgHandler) Delete(s ssh.Session, entry *sendutils.FileEntry) error {
73 user, err := util.GetUser(s.Context())
74 if err != nil {
75 return err
+5,
-4
1@@ -10,7 +10,8 @@ import (
2 "github.com/picosh/pico/db"
3 "github.com/picosh/pico/filehandlers/util"
4 "github.com/picosh/pico/shared"
5- "github.com/picosh/send/send/utils"
6+ sendutils "github.com/picosh/send/utils"
7+ "github.com/picosh/utils"
8 )
9
10 func (h *UploadImgHandler) validateImg(data *PostMetaData) (bool, error) {
11@@ -29,7 +30,7 @@ func (h *UploadImgHandler) validateImg(data *PostMetaData) (bool, error) {
12 return false, fmt.Errorf("ERROR: user (%s) has exceeded (%d bytes) max (%d bytes)", data.User.Name, totalFileSize, storageMax)
13 }
14
15- if !shared.IsExtAllowed(data.Filepath, h.Cfg.AllowedExt) {
16+ if !utils.IsExtAllowed(data.Filepath, h.Cfg.AllowedExt) {
17 extStr := strings.Join(h.Cfg.AllowedExt, ",")
18 err := fmt.Errorf(
19 "ERROR: (%s) invalid file, format must be (%s), skipping",
20@@ -59,8 +60,8 @@ func (h *UploadImgHandler) metaImg(data *PostMetaData) error {
21 fname, _, err := h.Storage.PutObject(
22 bucket,
23 data.Filename,
24- utils.NopReaderAtCloser(reader),
25- &utils.FileEntry{},
26+ sendutils.NopReaderAtCloser(reader),
27+ &sendutils.FileEntry{},
28 )
29 if err != nil {
30 return err
+10,
-9
1@@ -15,7 +15,8 @@ import (
2 "github.com/picosh/pico/filehandlers/util"
3 "github.com/picosh/pico/shared"
4 "github.com/picosh/pico/shared/storage"
5- "github.com/picosh/send/send/utils"
6+ sendutils "github.com/picosh/send/utils"
7+ "github.com/picosh/utils"
8 )
9
10 type PostMetaData struct {
11@@ -23,7 +24,7 @@ type PostMetaData struct {
12 Cur *db.Post
13 Tags []string
14 User *db.User
15- FileEntry *utils.FileEntry
16+ FileEntry *sendutils.FileEntry
17 Aliases []string
18 }
19
20@@ -46,7 +47,7 @@ func NewScpPostHandler(dbpool db.DB, cfg *shared.ConfigSite, hooks ScpFileHooks,
21 }
22 }
23
24-func (h *ScpUploadHandler) Read(s ssh.Session, entry *utils.FileEntry) (os.FileInfo, utils.ReaderAtCloser, error) {
25+func (h *ScpUploadHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
26 user, err := util.GetUser(s.Context())
27 if err != nil {
28 return nil, nil, err
29@@ -62,19 +63,19 @@ func (h *ScpUploadHandler) Read(s ssh.Session, entry *utils.FileEntry) (os.FileI
30 return nil, nil, err
31 }
32
33- fileInfo := &utils.VirtualFile{
34+ fileInfo := &sendutils.VirtualFile{
35 FName: post.Filename,
36 FIsDir: false,
37 FSize: int64(post.FileSize),
38 FModTime: *post.UpdatedAt,
39 }
40
41- reader := utils.NopReaderAtCloser(strings.NewReader(post.Text))
42+ reader := sendutils.NopReaderAtCloser(strings.NewReader(post.Text))
43
44 return fileInfo, reader, nil
45 }
46
47-func (h *ScpUploadHandler) Write(s ssh.Session, entry *utils.FileEntry) (string, error) {
48+func (h *ScpUploadHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (string, error) {
49 logger := h.Cfg.Logger
50 user, err := util.GetUser(s.Context())
51 if err != nil {
52@@ -106,9 +107,9 @@ func (h *ScpUploadHandler) Write(s ssh.Session, entry *utils.FileEntry) (string,
53 }
54
55 now := time.Now()
56- slug := shared.SanitizeFileExt(filename)
57+ slug := utils.SanitizeFileExt(filename)
58 fileSize := binary.Size(origText)
59- shasum := shared.Shasum(origText)
60+ shasum := utils.Shasum(origText)
61
62 nextPost := db.Post{
63 Filename: filename,
64@@ -261,7 +262,7 @@ func (h *ScpUploadHandler) Write(s ssh.Session, entry *utils.FileEntry) (string,
65 return h.Cfg.FullPostURL(curl, user.Name, metadata.Slug), nil
66 }
67
68-func (h *ScpUploadHandler) Delete(s ssh.Session, entry *utils.FileEntry) error {
69+func (h *ScpUploadHandler) Delete(s ssh.Session, entry *sendutils.FileEntry) error {
70 logger := h.Cfg.Logger
71 user, err := util.GetUser(s.Context())
72 if err != nil {
+1,
-1
1@@ -12,7 +12,7 @@ import (
2 "github.com/picosh/pico/db"
3 "github.com/picosh/pico/filehandlers/util"
4 "github.com/picosh/pico/shared"
5- "github.com/picosh/send/send/utils"
6+ "github.com/picosh/send/utils"
7 )
8
9 type ReadWriteHandler interface {
+2,
-4
1@@ -6,6 +6,7 @@ import (
2 "github.com/charmbracelet/ssh"
3 "github.com/picosh/pico/db"
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 type SshAuthHandler struct {
9@@ -25,10 +26,7 @@ func NewSshAuthHandler(dbpool db.DB, logger *slog.Logger, cfg *shared.ConfigSite
10 func (r *SshAuthHandler) PubkeyAuthHandler(ctx ssh.Context, key ssh.PublicKey) bool {
11 shared.SetPublicKeyCtx(ctx, key)
12
13- pubkey, err := shared.KeyForKeyText(key)
14- if err != nil {
15- return false
16- }
17+ pubkey := utils.KeyForKeyText(key)
18
19 user, err := r.DBPool.FindUserForKey(ctx.User(), pubkey)
20 if err != nil {
M
go.mod
+47,
-44
1@@ -30,15 +30,16 @@ require (
2 github.com/gorilla/feeds v1.1.2
3 github.com/lib/pq v1.10.9
4 github.com/microcosm-cc/bluemonday v1.0.26
5- github.com/minio/minio-go/v7 v7.0.70
6+ github.com/minio/minio-go/v7 v7.0.77
7 github.com/mmcdole/gofeed v1.3.0
8 github.com/muesli/reflow v0.3.0
9- github.com/muesli/termenv v0.15.3-0.20240509142007-81b8f94111d5
10+ github.com/muesli/termenv v0.15.3-0.20240912151726-82936c5ea257
11 github.com/neurosnap/go-exif-remove v0.0.0-20221010134343-50d1e3c35577
12- github.com/picosh/pobj v0.0.0-20241005185823-c92bd8ee07f8
13- github.com/picosh/pubsub v0.0.0-20241003170126-d92d74f10efe
14- github.com/picosh/send v0.0.0-20240820031602-5d3b1a4494cc
15+ github.com/picosh/pobj v0.0.0-20241008013754-bbbfc341e2cf
16+ github.com/picosh/pubsub v0.0.0-20241008010300-a63fd95dc8ed
17+ github.com/picosh/send v0.0.0-20241008013240-6fdbff00f848
18 github.com/picosh/tunkit v0.0.0-20240709033345-8315d4f3cd0e
19+ github.com/picosh/utils v0.0.0-20241008004349-f48b50af554b
20 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
21 github.com/sendgrid/sendgrid-go v3.14.0+incompatible
22 github.com/simplesurance/go-ip-anonymizer v0.0.0-20200429124537-35a880f8e87d
23@@ -49,7 +50,7 @@ require (
24 go.abhg.dev/goldmark/anchor v0.1.1
25 go.abhg.dev/goldmark/hashtag v0.3.1
26 go.abhg.dev/goldmark/toc v0.10.0
27- golang.org/x/crypto v0.27.0
28+ golang.org/x/crypto v0.28.0
29 gopkg.in/yaml.v2 v2.4.0
30 )
31
32@@ -61,25 +62,25 @@ require (
33 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
34 github.com/antoniomika/syncmap v1.0.0 // indirect
35 github.com/atotto/clipboard v0.1.4 // indirect
36- github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect
37- github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect
38- github.com/aws/aws-sdk-go-v2/config v1.27.28 // indirect
39- github.com/aws/aws-sdk-go-v2/credentials v1.17.28 // indirect
40- github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect
41- github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.11 // indirect
42- github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
43- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
44+ github.com/aws/aws-sdk-go-v2 v1.32.1 // indirect
45+ github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect
46+ github.com/aws/aws-sdk-go-v2/config v1.27.42 // indirect
47+ github.com/aws/aws-sdk-go-v2/credentials v1.17.40 // indirect
48+ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.16 // indirect
49+ github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.29 // indirect
50+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.20 // indirect
51+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.20 // indirect
52 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
53- github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16 // indirect
54- github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
55- github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18 // indirect
56- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect
57- github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16 // indirect
58- github.com/aws/aws-sdk-go-v2/service/s3 v1.59.0 // indirect
59- github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect
60- github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect
61- github.com/aws/aws-sdk-go-v2/service/sts v1.30.4 // indirect
62- github.com/aws/smithy-go v1.20.4 // indirect
63+ github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.20 // indirect
64+ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect
65+ github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.1 // indirect
66+ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.1 // indirect
67+ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.1 // indirect
68+ github.com/aws/aws-sdk-go-v2/service/s3 v1.65.1 // indirect
69+ github.com/aws/aws-sdk-go-v2/service/sso v1.24.1 // indirect
70+ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.1 // indirect
71+ github.com/aws/aws-sdk-go-v2/service/sts v1.32.1 // indirect
72+ github.com/aws/smithy-go v1.22.0 // indirect
73 github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
74 github.com/aymerick/douceur v0.2.0 // indirect
75 github.com/beorn7/perks v1.0.1 // indirect
76@@ -88,7 +89,7 @@ require (
77 github.com/charmbracelet/log v0.4.0 // indirect
78 github.com/charmbracelet/x/ansi v0.3.2 // indirect
79 github.com/charmbracelet/x/conpty v0.1.0 // indirect
80- github.com/charmbracelet/x/errors v0.0.0-20240919170804-a4978c8e603a // indirect
81+ github.com/charmbracelet/x/errors v0.0.0-20241007193646-7cc13b2883e3 // indirect
82 github.com/charmbracelet/x/input v0.2.0 // indirect
83 github.com/charmbracelet/x/term v0.2.0 // indirect
84 github.com/charmbracelet/x/termios v0.1.0 // indirect
85@@ -109,6 +110,7 @@ require (
86 github.com/gdamore/encoding v1.0.1 // indirect
87 github.com/gdamore/tcell/v2 v2.7.4 // indirect
88 github.com/go-errors/errors v1.5.1 // indirect
89+ github.com/go-ini/ini v1.67.0 // indirect
90 github.com/go-logfmt/logfmt v0.6.0 // indirect
91 github.com/go-ole/go-ole v1.3.0 // indirect
92 github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
93@@ -120,16 +122,16 @@ require (
94 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
95 github.com/gorilla/css v1.0.1 // indirect
96 github.com/json-iterator/go v1.1.12 // indirect
97- github.com/klauspost/compress v1.17.8 // indirect
98- github.com/klauspost/cpuid/v2 v2.2.7 // indirect
99+ github.com/klauspost/compress v1.17.10 // indirect
100+ github.com/klauspost/cpuid/v2 v2.2.8 // indirect
101 github.com/kr/fs v0.1.0 // indirect
102 github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
103- github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
104+ github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
105 github.com/mattn/go-isatty v0.0.20 // indirect
106 github.com/mattn/go-localereader v0.0.1 // indirect
107 github.com/mattn/go-runewidth v0.0.16 // indirect
108 github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
109- github.com/minio/madmin-go/v3 v3.0.54 // indirect
110+ github.com/minio/madmin-go/v3 v3.0.70 // indirect
111 github.com/minio/md5-simd v1.1.2 // indirect
112 github.com/mmcdole/goxpp v1.1.1 // indirect
113 github.com/mmcloughlin/md4 v0.1.2 // indirect
114@@ -137,39 +139,40 @@ require (
115 github.com/modern-go/reflect2 v1.0.2 // indirect
116 github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
117 github.com/muesli/cancelreader v0.2.2 // indirect
118+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
119 github.com/neurosnap/go-jpeg-image-structure v0.0.0-20221010133817-70b1c1ff679e // indirect
120 github.com/olekukonko/tablewriter v0.0.5 // indirect
121- github.com/philhofer/fwd v1.1.2 // indirect
122+ github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
123 github.com/picosh/go-rsync-receiver v0.0.0-20240709135253-1daf4b12a9fc // indirect
124 github.com/pkg/sftp v1.13.6 // indirect
125 github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
126- github.com/prometheus/client_golang v1.19.1 // indirect
127+ github.com/prometheus/client_golang v1.20.4 // indirect
128 github.com/prometheus/client_model v0.6.1 // indirect
129- github.com/prometheus/common v0.54.0 // indirect
130+ github.com/prometheus/common v0.60.0 // indirect
131 github.com/prometheus/procfs v0.15.1 // indirect
132- github.com/prometheus/prom2json v1.3.3 // indirect
133+ github.com/prometheus/prom2json v1.4.1 // indirect
134+ github.com/prometheus/prometheus v0.54.1 // indirect
135 github.com/rivo/uniseg v0.4.7 // indirect
136 github.com/rogpeppe/go-internal v1.11.0 // indirect
137- github.com/rs/xid v1.5.0 // indirect
138- github.com/safchain/ethtool v0.3.0 // indirect
139+ github.com/rs/xid v1.6.0 // indirect
140+ github.com/safchain/ethtool v0.4.1 // indirect
141 github.com/secure-io/sio-go v0.3.1 // indirect
142 github.com/sendgrid/rest v2.6.9+incompatible // indirect
143 github.com/shirou/gopsutil/v3 v3.24.5 // indirect
144 github.com/shoenig/go-m1cpu v0.1.6 // indirect
145- github.com/tinylib/msgp v1.1.9 // indirect
146+ github.com/tinylib/msgp v1.2.2 // indirect
147 github.com/tklauser/go-sysconf v0.3.14 // indirect
148- github.com/tklauser/numcpus v0.8.0 // indirect
149+ github.com/tklauser/numcpus v0.9.0 // indirect
150 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
151 github.com/yuin/goldmark-emoji v1.0.2 // indirect
152 github.com/yusufpapurcu/wmi v1.2.4 // indirect
153- golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
154- golang.org/x/net v0.25.0 // indirect
155+ golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 // indirect
156+ golang.org/x/net v0.30.0 // indirect
157 golang.org/x/sync v0.8.0 // indirect
158- golang.org/x/sys v0.25.0 // indirect
159- golang.org/x/term v0.24.0 // indirect
160- golang.org/x/text v0.18.0 // indirect
161+ golang.org/x/sys v0.26.0 // indirect
162+ golang.org/x/term v0.25.0 // indirect
163+ golang.org/x/text v0.19.0 // indirect
164 golang.org/x/time v0.6.0 // indirect
165- google.golang.org/protobuf v1.34.1 // indirect
166- gopkg.in/ini.v1 v1.67.0 // indirect
167+ google.golang.org/protobuf v1.35.1 // indirect
168 mvdan.cc/xurls/v2 v2.5.0 // indirect
169 )
M
go.sum
+99,
-93
1@@ -23,44 +23,44 @@ github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhP
2 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
3 github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
4 github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
5-github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8=
6-github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
7-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 h1:70PVAiL15/aBMh5LThwgXdSQorVr91L127ttckI9QQU=
8-github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4/go.mod h1:/MQxMqci8tlqDH+pjmoLu1i0tbWCUP1hhyMRuFxpQCw=
9-github.com/aws/aws-sdk-go-v2/config v1.27.28 h1:OTxWGW/91C61QlneCtnD62NLb4W616/NM1jA8LhJqbg=
10-github.com/aws/aws-sdk-go-v2/config v1.27.28/go.mod h1:uzVRVtJSU5EFv6Fu82AoVFKozJi2ZCY6WRCXj06rbvs=
11-github.com/aws/aws-sdk-go-v2/credentials v1.17.28 h1:m8+AHY/ND8CMHJnPoH7PJIRakWGa4gbfbxuY9TGTUXM=
12-github.com/aws/aws-sdk-go-v2/credentials v1.17.28/go.mod h1:6TF7dSc78ehD1SL6KpRIPKMA1GyyWflIkjqg+qmf4+c=
13-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 h1:yjwoSyDZF8Jth+mUk5lSPJCkMC0lMy6FaCD51jm6ayE=
14-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12/go.mod h1:fuR57fAgMk7ot3WcNQfb6rSEn+SUffl7ri+aa8uKysI=
15-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.11 h1:FEDZD/Axt5tKSkPAs967KZ++MkvYdBqr0a+cetRbjLM=
16-github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.11/go.mod h1:dvlsbA32KfvCzqwTiX7maABgFek2RyUuYEJ3kyn/PmQ=
17-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY=
18-github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc=
19-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I=
20-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs=
21+github.com/aws/aws-sdk-go-v2 v1.32.1 h1:8WuZ43ytA+TV6QEPT/R23mr7pWyI7bSSiEHdt9BS2Pw=
22+github.com/aws/aws-sdk-go-v2 v1.32.1/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
23+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 h1:pT3hpW0cOHRJx8Y0DfJUEQuqPild8jRGmSFmBgvydr0=
24+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6/go.mod h1:j/I2++U0xX+cr44QjHay4Cvxj6FUbnxrgmqN3H1jTZA=
25+github.com/aws/aws-sdk-go-v2/config v1.27.42 h1:Zsy9coUPuOsCWkjTvHpl2/DB9bptXtv7WeNPxvFr87s=
26+github.com/aws/aws-sdk-go-v2/config v1.27.42/go.mod h1:FGASs+PuJM2EY+8rt8qyQKLPbbX/S5oY+6WzJ/KE7ko=
27+github.com/aws/aws-sdk-go-v2/credentials v1.17.40 h1:RjnlA7t0p/IamxAM7FUJ5uS13Vszh4sjVGvsx91tGro=
28+github.com/aws/aws-sdk-go-v2/credentials v1.17.40/go.mod h1:dgpdnSs1Bp/atS6vLlW83h9xZPP+uSPB/27dFSgC1BM=
29+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.16 h1:fwrer1pJeaiia0CcOfWVbZxvj9Adc7rsuaMTwPR0DIA=
30+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.16/go.mod h1:XyEwwp8XI4zMar7MTnJ0Sk7qY/9aN8Hp929XhuX5SF8=
31+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.29 h1:eyeHfJ9FAb7sd5ODTkjrfot3gS0Ln4vn/18l7zZMCik=
32+github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.29/go.mod h1:JpzRPe12SjlOmuqgi+/5RmgfbsWzDYdfxe3Abrk2kW8=
33+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.20 h1:OErdlGnt+hg3tTwGYAlKvFkKVUo/TXkoHcxDxuhYYU8=
34+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.20/go.mod h1:HsPfuL5gs+407ByRXBMgpYoyrV1sgMrzd18yMXQHJpo=
35+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.20 h1:822cE1CYSwY/EZnErlF46pyynuxvf1p+VydHRQW+XNs=
36+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.20/go.mod h1:79/Tn7H7hYC5Gjz6fbnOV4OeBpkao7E8Tv95RO72pMM=
37 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
38 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
39-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16 h1:mimdLQkIX1zr8GIPY1ZtALdBQGxcASiBd2MOp8m/dMc=
40-github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16/go.mod h1:YHk6owoSwrIsok+cAH9PENCOGoH5PU2EllX4vLtSrsY=
41-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI=
42-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc=
43-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18 h1:GckUnpm4EJOAio1c8o25a+b3lVfwVzC9gnSBqiiNmZM=
44-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18/go.mod h1:Br6+bxfG33Dk3ynmkhsW2Z/t9D4+lRqdLDNCKi85w0U=
45-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 h1:tJ5RnkHCiSH0jyd6gROjlJtNwov0eGYNz8s8nFcR0jQ=
46-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18/go.mod h1:++NHzT+nAF7ZPrHPsA+ENvsXkOO8wEu+C6RXltAG4/c=
47-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16 h1:jg16PhLPUiHIj8zYIW6bqzeQSuHVEiWnGA0Brz5Xv2I=
48-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16/go.mod h1:Uyk1zE1VVdsHSU7096h/rwnXDzOzYQVl+FNPhPw7ShY=
49-github.com/aws/aws-sdk-go-v2/service/s3 v1.59.0 h1:Cso4Ev/XauMVsbwdhYEoxg8rxZWw43CFqqaPB5w3W2c=
50-github.com/aws/aws-sdk-go-v2/service/s3 v1.59.0/go.mod h1:BSPI0EfnYUuNHPS0uqIo5VrRwzie+Fp+YhQOUs16sKI=
51-github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 h1:zCsFCKvbj25i7p1u94imVoO447I/sFv8qq+lGJhRN0c=
52-github.com/aws/aws-sdk-go-v2/service/sso v1.22.5/go.mod h1:ZeDX1SnKsVlejeuz41GiajjZpRSWR7/42q/EyA/QEiM=
53-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 h1:SKvPgvdvmiTWoi0GAJ7AsJfOz3ngVkD/ERbs5pUnHNI=
54-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5/go.mod h1:20sz31hv/WsPa3HhU3hfrIet2kxM4Pe0r20eBZ20Tac=
55-github.com/aws/aws-sdk-go-v2/service/sts v1.30.4 h1:iAckBT2OeEK/kBDyN/jDtpEExhjeeA/Im2q4X0rJZT8=
56-github.com/aws/aws-sdk-go-v2/service/sts v1.30.4/go.mod h1:vmSqFK+BVIwVpDAGZB3CoCXHzurt4qBE8lf+I/kRTh0=
57-github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
58-github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
59+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.20 h1:HO5UCCkLmeWkJZHLvLDfylKv8ca28XLAX3HojZz2shI=
60+github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.20/go.mod h1:IO0HUM6Ouk/s7Rx3hiLtFU3mc+9OJFFygjsaxFBhAbk=
61+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g=
62+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ=
63+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.1 h1:UeW3Ul28hkKvB3beWImBvO7U62tSmapxaqk8sX9SMCU=
64+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.1/go.mod h1:TER/1DuTxSN6RFQpk3xfD9hK4A1gQ7ainfkwHV3LPtU=
65+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.1 h1:5vBMBTakOvtd8aNaicswcrr9qqCYUlasuzyoU6/0g8I=
66+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.1/go.mod h1:WSUbDa5qdg05Q558KXx2Scb+EDvOPXT9gfET0fyrJSk=
67+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.1 h1:T6oOYbNQ+iqdtG1/mTJvMBg/YFyHR8Z8URyG3qK+Anc=
68+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.1/go.mod h1:25CEM6c1e2vyLcr3fPritPsdsoMwNAOc9//M1QAwtDk=
69+github.com/aws/aws-sdk-go-v2/service/s3 v1.65.1 h1:HQR79P0F0C2YQOaS2Z+90YK9DH22z9D6Neplaj0yuy4=
70+github.com/aws/aws-sdk-go-v2/service/s3 v1.65.1/go.mod h1:xYVl5BX9Ws7+ZM58b3w0kq36TR1Dgw2OMkjSr6YTWXg=
71+github.com/aws/aws-sdk-go-v2/service/sso v1.24.1 h1:aAIr0WhAgvKrxZtkBqne87Gjmd7/lJVTFkR2l2yuhL8=
72+github.com/aws/aws-sdk-go-v2/service/sso v1.24.1/go.mod h1:8XhxGMWUfikJuginPQl5SGZ0LSJuNX3TCEQmFWZwHTM=
73+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.1 h1:J6kIsIkgFOaU6aKjigXJoue1XEHtKIIrpSh4vKdmRTs=
74+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.1/go.mod h1:2V2JLP7tXOmUbL3Hd1ojq+774t2KUAEQ35//shoNEL0=
75+github.com/aws/aws-sdk-go-v2/service/sts v1.32.1 h1:q76Ig4OaJzVJGNUSGO3wjSTBS94g+EhHIbpY9rPvkxs=
76+github.com/aws/aws-sdk-go-v2/service/sts v1.32.1/go.mod h1:664dajZ7uS7JMUMUG0R5bWbtN97KECNCVdFDdQ6Ipu8=
77+github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
78+github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
79 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
80 github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
81 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
82@@ -91,8 +91,8 @@ github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyf
83 github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
84 github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U=
85 github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ=
86-github.com/charmbracelet/x/errors v0.0.0-20240919170804-a4978c8e603a h1:IlWNrDYRP6lyttqsFHDhmo8NXggMwBuhvvCP+Wmb2a8=
87-github.com/charmbracelet/x/errors v0.0.0-20240919170804-a4978c8e603a/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
88+github.com/charmbracelet/x/errors v0.0.0-20241007193646-7cc13b2883e3 h1:nsBhzPXBqeXEGZ9ztveSIPdf790BcDikbaEH3vMglH4=
89+github.com/charmbracelet/x/errors v0.0.0-20241007193646-7cc13b2883e3/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
90 github.com/charmbracelet/x/input v0.2.0 h1:1Sv+y/flcqUfUH2PXNIDKDIdT2G8smOnGOgawqhwy8A=
91 github.com/charmbracelet/x/input v0.2.0/go.mod h1:KUSFIS6uQymtnr5lHVSOK9j8RvwTD4YHnWnzJUYnd/M=
92 github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
93@@ -102,8 +102,9 @@ github.com/charmbracelet/x/termios v0.1.0/go.mod h1:H/EVv/KRnrYjz+fCYa9bsKdqF3S8
94 github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0=
95 github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
96 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
97-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
98 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
99+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
100+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
101 github.com/delthas/go-libnp v0.0.0-20221222161248-0e45ece1f878 h1:v8W8eW7eb2bHFXBA80UKcoe0TvEu46NlTHSDRvgAbMU=
102 github.com/delthas/go-libnp v0.0.0-20221222161248-0e45ece1f878/go.mod h1:aGVXnhWpDlt5U4SphG97o1gszctZKvBTXy320E8Buw4=
103 github.com/delthas/go-localeinfo v0.0.0-20221116001557-686a1e185118 h1:Xzf9ra1QRJXD62gwudjI2iBq7x9CusvHd83Dg2OnUmE=
104@@ -152,6 +153,8 @@ github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE
105 github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
106 github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
107 github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
108+github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
109+github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
110 github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
111 github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
112 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
113@@ -189,23 +192,25 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo
114 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
115 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
116 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
117-github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
118-github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
119+github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
120+github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
121 github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
122-github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
123-github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
124+github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
125+github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
126 github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
127 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
128 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
129 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
130 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
131 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
132+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
133+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
134 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
135 github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
136 github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
137 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
138-github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
139-github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
140+github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
141+github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
142 github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
143 github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
144 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
145@@ -223,12 +228,12 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk
146 github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
147 github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
148 github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
149-github.com/minio/madmin-go/v3 v3.0.54 h1:Mwp6JYSY9GxbrUoMMij2rTzSBaCps575dlsuKO8D3yk=
150-github.com/minio/madmin-go/v3 v3.0.54/go.mod h1:IFAwr0XMrdsLovxAdCcuq/eoL4nRuMVQQv0iubJANQw=
151+github.com/minio/madmin-go/v3 v3.0.70 h1:zrFCXLcV6PR74JC0yytK4Dk2qsaCV8kXQoPTvcusR2k=
152+github.com/minio/madmin-go/v3 v3.0.70/go.mod h1:TOTc96ZkMknNhl+ReO/V68bQfgRGfH+8iy7YaDzHdXA=
153 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
154 github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
155-github.com/minio/minio-go/v7 v7.0.70 h1:1u9NtMgfK1U42kUxcsl5v0yj6TEOPR497OAQxpJnn2g=
156-github.com/minio/minio-go/v7 v7.0.70/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo=
157+github.com/minio/minio-go/v7 v7.0.77 h1:GaGghJRg9nwDVlNbwYjSDJT1rqltQkBFDsypWX1v3Bw=
158+github.com/minio/minio-go/v7 v7.0.77/go.mod h1:AVM3IUN6WwKzmwBxVdjzhH8xq+f57JSbbvzqvUzR6eg=
159 github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
160 github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE=
161 github.com/mmcdole/goxpp v1.1.1 h1:RGIX+D6iQRIunGHrKqnA2+700XMCnNv0bAOOv5MUhx8=
162@@ -246,32 +251,32 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
163 github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
164 github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
165 github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
166-github.com/muesli/termenv v0.15.3-0.20240509142007-81b8f94111d5 h1:NiONcKK0EV5gUZcnCiPMORaZA0eBDc+Fgepl9xl4lZ8=
167-github.com/muesli/termenv v0.15.3-0.20240509142007-81b8f94111d5/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ=
168+github.com/muesli/termenv v0.15.3-0.20240912151726-82936c5ea257 h1:RNw/zu+CJemcRlDFPjElZUbY2UlI/MA2B3I6PM3Isiw=
169+github.com/muesli/termenv v0.15.3-0.20240912151726-82936c5ea257/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ=
170+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
171+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
172 github.com/neurosnap/go-exif-remove v0.0.0-20221010134343-50d1e3c35577 h1:hVmVNttSLNloGsbFKVXAUHonXTd8KKrv30U/8UkloKI=
173 github.com/neurosnap/go-exif-remove v0.0.0-20221010134343-50d1e3c35577/go.mod h1:G3Cu1AW+dmRLDFpOi8eUAfc3cGoRHUjTkGjeRcndgl4=
174 github.com/neurosnap/go-jpeg-image-structure v0.0.0-20221010133817-70b1c1ff679e h1:76Dng5ms0fR+26doKZAvNqhi2UPfnLxGfPIDEr+BBlM=
175 github.com/neurosnap/go-jpeg-image-structure v0.0.0-20221010133817-70b1c1ff679e/go.mod h1:nZBDA7+RD63GDJwjZmxhxac65MJqiCIHUUUvdYOsFkk=
176 github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
177 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
178-github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
179-github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
180+github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
181+github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
182 github.com/picosh/go-rsync-receiver v0.0.0-20240709135253-1daf4b12a9fc h1:bvcsoOvaNHPquFnRkdraEo7+8t6bW7nWEhlALnwZPdI=
183 github.com/picosh/go-rsync-receiver v0.0.0-20240709135253-1daf4b12a9fc/go.mod h1:i0iR3W4GSm1PuvVxB9OH32E5jP+CYkVb2NQSe0JCtlo=
184-github.com/picosh/pobj v0.0.0-20240709135546-27097077b26a h1:Cr1xODiyd/SjjBRtYA9VX6Do3D+w+DansQzkb4NGeyA=
185-github.com/picosh/pobj v0.0.0-20240709135546-27097077b26a/go.mod h1:VIkR1MZBvxSK2OO47jikxikAO/sKb/vTmXX5ZuYTIvo=
186-github.com/picosh/pobj v0.0.0-20241005184424-366421c762d7 h1:QiKWMuxqhNKfOacqfZl0fJn7Oc53hIoB1WH7eluOqs0=
187-github.com/picosh/pobj v0.0.0-20241005184424-366421c762d7/go.mod h1:tPv/ydMnuXlGVpWUMvBlcWlyn/xu9PsVPdncYibweWY=
188-github.com/picosh/pobj v0.0.0-20241005185823-c92bd8ee07f8 h1:M6GjB28u/sThE3gyKx4V2iqrokdMJAr1pQ41Q3mK04I=
189-github.com/picosh/pobj v0.0.0-20241005185823-c92bd8ee07f8/go.mod h1:tPv/ydMnuXlGVpWUMvBlcWlyn/xu9PsVPdncYibweWY=
190-github.com/picosh/pubsub v0.0.0-20241003170126-d92d74f10efe h1:NQA2eXxqFPjVr/8DX073Vap5kMnJk3/AAAgt/jz8cyc=
191-github.com/picosh/pubsub v0.0.0-20241003170126-d92d74f10efe/go.mod h1:gWhwrStKWJNzp9i44Bc3YQmnC+pIKvI5dYWm1GRHJac=
192-github.com/picosh/send v0.0.0-20240820031602-5d3b1a4494cc h1:IIsJuAFG2ju3cygKVKTIsYYZf21q5S3Dr1H4fGbfgJg=
193-github.com/picosh/send v0.0.0-20240820031602-5d3b1a4494cc/go.mod h1:RAgLDK3LrDK6pNeXtU9tjo28obl5DxShcTUk2nm/KCM=
194+github.com/picosh/pobj v0.0.0-20241008013754-bbbfc341e2cf h1:Ul+LuTVXRimpIneOHez05k7VOV/lDVw37I18rceEplw=
195+github.com/picosh/pobj v0.0.0-20241008013754-bbbfc341e2cf/go.mod h1:cF+eAl4G1vU+WOD8cYCKaxokHo6MWmbR8J4/SJnvESg=
196+github.com/picosh/pubsub v0.0.0-20241008010300-a63fd95dc8ed h1:aBJeQoLvq/V3hX6bgWjuuTmGzgbPNYuuwaCWU4aSJcU=
197+github.com/picosh/pubsub v0.0.0-20241008010300-a63fd95dc8ed/go.mod h1:ajolgob5MxlHdp5HllF7u3rTlCgER4InqfP7M/xl6HQ=
198+github.com/picosh/send v0.0.0-20241008013240-6fdbff00f848 h1:VWbjNNOqpJ8AB3zdw+M5+XC/SINooWLGi6WCozKwt1o=
199+github.com/picosh/send v0.0.0-20241008013240-6fdbff00f848/go.mod h1:RAgLDK3LrDK6pNeXtU9tjo28obl5DxShcTUk2nm/KCM=
200 github.com/picosh/senpai v0.0.0-20240503200611-af89e73973b0 h1:pBRIbiCj7K6rGELijb//dYhyCo8A3fvxW5dijrJVtjs=
201 github.com/picosh/senpai v0.0.0-20240503200611-af89e73973b0/go.mod h1:QaBDtybFC5gz7EG/9c3bgzuyW7W5W2rYLFZxWNuWk3Q=
202 github.com/picosh/tunkit v0.0.0-20240709033345-8315d4f3cd0e h1:3rNSjBJ6VlvngWF58V/z0fPLH7WyzKpSboC6YznECgw=
203 github.com/picosh/tunkit v0.0.0-20240709033345-8315d4f3cd0e/go.mod h1:UrDH/VCIc1wg/L6iY2zSYt4TiGw+25GsKSnkVkU40Dw=
204+github.com/picosh/utils v0.0.0-20241008004349-f48b50af554b h1:PvWk8Y7JhC1bK4Ns7FUFfcvi+BGZ+K07wTA2VDTmfDQ=
205+github.com/picosh/utils v0.0.0-20241008004349-f48b50af554b/go.mod h1:ftrp1FjbKK/mFnBAYGymA1QEtPlkA0+lWkPI5h0HKt4=
206 github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
207 github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
208 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
209@@ -279,16 +284,18 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
210 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
211 github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
212 github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
213-github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
214-github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
215+github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
216+github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
217 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
218 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
219-github.com/prometheus/common v0.54.0 h1:ZlZy0BgJhTwVZUn7dLOkwCZHUkrAqd3WYtcFCWnM1D8=
220-github.com/prometheus/common v0.54.0/go.mod h1:/TQgMJP5CuVYveyT7n/0Ix8yLNNXy9yRSkhnLTHPDIQ=
221+github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA=
222+github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
223 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
224 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
225-github.com/prometheus/prom2json v1.3.3 h1:IYfSMiZ7sSOfliBoo89PcufjWO4eAR0gznGcETyaUgo=
226-github.com/prometheus/prom2json v1.3.3/go.mod h1:Pv4yIPktEkK7btWsrUTWDDDrnpUrAELaOCj+oFwlgmc=
227+github.com/prometheus/prom2json v1.4.1 h1:7McxdrHgPEOtMwWjkKtd0v5AhpR2Q6QAnlHKVxq0+tQ=
228+github.com/prometheus/prom2json v1.4.1/go.mod h1:CzOQykSKFxXuC7ELUZHOHQvwKesQ3eN0p2PWLhFitQM=
229+github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ=
230+github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY=
231 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
232 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
233 github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
234@@ -297,12 +304,12 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
235 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
236 github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
237 github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
238-github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
239-github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
240+github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
241+github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
242 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
243 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
244-github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
245-github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
246+github.com/safchain/ethtool v0.4.1 h1:S6mEleTADqgynileXoiapt/nKnatyR6bmIHoF+h2ADo=
247+github.com/safchain/ethtool v0.4.1/go.mod h1:XLLnZmy4OCRTkksP/UiMjij96YmIsBfmBQcs7H6tA48=
248 github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
249 github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc=
250 github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs=
251@@ -327,12 +334,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
252 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
253 github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
254 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
255-github.com/tinylib/msgp v1.1.9 h1:SHf3yoO2sGA0veCJeCBYLHuttAVFHGm2RHgNodW7wQU=
256-github.com/tinylib/msgp v1.1.9/go.mod h1:BCXGB54lDD8qUEPmiG0cQQUANC4IUQyB2ItS2UDlO/k=
257+github.com/tinylib/msgp v1.2.2 h1:iHiBE1tJQwFI740SPEPkGE8cfhNfrqOYRlH450BnC/4=
258+github.com/tinylib/msgp v1.2.2/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
259 github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
260 github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
261-github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
262-github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
263+github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
264+github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
265 github.com/x-way/crawlerdetect v0.2.21 h1:LORs0nEy+MWUsC3XvKf00hXyO7drB5w/hlGB8bztXbI=
266 github.com/x-way/crawlerdetect v0.2.21/go.mod h1:DVupfue81iupuoUmFjIyDUqPqGaJhtZfYQDWoP1ZUR4=
267 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
268@@ -361,10 +368,10 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
269 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
270 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
271 golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
272-golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
273-golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
274-golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
275-golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
276+golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
277+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
278+golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw=
279+golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
280 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
281 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
282 golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
283@@ -382,8 +389,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
284 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
285 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
286 golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
287-golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
288-golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
289+golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
290+golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
291 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
292 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
293 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
294@@ -407,8 +414,9 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
295 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
296 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
297 golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
298-golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
299-golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
300+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
301+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
302+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
303 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
304 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
305 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
306@@ -416,8 +424,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
307 golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
308 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
309 golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
310-golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
311-golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
312+golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
313+golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
314 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
315 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
316 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
317@@ -425,8 +433,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
318 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
319 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
320 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
321-golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
322-golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
323+golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
324+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
325 golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
326 golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
327 golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
328@@ -435,13 +443,11 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
329 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
330 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
331 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
332-google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
333-google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
334+google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
335+google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
336 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
337 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
338 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
339-gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
340-gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
341 gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
342 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
343 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+2,
-1
1@@ -17,6 +17,7 @@ import (
2 "github.com/picosh/pico/pgs"
3 "github.com/picosh/pico/shared"
4 "github.com/picosh/pico/shared/storage"
5+ "github.com/picosh/utils"
6 )
7
8 type PostPageData struct {
9@@ -219,7 +220,7 @@ func ImgRequest(w http.ResponseWriter, r *http.Request) {
10 if ext == fext {
11 // users might add the file extension when requesting an image
12 // but we want to remove that
13- slug = shared.SanitizeFileExt(slug)
14+ slug = utils.SanitizeFileExt(slug)
15 break
16 }
17 }
+7,
-7
1@@ -14,21 +14,21 @@ import (
2 "github.com/charmbracelet/wish"
3 "github.com/google/uuid"
4 "github.com/picosh/pico/db"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/shared/storage"
7 "github.com/picosh/pico/tui/common"
8 sst "github.com/picosh/pobj/storage"
9 psub "github.com/picosh/pubsub"
10- "github.com/picosh/send/send/utils"
11+ sendutils "github.com/picosh/send/utils"
12+ "github.com/picosh/utils"
13 )
14
15 func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
16- var err error
17- key, err := shared.KeyText(s)
18- if err != nil {
19+ if s.PublicKey() == nil {
20 return nil, fmt.Errorf("key not found")
21 }
22
23+ key := utils.KeyForKeyText(s.PublicKey())
24+
25 user, err := dbpool.FindUserForKey(s.User(), key)
26 if err != nil {
27 return nil, err
28@@ -60,7 +60,7 @@ func flagCheck(cmd *flag.FlagSet, posArg string, cmdArgs []string) bool {
29
30 type Cmd struct {
31 User *db.User
32- Session shared.CmdSession
33+ Session utils.CmdSession
34 Log *slog.Logger
35 Dbpool db.DB
36 Write bool
37@@ -201,7 +201,7 @@ func WishMiddleware(handler *CliHandler) wish.Middleware {
38 return func(sesh ssh.Session) {
39 user, err := getUser(sesh, dbpool)
40 if err != nil {
41- utils.ErrorHandler(sesh, err)
42+ sendutils.ErrorHandler(sesh, err)
43 return
44 }
45
+10,
-9
1@@ -2,18 +2,19 @@ package imgs
2
3 import (
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 func NewConfigSite() *shared.ConfigSite {
9- debug := shared.GetEnv("IMGS_DEBUG", "0")
10- domain := shared.GetEnv("IMGS_DOMAIN", "prose.sh")
11- port := shared.GetEnv("IMGS_WEB_PORT", "3000")
12- protocol := shared.GetEnv("IMGS_PROTOCOL", "https")
13- storageDir := shared.GetEnv("IMGS_STORAGE_DIR", ".storage")
14- minioURL := shared.GetEnv("MINIO_URL", "")
15- minioUser := shared.GetEnv("MINIO_ROOT_USER", "")
16- minioPass := shared.GetEnv("MINIO_ROOT_PASSWORD", "")
17- dbURL := shared.GetEnv("DATABASE_URL", "")
18+ debug := utils.GetEnv("IMGS_DEBUG", "0")
19+ domain := utils.GetEnv("IMGS_DOMAIN", "prose.sh")
20+ port := utils.GetEnv("IMGS_WEB_PORT", "3000")
21+ protocol := utils.GetEnv("IMGS_PROTOCOL", "https")
22+ storageDir := utils.GetEnv("IMGS_STORAGE_DIR", ".storage")
23+ minioURL := utils.GetEnv("MINIO_URL", "")
24+ minioUser := utils.GetEnv("MINIO_ROOT_USER", "")
25+ minioPass := utils.GetEnv("MINIO_ROOT_PASSWORD", "")
26+ dbURL := utils.GetEnv("DATABASE_URL", "")
27
28 cfg := shared.ConfigSite{
29 Debug: debug == "1",
+6,
-9
1@@ -27,6 +27,7 @@ import (
2 "github.com/picosh/pico/shared/storage"
3 psub "github.com/picosh/pubsub"
4 "github.com/picosh/tunkit"
5+ "github.com/picosh/utils"
6 )
7
8 type ctxUserKey struct{}
9@@ -44,11 +45,7 @@ func setUserCtx(ctx ssh.Context, user *db.User) {
10
11 func AuthHandler(dbh db.DB, log *slog.Logger) func(ssh.Context, ssh.PublicKey) bool {
12 return func(ctx ssh.Context, key ssh.PublicKey) bool {
13- kk, err := shared.KeyForKeyText(key)
14- if err != nil {
15- log.Error("cannot get pubkey", "err", err)
16- return false
17- }
18+ kk := utils.KeyForKeyText(key)
19
20 user, err := dbh.FindUserForKey("", kk)
21 if err != nil {
22@@ -263,10 +260,10 @@ func StartSshServer() {
23 port = "2222"
24 }
25 dbUrl := os.Getenv("DATABASE_URL")
26- registryUrl := shared.GetEnv("REGISTRY_URL", "0.0.0.0:5000")
27- minioUrl := shared.GetEnv("MINIO_URL", "http://0.0.0.0:9000")
28- minioUser := shared.GetEnv("MINIO_ROOT_USER", "")
29- minioPass := shared.GetEnv("MINIO_ROOT_PASSWORD", "")
30+ registryUrl := utils.GetEnv("REGISTRY_URL", "0.0.0.0:5000")
31+ minioUrl := utils.GetEnv("MINIO_URL", "http://0.0.0.0:9000")
32+ minioUser := utils.GetEnv("MINIO_ROOT_USER", "")
33+ minioPass := utils.GetEnv("MINIO_ROOT_PASSWORD", "")
34
35 logger := shared.CreateLogger("imgs")
36 logger.Info("bootup", "registry", registryUrl, "minio", minioUrl)
+2,
-1
1@@ -12,6 +12,7 @@ import (
2 "github.com/picosh/pico/db/postgres"
3 "github.com/picosh/pico/shared"
4 "github.com/picosh/pico/shared/storage"
5+ "github.com/picosh/utils"
6 )
7
8 type PageData struct {
9@@ -123,7 +124,7 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
10 Title: post.Filename,
11 PublishAt: post.PublishAt.Format(time.DateOnly),
12 PublishAtISO: post.PublishAt.Format(time.RFC3339),
13- UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
14+ UpdatedTimeAgo: utils.TimeAgo(post.UpdatedAt),
15 UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
16 }
17 postCollection = append(postCollection, p)
+10,
-9
1@@ -2,18 +2,19 @@ package pastes
2
3 import (
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 func NewConfigSite() *shared.ConfigSite {
9- debug := shared.GetEnv("PASTES_DEBUG", "0")
10- domain := shared.GetEnv("PASTES_DOMAIN", "pastes.sh")
11- port := shared.GetEnv("PASTES_WEB_PORT", "3000")
12- dbURL := shared.GetEnv("DATABASE_URL", "")
13- protocol := shared.GetEnv("PASTES_PROTOCOL", "https")
14- storageDir := shared.GetEnv("IMGS_STORAGE_DIR", ".storage")
15- minioURL := shared.GetEnv("MINIO_URL", "")
16- minioUser := shared.GetEnv("MINIO_ROOT_USER", "")
17- minioPass := shared.GetEnv("MINIO_ROOT_PASSWORD", "")
18+ debug := utils.GetEnv("PASTES_DEBUG", "0")
19+ domain := utils.GetEnv("PASTES_DOMAIN", "pastes.sh")
20+ port := utils.GetEnv("PASTES_WEB_PORT", "3000")
21+ dbURL := utils.GetEnv("DATABASE_URL", "")
22+ protocol := utils.GetEnv("PASTES_PROTOCOL", "https")
23+ storageDir := utils.GetEnv("IMGS_STORAGE_DIR", ".storage")
24+ minioURL := utils.GetEnv("MINIO_URL", "")
25+ minioUser := utils.GetEnv("MINIO_ROOT_USER", "")
26+ minioPass := utils.GetEnv("MINIO_ROOT_PASSWORD", "")
27
28 return &shared.ConfigSite{
29 Debug: debug == "1",
+3,
-2
1@@ -11,6 +11,7 @@ import (
2 "github.com/picosh/pico/db"
3 "github.com/picosh/pico/filehandlers"
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 var DEFAULT_EXPIRES_AT = 90
9@@ -21,7 +22,7 @@ type FileHooks struct {
10 }
11
12 func (p *FileHooks) FileValidate(s ssh.Session, data *filehandlers.PostMetaData) (bool, error) {
13- if !shared.IsTextFile(string(data.Text)) {
14+ if !utils.IsTextFile(string(data.Text)) {
15 err := fmt.Errorf(
16 "WARNING: (%s) invalid file must be plain text (utf-8), skipping",
17 data.Filename,
18@@ -33,7 +34,7 @@ func (p *FileHooks) FileValidate(s ssh.Session, data *filehandlers.PostMetaData)
19 }
20
21 func (p *FileHooks) FileMeta(s ssh.Session, data *filehandlers.PostMetaData) error {
22- data.Title = shared.ToUpper(data.Slug)
23+ data.Title = utils.ToUpper(data.Slug)
24 // we want the slug to be the filename for pastes
25 data.Slug = data.Filename
26
+8,
-8
1@@ -14,16 +14,16 @@ import (
2 "github.com/picosh/pico/db/postgres"
3 "github.com/picosh/pico/filehandlers"
4 "github.com/picosh/pico/filehandlers/util"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/shared/storage"
7 wsh "github.com/picosh/pico/wish"
8+ "github.com/picosh/send/auth"
9 "github.com/picosh/send/list"
10 "github.com/picosh/send/pipe"
11+ wishrsync "github.com/picosh/send/protocols/rsync"
12+ "github.com/picosh/send/protocols/scp"
13+ "github.com/picosh/send/protocols/sftp"
14 "github.com/picosh/send/proxy"
15- "github.com/picosh/send/send/auth"
16- wishrsync "github.com/picosh/send/send/rsync"
17- "github.com/picosh/send/send/scp"
18- "github.com/picosh/send/send/sftp"
19+ "github.com/picosh/utils"
20 )
21
22 func createRouter(handler *filehandlers.FileHandlerRouter) proxy.Router {
23@@ -52,9 +52,9 @@ func withProxy(handler *filehandlers.FileHandlerRouter, otherMiddleware ...wish.
24 }
25
26 func StartSshServer() {
27- host := shared.GetEnv("PASTES_HOST", "0.0.0.0")
28- port := shared.GetEnv("PASTES_SSH_PORT", "2222")
29- promPort := shared.GetEnv("PASTES_PROM_PORT", "9222")
30+ host := utils.GetEnv("PASTES_HOST", "0.0.0.0")
31+ port := utils.GetEnv("PASTES_SSH_PORT", "2222")
32+ promPort := utils.GetEnv("PASTES_PROM_PORT", "9222")
33 cfg := NewConfigSite()
34 logger := cfg.Logger
35 dbh := postgres.NewDB(cfg.DbURL, cfg.Logger)
+1,
-1
1@@ -10,7 +10,7 @@ import (
2
3 "github.com/picosh/pico/shared"
4 "github.com/picosh/pico/shared/storage"
5- "github.com/picosh/send/send/utils"
6+ "github.com/picosh/send/utils"
7 )
8
9 type HttpReply struct {
+6,
-5
1@@ -15,6 +15,7 @@ import (
2 "github.com/picosh/pico/shared"
3 "github.com/picosh/pico/shared/storage"
4 "github.com/picosh/pico/tui/common"
5+ "github.com/picosh/utils"
6 )
7
8 func projectTable(styles common.Styles, projects []*db.Project, width int) *table.Table {
9@@ -117,7 +118,7 @@ func getHelpText(styles common.Styles, userName string, width int) string {
10
11 type Cmd struct {
12 User *db.User
13- Session shared.CmdSession
14+ Session utils.CmdSession
15 Log *slog.Logger
16 Store storage.StorageServe
17 Dbpool db.DB
18@@ -209,7 +210,7 @@ func (c *Cmd) statsByProject(projectName string) error {
19 FkID: project.ID,
20 By: "project_id",
21 Interval: "day",
22- Origin: shared.StartOfMonth(),
23+ Origin: utils.StartOfMonth(),
24 }
25
26 summary, err := c.Dbpool.VisitSummary(opts)
27@@ -237,7 +238,7 @@ func (c *Cmd) statsSites() error {
28 FkID: c.User.ID,
29 By: "user_id",
30 Interval: "day",
31- Origin: shared.StartOfMonth(),
32+ Origin: utils.StartOfMonth(),
33 }
34
35 summary, err := c.Dbpool.VisitSummary(opts)
36@@ -325,8 +326,8 @@ func (c *Cmd) stats(cfgMaxSize uint64) error {
37
38 headers := []string{"Used (GB)", "Quota (GB)", "Used (%)", "Projects (#)"}
39 data := []string{
40- fmt.Sprintf("%.4f", shared.BytesToGB(int(totalFileSize))),
41- fmt.Sprintf("%.4f", shared.BytesToGB(int(storageMax))),
42+ fmt.Sprintf("%.4f", utils.BytesToGB(int(totalFileSize))),
43+ fmt.Sprintf("%.4f", utils.BytesToGB(int(storageMax))),
44 fmt.Sprintf("%.4f", (float32(totalFileSize)/float32(storageMax))*100),
45 fmt.Sprintf("%d", len(projects)),
46 }
+12,
-11
1@@ -2,21 +2,22 @@ package pgs
2
3 import (
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8-var maxSize = uint64(25 * shared.MB)
9-var maxAssetSize = int64(10 * shared.MB)
10+var maxSize = uint64(25 * utils.MB)
11+var maxAssetSize = int64(10 * utils.MB)
12
13 func NewConfigSite() *shared.ConfigSite {
14- domain := shared.GetEnv("PGS_DOMAIN", "pgs.sh")
15- port := shared.GetEnv("PGS_WEB_PORT", "3000")
16- protocol := shared.GetEnv("PGS_PROTOCOL", "https")
17- storageDir := shared.GetEnv("PGS_STORAGE_DIR", ".storage")
18- minioURL := shared.GetEnv("MINIO_URL", "")
19- minioUser := shared.GetEnv("MINIO_ROOT_USER", "")
20- minioPass := shared.GetEnv("MINIO_ROOT_PASSWORD", "")
21- dbURL := shared.GetEnv("DATABASE_URL", "")
22- secret := shared.GetEnv("PICO_SECRET", "")
23+ domain := utils.GetEnv("PGS_DOMAIN", "pgs.sh")
24+ port := utils.GetEnv("PGS_WEB_PORT", "3000")
25+ protocol := utils.GetEnv("PGS_PROTOCOL", "https")
26+ storageDir := utils.GetEnv("PGS_STORAGE_DIR", ".storage")
27+ minioURL := utils.GetEnv("MINIO_URL", "")
28+ minioUser := utils.GetEnv("MINIO_ROOT_USER", "")
29+ minioPass := utils.GetEnv("MINIO_ROOT_PASSWORD", "")
30+ dbURL := utils.GetEnv("DATABASE_URL", "")
31+ secret := utils.GetEnv("PICO_SECRET", "")
32 if secret == "" {
33 panic("must provide PICO_SECRET environment variable")
34 }
+8,
-7
1@@ -18,14 +18,15 @@ import (
2 "github.com/picosh/pico/shared"
3 "github.com/picosh/pico/shared/storage"
4 wsh "github.com/picosh/pico/wish"
5+ "github.com/picosh/send/auth"
6 "github.com/picosh/send/list"
7 "github.com/picosh/send/pipe"
8+ wishrsync "github.com/picosh/send/protocols/rsync"
9+ "github.com/picosh/send/protocols/scp"
10+ "github.com/picosh/send/protocols/sftp"
11 "github.com/picosh/send/proxy"
12- "github.com/picosh/send/send/auth"
13- wishrsync "github.com/picosh/send/send/rsync"
14- "github.com/picosh/send/send/scp"
15- "github.com/picosh/send/send/sftp"
16 "github.com/picosh/tunkit"
17+ "github.com/picosh/utils"
18 )
19
20 func createRouter(cfg *shared.ConfigSite, handler *uploadassets.UploadAssetHandler) proxy.Router {
21@@ -55,9 +56,9 @@ func withProxy(cfg *shared.ConfigSite, handler *uploadassets.UploadAssetHandler,
22 }
23
24 func StartSshServer() {
25- host := shared.GetEnv("PGS_HOST", "0.0.0.0")
26- port := shared.GetEnv("PGS_SSH_PORT", "2222")
27- promPort := shared.GetEnv("PGS_PROM_PORT", "9222")
28+ host := utils.GetEnv("PGS_HOST", "0.0.0.0")
29+ port := utils.GetEnv("PGS_SSH_PORT", "2222")
30+ promPort := utils.GetEnv("PGS_PROM_PORT", "9222")
31 cfg := NewConfigSite()
32 logger := cfg.Logger
33 dbpool := postgres.NewDB(cfg.DbURL, cfg.Logger)
+4,
-5
1@@ -10,6 +10,7 @@ import (
2 "github.com/picosh/pico/db"
3 "github.com/picosh/pico/shared"
4 "github.com/picosh/pico/ui"
5+ "github.com/picosh/utils"
6 )
7
8 func allowPerm(proj *db.Project) bool {
9@@ -42,11 +43,9 @@ func createHttpHandler(apiConfig *shared.ApiConfig) CtxHttpBridge {
10 log.Error(err.Error(), "subdomain", subdomain)
11 return http.HandlerFunc(shared.UnauthorizedHandler)
12 }
13- pubkeyStr, err := shared.KeyForKeyText(pubkey)
14- if err != nil {
15- log.Error(err.Error())
16- return http.HandlerFunc(shared.UnauthorizedHandler)
17- }
18+
19+ pubkeyStr := utils.KeyForKeyText(pubkey)
20+
21 log = log.With(
22 "pubkey", pubkeyStr,
23 )
+6,
-6
1@@ -11,18 +11,18 @@ import (
2 bm "github.com/charmbracelet/wish/bubbletea"
3 "github.com/picosh/pico/db"
4 uploadassets "github.com/picosh/pico/filehandlers/assets"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/tui/common"
7- "github.com/picosh/send/send/utils"
8+ sendutils "github.com/picosh/send/utils"
9+ "github.com/picosh/utils"
10 )
11
12 func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
13- var err error
14- key, err := shared.KeyText(s)
15- if err != nil {
16+ if s.PublicKey() == nil {
17 return nil, fmt.Errorf("key not found")
18 }
19
20+ key := utils.KeyForKeyText(s.PublicKey())
21+
22 user, err := dbpool.FindUserForKey(s.User(), key)
23 if err != nil {
24 return nil, err
25@@ -88,7 +88,7 @@ func WishMiddleware(handler *uploadassets.UploadAssetHandler) wish.Middleware {
26
27 user, err := getUser(sesh, dbpool)
28 if err != nil {
29- utils.ErrorHandler(sesh, err)
30+ sendutils.ErrorHandler(sesh, err)
31 return
32 }
33
+16,
-6
1@@ -15,15 +15,18 @@ import (
2 "github.com/picosh/pico/tui/common"
3 "github.com/picosh/pico/tui/notifications"
4 "github.com/picosh/pico/tui/plus"
5+ "github.com/picosh/utils"
6+
7+ pipeLogger "github.com/picosh/pubsub/log"
8 )
9
10 func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
11- var err error
12- key, err := shared.KeyText(s)
13- if err != nil {
14+ if s.PublicKey() == nil {
15 return nil, fmt.Errorf("key not found")
16 }
17
18+ key := utils.KeyForKeyText(s.PublicKey())
19+
20 user, err := dbpool.FindUserForKey(s.User(), key)
21 if err != nil {
22 return nil, err
23@@ -39,7 +42,7 @@ func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
24 type Cmd struct {
25 User *db.User
26 SshSession ssh.Session
27- Session shared.CmdSession
28+ Session utils.CmdSession
29 Log *slog.Logger
30 Dbpool db.DB
31 Write bool
32@@ -67,7 +70,14 @@ func (c *Cmd) notifications() error {
33 }
34
35 func (c *Cmd) logs(ctx context.Context) error {
36- stdoutPipe, err := shared.ConnectToLogs(ctx)
37+ stdoutPipe, err := pipeLogger.ConnectToLogs(ctx, &pipeLogger.PubSubConnectionInfo{
38+ RemoteHost: utils.GetEnv("PICO_PIPE_ENDPOINT", "pipe.pico.sh:22"),
39+ KeyLocation: utils.GetEnv("PICO_PIPE_KEY", "ssh_data/term_info_ed25519"),
40+ KeyPassphrase: utils.GetEnv("PICO_PIPE_PASSPHRASE", ""),
41+ RemoteHostname: utils.GetEnv("PICO_PIPE_REMOTE_HOST", "pipe.pico.sh"),
42+ RemoteUser: utils.GetEnv("PICO_PIPE_USER", "pico"),
43+ })
44+
45 if err != nil {
46 return err
47 }
48@@ -83,7 +93,7 @@ func (c *Cmd) logs(ctx context.Context) error {
49 continue
50 }
51
52- user := shared.AnyToStr(parsedData, "user")
53+ user := utils.AnyToStr(parsedData, "user")
54 if user == c.User.Name {
55 wish.Println(c.SshSession, line)
56 }
+2,
-1
1@@ -2,10 +2,11 @@ package pico
2
3 import (
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 func NewConfigSite() *shared.ConfigSite {
9- dbURL := shared.GetEnv("DATABASE_URL", "")
10+ dbURL := utils.GetEnv("DATABASE_URL", "")
11
12 return &shared.ConfigSite{
13 DbURL: dbURL,
+12,
-17
1@@ -16,7 +16,8 @@ import (
2 "github.com/picosh/pico/db"
3 "github.com/picosh/pico/filehandlers/util"
4 "github.com/picosh/pico/shared"
5- "github.com/picosh/send/send/utils"
6+ sendutils "github.com/picosh/send/utils"
7+ "github.com/picosh/utils"
8 )
9
10 type UploadHandler struct {
11@@ -31,7 +32,7 @@ func NewUploadHandler(dbpool db.DB, cfg *shared.ConfigSite) *UploadHandler {
12 }
13 }
14
15-func (h *UploadHandler) getAuthorizedKeyFile(user *db.User) (*utils.VirtualFile, string, error) {
16+func (h *UploadHandler) getAuthorizedKeyFile(user *db.User) (*sendutils.VirtualFile, string, error) {
17 keys, err := h.DBPool.FindKeysForUser(user)
18 text := ""
19 var modTime time.Time
20@@ -42,7 +43,7 @@ func (h *UploadHandler) getAuthorizedKeyFile(user *db.User) (*utils.VirtualFile,
21 if err != nil {
22 return nil, "", err
23 }
24- fileInfo := &utils.VirtualFile{
25+ fileInfo := &sendutils.VirtualFile{
26 FName: "authorized_keys",
27 FIsDir: false,
28 FSize: int64(len(text)),
29@@ -51,11 +52,11 @@ func (h *UploadHandler) getAuthorizedKeyFile(user *db.User) (*utils.VirtualFile,
30 return fileInfo, text, nil
31 }
32
33-func (h *UploadHandler) Delete(s ssh.Session, entry *utils.FileEntry) error {
34+func (h *UploadHandler) Delete(s ssh.Session, entry *sendutils.FileEntry) error {
35 return errors.New("unsupported")
36 }
37
38-func (h *UploadHandler) Read(s ssh.Session, entry *utils.FileEntry) (os.FileInfo, utils.ReaderAtCloser, error) {
39+func (h *UploadHandler) Read(s ssh.Session, entry *sendutils.FileEntry) (os.FileInfo, sendutils.ReaderAtCloser, error) {
40 user, err := util.GetUser(s.Context())
41 if err != nil {
42 return nil, nil, err
43@@ -71,7 +72,7 @@ func (h *UploadHandler) Read(s ssh.Session, entry *utils.FileEntry) (os.FileInfo
44 if err != nil {
45 return nil, nil, err
46 }
47- reader := utils.NopReaderAtCloser(strings.NewReader(text))
48+ reader := sendutils.NopReaderAtCloser(strings.NewReader(text))
49 return fileInfo, reader, nil
50 }
51
52@@ -92,7 +93,7 @@ func (h *UploadHandler) List(s ssh.Session, fpath string, isDir bool, recursive
53 name = "/"
54 }
55
56- fileList = append(fileList, &utils.VirtualFile{
57+ fileList = append(fileList, &sendutils.VirtualFile{
58 FName: name,
59 FIsDir: true,
60 })
61@@ -121,7 +122,7 @@ func (h *UploadHandler) GetLogger() *slog.Logger {
62
63 func (h *UploadHandler) Validate(s ssh.Session) error {
64 var err error
65- key, err := utils.KeyText(s)
66+ key, err := sendutils.KeyText(s)
67 if err != nil {
68 return fmt.Errorf("key not found")
69 }
70@@ -231,10 +232,7 @@ func (h *UploadHandler) ProcessAuthorizedKeys(text []byte, logger *slog.Logger,
71 diff := authorizedKeysDiff(s.PublicKey(), curKeys, nextKeys)
72
73 for _, pk := range diff.Add {
74- key, err := shared.KeyForKeyText(pk.Pk)
75- if err != nil {
76- continue
77- }
78+ key := utils.KeyForKeyText(pk.Pk)
79
80 wish.Errorf(s, "adding pubkey (%s)\n", key)
81 logger.Info("adding pubkey", "pubkey", key)
82@@ -247,10 +245,7 @@ func (h *UploadHandler) ProcessAuthorizedKeys(text []byte, logger *slog.Logger,
83 }
84
85 for _, pk := range diff.Update {
86- key, err := shared.KeyForKeyText(pk.Pk)
87- if err != nil {
88- continue
89- }
90+ key := utils.KeyForKeyText(pk.Pk)
91
92 wish.Errorf(s, "updating pubkey with comment: %s (%s)\n", pk.Comment, key)
93 logger.Info(
94@@ -280,7 +275,7 @@ func (h *UploadHandler) ProcessAuthorizedKeys(text []byte, logger *slog.Logger,
95 return nil
96 }
97
98-func (h *UploadHandler) Write(s ssh.Session, entry *utils.FileEntry) (string, error) {
99+func (h *UploadHandler) Write(s ssh.Session, entry *sendutils.FileEntry) (string, error) {
100 logger := h.Cfg.Logger
101 user, err := util.GetUser(s.Context())
102 if err != nil {
+8,
-7
1@@ -17,13 +17,14 @@ import (
2 "github.com/picosh/pico/shared"
3 "github.com/picosh/pico/tui"
4 wsh "github.com/picosh/pico/wish"
5+ "github.com/picosh/send/auth"
6 "github.com/picosh/send/list"
7 "github.com/picosh/send/pipe"
8+ wishrsync "github.com/picosh/send/protocols/rsync"
9+ "github.com/picosh/send/protocols/scp"
10+ "github.com/picosh/send/protocols/sftp"
11 "github.com/picosh/send/proxy"
12- "github.com/picosh/send/send/auth"
13- wishrsync "github.com/picosh/send/send/rsync"
14- "github.com/picosh/send/send/scp"
15- "github.com/picosh/send/send/sftp"
16+ "github.com/picosh/utils"
17 )
18
19 func authHandler(ctx ssh.Context, key ssh.PublicKey) bool {
20@@ -58,9 +59,9 @@ func withProxy(cfg *shared.ConfigSite, handler *UploadHandler, cliHandler *CliHa
21 }
22
23 func StartSshServer() {
24- host := shared.GetEnv("PICO_HOST", "0.0.0.0")
25- port := shared.GetEnv("PICO_SSH_PORT", "2222")
26- promPort := shared.GetEnv("PICO_PROM_PORT", "9222")
27+ host := utils.GetEnv("PICO_HOST", "0.0.0.0")
28+ port := utils.GetEnv("PICO_SSH_PORT", "2222")
29+ promPort := utils.GetEnv("PICO_PROM_PORT", "9222")
30 cfg := NewConfigSite()
31 logger := cfg.Logger
32 dbpool := postgres.NewDB(cfg.DbURL, cfg.Logger)
+7,
-6
1@@ -20,6 +20,7 @@ import (
2 "github.com/picosh/pico/imgs"
3 "github.com/picosh/pico/shared"
4 "github.com/picosh/pico/shared/storage"
5+ "github.com/picosh/utils"
6 )
7
8 type PageData struct {
9@@ -260,10 +261,10 @@ func blogHandler(w http.ResponseWriter, r *http.Request) {
10 p := PostItemData{
11 URL: template.URL(cfg.FullPostURL(curl, post.Username, post.Slug)),
12 BlogURL: template.URL(cfg.FullBlogURL(curl, post.Username)),
13- Title: shared.FilenameToTitle(post.Filename, post.Title),
14+ Title: utils.FilenameToTitle(post.Filename, post.Title),
15 PublishAt: post.PublishAt.Format(time.DateOnly),
16 PublishAtISO: post.PublishAt.Format(time.RFC3339),
17- UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
18+ UpdatedTimeAgo: utils.TimeAgo(post.UpdatedAt),
19 UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
20 }
21 postCollection = append(postCollection, p)
22@@ -446,7 +447,7 @@ func postHandler(w http.ResponseWriter, r *http.Request) {
23 URL: template.URL(cfg.FullPostURL(curl, post.Username, post.Slug)),
24 BlogURL: template.URL(cfg.FullBlogURL(curl, username)),
25 Description: post.Description,
26- Title: shared.FilenameToTitle(post.Filename, post.Title),
27+ Title: utils.FilenameToTitle(post.Filename, post.Title),
28 Slug: post.Slug,
29 PublishAt: post.PublishAt.Format(time.DateOnly),
30 PublishAtISO: post.PublishAt.Format(time.RFC3339),
31@@ -595,12 +596,12 @@ func readHandler(w http.ResponseWriter, r *http.Request) {
32 item := PostItemData{
33 URL: template.URL(cfg.FullPostURL(curl, post.Username, post.Slug)),
34 BlogURL: template.URL(cfg.FullBlogURL(curl, post.Username)),
35- Title: shared.FilenameToTitle(post.Filename, post.Title),
36+ Title: utils.FilenameToTitle(post.Filename, post.Title),
37 Description: post.Description,
38 Username: post.Username,
39 PublishAt: post.PublishAt.Format(time.DateOnly),
40 PublishAtISO: post.PublishAt.Format(time.RFC3339),
41- UpdatedTimeAgo: shared.TimeAgo(post.UpdatedAt),
42+ UpdatedTimeAgo: utils.TimeAgo(post.UpdatedAt),
43 UpdatedAtISO: post.UpdatedAt.Format(time.RFC3339),
44 }
45 data.Posts = append(data.Posts, item)
46@@ -725,7 +726,7 @@ func rssBlogHandler(w http.ResponseWriter, r *http.Request) {
47
48 item := &feeds.Item{
49 Id: feedId,
50- Title: shared.FilenameToTitle(post.Filename, post.Title),
51+ Title: utils.FilenameToTitle(post.Filename, post.Title),
52 Link: &feeds.Link{Href: realUrl},
53 Content: tpl.String(),
54 Updated: *post.UpdatedAt,
+7,
-7
1@@ -10,17 +10,17 @@ import (
2 "github.com/charmbracelet/wish"
3 bm "github.com/charmbracelet/wish/bubbletea"
4 "github.com/picosh/pico/db"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/tui/common"
7+ "github.com/picosh/utils"
8 )
9
10 func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
11- var err error
12- key, err := shared.KeyText(s)
13- if err != nil {
14+ if s.PublicKey() == nil {
15 return nil, fmt.Errorf("key not found")
16 }
17
18+ key := utils.KeyForKeyText(s.PublicKey())
19+
20 user, err := dbpool.FindUserForKey(s.User(), key)
21 if err != nil {
22 return nil, err
23@@ -35,7 +35,7 @@ func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
24
25 type Cmd struct {
26 User *db.User
27- Session shared.CmdSession
28+ Session utils.CmdSession
29 Log *slog.Logger
30 Dbpool db.DB
31 Styles common.Styles
32@@ -62,7 +62,7 @@ func (c *Cmd) statsByPost(postSlug string) error {
33 FkID: post.ID,
34 By: "post_id",
35 Interval: "day",
36- Origin: shared.StartOfMonth(),
37+ Origin: utils.StartOfMonth(),
38 }
39
40 summary, err := c.Dbpool.VisitSummary(opts)
41@@ -90,7 +90,7 @@ func (c *Cmd) stats() error {
42 FkID: c.User.ID,
43 By: "user_id",
44 Interval: "day",
45- Origin: shared.StartOfMonth(),
46+ Origin: utils.StartOfMonth(),
47 Where: "AND (post_id IS NOT NULL OR (post_id IS NULL AND project_id IS NULL))",
48 }
49
+13,
-12
1@@ -2,21 +2,22 @@ package prose
2
3 import (
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 func NewConfigSite() *shared.ConfigSite {
9- debug := shared.GetEnv("PROSE_DEBUG", "0")
10- domain := shared.GetEnv("PROSE_DOMAIN", "prose.sh")
11- port := shared.GetEnv("PROSE_WEB_PORT", "3000")
12- protocol := shared.GetEnv("PROSE_PROTOCOL", "https")
13- storageDir := shared.GetEnv("IMGS_STORAGE_DIR", ".storage")
14- minioURL := shared.GetEnv("MINIO_URL", "")
15- minioUser := shared.GetEnv("MINIO_ROOT_USER", "")
16- minioPass := shared.GetEnv("MINIO_ROOT_PASSWORD", "")
17- dbURL := shared.GetEnv("DATABASE_URL", "")
18- maxSize := uint64(500 * shared.MB)
19- maxImgSize := int64(10 * shared.MB)
20- secret := shared.GetEnv("PICO_SECRET", "")
21+ debug := utils.GetEnv("PROSE_DEBUG", "0")
22+ domain := utils.GetEnv("PROSE_DOMAIN", "prose.sh")
23+ port := utils.GetEnv("PROSE_WEB_PORT", "3000")
24+ protocol := utils.GetEnv("PROSE_PROTOCOL", "https")
25+ storageDir := utils.GetEnv("IMGS_STORAGE_DIR", ".storage")
26+ minioURL := utils.GetEnv("MINIO_URL", "")
27+ minioUser := utils.GetEnv("MINIO_ROOT_USER", "")
28+ minioPass := utils.GetEnv("MINIO_ROOT_PASSWORD", "")
29+ dbURL := utils.GetEnv("DATABASE_URL", "")
30+ maxSize := uint64(500 * utils.MB)
31+ maxImgSize := int64(10 * utils.MB)
32+ secret := utils.GetEnv("PICO_SECRET", "")
33 if secret == "" {
34 panic("must provide PICO_SECRET environment variable")
35 }
+4,
-3
1@@ -10,6 +10,7 @@ import (
2 "github.com/picosh/pico/db"
3 "github.com/picosh/pico/filehandlers"
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 type MarkdownHooks struct {
9@@ -18,7 +19,7 @@ type MarkdownHooks struct {
10 }
11
12 func (p *MarkdownHooks) FileValidate(s ssh.Session, data *filehandlers.PostMetaData) (bool, error) {
13- if !shared.IsTextFile(data.Text) {
14+ if !utils.IsTextFile(data.Text) {
15 err := fmt.Errorf(
16 "WARNING: (%s) invalid file must be plain text (utf-8), skipping",
17 data.Filename,
18@@ -33,7 +34,7 @@ func (p *MarkdownHooks) FileValidate(s ssh.Session, data *filehandlers.PostMetaD
19 return true, nil
20 }
21
22- if !shared.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
23+ if !utils.IsExtAllowed(data.Filename, p.Cfg.AllowedExt) {
24 extStr := strings.Join(p.Cfg.AllowedExt, ",")
25 err := fmt.Errorf(
26 "WARNING: (%s) invalid file, format must be (%s), skipping",
27@@ -53,7 +54,7 @@ func (p *MarkdownHooks) FileMeta(s ssh.Session, data *filehandlers.PostMetaData)
28 }
29
30 if parsedText.Title == "" {
31- data.Title = shared.ToUpper(data.Slug)
32+ data.Title = utils.ToUpper(data.Slug)
33 } else {
34 data.Title = parsedText.Title
35 }
+8,
-8
1@@ -15,16 +15,16 @@ import (
2 "github.com/picosh/pico/filehandlers"
3 uploadimgs "github.com/picosh/pico/filehandlers/imgs"
4 "github.com/picosh/pico/filehandlers/util"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/shared/storage"
7 wsh "github.com/picosh/pico/wish"
8+ "github.com/picosh/send/auth"
9 "github.com/picosh/send/list"
10 "github.com/picosh/send/pipe"
11+ wishrsync "github.com/picosh/send/protocols/rsync"
12+ "github.com/picosh/send/protocols/scp"
13+ "github.com/picosh/send/protocols/sftp"
14 "github.com/picosh/send/proxy"
15- "github.com/picosh/send/send/auth"
16- wishrsync "github.com/picosh/send/send/rsync"
17- "github.com/picosh/send/send/scp"
18- "github.com/picosh/send/send/sftp"
19+ "github.com/picosh/utils"
20 )
21
22 func createRouter(handler *filehandlers.FileHandlerRouter, cliHandler *CliHandler) proxy.Router {
23@@ -54,9 +54,9 @@ func withProxy(handler *filehandlers.FileHandlerRouter, cliHandler *CliHandler,
24 }
25
26 func StartSshServer() {
27- host := shared.GetEnv("PROSE_HOST", "0.0.0.0")
28- port := shared.GetEnv("PROSE_SSH_PORT", "2222")
29- promPort := shared.GetEnv("PROSE_PROM_PORT", "9222")
30+ host := utils.GetEnv("PROSE_HOST", "0.0.0.0")
31+ port := utils.GetEnv("PROSE_SSH_PORT", "2222")
32+ promPort := utils.GetEnv("PROSE_PROM_PORT", "9222")
33 cfg := NewConfigSite()
34 logger := cfg.Logger
35 dbh := postgres.NewDB(cfg.DbURL, cfg.Logger)
+2,
-2
1@@ -17,7 +17,7 @@ import (
2 "github.com/picosh/pico/db"
3 "github.com/picosh/pico/shared"
4 psub "github.com/picosh/pubsub"
5- "github.com/picosh/send/send/utils"
6+ "github.com/picosh/send/utils"
7 )
8
9 func flagSet(cmdName string, sesh ssh.Session) *flag.FlagSet {
10@@ -48,7 +48,7 @@ func NewTabWriter(out io.Writer) *tabwriter.Writer {
11
12 func getUser(s ssh.Session, dbpool db.DB) (*db.User, error) {
13 var err error
14- key, err := shared.KeyText(s)
15+ key, err := utils.KeyText(s)
16 if err != nil {
17 return nil, fmt.Errorf("key not found")
18 }
+5,
-4
1@@ -2,13 +2,14 @@ package pubsub
2
3 import (
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 func NewConfigSite() *shared.ConfigSite {
9- domain := shared.GetEnv("PUBSUB_DOMAIN", "send.pico.sh")
10- port := shared.GetEnv("PUBSUB_WEB_PORT", "3000")
11- dbURL := shared.GetEnv("DATABASE_URL", "")
12- protocol := shared.GetEnv("PUBSUB_PROTOCOL", "https")
13+ domain := utils.GetEnv("PUBSUB_DOMAIN", "pipe.pico.sh")
14+ port := utils.GetEnv("PUBSUB_WEB_PORT", "3000")
15+ dbURL := utils.GetEnv("DATABASE_URL", "")
16+ protocol := utils.GetEnv("PUBSUB_PROTOCOL", "https")
17
18 return &shared.ConfigSite{
19 Domain: domain,
+5,
-5
1@@ -12,16 +12,16 @@ import (
2 "github.com/charmbracelet/wish"
3 "github.com/picosh/pico/db/postgres"
4 "github.com/picosh/pico/filehandlers/util"
5- "github.com/picosh/pico/shared"
6 wsh "github.com/picosh/pico/wish"
7 psub "github.com/picosh/pubsub"
8+ "github.com/picosh/utils"
9 )
10
11 func StartSshServer() {
12- host := shared.GetEnv("PUBSUB_HOST", "0.0.0.0")
13- port := shared.GetEnv("PUBSUB_SSH_PORT", "2222")
14- portOverride := shared.GetEnv("PUBSUB_SSH_PORT_OVERRIDE", port)
15- promPort := shared.GetEnv("PUBSUB_PROM_PORT", "9222")
16+ host := utils.GetEnv("PUBSUB_HOST", "0.0.0.0")
17+ port := utils.GetEnv("PUBSUB_SSH_PORT", "2222")
18+ portOverride := utils.GetEnv("PUBSUB_SSH_PORT_OVERRIDE", port)
19+ promPort := utils.GetEnv("PUBSUB_PROM_PORT", "9222")
20 cfg := NewConfigSite()
21 logger := cfg.Logger
22 dbh := postgres.NewDB(cfg.DbURL, cfg.Logger)
1@@ -10,6 +10,7 @@ import (
2
3 "github.com/charmbracelet/ssh"
4 "github.com/picosh/pico/db"
5+ "github.com/picosh/utils"
6 )
7
8 func CorsHeaders(headers http.Header) {
9@@ -43,7 +44,7 @@ type UserApi struct {
10 func NewUserApi(user *db.User, pubkey ssh.PublicKey) *UserApi {
11 return &UserApi{
12 User: user,
13- Fingerprint: KeyForSha256(pubkey),
14+ Fingerprint: utils.KeyForSha256(pubkey),
15 }
16 }
17
1@@ -6,7 +6,7 @@ import (
2 "path/filepath"
3 "strings"
4
5- "github.com/picosh/send/send/utils"
6+ "github.com/picosh/send/utils"
7 )
8
9 func GetImgsBucketName(userID string) string {
1@@ -1,37 +0,0 @@
2-package shared
3-
4-import (
5- "fmt"
6- "io"
7- "log/slog"
8- "os"
9-)
10-
11-type CmdSessionLogger struct {
12- Log *slog.Logger
13-}
14-
15-func (c *CmdSessionLogger) Write(out []byte) (int, error) {
16- c.Log.Info(string(out))
17- return 0, nil
18-}
19-
20-func (c *CmdSessionLogger) Exit(code int) error {
21- os.Exit(code)
22- return fmt.Errorf("panic %d", code)
23-}
24-
25-func (c *CmdSessionLogger) Close() error {
26- return fmt.Errorf("closing")
27-}
28-
29-func (c *CmdSessionLogger) Stderr() io.ReadWriter {
30- return nil
31-}
32-
33-type CmdSession interface {
34- Write([]byte) (int, error)
35- Exit(code int) error
36- Close() error
37- Stderr() io.ReadWriter
38-}
1@@ -11,6 +11,9 @@ import (
2 "strings"
3
4 "github.com/picosh/pico/db"
5+ "github.com/picosh/utils"
6+
7+ pipeLogger "github.com/picosh/pubsub/log"
8 )
9
10 var DefaultEmail = "hello@pico.sh"
11@@ -274,8 +277,15 @@ func CreateLogger(space string) *slog.Logger {
12
13 newLogger := log
14
15- if strings.ToLower(GetEnv("PICO_SENDLOG_ENABLED", "true")) == "true" {
16- newLog, err := SendLogRegister(log, 100)
17+ if strings.ToLower(utils.GetEnv("PICO_PIPE_ENABLED", "true")) == "true" {
18+ newLog, err := pipeLogger.SendLogRegister(log, &pipeLogger.PubSubConnectionInfo{
19+ RemoteHost: utils.GetEnv("PICO_PIPE_ENDPOINT", "pipe.pico.sh:22"),
20+ KeyLocation: utils.GetEnv("PICO_PIPE_KEY", "ssh_data/term_info_ed25519"),
21+ KeyPassphrase: utils.GetEnv("PICO_PIPE_PASSPHRASE", ""),
22+ RemoteHostname: utils.GetEnv("PICO_PIPE_REMOTE_HOST", "pipe.pico.sh"),
23+ RemoteUser: utils.GetEnv("PICO_PIPE_USER", "pico"),
24+ }, 100)
25+
26 if err == nil {
27 newLogger = newLog
28 } else {
1@@ -1,35 +0,0 @@
2-package shared
3-
4-import (
5- "errors"
6- "fmt"
7- "io"
8-)
9-
10-// Throws an error if the reader is bigger than limit.
11-var ErrSizeExceeded = errors.New("stream size exceeded")
12-
13-type MaxBytesReader struct {
14- io.Reader // reader object
15- Limit int64
16- N int64 // max bytes remaining.
17-}
18-
19-func NewMaxBytesReader(r io.Reader, limit int64) *MaxBytesReader {
20- return &MaxBytesReader{r, limit, limit}
21-}
22-
23-func (b *MaxBytesReader) Read(p []byte) (n int, err error) {
24- if b.N <= 0 {
25- err := fmt.Errorf("%w: %.2fmb", ErrSizeExceeded, BytesToMB(int(b.Limit)))
26- return 0, err
27- }
28-
29- if int64(len(p)) > b.N {
30- p = p[0:b.N]
31- }
32-
33- n, err = b.Reader.Read(p)
34- b.N -= int64(n)
35- return
36-}
1@@ -1,386 +0,0 @@
2-package shared
3-
4-import (
5- "context"
6- "errors"
7- "fmt"
8- "io"
9- "log/slog"
10- "net"
11- "os"
12- "path/filepath"
13- "slices"
14- "strings"
15- "sync"
16- "time"
17-
18- "golang.org/x/crypto/ssh"
19-)
20-
21-type MultiHandler struct {
22- Handlers []slog.Handler
23- mu sync.Mutex
24-}
25-
26-func (m *MultiHandler) Enabled(ctx context.Context, l slog.Level) bool {
27- m.mu.Lock()
28- defer m.mu.Unlock()
29-
30- for _, h := range m.Handlers {
31- if h.Enabled(ctx, l) {
32- return true
33- }
34- }
35-
36- return false
37-}
38-
39-func (m *MultiHandler) Handle(ctx context.Context, r slog.Record) error {
40- m.mu.Lock()
41- defer m.mu.Unlock()
42-
43- var errs []error
44- for _, h := range m.Handlers {
45- if h.Enabled(ctx, r.Level) {
46- errs = append(errs, h.Handle(ctx, r.Clone()))
47- }
48- }
49-
50- return errors.Join(errs...)
51-}
52-
53-func (m *MultiHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
54- m.mu.Lock()
55- defer m.mu.Unlock()
56-
57- var handlers []slog.Handler
58-
59- for _, h := range m.Handlers {
60- handlers = append(handlers, h.WithAttrs(slices.Clone(attrs)))
61- }
62-
63- return &MultiHandler{
64- Handlers: handlers,
65- }
66-}
67-
68-func (m *MultiHandler) WithGroup(name string) slog.Handler {
69- if name == "" {
70- return m
71- }
72-
73- m.mu.Lock()
74- defer m.mu.Unlock()
75-
76- var handlers []slog.Handler
77-
78- for _, h := range m.Handlers {
79- handlers = append(handlers, h.WithGroup(name))
80- }
81-
82- return &MultiHandler{
83- Handlers: handlers,
84- }
85-}
86-
87-type SendLogWriter struct {
88- SSHClient *ssh.Client
89- Session *ssh.Session
90- StdinPipe io.WriteCloser
91- Done chan struct{}
92- Messages chan []byte
93- Timeout time.Duration
94- BufferSize int
95- closeOnce sync.Once
96- closeMessageOnce sync.Once
97- startOnce sync.Once
98- connecMu sync.Mutex
99-}
100-
101-func (c *SendLogWriter) Close() error {
102- c.connecMu.Lock()
103- defer c.connecMu.Unlock()
104-
105- if c.Done != nil {
106- c.closeOnce.Do(func() {
107- close(c.Done)
108- })
109- }
110-
111- if c.Messages != nil {
112- c.closeMessageOnce.Do(func() {
113- close(c.Messages)
114- })
115- }
116-
117- var errs []error
118-
119- if c.StdinPipe != nil {
120- errs = append(errs, c.StdinPipe.Close())
121- }
122-
123- if c.Session != nil {
124- errs = append(errs, c.Session.Close())
125- }
126-
127- if c.SSHClient != nil {
128- errs = append(errs, c.SSHClient.Close())
129- }
130-
131- return errors.Join(errs...)
132-}
133-
134-func (c *SendLogWriter) Open() error {
135- c.Close()
136-
137- c.connecMu.Lock()
138-
139- c.Done = make(chan struct{})
140- c.Messages = make(chan []byte, c.BufferSize)
141-
142- sshClient, err := CreateSSHClient(
143- GetEnv("PICO_SENDLOG_ENDPOINT", "send.pico.sh:22"),
144- GetEnv("PICO_SENDLOG_KEY", "ssh_data/term_info_ed25519"),
145- GetEnv("PICO_SENDLOG_PASSPHRASE", ""),
146- GetEnv("PICO_SENDLOG_REMOTE_HOST", "send.pico.sh"),
147- GetEnv("PICO_SENDLOG_USER", "pico"),
148- )
149- if err != nil {
150- c.connecMu.Unlock()
151- return err
152- }
153-
154- session, err := sshClient.NewSession()
155- if err != nil {
156- c.connecMu.Unlock()
157- return err
158- }
159-
160- stdinPipe, err := session.StdinPipe()
161- if err != nil {
162- c.connecMu.Unlock()
163- return err
164- }
165-
166- err = session.Start("pub log-drain -b=false")
167- if err != nil {
168- c.connecMu.Unlock()
169- return err
170- }
171-
172- c.SSHClient = sshClient
173- c.Session = session
174- c.StdinPipe = stdinPipe
175-
176- c.closeOnce = sync.Once{}
177- c.startOnce = sync.Once{}
178-
179- c.connecMu.Unlock()
180-
181- c.Start()
182-
183- return nil
184-}
185-
186-func (c *SendLogWriter) Start() {
187- c.startOnce.Do(func() {
188- go func() {
189- defer c.Reconnect()
190-
191- for {
192- select {
193- case data, ok := <-c.Messages:
194- _, err := c.StdinPipe.Write(data)
195- if !ok || err != nil {
196- slog.Error("received error on write, reopening logger", "error", err)
197- return
198- }
199- case <-c.Done:
200- return
201- }
202- }
203- }()
204- })
205-}
206-
207-func (c *SendLogWriter) Write(data []byte) (int, error) {
208- var (
209- n int
210- err error
211- )
212-
213- ok := c.connecMu.TryLock()
214-
215- if !ok {
216- return n, fmt.Errorf("unable to acquire lock to write")
217- }
218-
219- defer c.connecMu.Unlock()
220-
221- if c.Messages == nil || c.Done == nil {
222- return n, fmt.Errorf("logger not viable")
223- }
224-
225- select {
226- case c.Messages <- slices.Clone(data):
227- n = len(data)
228- case <-time.After(c.Timeout):
229- err = fmt.Errorf("unable to send data within timeout")
230- case <-c.Done:
231- break
232- }
233-
234- return n, err
235-}
236-
237-func (c *SendLogWriter) Reconnect() {
238- go func() {
239- for {
240- err := c.Open()
241- if err != nil {
242- slog.Error("unable to open send logger. retrying in 10 seconds", "error", err)
243- } else {
244- return
245- }
246-
247- <-time.After(10 * time.Second)
248- }
249- }()
250-}
251-
252-func CreateSSHClient(remoteHost string, keyLocation string, keyPassphrase string, remoteHostname string, remoteUser string) (*ssh.Client, error) {
253- if !strings.Contains(remoteHost, ":") {
254- remoteHost += ":22"
255- }
256-
257- rawConn, err := net.Dial("tcp", remoteHost)
258- if err != nil {
259- return nil, err
260- }
261-
262- keyPath, err := filepath.Abs(keyLocation)
263- if err != nil {
264- return nil, err
265- }
266-
267- f, err := os.Open(keyPath)
268- if err != nil {
269- return nil, err
270- }
271- defer f.Close()
272-
273- data, err := io.ReadAll(f)
274- if err != nil {
275- return nil, err
276- }
277-
278- var signer ssh.Signer
279-
280- if keyPassphrase != "" {
281- signer, err = ssh.ParsePrivateKeyWithPassphrase(data, []byte(keyPassphrase))
282- } else {
283- signer, err = ssh.ParsePrivateKey(data)
284- }
285-
286- if err != nil {
287- return nil, err
288- }
289-
290- sshConn, chans, reqs, err := ssh.NewClientConn(rawConn, remoteHostname, &ssh.ClientConfig{
291- Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
292- HostKeyCallback: ssh.InsecureIgnoreHostKey(),
293- User: remoteUser,
294- })
295-
296- if err != nil {
297- return nil, err
298- }
299-
300- sshClient := ssh.NewClient(sshConn, chans, reqs)
301-
302- return sshClient, nil
303-}
304-
305-func SendLogRegister(logger *slog.Logger, buffer int) (*slog.Logger, error) {
306- if buffer < 0 {
307- buffer = 0
308- }
309-
310- currentHandler := logger.Handler()
311-
312- logWriter := &SendLogWriter{
313- Timeout: 10 * time.Millisecond,
314- BufferSize: buffer,
315- }
316-
317- logWriter.Reconnect()
318-
319- return slog.New(
320- &MultiHandler{
321- Handlers: []slog.Handler{
322- currentHandler,
323- slog.NewJSONHandler(logWriter, &slog.HandlerOptions{
324- AddSource: true,
325- Level: slog.LevelDebug,
326- }),
327- },
328- },
329- ), nil
330-}
331-
332-var _ io.Writer = (*SendLogWriter)(nil)
333-var _ slog.Handler = (*MultiHandler)(nil)
334-
335-func AnyToStr(mp map[string]any, key string) string {
336- if value, ok := mp[key]; ok {
337- if value, ok := value.(string); ok {
338- return value
339- }
340- }
341- return ""
342-}
343-
344-func AnyToFloat(mp map[string]any, key string) float64 {
345- if value, ok := mp[key]; ok {
346- if value, ok := value.(float64); ok {
347- return value
348- }
349- }
350- return 0
351-}
352-
353-func ConnectToLogs(ctx context.Context) (io.Reader, error) {
354- sshClient, err := CreateSSHClient(
355- GetEnv("PICO_SENDLOG_ENDPOINT", "send.pico.sh:22"),
356- GetEnv("PICO_SENDLOG_KEY", "ssh_data/term_info_ed25519"),
357- GetEnv("PICO_SENDLOG_PASSPHRASE", ""),
358- GetEnv("PICO_SENDLOG_REMOTE_HOST", "send.pico.sh"),
359- GetEnv("PICO_SENDLOG_USER", "pico"),
360- )
361- if err != nil {
362- return nil, err
363- }
364-
365- session, err := sshClient.NewSession()
366- if err != nil {
367- return nil, err
368- }
369-
370- stdoutPipe, err := session.StdoutPipe()
371- if err != nil {
372- return nil, err
373- }
374-
375- err = session.Start("sub log-drain -k")
376- if err != nil {
377- return nil, err
378- }
379-
380- go func() {
381- <-ctx.Done()
382- session.Close()
383- sshClient.Close()
384- }()
385-
386- return stdoutPipe, nil
387-}
1@@ -1,169 +0,0 @@
2-package shared
3-
4-import (
5- "crypto/sha256"
6- "encoding/base64"
7- "encoding/hex"
8- "fmt"
9- "math"
10- "os"
11- pathpkg "path"
12- "path/filepath"
13- "regexp"
14- "strings"
15- "time"
16- "unicode"
17- "unicode/utf8"
18-
19- "slices"
20-
21- "github.com/charmbracelet/ssh"
22- gossh "golang.org/x/crypto/ssh"
23-)
24-
25-var fnameRe = regexp.MustCompile(`[-_]+`)
26-var subdomainRe = regexp.MustCompile(`^[a-z0-9-]+$`)
27-
28-var KB = 1000
29-var MB = KB * 1000
30-
31-func IsValidSubdomain(subd string) bool {
32- return subdomainRe.MatchString(subd)
33-}
34-
35-func FilenameToTitle(filename string, title string) string {
36- if filename != title {
37- return title
38- }
39-
40- return ToUpper(title)
41-}
42-
43-func ToUpper(str string) string {
44- pre := fnameRe.ReplaceAllString(str, " ")
45-
46- r := []rune(pre)
47- if len(r) > 0 {
48- r[0] = unicode.ToUpper(r[0])
49- }
50-
51- return string(r)
52-}
53-
54-func SanitizeFileExt(fname string) string {
55- return strings.TrimSuffix(fname, filepath.Ext(fname))
56-}
57-
58-func KeyText(s ssh.Session) (string, error) {
59- if s.PublicKey() == nil {
60- return "", fmt.Errorf("Session doesn't have public key")
61- }
62- return KeyForKeyText(s.PublicKey())
63-}
64-
65-func KeyForKeyText(pk ssh.PublicKey) (string, error) {
66- kb := base64.StdEncoding.EncodeToString(pk.Marshal())
67- return fmt.Sprintf("%s %s", pk.Type(), kb), nil
68-}
69-
70-func KeyForSha256(pk ssh.PublicKey) string {
71- return gossh.FingerprintSHA256(pk)
72-}
73-
74-func GetEnv(key string, defaultVal string) string {
75- if value, exists := os.LookupEnv(key); exists {
76- return value
77- }
78-
79- return defaultVal
80-}
81-
82-// IsText reports whether a significant prefix of s looks like correct UTF-8;
83-// that is, if it is likely that s is human-readable text.
84-func IsText(s string) bool {
85- const max = 1024 // at least utf8.UTFMax
86- if len(s) > max {
87- s = s[0:max]
88- }
89- for i, c := range s {
90- if i+utf8.UTFMax > len(s) {
91- // last char may be incomplete - ignore
92- break
93- }
94- if c == 0xFFFD || c < ' ' && c != '\n' && c != '\t' && c != '\f' && c != '\r' {
95- // decoding error or control character - not a text file
96- return false
97- }
98- }
99- return true
100-}
101-
102-func IsExtAllowed(filename string, allowedExt []string) bool {
103- ext := pathpkg.Ext(filename)
104- return slices.Contains(allowedExt, ext)
105-}
106-
107-// IsTextFile reports whether the file has a known extension indicating
108-// a text file, or if a significant chunk of the specified file looks like
109-// correct UTF-8; that is, if it is likely that the file contains human-
110-// readable text.
111-func IsTextFile(text string) bool {
112- num := math.Min(float64(len(text)), 1024)
113- return IsText(text[0:int(num)])
114-}
115-
116-const solarYearSecs = 31556926
117-
118-func TimeAgo(t *time.Time) string {
119- d := time.Since(*t)
120- var metric string
121- var amount int
122- if d.Seconds() < 60 {
123- amount = int(d.Seconds())
124- metric = "second"
125- } else if d.Minutes() < 60 {
126- amount = int(d.Minutes())
127- metric = "minute"
128- } else if d.Hours() < 24 {
129- amount = int(d.Hours())
130- metric = "hour"
131- } else if d.Seconds() < solarYearSecs {
132- amount = int(d.Hours()) / 24
133- metric = "day"
134- } else {
135- amount = int(d.Seconds()) / solarYearSecs
136- metric = "year"
137- }
138- if amount == 1 {
139- return fmt.Sprintf("%d %s ago", amount, metric)
140- } else {
141- return fmt.Sprintf("%d %ss ago", amount, metric)
142- }
143-}
144-
145-func Shasum(data []byte) string {
146- h := sha256.New()
147- h.Write(data)
148- bs := h.Sum(nil)
149- return hex.EncodeToString(bs)
150-}
151-
152-func BytesToMB(size int) float32 {
153- return ((float32(size) / 1000) / 1000)
154-}
155-
156-func BytesToGB(size int) float32 {
157- return BytesToMB(size) / 1000
158-}
159-
160-// https://stackoverflow.com/a/46964105
161-func StartOfMonth() time.Time {
162- now := time.Now()
163- firstday := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC)
164- return firstday
165-}
166-
167-func StartOfYear() time.Time {
168- now := time.Now()
169- return now.AddDate(-1, 0, 0)
170-}
+3,
-6
1@@ -8,8 +8,8 @@ import (
2 input "github.com/charmbracelet/bubbles/textinput"
3 tea "github.com/charmbracelet/bubbletea"
4 "github.com/picosh/pico/db"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/tui/common"
7+ "github.com/picosh/utils"
8 )
9
10 type state int
11@@ -200,7 +200,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
12 // View renders current view from the model.
13 func (m Model) View() string {
14 s := common.LogoView() + "\n\n"
15- pubkey := fmt.Sprintf("pubkey: %s", shared.KeyForSha256(m.shared.Session.PublicKey()))
16+ pubkey := fmt.Sprintf("pubkey: %s", utils.KeyForSha256(m.shared.Session.PublicKey()))
17 s += m.shared.Styles.Label.SetString(pubkey).String()
18 s += "\n\n" + m.input.View() + "\n\n"
19
20@@ -228,10 +228,7 @@ func (m *Model) createAccount() tea.Cmd {
21 return NameInvalidMsg{}
22 }
23
24- key, err := shared.KeyForKeyText(m.shared.Session.PublicKey())
25- if err != nil {
26- return errMsg{err}
27- }
28+ key := utils.KeyForKeyText(m.shared.Session.PublicKey())
29
30 user, err := m.shared.Dbpool.RegisterUser(m.newName, key, "")
31 if err != nil {
+3,
-5
1@@ -7,9 +7,9 @@ import (
2 input "github.com/charmbracelet/bubbles/textinput"
3 tea "github.com/charmbracelet/bubbletea"
4 "github.com/picosh/pico/db"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/tui/common"
7 "github.com/picosh/pico/tui/pages"
8+ "github.com/picosh/utils"
9 "golang.org/x/crypto/ssh"
10 )
11
12@@ -239,10 +239,8 @@ func (m *Model) addPublicKey() tea.Cmd {
13 return KeyInvalidMsg{}
14 }
15
16- key, err := shared.KeyForKeyText(pk)
17- if err != nil {
18- return KeyInvalidMsg{}
19- }
20+ key := utils.KeyForKeyText(pk)
21+
22 err = m.shared.Dbpool.InsertPublicKey(m.shared.User.ID, key, comment, nil)
23 if err != nil {
24 if errors.Is(err, db.ErrPublicKeyTaken) {
+19,
-10
1@@ -11,9 +11,11 @@ import (
2 "github.com/charmbracelet/bubbles/viewport"
3 tea "github.com/charmbracelet/bubbletea"
4 "github.com/charmbracelet/lipgloss"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/tui/common"
7 "github.com/picosh/pico/tui/pages"
8+ "github.com/picosh/utils"
9+
10+ pipeLogger "github.com/picosh/pubsub/log"
11 )
12
13 type state int
14@@ -168,7 +170,14 @@ func (m Model) waitForActivity(sub chan map[string]any) tea.Cmd {
15
16 func (m Model) connectLogs(sub chan map[string]any) tea.Cmd {
17 return func() tea.Msg {
18- stdoutPipe, err := shared.ConnectToLogs(m.ctx)
19+ stdoutPipe, err := pipeLogger.ConnectToLogs(m.ctx, &pipeLogger.PubSubConnectionInfo{
20+ RemoteHost: utils.GetEnv("PICO_PIPE_ENDPOINT", "pipe.pico.sh:22"),
21+ KeyLocation: utils.GetEnv("PICO_PIPE_KEY", "ssh_data/term_info_ed25519"),
22+ KeyPassphrase: utils.GetEnv("PICO_PIPE_PASSPHRASE", ""),
23+ RemoteHostname: utils.GetEnv("PICO_PIPE_REMOTE_HOST", "pipe.pico.sh"),
24+ RemoteUser: utils.GetEnv("PICO_PIPE_USER", "pico"),
25+ })
26+
27 if err != nil {
28 return errMsg(err)
29 }
30@@ -184,7 +193,7 @@ func (m Model) connectLogs(sub chan map[string]any) tea.Cmd {
31 continue
32 }
33
34- user := shared.AnyToStr(parsedData, "user")
35+ user := utils.AnyToStr(parsedData, "user")
36 if user == m.shared.User.Name {
37 sub <- parsedData
38 }
39@@ -201,13 +210,13 @@ func matched(str, match string) bool {
40 }
41
42 func logToStr(styles common.Styles, data map[string]any, match string) string {
43- time := shared.AnyToStr(data, "time")
44- service := shared.AnyToStr(data, "service")
45- level := shared.AnyToStr(data, "level")
46- msg := shared.AnyToStr(data, "msg")
47- errMsg := shared.AnyToStr(data, "err")
48- status := shared.AnyToFloat(data, "status")
49- url := shared.AnyToStr(data, "url")
50+ time := utils.AnyToStr(data, "time")
51+ service := utils.AnyToStr(data, "service")
52+ level := utils.AnyToStr(data, "level")
53+ msg := utils.AnyToStr(data, "msg")
54+ errMsg := utils.AnyToStr(data, "err")
55+ status := utils.AnyToFloat(data, "status")
56+ url := utils.AnyToStr(data, "url")
57
58 if match != "" {
59 lvlMatch := matched(level, match)
+2,
-2
1@@ -8,9 +8,9 @@ import (
2 "github.com/charmbracelet/lipgloss"
3 "github.com/charmbracelet/lipgloss/table"
4 "github.com/picosh/pico/db"
5- "github.com/picosh/pico/shared"
6 "github.com/picosh/pico/tui/common"
7 "github.com/picosh/pico/tui/pages"
8+ "github.com/picosh/utils"
9 )
10
11 var maxWidth = 50
12@@ -125,7 +125,7 @@ func (m Model) featuresView() string {
13
14 data := [][]string{}
15 for _, feature := range m.features {
16- storeMax := shared.BytesToGB(int(feature.FindStorageMax(0)))
17+ storeMax := utils.BytesToGB(int(feature.FindStorageMax(0)))
18 row := []string{
19 feature.Name,
20 fmt.Sprintf("%.2f", storeMax),
+7,
-5
1@@ -2,10 +2,11 @@ package tui
2
3 import (
4 "errors"
5+ "fmt"
6
7 "github.com/picosh/pico/db"
8- "github.com/picosh/pico/shared"
9 "github.com/picosh/pico/tui/common"
10+ "github.com/picosh/utils"
11 )
12
13 func findUser(shrd common.SharedModel) (*db.User, error) {
14@@ -13,12 +14,13 @@ func findUser(shrd common.SharedModel) (*db.User, error) {
15 var user *db.User
16 usr := shrd.Session.User()
17
18- key, err := shared.KeyForKeyText(shrd.Session.PublicKey())
19- if err != nil {
20- return nil, err
21+ if shrd.Session.PublicKey() == nil {
22+ return nil, fmt.Errorf("unable to find public key")
23 }
24
25- user, err = shrd.Dbpool.FindUserForKey(usr, key)
26+ key := utils.KeyForKeyText(shrd.Session.PublicKey())
27+
28+ user, err := shrd.Dbpool.FindUserForKey(usr, key)
29 if err != nil {
30 logger.Error("no user found for public key", "err", err.Error())
31 // we only want to throw an error for specific cases
+5,
-10
1@@ -12,6 +12,7 @@ import (
2 "github.com/charmbracelet/ssh"
3 "github.com/picosh/pico/db"
4 "github.com/picosh/pico/shared"
5+ "github.com/picosh/utils"
6 )
7
8 type registerPayload struct {
9@@ -35,13 +36,7 @@ func registerUser(apiConfig *shared.ApiConfig, ctx ssh.Context, pubkey ssh.Publi
10 body, _ := io.ReadAll(r.Body)
11 _ = json.Unmarshal(body, &payload)
12
13- pubkeyStr, err := shared.KeyForKeyText(pubkey)
14- if err != nil {
15- errMsg := fmt.Sprintf("could not get pubkey text: %s", err.Error())
16- logger.Error("could not get pubkey text", "err", err.Error())
17- shared.JSONError(w, errMsg, http.StatusUnprocessableEntity)
18- return
19- }
20+ pubkeyStr := utils.KeyForKeyText(pubkey)
21
22 user, err := dbpool.RegisterUser(payload.Name, pubkeyStr, "")
23 if err != nil {
24@@ -130,7 +125,7 @@ func toFingerprint(pubkey string) (string, error) {
25 if err != nil {
26 return "", err
27 }
28- return shared.KeyForSha256(kk), nil
29+ return utils.KeyForSha256(kk), nil
30 }
31
32 func getPublicKeys(httpCtx *shared.ApiConfig, ctx ssh.Context) http.HandlerFunc {
33@@ -636,8 +631,8 @@ func getAnalytics(apiConfig *shared.ApiConfig, ctx ssh.Context, sumtype, bytype,
34 by = "post_id"
35 }
36
37- year := &db.SummaryOpts{FkID: fkID, By: by, Interval: "month", Origin: shared.StartOfYear(), Where: where}
38- month := &db.SummaryOpts{FkID: fkID, By: by, Interval: "day", Origin: shared.StartOfMonth(), Where: where}
39+ year := &db.SummaryOpts{FkID: fkID, By: by, Interval: "month", Origin: utils.StartOfYear(), Where: where}
40+ month := &db.SummaryOpts{FkID: fkID, By: by, Interval: "day", Origin: utils.StartOfMonth(), Where: where}
41
42 opts := year
43 if sumtype == "month" {