Add eBay Browse API integration with daily call quota

eBay marketplaces are now polled through eBay's official Buy > Browse API (client-credentials OAuth2) instead of an Apify scraper actor; Apify still handles Yahoo JP and Mercari. Browse API calls are tracked per day in a new ebay_api_usage table and capped (default 5000, configurable) on eBay's Pacific-time reset clock, so polling halts before the limit is hit. Credentials live in config.toml [ebay] and are overridable via /settings, which also surfaces the day's running call count.

Also carries the server.secure_cookies config plumbing (field, accessor, example) consumed by the following commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-14 12:10:39 -07:00
parent cfa01bd4ef
commit 1ae2c50b9a
12 changed files with 1092 additions and 262 deletions

View File

@@ -213,6 +213,60 @@ func (s *Store) SetSetting(ctx context.Context, key, value string) error {
return err
}
// ============ ebay api usage ============
// ebayResetLoc is the timezone eBay's API rate limits reset in: midnight
// Pacific (it observes US DST). If the zone database is somehow unavailable
// we fall back to UTC rather than failing — main.go embeds time/tzdata so in
// practice the lookup always succeeds.
var ebayResetLoc = func() *time.Location {
loc, err := time.LoadLocation("America/Los_Angeles")
if err != nil {
return time.UTC
}
return loc
}()
// ebayUsageDay returns the date key used to bucket eBay API calls, aligned to
// eBay's own quota reset (midnight US Pacific).
func ebayUsageDay() string {
return time.Now().In(ebayResetLoc).Format("2006-01-02")
}
// EbayUsageToday returns the number of eBay Browse API calls recorded for the
// current UTC day. A missing row counts as zero.
func (s *Store) EbayUsageToday(ctx context.Context) (int, error) {
var n int
err := s.DB.QueryRowContext(ctx,
`SELECT call_count FROM ebay_api_usage WHERE usage_date = ?`, ebayUsageDay()).Scan(&n)
if errors.Is(err, sql.ErrNoRows) {
return 0, nil
}
if err != nil {
return 0, err
}
return n, nil
}
// IncrementEbayUsage records one eBay Browse API call against the current UTC
// day and returns the new running total.
func (s *Store) IncrementEbayUsage(ctx context.Context) (int, error) {
day := ebayUsageDay()
_, err := s.DB.ExecContext(ctx, `
INSERT INTO ebay_api_usage (usage_date, call_count) VALUES (?, 1)
ON CONFLICT(usage_date) DO UPDATE SET call_count = call_count + 1
`, day)
if err != nil {
return 0, err
}
var n int
if err := s.DB.QueryRowContext(ctx,
`SELECT call_count FROM ebay_api_usage WHERE usage_date = ?`, day).Scan(&n); err != nil {
return 0, err
}
return n, nil
}
// ============ items ============
func (s *Store) CreateItem(ctx context.Context, it *models.Item) (int64, error) {