feat: API Key pro Agent statt pro User

- Neue Tabelle agent_api_keys (user_id, agent_id, key_id)
- Migration: bestehende selected_api_key_id auf alle Agents verteilt
- Backend: GetAgentApiKeyRecord(userId, agentId) in Chat, Telegram, Cronjobs
- Endpoints: select/selected mit ?agentId Query-Parameter
- Frontend: currentAgentId aus Session, alle Key-Calls agent-aware
- DeleteApiKey bereinigt agent_api_keys statt users.selected_api_key_id
This commit is contained in:
Meleki committed 2026-03-14 21:41:27 +01:00
1 parent b04b1a2cf9
commit 1effc6d00f
10 files changed
+128 -43

No files matched your search

+52 -1
View File
@@ -357,9 +357,23 @@ public partial class DatabaseService
TryAddColumn(conn, "api_keys", "source_url", "TEXT");
TryAddColumn(conn, "api_keys", "is_public", "INTEGER NOT NULL DEFAULT 0");
// users: ausgewählter API-Key
// users: ausgewählter API-Key (legacy, wird durch agent_api_keys ersetzt)
TryAddColumn(conn, "users", "selected_api_key_id", "TEXT");
conn.Execute(@"
CREATE TABLE IF NOT EXISTS agent_api_keys (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL,
agent_id TEXT NOT NULL,
key_id TEXT NOT NULL,
UNIQUE(user_id, agent_id)
);
CREATE INDEX IF NOT EXISTS idx_agent_api_keys_user ON agent_api_keys(user_id);
");
// Migration: selected_api_key_id → agent_api_keys (einmalig)
MigrateSelectedApiKeysToAgentKeys(conn);
// Migration: Bestehende PNG/JPEG-Avatare/Images zu WebP konvertieren
MigrateImagesToWebP(conn);
@@ -367,6 +381,43 @@ public partial class DatabaseService
MigrateImageThumbnails(conn);
}
private void MigrateSelectedApiKeysToAgentKeys(SqliteConnection conn)
{
// Nur migrieren wenn agent_api_keys leer ist und users mit selected_api_key_id existieren
var count = conn.ExecuteScalar<int>("SELECT COUNT(*) FROM agent_api_keys");
if (count > 0) return;
var usersWithKeys = conn.Query<(string UserId, string SelectedApiKeyId)>(
"SELECT user_id, selected_api_key_id FROM users WHERE selected_api_key_id IS NOT NULL AND selected_api_key_id != ''")
.ToList();
foreach (var (userId, keyId) in usersWithKeys)
{
// Alle Agents des Users + öffentliche Agents
var agentIds = conn.Query<string>(
"SELECT agent_id FROM agents WHERE user_id = @userId OR is_public = 1",
new { userId }).ToList();
// Alle Sessions des Users mit Agent → deren Agents auch berücksichtigen
var sessionAgentIds = conn.Query<string>(
"SELECT DISTINCT agent_id FROM sessions WHERE user_id = @userId AND agent_id IS NOT NULL AND agent_id != ''",
new { userId }).ToList();
var allAgentIds = agentIds.Union(sessionAgentIds).Distinct().ToList();
foreach (var agentId in allAgentIds)
{
try
{
conn.Execute(
"INSERT OR IGNORE INTO agent_api_keys (user_id, agent_id, key_id) VALUES (@userId, @agentId, @keyId)",
new { userId, agentId, keyId });
}
catch { /* UNIQUE constraint – already exists */ }
}
}
}
private void MigrateImagesToWebP(SqliteConnection conn)
{
const int webPQuality = 80;