From 6fdedee8dc73ac6d79e6dfe360f013afaad6024f Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Tue, 14 Jul 2026 14:08:45 -0700 Subject: [PATCH] spxrpc: finish migrating a bunch of stuff --- .../content/docs/lex-reference/openapi.json | 234 ++++++++++++++++++ lexicons.json | 17 +- pkg/atproto/sync.go | 5 + pkg/comatproto/serverdescribeserver.go | 107 ++++++++ pkg/comatproto/syncgetrecord.go | 32 +++ pkg/comatproto/synclistrepos.go | 83 +++++++ pkg/spxrpc/com_atproto_server.go | 5 +- pkg/spxrpc/com_atproto_sync.go | 12 +- pkg/spxrpc/spxrpc.go | 14 +- pkg/spxrpc/stubs.go | 54 ++++ 10 files changed, 552 insertions(+), 11 deletions(-) create mode 100644 pkg/comatproto/serverdescribeserver.go create mode 100644 pkg/comatproto/syncgetrecord.go create mode 100644 pkg/comatproto/synclistrepos.go diff --git a/js/docs/src/content/docs/lex-reference/openapi.json b/js/docs/src/content/docs/lex-reference/openapi.json index 18816e6c..afa5c9dc 100644 --- a/js/docs/src/content/docs/lex-reference/openapi.json +++ b/js/docs/src/content/docs/lex-reference/openapi.json @@ -4470,6 +4470,92 @@ ] } }, + "/xrpc/com.atproto.sync.getRecord": { + "get": { + "summary": "Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth.", + "operationId": "com.atproto.sync.getRecord", + "tags": ["com.atproto.sync"], + "responses": { + "200": { + "description": "Success", + "content": { + "application/vnd.ipld.car": { + "schema": {} + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["error", "message"], + "properties": { + "error": { + "type": "string", + "oneOf": [ + { + "const": "RecordNotFound" + }, + { + "const": "RepoNotFound" + }, + { + "const": "RepoTakendown" + }, + { + "const": "RepoSuspended" + }, + { + "const": "RepoDeactivated" + } + ] + }, + "message": { + "type": "string" + } + } + } + } + } + } + }, + "parameters": [ + { + "name": "did", + "in": "query", + "required": true, + "description": "The DID of the repo.", + "schema": { + "type": "string", + "description": "The DID of the repo.", + "format": "did" + } + }, + { + "name": "rkey", + "in": "query", + "required": true, + "description": "Record Key", + "schema": { + "type": "string", + "description": "Record Key", + "format": "record-key" + } + }, + { + "name": "collection", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "nsid" + } + } + ] + } + }, "/xrpc/com.atproto.sync.getRepo": { "get": { "summary": "Download a repository export as CAR file. Optionally only a 'diff' since a previous revision. Does not require auth; implemented by PDS.", @@ -4544,6 +4630,58 @@ ] } }, + "/xrpc/com.atproto.sync.listRepos": { + "get": { + "summary": "Enumerates all the DID, rev, and commit CID for all repos hosted by this service. Does not require auth; implemented by PDS and Relay.", + "operationId": "com.atproto.sync.listRepos", + "tags": ["com.atproto.sync"], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "repos": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.atproto.sync.listRepos_repo" + } + }, + "cursor": { + "type": "string" + } + }, + "required": ["repos"] + } + } + } + } + }, + "parameters": [ + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 500, + "minimum": 1, + "maximum": 1000 + } + }, + { + "name": "cursor", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + } + ] + } + }, "/xrpc/com.atproto.sync.subscribeRepos": { "get": { "summary": "Repository event stream, aka Firehose endpoint. Outputs repo commits with diff data, and identity update events, for all repositories on the current server. See the atproto specifications for details around stream sequencing, repo versioning, CAR diff format, and more. Public and does not require auth; implemented by PDS and Relay.", @@ -4721,6 +4859,55 @@ } } }, + "/xrpc/com.atproto.server.describeServer": { + "get": { + "summary": "Describes the server's account creation requirements and capabilities. Implemented by PDS.", + "operationId": "com.atproto.server.describeServer", + "tags": ["com.atproto.server"], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "links": { + "$ref": "#/components/schemas/com.atproto.server.describeServer_links", + "description": "URLs of service policy documents." + }, + "contact": { + "$ref": "#/components/schemas/com.atproto.server.describeServer_contact", + "description": "Contact information" + }, + "inviteCodeRequired": { + "type": "boolean", + "description": "If true, an invite code must be supplied to create an account on this instance." + }, + "availableUserDomains": { + "type": "array", + "description": "List of domain suffixes that can be used in account handles.", + "items": { + "type": "string" + } + }, + "phoneVerificationRequired": { + "type": "boolean", + "description": "If true, a phone verification token must be supplied to create an account on this instance." + } + }, + "required": ["did", "availableUserDomains"] + } + } + } + } + } + } + }, "/xrpc/com.atproto.repo.createRecord": { "post": { "summary": "Create a single new repository record. Requires auth, implemented by PDS.", @@ -7029,6 +7216,32 @@ }, "required": ["issuanceUri", "badgeType", "issuer"] }, + "com.atproto.sync.listRepos_repo": { + "type": "object", + "properties": { + "did": { + "type": "string", + "format": "did" + }, + "rev": { + "type": "string", + "format": "tid" + }, + "head": { + "type": "string", + "description": "Current repo commit CID", + "format": "cid" + }, + "active": { + "type": "boolean" + }, + "status": { + "type": "string", + "description": "If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted." + } + }, + "required": ["did", "head", "rev"] + }, "com.atproto.sync.subscribeRepos_commit": { "type": "object", "description": "Represents an update of repository state. Note that empty commits are allowed, which include no repo data changes, but an update to rev and signature.", @@ -7230,6 +7443,27 @@ }, "required": ["name"] }, + "com.atproto.server.describeServer_links": { + "type": "object", + "properties": { + "privacyPolicy": { + "type": "string", + "format": "uri" + }, + "termsOfService": { + "type": "string", + "format": "uri" + } + } + }, + "com.atproto.server.describeServer_contact": { + "type": "object", + "properties": { + "email": { + "type": "string" + } + } + }, "com.atproto.repo.defs_commitMeta": { "type": "object", "properties": { diff --git a/lexicons.json b/lexicons.json index fd025b1c..94261f2b 100644 --- a/lexicons.json +++ b/lexicons.json @@ -23,7 +23,10 @@ "com.atproto.repo.strongRef", "com.atproto.repo.uploadBlob", "com.atproto.server.createSession", + "com.atproto.server.describeServer", + "com.atproto.sync.getRecord", "com.atproto.sync.getRepo", + "com.atproto.sync.listRepos", "com.atproto.sync.subscribeRepos", "games.gamesgamesgamesgames.defs", "games.gamesgamesgamesgames.search" @@ -181,10 +184,22 @@ "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.server.createSession", "cid": "bafyreieftxrfcsudzvfk5l73r2cukzwa6axzkfjs2l7dxtieyqnkdngwsq" }, + "com.atproto.server.describeServer": { + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.server.describeServer", + "cid": "bafyreidjve4qbhebxzppq23ogsto6luddd63mmn7a57rmml6qi3yf7n3ee" + }, + "com.atproto.sync.getRecord": { + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.sync.getRecord", + "cid": "bafyreiermob6nqcfc6hlhmvuo7jv3imo7hfy3og34oom2p3qe6tldajoru" + }, "com.atproto.sync.getRepo": { "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.sync.getRepo", "cid": "bafyreieyt5x6wf4pgcn56tb2724uplmwcqjivkubmi25km3xrvroz6dw5q" }, + "com.atproto.sync.listRepos": { + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.sync.listRepos", + "cid": "bafyreic6q46ptlr4kv4eonzw3isiqegom5ldr32onhrv2kyrmppgakss2i" + }, "com.atproto.sync.subscribeRepos": { "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.sync.subscribeRepos", "cid": "bafyreid5fdkjulxpnbfqwi4hjehx7ibzzj2h5ln7nachurdzqcpihytbuu" @@ -202,4 +217,4 @@ "cid": "bafyreic3l2rmh2ugirt3jz372wcvy333m7t2ynlyzj2k54oshijs6lxdfu" } } -} \ No newline at end of file +} diff --git a/pkg/atproto/sync.go b/pkg/atproto/sync.go index 28ece9aa..652fbe96 100644 --- a/pkg/atproto/sync.go +++ b/pkg/atproto/sync.go @@ -463,6 +463,11 @@ func (atsync *ATProtoSynchronizer) handleCreateUpdate(ctx context.Context, userD go atsync.Bus.Publish(userDID, lsv) if !isFirstSync { + if atsync.CLI.StreamIsAllowed(userDID) != nil { + // they're live somewhere but they don't have nothin' to do with us + return nil + } + log.Log(ctx, "stream is allowed, queuing finalize task") // queue a task to clean up the livestream if it's been inactive for too long task := &statedb.FinalizeLivestreamTask{ LivestreamURI: aturi.String(), diff --git a/pkg/comatproto/serverdescribeserver.go b/pkg/comatproto/serverdescribeserver.go new file mode 100644 index 00000000..d68a313e --- /dev/null +++ b/pkg/comatproto/serverdescribeserver.go @@ -0,0 +1,107 @@ +// Code generated by glex; DO NOT EDIT. + +// Lexicon schema: com.atproto.server.describeServer + +package comatproto + +import ( + "context" + "io" + + glex "github.com/streamplace/glex/runtime" + cbg "github.com/whyrusleeping/cbor-gen" +) + +type ServerDescribeServer_Output struct { + LexiconTypeID string `json:"$type,omitempty"` + // availableUserDomains: List of domain suffixes that can be used in account handles. + AvailableUserDomains []string `json:"availableUserDomains"` + // contact: Contact information + Contact *ServerDescribeServer_Contact `json:"contact,omitempty"` + Did string `json:"did"` + // inviteCodeRequired: If true, an invite code must be supplied to create an account on this instance. + InviteCodeRequired *bool `json:"inviteCodeRequired,omitempty"` + // links: URLs of service policy documents. + Links *ServerDescribeServer_Links `json:"links,omitempty"` + // phoneVerificationRequired: If true, a phone verification token must be supplied to create an account on this instance. + PhoneVerificationRequired *bool `json:"phoneVerificationRequired,omitempty"` +} + +// RecordTypeID implements glex.Record. +func (t *ServerDescribeServer_Output) RecordTypeID() string { + return "com.atproto.server.describeServer" +} + +func (t *ServerDescribeServer_Output) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + t.LexiconTypeID = "com.atproto.server.describeServer" + return glex.MarshalCBOR(w, t) +} + +func (t *ServerDescribeServer_Output) UnmarshalCBOR(r io.Reader) error { + return glex.UnmarshalCBOR(r, t) +} + +// ServerDescribeServer calls the XRPC method "com.atproto.server.describeServer". +// +// Describes the server's account creation requirements and capabilities. Implemented by PDS. +func ServerDescribeServer(ctx context.Context, c glex.LexClient) (*ServerDescribeServer_Output, error) { + var out ServerDescribeServer_Output + + if err := c.LexDo(ctx, glex.Query, "", "com.atproto.server.describeServer", nil, nil, &out); err != nil { + return nil, err + } + return &out, nil +} + +// ServerDescribeServer_Contact is a "contact" in the com.atproto.server.describeServer schema. +type ServerDescribeServer_Contact struct { + LexiconTypeID string `json:"$type,omitempty"` + Email *string `json:"email,omitempty"` +} + +// RecordTypeID implements glex.Record. +func (t *ServerDescribeServer_Contact) RecordTypeID() string { + return "com.atproto.server.describeServer#contact" +} + +func (t *ServerDescribeServer_Contact) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + t.LexiconTypeID = "com.atproto.server.describeServer#contact" + return glex.MarshalCBOR(w, t) +} + +func (t *ServerDescribeServer_Contact) UnmarshalCBOR(r io.Reader) error { + return glex.UnmarshalCBOR(r, t) +} + +// ServerDescribeServer_Links is a "links" in the com.atproto.server.describeServer schema. +type ServerDescribeServer_Links struct { + LexiconTypeID string `json:"$type,omitempty"` + PrivacyPolicy *string `json:"privacyPolicy,omitempty"` + TermsOfService *string `json:"termsOfService,omitempty"` +} + +// RecordTypeID implements glex.Record. +func (t *ServerDescribeServer_Links) RecordTypeID() string { + return "com.atproto.server.describeServer#links" +} + +func (t *ServerDescribeServer_Links) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + t.LexiconTypeID = "com.atproto.server.describeServer#links" + return glex.MarshalCBOR(w, t) +} + +func (t *ServerDescribeServer_Links) UnmarshalCBOR(r io.Reader) error { + return glex.UnmarshalCBOR(r, t) +} diff --git a/pkg/comatproto/syncgetrecord.go b/pkg/comatproto/syncgetrecord.go new file mode 100644 index 00000000..f1764fbf --- /dev/null +++ b/pkg/comatproto/syncgetrecord.go @@ -0,0 +1,32 @@ +// Code generated by glex; DO NOT EDIT. + +// Lexicon schema: com.atproto.sync.getRecord + +package comatproto + +import ( + "bytes" + "context" + + glex "github.com/streamplace/glex/runtime" +) + +// SyncGetRecord calls the XRPC method "com.atproto.sync.getRecord". +// +// Get data blocks needed to prove the existence or non-existence of record in the current version of repo. Does not require auth. +// +// did: The DID of the repo. +// rkey: Record Key +func SyncGetRecord(ctx context.Context, c glex.LexClient, collection string, did string, rkey string) ([]byte, error) { + buf := new(bytes.Buffer) + + params := map[string]interface{}{} + params["collection"] = collection + params["did"] = did + params["rkey"] = rkey + + if err := c.LexDo(ctx, glex.Query, "", "com.atproto.sync.getRecord", params, nil, buf); err != nil { + return nil, err + } + return buf.Bytes(), nil +} diff --git a/pkg/comatproto/synclistrepos.go b/pkg/comatproto/synclistrepos.go new file mode 100644 index 00000000..16acc731 --- /dev/null +++ b/pkg/comatproto/synclistrepos.go @@ -0,0 +1,83 @@ +// Code generated by glex; DO NOT EDIT. + +// Lexicon schema: com.atproto.sync.listRepos + +package comatproto + +import ( + "context" + "io" + + glex "github.com/streamplace/glex/runtime" + cbg "github.com/whyrusleeping/cbor-gen" +) + +type SyncListRepos_Output struct { + LexiconTypeID string `json:"$type,omitempty"` + Cursor *string `json:"cursor,omitempty"` + Repos []SyncListRepos_Repo `json:"repos"` +} + +// RecordTypeID implements glex.Record. +func (t *SyncListRepos_Output) RecordTypeID() string { return "com.atproto.sync.listRepos" } + +func (t *SyncListRepos_Output) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + t.LexiconTypeID = "com.atproto.sync.listRepos" + return glex.MarshalCBOR(w, t) +} + +func (t *SyncListRepos_Output) UnmarshalCBOR(r io.Reader) error { + return glex.UnmarshalCBOR(r, t) +} + +// SyncListRepos calls the XRPC method "com.atproto.sync.listRepos". +// +// Enumerates all the DID, rev, and commit CID for all repos hosted by this service. Does not require auth; implemented by PDS and Relay. +func SyncListRepos(ctx context.Context, c glex.LexClient, cursor string, limit *int64) (*SyncListRepos_Output, error) { + var out SyncListRepos_Output + + params := map[string]interface{}{} + if cursor != "" { + params["cursor"] = cursor + } + if limit != nil { + params["limit"] = *limit + } + + if err := c.LexDo(ctx, glex.Query, "", "com.atproto.sync.listRepos", params, nil, &out); err != nil { + return nil, err + } + return &out, nil +} + +// SyncListRepos_Repo is a "repo" in the com.atproto.sync.listRepos schema. +type SyncListRepos_Repo struct { + LexiconTypeID string `json:"$type,omitempty"` + Active *bool `json:"active,omitempty"` + Did string `json:"did"` + // head: Current repo commit CID + Head string `json:"head"` + Rev string `json:"rev"` + // status: If active=false, this optional field indicates a possible reason for why the account is not active. If active=false and no status is supplied, then the host makes no claim for why the repository is no longer being hosted. + Status *string `json:"status,omitempty"` +} + +// RecordTypeID implements glex.Record. +func (t *SyncListRepos_Repo) RecordTypeID() string { return "com.atproto.sync.listRepos#repo" } + +func (t *SyncListRepos_Repo) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + t.LexiconTypeID = "com.atproto.sync.listRepos#repo" + return glex.MarshalCBOR(w, t) +} + +func (t *SyncListRepos_Repo) UnmarshalCBOR(r io.Reader) error { + return glex.UnmarshalCBOR(r, t) +} diff --git a/pkg/spxrpc/com_atproto_server.go b/pkg/spxrpc/com_atproto_server.go index 873ac3a0..ad5ddc52 100644 --- a/pkg/spxrpc/com_atproto_server.go +++ b/pkg/spxrpc/com_atproto_server.go @@ -4,15 +4,14 @@ import ( "context" "fmt" - indigoatproto "github.com/bluesky-social/indigo/api/atproto" "github.com/labstack/echo/v4" "stream.place/streamplace/pkg/comatproto" ) -func (s *Server) handleComAtprotoServerDescribeServer(ctx context.Context) (*indigoatproto.ServerDescribeServer_Output, error) { +func (s *Server) handleComAtprotoServerDescribeServer(ctx context.Context) (*comatproto.ServerDescribeServer_Output, error) { did := fmt.Sprintf("did:web:%s", s.cli.BroadcasterHost) trueVar := true - return &indigoatproto.ServerDescribeServer_Output{ + return &comatproto.ServerDescribeServer_Output{ Did: did, InviteCodeRequired: &trueVar, AvailableUserDomains: []string{ diff --git a/pkg/spxrpc/com_atproto_sync.go b/pkg/spxrpc/com_atproto_sync.go index 221c3dec..28fff03f 100644 --- a/pkg/spxrpc/com_atproto_sync.go +++ b/pkg/spxrpc/com_atproto_sync.go @@ -7,9 +7,9 @@ import ( "io" "net/http" "strconv" + "stream.place/streamplace/pkg/comatproto" - indigoatproto "github.com/bluesky-social/indigo/api/atproto" "github.com/bluesky-social/indigo/events" "github.com/gorilla/websocket" "github.com/labstack/echo/v4" @@ -17,13 +17,13 @@ import ( "stream.place/streamplace/pkg/log" ) -func (s *Server) handleComAtprotoSyncListRepos(ctx context.Context, cursor string, limit int) (*indigoatproto.SyncListRepos_Output, error) { +func (s *Server) handleComAtprotoSyncListRepos(ctx context.Context, cursor string, limit int) (*comatproto.SyncListRepos_Output, error) { active := true if s.isServerPDS(ctx) { // Server PDS: only the server repo - return &indigoatproto.SyncListRepos_Output{ - Repos: []*indigoatproto.SyncListRepos_Repo{ + return &comatproto.SyncListRepos_Output{ + Repos: []comatproto.SyncListRepos_Repo{ { Did: atproto.ServerRepo.RepoDid(), Head: atproto.ServerRepo.SignedCommit().Data.String(), @@ -35,8 +35,8 @@ func (s *Server) handleComAtprotoSyncListRepos(ctx context.Context, cursor strin } // Broadcaster PDS: only the lexicon repo - return &indigoatproto.SyncListRepos_Output{ - Repos: []*indigoatproto.SyncListRepos_Repo{ + return &comatproto.SyncListRepos_Output{ + Repos: []comatproto.SyncListRepos_Repo{ { Did: atproto.LexiconRepo.RepoDid(), Head: atproto.LexiconRepo.SignedCommit().Data.String(), diff --git a/pkg/spxrpc/spxrpc.go b/pkg/spxrpc/spxrpc.go index b0053dd7..ad324491 100644 --- a/pkg/spxrpc/spxrpc.go +++ b/pkg/spxrpc/spxrpc.go @@ -85,8 +85,20 @@ func NewServer(ctx context.Context, cli *config.CLI, model model.Model, stateful if err != nil { return nil, err } + err = s.RegisterHandlersAppbsky(e) + if err != nil { + return nil, err + } + err = s.RegisterHandlersGamesgamesgamesgamesgames(e) + if err != nil { + return nil, err + } + err = s.RegisterHandlersComatproto(e) + if err != nil { + return nil, err + } e.GET("/xrpc/_health", func(c echo.Context) error { - return c.JSON(http.StatusOK, map[string]string{"version": cli.Build.Version}) + return c.JSON(http.StatusOK, map[string]string{"version": fmt.Sprintf("streamplace %s", cli.Build.Version)}) }) e.GET("/xrpc/com.atproto.sync.subscribeRepos", s.handleComAtprotoSyncSubscribeRepos) e.GET("/xrpc/place.stream.live.subscribeSegments", s.handlePlaceStreamLiveSubscribeSegments) diff --git a/pkg/spxrpc/stubs.go b/pkg/spxrpc/stubs.go index 5ce17ae7..528df521 100644 --- a/pkg/spxrpc/stubs.go +++ b/pkg/spxrpc/stubs.go @@ -69,7 +69,10 @@ func (s *Server) RegisterHandlersComatproto(e *echo.Echo) error { e.POST("/xrpc/com.atproto.repo.putRecord", s.HandleComAtprotoRepoPutRecord) e.POST("/xrpc/com.atproto.repo.uploadBlob", s.HandleComAtprotoRepoUploadBlob) e.POST("/xrpc/com.atproto.server.createSession", s.HandleComAtprotoServerCreateSession) + e.GET("/xrpc/com.atproto.server.describeServer", s.HandleComAtprotoServerDescribeServer) + e.GET("/xrpc/com.atproto.sync.getRecord", s.HandleComAtprotoSyncGetRecord) e.GET("/xrpc/com.atproto.sync.getRepo", s.HandleComAtprotoSyncGetRepo) + e.GET("/xrpc/com.atproto.sync.listRepos", s.HandleComAtprotoSyncListRepos) return nil } @@ -266,6 +269,35 @@ func (s *Server) HandleComAtprotoServerCreateSession(c echo.Context) error { return c.JSON(200, out) } +func (s *Server) HandleComAtprotoServerDescribeServer(c echo.Context) error { + ctx, span := otel.Tracer("server").Start(c.Request().Context(), "HandleComAtprotoServerDescribeServer") + defer span.End() + var out *comatproto.ServerDescribeServer_Output + var handleErr error + // func (s *Server) handleComAtprotoServerDescribeServer(ctx context.Context) (*comatproto.ServerDescribeServer_Output, error) + out, handleErr = s.handleComAtprotoServerDescribeServer(ctx) + if handleErr != nil { + return handleErr + } + return c.JSON(200, out) +} + +func (s *Server) HandleComAtprotoSyncGetRecord(c echo.Context) error { + ctx, span := otel.Tracer("server").Start(c.Request().Context(), "HandleComAtprotoSyncGetRecord") + defer span.End() + collection := c.QueryParam("collection") + did := c.QueryParam("did") + rkey := c.QueryParam("rkey") + var out io.Reader + var handleErr error + // func (s *Server) handleComAtprotoSyncGetRecord(ctx context.Context,collection string,did string,rkey string) (io.Reader, error) + out, handleErr = s.handleComAtprotoSyncGetRecord(ctx, collection, did, rkey) + if handleErr != nil { + return handleErr + } + return c.Stream(200, "application/octet-stream", out) +} + func (s *Server) HandleComAtprotoSyncGetRepo(c echo.Context) error { ctx, span := otel.Tracer("server").Start(c.Request().Context(), "HandleComAtprotoSyncGetRepo") defer span.End() @@ -281,6 +313,28 @@ func (s *Server) HandleComAtprotoSyncGetRepo(c echo.Context) error { return c.Stream(200, "application/octet-stream", out) } +func (s *Server) HandleComAtprotoSyncListRepos(c echo.Context) error { + ctx, span := otel.Tracer("server").Start(c.Request().Context(), "HandleComAtprotoSyncListRepos") + defer span.End() + cursor := c.QueryParam("cursor") + limit := 0 + if p := c.QueryParam("limit"); p != "" { + var err error + limit, err = strconv.Atoi(p) + if err != nil { + return err + } + } + var out *comatproto.SyncListRepos_Output + var handleErr error + // func (s *Server) handleComAtprotoSyncListRepos(ctx context.Context,cursor string,limit int) (*comatproto.SyncListRepos_Output, error) + out, handleErr = s.handleComAtprotoSyncListRepos(ctx, cursor, limit) + if handleErr != nil { + return handleErr + } + return c.JSON(200, out) +} + func (s *Server) RegisterHandlersGamesgamesgamesgamesgames(e *echo.Echo) error { e.GET("/xrpc/games.gamesgamesgamesgames.search", s.HandleGamesGamesgamesgamesgamesSearch) return nil -- 2.51.2