From d52fd6ba58a883a49f5b691d4db53e5d1fc78e61 Mon Sep 17 00:00:00 2001 From: Guillermo Serrahima Date: Fri, 20 Mar 2026 01:18:24 +0100 Subject: Single post handler --- backend/fetchers/app.bsky.feed.go | 2 ++ backend/main.go | 44 +++++++++++++++++++++++++++++++++++++++ backend/notes/note.go | 9 ++++++++ 3 files changed, 55 insertions(+) diff --git a/backend/fetchers/app.bsky.feed.go b/backend/fetchers/app.bsky.feed.go index 1b9acc1..5d87cca 100644 --- a/backend/fetchers/app.bsky.feed.go +++ b/backend/fetchers/app.bsky.feed.go @@ -152,6 +152,8 @@ func FetchParents(logger *slog.Logger, replyToURI string, slingshotURL string, s note, err := GetAppBskyFeedPostByURI(logger, currentURI, slingshotURL, session, cache) if err != nil { logger.Warn("failed to fetch parent", "uri", currentURI, "error", err) + cache.Set(currentURI, notes.MissingNote(currentURI)) + parents = append(parents, currentURI) break } diff --git a/backend/main.go b/backend/main.go index 234d57d..c7a463e 100644 --- a/backend/main.go +++ b/backend/main.go @@ -7,6 +7,7 @@ import ( "llostre/backend/fetchers" "log/slog" "net/http" + "net/url" "os" "strings" "sync" @@ -317,6 +318,48 @@ func postsHandler(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(posts) } +func postHandler(w http.ResponseWriter, r *http.Request) { + uri := strings.TrimPrefix(r.URL.Path, "/api/post/") + if uri == "" { + http.Error(w, "uri required", http.StatusBadRequest) + return + } + + // decode if URL-encoded + uri, err := url.QueryUnescape(uri) + if err != nil { + http.Error(w, "invalid uri", http.StatusBadRequest) + return + } + + var currentSession *auth.OAuthState + cookie, err := r.Cookie("session_token") + if err != nil { + logger.Warn("unauthorized") + } else { + sessionsMutex.RLock() + session, exists := sessions[cookie.Value] + sessionsMutex.RUnlock() + + if !exists { + logger.Warn("invalid session") + } else { + refreshTokens(session, cookie.Value) + + currentSession = session + } + } + + note, err := fetchers.GetAppBskyFeedPostByURI(logger, uri, slingshotURL, currentSession, cache) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(note) +} + func main() { // serve static files fs := http.FileServer(http.Dir("../frontend/dist")) @@ -327,5 +370,6 @@ func main() { http.HandleFunc("/api/session", sessionHandler) http.HandleFunc("/api/login", loginHandler) http.HandleFunc("/api/callback", callbackHandler) + http.HandleFunc("/api/post/", postHandler) // /api/post/{uri} http.ListenAndServe("0.0.0.0:8080", nil) } diff --git a/backend/notes/note.go b/backend/notes/note.go index 591caff..569188c 100644 --- a/backend/notes/note.go +++ b/backend/notes/note.go @@ -26,6 +26,15 @@ type Facet struct { Value string `json:"value"` // the link destination, whether url, profile or tag } +func MissingNote(uri string) *Note { + return &Note{ + URI: uri, + AuthorDID: "MISSING", + Text: "This post could not be fetched. It may have been deleted.", + CreatedAt: time.Time{}, + } +} + func isChainContained(smallChain, bigChain []string) bool { if len(smallChain) > len(bigChain) { return false -- cgit v1.2.3