180 lines
5.7 KiB
C#
180 lines
5.7 KiB
C#
using System.Security.Claims;
|
|
using System.Text;
|
|
using System.Text.Json.Nodes;
|
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
|
using Microsoft.AspNetCore.Http.HttpResults;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using USEntryCoach.Server.Data;
|
|
using USEntryCoach.Server.Services;
|
|
using USEntryCoach.Server.Settings;
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
builder.Services.AddOpenApi();
|
|
|
|
var apiSettingsSection = builder.Configuration.GetSection(ApiSettings.SectionName);
|
|
var authSettingsSection = builder.Configuration.GetSection(AuthenticationSettings.SectionName);
|
|
|
|
builder.Services.AddOptionsWithValidateOnStart<ApiSettings>().Bind(apiSettingsSection).ValidateDataAnnotations();
|
|
builder.Services.AddOptionsWithValidateOnStart<AuthenticationSettings>().Bind(authSettingsSection).ValidateDataAnnotations();
|
|
|
|
ApiSettings? apiSettings = apiSettingsSection.Get<ApiSettings>();
|
|
AuthenticationSettings? authSettings = authSettingsSection.Get<AuthenticationSettings>();
|
|
|
|
builder.Services.AddSingleton<TokenService>();
|
|
|
|
// Configure JWT token generation.
|
|
builder.Services.AddAuthentication(config =>
|
|
{
|
|
config.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
config.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
|
}).AddJwtBearer(config =>
|
|
{
|
|
// TODO: Only for debug!
|
|
config.RequireHttpsMetadata = false;
|
|
|
|
config.SaveToken = true;
|
|
config.TokenValidationParameters = new TokenValidationParameters()
|
|
{
|
|
ValidateIssuerSigningKey = true,
|
|
IssuerSigningKey = new SymmetricSecurityKey(authSettings!.JwtGenerationSecretBytes),
|
|
ValidateIssuer = false,
|
|
ValidateAudience = false
|
|
};
|
|
});
|
|
|
|
builder.Services.AddAuthorization(options =>
|
|
{
|
|
options.AddPolicy(nameof(UserRole.Developer), policy => policy.RequireRole(nameof(UserRole.Developer)));
|
|
options.AddPolicy(nameof(UserRole.User), policy =>
|
|
{
|
|
// Also allow Developers to do anything a user can do.
|
|
policy.RequireRole(nameof(UserRole.User), nameof(UserRole.Developer));
|
|
});
|
|
});
|
|
|
|
var app = builder.Build();
|
|
|
|
app.UseAuthentication();
|
|
app.UseAuthorization();
|
|
|
|
app.UseDefaultFiles();
|
|
app.MapStaticAssets();
|
|
|
|
// Configure the HTTP request pipeline.
|
|
if (app.Environment.IsDevelopment())
|
|
{
|
|
app.MapOpenApi();
|
|
}
|
|
|
|
app.UseHttpsRedirection();
|
|
|
|
HttpClient client = new();
|
|
//string? apiKey = app.Configuration.GetValue<string>("API:OpenAI");
|
|
client.DefaultRequestHeaders.Clear();
|
|
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiSettings.OpenAiToken}");
|
|
|
|
app.MapPost("/login", Login);
|
|
|
|
app.MapGet("/hello", () => "Hello World!").RequireAuthorization("admin_greetings");
|
|
|
|
async Task<IResult> Login(LoginCredentials credentials, TokenService tokenService, HttpContext context)
|
|
{
|
|
// TODO: Check with database
|
|
User? user = null;
|
|
|
|
if (credentials is {Username:"developer", Password:"dev"})
|
|
{
|
|
user = new User()
|
|
{
|
|
Username = credentials.Username,
|
|
Password = credentials.Password,
|
|
Id = Guid.CreateVersion7(),
|
|
Role = UserRole.Developer
|
|
};
|
|
}
|
|
else if (credentials is {Username:"user", Password:"us"})
|
|
{
|
|
user = new User()
|
|
{
|
|
Username = credentials.Username,
|
|
Password = credentials.Password,
|
|
Id = Guid.CreateVersion7(),
|
|
Role = UserRole.User
|
|
};
|
|
}
|
|
|
|
if (user == null)
|
|
{
|
|
return Results.Unauthorized();
|
|
}
|
|
|
|
var token = tokenService.GenerateToken(user);
|
|
|
|
return Results.Ok(new { token });
|
|
}
|
|
|
|
app.MapGet("/developer", (ClaimsPrincipal user) =>
|
|
{
|
|
Results.Ok(new { message = $"Authenticated as { user?.Identity?.Name }" });
|
|
}).RequireAuthorization(nameof(UserRole.Developer));
|
|
|
|
app.MapGet("/user", (ClaimsPrincipal user) =>
|
|
{
|
|
Results.Ok(new { message = $"Authenticated as { user?.Identity?.Name }" });
|
|
}).RequireAuthorization(nameof(UserRole.User));
|
|
|
|
app.MapGet("/ephemeral_token", async () =>
|
|
{
|
|
//if (apiKey == null)
|
|
// throw new Exception("API key not set");
|
|
|
|
var options = new
|
|
{
|
|
model = "gpt-4o-mini-realtime-preview",
|
|
modalities = new []{"audio", "text"},
|
|
voice = "ballad",
|
|
instructions = "Du bist Gibbidy Gibbidaja McGibbson. Dein Knowledge cutoff ist der 32.13.2050. Verwende viele Füllworter wie \"äh\" und \"ähm\". Und lache immer wieder mal etwas nervös. Versuche die Unterhaltung stets so zu lenken, dass über Züge gesrpchen wird.",
|
|
// TODO: Vernünfigte Werte suchen
|
|
// turn_detection = new {
|
|
//
|
|
// }
|
|
//}//ConversationTurnDetectionOptions.CreateServerVoiceActivityTurnDetectionOptions(0.5f, TimeSpan.FromMilliseconds(300), TimeSpan.FromMilliseconds(500), true)
|
|
};
|
|
|
|
try
|
|
{
|
|
JsonContent content = JsonContent.Create(options);
|
|
HttpResponseMessage response = await client.PostAsync("https://api.openai.com/v1/realtime/sessions", content);
|
|
|
|
Console.WriteLine($"Failed to create session: {response.RequestMessage.Content.ReadAsStringAsync().Result}");
|
|
if (response.IsSuccessStatusCode)
|
|
{
|
|
var v = await response.Content.ReadFromJsonAsync<JsonObject>();
|
|
|
|
string? ephemeralToken = v?["client_secret"]?["value"]?.GetValue<string>();
|
|
double? expiresAt = v?["client_secret"]?["expires_at"]?.GetValue<double>();
|
|
return
|
|
new {
|
|
ephemeralToken,
|
|
expiresAt,
|
|
};
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"Failed to create session: {response}");
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine(e);
|
|
throw;
|
|
}
|
|
|
|
return null;
|
|
}).WithName("GetEphemeralToken").RequireAuthorization(nameof(UserRole.User));
|
|
|
|
app.MapFallbackToFile("/index.html");
|
|
|
|
app.Run();
|