Show OpenWeather API call usage in the app
OpenWeather reports no remaining quota (only 429 when over), so add an in-app gauge that counts the calls PilotVault itself makes and shows them against the plan's per-minute limit. - Plugin: process-wide usage store bucketed per API key (hashed) into the current minute and UTC day; a do() wrapper counts each request that reaches OpenWeather (transport errors consume no quota, so uncounted). Health and Invoke both route through it. - New callsPerMinute config field (default 60) that cascades global -> org -> user; plugin contract gains plugins.HealthUsage on Health. - Web App: a "Calls per minute limit" field and an "API call usage" meter in the OpenWeather card, shown after Test connection and cached. - Counts reset on restart and cover only PilotVault's own calls (noted in the UI); the account dashboard stays authoritative. - Tests for per-key counting and minute/day rollover; rebuilt frontend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
285551fe8a
commit
6d73c89cc8
@@ -29,7 +29,7 @@ const openWeatherPlugin = "openweather"
|
||||
|
||||
// owFields are the plugin's ConfigField keys, in display order. Kept in sync with
|
||||
// the plugin descriptor so the cascade covers every setting.
|
||||
var owFields = []string{"apiKey", "units", "lat", "lon", "lang"}
|
||||
var owFields = []string{"apiKey", "units", "lat", "lon", "lang", "callsPerMinute"}
|
||||
|
||||
// owSecretKeys are masked in every view and preserved on save when left at the mask.
|
||||
var owSecretKeys = map[string]bool{"apiKey": true}
|
||||
@@ -37,11 +37,12 @@ var owSecretKeys = map[string]bool{"apiKey": true}
|
||||
// owConfig is one layer's openweather settings. Values are strings to match the
|
||||
// plugin's ConfigField keys 1:1 (they are handed straight to plugins.Init).
|
||||
type owConfig struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
Units string `json:"units"`
|
||||
Lat string `json:"lat"`
|
||||
Lon string `json:"lon"`
|
||||
Lang string `json:"lang"`
|
||||
APIKey string `json:"apiKey"`
|
||||
Units string `json:"units"`
|
||||
Lat string `json:"lat"`
|
||||
Lon string `json:"lon"`
|
||||
Lang string `json:"lang"`
|
||||
CallsPerMinute string `json:"callsPerMinute"`
|
||||
}
|
||||
|
||||
// owStored is what we persist per user/org under pluginSettings.openweather.
|
||||
@@ -74,7 +75,7 @@ type owResolution struct {
|
||||
|
||||
// owConfigFromMap builds an owConfig from a flat string map (global plugin config).
|
||||
func owConfigFromMap(m map[string]string) owConfig {
|
||||
return owConfig{APIKey: m["apiKey"], Units: m["units"], Lat: m["lat"], Lon: m["lon"], Lang: m["lang"]}
|
||||
return owConfig{APIKey: m["apiKey"], Units: m["units"], Lat: m["lat"], Lon: m["lon"], Lang: m["lang"], CallsPerMinute: m["callsPerMinute"]}
|
||||
}
|
||||
|
||||
// owGet returns a config field by the plugin's key name.
|
||||
@@ -90,6 +91,8 @@ func owGet(c owConfig, key string) string {
|
||||
return c.Lon
|
||||
case "lang":
|
||||
return c.Lang
|
||||
case "callsPerMinute":
|
||||
return c.CallsPerMinute
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -107,6 +110,8 @@ func owSet(c *owConfig, key, v string) {
|
||||
c.Lon = v
|
||||
case "lang":
|
||||
c.Lang = v
|
||||
case "callsPerMinute":
|
||||
c.CallsPerMinute = v
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user