using System; using System.Collections.Generic; using System.Threading.Tasks; using Dapper; using Gott.Models; namespace Gott.Services; public partial class DatabaseService { // ── Shared Messages ── public async Task CreateSharedMessage(long messageId, string userId, bool includeContext, string expiresAt = null) { using var conn = CreateConnection(); var record = new SharedMessageRecord { ShareToken = Guid.NewGuid().ToString("N"), MessageId = messageId, UserId = userId, IncludeContext = includeContext, ExpiresAt = expiresAt, CreatedAt = DateTime.UtcNow.ToString("o") }; await conn.ExecuteAsync(@" INSERT INTO shared_messages (share_token, message_id, user_id, include_context, expires_at, created_at) VALUES (@ShareToken, @MessageId, @UserId, @IncludeContext, @ExpiresAt, @CreatedAt) ", record); return record; } public async Task GetSharedMessage(string shareToken) { using var conn = CreateConnection(); return await conn.QuerySingleOrDefaultAsync( "SELECT * FROM shared_messages WHERE share_token = @shareToken", new { shareToken }); } public async Task> GetSharedMessagesByUser(string userId) { using var conn = CreateConnection(); return await conn.QueryAsync( "SELECT * FROM shared_messages WHERE user_id = @userId ORDER BY created_at DESC", new { userId }); } public async Task DeleteSharedMessage(string shareToken, string userId) { using var conn = CreateConnection(); await conn.ExecuteAsync( "DELETE FROM shared_messages WHERE share_token = @shareToken AND user_id = @userId", new { shareToken, userId }); } public async Task GetMessage(long messageId) { using var conn = CreateConnection(); return await conn.QuerySingleOrDefaultAsync( "SELECT * FROM messages WHERE id = @messageId", new { messageId }); } public async Task> GetChainMessagesUpTo(string chainId, long messageId) { using var conn = CreateConnection(); var result = await conn.QueryAsync( "SELECT * FROM messages WHERE chain_id = @chainId AND id <= @messageId ORDER BY id ASC", new { chainId, messageId }); return result.AsList(); } // ── Users ── public void EnsureDefaultUser(AuthService auth) { using var conn = CreateConnection(); var count = conn.ExecuteScalar("SELECT COUNT(*) FROM users"); string adminUserId; if (count == 0) { adminUserId = Guid.NewGuid().ToString("N"); var now = DateTime.UtcNow.ToString("o"); conn.Execute(@" INSERT INTO users (user_id, username, password_hash, display_name, is_admin, created_at, updated_at) VALUES (@userId, @username, @hash, @displayName, 1, @now, @now)", new { userId = adminUserId, username = "admin", hash = auth.HashPassword("admin"), displayName = "Admin", now }); } else { adminUserId = conn.ExecuteScalar("SELECT user_id FROM users ORDER BY id LIMIT 1"); } // Bestehende Sessions/Agents ohne User dem ersten User zuweisen conn.Execute("UPDATE sessions SET user_id = @userId WHERE user_id IS NULL", new { userId = adminUserId }); conn.Execute("UPDATE agents SET user_id = @userId WHERE user_id IS NULL", new { userId = adminUserId }); // Erster User ist immer Admin conn.Execute("UPDATE users SET is_admin = 1 WHERE user_id = @userId AND is_admin = 0", new { userId = adminUserId }); } public async Task GetUserByUsername(string username) { using var conn = CreateConnection(); return await conn.QuerySingleOrDefaultAsync( "SELECT * FROM users WHERE username = @username COLLATE NOCASE", new { username }); } public async Task GetUser(string userId) { using var conn = CreateConnection(); return await conn.QuerySingleOrDefaultAsync( "SELECT * FROM users WHERE user_id = @userId", new { userId }); } public async Task> GetUsers() { using var conn = CreateConnection(); return await conn.QueryAsync( "SELECT * FROM users ORDER BY username"); } public async Task UpdateUserPassword(string userId, string passwordHash) { using var conn = CreateConnection(); await conn.ExecuteAsync( "UPDATE users SET password_hash = @passwordHash, updated_at = @now WHERE user_id = @userId", new { userId, passwordHash, now = DateTime.UtcNow.ToString("o") }); } public async Task UpdateUserProfile(string userId, string displayName, string persona) { using var conn = CreateConnection(); await conn.ExecuteAsync( "UPDATE users SET display_name = @displayName, persona = @persona, updated_at = @now WHERE user_id = @userId", new { userId, displayName, persona, now = DateTime.UtcNow.ToString("o") }); } public async Task GetUserSettings(string userId) { using var conn = CreateConnection(); return await conn.QuerySingleOrDefaultAsync( "SELECT settings FROM users WHERE user_id = @userId", new { userId }); } public async Task UpdateUserSettings(string userId, string settings) { using var conn = CreateConnection(); await conn.ExecuteAsync( "UPDATE users SET settings = @settings, updated_at = @now WHERE user_id = @userId", new { userId, settings, now = DateTime.UtcNow.ToString("o") }); } public async Task CreateUser(string username, string passwordHash, string displayName, bool isAdmin, string defaultPath = null, bool canCreateAgents = true, bool canUseCli = true) { using var conn = CreateConnection(); var now = DateTime.UtcNow.ToString("o"); var userId = Guid.NewGuid().ToString("N"); await conn.ExecuteAsync(@" INSERT INTO users (user_id, username, password_hash, display_name, is_admin, default_path, can_create_agents, can_use_cli, created_at, updated_at) VALUES (@userId, @username, @passwordHash, @displayName, @isAdmin, @defaultPath, @canCreateAgents, @canUseCli, @now, @now)", new { userId, username, passwordHash, displayName, isAdmin, defaultPath, canCreateAgents, canUseCli, now }); return await GetUser(userId); } public async Task UpdateUserAdmin(string userId, string username, string displayName, bool isAdmin, string defaultPath, bool canCreateAgents, bool canUseCli) { using var conn = CreateConnection(); await conn.ExecuteAsync( "UPDATE users SET username = @username, display_name = @displayName, is_admin = @isAdmin, default_path = @defaultPath, can_create_agents = @canCreateAgents, can_use_cli = @canUseCli, updated_at = @now WHERE user_id = @userId", new { userId, username, displayName, isAdmin, defaultPath, canCreateAgents, canUseCli, now = DateTime.UtcNow.ToString("o") }); } public async Task DeleteUser(string userId) { using var conn = CreateConnection(); await conn.ExecuteAsync("DELETE FROM tokens WHERE user_id = @userId", new { userId }); await conn.ExecuteAsync("DELETE FROM shared_messages WHERE user_id = @userId", new { userId }); await conn.ExecuteAsync("DELETE FROM users WHERE user_id = @userId", new { userId }); } // ── Agent API-Key Zuordnung ── public async Task SetAgentApiKey(string userId, string agentId, string keyId) { using var conn = CreateConnection(); if (string.IsNullOrEmpty(keyId)) { await conn.ExecuteAsync( "DELETE FROM agent_api_keys WHERE user_id = @userId AND agent_id = @agentId", new { userId, agentId }); } else { await conn.ExecuteAsync(@" INSERT INTO agent_api_keys (user_id, agent_id, key_id) VALUES (@userId, @agentId, @keyId) ON CONFLICT(user_id, agent_id) DO UPDATE SET key_id = @keyId", new { userId, agentId, keyId }); } } public async Task GetAgentApiKeyId(string userId, string agentId) { using var conn = CreateConnection(); return await conn.QuerySingleOrDefaultAsync( "SELECT key_id FROM agent_api_keys WHERE user_id = @userId AND agent_id = @agentId", new { userId, agentId }); } public async Task GetAgentApiKeyRecord(string userId, string agentId) { using var conn = CreateConnection(); return await conn.QuerySingleOrDefaultAsync(@" SELECT k.* FROM api_keys k INNER JOIN agent_api_keys a ON a.key_id = k.key_id WHERE a.user_id = @userId AND a.agent_id = @agentId", new { userId, agentId }); } /// /// Gibt den ersten aktiven API-Key für einen bestimmten Provider zurück. /// Wird verwendet um z.B. den OpenAI-Key für Modell-Abfragen zu holen. /// public async Task GetFirstActiveKeyByProvider(string provider) { using var conn = CreateConnection(); return await conn.QuerySingleOrDefaultAsync(@" SELECT api_key FROM api_keys WHERE provider = @provider AND (status IS NULL OR status = 'valid' OR status = 'ok') ORDER BY created_at ASC LIMIT 1", new { provider }); } }