Files
gott/Services/OpenAiKeyChecker.cs
T
Meleki c8859a89d0 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)
2026-03-13 20:43:20 +01:00

116 lines
4.9 KiB
C#

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 };
}
}
}
}