231 lines
5.8 KiB
Go
231 lines
5.8 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
|
|
"github.com/szabadpalya/api/internal/store/dbgen"
|
|
)
|
|
|
|
// The wire shapes below are what the frontend already expected from the
|
|
// previous stack, so the view layer never learned about the schema and does
|
|
// not have to now.
|
|
|
|
type Creator struct {
|
|
Name string `json:"name"`
|
|
Initials string `json:"initials"`
|
|
Location string `json:"location"`
|
|
Blurb string `json:"blurb"`
|
|
// Empty when the owner has no photo. The client falls back to Initials,
|
|
// so this is omitted from the payload rather than sent as "".
|
|
AvatarURL string `json:"avatarUrl,omitempty"`
|
|
}
|
|
|
|
type StorySection struct {
|
|
Heading string `json:"heading"`
|
|
Body []string `json:"body"`
|
|
}
|
|
|
|
type Tier struct {
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
Price int `json:"price"`
|
|
ListPrice *int `json:"listPrice,omitempty"`
|
|
Title string `json:"title"`
|
|
Includes []string `json:"includes"`
|
|
Backers int `json:"backers"`
|
|
InventoryLimit *int `json:"inventoryLimit,omitempty"`
|
|
EstimatedDelivery string `json:"estimatedDelivery,omitempty"`
|
|
Featured bool `json:"featured"`
|
|
}
|
|
|
|
type CampaignSummary struct {
|
|
Slug string `json:"slug"`
|
|
Title string `json:"title"`
|
|
Category string `json:"category"`
|
|
Summary string `json:"summary"`
|
|
Art string `json:"art"`
|
|
Creator Creator `json:"creator"`
|
|
Goal int `json:"goal"`
|
|
Raised int `json:"raised"`
|
|
Backers int `json:"backers"`
|
|
EndsAt string `json:"endsAt"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type Campaign struct {
|
|
CampaignSummary
|
|
Story []StorySection `json:"story"`
|
|
Tiers []Tier `json:"tiers"`
|
|
Updates int `json:"updates"`
|
|
Comments int `json:"comments"`
|
|
}
|
|
|
|
var ErrNotFound = errors.New("not found")
|
|
|
|
// Only these statuses are visible without an admin session. A draft campaign
|
|
// must 404 for the public exactly as a non-existent one does.
|
|
var publicStatuses = []string{"AKTIV", "FINANSZIROZOTT", "LEZART"}
|
|
|
|
// A pledge counts towards the total once the card is authorized; released and
|
|
// refunded pledges drop back out. The raised amount and the backer count are
|
|
// ALWAYS derived from these rows and stored nowhere.
|
|
var countedPledges = []string{"AUTHORIZED", "CAPTURED"}
|
|
|
|
var statusOut = map[string]string{
|
|
"AKTIV": "aktiv",
|
|
"FINANSZIROZOTT": "finanszirozott",
|
|
"LEZART": "lezart",
|
|
}
|
|
|
|
var tierTypeOut = map[string]string{
|
|
"DONATION": "donation",
|
|
"DIGITAL": "digital",
|
|
"PHYSICAL": "physical",
|
|
}
|
|
|
|
func outStatus(s string) string {
|
|
if v, ok := statusOut[s]; ok {
|
|
return v
|
|
}
|
|
return "aktiv"
|
|
}
|
|
|
|
func outTierType(s string) string {
|
|
if v, ok := tierTypeOut[s]; ok {
|
|
return v
|
|
}
|
|
return "donation"
|
|
}
|
|
|
|
func (s *Store) ListCampaigns(ctx context.Context) ([]CampaignSummary, error) {
|
|
rows, err := s.q.ListCampaigns(ctx, dbgen.ListCampaignsParams{
|
|
Counted: countedPledges,
|
|
Public: publicStatuses,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out := make([]CampaignSummary, 0, len(rows))
|
|
for _, r := range rows {
|
|
out = append(out, CampaignSummary{
|
|
Slug: r.Slug,
|
|
Title: r.Title,
|
|
Category: r.Category,
|
|
Summary: r.Summary,
|
|
Art: r.Art,
|
|
Creator: Creator{
|
|
Name: r.Name,
|
|
Initials: r.Initials,
|
|
Location: r.Location,
|
|
Blurb: r.Blurb,
|
|
AvatarURL: r.AvatarUrl,
|
|
},
|
|
Goal: r.GoalAmount,
|
|
EndsAt: s.Day(r.EndsAt),
|
|
Status: outStatus(r.Status),
|
|
Raised: r.Raised,
|
|
Backers: r.Backers,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *Store) GetCampaignBySlug(ctx context.Context, slug string) (*Campaign, error) {
|
|
r, err := s.q.GetCampaignBySlug(ctx, dbgen.GetCampaignBySlugParams{
|
|
Counted: countedPledges,
|
|
Slug: slug,
|
|
Public: publicStatuses,
|
|
})
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, ErrNotFound
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c := &Campaign{
|
|
CampaignSummary: CampaignSummary{
|
|
Slug: r.Slug,
|
|
Title: r.Title,
|
|
Category: r.Category,
|
|
Summary: r.Summary,
|
|
Art: r.Art,
|
|
Creator: Creator{
|
|
Name: r.Name,
|
|
Initials: r.Initials,
|
|
Location: r.Location,
|
|
Blurb: r.Blurb,
|
|
AvatarURL: r.AvatarUrl,
|
|
},
|
|
Goal: r.GoalAmount,
|
|
EndsAt: s.Day(r.EndsAt),
|
|
Status: outStatus(r.Status),
|
|
Raised: r.Raised,
|
|
Backers: r.Backers,
|
|
},
|
|
Updates: r.Updates,
|
|
Comments: r.Comments,
|
|
}
|
|
|
|
if c.Story, err = s.storySections(ctx, r.ID); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tiers, err := s.q.ListTiers(ctx, dbgen.ListTiersParams{
|
|
CampaignID: r.ID,
|
|
Counted: countedPledges,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c.Tiers = make([]Tier, 0, len(tiers))
|
|
for _, t := range tiers {
|
|
tier := Tier{
|
|
ID: t.ID,
|
|
Type: outTierType(t.Type),
|
|
Price: t.Price,
|
|
ListPrice: t.ListPrice,
|
|
Title: t.Title,
|
|
Includes: t.Includes,
|
|
InventoryLimit: t.InventoryLimit,
|
|
Featured: t.Featured,
|
|
Backers: t.Backers,
|
|
}
|
|
if t.EstimatedDelivery != nil {
|
|
tier.EstimatedDelivery = s.Day(*t.EstimatedDelivery)
|
|
}
|
|
c.Tiers = append(c.Tiers, tier)
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func (s *Store) storySections(ctx context.Context, campaignID string) ([]StorySection, error) {
|
|
rows, err := s.q.ListStorySections(ctx, campaignID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]StorySection, 0, len(rows))
|
|
for _, r := range rows {
|
|
out = append(out, StorySection{Heading: r.Heading, Body: r.Body})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
type PlatformStats struct {
|
|
ActiveCount int `json:"activeCount"`
|
|
Backers int `json:"backers"`
|
|
}
|
|
|
|
func (s *Store) PlatformStats(ctx context.Context) (PlatformStats, error) {
|
|
r, err := s.q.PlatformStats(ctx, countedPledges)
|
|
if err != nil {
|
|
return PlatformStats{}, err
|
|
}
|
|
return PlatformStats{ActiveCount: r.ActiveCount, Backers: r.Backers}, nil
|
|
}
|