cart
Build and Publish / BuildAndDeployAmd64 (push) Failing after 3s
Build and Publish / BuildAndDeployArm64 (push) Failing after 5s

This commit is contained in:
2026-07-05 18:09:55 +02:00
parent e2f835c01b
commit 781c1bd171
3 changed files with 79 additions and 21 deletions
+5 -3
View File
@@ -58,9 +58,10 @@ type Config struct {
// App is the commerce admin control plane. Construct with New, register its // App is the commerce admin control plane. Construct with New, register its
// HTTP surface with RegisterRoutes, run background work with Start. // HTTP surface with RegisterRoutes, run background work with Start.
type App struct { type App struct {
fs *FileServer fs *FileServer
hub *Hub hub *Hub
cs *CustomerServer cs *CustomerServer
OnVouchersChange func(codes []string)
} }
// New constructs the commerce admin: it opens the cart/checkout disk event-log // New constructs the commerce admin: it opens the cart/checkout disk event-log
@@ -171,6 +172,7 @@ func New(cfg Config) (*App, error) {
// apply auth or CORS — the composing host (or standalone main) wraps the mux // apply auth or CORS — the composing host (or standalone main) wraps the mux
// with those. // with those.
func (a *App) RegisterRoutes(mux *http.ServeMux) { func (a *App) RegisterRoutes(mux *http.ServeMux) {
a.fs.OnVouchersChange = a.OnVouchersChange
mux.HandleFunc("GET /carts", a.fs.CartsHandler) mux.HandleFunc("GET /carts", a.fs.CartsHandler)
mux.HandleFunc("GET /cart/{id}", a.fs.CartHandler) mux.HandleFunc("GET /cart/{id}", a.fs.CartHandler)
mux.HandleFunc("GET /checkouts", a.fs.CheckoutsHandler) mux.HandleFunc("GET /checkouts", a.fs.CheckoutsHandler)
+32 -9
View File
@@ -24,11 +24,12 @@ import (
type FileServer struct { type FileServer struct {
// Define fields here // Define fields here
dataDir string dataDir string
checkoutDataDir string checkoutDataDir string
promotionsFile string promotionsFile string
storage actor.LogStorage[cart.CartGrain] storage actor.LogStorage[cart.CartGrain]
checkoutStorage actor.LogStorage[checkout.CheckoutGrain] checkoutStorage actor.LogStorage[checkout.CheckoutGrain]
OnVouchersChange func(codes []string)
} }
func NewFileServer(dataDir string, checkoutDataDir string, promotionsFile string, storage actor.LogStorage[cart.CartGrain], checkoutStorage actor.LogStorage[checkout.CheckoutGrain]) *FileServer { func NewFileServer(dataDir string, checkoutDataDir string, promotionsFile string, storage actor.LogStorage[cart.CartGrain], checkoutStorage actor.LogStorage[checkout.CheckoutGrain]) *FileServer {
@@ -276,15 +277,37 @@ func (fs *FileServer) VoucherHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
_ = os.MkdirAll(filepath.Dir(fileName), 0755) body, err := io.ReadAll(r.Body)
file, err := os.Create(fileName)
if err != nil { if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()}) writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return return
} }
defer file.Close()
io.Copy(file, r.Body) var parsed struct {
State struct {
Vouchers []struct {
Code string `json:"code"`
} `json:"vouchers"`
} `json:"state"`
}
var codes []string
if err := json.Unmarshal(body, &parsed); err == nil {
for _, v := range parsed.State.Vouchers {
if v.Code != "" {
codes = append(codes, v.Code)
}
}
}
_ = os.MkdirAll(filepath.Dir(fileName), 0755)
if err := os.WriteFile(fileName, body, 0644); err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
return
}
if fs.OnVouchersChange != nil {
fs.OnVouchersChange(codes)
}
return return
} }
+42 -9
View File
@@ -18,6 +18,7 @@ type Store struct {
path string path string
version int version int
promotions []PromotionRule promotions []PromotionRule
OnChange func(id string, isDelete bool)
} }
// NewStore loads the state file at path (an absent file yields an empty store) // NewStore loads the state file at path (an absent file yields an empty store)
@@ -72,40 +73,72 @@ func (s *Store) Upsert(rule PromotionRule) (replaced bool, err error) {
if rule.ID == "" { if rule.ID == "" {
return false, fmt.Errorf("promotion id is required") return false, fmt.Errorf("promotion id is required")
} }
var cb func(string, bool)
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer func() {
s.mu.Unlock()
if err == nil && cb != nil {
cb(rule.ID, false)
}
}()
cb = s.OnChange
for i := range s.promotions { for i := range s.promotions {
if s.promotions[i].ID == rule.ID { if s.promotions[i].ID == rule.ID {
s.promotions[i] = rule s.promotions[i] = rule
return true, s.saveLocked() replaced = true
err = s.saveLocked()
return
} }
} }
s.promotions = append(s.promotions, rule) s.promotions = append(s.promotions, rule)
return false, s.saveLocked() replaced = false
err = s.saveLocked()
return
} }
// SetStatus changes the status of a rule and persists. Returns false if no rule // SetStatus changes the status of a rule and persists. Returns false if no rule
// with the id exists. // with the id exists.
func (s *Store) SetStatus(id string, status PromotionStatus) (bool, error) { func (s *Store) SetStatus(id string, status PromotionStatus) (found bool, err error) {
var cb func(string, bool)
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer func() {
s.mu.Unlock()
if err == nil && found && cb != nil {
cb(id, false)
}
}()
cb = s.OnChange
for i := range s.promotions { for i := range s.promotions {
if s.promotions[i].ID == id { if s.promotions[i].ID == id {
s.promotions[i].Status = status s.promotions[i].Status = status
return true, s.saveLocked() found = true
err = s.saveLocked()
return
} }
} }
return false, nil return false, nil
} }
// Delete removes the rule with the id and persists. Returns false if not found. // Delete removes the rule with the id and persists. Returns false if not found.
func (s *Store) Delete(id string) (bool, error) { func (s *Store) Delete(id string) (found bool, err error) {
var cb func(string, bool)
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer func() {
s.mu.Unlock()
if err == nil && found && cb != nil {
cb(id, true)
}
}()
cb = s.OnChange
for i := range s.promotions { for i := range s.promotions {
if s.promotions[i].ID == id { if s.promotions[i].ID == id {
s.promotions = append(s.promotions[:i], s.promotions[i+1:]...) s.promotions = append(s.promotions[:i], s.promotions[i+1:]...)
return true, s.saveLocked() found = true
err = s.saveLocked()
return
} }
} }
return false, nil return false, nil