Multi-Provider Key Scanner + Checker (OpenAI, Google, 15 Key-Typen)
- KeyPatterns.cs: Zentrale Pattern-Klasse für 15 Key-Typen (Anthropic, OpenAI, Google, AWS, GitHub, GitLab, HuggingFace, Stripe, SendGrid, Slack) - OpenAiKeyChecker.cs: Prüft sk-proj/sk-svcacct/sk- Keys via /v1/models (Org, Modelle, RPM/TPM, Tier-Erkennung Free bis 5+) - GoogleKeyChecker.cs: Prüft AIza-Keys via Gemini /v1/models (Gemini-Modelle, Maps-Key-Erkennung) - KeyScannerService + GitHubEventScannerService: Dispatch nach Key-Typ - ApiKeysEndpoint: GET /scanned/summary + ?type= Filter - Frontend: 2-stufige UI (Typ-Übersicht → Keys pro Typ)
This commit is contained in:
1 parent
eb8844a4f1
commit
c8859a89d0
9 files changed
+536
-52
No files matched your search
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Gott.Services
|
||||
{
|
||||
public class OpenAiCheckResult
|
||||
{
|
||||
public string Status { get; set; } // "ok", "invalid", "rate_limited", "quota_exceeded", "error"
|
||||
public string Message { get; set; }
|
||||
public string Organization { get; set; }
|
||||
public string[] Models { get; set; }
|
||||
public int? RateLimitRpm { get; set; }
|
||||
public int? RateLimitTpm { get; set; }
|
||||
}
|
||||
|
||||
public static class OpenAiKeyChecker
|
||||
{
|
||||
public static async Task<OpenAiCheckResult> CheckKeyAsync(IHttpClientFactory httpFactory, string apiKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = httpFactory.CreateClient();
|
||||
client.Timeout = TimeSpan.FromSeconds(30);
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.openai.com/v1/models");
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
|
||||
|
||||
var resp = await client.SendAsync(request);
|
||||
var statusCode = (int)resp.StatusCode;
|
||||
|
||||
if (statusCode == 401)
|
||||
return new OpenAiCheckResult { Status = "invalid", Message = "Ungültig oder abgelaufen (401)" };
|
||||
|
||||
if (statusCode == 403)
|
||||
return new OpenAiCheckResult { Status = "quota_exceeded", Message = "Keine Berechtigung / Quota erschöpft (403)" };
|
||||
|
||||
if (statusCode == 429)
|
||||
{
|
||||
// Rate-Limited → Key existiert, ist aber gerade nicht nutzbar
|
||||
return new OpenAiCheckResult { Status = "rate_limited", Message = "Rate Limited (429)" };
|
||||
}
|
||||
|
||||
if (statusCode >= 400)
|
||||
return new OpenAiCheckResult { Status = "error", Message = "API-Fehler (" + statusCode + ")" };
|
||||
|
||||
// 200 OK → Key ist gültig
|
||||
var body = await resp.Content.ReadAsStringAsync();
|
||||
var doc = JsonDocument.Parse(body);
|
||||
|
||||
// Organisation aus Header
|
||||
string org = null;
|
||||
if (resp.Headers.TryGetValues("openai-organization", out var orgValues))
|
||||
org = orgValues.FirstOrDefault();
|
||||
|
||||
// Rate-Limits aus Headern
|
||||
int? rpm = null;
|
||||
int? tpm = null;
|
||||
if (resp.Headers.TryGetValues("x-ratelimit-limit-requests", out var rpmVals)
|
||||
&& int.TryParse(rpmVals.FirstOrDefault(), out var rpmVal))
|
||||
rpm = rpmVal;
|
||||
if (resp.Headers.TryGetValues("x-ratelimit-limit-tokens", out var tpmVals)
|
||||
&& int.TryParse(tpmVals.FirstOrDefault(), out var tpmVal))
|
||||
tpm = tpmVal;
|
||||
|
||||
// Modell-Liste extrahieren (nur die interessantesten)
|
||||
string[] models = Array.Empty<string>();
|
||||
if (doc.RootElement.TryGetProperty("data", out var data))
|
||||
{
|
||||
var interestingPrefixes = new[] { "gpt-4", "gpt-3.5", "o1", "o3", "o4" };
|
||||
models = data.EnumerateArray()
|
||||
.Select(m => m.GetProperty("id").GetString())
|
||||
.Where(id => interestingPrefixes.Any(p => id.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
|
||||
.Distinct()
|
||||
.OrderBy(x => x)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
// Plan-Erkennung anhand der Modelle
|
||||
string plan;
|
||||
if (models.Any(m => m.StartsWith("o3", StringComparison.OrdinalIgnoreCase)
|
||||
|| m.StartsWith("o4", StringComparison.OrdinalIgnoreCase)))
|
||||
plan = "Tier 5+";
|
||||
else if (models.Any(m => m.StartsWith("gpt-4o", StringComparison.OrdinalIgnoreCase)))
|
||||
plan = "Tier 3+";
|
||||
else if (models.Any(m => m.StartsWith("gpt-4", StringComparison.OrdinalIgnoreCase)))
|
||||
plan = "Tier 2+";
|
||||
else if (models.Any(m => m.StartsWith("gpt-3.5", StringComparison.OrdinalIgnoreCase)))
|
||||
plan = "Tier 1";
|
||||
else
|
||||
plan = "Free";
|
||||
|
||||
var msg = plan + " · Org: " + (org ?? "?") + " · " + models.Length + " Modelle";
|
||||
if (rpm.HasValue) msg += " · " + rpm.Value + " RPM";
|
||||
|
||||
return new OpenAiCheckResult
|
||||
{
|
||||
Status = "ok",
|
||||
Message = msg,
|
||||
Organization = org,
|
||||
Models = models,
|
||||
RateLimitRpm = rpm,
|
||||
RateLimitTpm = tpm
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OpenAiCheckResult { Status = "error", Message = ex.Message };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user