Live video on the AT Protocol
79
fork

Configure Feed

Select the types of activity you want to include in your feed.

at eli/fix-context-recursion 104 lines 3.0 kB view raw
1package spxrpc 2 3import ( 4 "context" 5 "net/http" 6 "time" 7 8 "github.com/labstack/echo/v4" 9 "github.com/patrickmn/go-cache" 10 "github.com/slok/go-http-metrics/middleware" 11 echomiddleware "github.com/slok/go-http-metrics/middleware/echo" 12 "github.com/streamplace/oatproxy/pkg/oatproxy" 13 "stream.place/streamplace/pkg/atproto" 14 "stream.place/streamplace/pkg/config" 15 "stream.place/streamplace/pkg/log" 16 "stream.place/streamplace/pkg/model" 17 "stream.place/streamplace/pkg/statedb" 18) 19 20type Server struct { 21 e *echo.Echo 22 cli *config.CLI 23 model model.Model 24 OGImageCache *cache.Cache 25 ATSync *atproto.ATProtoSynchronizer 26 statefulDB *statedb.StatefulDB 27} 28 29func NewServer(ctx context.Context, cli *config.CLI, model model.Model, statefulDB *statedb.StatefulDB, op *oatproxy.OATProxy, mdlw middleware.Middleware, atsync *atproto.ATProtoSynchronizer) (*Server, error) { 30 e := echo.New() 31 s := &Server{ 32 e: e, 33 cli: cli, 34 model: model, 35 OGImageCache: cache.New(5*time.Minute, 10*time.Minute), // 5min TTL, 10min cleanup 36 ATSync: atsync, 37 statefulDB: statefulDB, 38 } 39 e.Use(s.ErrorHandlingMiddleware()) 40 e.Use(s.ContextPreservingMiddleware()) 41 e.Use(echomiddleware.Handler("", mdlw)) 42 e.Use(op.OAuthMiddleware) 43 err := s.RegisterHandlersPlaceStream(e) 44 if err != nil { 45 return nil, err 46 } 47 err = s.RegisterHandlersAppBsky(e) 48 if err != nil { 49 return nil, err 50 } 51 err = s.RegisterHandlersComAtproto(e) 52 if err != nil { 53 return nil, err 54 } 55 e.GET("/xrpc/_health", func(c echo.Context) error { 56 return c.JSON(http.StatusOK, map[string]string{"version": cli.Build.Version}) 57 }) 58 e.GET("/xrpc/com.atproto.sync.subscribeRepos", s.handleComAtprotoSyncSubscribeRepos) 59 e.GET("/xrpc/*", s.HandleWildcard) 60 e.POST("/xrpc/*", s.HandleWildcard) 61 return s, nil 62} 63 64func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 65 s.e.ServeHTTP(w, r) 66} 67 68func (s *Server) ErrorHandlingMiddleware() echo.MiddlewareFunc { 69 return func(next echo.HandlerFunc) echo.HandlerFunc { 70 return func(c echo.Context) error { 71 err := next(c) 72 if err == nil { 73 return nil 74 } 75 httpError, ok := err.(*echo.HTTPError) 76 if ok { 77 log.Error(c.Request().Context(), "http error", "code", httpError.Code, "message", httpError.Message, "internal", httpError.Internal) 78 return err 79 } 80 log.Error(c.Request().Context(), "unhandled error", "error", err) 81 return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) 82 } 83 } 84} 85 86// unique type to prevent assignment. 87type echoContextKeyType struct{} 88 89// singleton value to identify our logging metadata in context 90var echoContextKey = echoContextKeyType{} 91 92func (s *Server) ContextPreservingMiddleware() echo.MiddlewareFunc { 93 return func(next echo.HandlerFunc) echo.HandlerFunc { 94 return func(c echo.Context) error { 95 ctx := c.Request().Context() 96 if ctx == nil { 97 ctx = context.Background() 98 } 99 ctx = context.WithValue(ctx, echoContextKey, c) 100 c.SetRequest(c.Request().WithContext(ctx)) 101 return next(c) 102 } 103 } 104}