collection of golang services under the Red Dwarf umbrella server.reddwarf.app
bluesky reddwarf microcosm appview
16
fork

Configure Feed

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

extremely terrible basic getFeed

+217 -6
+168 -6
main.go
··· 10 10 "log" 11 11 "net/http" 12 12 "os" 13 + "strings" 13 14 "sync" 14 15 "time" 15 16 ··· 79 80 // spacedust is type definitions only 80 81 // jetstream types is probably available from jetstream/pkg/models 81 82 82 - router := gin.New() 83 - router.Use(gin.Logger()) 84 - router.Use(gin.Recovery()) 85 - router.Use(cors.Default()) 83 + r_unsafe := gin.New() 84 + r_unsafe.Use(gin.Logger()) 85 + r_unsafe.Use(gin.Recovery()) 86 + r_unsafe.Use(cors.Default()) 86 87 87 - router.GET("/.well-known/did.json", GetWellKnownDID) 88 + r_unsafe.GET("/.well-known/did.json", GetWellKnownDID) 88 89 89 90 auther, err := auth.NewAuth( 90 91 100_000, ··· 96 97 log.Fatalf("Failed to create Auth: %v", err) 97 98 } 98 99 100 + router := r_unsafe.Group("/") 101 + router.Use(auther.AuthenticateGinRequestViaJWT) 99 102 router.Use(auther.AuthenticateGinRequestViaJWT) 100 103 101 104 responsewow, err := agnostic.RepoGetRecord(ctx, sl, "", "app.bsky.actor.profile", "did:web:did12.whey.party", "self") ··· 410 413 }) 411 414 }) 412 415 416 + r_unsafe.GET("/xrpc/app.bsky.feed.getFeed", 417 + func(c *gin.Context) { 418 + ctx := c.Request.Context() 419 + 420 + feedGenAturiRaw := c.Query("feed") 421 + if feedGenAturiRaw == "" { 422 + c.JSON(http.StatusBadRequest, gin.H{"error": "Missing feed param"}) 423 + return 424 + } 425 + 426 + feedGenAturi, err := syntax.ParseATURI(feedGenAturiRaw) 427 + if err != nil { 428 + return 429 + } 430 + 431 + feedGeneratorRecordResponse, err := agnostic.RepoGetRecord(ctx, sl, "", "app.bsky.feed.generator", feedGenAturi.Authority().String(), feedGenAturi.RecordKey().String()) 432 + if err != nil { 433 + c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("Failed to resolve feed generator record: %v", err)}) 434 + return 435 + } 436 + 437 + var feedGeneratorRecord appbsky.FeedGenerator 438 + if err := json.Unmarshal(*feedGeneratorRecordResponse.Value, &feedGeneratorRecord); err != nil { 439 + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse feed generator record JSON"}) 440 + return 441 + } 442 + 443 + feedGenDID := feedGeneratorRecord.Did 444 + 445 + didDoc, err := ResolveDID(feedGenDID) 446 + if err != nil { 447 + c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("Failed to resolve DID: %v", err)}) 448 + return 449 + } 450 + 451 + if err != nil { 452 + c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("Failed to resolve DID: %v", err)}) 453 + return 454 + } 455 + 456 + var targetEndpoint string 457 + for _, svc := range didDoc.Service { 458 + if svc.Type == "BskyFeedGenerator" && strings.HasSuffix(svc.ID, "#bsky_fg") { 459 + targetEndpoint = svc.ServiceEndpoint 460 + break 461 + } 462 + } 463 + if targetEndpoint == "" { 464 + c.JSON(http.StatusBadGateway, gin.H{"error": "Feed Generator service endpoint not found in DID document"}) 465 + return 466 + } 467 + upstreamURL := fmt.Sprintf("%s/xrpc/app.bsky.feed.getFeedSkeleton?%s", 468 + strings.TrimSuffix(targetEndpoint, "/"), 469 + c.Request.URL.RawQuery, 470 + ) 471 + req, err := http.NewRequestWithContext(ctx, "GET", upstreamURL, nil) 472 + if err != nil { 473 + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create upstream request"}) 474 + return 475 + } 476 + headersToForward := []string{"Authorization", "Content-Type", "Accept", "User-Agent"} 477 + for _, k := range headersToForward { 478 + if v := c.GetHeader(k); v != "" { 479 + req.Header.Set(k, v) 480 + } 481 + } 482 + client := &http.Client{} 483 + resp, err := client.Do(req) 484 + if err != nil { 485 + c.JSON(http.StatusBadGateway, gin.H{"error": fmt.Sprintf("Upstream request failed: %v", err)}) 486 + return 487 + } 488 + defer resp.Body.Close() 489 + 490 + bodyBytes, err := io.ReadAll(resp.Body) 491 + if err != nil { 492 + c.JSON(http.StatusBadGateway, gin.H{"error": "Failed to read upstream body"}) 493 + return 494 + } 495 + if resp.StatusCode != http.StatusOK { 496 + // Forward the upstream error raw 497 + c.Data(resp.StatusCode, resp.Header.Get("Content-Type"), bodyBytes) 498 + return 499 + } 500 + 501 + var feekskeleton appbsky.FeedGetFeedSkeleton_Output 502 + if err := json.Unmarshal(bodyBytes, &feekskeleton); err != nil { 503 + c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to parse upstream JSON"}) 504 + return 505 + } 506 + 507 + skeletonposts := feekskeleton.Feed 508 + 509 + type result struct { 510 + view *appbsky.FeedDefs_FeedViewPost 511 + } 512 + 513 + results := make([]result, len(skeletonposts)) 514 + 515 + var wg sync.WaitGroup 516 + wg.Add(len(skeletonposts)) 517 + 518 + for i, raw := range skeletonposts { 519 + go func(i int, raw appbsky.FeedDefs_SkeletonFeedPost) { 520 + defer wg.Done() 521 + 522 + post, _, err := appbskyfeeddefs.PostView(ctx, raw.Post, sl, BSKYIMAGECDN_URL) 523 + if err != nil || post == nil { 524 + return 525 + } 526 + 527 + feedviewpost := &appbsky.FeedDefs_FeedViewPost{ 528 + // FeedContext *string `json:"feedContext,omitempty" cborgen:"feedContext,omitempty"` 529 + // Post *FeedDefs_PostView `json:"post" cborgen:"post"` 530 + Post: post, 531 + // Reason *FeedDefs_FeedViewPost_Reason `json:"reason,omitempty" cborgen:"reason,omitempty"` 532 + // Reason: &appbsky.FeedDefs_FeedViewPost_Reason{ 533 + // // FeedDefs_ReasonRepost *FeedDefs_ReasonRepost 534 + // FeedDefs_ReasonRepost: &appbsky.FeedDefs_ReasonRepost{ 535 + // // LexiconTypeID string `json:"$type" cborgen:"$type,const=app.bsky.feed.defs#reasonRepost"` 536 + // LexiconTypeID: "app.bsky.feed.defs#reasonRepost", 537 + // // By *ActorDefs_ProfileViewBasic `json:"by" cborgen:"by"` 538 + // // Cid *string `json:"cid,omitempty" cborgen:"cid,omitempty"` 539 + // // IndexedAt string `json:"indexedAt" cborgen:"indexedAt"` 540 + // // Uri *string `json:"uri,omitempty" cborgen:"uri,omitempty"` 541 + // Uri: &raw.Reason.FeedDefs_SkeletonReasonRepost.Repost, 542 + // }, 543 + // // FeedDefs_ReasonPin *FeedDefs_ReasonPin 544 + // FeedDefs_ReasonPin: &appbsky.FeedDefs_ReasonPin{ 545 + // // LexiconTypeID string `json:"$type" cborgen:"$type,const=app.bsky.feed.defs#reasonPin"` 546 + // LexiconTypeID: "app.bsky.feed.defs#reasonPin", 547 + // }, 548 + // }, 549 + // Reply *FeedDefs_ReplyRef `json:"reply,omitempty" cborgen:"reply,omitempty"` 550 + // // reqId: Unique identifier per request that may be passed back alongside interactions. 551 + // ReqId *string `json:"reqId,omitempty" cborgen:"reqId,omitempty"` 552 + 553 + } 554 + 555 + results[i].view = feedviewpost 556 + }(i, *raw) 557 + } 558 + 559 + wg.Wait() 560 + 561 + // build final slice 562 + out := make([]*appbsky.FeedDefs_FeedViewPost, 0, len(results)) 563 + for _, r := range results { 564 + if r.view != nil && r.view.Post != nil { 565 + out = append(out, r.view) 566 + } 567 + } 568 + 569 + c.JSON(http.StatusOK, &appbsky.FeedGetFeed_Output{ 570 + Cursor: feekskeleton.Cursor, 571 + Feed: out, 572 + }) 573 + }) 574 + 413 575 yourJSONBytes, _ := os.ReadFile("./public/getConfig.json") 414 576 router.GET("/xrpc/app.bsky.unspecced.getConfig", func(c *gin.Context) { 415 577 c.DataFromReader(200, -1, "application/json", ··· 442 604 }(clientUUID) 443 605 } 444 606 }) 445 - router.Run(":7152") 607 + r_unsafe.Run(":7152") 446 608 } 447 609 448 610 func getPostThreadV2(w http.ResponseWriter, r *http.Request) {
+49
utils.go
··· 1 + package main 2 + 3 + import ( 4 + "encoding/json" 5 + "fmt" 6 + "net/http" 7 + "strings" 8 + ) 9 + 10 + type DIDDocument struct { 11 + ID string `json:"id"` 12 + Service []struct { 13 + ID string `json:"id"` 14 + Type string `json:"type"` 15 + ServiceEndpoint string `json:"serviceEndpoint"` 16 + } `json:"service"` 17 + } 18 + 19 + func ResolveDID(did string) (*DIDDocument, error) { 20 + var url string 21 + 22 + if strings.HasPrefix(did, "did:plc:") { 23 + // Resolve via PLC Directory 24 + url = "https://plc.directory/" + did 25 + } else if strings.HasPrefix(did, "did:web:") { 26 + // Resolve via Web (simplified) 27 + domain := strings.TrimPrefix(did, "did:web:") 28 + url = "https://" + domain + "/.well-known/did.json" 29 + } else { 30 + return nil, fmt.Errorf("unsupported DID format: %s", did) 31 + } 32 + 33 + resp, err := http.Get(url) 34 + if err != nil { 35 + return nil, err 36 + } 37 + defer resp.Body.Close() 38 + 39 + if resp.StatusCode != http.StatusOK { 40 + return nil, fmt.Errorf("resolver returned status: %d", resp.StatusCode) 41 + } 42 + 43 + var doc DIDDocument 44 + if err := json.NewDecoder(resp.Body).Decode(&doc); err != nil { 45 + return nil, err 46 + } 47 + 48 + return &doc, nil 49 + }