Commit 3004275
Eric Bower
·
2026-05-10 20:54:24 -0400 EDT
parent d634672
chore: script to find orphaned buckets
1 files changed,
+114,
-0
+114,
-0
1@@ -0,0 +1,114 @@
2+package main
3+
4+import (
5+ "flag"
6+ "fmt"
7+ "log/slog"
8+ "os"
9+ "strings"
10+
11+ "github.com/picosh/pico/pkg/db/postgres"
12+ "github.com/picosh/pico/pkg/shared"
13+ "github.com/picosh/pico/pkg/storage"
14+)
15+
16+func main() {
17+ deleteFlag := flag.Bool("delete", false, "delete orphaned bucket folders")
18+ flag.Parse()
19+
20+ logger := slog.Default()
21+
22+ dbURL := shared.GetEnv("DATABASE_URL", "")
23+ if dbURL == "" {
24+ logger.Error("DATABASE_URL is required")
25+ os.Exit(1)
26+ }
27+
28+ dbpool := postgres.NewDB(dbURL, logger)
29+ defer func() { _ = dbpool.Close() }()
30+
31+ st, err := storage.NewStorage(logger, "fs")
32+ if err != nil {
33+ logger.Error("failed to create storage", "err", err)
34+ os.Exit(1)
35+ }
36+
37+ // Collect all valid user IDs from the database
38+ logger.Info("fetching all users from database")
39+ users, err := dbpool.FindUsers()
40+ if err != nil {
41+ logger.Error("failed to fetch users", "err", err)
42+ os.Exit(1)
43+ }
44+
45+ validUserIDs := make(map[string]struct{}, len(users))
46+ for _, user := range users {
47+ validUserIDs[user.ID] = struct{}{}
48+ logger.Info("found user", "id", user.ID, "name", user.Name)
49+ }
50+ logger.Info("total users", "count", len(validUserIDs))
51+
52+ // List all buckets in the storage directory
53+ logger.Info("listing all buckets in storage")
54+ buckets, err := st.ListBuckets()
55+ if err != nil {
56+ logger.Error("failed to list buckets", "err", err)
57+ os.Exit(1)
58+ }
59+ logger.Info("total buckets", "count", len(buckets))
60+
61+ // Find orphaned buckets (no associated user)
62+ orphaned := []string{}
63+ for _, bucket := range buckets {
64+ // Buckets are either the user ID directly (e.g., images)
65+ // or prefixed with "static-" (e.g., static-{userID} for assets)
66+ userID := bucket
67+ if strings.HasPrefix(bucket, "static-") {
68+ userID = strings.TrimPrefix(bucket, "static-")
69+ }
70+
71+ if _, exists := validUserIDs[userID]; !exists {
72+ orphaned = append(orphaned, bucket)
73+ }
74+ }
75+
76+ if len(orphaned) == 0 {
77+ logger.Info("no orphaned buckets found")
78+ return
79+ }
80+
81+ logger.Info("orphaned buckets found", "count", len(orphaned))
82+ for _, bucket := range orphaned {
83+ fmt.Printf(" - %s\n", bucket)
84+ }
85+
86+ if !*deleteFlag {
87+ fmt.Printf("\nFound %d orphaned bucket(s). Use --delete to remove them.\n", len(orphaned))
88+ return
89+ }
90+
91+ // Delete orphaned buckets
92+ logger.Info("deleting orphaned buckets")
93+ deleted := 0
94+ failed := 0
95+ for _, bucket := range orphaned {
96+ b, err := st.GetBucket(bucket)
97+ if err != nil {
98+ logger.Error("failed to get bucket", "bucket", bucket, "err", err)
99+ failed++
100+ continue
101+ }
102+
103+ err = st.DeleteBucket(b)
104+ if err != nil {
105+ logger.Error("failed to delete bucket", "bucket", bucket, "err", err)
106+ failed++
107+ continue
108+ }
109+
110+ logger.Info("deleted bucket", "bucket", bucket)
111+ deleted++
112+ }
113+
114+ fmt.Printf("\nDeleted %d bucket(s), %d failed\n", deleted, failed)
115+}