fix(fleet): resolve remote node capability detection failures (#388)

* fix(licensing): backward-compatible tier/variant enforcement and self-healing variant detection

Accept legacy tier ('pro') and variant ('personal', 'team') names from older
remote nodes, normalizing them to current values ('paid', 'skipper', 'admiral')
in authMiddleware. This fixes distributed license enforcement failing between
v0.38.3 and v0.38.0 nodes due to the tier rename in v0.38.1.

Also fixes:
- Self-healing getVariant() that cross-checks stored variant_type against
  product/variant name metadata on every call, correcting stale cached values
  from previous buggy resolution logic
- Unguarded API responses in ResourcesView causing potential t.map crashes
- Fleet update status now polls on a 120s interval (was only fetched on mount)
- fetchRemoteMeta failures now logged for diagnosability

* fix(fleet): resolve remote node capability detection failures

Exempt /api/meta and /api/health from the global rate limiter so
capability fetches are never blocked by proxied traffic. Add
backend-side caching (3-min TTL) with stale-while-revalidate to
absorb transient failures. Shorten frontend failure cache to 30s
for faster recovery. Evict meta cache on node deletion.
This commit is contained in:
Anso
2026-04-05 22:46:47 -04:00
committed by GitHub
parent 2bc9ed8273
commit dee7c6685b
3 changed files with 37 additions and 8 deletions
+7 -2
View File
@@ -32,7 +32,8 @@ interface NodeContextType {
refreshNodeMeta: (nodeId: number) => Promise<void>;
}
const META_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
const META_CACHE_TTL = 5 * 60 * 1000;
const META_FAILURE_TTL = 30 * 1000;
const NodeContext = createContext<NodeContextType | undefined>(undefined);
@@ -50,7 +51,11 @@ export function NodeProvider({ children }: { children: React.ReactNode }) {
const fetchNodeMeta = useCallback(async (nodeId: number) => {
const cached = nodeMetaRef.current.get(nodeId);
if (cached && Date.now() - cached.fetchedAt < META_CACHE_TTL) return;
if (cached) {
// Use shorter TTL for failed fetches so we retry quickly after transient errors
const ttl = cached.capabilities.length > 0 ? META_CACHE_TTL : META_FAILURE_TTL;
if (Date.now() - cached.fetchedAt < ttl) return;
}
try {
const res = await apiFetch(`/nodes/${nodeId}/meta`, { localOnly: true });