From 301eae7bcb4683c20618addd14cde1b9c0cb60b7 Mon Sep 17 00:00:00 2001 From: Bretton Date: Sat, 8 Nov 2025 23:21:04 -0800 Subject: [PATCH] feat: integrate unfurl and blob services into post creation flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire unfurl and blob services into the post creation pipeline, enabling automatic enhancement of external embeds with rich metadata and thumbnails. **Post Service Integration:** - Add optional BlobService and UnfurlService dependencies - Update constructor to accept blob/unfurl services (nil-safe) - Add ThumbnailURL field to CreatePostRequest for client-provided URLs - Add PDSURL to CommunityRef for blob URL transformation (internal only) **Server Main Changes:** - Initialize unfurl repository with PostgreSQL - Initialize blob service with default PDS URL - Initialize unfurl service with: - 10s timeout for HTTP fetches - 24h cache TTL - CovesBot/1.0 user agent - Pass blob and unfurl services to post service constructor **Flow:** ``` Client POST → CreateHandler ↓ PostService.Create() [external embed detected] ↓ (if no thumb provided) UnfurlService.UnfurlURL() [fetch oEmbed/OpenGraph] ↓ (cache miss) HTTP fetch → oEmbed provider / HTML parser ↓ (thumbnail URL found) BlobService.UploadBlobFromURL() [download & upload to PDS] ↓ com.atproto.repo.uploadBlob → PDS ↓ (returns BlobRef with CID) Embed enriched with thumb blob → Write to PDS ``` **Interface Documentation:** - Added comments explaining optional blob/unfurl service injection - Unfurl service auto-enriches external embeds when provided - Blob service uploads thumbnails from unfurled URLs This is the core integration that enables the full unfurling feature. The actual unfurl logic in posts/service.go will be implemented separately. --- cmd/server/main.go | 19 ++++++++++++++++++- internal/core/posts/interfaces.go | 8 +++++++- internal/core/posts/post.go | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index e590943..b7ddac6 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -7,12 +7,14 @@ import ( "Coves/internal/atproto/identity" "Coves/internal/atproto/jetstream" "Coves/internal/core/aggregators" + "Coves/internal/core/blobs" "Coves/internal/core/comments" "Coves/internal/core/communities" "Coves/internal/core/communityFeeds" "Coves/internal/core/discover" "Coves/internal/core/posts" "Coves/internal/core/timeline" + "Coves/internal/core/unfurl" "Coves/internal/core/users" "bytes" "context" @@ -281,9 +283,24 @@ func main() { aggregatorService := aggregators.NewAggregatorService(aggregatorRepo, communityService) log.Println("✅ Aggregator service initialized") + // Initialize unfurl cache repository + unfurlRepo := unfurl.NewRepository(db) + + // Initialize blob upload service + blobService := blobs.NewBlobService(defaultPDS) + + // Initialize unfurl service with configuration + unfurlService := unfurl.NewService( + unfurlRepo, + unfurl.WithTimeout(10*time.Second), + unfurl.WithUserAgent("CovesBot/1.0 (+https://coves.social)"), + unfurl.WithCacheTTL(24*time.Hour), + ) + log.Println("✅ Unfurl and blob services initialized") + // Initialize post service (with aggregator support) postRepo := postgresRepo.NewPostRepository(db) - postService := posts.NewPostService(postRepo, communityService, aggregatorService, defaultPDS) + postService := posts.NewPostService(postRepo, communityService, aggregatorService, blobService, unfurlService, defaultPDS) // Initialize vote repository (used by Jetstream consumer for indexing) voteRepo := postgresRepo.NewVoteRepository(db) diff --git a/internal/core/posts/interfaces.go b/internal/core/posts/interfaces.go index df9091a..2a07ca5 100644 --- a/internal/core/posts/interfaces.go +++ b/internal/core/posts/interfaces.go @@ -1,6 +1,12 @@ package posts -import "context" +import ( + "context" +) + +// Service constructor accepts optional blobs.Service and unfurl.Service for embed enhancement. +// When unfurlService is provided, external embeds will be automatically enriched with metadata. +// When blobService is provided, thumbnails from unfurled URLs will be uploaded as blobs. // Service defines the business logic interface for posts // Coordinates between Repository, community service, and PDS diff --git a/internal/core/posts/post.go b/internal/core/posts/post.go index 10f8ec8..51fa4d9 100644 --- a/internal/core/posts/post.go +++ b/internal/core/posts/post.go @@ -50,6 +50,7 @@ type CreatePostRequest struct { Title *string `json:"title,omitempty"` Content *string `json:"content,omitempty"` Embed map[string]interface{} `json:"embed,omitempty"` + ThumbnailURL *string `json:"thumbnailUrl,omitempty"` Labels *SelfLabels `json:"labels,omitempty"` Community string `json:"community"` AuthorDID string `json:"authorDid"` @@ -121,6 +122,7 @@ type CommunityRef struct { DID string `json:"did"` Handle string `json:"handle"` Name string `json:"name"` + PDSURL string `json:"-"` // Not exposed to API, used for blob URL transformation } // PostStats represents aggregated statistics -- 2.51.2