A tool for backing up ATProto related data to S3
at main 120 lines 2.3 kB view raw
1package main 2 3import ( 4 "context" 5 "log/slog" 6 "net/http" 7 "os" 8 "os/signal" 9 "time" 10 11 "github.com/bugsnag/bugsnag-go/v2" 12 "github.com/joho/godotenv" 13 "github.com/minio/minio-go/v7" 14 "github.com/minio/minio-go/v7/pkg/credentials" 15 "github.com/robfig/cron" 16) 17 18type service struct { 19 pdsHost string 20 did string 21 blobDir string 22 bucketName string 23 httpClient *http.Client 24 minioClient *minio.Client 25} 26 27func main() { 28 ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill) 29 defer stop() 30 31 envLocation := os.Getenv("ENV_LOCATION") 32 if envLocation == "" { 33 envLocation = ".env" 34 } 35 36 err := godotenv.Load(envLocation) 37 if err != nil { 38 if !os.IsNotExist(err) { 39 slog.Error("load env", "error", err) 40 return 41 } 42 } 43 44 configureBugsnag() 45 46 minioClient, err := createMinioClient() 47 if err != nil { 48 slog.Error("create minio client", "error", err) 49 bugsnag.Notify(err) 50 return 51 } 52 53 bucketName := os.Getenv("S3_BUCKET_NAME") 54 55 err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{}) 56 if err != nil { 57 slog.Error("create bucket", "error", err) 58 bugsnag.Notify(err) 59 return 60 } 61 62 pdsHost := os.Getenv("PDS_HOST") 63 did := os.Getenv("DID") 64 blobDir := os.Getenv("BLOB_DIR") 65 66 service := service{ 67 pdsHost: pdsHost, 68 did: did, 69 blobDir: blobDir, 70 bucketName: bucketName, 71 minioClient: minioClient, 72 httpClient: &http.Client{ 73 Timeout: time.Second * 5, 74 Transport: &http.Transport{ 75 IdleConnTimeout: time.Second * 90, 76 }, 77 }, 78 } 79 80 service.backupPDS(ctx) 81 service.backupTangledKnot(ctx) 82 83 c := cron.New() 84 85 c.AddFunc("@hourly", func() { 86 service.backupPDS(ctx) 87 }) 88 c.AddFunc("@hourly", func() { 89 service.backupTangledKnot(ctx) 90 }) 91 92 c.Start() 93 defer c.Stop() 94 95 <-ctx.Done() 96} 97 98func createMinioClient() (*minio.Client, error) { 99 endpoint := os.Getenv("S3_ENDPOINT") 100 accessKeyID := os.Getenv("S3_ACCESS_ID") 101 secretAccessKey := os.Getenv("S3_SECRET_ACCESS_KEY") 102 useSSL := true 103 104 return minio.New(endpoint, &minio.Options{ 105 Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), 106 Secure: useSSL, 107 }) 108} 109 110func configureBugsnag() { 111 apiKey := os.Getenv("BUGSNAG_API_KEY") 112 if apiKey == "" { 113 slog.Info("bugsnag not configured") 114 return 115 } 116 bugsnag.Configure(bugsnag.Configuration{ 117 APIKey: apiKey, 118 ReleaseStage: "production", 119 }) 120}