Few more migrations

This commit is contained in:
notabird
2026-06-07 11:25:49 -07:00
parent a1097c4070
commit f4fe623e24
7 changed files with 637 additions and 645 deletions
+100 -37
View File
@@ -1,48 +1,111 @@
using CannedNet; using CannedNet.Data;
using CannedNet.Hubs;
using CannedNet.Services; using CannedNet.Services;
using CannedNet.Services.Infrastructure; using CannedNet.Services.Infrastructure;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore; 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(); public static async Task Main(string[] args) {
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddLogging(logging => //builder.Services.AddLogging(logging => {
{ // logging.AddConsole();
logging.AddConsole(); // logging.SetMinimumLevel(LogLevel.Debug);
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<AppDbContext>(options => options.UseNpgsql(connectionString));
builder.Services.AddSingleton<NotificationService>();
builder.Services.AddScoped<StorefrontFillService>();
builder.Services.AddScoped<JwtTokenService>();
builder.Services.AddSignalR();
WebApplication app = builder.Build();
app.MapHub<NotificationsHub>("/hub/v1");
app.UseHttpsRedirection();
//app.UseRequestLogging();
app.MapControllers();
IHubContext<NotificationsHub> hubContext = app.Services.GetRequiredService<IHubContext<NotificationsHub>>();
NotificationService.SetHubContext(hubContext);
//if (app.Environment.IsDevelopment()) {
app.UseSwagger();
app.UseSwaggerUI(options => {
options.SwaggerEndpoint("/swagger/v1/swagger.json", "CannedNet v1");
options.RoutePrefix = "swagger/ui/";
}); });
//}
service.ConfigureBuilder(builder); using IServiceScope scope = app.Services.CreateScope();
builder.WebHost.UseUrls($"http://*:{service.Port}"); AppDbContext db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
builder.Configuration.AddJsonFile($"AppConfigs/appsettings.{service.Name}.json", optional: true, reloadOnChange: true);
apps.Add((builder.Build(), service)); StorefrontFillService seedingService = scope.ServiceProvider.GetRequiredService<StorefrontFillService>();
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<CannedNet.Data.AppDbContext>();
// try
// {
// dbContext.Database.Migrate();
// }
// catch (Exception ex)
// {
// dbContext.Database.EnsureCreated();
// }
//
// // automatically fill out storefronts tables from the JSON's
// var seedingService = scope.ServiceProvider.GetRequiredService<StorefrontFillService>();
// await seedingService.FillStorefrontsAsync();
//
// var jwtService = scope.ServiceProvider.GetRequiredService<JwtTokenService>();
//
// 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<CannedNet.Data.AppDbContext>();
try
{
dbContext.Database.Migrate();
}
catch (Exception ex)
{
dbContext.Database.EnsureCreated();
}
// automatically fill out storefronts tables from the JSON's
var seedingService = scope.ServiceProvider.GetRequiredService<StorefrontFillService>();
await seedingService.FillStorefrontsAsync();
var jwtService = scope.ServiceProvider.GetRequiredService<JwtTokenService>();
foreach (var (app, service) in apps)
{
service.MapEndpoints(app, jwtService);
app.UseRequestLogging();
}
await Task.WhenAll(apps.Select(t => t.App.RunAsync()));
@@ -1,43 +1,37 @@
using CannedNet.Data; using CannedNet.Data;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using CannedNet.Services; using Microsoft.AspNetCore.Mvc;
using CannedNet.Services.Infrastructure; using Microsoft.Extensions.Primitives;
namespace CannedNet.Services.Controllers; 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<IResult> Root() {
// return Task.FromResult(Results.Ok("FUCKL MYSJDIOJD"));
//}
public void MapEndpoints(WebApplication app, JwtTokenService jwtService) [HttpGet("me")]
{ public async Task<IResult> Me(HttpRequest request, AppDbContext db, JwtTokenService jwtService) {
app.MapGet("/", (HttpRequest request) => string authHeader = request.Headers.Authorization.ToString();
{
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)) if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized(); return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length); string token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token); string? accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId)) if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out int id))
return Results.Unauthorized(); return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id)) Account? account = await db.Accounts.FindAsync(id);
return Results.Unauthorized();
var account = await db.Accounts.FindAsync(id);
if (account == null) if (account == null)
return Results.NotFound(); return Results.NotFound();
var selfAccount = new SelfAccount SelfAccount selfAccount = new SelfAccount {
{
AccountId = id, AccountId = id,
ProfileImage = account.ProfileImage ?? "hdqeamlcmatc6qzoi2ybgf0ddijjcf.jpg", ProfileImage = account.ProfileImage ?? "hdqeamlcmatc6qzoi2ybgf0ddijjcf.jpg",
IsJunior = account.IsJunior, IsJunior = account.IsJunior,
@@ -56,27 +50,24 @@ public class AccountsController
}; };
return Results.Ok(selfAccount); return Results.Ok(selfAccount);
}); }
app.MapGet("/account/bulk", async (HttpRequest request, AppDbContext db) => [HttpGet("bulk")]
{ public async Task<IResult> Bulk(HttpRequest request, AppDbContext db) {
var ids = request.Query["id"]; StringValues ids = request.Query["id"];
var accountIds = new List<int>(); List<int> accountIds = [];
foreach (var id in ids) foreach (string? id in ids) {
{ if (int.TryParse(id, out int accountId)) {
if (int.TryParse(id, out var accountId))
{
accountIds.Add(accountId); accountIds.Add(accountId);
} }
} }
var accounts = await db.Accounts List<Account> accounts = await db.Accounts
.Where(a => accountIds.Contains(a.AccountId.Value)) .Where(a => accountIds.Contains(a.AccountId.Value))
.ToListAsync(); .ToListAsync();
var result = accounts.Select(a => new Account List<Account> result = accounts.Select(a => new Account {
{
AccountId = a.AccountId, AccountId = a.AccountId,
ProfileImage = a.ProfileImage ?? "DefaultProfileImage.jpg", ProfileImage = a.ProfileImage ?? "DefaultProfileImage.jpg",
IsJunior = a.IsJunior, IsJunior = a.IsJunior,
@@ -89,19 +80,18 @@ public class AccountsController
}).ToList(); }).ToList();
return Results.Json(result); return Results.Json(result);
}); }
app.MapGet("/account/{id}", async (HttpRequest request, string id, AppDbContext db) => [HttpGet("{id}")]
{ public async Task<IResult> Id(HttpRequest request, string id, AppDbContext db) {
if (!int.TryParse(id, out var accountId)) if (!int.TryParse(id, out int accountId))
return Results.BadRequest(); return Results.BadRequest();
var account = await db.Accounts.FindAsync(accountId); Account? account = await db.Accounts.FindAsync(accountId);
if (account == null) if (account == null)
return Results.NotFound(); return Results.NotFound();
var result = new Account Account result = new() {
{
AccountId = account.AccountId, AccountId = account.AccountId,
ProfileImage = account.ProfileImage ?? "DefaultProfileImage.jpg", ProfileImage = account.ProfileImage ?? "DefaultProfileImage.jpg",
IsJunior = account.IsJunior, IsJunior = account.IsJunior,
@@ -114,48 +104,46 @@ public class AccountsController
}; };
return Results.Json(result); return Results.Json(result);
}); }
app.MapPost("/account/create", async (HttpRequest httpRequest, AppDbContext db) => [HttpPost("cerate")]
{ public async Task<IResult> Create(HttpRequest httpRequest, AppDbContext db) {
int platform = 0; int platform = 0;
string platformId = ""; string platformId = "";
if (httpRequest.ContentLength.HasValue && httpRequest.ContentLength > 0) if (httpRequest.ContentLength is > 0) {
{ try {
try string contentType = httpRequest.ContentType ?? "";
{
var contentType = httpRequest.ContentType ?? "";
httpRequest.EnableBuffering(); httpRequest.EnableBuffering();
using var reader = new StreamReader(httpRequest.Body, leaveOpen: true); using StreamReader reader = new(httpRequest.Body, leaveOpen: true);
var body = await reader.ReadToEndAsync(); string body = await reader.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(body) && contentType.Contains("application/x-www-form-urlencoded")) if (!string.IsNullOrWhiteSpace(body) && contentType.Contains("application/x-www-form-urlencoded")) {
{ foreach (string pair in body.Split('&')) {
foreach (var pair in body.Split('&')) string[] keyValue = pair.Split('=');
{ if (keyValue.Length != 2) continue;
var keyValue = pair.Split('='); string key = Uri.UnescapeDataString(keyValue[0]);
if (keyValue.Length == 2) string value = Uri.UnescapeDataString(keyValue[1]);
{
var key = Uri.UnescapeDataString(keyValue[0]);
var value = Uri.UnescapeDataString(keyValue[1]);
if (key == "platform" && int.TryParse(value, out var parsedPlatform)) switch (key) {
case "platform" when int.TryParse(value, out var parsedPlatform):
platform = parsedPlatform; platform = parsedPlatform;
else if (key == "platformId") break;
case "platformId":
platformId = value; platformId = value;
break;
} }
} }
} }
httpRequest.Body.Position = 0; httpRequest.Body.Position = 0;
} }
catch { } catch { }
} }
var accountId = new Random().Next(10000, 99999); int accountId = new Random().Next(10000, 99999);
var account = new Account Account account = new() {
{
AccountId = accountId, AccountId = accountId,
ProfileImage = "DefaultProfileImage.jpg", ProfileImage = "DefaultProfileImage.jpg",
IsJunior = false, IsJunior = false,
@@ -169,10 +157,8 @@ public class AccountsController
db.Accounts.Add(account); db.Accounts.Add(account);
if (!string.IsNullOrEmpty(platformId)) if (!string.IsNullOrEmpty(platformId)) {
{ db.CachedLogins.Add(new CachedLogin {
db.CachedLogins.Add(new CachedLogin
{
AccountId = accountId, AccountId = accountId,
Platform = (PlatformType)platform, Platform = (PlatformType)platform,
PlatformID = platformId, PlatformID = platformId,
@@ -184,12 +170,11 @@ public class AccountsController
await db.SaveChangesAsync(); await db.SaveChangesAsync();
// create players dorm room // create players dorm room
var maxRoomId = await db.Rooms.MaxAsync(r => (int?)r.RoomId) ?? 0; int maxRoomId = await db.Rooms.MaxAsync(r => (int?)r.RoomId) ?? 0;
var maxId = await db.Rooms.MaxAsync(r => (int?)r.Id) ?? 0; int maxId = await db.Rooms.MaxAsync(r => (int?)r.Id) ?? 0;
var dormRoomId = maxRoomId + 1; int dormRoomId = maxRoomId + 1;
var dormId = maxId + 1; //int dormId = maxId + 1;
var dormRoom = new Room Room dormRoom = new Room {
{
Id = maxId + 1, Id = maxId + 1,
RoomId = dormRoomId, RoomId = dormRoomId,
Name = "DormRoom", Name = "DormRoom",
@@ -221,8 +206,7 @@ public class AccountsController
db.Rooms.Add(dormRoom); db.Rooms.Add(dormRoom);
// Create a sub room for the dorm // Create a sub room for the dorm
var dormSubRoom = new SubRoom SubRoom dormSubRoom = new SubRoom {
{
RoomId = dormRoomId, RoomId = dormRoomId,
SubRoomId = 1, SubRoomId = 1,
Name = "DormRoom", Name = "DormRoom",
@@ -236,75 +220,48 @@ public class AccountsController
db.SubRooms.Add(dormSubRoom); db.SubRooms.Add(dormSubRoom);
await db.SaveChangesAsync(); await db.SaveChangesAsync();
return Results.Ok(RecNetResult.Ok(account));
}
return Results.Ok(RecNetResultWithValue<Account>.Ok(account)); [HttpPut("me/displayname")]
}); public async Task<IResult> PutMeDisplayName(HttpRequest request, AppDbContext db, JwtTokenService jwtService) {
app.MapGet("/parentalcontrol/me", async (HttpRequest request, AppDbContext db) => string authHeader = request.Headers.Authorization.ToString();
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized(); return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length); string token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token); string? accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId)) if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out var id))
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(); return Results.Unauthorized();
string newDisplayName = ""; string newDisplayName = "";
if (request.ContentLength.HasValue && request.ContentLength > 0) if (request.ContentLength is > 0) {
{ try {
try
{
request.EnableBuffering(); request.EnableBuffering();
using var reader = new StreamReader(request.Body, leaveOpen: true); using StreamReader reader = new(request.Body, leaveOpen: true);
var body = await reader.ReadToEndAsync(); string body = await reader.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(body)) if (!string.IsNullOrWhiteSpace(body)) {
{ foreach (string pair in body.Split('&')) {
foreach (var pair in body.Split('&')) string[] keyValue = pair.Split('=');
{ if (keyValue.Length != 2) continue;
var keyValue = pair.Split('='); string key = Uri.UnescapeDataString(keyValue[0]);
if (keyValue.Length == 2) string value = Uri.UnescapeDataString(keyValue[1]);
{
var key = Uri.UnescapeDataString(keyValue[0]);
var value = Uri.UnescapeDataString(keyValue[1]);
if (key == "displayName") if (key == "displayName")
newDisplayName = value; newDisplayName = value;
} }
} }
}
request.Body.Position = 0; request.Body.Position = 0;
} }
catch { } catch { }
} }
var account = await db.Accounts.FindAsync(id); Account? account = await db.Accounts.FindAsync(id);
if (account == null) if (account == null)
return Results.NotFound(); return Results.NotFound();
@@ -312,55 +269,48 @@ public class AccountsController
await db.SaveChangesAsync(); await db.SaveChangesAsync();
return Results.Ok(RecNetResult.Ok()); return Results.Ok(RecNetResult.Ok());
}); }
app.MapPut("/account/me/username", async (HttpRequest request, AppDbContext db) => [HttpPut("me/username")]
{ public async Task<IResult> PutMeUsername(HttpRequest request, AppDbContext db, JwtTokenService jwtService) {
var authHeader = request.Headers.Authorization.ToString(); string authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) if (string.IsNullOrEmpty(authHeader) ||
!authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized(); return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length); string token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token); string? accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId)) if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out int id))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized(); return Results.Unauthorized();
string newAccountName = ""; string newAccountName = "";
if (request.ContentLength.HasValue && request.ContentLength > 0) if (request.ContentLength is > 0) {
{ try {
try
{
request.EnableBuffering(); request.EnableBuffering();
using var reader = new StreamReader(request.Body, leaveOpen: true); using StreamReader reader = new(request.Body, leaveOpen: true);
var body = await reader.ReadToEndAsync(); string body = await reader.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(body)) if (!string.IsNullOrWhiteSpace(body)) {
{ foreach (string pair in body.Split('&')) {
foreach (var pair in body.Split('&')) string[] keyValue = pair.Split('=');
{ if (keyValue.Length != 2) continue;
var keyValue = pair.Split('='); string key = Uri.UnescapeDataString(keyValue[0]);
if (keyValue.Length == 2) string value = Uri.UnescapeDataString(keyValue[1]);
{
var key = Uri.UnescapeDataString(keyValue[0]);
var value = Uri.UnescapeDataString(keyValue[1]);
if (key == "username") if (key == "username")
newAccountName = value; newAccountName = value;
} }
} }
}
request.Body.Position = 0; request.Body.Position = 0;
} }
catch { } catch { }
} }
var account = await db.Accounts.FindAsync(id); Account? account = await db.Accounts.FindAsync(id);
if (account == null) if (account == null)
return Results.NotFound(); return Results.NotFound();
@@ -368,82 +318,69 @@ public class AccountsController
await db.SaveChangesAsync(); await db.SaveChangesAsync();
return Results.Ok(RecNetResult.Ok()); 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 }); [HttpGet("{id}/bio")]
}); public async Task<IResult> GetBio(string id, AppDbContext db) {
if (!int.TryParse(id, out int accountId))
return Results.BadRequest();
app.MapPut("/account/me/bio", async (HttpRequest request, AppDbContext db) => PlayerBio? bio = await db.PlayerBios.FirstOrDefaultAsync(b => b.accountId == accountId);
{
var authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) return Results.Json(bio == null
? new { accountId = accountId, bio = "" }
: new { accountId = bio.accountId, bio = bio.bio });
}
[HttpPut("me/bio")]
public async Task<IResult> 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(); return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length); string token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token); string? accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId)) if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out int id))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized(); return Results.Unauthorized();
string newBio = ""; string newBio = "";
if (request.ContentLength.HasValue && request.ContentLength > 0) if (request.ContentLength is > 0) {
{ try {
try
{
request.EnableBuffering(); request.EnableBuffering();
using var reader = new StreamReader(request.Body, leaveOpen: true); using StreamReader reader = new StreamReader(request.Body, leaveOpen: true);
var body = await reader.ReadToEndAsync(); string body = await reader.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(body)) if (!string.IsNullOrWhiteSpace(body)) {
{ foreach (string pair in body.Split('&')) {
foreach (var pair in body.Split('&')) string[] keyValue = pair.Split('=');
{ if (keyValue.Length != 2) continue;
var keyValue = pair.Split('='); string key = Uri.UnescapeDataString(keyValue[0]);
if (keyValue.Length == 2) string value = Uri.UnescapeDataString(keyValue[1]);
{
var key = Uri.UnescapeDataString(keyValue[0]);
var value = Uri.UnescapeDataString(keyValue[1]);
if (key == "bio") if (key == "bio")
newBio = Uri.UnescapeDataString(value); newBio = Uri.UnescapeDataString(value);
} }
} }
}
request.Body.Position = 0; request.Body.Position = 0;
} }
catch { } catch { }
} }
var bio = await db.PlayerBios.FirstOrDefaultAsync(b => b.accountId == id); PlayerBio? bio = await db.PlayerBios.FirstOrDefaultAsync(b => b.accountId == id);
if (bio == null) if (bio == null) {
{ bio = new PlayerBio {
bio = new PlayerBio
{
accountId = id, accountId = id,
bio = newBio bio = newBio
}; };
db.PlayerBios.Add(bio); db.PlayerBios.Add(bio);
} }
else else {
{
bio.bio = newBio; bio.bio = newBio;
db.PlayerBios.Update(bio); db.PlayerBios.Update(bio);
} }
@@ -451,55 +388,48 @@ public class AccountsController
await db.SaveChangesAsync(); await db.SaveChangesAsync();
return Results.Json(new { success = true }); return Results.Json(new { success = true });
}); }
app.MapPut("/account/me/profileimage", async (HttpRequest request, AppDbContext db) => [HttpPut("me/profileimage")]
{ public async Task<IResult> PutMeProfileImage(HttpRequest request, AppDbContext db, JwtTokenService jwtService) {
var authHeader = request.Headers.Authorization.ToString(); string authHeader = request.Headers.Authorization.ToString();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) if (string.IsNullOrEmpty(authHeader) ||
!authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
return Results.Unauthorized(); return Results.Unauthorized();
var token = authHeader.Substring("Bearer ".Length); string token = authHeader.Substring("Bearer ".Length);
var accountId = jwtService.ValidateAndGetAccountId(token); string? accountId = jwtService.ValidateAndGetAccountId(token);
if (string.IsNullOrEmpty(accountId)) if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId.AsSpan(), out int id))
return Results.Unauthorized();
if (!int.TryParse(accountId.AsSpan(), out var id))
return Results.Unauthorized(); return Results.Unauthorized();
string newProfileImage = ""; string newProfileImage = "";
if (request.ContentLength.HasValue && request.ContentLength > 0) if (request.ContentLength is > 0) {
{ try {
try
{
request.EnableBuffering(); request.EnableBuffering();
using var reader = new StreamReader(request.Body, leaveOpen: true); using StreamReader reader = new(request.Body, leaveOpen: true);
var body = await reader.ReadToEndAsync(); string body = await reader.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(body)) if (!string.IsNullOrWhiteSpace(body)) {
{ foreach (var pair in body.Split('&')) {
foreach (var pair in body.Split('&')) string[] keyValue = pair.Split('=');
{ if (keyValue.Length != 2) continue;
var keyValue = pair.Split('='); string key = Uri.UnescapeDataString(keyValue[0]);
if (keyValue.Length == 2) string value = Uri.UnescapeDataString(keyValue[1]);
{
var key = Uri.UnescapeDataString(keyValue[0]);
var value = Uri.UnescapeDataString(keyValue[1]);
if (key == "imageName") if (key == "imageName")
newProfileImage = value; newProfileImage = value;
} }
} }
}
request.Body.Position = 0; request.Body.Position = 0;
} }
catch { } catch { }
} }
var account = await db.Accounts.FindAsync(id); Account? account = await db.Accounts.FindAsync(id);
if (account == null) if (account == null)
return Results.NotFound(); return Results.NotFound();
@@ -507,6 +437,22 @@ public class AccountsController
await db.SaveChangesAsync(); await db.SaveChangesAsync();
return Results.Ok(RecNetResult.Ok()); return Results.Ok(RecNetResult.Ok());
}); }
[HttpGet("parentalcontrol/me")]
public async Task<IResult> 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");
} }
} }
@@ -1,13 +1,13 @@
using CannedNet.Services.Infrastructure; using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
public class ChatController [ApiController]
public class ChatController : ControllerBase
{ {
public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args); [HttpGet("thread")]
public async Task<IResult> Get() {
return Results.Content("[]", "application/json");
public void MapEndpoints(WebApplication app)
{
app.MapGet("/thread", () => Results.Content("[]", "application/json"));
} }
} }
@@ -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;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@@ -7,42 +7,36 @@ using SixLabors.ImageSharp.Processing;
namespace CannedNet.Services.Controllers; namespace CannedNet.Services.Controllers;
public class ImageController [ApiController, Route("")]
public class ImageController : ControllerBase
{ {
private static readonly byte[] PlaceholderJpeg; private static readonly byte[] PlaceholderJpeg;
static ImageController() static ImageController() {
{
Signatures.Init(); Signatures.Init();
using var image = new Image<Rgba32>(64, 64); using Image<Rgba32> image = new(64, 64);
image.Mutate(x => x.BackgroundColor(Color.FromRgb(32, 32, 32))); 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 }); image.SaveAsJpeg(ms, new JpegEncoder { Quality = 50 });
PlaceholderJpeg = ms.ToArray(); PlaceholderJpeg = ms.ToArray();
} }
public WebApplicationBuilder Initialize(string[]? args = null) public WebApplicationBuilder Initialize(string[]? args = null) => ServiceExtensions.CreateRecNetBuilder(args);
=> ServiceExtensions.CreateRecNetBuilder(args);
public void MapEndpoints(WebApplication app) [HttpGet("{imageName}")]
{ public async Task<IResult> GetImage(HttpContext context, string imageName) {
var imagesDir = Path.Combine("Images"); string imagesDir = Path.Combine("Images");
Directory.CreateDirectory(imagesDir); 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; byte[] imageBytes;
string contentType; string contentType;
if (File.Exists(filePath)) if (System.IO.File.Exists(filePath)) {
{ imageBytes = await System.IO.File.ReadAllBytesAsync(filePath);
imageBytes = await File.ReadAllBytesAsync(filePath); string ext = Path.GetExtension(imageName).ToLowerInvariant();
var ext = Path.GetExtension(imageName).ToLowerInvariant(); contentType = ext switch {
contentType = ext switch
{
".jpg" or ".jpeg" => "image/jpeg", ".jpg" or ".jpeg" => "image/jpeg",
".gif" => "image/gif", ".gif" => "image/gif",
".webp" => "image/webp", ".webp" => "image/webp",
@@ -50,61 +44,54 @@ public class ImageController
_ => "image/png" _ => "image/png"
}; };
} }
else else {
{
imageBytes = PlaceholderJpeg; imageBytes = PlaceholderJpeg;
contentType = "image/jpeg"; contentType = "image/jpeg";
} }
var cropSquare = context.Request.Query["cropSquare"].FirstOrDefault(); string? cropSquare = context.Request.Query["cropSquare"].FirstOrDefault();
var widthStr = context.Request.Query["width"].FirstOrDefault(); string? widthStr = context.Request.Query["width"].FirstOrDefault();
var heightStr = context.Request.Query["height"].FirstOrDefault(); string? heightStr = context.Request.Query["height"].FirstOrDefault();
if (string.IsNullOrEmpty(widthStr) && string.IsNullOrEmpty(heightStr) && string.IsNullOrEmpty(cropSquare)) if (string.IsNullOrEmpty(widthStr) && string.IsNullOrEmpty(heightStr) && string.IsNullOrEmpty(cropSquare)) {
{
SignImageResponse(context, ref imageBytes); SignImageResponse(context, ref imageBytes);
return Results.File(imageBytes, contentType); return Results.File(imageBytes, contentType);
} }
using var image = Image.Load(imageBytes); using Image image = Image.Load(imageBytes);
var resizeWidth = 0; int resizeWidth = 0;
var resizeHeight = 0; int resizeHeight = 0;
if (!string.IsNullOrEmpty(cropSquare) && cropSquare != "0" && cropSquare != "false") if (!string.IsNullOrEmpty(cropSquare) && cropSquare != "0" && cropSquare != "false") {
{ int size = Math.Min(image.Width, image.Height);
var size = Math.Min(image.Width, image.Height); int x = (image.Width - size) / 2;
var x = (image.Width - size) / 2; int y = (image.Height - size) / 2;
var y = (image.Height - size) / 2;
image.Mutate(img => img.Crop(new Rectangle(x, y, size, size))); image.Mutate(img => img.Crop(new Rectangle(x, y, size, size)));
} }
if (int.TryParse(widthStr, out var w)) if (int.TryParse(widthStr, out int w))
resizeWidth = w; resizeWidth = w;
if (int.TryParse(heightStr, out var h)) if (int.TryParse(heightStr, out int h))
resizeHeight = h; resizeHeight = h;
if (resizeWidth > 0 || resizeHeight > 0) if (resizeWidth > 0 || resizeHeight > 0) {
{
image.Mutate(x => x.Resize(resizeWidth, resizeHeight)); image.Mutate(x => x.Resize(resizeWidth, resizeHeight));
} }
using var output = new MemoryStream(); using MemoryStream output = new();
await image.SaveAsJpegAsync(output, new JpegEncoder { Quality = 85 }); await image.SaveAsJpegAsync(output, new JpegEncoder { Quality = 85 });
imageBytes = output.ToArray(); imageBytes = output.ToArray();
SignImageResponse(context, ref imageBytes); SignImageResponse(context, ref imageBytes);
return Results.File(imageBytes, "image/jpeg"); 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; if (context.Request.Query["sig"] != "p1") return;
var signature = Signatures.Sign(imageBytes); string? signature = Signatures.Sign(imageBytes);
if (signature != null) if (signature != null) {
{
context.Response.Headers["Content-Signature"] = $"key-id=KEY:RSA:p1.rec.net; data={signature}"; context.Response.Headers["Content-Signature"] = $"key-id=KEY:RSA:p1.rec.net; data={signature}";
} }
} }
+7 -11
View File
@@ -1,18 +1,14 @@
using CannedNet.Services.Infrastructure; using Microsoft.AspNetCore.Mvc;
namespace CannedNet.Services.Controllers; 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); [HttpGet]
public async Task<IResult> Get() {
public void MapEndpoints(WebApplication app) string json = await System.IO.File.ReadAllTextAsync("JSON/endpoints.json");
{
// 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"); return Results.Content(json, "application/json");
});
} }
} }
@@ -1,4 +1,4 @@
using CannedNet.Hubs; /*using CannedNet.Hubs;
using CannedNet.Services.Infrastructure; using CannedNet.Services.Infrastructure;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
@@ -16,3 +16,4 @@ public class NotifyController
NotificationService.SetHubContext(hubContext); NotificationService.SetHubContext(hubContext);
} }
} }
*/
@@ -1,4 +1,3 @@
using System.Net.Mime;
using CannedNet.Services.Controllers; using CannedNet.Services.Controllers;
namespace CannedNet.Services.Infrastructure; namespace CannedNet.Services.Infrastructure;
@@ -16,15 +15,15 @@ public static class Services
{ {
public static readonly List<ServiceRegistry> All = public static readonly List<ServiceRegistry> 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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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) }, new() { Name = "Econ", Port = 5011, ServiceType = typeof(EconController), ConfigureBuilder = b => b.ConfigureRecNetServices(), MapEndpoints = (app, _) => new EconController().MapEndpoints(app) },