package httpapi import ( "errors" "fmt" "net/http" "time" "github.com/gin-gonic/gin" "github.com/szabadpalya/api/internal/hu" "github.com/szabadpalya/api/internal/ogimage" "github.com/szabadpalya/api/internal/store" ) // ogImage serves the per-campaign share card. A crawler fetches this once per // share and caches it for a long time, so an hour of staleness is fine and the // header keeps us from redrawing on every Facebook refresh. func (s *Server) ogImage(c *gin.Context) { campaign, err := s.store.GetCampaignBySlug(c.Request.Context(), c.Param("slug")) if errors.Is(err, store.ErrNotFound) { png, err := ogimage.Fallback() if err != nil { s.fail(c, err) return } c.Header("Cache-Control", "public, max-age=300") c.Data(http.StatusOK, "image/png", png) return } if err != nil { s.fail(c, err) return } pct := hu.ProgressPct(campaign.Raised, campaign.Goal) // The bar is scaled so an overfunded campaign still shows its 100% mark // rather than a bar that is simply full. scale := max(100, pct) card := ogimage.Card{ Title: campaign.Title, Category: campaign.Category, Creator: campaign.Creator.Name + " · " + campaign.Creator.Location, Raised: hu.FormatHUF(campaign.Raised), GoalNote: fmt.Sprintf("a %s-os célból", hu.FormatHUF(campaign.Goal)), Percent: fmt.Sprintf("%d%%", pct), Fraction: float64(min(pct, 100)) / float64(scale), Backers: hu.FormatNumber(campaign.Backers) + " támogató", Deadline: deadlineLabel(campaign, s.store.Budapest), } if pct > 100 { card.Over = float64(pct-100) / float64(scale) } png, err := ogimage.Render(card) if err != nil { s.fail(c, err) return } c.Header("Cache-Control", "public, max-age=3600") c.Data(http.StatusOK, "image/png", png) } func deadlineLabel(campaign *store.Campaign, loc *time.Location) string { if campaign.Status == "lezart" { return "lezárult" } return fmt.Sprintf("%d nap van hátra", hu.DaysLeft(campaign.EndsAt, loc, time.Now())) }