From f4fe623e24045282935f9313fa9bdd4043ba1c08 Mon Sep 17 00:00:00 2001 From: notabird Date: Sun, 7 Jun 2026 11:25:49 -0700 Subject: [PATCH] Few more migrations --- CannedNet/Program.cs | 145 ++- .../Controllers/AccountsController.cs | 942 +++++++++--------- .../Services/Controllers/ChatController.cs | 14 +- .../Services/Controllers/ImageController.cs | 145 ++- .../Services/Controllers/NSController.cs | 20 +- .../Services/Controllers/NotifyController.cs | 3 +- .../Infrastructure/ServiceRegistry.cs | 13 +- 7 files changed, 637 insertions(+), 645 deletions(-) diff --git a/CannedNet/Program.cs b/CannedNet/Program.cs index 8fb2a9e..5ef395c 100644 --- a/CannedNet/Program.cs +++ b/CannedNet/Program.cs @@ -1,48 +1,111 @@ -using CannedNet; +using CannedNet.Data; +using CannedNet.Hubs; using CannedNet.Services; using CannedNet.Services.Infrastructure; +using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; -var apps = new List<(WebApplication App, ServiceRegistry Service)>(); +namespace CannedNet; -foreach (var service in Services.All) +public static class Program { - var builder = WebApplication.CreateBuilder(); - - builder.Services.AddLogging(logging => - { - logging.AddConsole(); - logging.SetMinimumLevel(LogLevel.Debug); - }); - - service.ConfigureBuilder(builder); - builder.WebHost.UseUrls($"http://*:{service.Port}"); - builder.Configuration.AddJsonFile($"AppConfigs/appsettings.{service.Name}.json", optional: true, reloadOnChange: true); - - apps.Add((builder.Build(), service)); + public static async Task Main(string[] args) { + WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + + //builder.Services.AddLogging(logging => { + // logging.AddConsole(); + // logging.SetMinimumLevel(LogLevel.Debug); + //}); + + string connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? "Host=localhost;Port=5432;Database=cannednet;Username=postgres;Password=postgres"; + + builder.Services.AddControllers(); + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + + builder.Services.ConfigureHttpJsonOptions(options => options.SerializerOptions.PropertyNamingPolicy = null); + builder.Services.AddDbContext(options => options.UseNpgsql(connectionString)); + builder.Services.AddSingleton(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddSignalR(); + + WebApplication app = builder.Build(); + + app.MapHub("/hub/v1"); + app.UseHttpsRedirection(); + //app.UseRequestLogging(); + app.MapControllers(); + + IHubContext hubContext = app.Services.GetRequiredService>(); + NotificationService.SetHubContext(hubContext); + + //if (app.Environment.IsDevelopment()) { + app.UseSwagger(); + app.UseSwaggerUI(options => { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "CannedNet v1"); + options.RoutePrefix = "swagger/ui/"; + }); + //} + + using IServiceScope scope = app.Services.CreateScope(); + AppDbContext db = scope.ServiceProvider.GetRequiredService(); + + StorefrontFillService seedingService = scope.ServiceProvider.GetRequiredService(); + await seedingService.FillStorefrontsAsync(); + + try { + await db.Database.MigrateAsync(); + } catch { + await db.Database.EnsureCreatedAsync(); + } + + await app.RunAsync(); + } + + //public static async Task T() { + // List<(WebApplication App, ServiceRegistry Service)> apps = new(); + // + // foreach (var service in Services.Infrastructure.Services.All) + // { + // var builder = WebApplication.CreateBuilder(); + // + // builder.Services.AddLogging(logging => + // { + // logging.AddConsole(); + // logging.SetMinimumLevel(LogLevel.Debug); + // }); + // + // service.ConfigureBuilder(builder); + // builder.WebHost.UseUrls($"http://*:{service.Port}"); + // builder.Configuration.AddJsonFile($"AppConfigs/appsettings.{service.Name}.json", optional: true, reloadOnChange: true); + // + // apps.Add((builder.Build(), service)); + // } + // + // using var scope = apps[0].App.Services.CreateScope(); + // var dbContext = scope.ServiceProvider.GetRequiredService(); + // try + // { + // dbContext.Database.Migrate(); + // } + // catch (Exception ex) + // { + // dbContext.Database.EnsureCreated(); + // } + // + // // automatically fill out storefronts tables from the JSON's + // var seedingService = scope.ServiceProvider.GetRequiredService(); + // await seedingService.FillStorefrontsAsync(); + // + // var jwtService = scope.ServiceProvider.GetRequiredService(); + // + // foreach (var (app, service) in apps) + // { + // service.MapEndpoints(app, jwtService); + // app.UseRequestLogging(); + // } + // + // await Task.WhenAll(apps.Select(t => t.App.RunAsync())); + //} } - -using var scope = apps[0].App.Services.CreateScope(); -var dbContext = scope.ServiceProvider.GetRequiredService(); -try -{ - dbContext.Database.Migrate(); -} -catch (Exception ex) -{ - dbContext.Database.EnsureCreated(); -} - -// automatically fill out storefronts tables from the JSON's -var seedingService = scope.ServiceProvider.GetRequiredService(); -await seedingService.FillStorefrontsAsync(); - -var jwtService = scope.ServiceProvider.GetRequiredService(); - -foreach (var (app, service) in apps) -{ - service.MapEndpoints(app, jwtService); - app.UseRequestLogging(); -} - -await Task.WhenAll(apps.Select(t => t.App.RunAsync())); diff --git a/CannedNet/Services/Controllers/AccountsController.cs b/CannedNet/Services/Controllers/AccountsController.cs index 2ef9129..ab91d28 100644 --- a/CannedNet/Services/Controllers/AccountsController.cs +++ b/CannedNet/Services/Controllers/AccountsController.cs @@ -1,512 +1,458 @@ using CannedNet.Data; using Microsoft.EntityFrameworkCore; -using CannedNet.Services; -using CannedNet.Services.Infrastructure; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Primitives; namespace CannedNet.Services.Controllers; -public class AccountsController +//TODO: remove duplicate code(most of it is checking if the user is authenticated, ASP.NET has stuff for this) +[ApiController, Route("account")] +public class AccountsController : ControllerBase { - public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); + //[HttpGet] + //public Task Root() { + // return Task.FromResult(Results.Ok("FUCKL MYSJDIOJD")); + //} - public void MapEndpoints(WebApplication app, JwtTokenService jwtService) - { - app.MapGet("/", (HttpRequest request) => - { - return Results.Ok("FUCKL MYSJDIOJD"); - }); - - app.MapGet("/account/me", async (HttpRequest request, AppDbContext db) => - { - var authHeader = request.Headers.Authorization.ToString(); - - if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) - return Results.Unauthorized(); + [HttpGet("me")] + public async Task Me(HttpRequest request, AppDbContext db, JwtTokenService jwtService) { + string authHeader = request.Headers.Authorization.ToString(); - var token = authHeader.Substring("Bearer ".Length); - var accountId = jwtService.ValidateAndGetAccountId(token); + if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + return Results.Unauthorized(); - if (string.IsNullOrEmpty(accountId)) - return Results.Unauthorized(); + string token = authHeader.Substring("Bearer ".Length); + string? accountId = jwtService.ValidateAndGetAccountId(token); - if (!int.TryParse(accountId.AsSpan(), out var id)) - return Results.Unauthorized(); + if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out int id)) + return Results.Unauthorized(); - var account = await db.Accounts.FindAsync(id); - if (account == null) - return Results.NotFound(); + Account? account = await db.Accounts.FindAsync(id); + if (account == null) + return Results.NotFound(); - var selfAccount = new SelfAccount - { - AccountId = id, - ProfileImage = account.ProfileImage ?? "hdqeamlcmatc6qzoi2ybgf0ddijjcf.jpg", - IsJunior = account.IsJunior, - Platforms = account.Platforms ?? 0, - PersonalPronouns = account.PersonalPronouns ?? 0, - IdentityFlags = account.IdentityFlags ?? 0, - Username = account.Username ?? $"Player{id}", - DisplayName = account.DisplayName ?? $"Player{id}", - CreatedAt = account.CreatedAt, - Email = null, - Phone = null, - JuniorState = null, - Birthday = null, - ParentAccountId = null, - AvailableUsernameChanges = 1 - }; + SelfAccount selfAccount = new SelfAccount { + AccountId = id, + ProfileImage = account.ProfileImage ?? "hdqeamlcmatc6qzoi2ybgf0ddijjcf.jpg", + IsJunior = account.IsJunior, + Platforms = account.Platforms ?? 0, + PersonalPronouns = account.PersonalPronouns ?? 0, + IdentityFlags = account.IdentityFlags ?? 0, + Username = account.Username ?? $"Player{id}", + DisplayName = account.DisplayName ?? $"Player{id}", + CreatedAt = account.CreatedAt, + Email = null, + Phone = null, + JuniorState = null, + Birthday = null, + ParentAccountId = null, + AvailableUsernameChanges = 1 + }; - return Results.Ok(selfAccount); - }); - - app.MapGet("/account/bulk", async (HttpRequest request, AppDbContext db) => - { - var ids = request.Query["id"]; - var accountIds = new List(); - - foreach (var id in ids) - { - if (int.TryParse(id, out var accountId)) - { - accountIds.Add(accountId); - } - } - - var accounts = await db.Accounts - .Where(a => accountIds.Contains(a.AccountId.Value)) - .ToListAsync(); - - var result = accounts.Select(a => new Account - { - AccountId = a.AccountId, - ProfileImage = a.ProfileImage ?? "DefaultProfileImage.jpg", - IsJunior = a.IsJunior, - Platforms = a.Platforms ?? 0, - PersonalPronouns = a.PersonalPronouns ?? 0, - IdentityFlags = a.IdentityFlags ?? 0, - Username = a.Username ?? $"Player{a.AccountId}", - DisplayName = a.DisplayName ?? $"Player{a.AccountId}", - CreatedAt = a.CreatedAt - }).ToList(); - - return Results.Json(result); - }); - - app.MapGet("/account/{id}", async (HttpRequest request, string id, AppDbContext db) => - { - if (!int.TryParse(id, out var accountId)) - return Results.BadRequest(); - - var account = await db.Accounts.FindAsync(accountId); - if (account == null) - return Results.NotFound(); - - var result = new Account - { - AccountId = account.AccountId, - ProfileImage = account.ProfileImage ?? "DefaultProfileImage.jpg", - IsJunior = account.IsJunior, - Platforms = account.Platforms ?? 0, - PersonalPronouns = account.PersonalPronouns ?? 0, - IdentityFlags = account.IdentityFlags ?? 0, - Username = account.Username ?? $"Player{account.AccountId}", - DisplayName = account.DisplayName ?? $"Player{account.AccountId}", - CreatedAt = account.CreatedAt - }; - - return Results.Json(result); - }); - - app.MapPost("/account/create", async (HttpRequest httpRequest, AppDbContext db) => - { - int platform = 0; - string platformId = ""; - - if (httpRequest.ContentLength.HasValue && httpRequest.ContentLength > 0) - { - try - { - var contentType = httpRequest.ContentType ?? ""; - httpRequest.EnableBuffering(); - - using var reader = new StreamReader(httpRequest.Body, leaveOpen: true); - var body = await reader.ReadToEndAsync(); - - if (!string.IsNullOrWhiteSpace(body) && contentType.Contains("application/x-www-form-urlencoded")) - { - foreach (var pair in body.Split('&')) - { - var keyValue = pair.Split('='); - if (keyValue.Length == 2) - { - var key = Uri.UnescapeDataString(keyValue[0]); - var value = Uri.UnescapeDataString(keyValue[1]); - - if (key == "platform" && int.TryParse(value, out var parsedPlatform)) - platform = parsedPlatform; - else if (key == "platformId") - platformId = value; - } - } - } - httpRequest.Body.Position = 0; - } - catch { } - } - - var accountId = new Random().Next(10000, 99999); - var account = new Account - { - AccountId = accountId, - ProfileImage = "DefaultProfileImage.jpg", - IsJunior = false, - Platforms = 0, - PersonalPronouns = 0, - IdentityFlags = 0, - Username = $"Player{accountId}", - DisplayName = $"Player{accountId}", - CreatedAt = DateTime.UtcNow - }; - - db.Accounts.Add(account); - - if (!string.IsNullOrEmpty(platformId)) - { - db.CachedLogins.Add(new CachedLogin - { - AccountId = accountId, - Platform = (PlatformType)platform, - PlatformID = platformId, - LastLoginTime = DateTime.UtcNow, - RequirePassword = false - }); - } - - await db.SaveChangesAsync(); - - // create players dorm room - var maxRoomId = await db.Rooms.MaxAsync(r => (int?)r.RoomId) ?? 0; - var maxId = await db.Rooms.MaxAsync(r => (int?)r.Id) ?? 0; - var dormRoomId = maxRoomId + 1; - var dormId = maxId + 1; - var dormRoom = new Room - { - Id = maxId + 1, - RoomId = dormRoomId, - Name = "DormRoom", - Description = "Your personal room", - CreatorAccountId = accountId, - ImageName = "", - State = 0, - Accessibility = 0, - SupportsLevelVoting = false, - IsRRO = false, - IsDorm = true, - CloningAllowed = false, - SupportsVRLow = true, - SupportsQuest2 = true, - SupportsMobile = true, - SupportsScreens = true, - SupportsWalkVR = true, - SupportsTeleportVR = true, - SupportsJuniors = true, - MinLevel = 0, - WarningMask = 0, - CustomWarning = null, - DisableMicAutoMute = false, - DisableRoomComments = false, - EncryptVoiceChat = false, - CreatedAt = DateTime.UtcNow, - Tags = "[]" - }; - db.Rooms.Add(dormRoom); - - // Create a sub room for the dorm - var dormSubRoom = new SubRoom - { - RoomId = dormRoomId, - SubRoomId = 1, - Name = "DormRoom", - DataBlob = "", - IsSandbox = false, - MaxPlayers = 4, - Accessibility = 0, - UnitySceneId = "76d98498-60a1-430c-ab76-b54a29b7a163", // Dorm scene ID - DataSavedAt = DateTime.UtcNow - }; - db.SubRooms.Add(dormSubRoom); - - await db.SaveChangesAsync(); - - return Results.Ok(RecNetResultWithValue.Ok(account)); - }); - app.MapGet("/parentalcontrol/me", async (HttpRequest request, AppDbContext db) => - { - var authHeader = request.Headers.Authorization.ToString(); - - if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) - return Results.Unauthorized(); - - var token = authHeader.Substring("Bearer ".Length); - var accountId = jwtService.ValidateAndGetAccountId(token); - - if (string.IsNullOrEmpty(accountId)) - return Results.Unauthorized(); - - if (!int.TryParse(accountId.AsSpan(), out var id)) - return Results.Unauthorized(); - - return Results.Content($"{{\"accountId\":{accountId},\"disallowInAppPurchases\":false}}", "application/json"); - }); - - app.MapPut("/account/me/displayname", async (HttpRequest request, AppDbContext db) => - { - var authHeader = request.Headers.Authorization.ToString(); - - if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) - return Results.Unauthorized(); - - var token = authHeader.Substring("Bearer ".Length); - var accountId = jwtService.ValidateAndGetAccountId(token); - - if (string.IsNullOrEmpty(accountId)) - return Results.Unauthorized(); - - if (!int.TryParse(accountId.AsSpan(), out var id)) - return Results.Unauthorized(); - - string newDisplayName = ""; - - if (request.ContentLength.HasValue && request.ContentLength > 0) - { - try - { - request.EnableBuffering(); - using var reader = new StreamReader(request.Body, leaveOpen: true); - var body = await reader.ReadToEndAsync(); - - if (!string.IsNullOrWhiteSpace(body)) - { - foreach (var pair in body.Split('&')) - { - var keyValue = pair.Split('='); - if (keyValue.Length == 2) - { - var key = Uri.UnescapeDataString(keyValue[0]); - var value = Uri.UnescapeDataString(keyValue[1]); - - if (key == "displayName") - newDisplayName = value; - } - } - } - request.Body.Position = 0; - } - catch { } - } - - var account = await db.Accounts.FindAsync(id); - if (account == null) - return Results.NotFound(); - - account.DisplayName = newDisplayName; - await db.SaveChangesAsync(); - - return Results.Ok(RecNetResult.Ok()); - }); - - app.MapPut("/account/me/username", async (HttpRequest request, AppDbContext db) => - { - var authHeader = request.Headers.Authorization.ToString(); - - if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) - return Results.Unauthorized(); - - var token = authHeader.Substring("Bearer ".Length); - var accountId = jwtService.ValidateAndGetAccountId(token); - - if (string.IsNullOrEmpty(accountId)) - return Results.Unauthorized(); - - if (!int.TryParse(accountId.AsSpan(), out var id)) - return Results.Unauthorized(); - - string newAccountName = ""; - - if (request.ContentLength.HasValue && request.ContentLength > 0) - { - try - { - request.EnableBuffering(); - using var reader = new StreamReader(request.Body, leaveOpen: true); - var body = await reader.ReadToEndAsync(); - - if (!string.IsNullOrWhiteSpace(body)) - { - foreach (var pair in body.Split('&')) - { - var keyValue = pair.Split('='); - if (keyValue.Length == 2) - { - var key = Uri.UnescapeDataString(keyValue[0]); - var value = Uri.UnescapeDataString(keyValue[1]); - - if (key == "username") - newAccountName = value; - } - } - } - request.Body.Position = 0; - } - catch { } - } - - var account = await db.Accounts.FindAsync(id); - if (account == null) - return Results.NotFound(); - - account.Username = newAccountName; - await db.SaveChangesAsync(); - - return Results.Ok(RecNetResult.Ok()); - }); - - app.MapGet("/account/{id}/bio", async (HttpRequest request, string id, AppDbContext db) => - { - if (!int.TryParse(id, out var accountId)) - return Results.BadRequest(); - - var bio = await db.PlayerBios.FirstOrDefaultAsync(b => b.accountId == accountId); - - if (bio == null) - { - return Results.Json(new { accountId = accountId, bio = "" }); - } - - return Results.Json(new { accountId = bio.accountId, bio = bio.bio }); - }); - - app.MapPut("/account/me/bio", async (HttpRequest request, AppDbContext db) => - { - var authHeader = request.Headers.Authorization.ToString(); - - if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) - return Results.Unauthorized(); - - var token = authHeader.Substring("Bearer ".Length); - var accountId = jwtService.ValidateAndGetAccountId(token); - - if (string.IsNullOrEmpty(accountId)) - return Results.Unauthorized(); - - if (!int.TryParse(accountId.AsSpan(), out var id)) - return Results.Unauthorized(); - - string newBio = ""; - - if (request.ContentLength.HasValue && request.ContentLength > 0) - { - try - { - request.EnableBuffering(); - using var reader = new StreamReader(request.Body, leaveOpen: true); - var body = await reader.ReadToEndAsync(); - - if (!string.IsNullOrWhiteSpace(body)) - { - foreach (var pair in body.Split('&')) - { - var keyValue = pair.Split('='); - if (keyValue.Length == 2) - { - var key = Uri.UnescapeDataString(keyValue[0]); - var value = Uri.UnescapeDataString(keyValue[1]); - - if (key == "bio") - newBio = Uri.UnescapeDataString(value); - } - } - } - request.Body.Position = 0; - } - catch { } - } - - var bio = await db.PlayerBios.FirstOrDefaultAsync(b => b.accountId == id); - - if (bio == null) - { - bio = new PlayerBio - { - accountId = id, - bio = newBio - }; - db.PlayerBios.Add(bio); - } - else - { - bio.bio = newBio; - db.PlayerBios.Update(bio); - } - - await db.SaveChangesAsync(); - - return Results.Json(new { success = true }); - }); - - app.MapPut("/account/me/profileimage", async (HttpRequest request, AppDbContext db) => - { - var authHeader = request.Headers.Authorization.ToString(); - - if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) - return Results.Unauthorized(); - - var token = authHeader.Substring("Bearer ".Length); - var accountId = jwtService.ValidateAndGetAccountId(token); - - if (string.IsNullOrEmpty(accountId)) - return Results.Unauthorized(); - - if (!int.TryParse(accountId.AsSpan(), out var id)) - return Results.Unauthorized(); - - string newProfileImage = ""; - - if (request.ContentLength.HasValue && request.ContentLength > 0) - { - try - { - request.EnableBuffering(); - using var reader = new StreamReader(request.Body, leaveOpen: true); - var body = await reader.ReadToEndAsync(); - - if (!string.IsNullOrWhiteSpace(body)) - { - foreach (var pair in body.Split('&')) - { - var keyValue = pair.Split('='); - if (keyValue.Length == 2) - { - var key = Uri.UnescapeDataString(keyValue[0]); - var value = Uri.UnescapeDataString(keyValue[1]); - - if (key == "imageName") - newProfileImage = value; - } - } - } - request.Body.Position = 0; - } - catch { } - } - - var account = await db.Accounts.FindAsync(id); - if (account == null) - return Results.NotFound(); - - account.ProfileImage = newProfileImage; - await db.SaveChangesAsync(); - - return Results.Ok(RecNetResult.Ok()); - }); + return Results.Ok(selfAccount); } -} + + [HttpGet("bulk")] + public async Task Bulk(HttpRequest request, AppDbContext db) { + StringValues ids = request.Query["id"]; + List accountIds = []; + + foreach (string? id in ids) { + if (int.TryParse(id, out int accountId)) { + accountIds.Add(accountId); + } + } + + List accounts = await db.Accounts + .Where(a => accountIds.Contains(a.AccountId.Value)) + .ToListAsync(); + + List result = accounts.Select(a => new Account { + AccountId = a.AccountId, + ProfileImage = a.ProfileImage ?? "DefaultProfileImage.jpg", + IsJunior = a.IsJunior, + Platforms = a.Platforms ?? 0, + PersonalPronouns = a.PersonalPronouns ?? 0, + IdentityFlags = a.IdentityFlags ?? 0, + Username = a.Username ?? $"Player{a.AccountId}", + DisplayName = a.DisplayName ?? $"Player{a.AccountId}", + CreatedAt = a.CreatedAt + }).ToList(); + + return Results.Json(result); + } + + [HttpGet("{id}")] + public async Task Id(HttpRequest request, string id, AppDbContext db) { + if (!int.TryParse(id, out int accountId)) + return Results.BadRequest(); + + Account? account = await db.Accounts.FindAsync(accountId); + if (account == null) + return Results.NotFound(); + + Account result = new() { + AccountId = account.AccountId, + ProfileImage = account.ProfileImage ?? "DefaultProfileImage.jpg", + IsJunior = account.IsJunior, + Platforms = account.Platforms ?? 0, + PersonalPronouns = account.PersonalPronouns ?? 0, + IdentityFlags = account.IdentityFlags ?? 0, + Username = account.Username ?? $"Player{account.AccountId}", + DisplayName = account.DisplayName ?? $"Player{account.AccountId}", + CreatedAt = account.CreatedAt + }; + + return Results.Json(result); + } + + [HttpPost("cerate")] + public async Task Create(HttpRequest httpRequest, AppDbContext db) { + int platform = 0; + string platformId = ""; + + if (httpRequest.ContentLength is > 0) { + try { + string contentType = httpRequest.ContentType ?? ""; + httpRequest.EnableBuffering(); + + using StreamReader reader = new(httpRequest.Body, leaveOpen: true); + string body = await reader.ReadToEndAsync(); + + if (!string.IsNullOrWhiteSpace(body) && contentType.Contains("application/x-www-form-urlencoded")) { + foreach (string pair in body.Split('&')) { + string[] keyValue = pair.Split('='); + if (keyValue.Length != 2) continue; + string key = Uri.UnescapeDataString(keyValue[0]); + string value = Uri.UnescapeDataString(keyValue[1]); + + switch (key) { + case "platform" when int.TryParse(value, out var parsedPlatform): + platform = parsedPlatform; + break; + case "platformId": + platformId = value; + break; + } + } + } + + httpRequest.Body.Position = 0; + } + catch { } + } + + int accountId = new Random().Next(10000, 99999); + Account account = new() { + AccountId = accountId, + ProfileImage = "DefaultProfileImage.jpg", + IsJunior = false, + Platforms = 0, + PersonalPronouns = 0, + IdentityFlags = 0, + Username = $"Player{accountId}", + DisplayName = $"Player{accountId}", + CreatedAt = DateTime.UtcNow + }; + + db.Accounts.Add(account); + + if (!string.IsNullOrEmpty(platformId)) { + db.CachedLogins.Add(new CachedLogin { + AccountId = accountId, + Platform = (PlatformType)platform, + PlatformID = platformId, + LastLoginTime = DateTime.UtcNow, + RequirePassword = false + }); + } + + await db.SaveChangesAsync(); + + // create players dorm room + int maxRoomId = await db.Rooms.MaxAsync(r => (int?)r.RoomId) ?? 0; + int maxId = await db.Rooms.MaxAsync(r => (int?)r.Id) ?? 0; + int dormRoomId = maxRoomId + 1; + //int dormId = maxId + 1; + Room dormRoom = new Room { + Id = maxId + 1, + RoomId = dormRoomId, + Name = "DormRoom", + Description = "Your personal room", + CreatorAccountId = accountId, + ImageName = "", + State = 0, + Accessibility = 0, + SupportsLevelVoting = false, + IsRRO = false, + IsDorm = true, + CloningAllowed = false, + SupportsVRLow = true, + SupportsQuest2 = true, + SupportsMobile = true, + SupportsScreens = true, + SupportsWalkVR = true, + SupportsTeleportVR = true, + SupportsJuniors = true, + MinLevel = 0, + WarningMask = 0, + CustomWarning = null, + DisableMicAutoMute = false, + DisableRoomComments = false, + EncryptVoiceChat = false, + CreatedAt = DateTime.UtcNow, + Tags = "[]" + }; + db.Rooms.Add(dormRoom); + + // Create a sub room for the dorm + SubRoom dormSubRoom = new SubRoom { + RoomId = dormRoomId, + SubRoomId = 1, + Name = "DormRoom", + DataBlob = "", + IsSandbox = false, + MaxPlayers = 4, + Accessibility = 0, + UnitySceneId = "76d98498-60a1-430c-ab76-b54a29b7a163", // Dorm scene ID + DataSavedAt = DateTime.UtcNow + }; + db.SubRooms.Add(dormSubRoom); + + await db.SaveChangesAsync(); + return Results.Ok(RecNetResult.Ok(account)); + } + + [HttpPut("me/displayname")] + public async Task PutMeDisplayName(HttpRequest request, AppDbContext db, JwtTokenService jwtService) { + string authHeader = request.Headers.Authorization.ToString(); + + if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + return Results.Unauthorized(); + + string token = authHeader.Substring("Bearer ".Length); + string? accountId = jwtService.ValidateAndGetAccountId(token); + + if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out var id)) + return Results.Unauthorized(); + + string newDisplayName = ""; + + if (request.ContentLength is > 0) { + try { + request.EnableBuffering(); + using StreamReader reader = new(request.Body, leaveOpen: true); + string body = await reader.ReadToEndAsync(); + + if (!string.IsNullOrWhiteSpace(body)) { + foreach (string pair in body.Split('&')) { + string[] keyValue = pair.Split('='); + if (keyValue.Length != 2) continue; + string key = Uri.UnescapeDataString(keyValue[0]); + string value = Uri.UnescapeDataString(keyValue[1]); + + if (key == "displayName") + newDisplayName = value; + } + } + + request.Body.Position = 0; + } + catch { } + } + + Account? account = await db.Accounts.FindAsync(id); + if (account == null) + return Results.NotFound(); + + account.DisplayName = newDisplayName; + await db.SaveChangesAsync(); + + return Results.Ok(RecNetResult.Ok()); + } + + [HttpPut("me/username")] + public async Task PutMeUsername(HttpRequest request, AppDbContext db, JwtTokenService jwtService) { + string authHeader = request.Headers.Authorization.ToString(); + + if (string.IsNullOrEmpty(authHeader) || + !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + return Results.Unauthorized(); + + string token = authHeader.Substring("Bearer ".Length); + string? accountId = jwtService.ValidateAndGetAccountId(token); + + if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out int id)) + return Results.Unauthorized(); + + string newAccountName = ""; + + if (request.ContentLength is > 0) { + try { + request.EnableBuffering(); + using StreamReader reader = new(request.Body, leaveOpen: true); + string body = await reader.ReadToEndAsync(); + + if (!string.IsNullOrWhiteSpace(body)) { + foreach (string pair in body.Split('&')) { + string[] keyValue = pair.Split('='); + if (keyValue.Length != 2) continue; + string key = Uri.UnescapeDataString(keyValue[0]); + string value = Uri.UnescapeDataString(keyValue[1]); + + if (key == "username") + newAccountName = value; + } + } + + request.Body.Position = 0; + } + catch { } + } + + Account? account = await db.Accounts.FindAsync(id); + if (account == null) + return Results.NotFound(); + + account.Username = newAccountName; + await db.SaveChangesAsync(); + + return Results.Ok(RecNetResult.Ok()); + } + + [HttpGet("{id}/bio")] + public async Task GetBio(string id, AppDbContext db) { + if (!int.TryParse(id, out int accountId)) + return Results.BadRequest(); + + PlayerBio? bio = await db.PlayerBios.FirstOrDefaultAsync(b => b.accountId == accountId); + + return Results.Json(bio == null + ? new { accountId = accountId, bio = "" } + : new { accountId = bio.accountId, bio = bio.bio }); + } + + [HttpPut("me/bio")] + public async Task PutMeBio(HttpRequest request, AppDbContext db, JwtTokenService jwtService) { + string authHeader = request.Headers.Authorization.ToString(); + + if (string.IsNullOrEmpty(authHeader) || + !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + return Results.Unauthorized(); + + string token = authHeader.Substring("Bearer ".Length); + string? accountId = jwtService.ValidateAndGetAccountId(token); + + if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out int id)) + return Results.Unauthorized(); + + string newBio = ""; + + if (request.ContentLength is > 0) { + try { + request.EnableBuffering(); + using StreamReader reader = new StreamReader(request.Body, leaveOpen: true); + string body = await reader.ReadToEndAsync(); + + if (!string.IsNullOrWhiteSpace(body)) { + foreach (string pair in body.Split('&')) { + string[] keyValue = pair.Split('='); + if (keyValue.Length != 2) continue; + string key = Uri.UnescapeDataString(keyValue[0]); + string value = Uri.UnescapeDataString(keyValue[1]); + + if (key == "bio") + newBio = Uri.UnescapeDataString(value); + } + } + + request.Body.Position = 0; + } + catch { } + } + + PlayerBio? bio = await db.PlayerBios.FirstOrDefaultAsync(b => b.accountId == id); + + if (bio == null) { + bio = new PlayerBio { + accountId = id, + bio = newBio + }; + db.PlayerBios.Add(bio); + } + else { + bio.bio = newBio; + db.PlayerBios.Update(bio); + } + + await db.SaveChangesAsync(); + + return Results.Json(new { success = true }); + } + + [HttpPut("me/profileimage")] + public async Task PutMeProfileImage(HttpRequest request, AppDbContext db, JwtTokenService jwtService) { + string authHeader = request.Headers.Authorization.ToString(); + + if (string.IsNullOrEmpty(authHeader) || + !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + return Results.Unauthorized(); + + string token = authHeader.Substring("Bearer ".Length); + string? accountId = jwtService.ValidateAndGetAccountId(token); + + if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out int id)) + return Results.Unauthorized(); + + string newProfileImage = ""; + + if (request.ContentLength is > 0) { + try { + request.EnableBuffering(); + using StreamReader reader = new(request.Body, leaveOpen: true); + string body = await reader.ReadToEndAsync(); + + if (!string.IsNullOrWhiteSpace(body)) { + foreach (var pair in body.Split('&')) { + string[] keyValue = pair.Split('='); + if (keyValue.Length != 2) continue; + string key = Uri.UnescapeDataString(keyValue[0]); + string value = Uri.UnescapeDataString(keyValue[1]); + + if (key == "imageName") + newProfileImage = value; + } + } + + request.Body.Position = 0; + } + catch { } + } + + Account? account = await db.Accounts.FindAsync(id); + if (account == null) + return Results.NotFound(); + + account.ProfileImage = newProfileImage; + await db.SaveChangesAsync(); + + return Results.Ok(RecNetResult.Ok()); + } + + [HttpGet("parentalcontrol/me")] + public async Task ParentalControl(HttpRequest request, JwtTokenService jwtService) { + string authHeader = request.Headers.Authorization.ToString(); + + if (string.IsNullOrEmpty(authHeader) || + !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) + return Results.Unauthorized(); + + string token = authHeader.Substring("Bearer ".Length); + string? accountId = jwtService.ValidateAndGetAccountId(token); + + if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out _)) + return Results.Unauthorized(); + + return Results.Content($"{{\"accountId\":{accountId},\"disallowInAppPurchases\":false}}", "application/json"); + } +} \ No newline at end of file diff --git a/CannedNet/Services/Controllers/ChatController.cs b/CannedNet/Services/Controllers/ChatController.cs index 348548c..352c91a 100644 --- a/CannedNet/Services/Controllers/ChatController.cs +++ b/CannedNet/Services/Controllers/ChatController.cs @@ -1,13 +1,13 @@ -using CannedNet.Services.Infrastructure; +using Microsoft.AspNetCore.Mvc; namespace CannedNet.Services.Controllers; -public class ChatController +[ApiController] +public class ChatController : ControllerBase { - public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); - - public void MapEndpoints(WebApplication app) - { - app.MapGet("/thread", () => Results.Content("[]", "application/json")); + [HttpGet("thread")] + public async Task Get() { + return Results.Content("[]", "application/json"); + } } diff --git a/CannedNet/Services/Controllers/ImageController.cs b/CannedNet/Services/Controllers/ImageController.cs index 7bed144..fc159c1 100644 --- a/CannedNet/Services/Controllers/ImageController.cs +++ b/CannedNet/Services/Controllers/ImageController.cs @@ -1,5 +1,5 @@ -using System.Net.Mime; -using CannedNet.Services.Infrastructure; +using CannedNet.Services.Infrastructure; +using Microsoft.AspNetCore.Mvc; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; @@ -7,104 +7,91 @@ using SixLabors.ImageSharp.Processing; namespace CannedNet.Services.Controllers; -public class ImageController +[ApiController, Route("")] +public class ImageController : ControllerBase { private static readonly byte[] PlaceholderJpeg; - static ImageController() - { + static ImageController() { Signatures.Init(); - using var image = new Image(64, 64); + using Image image = new(64, 64); image.Mutate(x => x.BackgroundColor(Color.FromRgb(32, 32, 32))); - using var ms = new MemoryStream(); + using MemoryStream ms = new(); image.SaveAsJpeg(ms, new JpegEncoder { Quality = 50 }); PlaceholderJpeg = ms.ToArray(); } - public WebApplicationBuilder Initialize(string[]? args = null) - => ServiceExtensions.CreateRecNetBuilder(args); + public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); - public void MapEndpoints(WebApplication app) - { - var imagesDir = Path.Combine("Images"); + [HttpGet("{imageName}")] + public async Task GetImage(HttpContext context, string imageName) { + string imagesDir = Path.Combine("Images"); Directory.CreateDirectory(imagesDir); + string filePath = Path.Combine(imagesDir, imageName); - app.MapGet("/{imageName}", async (HttpContext context, string imageName) => - { - var filePath = Path.Combine(imagesDir, imageName); + byte[] imageBytes; + string contentType; - byte[] imageBytes; - string contentType; + if (System.IO.File.Exists(filePath)) { + imageBytes = await System.IO.File.ReadAllBytesAsync(filePath); + string ext = Path.GetExtension(imageName).ToLowerInvariant(); + contentType = ext switch { + ".jpg" or ".jpeg" => "image/jpeg", + ".gif" => "image/gif", + ".webp" => "image/webp", + ".bmp" => "image/bmp", + _ => "image/png" + }; + } + else { + imageBytes = PlaceholderJpeg; + contentType = "image/jpeg"; + } - if (File.Exists(filePath)) - { - imageBytes = await File.ReadAllBytesAsync(filePath); - var ext = Path.GetExtension(imageName).ToLowerInvariant(); - contentType = ext switch - { - ".jpg" or ".jpeg" => "image/jpeg", - ".gif" => "image/gif", - ".webp" => "image/webp", - ".bmp" => "image/bmp", - _ => "image/png" - }; - } - else - { - imageBytes = PlaceholderJpeg; - contentType = "image/jpeg"; - } + string? cropSquare = context.Request.Query["cropSquare"].FirstOrDefault(); + string? widthStr = context.Request.Query["width"].FirstOrDefault(); + string? heightStr = context.Request.Query["height"].FirstOrDefault(); - var cropSquare = context.Request.Query["cropSquare"].FirstOrDefault(); - var widthStr = context.Request.Query["width"].FirstOrDefault(); - var heightStr = context.Request.Query["height"].FirstOrDefault(); - - if (string.IsNullOrEmpty(widthStr) && string.IsNullOrEmpty(heightStr) && string.IsNullOrEmpty(cropSquare)) - { - SignImageResponse(context, ref imageBytes); - return Results.File(imageBytes, contentType); - } - - using var image = Image.Load(imageBytes); - - var resizeWidth = 0; - var resizeHeight = 0; - - if (!string.IsNullOrEmpty(cropSquare) && cropSquare != "0" && cropSquare != "false") - { - var size = Math.Min(image.Width, image.Height); - var x = (image.Width - size) / 2; - var y = (image.Height - size) / 2; - image.Mutate(img => img.Crop(new Rectangle(x, y, size, size))); - } - - if (int.TryParse(widthStr, out var w)) - resizeWidth = w; - - if (int.TryParse(heightStr, out var h)) - resizeHeight = h; - - if (resizeWidth > 0 || resizeHeight > 0) - { - image.Mutate(x => x.Resize(resizeWidth, resizeHeight)); - } - - using var output = new MemoryStream(); - await image.SaveAsJpegAsync(output, new JpegEncoder { Quality = 85 }); - imageBytes = output.ToArray(); + if (string.IsNullOrEmpty(widthStr) && string.IsNullOrEmpty(heightStr) && string.IsNullOrEmpty(cropSquare)) { SignImageResponse(context, ref imageBytes); - return Results.File(imageBytes, "image/jpeg"); - }); + return Results.File(imageBytes, contentType); + } + + using Image image = Image.Load(imageBytes); + + int resizeWidth = 0; + int resizeHeight = 0; + + if (!string.IsNullOrEmpty(cropSquare) && cropSquare != "0" && cropSquare != "false") { + int size = Math.Min(image.Width, image.Height); + int x = (image.Width - size) / 2; + int y = (image.Height - size) / 2; + image.Mutate(img => img.Crop(new Rectangle(x, y, size, size))); + } + + if (int.TryParse(widthStr, out int w)) + resizeWidth = w; + + if (int.TryParse(heightStr, out int h)) + resizeHeight = h; + + if (resizeWidth > 0 || resizeHeight > 0) { + image.Mutate(x => x.Resize(resizeWidth, resizeHeight)); + } + + using MemoryStream output = new(); + await image.SaveAsJpegAsync(output, new JpegEncoder { Quality = 85 }); + imageBytes = output.ToArray(); + SignImageResponse(context, ref imageBytes); + return Results.File(imageBytes, "image/jpeg"); } - private static void SignImageResponse(HttpContext context, ref byte[] imageBytes) - { + private static void SignImageResponse(HttpContext context, ref byte[] imageBytes) { if (context.Request.Query["sig"] != "p1") return; - var signature = Signatures.Sign(imageBytes); - if (signature != null) - { + string? signature = Signatures.Sign(imageBytes); + if (signature != null) { context.Response.Headers["Content-Signature"] = $"key-id=KEY:RSA:p1.rec.net; data={signature}"; } } diff --git a/CannedNet/Services/Controllers/NSController.cs b/CannedNet/Services/Controllers/NSController.cs index 1c54866..5184db5 100644 --- a/CannedNet/Services/Controllers/NSController.cs +++ b/CannedNet/Services/Controllers/NSController.cs @@ -1,18 +1,14 @@ -using CannedNet.Services.Infrastructure; +using Microsoft.AspNetCore.Mvc; namespace CannedNet.Services.Controllers; -public class NSController +//TODO: proper NameServer configs +[ApiController, Route("")] +public class NSController : ControllerBase { - public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); - - public void MapEndpoints(WebApplication app) - { - // Rec Room automatically calls this and sets the endpoints that correspond to services in the JSON file. These will be moved to the main server config once I get there. - app.MapGet("/", () => - { - var json = File.ReadAllText("JSON/endpoints.json"); - return Results.Content(json, "application/json"); - }); + [HttpGet] + public async Task Get() { + string json = await System.IO.File.ReadAllTextAsync("JSON/endpoints.json"); + return Results.Content(json, "application/json"); } } diff --git a/CannedNet/Services/Controllers/NotifyController.cs b/CannedNet/Services/Controllers/NotifyController.cs index 7d3fae6..4952a13 100644 --- a/CannedNet/Services/Controllers/NotifyController.cs +++ b/CannedNet/Services/Controllers/NotifyController.cs @@ -1,4 +1,4 @@ -using CannedNet.Hubs; +/*using CannedNet.Hubs; using CannedNet.Services.Infrastructure; using Microsoft.AspNetCore.SignalR; @@ -16,3 +16,4 @@ public class NotifyController NotificationService.SetHubContext(hubContext); } } +*/ \ No newline at end of file diff --git a/CannedNet/Services/Infrastructure/ServiceRegistry.cs b/CannedNet/Services/Infrastructure/ServiceRegistry.cs index 3631204..86d8d95 100644 --- a/CannedNet/Services/Infrastructure/ServiceRegistry.cs +++ b/CannedNet/Services/Infrastructure/ServiceRegistry.cs @@ -1,4 +1,3 @@ -using System.Net.Mime; using CannedNet.Services.Controllers; namespace CannedNet.Services.Infrastructure; @@ -16,15 +15,15 @@ public static class Services { public static readonly List All = [ - new() { Name = "NS", Port = 5001, ServiceType = typeof(NSController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new NSController().MapEndpoints(app) }, + //new() { Name = "NS", Port = 5001, ServiceType = typeof(NSController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new NSController().MapEndpoints(app) }, new() { Name = "API", Port = 5000, ServiceType = typeof(APIController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new APIController().MapEndpoints(app) }, - new() { Name = "Auth", Port = 5002, ServiceType = typeof(AuthController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, jwt) => new AuthController().MapEndpoints(app) }, - new() { Name = "Chat", Port = 5003, ServiceType = typeof(ChatController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new ChatController().MapEndpoints(app) }, + //new() { Name = "Auth", Port = 5002, ServiceType = typeof(AuthController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, jwt) => new AuthController().MapEndpoints(app) }, + //new() { Name = "Chat", Port = 5003, ServiceType = typeof(ChatController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new ChatController().MapEndpoints(app) }, new() { Name = "Match", Port = 5004, ServiceType = typeof(MatchmakingController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new MatchmakingController().MapEndpoints(app) }, - new() { Name = "Accounts", Port = 5005, ServiceType = typeof(AccountsController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, jwt) => new AccountsController().MapEndpoints(app, jwt) }, - new() { Name = "Notify", Port = 5006, ServiceType = typeof(NotifyController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new NotifyController().MapEndpoints(app) }, + //new() { Name = "Accounts", Port = 5005, ServiceType = typeof(AccountsController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, jwt) => new AccountsController().MapEndpoints(app, jwt) }, + //new() { Name = "Notify", Port = 5006, ServiceType = typeof(NotifyController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new NotifyController().MapEndpoints(app) }, new() { Name = "CDN", Port = 5007, ServiceType = typeof(CDNController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new CDNController().MapEndpoints(app) }, - new() { Name = "Image", Port = 5008, ServiceType = typeof(ImageController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new ImageController().MapEndpoints(app) }, + //new() { Name = "Image", Port = 5008, ServiceType = typeof(ImageController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new ImageController().MapEndpoints(app) }, new() { Name = "Clubs", Port = 5009, ServiceType = typeof(ClubsController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new ClubsController().MapEndpoints(app) }, new() { Name = "Commerce", Port = 5010, ServiceType = typeof(CommerceController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new CommerceController().MapEndpoints(app) }, new() { Name = "Econ", Port = 5011, ServiceType = typeof(EconController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new EconController().MapEndpoints(app) },