- 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)
94 lines
3.9 KiB
C#
94 lines
3.9 KiB
C#
using System;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Text.Json;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace Gott.Services
|
||
{
|
||
public class GoogleCheckResult
|
||
{
|
||
public string Status { get; set; } // "ok", "invalid", "disabled", "error"
|
||
public string Message { get; set; }
|
||
public string[] Models { get; set; }
|
||
}
|
||
|
||
public static class GoogleKeyChecker
|
||
{
|
||
public static async Task<GoogleCheckResult> CheckKeyAsync(IHttpClientFactory httpFactory, string apiKey)
|
||
{
|
||
try
|
||
{
|
||
var client = httpFactory.CreateClient();
|
||
client.Timeout = TimeSpan.FromSeconds(30);
|
||
|
||
// Gemini / Generative Language API – listet verfügbare Modelle
|
||
var url = "https://generativelanguage.googleapis.com/v1/models?key=" + apiKey;
|
||
var resp = await client.GetAsync(url);
|
||
var statusCode = (int)resp.StatusCode;
|
||
|
||
if (statusCode == 400)
|
||
return new GoogleCheckResult { Status = "invalid", Message = "API-Key ungültig (400)" };
|
||
|
||
if (statusCode == 403)
|
||
{
|
||
// Key gültig, aber Gemini API nicht aktiviert (z.B. Maps-Key)
|
||
var errorBody = await resp.Content.ReadAsStringAsync();
|
||
if (errorBody.Contains("API_KEY_INVALID", StringComparison.OrdinalIgnoreCase))
|
||
return new GoogleCheckResult { Status = "invalid", Message = "API-Key ungültig (403)" };
|
||
|
||
return new GoogleCheckResult { Status = "disabled", Message = "Key gültig, aber Gemini API nicht aktiviert (403)" };
|
||
}
|
||
|
||
if (statusCode == 429)
|
||
return new GoogleCheckResult { Status = "rate_limited", Message = "Rate Limited (429)" };
|
||
|
||
if (statusCode >= 400)
|
||
return new GoogleCheckResult { Status = "error", Message = "API-Fehler (" + statusCode + ")" };
|
||
|
||
// 200 OK → Key gültig + Gemini API aktiv
|
||
var body = await resp.Content.ReadAsStringAsync();
|
||
var doc = JsonDocument.Parse(body);
|
||
|
||
string[] models = Array.Empty<string>();
|
||
if (doc.RootElement.TryGetProperty("models", out var modelsArr))
|
||
{
|
||
var interestingPrefixes = new[] { "gemini" };
|
||
models = modelsArr.EnumerateArray()
|
||
.Where(m => m.TryGetProperty("name", out _))
|
||
.Select(m => m.GetProperty("name").GetString())
|
||
.Where(n => n != null)
|
||
.Select(n => n.StartsWith("models/", StringComparison.Ordinal) ? n.Substring(7) : n)
|
||
.Where(n => interestingPrefixes.Any(p => n.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
|
||
.Distinct()
|
||
.OrderBy(x => x)
|
||
.ToArray();
|
||
}
|
||
|
||
// Plan-Erkennung
|
||
string plan;
|
||
if (models.Any(m => m.Contains("2.5", StringComparison.OrdinalIgnoreCase)
|
||
|| m.Contains("ultra", StringComparison.OrdinalIgnoreCase)))
|
||
plan = "Premium";
|
||
else if (models.Any(m => m.Contains("pro", StringComparison.OrdinalIgnoreCase)))
|
||
plan = "Standard";
|
||
else if (models.Any(m => m.Contains("flash", StringComparison.OrdinalIgnoreCase)))
|
||
plan = "Basic";
|
||
else
|
||
plan = "Free";
|
||
|
||
return new GoogleCheckResult
|
||
{
|
||
Status = "ok",
|
||
Message = models.Length + " Gemini-Modelle · " + plan,
|
||
Models = models
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new GoogleCheckResult { Status = "error", Message = ex.Message };
|
||
}
|
||
}
|
||
}
|
||
}
|