package store import "fmt" // SyncProjectWorkload upserts the Workload row paired with a project so that // its name, notification config, and webhook secrets stay in sync. Called from // CreateProject / UpdateProject / SetProject*Secret paths. Idempotent — safe // to call when a workload row already exists for the (project, RefID) pair. func (s *Store) SyncProjectWorkload(p Project) error { existing, err := s.GetWorkloadByRef(WorkloadKindProject, p.ID) if err == nil { existing.Name = p.Name existing.NotificationURL = p.NotificationURL existing.NotificationSecret = p.NotificationSecret existing.WebhookSecret = p.WebhookSecret existing.WebhookSigningSecret = p.WebhookSigningSecret existing.WebhookRequireSignature = p.WebhookRequireSignature return s.UpdateWorkload(existing) } _, err = s.CreateWorkload(Workload{ Kind: string(WorkloadKindProject), RefID: p.ID, Name: p.Name, NotificationURL: p.NotificationURL, NotificationSecret: p.NotificationSecret, WebhookSecret: p.WebhookSecret, WebhookSigningSecret: p.WebhookSigningSecret, WebhookRequireSignature: p.WebhookRequireSignature, }) if err != nil { return fmt.Errorf("create project workload: %w", err) } return nil } // SyncStackWorkload upserts the Workload row paired with a stack. Stacks // don't (yet) carry their own notification or webhook config — those fields // stay empty on the workload row until the stack model gains them. func (s *Store) SyncStackWorkload(st Stack) error { existing, err := s.GetWorkloadByRef(WorkloadKindStack, st.ID) if err == nil { existing.Name = st.Name return s.UpdateWorkload(existing) } _, err = s.CreateWorkload(Workload{ Kind: string(WorkloadKindStack), RefID: st.ID, Name: st.Name, }) if err != nil { return fmt.Errorf("create stack workload: %w", err) } return nil } // SyncStaticSiteWorkload upserts the Workload row paired with a static site. func (s *Store) SyncStaticSiteWorkload(site StaticSite) error { existing, err := s.GetWorkloadByRef(WorkloadKindSite, site.ID) if err == nil { existing.Name = site.Name existing.NotificationURL = site.NotificationURL existing.NotificationSecret = site.NotificationSecret existing.WebhookSecret = site.WebhookSecret existing.WebhookSigningSecret = site.WebhookSigningSecret existing.WebhookRequireSignature = site.WebhookRequireSignature return s.UpdateWorkload(existing) } _, err = s.CreateWorkload(Workload{ Kind: string(WorkloadKindSite), RefID: site.ID, Name: site.Name, NotificationURL: site.NotificationURL, NotificationSecret: site.NotificationSecret, WebhookSecret: site.WebhookSecret, WebhookSigningSecret: site.WebhookSigningSecret, WebhookRequireSignature: site.WebhookRequireSignature, }) if err != nil { return fmt.Errorf("create static site workload: %w", err) } return nil } // BackfillWorkloads scans every project / stack / static_site row and ensures // each has a matching workload row. Called once at boot before HTTP starts so // any pre-Workload-refactor data is upgraded transparently. Idempotent. func (s *Store) BackfillWorkloads() error { projects, err := s.GetAllProjects() if err != nil { return fmt.Errorf("backfill: list projects: %w", err) } for _, p := range projects { if err := s.SyncProjectWorkload(p); err != nil { return fmt.Errorf("backfill project %s: %w", p.ID, err) } } stacks, err := s.GetAllStacks() if err != nil { return fmt.Errorf("backfill: list stacks: %w", err) } for _, st := range stacks { if err := s.SyncStackWorkload(st); err != nil { return fmt.Errorf("backfill stack %s: %w", st.ID, err) } } sites, err := s.GetAllStaticSites() if err != nil { return fmt.Errorf("backfill: list static sites: %w", err) } for _, site := range sites { if err := s.SyncStaticSiteWorkload(site); err != nil { return fmt.Errorf("backfill static site %s: %w", site.ID, err) } } return nil }