feat: GitHub Key Scanner mit automatischem Intervall und manueller UI
- KeyScannerService als BackgroundService (Timer + manueller Trigger) - GitHub Code Search: sucht nach sk-ant-oat01-* Keys in Dotenv-Dateien - AnthropicKeyChecker: shared Key-Check-Logik (ersetzt Duplikat in ApiKeysEndpoint) - ScannedKeyRecord Model mit Soft-Delete und Adopt-Verknüpfung - Scan-Endpoints: trigger, status, list, recheck, adopt, delete - API-Keys-Page: Scanner-UI mit Status-Indikator und Scanned-Keys-Liste - GitHub-URL pro gefundenem Key (html_url aus Search API) - Server-Settings: GitHubToken + ScanIntervalHours konfigurierbar - DB-Migration: scanned_keys Tabelle + api_keys Meta-Spalten (status, plan, usage) - ApiKeyRecord erweitert um Status/Plan/Usage-Felder mit UpdateApiKeyMeta()
This commit is contained in:
1 parent
db1a7c7f64
commit
006ef223bf
15 files changed
+996
-107
No files matched your search
@@ -367,13 +367,15 @@ public partial class DatabaseService
|
||||
new { keyId });
|
||||
}
|
||||
|
||||
public async Task CreateApiKey(ApiKeyRecord record)
|
||||
|
||||
public async Task<long> CreateApiKey(ApiKeyRecord record)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync(@"
|
||||
INSERT INTO api_keys (key_id, provider, name, api_key, is_active, created_at, updated_at)
|
||||
VALUES (@KeyId, @Provider, @Name, @ApiKey, @IsActive, @CreatedAt, @UpdatedAt)",
|
||||
INSERT INTO api_keys (key_id, provider, name, api_key, is_active, status, plan, usage_7d, usage_5h, checked_at, created_at, updated_at)
|
||||
VALUES (@KeyId, @Provider, @Name, @ApiKey, @IsActive, @Status, @Plan, @Usage7d, @Usage5h, @CheckedAt, @CreatedAt, @UpdatedAt)",
|
||||
record);
|
||||
return await conn.ExecuteScalarAsync<long>("SELECT last_insert_rowid()");
|
||||
}
|
||||
|
||||
public async Task UpdateApiKey(string keyId, string name, string apiKey = null)
|
||||
@@ -393,6 +395,15 @@ public partial class DatabaseService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateApiKeyMeta(string keyId, string status, string plan, double? usage7d, double? usage5h)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync(@"
|
||||
UPDATE api_keys SET status = @status, plan = @plan, usage_7d = @usage7d, usage_5h = @usage5h, checked_at = @now, updated_at = @now
|
||||
WHERE key_id = @keyId",
|
||||
new { keyId, status, plan, usage7d, usage5h, now = DateTime.UtcNow.ToString("o") });
|
||||
}
|
||||
|
||||
public async Task SetActiveApiKey(string keyId, string provider)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
@@ -419,4 +430,68 @@ public partial class DatabaseService
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync("DELETE FROM api_keys WHERE key_id = @keyId", new { keyId });
|
||||
}
|
||||
|
||||
// ── Scanned Keys ──
|
||||
|
||||
public async Task<bool> ScannedKeyExists(string apiKey)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
return await conn.ExecuteScalarAsync<int>(
|
||||
"SELECT COUNT(*) FROM scanned_keys WHERE api_key = @apiKey", new { apiKey }) > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> ApiKeyExistsInKeys(string apiKey)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
return await conn.ExecuteScalarAsync<int>(
|
||||
"SELECT COUNT(*) FROM api_keys WHERE api_key = @apiKey", new { apiKey }) > 0;
|
||||
}
|
||||
|
||||
public async Task CreateScannedKey(ScannedKeyRecord record)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync(@"
|
||||
INSERT OR IGNORE INTO scanned_keys (scanned_key_id, api_key, source_url, status, plan, usage_7d, usage_5h, scanned_at, checked_at)
|
||||
VALUES (@ScannedKeyId, @ApiKey, @SourceUrl, @Status, @Plan, @Usage7d, @Usage5h, @ScannedAt, @CheckedAt)",
|
||||
record);
|
||||
}
|
||||
|
||||
public async Task UpdateScannedKeyStatus(string scannedKeyId, string status, string plan, double? usage7d, double? usage5h)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync(@"
|
||||
UPDATE scanned_keys SET status = @status, plan = @plan, usage_7d = @usage7d, usage_5h = @usage5h, checked_at = @now
|
||||
WHERE scanned_key_id = @scannedKeyId",
|
||||
new { scannedKeyId, status, plan, usage7d, usage5h, now = DateTime.UtcNow.ToString("o") });
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ScannedKeyRecord>> GetScannedKeys()
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
return await conn.QueryAsync<ScannedKeyRecord>(
|
||||
"SELECT * FROM scanned_keys WHERE deleted = 0 ORDER BY scanned_at DESC");
|
||||
}
|
||||
|
||||
public async Task<ScannedKeyRecord> GetScannedKey(string scannedKeyId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
return await conn.QuerySingleOrDefaultAsync<ScannedKeyRecord>(
|
||||
"SELECT * FROM scanned_keys WHERE scanned_key_id = @scannedKeyId",
|
||||
new { scannedKeyId });
|
||||
}
|
||||
|
||||
public async Task SetScannedKeyAdopted(string scannedKeyId, long apiKeyId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync(
|
||||
"UPDATE scanned_keys SET adopted_api_key_id = @apiKeyId WHERE scanned_key_id = @scannedKeyId",
|
||||
new { scannedKeyId, apiKeyId });
|
||||
}
|
||||
|
||||
public async Task DeleteScannedKey(string scannedKeyId)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
await conn.ExecuteAsync("UPDATE scanned_keys SET deleted = 1 WHERE scanned_key_id = @scannedKeyId", new { scannedKeyId });
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user