backend for xcvr appview
2
fork

Configure Feed

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

add lastSeen lexicon

+92
+45
lexicons/org/xcvr/actor/getLastSeen.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.xcvr.actor.getLastSeen", 4 + "defs": { 5 + "main": { 6 + "type": "query", 7 + "description": "gets a user's profileView", 8 + "parameters": { 9 + "type": "params" 10 + "union": [ 11 + { 12 + "type": "object", 13 + "required": ["handle"], 14 + "properties": { 15 + "handle": {"type": "string"}, 16 + } 17 + }, 18 + { 19 + "type": "object", 20 + "required": ["did"], 21 + "properties": { 22 + "did": {"type": "string"}, 23 + } 24 + } 25 + ] 26 + }, 27 + "output": { 28 + "encoding": "application/json", 29 + "schema": { 30 + "type": "object", 31 + "properties": { 32 + "where": { 33 + "type": "string", 34 + "format": "at-uri" 35 + }, 36 + "when": { 37 + "type": "string", 38 + "format": "datetime" 39 + } 40 + } 41 + } 42 + } 43 + } 44 + } 45 + }
+13
server/internal/db/db.go
··· 6 6 "fmt" 7 7 "os" 8 8 "rvcx/internal/types" 9 + "time" 9 10 10 11 "github.com/jackc/pgx/v5/pgxpool" 11 12 ) ··· 83 84 return errors.New("error storing did/handle: " + err.Error()) 84 85 } 85 86 return nil 87 + } 88 + 89 + func (s *Store) GetLastSeen(did string, ctx context.Context) (where *string, when *time.Time) { 90 + row := s.pool.QueryRow(ctx, `SELECT 91 + s.channel_uri, m.posted_at 92 + FROM messages m 93 + JOIN signets s ON m.signet_uri = s.uri 94 + JOIN did_handles dh ON m.did = dh.did 95 + WHERE m.did = $1 AND dh.handle = s.author_handle 96 + ORDER BY s.message_id DESC`, did) 97 + row.Scan(&where, &when) 98 + return 86 99 } 87 100 88 101 func (s *Store) GetMessages(channelURI string, limit int, cursor *int, ctx context.Context) ([]types.SignedMessageView, error) {
+1
server/internal/handler/handler.go
··· 40 40 mux.HandleFunc("GET /xrpc/org.xcvr.actor.resolveChannel", h.WithCORS(h.resolveChannel)) 41 41 mux.HandleFunc("GET /xrpc/org.xcvr.actor.getProfileView", h.WithCORS(h.getProfileView)) 42 42 mux.HandleFunc("GET /xrpc/org.xcvr.lrc.subscribeLexStream", h.WithCORS(h.subscribeLexStream)) 43 + mux.HandleFunc("GET /xrpc/org.xcvr.actor.getLastSeen", h.WithCORS(h.getLastSeen)) 43 44 // backend metadata handlers 44 45 mux.HandleFunc(clientMetadataPath(), h.WithCORS(h.serveClientMetadata)) 45 46 mux.HandleFunc(clientTOSPath(), h.WithCORS(h.serveTOS))
+33
server/internal/handler/lexiconHandlers.go
··· 9 9 "rvcx/internal/atputils" 10 10 "rvcx/internal/types" 11 11 "strconv" 12 + "time" 12 13 ) 13 14 14 15 func (h *Handler) getChannels(w http.ResponseWriter, r *http.Request) { ··· 129 130 encoder := json.NewEncoder(w) 130 131 encoder.Encode(profile) 131 132 } 133 + 134 + func (h *Handler) getLastSeen(w http.ResponseWriter, r *http.Request) { 135 + handle := r.URL.Query().Get("handle") 136 + did := r.URL.Query().Get("did") 137 + if did == "" { 138 + if handle == "" { 139 + h.badRequest(w, errors.New("did not provide did or handle")) 140 + return 141 + } 142 + var err error 143 + did, err = h.db.ResolveHandle(handle, r.Context()) 144 + if err != nil { 145 + did, err = atputils.TryLookupHandle(r.Context(), handle) 146 + if err != nil { 147 + h.serverError(w, errors.New("i think the handle might not exist?"+err.Error())) 148 + return 149 + } 150 + go h.db.StoreDidHandle(did, handle, context.Background()) 151 + } 152 + } 153 + where, when := h.db.GetLastSeen(did, r.Context()) 154 + type lastSeenResp struct { 155 + Where *string `json:"where,omitempty"` 156 + When *time.Time `json:"when,omitempty"` 157 + } 158 + resp := lastSeenResp{ 159 + where, when, 160 + } 161 + w.Header().Set("Content-Type", "application/json") 162 + encoder := json.NewEncoder(w) 163 + encoder.Encode(resp) 164 + }