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

@@ -8,15 +8,24 @@ import (
type SettingsData struct {
Page
Values map[string]string
IsAdmin bool
Users []models.User
TestNtfyOK string
TestApifyOK string
PasswordMsg string
PasswordError string
UserMsg string
UserError string
Values map[string]string
IsAdmin bool
Users []models.User
TestNtfyOK string
TestApifyOK string
TestEbayOK string
EbayUsedToday int
EbayDailyLimit int
PasswordMsg string
PasswordError string
UserMsg string
UserError string
}
// EbayLimitReached reports whether eBay polling is currently halted because
// the daily call limit has been hit.
func (d SettingsData) EbayLimitReached() bool {
return d.EbayDailyLimit > 0 && d.EbayUsedToday >= d.EbayDailyLimit
}
templ settingsBody(d SettingsData) {
@@ -24,14 +33,37 @@ templ settingsBody(d SettingsData) {
<h1 class="text-3xl font-semibold">Settings</h1>
<section class="v-card p-6">
<h2 class="font-semibold mb-4">Apify and Ntfy</h2>
<h2 class="font-semibold mb-4">Apify, eBay and Ntfy</h2>
<form method="post" action="/settings" class="space-y-4">
@CSRFInput(d.CSRFToken)
<div>
<label class="v-label">Apify API Key</label>
<input class="v-input font-mono" type="password" name="apify_api_key" value={ d.Values["apify_api_key"] }/>
</div>
<div class="border-t border-white/10 pt-4">
<label class="v-label">eBay App ID (Client ID)</label>
<input class="v-input font-mono" type="password" name="ebay_client_id" value={ d.Values["ebay_client_id"] } placeholder="used for eBay marketplaces instead of Apify"/>
</div>
<div>
<label class="v-label">eBay Cert ID (Client Secret)</label>
<input class="v-input font-mono" type="password" name="ebay_client_secret" value={ d.Values["ebay_client_secret"] }/>
</div>
<div>
<label class="v-label">eBay Daily Call Limit</label>
<input class="v-input font-mono" name="ebay_daily_call_limit" type="number" value={ d.Values["ebay_daily_call_limit"] } placeholder="5000 (blank uses config default)"/>
</div>
<div class="text-sm">
<span class="v-muted">eBay API calls today:</span>
if d.EbayDailyLimit > 0 {
<span class="font-mono">{ fmt.Sprintf("%d / %d", d.EbayUsedToday, d.EbayDailyLimit) }</span>
} else {
<span class="font-mono">{ fmt.Sprintf("%d (uncapped)", d.EbayUsedToday) }</span>
}
if d.EbayLimitReached() {
<span class="v-flash-error inline-block ml-2">Limit reached. eBay polling halted until the next reset (midnight US Pacific).</span>
}
</div>
<div class="border-t border-white/10 pt-4">
<label class="v-label">Ntfy Base URL</label>
<input class="v-input" name="ntfy_base_url" value={ d.Values["ntfy_base_url"] }/>
</div>
@@ -58,6 +90,7 @@ templ settingsBody(d SettingsData) {
<button class="v-btn" type="submit">Save</button>
<button class="v-btn-ghost" type="submit" formaction="/settings/test-ntfy">Test Ntfy</button>
<button class="v-btn-ghost" type="submit" formaction="/settings/test-apify">Test Apify</button>
<button class="v-btn-ghost" type="submit" formaction="/settings/test-ebay">Test eBay</button>
</div>
}
</form>
@@ -67,6 +100,9 @@ templ settingsBody(d SettingsData) {
if d.TestApifyOK != "" {
<div class="v-flash mt-3">{ d.TestApifyOK }</div>
}
if d.TestEbayOK != "" {
<div class="v-flash mt-3">{ d.TestEbayOK }</div>
}
</section>
<section class="v-card p-6">