szabadpalya/api/internal/httpapi/ogimage.go

69 lines
1.9 KiB
Go

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()))
}