Live video on the AT Protocol

moderation: ozone proxy experiment

+123 -4
+3 -1
go.mod
··· 10 11 replace github.com/AxisCommunications/go-dpop => github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4 12 13 require ( 14 firebase.google.com/go/v4 v4.14.1 15 git.stream.place/streamplace/c2pa-go v0.7.0 ··· 37 github.com/johncgriffin/overflow v0.0.0-20211019200055-46fa312c352c 38 github.com/julienschmidt/httprouter v1.3.0 39 github.com/labstack/echo/v4 v4.13.3 40 github.com/lestrrat-go/jwx/v2 v2.1.6 41 github.com/livepeer/lpms v0.0.0-20240812093642-b5181eb92cb2 42 github.com/lmittmann/tint v1.1.0 ··· 267 github.com/klauspost/cpuid/v2 v2.2.10 // indirect 268 github.com/kulti/thelper v0.6.3 // indirect 269 github.com/kunwardeep/paralleltest v1.0.14 // indirect 270 - github.com/labstack/gommon v0.4.2 // indirect 271 github.com/lasiar/canonicalheader v1.1.2 // indirect 272 github.com/ldez/exptostd v0.4.3 // indirect 273 github.com/ldez/gomoddirectives v0.6.1 // indirect
··· 10 11 replace github.com/AxisCommunications/go-dpop => github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4 12 13 + replace github.com/streamplace/oatproxy => ../oatproxy 14 + 15 require ( 16 firebase.google.com/go/v4 v4.14.1 17 git.stream.place/streamplace/c2pa-go v0.7.0 ··· 39 github.com/johncgriffin/overflow v0.0.0-20211019200055-46fa312c352c 40 github.com/julienschmidt/httprouter v1.3.0 41 github.com/labstack/echo/v4 v4.13.3 42 + github.com/labstack/gommon v0.4.2 43 github.com/lestrrat-go/jwx/v2 v2.1.6 44 github.com/livepeer/lpms v0.0.0-20240812093642-b5181eb92cb2 45 github.com/lmittmann/tint v1.1.0 ··· 270 github.com/klauspost/cpuid/v2 v2.2.10 // indirect 271 github.com/kulti/thelper v0.6.3 // indirect 272 github.com/kunwardeep/paralleltest v1.0.14 // indirect 273 github.com/lasiar/canonicalheader v1.1.2 // indirect 274 github.com/ldez/exptostd v0.4.3 // indirect 275 github.com/ldez/gomoddirectives v0.6.1 // indirect
-2
go.sum
··· 938 github.com/streamplace/atproto-oauth-golang v0.0.0-20250619231223-a9c04fb888ac/go.mod h1:9LlKkqciiO5lRfbX0n4Wn5KNY9nvFb4R3by8FdW2TWc= 939 github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4 h1:L1fS4HJSaAyNnkwfuZubgfeZy8rkWmA0cMtH5Z0HqNc= 940 github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4/go.mod h1:bGUXY9Wd4mnd+XUrOYZr358J2f6z9QO/dLhL1SsiD+0= 941 - github.com/streamplace/oatproxy v0.0.0-20250619231549-b15df1b82a3a h1:rbfSEm9IgK6jhXXtOExexL+ACNDOE/zJ5VYp6ujTpCA= 942 - github.com/streamplace/oatproxy v0.0.0-20250619231549-b15df1b82a3a/go.mod h1:pXi24hA7xBHj8eEywX6wGqJOR9FaEYlGwQ/72rN6okw= 943 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 944 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 945 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
··· 938 github.com/streamplace/atproto-oauth-golang v0.0.0-20250619231223-a9c04fb888ac/go.mod h1:9LlKkqciiO5lRfbX0n4Wn5KNY9nvFb4R3by8FdW2TWc= 939 github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4 h1:L1fS4HJSaAyNnkwfuZubgfeZy8rkWmA0cMtH5Z0HqNc= 940 github.com/streamplace/go-dpop v0.0.0-20250510031900-c897158a8ad4/go.mod h1:bGUXY9Wd4mnd+XUrOYZr358J2f6z9QO/dLhL1SsiD+0= 941 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 942 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 943 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+99 -1
pkg/spxrpc/com_atproto_moderation.go
··· 1 package spxrpc 2 3 import ( 4 "context" 5 6 comatprototypes "github.com/bluesky-social/indigo/api/atproto" 7 ) 8 9 func (s *Server) handleComAtprotoModerationCreateReport(ctx context.Context, body *comatprototypes.ModerationCreateReport_Input) (*comatprototypes.ModerationCreateReport_Output, error) { 10 - panic("not implemented") 11 }
··· 1 package spxrpc 2 3 import ( 4 + "bytes" 5 "context" 6 + "encoding/json" 7 + "fmt" 8 + "io" 9 + "net/http" 10 11 comatprototypes "github.com/bluesky-social/indigo/api/atproto" 12 + "github.com/bluesky-social/indigo/xrpc" 13 + "github.com/labstack/echo/v4" 14 + "github.com/streamplace/oatproxy/pkg/oatproxy" 15 + "stream.place/streamplace/pkg/aqhttp" 16 + "stream.place/streamplace/pkg/log" 17 ) 18 19 func (s *Server) handleComAtprotoModerationCreateReport(ctx context.Context, body *comatprototypes.ModerationCreateReport_Input) (*comatprototypes.ModerationCreateReport_Output, error) { 20 + c, ok := ctx.Value(echoContextKey).(echo.Context) 21 + if !ok { 22 + return nil, echo.NewHTTPError(http.StatusInternalServerError, "echo context not found") 23 + } 24 + 25 + dPoP := c.Request().Header.Get("DPoP") 26 + if dPoP == "" { 27 + return s.handleComAtprotoModerationCreateReportToOzone(ctx, c, body) 28 + } else { 29 + return s.handleComAtprotoModerationCreateReportToPDS(ctx, c, body) 30 + } 31 + } 32 + 33 + func (s *Server) handleComAtprotoModerationCreateReportToOzone(ctx context.Context, c echo.Context, body *comatprototypes.ModerationCreateReport_Input) (*comatprototypes.ModerationCreateReport_Output, error) { 34 + // Serialize the input body to JSON 35 + jsonBody, err := json.Marshal(body) 36 + if err != nil { 37 + return nil, echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to marshal request body: %v", err)) 38 + } 39 + 40 + // Create the request URL 41 + url := fmt.Sprintf("%s/xrpc/com.atproto.moderation.createReport", s.cli.OzoneURL) 42 + 43 + // Create the HTTP request 44 + req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(jsonBody)) 45 + if err != nil { 46 + return nil, echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to create request: %v", err)) 47 + } 48 + 49 + authorization := c.Request().Header.Get("Authorization") 50 + if authorization == "" { 51 + return nil, echo.NewHTTPError(http.StatusUnauthorized, "Authorization header not found") 52 + } 53 + 54 + log.Log(ctx, "handleComAtprotoModerationCreateReportToOzone", "authorization", authorization) 55 + 56 + // Set headers 57 + req.Header.Set("Content-Type", "application/json") 58 + req.Header.Set("Authorization", authorization) 59 + 60 + // Send the request 61 + resp, err := aqhttp.Client.Do(req) 62 + if err != nil { 63 + return nil, echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to send request: %v", err)) 64 + } 65 + defer resp.Body.Close() 66 + 67 + // Read the response body 68 + respBody, err := io.ReadAll(resp.Body) 69 + if err != nil { 70 + return nil, echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to read response body: %v", err)) 71 + } 72 + 73 + // Check if the response was successful 74 + if resp.StatusCode != http.StatusOK { 75 + return nil, echo.NewHTTPError(resp.StatusCode, fmt.Sprintf("upstream error: %s", string(respBody))) 76 + } 77 + 78 + // Deserialize the response 79 + var output comatprototypes.ModerationCreateReport_Output 80 + if err := json.Unmarshal(respBody, &output); err != nil { 81 + return nil, echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to unmarshal response: %v", err)) 82 + } 83 + 84 + return &output, nil 85 + } 86 + 87 + func (s *Server) handleComAtprotoModerationCreateReportToPDS(ctx context.Context, c echo.Context, body *comatprototypes.ModerationCreateReport_Input) (*comatprototypes.ModerationCreateReport_Output, error) { 88 + log.Log(ctx, "handleComAtprotoModerationCreateReport", "body", body) 89 + if s.cli.OzoneURL == "" { 90 + return nil, echo.NewHTTPError(http.StatusNotImplemented, "Ozone URL is not set") 91 + } 92 + 93 + session, client := oatproxy.GetOAuthSession(ctx) 94 + if session == nil { 95 + return nil, echo.NewHTTPError(http.StatusUnauthorized, "oauth session not found") 96 + } 97 + 98 + client.SetHeaders(map[string]string{ 99 + "Atproto-Proxy": fmt.Sprintf("%s#atproto_labeler", s.cli.MyDID()), 100 + }) 101 + 102 + var output comatprototypes.ModerationCreateReport_Output 103 + err := client.Do(ctx, xrpc.Procedure, "application/json", "com.atproto.moderation.createReport", nil, body, &output) 104 + if err != nil { 105 + return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error()) 106 + } 107 + 108 + return &output, nil 109 }
+21
pkg/spxrpc/spxrpc.go
··· 27 model: model, 28 } 29 e.Use(s.ErrorHandlingMiddleware()) 30 e.Use(echomiddleware.Handler("", mdlw)) 31 e.Use(op.OAuthMiddleware) 32 err := s.RegisterHandlersPlaceStream(e) ··· 71 } 72 } 73 }
··· 27 model: model, 28 } 29 e.Use(s.ErrorHandlingMiddleware()) 30 + e.Use(s.ContextPreservingMiddleware()) 31 e.Use(echomiddleware.Handler("", mdlw)) 32 e.Use(op.OAuthMiddleware) 33 err := s.RegisterHandlersPlaceStream(e) ··· 72 } 73 } 74 } 75 + 76 + // unique type to prevent assignment. 77 + type echoContextKeyType struct{} 78 + 79 + // singleton value to identify our logging metadata in context 80 + var echoContextKey = echoContextKeyType{} 81 + 82 + func (s *Server) ContextPreservingMiddleware() echo.MiddlewareFunc { 83 + return func(next echo.HandlerFunc) echo.HandlerFunc { 84 + return func(c echo.Context) error { 85 + ctx := c.Request().Context() 86 + if ctx == nil { 87 + ctx = context.Background() 88 + } 89 + ctx = context.WithValue(ctx, echoContextKey, c) 90 + c.SetRequest(c.Request().WithContext(ctx)) 91 + return next(c) 92 + } 93 + } 94 + }