fix: add 3s timeout to sync link preview fetch, async fallback

This commit is contained in:
Patrick Britton 2026-02-08 14:33:48 -06:00
parent 46bf51a6c4
commit 628bdf3c40

View file

@ -575,15 +575,20 @@ func (h *PostHandler) CreatePost(c *gin.Context) {
h.moderationService.LogAIDecision(c.Request.Context(), "post", post.ID, userID, req.Body, scores, nil, decision, flagReason, orDecision, nil) h.moderationService.LogAIDecision(c.Request.Context(), "post", post.ID, userID, req.Body, scores, nil, decision, flagReason, orDecision, nil)
} }
// Auto-extract link preview from post body (synchronous so it's in the response) // Auto-extract link preview from post body
// Try synchronously with a short timeout so the response isn't blocked.
// If it times out, fall back to async so it's saved for later views.
if h.linkPreviewService != nil { if h.linkPreviewService != nil {
linkURL := services.ExtractFirstURL(req.Body) linkURL := services.ExtractFirstURL(req.Body)
if linkURL != "" { if linkURL != "" {
// Check if author is an official account (trusted = skip safety checks)
var isOfficial bool var isOfficial bool
_ = h.postRepo.Pool().QueryRow(c.Request.Context(), `SELECT COALESCE(is_official, false) FROM profiles WHERE id = $1`, userID).Scan(&isOfficial) _ = h.postRepo.Pool().QueryRow(c.Request.Context(), `SELECT COALESCE(is_official, false) FROM profiles WHERE id = $1`, userID).Scan(&isOfficial)
lp, lpErr := h.linkPreviewService.FetchPreview(c.Request.Context(), linkURL, isOfficial) // 3-second budget for synchronous fetch
fastCtx, fastCancel := context.WithTimeout(c.Request.Context(), 3*time.Second)
lp, lpErr := h.linkPreviewService.FetchPreview(fastCtx, linkURL, isOfficial)
fastCancel()
if lpErr == nil && lp != nil { if lpErr == nil && lp != nil {
_ = h.linkPreviewService.SaveLinkPreview(c.Request.Context(), post.ID.String(), lp) _ = h.linkPreviewService.SaveLinkPreview(c.Request.Context(), post.ID.String(), lp)
post.LinkPreviewURL = &lp.URL post.LinkPreviewURL = &lp.URL
@ -591,6 +596,17 @@ func (h *PostHandler) CreatePost(c *gin.Context) {
post.LinkPreviewDescription = &lp.Description post.LinkPreviewDescription = &lp.Description
post.LinkPreviewImageURL = &lp.ImageURL post.LinkPreviewImageURL = &lp.ImageURL
post.LinkPreviewSiteName = &lp.SiteName post.LinkPreviewSiteName = &lp.SiteName
} else if lpErr != nil {
// Timed out or failed — fire async so it's saved for later views
postID := post.ID.String()
go func() {
bgCtx, bgCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer bgCancel()
bgLp, bgErr := h.linkPreviewService.FetchPreview(bgCtx, linkURL, isOfficial)
if bgErr == nil && bgLp != nil {
_ = h.linkPreviewService.SaveLinkPreview(bgCtx, postID, bgLp)
}
}()
} }
} }
} }