using Dapper; using DexDemoBackend; using Microsoft.AspNetCore.Mvc; using Npgsql; var builder = WebApplication.CreateBuilder(args); // // Force binding to all interfaces in Kubernetes // Console.WriteLine($"ASPNETCORE_URLS env var: {Environment.GetEnvironmentVariable("ASPNETCORE_URLS")}"); // Console.WriteLine($"ASPNETCORE_HTTP_PORTS env var: {Environment.GetEnvironmentVariable("ASPNETCORE_HTTP_PORTS")}"); // // Clear any existing configuration and force our URL // builder.WebHost.ConfigureKestrel(options => // { // options.ListenAnyIP(8000); // }); // Configuration var config = new AppConfig { DbHost = Environment.GetEnvironmentVariable("DB_HOST") ?? "postgres", DbPort = Environment.GetEnvironmentVariable("DB_PORT") ?? "5440", DbName = Environment.GetEnvironmentVariable("DB_NAME") ?? "dexdemo", DbUser = Environment.GetEnvironmentVariable("DB_USER") ?? "dexdemo", DbPassword = Environment.GetEnvironmentVariable("DB_PASSWORD") ?? "dexdemo", DexIssuer = Environment.GetEnvironmentVariable("DEX_ISSUER") ?? "https://dex.127.0.0.1.sslip.io/", InsecureDevMode = Environment.GetEnvironmentVariable("INSECURE_DEV_MODE")?.ToLower() == "true", InsecureDevEmail = Environment.GetEnvironmentVariable("INSECURE_DEV_EMAIL") }; builder.Services.AddSingleton(config); builder.Services.AddSingleton(); // Configure JSON serialization to use snake_case builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.SnakeCaseLower; }); builder.Services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); var app = builder.Build(); Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; app.UseCors(); // Health check endpoint app.MapGet("/api/health", () => Results.Ok(new { status = "ok" })); // User info endpoint app.MapGet("/api/user-info", async (HttpContext context, [FromServices] AppConfig cfg, [FromServices] JwtValidator jwtValidator) => { var email = await GetUserEmail(context, cfg, jwtValidator); await using var conn = new NpgsqlConnection(cfg.ConnectionString); await conn.OpenAsync(); // Get user info var userResult = await conn.QuerySingleOrDefaultAsync(@" SELECT u.email, u.full_name, u.organization_id, o.name as org_name FROM users u LEFT JOIN organizations o ON u.organization_id = o.id WHERE u.email = @email", new { email }); if (userResult is null) { return Results.NotFound(new { detail = "User not found in database" }); } var organization = userResult.OrganizationId.HasValue && userResult.OrgName != null ? new Organization(userResult.OrganizationId.Value, userResult.OrgName) : null; // Get user roles var roles = (await conn.QueryAsync(@" SELECT r.id, r.name, r.description FROM roles r JOIN user_roles ur ON r.id = ur.role_id JOIN users u ON ur.user_id = u.id WHERE u.email = @email", new { email })).ToList(); // Get available links var links = (await conn.QueryAsync(@" SELECT DISTINCT l.id, l.title, l.url, l.description FROM links l JOIN role_links rl ON l.id = rl.link_id JOIN user_roles ur ON rl.role_id = ur.role_id JOIN users u ON ur.user_id = u.id WHERE u.email = @email ORDER BY l.id", new { email })).ToList(); return Results.Ok(new UserInfo(userResult.Email, userResult.FullName, organization, roles, links)); }); app.Run(); static async Task GetUserEmail(HttpContext context, AppConfig config, JwtValidator jwtValidator) { // Dev mode if (config.InsecureDevMode) { Console.WriteLine($"INSECURE_DEV_MODE: Using email {config.InsecureDevEmail}"); return config.InsecureDevEmail!; } // Try Authorization header if (context.Request.Headers.TryGetValue("Authorization", out var authHeader)) { var headerValue = authHeader.ToString(); if (headerValue.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { var token = headerValue[7..]; var payload = await jwtValidator.ValidateToken(token); if (payload.TryGetValue("email", out var emailClaim)) { return emailClaim.ToString()!; } } } // Try OAuth2 proxy header if (context.Request.Headers.TryGetValue("X-Auth-Request-Email", out var emailHeader)) { return emailHeader.ToString(); } throw new UnauthorizedAccessException("No authentication information found"); } // Internal query result model record UserQueryResult(string Email, string FullName, int? OrganizationId, string? OrgName);