using System; using System.Collections.Generic; using System.Threading.Tasks; using Dapper; using Gott.Extensions; using Gott.Services; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Data.Sqlite; namespace Gott.Endpoints; public static class DatabaseExplorerEndpoint { public static void MapDatabaseExplorerEndpoints(this WebApplication app) { app.MapGet("/api/admin/db/tables", HandleGetTables); app.MapGet("/api/admin/db/tables/{name}", HandleGetTableData); } private static async Task HandleGetTables(HttpContext ctx, DatabaseService db) { if (!ctx.IsAdmin()) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Admin access required" }); return; } using var conn = db.GetConnection(); var tables = conn.Query( "SELECT name, (SELECT COUNT(*) FROM pragma_table_info(m.name)) as column_count " + "FROM sqlite_master m WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name"); var result = new List(); foreach (var t in tables) { var rowCount = conn.ExecuteScalar($"SELECT COUNT(*) FROM \"{t.Name}\""); result.Add(new { name = t.Name, columns = t.ColumnCount, rows = rowCount }); } await ctx.Response.WriteAsJsonAsync(new { tables = result }); } private static async Task HandleGetTableData(HttpContext ctx, DatabaseService db, string name) { if (!ctx.IsAdmin()) { ctx.Response.StatusCode = 403; await ctx.Response.WriteAsJsonAsync(new { error = "Admin access required" }); return; } // Tabellennamen validieren (SQL-Injection verhindern) using var conn = db.GetConnection(); var exists = conn.ExecuteScalar( "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name = @name", new { name }); if (exists == 0) { ctx.Response.StatusCode = 404; await ctx.Response.WriteAsJsonAsync(new { error = "Table not found" }); return; } // Pagination var page = 1; var pageSize = 1000; if (ctx.Request.Query.TryGetValue("page", out var pageStr) && int.TryParse(pageStr, out var p) && p > 0) page = p; if (ctx.Request.Query.TryGetValue("pageSize", out var psStr) && int.TryParse(psStr, out var ps) && ps > 0 && ps <= 10000) pageSize = ps; var offset = (page - 1) * pageSize; // Spalten-Info var columns = conn.Query($"PRAGMA table_info(\"{name}\")"); // Daten var totalRows = conn.ExecuteScalar($"SELECT COUNT(*) FROM \"{name}\""); var rows = conn.Query($"SELECT * FROM \"{name}\" LIMIT @limit OFFSET @offset", new { limit = pageSize, offset }); await ctx.Response.WriteAsJsonAsync(new { table = name, columns, rows, pagination = new { page, pageSize, totalRows, totalPages = (int)Math.Ceiling((double)totalRows / pageSize) } }); } private class TableInfo { public string Name { get; set; } public int ColumnCount { get; set; } } private class ColumnInfo { public int Cid { get; set; } public string Name { get; set; } public string Type { get; set; } public int Notnull { get; set; } public string DfltValue { get; set; } public int Pk { get; set; } } }