API-Keys: Source-URL, einheitliches Status-Schema, passive Usage-Erfassung, Usage-Bars

- SourceUrl-Feld in api_keys: GitHub-URL bei adoptierten Scan-Keys gespeichert
- HandleAdopt: Name = user/repo (aus URL extrahiert), SourceUrl = vollständige URL
- Status-Schema vereinheitlicht: überall 'valid' statt 'ok'/'usage_limited'
- AnthropicKeyChecker.ExtractMetaFromHeaders(): passive Header-Auswertung
- AnthropicService: TryUpdateKeyMeta() nach jedem Chat-Request (fire-and-forget)
- DatabaseService: UpdateApiKeyMetaByToken() für passive Updates per Token
- Frontend: formatKeyStatus() zentrale Status-Labels für API-Keys + Scanned-Keys
- Frontend: createUsageBadge() zweizeiliger Balken (Sitzung/Woche) mit Farbstufen
- CSS: Usage-Badge mit Track/Fill, warn (>=70%) und danger (>=90%) Klassen
This commit is contained in:
Meleki committed 2026-03-12 18:33:37 +01:00
1 parent 608118a823
commit de388806cb
8 files changed
+226 -32

No files matched your search

+40
View File
@@ -122,6 +122,46 @@ namespace Gott.Services
}
}
/// <summary>
/// Extrahiert Plan/Usage-Metadaten nur aus Response-Headern (kein eigener HTTP-Request).
/// Kann null zurückgeben wenn keine Usage-Header vorhanden sind.
/// </summary>
public static KeyCheckResult ExtractMetaFromHeaders(HttpResponseHeaders headers)
{
var d7Util = GetHeader(headers, "anthropic-ratelimit-unified-7d-utilization", "unified-7d-utilization");
var d5hUtil = GetHeader(headers, "anthropic-ratelimit-unified-5h-utilization", "unified-5h-utilization");
if (d7Util == null && d5hUtil == null)
return null; // Keine Usage-Header → nichts zu updaten
var overageUtil = GetHeader(headers, "anthropic-ratelimit-unified-overage-utilization", "unified-overage-utilization");
var fallbackPct = GetHeader(headers, "anthropic-ratelimit-unified-fallback-percentage", "unified-fallback-percentage");
string plan;
if (overageUtil != null && double.TryParse(overageUtil, NumberStyles.Float, CultureInfo.InvariantCulture, out var ov) && ov > 0)
plan = "Max";
else if (fallbackPct == null)
plan = "Pro";
else
plan = "Pro/Max";
var result = new KeyCheckResult
{
Status = "valid",
Plan = plan,
IsSubscription = true
};
if (d7Util != null && double.TryParse(d7Util, NumberStyles.Float, CultureInfo.InvariantCulture, out var d7))
result.Usage7d = Math.Round(d7 * 100, 1);
if (d5hUtil != null && double.TryParse(d5hUtil, NumberStyles.Float, CultureInfo.InvariantCulture, out var d5))
result.Usage5h = Math.Round(d5 * 100, 1);
if (overageUtil != null && double.TryParse(overageUtil, NumberStyles.Float, CultureInfo.InvariantCulture, out var ovVal))
result.Overage = Math.Round(ovVal * 100, 1);
return result;
}
private static string GetHeader(HttpResponseHeaders headers, params string[] names)
{
foreach (var name in names)