An implementation of the ATProto statusphere example app but in Go

Gitignore kicking my ass

+113
+113
cmd/main.go
··· 1 + package main 2 + 3 + import ( 4 + "context" 5 + "errors" 6 + "log" 7 + "log/slog" 8 + "net/http" 9 + "os" 10 + "os/signal" 11 + "path" 12 + "syscall" 13 + "time" 14 + 15 + "github.com/avast/retry-go/v4" 16 + "github.com/joho/godotenv" 17 + "github.com/willdot/statusphere-go" 18 + "github.com/willdot/statusphere-go/database" 19 + "github.com/willdot/statusphere-go/oauth" 20 + ) 21 + 22 + const ( 23 + defaultServerAddr = "wss://jetstream.atproto.tools/subscribe" 24 + httpClientTimeoutDuration = time.Second * 5 25 + transportIdleConnTimeoutDuration = time.Second * 90 26 + ) 27 + 28 + func main() { 29 + err := godotenv.Load(".env") 30 + if err != nil { 31 + if !os.IsNotExist(err) { 32 + log.Fatal("Error loading .env file") 33 + } 34 + } 35 + 36 + host := os.Getenv("HOST") 37 + if host == "" { 38 + slog.Error("missing HOST env variable") 39 + return 40 + } 41 + 42 + dbMountPath := os.Getenv("DATABASE_MOUNT_PATH") 43 + if dbMountPath == "" { 44 + slog.Error("DATABASE_MOUNT_PATH env not set") 45 + return 46 + } 47 + 48 + dbFilename := path.Join(dbMountPath, "database.db") 49 + db, err := database.New(dbFilename) 50 + if err != nil { 51 + slog.Error("create new database", "error", err) 52 + return 53 + } 54 + defer db.Close() 55 + 56 + httpClient := &http.Client{ 57 + Timeout: httpClientTimeoutDuration, 58 + Transport: &http.Transport{ 59 + IdleConnTimeout: transportIdleConnTimeoutDuration, 60 + }, 61 + } 62 + 63 + oauthService, err := oauth.NewService(db, host, httpClient) 64 + if err != nil { 65 + slog.Error("creating new oauth service", "error", err) 66 + return 67 + } 68 + 69 + server, err := statusphere.NewServer(host, 8080, db, oauthService, httpClient) 70 + if err != nil { 71 + slog.Error("create new server", "error", err) 72 + return 73 + } 74 + 75 + signals := make(chan os.Signal, 1) 76 + signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) 77 + 78 + ctx, cancel := context.WithCancel(context.Background()) 79 + defer cancel() 80 + 81 + go func() { 82 + <-signals 83 + cancel() 84 + _ = server.Stop(context.Background()) 85 + }() 86 + 87 + go consumeLoop(ctx, db) 88 + 89 + server.Run() 90 + } 91 + 92 + func consumeLoop(ctx context.Context, db *database.DB) { 93 + jsServerAddr := os.Getenv("JS_SERVER_ADDR") 94 + if jsServerAddr == "" { 95 + jsServerAddr = defaultServerAddr 96 + } 97 + 98 + consumer := statusphere.NewConsumer(jsServerAddr, slog.Default(), db) 99 + 100 + err := retry.Do(func() error { 101 + err := consumer.Consume(ctx) 102 + if err != nil { 103 + if errors.Is(err, context.Canceled) { 104 + return nil 105 + } 106 + slog.Error("consume loop", "error", err) 107 + return err 108 + } 109 + return nil 110 + }, retry.UntilSucceeded()) // retry indefinitly until context canceled 111 + slog.Error(err.Error()) 112 + slog.Warn("exiting consume loop") 113 + }