mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-05-05 18:22:27 +00:00
The Great Formatting of 2022
This commit is contained in:
parent
59cc7f02fb
commit
35f50f5f8c
162 changed files with 6609 additions and 6809 deletions
|
@ -6,62 +6,61 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace ProjectLighthouse.Tests.GameApiTests
|
namespace ProjectLighthouse.Tests.GameApiTests;
|
||||||
|
|
||||||
|
public class AuthenticationTests : LighthouseServerTest
|
||||||
{
|
{
|
||||||
public class AuthenticationTests : LighthouseServerTest
|
[Fact]
|
||||||
|
public async Task ShouldReturnErrorOnNoPostData()
|
||||||
{
|
{
|
||||||
[Fact]
|
HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", null!);
|
||||||
public async Task ShouldReturnErrorOnNoPostData()
|
Assert.False(response.IsSuccessStatusCode);
|
||||||
{
|
#if NET6_0_OR_GREATER
|
||||||
HttpResponseMessage response = await this.Client.PostAsync("/LITTLEBIGPLANETPS3_XML/login", null!);
|
Assert.True(response.StatusCode == HttpStatusCode.BadRequest);
|
||||||
Assert.False(response.IsSuccessStatusCode);
|
#else
|
||||||
#if NET6_0_OR_GREATER
|
|
||||||
Assert.True(response.StatusCode == HttpStatusCode.BadRequest);
|
|
||||||
#else
|
|
||||||
Assert.True(response.StatusCode == HttpStatusCode.NotAcceptable);
|
Assert.True(response.StatusCode == HttpStatusCode.NotAcceptable);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[DatabaseFact]
|
[DatabaseFact]
|
||||||
public async Task ShouldReturnWithValidData()
|
public async Task ShouldReturnWithValidData()
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await this.AuthenticateResponse();
|
HttpResponseMessage response = await this.AuthenticateResponse();
|
||||||
Assert.True(response.IsSuccessStatusCode);
|
Assert.True(response.IsSuccessStatusCode);
|
||||||
string responseContent = await response.Content.ReadAsStringAsync();
|
string responseContent = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Contains("MM_AUTH=", responseContent);
|
Assert.Contains("MM_AUTH=", responseContent);
|
||||||
Assert.Contains(ServerStatics.ServerName, responseContent);
|
Assert.Contains(ServerStatics.ServerName, responseContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DatabaseFact]
|
[DatabaseFact]
|
||||||
public async Task CanSerializeBack()
|
public async Task CanSerializeBack()
|
||||||
{
|
{
|
||||||
LoginResult loginResult = await this.Authenticate();
|
LoginResult loginResult = await this.Authenticate();
|
||||||
|
|
||||||
Assert.NotNull(loginResult);
|
Assert.NotNull(loginResult);
|
||||||
Assert.NotNull(loginResult.AuthTicket);
|
Assert.NotNull(loginResult.AuthTicket);
|
||||||
Assert.NotNull(loginResult.LbpEnvVer);
|
Assert.NotNull(loginResult.LbpEnvVer);
|
||||||
|
|
||||||
Assert.Contains("MM_AUTH=", loginResult.AuthTicket);
|
Assert.Contains("MM_AUTH=", loginResult.AuthTicket);
|
||||||
Assert.Equal(ServerStatics.ServerName, loginResult.LbpEnvVer);
|
Assert.Equal(ServerStatics.ServerName, loginResult.LbpEnvVer);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DatabaseFact]
|
[DatabaseFact]
|
||||||
public async Task CanUseToken()
|
public async Task CanUseToken()
|
||||||
{
|
{
|
||||||
LoginResult loginResult = await this.Authenticate();
|
LoginResult loginResult = await this.Authenticate();
|
||||||
|
|
||||||
HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/enterLevel/1", loginResult.AuthTicket);
|
HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/enterLevel/1", loginResult.AuthTicket);
|
||||||
string responseContent = await response.Content.ReadAsStringAsync();
|
string responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DatabaseFact]
|
[DatabaseFact]
|
||||||
public async Task ShouldReturnForbiddenWhenNotAuthenticated()
|
public async Task ShouldReturnForbiddenWhenNotAuthenticated()
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/announce");
|
HttpResponseMessage response = await this.Client.GetAsync("/LITTLEBIGPLANETPS3_XML/announce");
|
||||||
Assert.False(response.IsSuccessStatusCode);
|
Assert.False(response.IsSuccessStatusCode);
|
||||||
Assert.True(response.StatusCode == HttpStatusCode.Forbidden);
|
Assert.True(response.StatusCode == HttpStatusCode.Forbidden);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,23 +6,22 @@ using LBPUnion.ProjectLighthouse.Tests;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace ProjectLighthouse.Tests.GameApiTests
|
namespace ProjectLighthouse.Tests.GameApiTests;
|
||||||
|
|
||||||
|
public class DatabaseTests : LighthouseServerTest
|
||||||
{
|
{
|
||||||
public class DatabaseTests : LighthouseServerTest
|
[DatabaseFact]
|
||||||
|
public async Task CanCreateUserTwice()
|
||||||
{
|
{
|
||||||
[DatabaseFact]
|
await using Database database = new();
|
||||||
public async Task CanCreateUserTwice()
|
int rand = new Random().Next();
|
||||||
{
|
|
||||||
await using Database database = new();
|
|
||||||
int rand = new Random().Next();
|
|
||||||
|
|
||||||
User userA = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken());
|
User userA = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken());
|
||||||
User userB = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken());
|
User userB = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken());
|
||||||
|
|
||||||
Assert.NotNull(userA);
|
Assert.NotNull(userA);
|
||||||
Assert.NotNull(userB);
|
Assert.NotNull(userB);
|
||||||
|
|
||||||
await database.RemoveUser(userA); // Only remove userA since userA and userB are the same user
|
await database.RemoveUser(userA); // Only remove userA since userA and userB are the same user
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,55 +8,54 @@ using LBPUnion.ProjectLighthouse.Tests;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace ProjectLighthouse.Tests.GameApiTests
|
namespace ProjectLighthouse.Tests.GameApiTests;
|
||||||
|
|
||||||
|
public class MatchTests : LighthouseServerTest
|
||||||
{
|
{
|
||||||
public class MatchTests : LighthouseServerTest
|
private static readonly SemaphoreSlim semaphore = new(1, 1);
|
||||||
|
|
||||||
|
[DatabaseFact]
|
||||||
|
public async Task ShouldRejectEmptyData()
|
||||||
{
|
{
|
||||||
private static readonly SemaphoreSlim semaphore = new(1, 1);
|
LoginResult loginResult = await this.Authenticate();
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
|
||||||
[DatabaseFact]
|
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest("LITTLEBIGPLANETPS3_XML/match", Array.Empty<byte>(), loginResult.AuthTicket);
|
||||||
public async Task ShouldRejectEmptyData()
|
|
||||||
{
|
|
||||||
LoginResult loginResult = await this.Authenticate();
|
|
||||||
await semaphore.WaitAsync();
|
|
||||||
|
|
||||||
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest("LITTLEBIGPLANETPS3_XML/match", Array.Empty<byte>(), loginResult.AuthTicket);
|
semaphore.Release();
|
||||||
|
Assert.False(result.IsSuccessStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
semaphore.Release();
|
[DatabaseFact]
|
||||||
Assert.False(result.IsSuccessStatusCode);
|
public async Task ShouldReturnOk()
|
||||||
}
|
{
|
||||||
|
LoginResult loginResult = await this.Authenticate();
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
|
||||||
[DatabaseFact]
|
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest
|
||||||
public async Task ShouldReturnOk()
|
("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket);
|
||||||
{
|
|
||||||
LoginResult loginResult = await this.Authenticate();
|
|
||||||
await semaphore.WaitAsync();
|
|
||||||
|
|
||||||
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest
|
semaphore.Release();
|
||||||
("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket);
|
Assert.True(result.IsSuccessStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
semaphore.Release();
|
[DatabaseFact]
|
||||||
Assert.True(result.IsSuccessStatusCode);
|
public async Task ShouldIncrementPlayerCount()
|
||||||
}
|
{
|
||||||
|
LoginResult loginResult = await this.Authenticate(new Random().Next());
|
||||||
|
|
||||||
[DatabaseFact]
|
await semaphore.WaitAsync();
|
||||||
public async Task ShouldIncrementPlayerCount()
|
|
||||||
{
|
|
||||||
LoginResult loginResult = await this.Authenticate(new Random().Next());
|
|
||||||
|
|
||||||
await semaphore.WaitAsync();
|
int oldPlayerCount = await StatisticsHelper.RecentMatches();
|
||||||
|
|
||||||
int oldPlayerCount = await StatisticsHelper.RecentMatches();
|
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest
|
||||||
|
("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket);
|
||||||
|
|
||||||
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest
|
Assert.True(result.IsSuccessStatusCode);
|
||||||
("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket);
|
|
||||||
|
|
||||||
Assert.True(result.IsSuccessStatusCode);
|
int playerCount = await StatisticsHelper.RecentMatches();
|
||||||
|
|
||||||
int playerCount = await StatisticsHelper.RecentMatches();
|
semaphore.Release();
|
||||||
|
Assert.Equal(oldPlayerCount + 1, playerCount);
|
||||||
semaphore.Release();
|
|
||||||
Assert.Equal(oldPlayerCount + 1, playerCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,85 +9,84 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace ProjectLighthouse.Tests.GameApiTests
|
namespace ProjectLighthouse.Tests.GameApiTests;
|
||||||
|
|
||||||
|
public class SlotTests : LighthouseServerTest
|
||||||
{
|
{
|
||||||
public class SlotTests : LighthouseServerTest
|
[DatabaseFact]
|
||||||
|
public async Task ShouldOnlyShowUsersLevels()
|
||||||
{
|
{
|
||||||
[DatabaseFact]
|
await using Database database = new();
|
||||||
public async Task ShouldOnlyShowUsersLevels()
|
|
||||||
|
Random r = new();
|
||||||
|
|
||||||
|
User userA = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken());
|
||||||
|
User userB = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken());
|
||||||
|
|
||||||
|
Location l = new()
|
||||||
{
|
{
|
||||||
await using Database database = new();
|
X = 0,
|
||||||
|
Y = 0,
|
||||||
|
};
|
||||||
|
database.Locations.Add(l);
|
||||||
|
await database.SaveChangesAsync();
|
||||||
|
|
||||||
Random r = new();
|
Slot slotA = new()
|
||||||
|
{
|
||||||
|
Creator = userA,
|
||||||
|
CreatorId = userA.UserId,
|
||||||
|
Name = "slotA",
|
||||||
|
Location = l,
|
||||||
|
LocationId = l.Id,
|
||||||
|
ResourceCollection = "",
|
||||||
|
};
|
||||||
|
|
||||||
User userA = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken());
|
Slot slotB = new()
|
||||||
User userB = await database.CreateUser($"unitTestUser{r.Next()}", HashHelper.GenerateAuthToken());
|
{
|
||||||
|
Creator = userB,
|
||||||
|
CreatorId = userB.UserId,
|
||||||
|
Name = "slotB",
|
||||||
|
Location = l,
|
||||||
|
LocationId = l.Id,
|
||||||
|
ResourceCollection = "",
|
||||||
|
};
|
||||||
|
|
||||||
Location l = new()
|
database.Slots.Add(slotA);
|
||||||
{
|
database.Slots.Add(slotB);
|
||||||
X = 0,
|
|
||||||
Y = 0,
|
|
||||||
};
|
|
||||||
database.Locations.Add(l);
|
|
||||||
await database.SaveChangesAsync();
|
|
||||||
|
|
||||||
Slot slotA = new()
|
await database.SaveChangesAsync();
|
||||||
{
|
|
||||||
Creator = userA,
|
|
||||||
CreatorId = userA.UserId,
|
|
||||||
Name = "slotA",
|
|
||||||
Location = l,
|
|
||||||
LocationId = l.Id,
|
|
||||||
ResourceCollection = "",
|
|
||||||
};
|
|
||||||
|
|
||||||
Slot slotB = new()
|
|
||||||
{
|
|
||||||
Creator = userB,
|
|
||||||
CreatorId = userB.UserId,
|
|
||||||
Name = "slotB",
|
|
||||||
Location = l,
|
|
||||||
LocationId = l.Id,
|
|
||||||
ResourceCollection = "",
|
|
||||||
};
|
|
||||||
|
|
||||||
database.Slots.Add(slotA);
|
|
||||||
database.Slots.Add(slotB);
|
|
||||||
|
|
||||||
await database.SaveChangesAsync();
|
|
||||||
|
|
||||||
// XmlSerializer serializer = new(typeof(Slot));
|
// XmlSerializer serializer = new(typeof(Slot));
|
||||||
// Slot slot = (Slot)serializer.Deserialize(new StringReader(bodyString));
|
// Slot slot = (Slot)serializer.Deserialize(new StringReader(bodyString));
|
||||||
|
|
||||||
LoginResult loginResult = await this.Authenticate();
|
LoginResult loginResult = await this.Authenticate();
|
||||||
|
|
||||||
HttpResponseMessage respMessageA = await this.AuthenticatedRequest
|
HttpResponseMessage respMessageA = await this.AuthenticatedRequest
|
||||||
($"LITTLEBIGPLANETPS3_XML/slots/by?u={userA.Username}&pageStart=1&pageSize=1", loginResult.AuthTicket);
|
($"LITTLEBIGPLANETPS3_XML/slots/by?u={userA.Username}&pageStart=1&pageSize=1", loginResult.AuthTicket);
|
||||||
HttpResponseMessage respMessageB = await this.AuthenticatedRequest
|
HttpResponseMessage respMessageB = await this.AuthenticatedRequest
|
||||||
($"LITTLEBIGPLANETPS3_XML/slots/by?u={userB.Username}&pageStart=1&pageSize=1", loginResult.AuthTicket);
|
($"LITTLEBIGPLANETPS3_XML/slots/by?u={userB.Username}&pageStart=1&pageSize=1", loginResult.AuthTicket);
|
||||||
|
|
||||||
Assert.True(respMessageA.IsSuccessStatusCode);
|
Assert.True(respMessageA.IsSuccessStatusCode);
|
||||||
Assert.True(respMessageB.IsSuccessStatusCode);
|
Assert.True(respMessageB.IsSuccessStatusCode);
|
||||||
|
|
||||||
string respA = await respMessageA.Content.ReadAsStringAsync();
|
string respA = await respMessageA.Content.ReadAsStringAsync();
|
||||||
string respB = await respMessageB.Content.ReadAsStringAsync();
|
string respB = await respMessageB.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
Assert.False(string.IsNullOrEmpty(respA));
|
Assert.False(string.IsNullOrEmpty(respA));
|
||||||
Assert.False(string.IsNullOrEmpty(respB));
|
Assert.False(string.IsNullOrEmpty(respB));
|
||||||
|
|
||||||
Assert.NotEqual(respA, respB);
|
Assert.NotEqual(respA, respB);
|
||||||
Assert.DoesNotContain(respA, "slotB");
|
Assert.DoesNotContain(respA, "slotB");
|
||||||
Assert.DoesNotContain(respB, "slotA");
|
Assert.DoesNotContain(respB, "slotA");
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
|
|
||||||
database.Slots.Remove(slotA);
|
database.Slots.Remove(slotA);
|
||||||
database.Slots.Remove(slotB);
|
database.Slots.Remove(slotB);
|
||||||
|
|
||||||
await database.RemoveUser(userA);
|
await database.RemoveUser(userA);
|
||||||
await database.RemoveUser(userB);
|
await database.RemoveUser(userB);
|
||||||
|
|
||||||
await database.SaveChangesAsync();
|
await database.SaveChangesAsync();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,54 +6,53 @@ using System.Threading.Tasks;
|
||||||
using LBPUnion.ProjectLighthouse.Tests;
|
using LBPUnion.ProjectLighthouse.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace ProjectLighthouse.Tests.GameApiTests
|
namespace ProjectLighthouse.Tests.GameApiTests;
|
||||||
|
|
||||||
|
public class UploadTests : LighthouseServerTest
|
||||||
{
|
{
|
||||||
public class UploadTests : LighthouseServerTest
|
public UploadTests()
|
||||||
{
|
{
|
||||||
public UploadTests()
|
string assetsDirectory = Path.Combine(Environment.CurrentDirectory, "r");
|
||||||
{
|
if (Directory.Exists(assetsDirectory)) Directory.Delete(assetsDirectory, true);
|
||||||
string assetsDirectory = Path.Combine(Environment.CurrentDirectory, "r");
|
}
|
||||||
if (Directory.Exists(assetsDirectory)) Directory.Delete(assetsDirectory, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ShouldNotAcceptScript()
|
public async Task ShouldNotAcceptScript()
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestScript.ff");
|
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestScript.ff");
|
||||||
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
||||||
Assert.False(response.IsSuccessStatusCode);
|
Assert.False(response.IsSuccessStatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ShouldNotAcceptFarc()
|
public async Task ShouldNotAcceptFarc()
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestFarc.farc");
|
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestFarc.farc");
|
||||||
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
||||||
Assert.False(response.IsSuccessStatusCode);
|
Assert.False(response.IsSuccessStatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ShouldNotAcceptGarbage()
|
public async Task ShouldNotAcceptGarbage()
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestGarbage.bin");
|
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestGarbage.bin");
|
||||||
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
||||||
Assert.False(response.IsSuccessStatusCode);
|
Assert.False(response.IsSuccessStatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ShouldAcceptTexture()
|
public async Task ShouldAcceptTexture()
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestTexture.tex");
|
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestTexture.tex");
|
||||||
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
||||||
Assert.True(response.IsSuccessStatusCode);
|
Assert.True(response.IsSuccessStatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ShouldAcceptLevel()
|
public async Task ShouldAcceptLevel()
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestLevel.lvl");
|
HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestLevel.lvl");
|
||||||
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
|
||||||
Assert.True(response.IsSuccessStatusCode);
|
Assert.True(response.IsSuccessStatusCode);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,58 +7,57 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using OpenQA.Selenium;
|
using OpenQA.Selenium;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace ProjectLighthouse.Tests.WebsiteTests
|
namespace ProjectLighthouse.Tests.WebsiteTests;
|
||||||
|
|
||||||
|
public class AdminTests : LighthouseWebTest
|
||||||
{
|
{
|
||||||
public class AdminTests : LighthouseWebTest
|
public const string AdminPanelButtonXPath = "/html/body/div/header/div/div/div/a[2]";
|
||||||
|
|
||||||
|
[DatabaseFact]
|
||||||
|
public async Task ShouldShowAdminPanelButtonWhenAdmin()
|
||||||
{
|
{
|
||||||
public const string AdminPanelButtonXPath = "/html/body/div/header/div/div/div/a[2]";
|
await using Database database = new();
|
||||||
|
Random random = new();
|
||||||
|
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||||
|
|
||||||
[DatabaseFact]
|
WebToken webToken = new()
|
||||||
public async Task ShouldShowAdminPanelButtonWhenAdmin()
|
|
||||||
{
|
{
|
||||||
await using Database database = new();
|
UserId = user.UserId,
|
||||||
Random random = new();
|
UserToken = HashHelper.GenerateAuthToken(),
|
||||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
};
|
||||||
|
|
||||||
WebToken webToken = new()
|
database.WebTokens.Add(webToken);
|
||||||
{
|
user.IsAdmin = true;
|
||||||
UserId = user.UserId,
|
await database.SaveChangesAsync();
|
||||||
UserToken = HashHelper.GenerateAuthToken(),
|
|
||||||
};
|
|
||||||
|
|
||||||
database.WebTokens.Add(webToken);
|
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/");
|
||||||
user.IsAdmin = true;
|
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
|
||||||
await database.SaveChangesAsync();
|
this.Driver.Navigate().Refresh();
|
||||||
|
|
||||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/");
|
Assert.Contains("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text);
|
||||||
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
|
}
|
||||||
this.Driver.Navigate().Refresh();
|
|
||||||
|
|
||||||
Assert.Contains("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text);
|
[DatabaseFact]
|
||||||
}
|
public async Task ShouldNotShowAdminPanelButtonWhenNotAdmin()
|
||||||
|
{
|
||||||
|
await using Database database = new();
|
||||||
|
Random random = new();
|
||||||
|
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||||
|
|
||||||
[DatabaseFact]
|
WebToken webToken = new()
|
||||||
public async Task ShouldNotShowAdminPanelButtonWhenNotAdmin()
|
|
||||||
{
|
{
|
||||||
await using Database database = new();
|
UserId = user.UserId,
|
||||||
Random random = new();
|
UserToken = HashHelper.GenerateAuthToken(),
|
||||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
};
|
||||||
|
|
||||||
WebToken webToken = new()
|
database.WebTokens.Add(webToken);
|
||||||
{
|
user.IsAdmin = false;
|
||||||
UserId = user.UserId,
|
await database.SaveChangesAsync();
|
||||||
UserToken = HashHelper.GenerateAuthToken(),
|
|
||||||
};
|
|
||||||
|
|
||||||
database.WebTokens.Add(webToken);
|
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/");
|
||||||
user.IsAdmin = false;
|
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
|
||||||
await database.SaveChangesAsync();
|
this.Driver.Navigate().Refresh();
|
||||||
|
|
||||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/");
|
Assert.DoesNotContain("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text);
|
||||||
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
|
|
||||||
this.Driver.Navigate().Refresh();
|
|
||||||
|
|
||||||
Assert.DoesNotContain("Admin Panel", this.Driver.FindElement(By.XPath(AdminPanelButtonXPath)).Text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,98 +9,97 @@ using Microsoft.EntityFrameworkCore;
|
||||||
using OpenQA.Selenium;
|
using OpenQA.Selenium;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace ProjectLighthouse.Tests.WebsiteTests
|
namespace ProjectLighthouse.Tests.WebsiteTests;
|
||||||
|
|
||||||
|
public class AuthenticationTests : LighthouseWebTest
|
||||||
{
|
{
|
||||||
public class AuthenticationTests : LighthouseWebTest
|
[DatabaseFact]
|
||||||
|
public async Task ShouldLoginWithPassword()
|
||||||
{
|
{
|
||||||
[DatabaseFact]
|
await using Database database = new();
|
||||||
public async Task ShouldLoginWithPassword()
|
Random random = new();
|
||||||
|
|
||||||
|
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||||
|
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash(HashHelper.Sha256Hash(password)));
|
||||||
|
|
||||||
|
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
||||||
|
|
||||||
|
this.Driver.FindElement(By.Id("text")).SendKeys(user.Username);
|
||||||
|
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
||||||
|
|
||||||
|
this.Driver.FindElement(By.Id("submit")).Click();
|
||||||
|
|
||||||
|
WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
|
||||||
|
Assert.NotNull(webToken);
|
||||||
|
|
||||||
|
await database.RemoveUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DatabaseFact]
|
||||||
|
public async Task ShouldNotLoginWithNoPassword()
|
||||||
|
{
|
||||||
|
await using Database database = new();
|
||||||
|
Random random = new();
|
||||||
|
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("just like the hindenberg,"));
|
||||||
|
|
||||||
|
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
||||||
|
|
||||||
|
this.Driver.FindElement(By.Id("text")).SendKeys(user.Username);
|
||||||
|
|
||||||
|
this.Driver.FindElement(By.Id("submit")).Click();
|
||||||
|
|
||||||
|
WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
|
||||||
|
Assert.Null(webToken);
|
||||||
|
|
||||||
|
await database.RemoveUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DatabaseFact]
|
||||||
|
public async Task ShouldNotLoginWithWrongPassword()
|
||||||
|
{
|
||||||
|
await using Database database = new();
|
||||||
|
Random random = new();
|
||||||
|
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||||
|
|
||||||
|
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
||||||
|
|
||||||
|
this.Driver.FindElement(By.Id("text")).SendKeys(user.Username);
|
||||||
|
this.Driver.FindElement(By.Id("password")).SendKeys("nah man");
|
||||||
|
|
||||||
|
this.Driver.FindElement(By.Id("submit")).Click();
|
||||||
|
|
||||||
|
WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
|
||||||
|
Assert.Null(webToken);
|
||||||
|
|
||||||
|
await database.RemoveUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DatabaseFact]
|
||||||
|
public async Task ShouldLoginWithInjectedCookie()
|
||||||
|
{
|
||||||
|
const string loggedInAsUsernameTextXPath = "/html/body/div/div/div/p[1]/b";
|
||||||
|
|
||||||
|
await using Database database = new();
|
||||||
|
Random random = new();
|
||||||
|
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
||||||
|
|
||||||
|
WebToken webToken = new()
|
||||||
{
|
{
|
||||||
await using Database database = new();
|
UserId = user.UserId,
|
||||||
Random random = new();
|
UserToken = HashHelper.GenerateAuthToken(),
|
||||||
|
};
|
||||||
|
|
||||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
database.WebTokens.Add(webToken);
|
||||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash(HashHelper.Sha256Hash(password)));
|
await database.SaveChangesAsync();
|
||||||
|
|
||||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
INavigation navigation = this.Driver.Navigate();
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("text")).SendKeys(user.Username);
|
navigation.GoToUrl(this.BaseAddress + "/");
|
||||||
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
|
||||||
|
Assert.Throws<NoSuchElementException>(() => this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)));
|
||||||
|
navigation.Refresh();
|
||||||
|
Assert.True(this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text == user.Username);
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("submit")).Click();
|
await database.RemoveUser(user);
|
||||||
|
|
||||||
WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
|
|
||||||
Assert.NotNull(webToken);
|
|
||||||
|
|
||||||
await database.RemoveUser(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
[DatabaseFact]
|
|
||||||
public async Task ShouldNotLoginWithNoPassword()
|
|
||||||
{
|
|
||||||
await using Database database = new();
|
|
||||||
Random random = new();
|
|
||||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("just like the hindenberg,"));
|
|
||||||
|
|
||||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("text")).SendKeys(user.Username);
|
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("submit")).Click();
|
|
||||||
|
|
||||||
WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
|
|
||||||
Assert.Null(webToken);
|
|
||||||
|
|
||||||
await database.RemoveUser(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
[DatabaseFact]
|
|
||||||
public async Task ShouldNotLoginWithWrongPassword()
|
|
||||||
{
|
|
||||||
await using Database database = new();
|
|
||||||
Random random = new();
|
|
||||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
|
||||||
|
|
||||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/login");
|
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("text")).SendKeys(user.Username);
|
|
||||||
this.Driver.FindElement(By.Id("password")).SendKeys("nah man");
|
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("submit")).Click();
|
|
||||||
|
|
||||||
WebToken? webToken = await database.WebTokens.FirstOrDefaultAsync(t => t.UserId == user.UserId);
|
|
||||||
Assert.Null(webToken);
|
|
||||||
|
|
||||||
await database.RemoveUser(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
[DatabaseFact]
|
|
||||||
public async Task ShouldLoginWithInjectedCookie()
|
|
||||||
{
|
|
||||||
const string loggedInAsUsernameTextXPath = "/html/body/div/div/div/p[1]/b";
|
|
||||||
|
|
||||||
await using Database database = new();
|
|
||||||
Random random = new();
|
|
||||||
User user = await database.CreateUser($"unitTestUser{random.Next()}", HashHelper.BCryptHash("i'm an engineering failure"));
|
|
||||||
|
|
||||||
WebToken webToken = new()
|
|
||||||
{
|
|
||||||
UserId = user.UserId,
|
|
||||||
UserToken = HashHelper.GenerateAuthToken(),
|
|
||||||
};
|
|
||||||
|
|
||||||
database.WebTokens.Add(webToken);
|
|
||||||
await database.SaveChangesAsync();
|
|
||||||
|
|
||||||
INavigation navigation = this.Driver.Navigate();
|
|
||||||
|
|
||||||
navigation.GoToUrl(this.BaseAddress + "/");
|
|
||||||
this.Driver.Manage().Cookies.AddCookie(new Cookie("LighthouseToken", webToken.UserToken));
|
|
||||||
Assert.Throws<NoSuchElementException>(() => this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)));
|
|
||||||
navigation.Refresh();
|
|
||||||
Assert.True(this.Driver.FindElement(By.XPath(loggedInAsUsernameTextXPath)).Text == user.Username);
|
|
||||||
|
|
||||||
await database.RemoveUser(user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,44 +7,43 @@ using OpenQA.Selenium;
|
||||||
using OpenQA.Selenium.Chrome;
|
using OpenQA.Selenium.Chrome;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace ProjectLighthouse.Tests.WebsiteTests
|
namespace ProjectLighthouse.Tests.WebsiteTests;
|
||||||
|
|
||||||
|
[Collection(nameof(LighthouseWebTest))]
|
||||||
|
public class LighthouseWebTest : IDisposable
|
||||||
{
|
{
|
||||||
[Collection(nameof(LighthouseWebTest))]
|
public readonly string BaseAddress;
|
||||||
public class LighthouseWebTest : IDisposable
|
|
||||||
|
public readonly IWebDriver Driver;
|
||||||
|
public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup<TestStartup>().UseWebRoot("StaticFiles").Build();
|
||||||
|
|
||||||
|
public LighthouseWebTest()
|
||||||
{
|
{
|
||||||
public readonly IWebHost WebHost = new WebHostBuilder().UseKestrel().UseStartup<TestStartup>().UseWebRoot("StaticFiles").Build();
|
this.WebHost.Start();
|
||||||
public readonly string BaseAddress;
|
|
||||||
|
|
||||||
public readonly IWebDriver Driver;
|
IServerAddressesFeature? serverAddressesFeature = this.WebHost.ServerFeatures.Get<IServerAddressesFeature>();
|
||||||
|
if (serverAddressesFeature == null) throw new ArgumentNullException();
|
||||||
|
|
||||||
public LighthouseWebTest()
|
this.BaseAddress = serverAddressesFeature.Addresses.First();
|
||||||
|
|
||||||
|
ChromeOptions chromeOptions = new();
|
||||||
|
if (Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false"))
|
||||||
{
|
{
|
||||||
this.WebHost.Start();
|
chromeOptions.AddArgument("headless");
|
||||||
|
chromeOptions.AddArgument("no-sandbox");
|
||||||
IServerAddressesFeature? serverAddressesFeature = WebHost.ServerFeatures.Get<IServerAddressesFeature>();
|
chromeOptions.AddArgument("disable-dev-shm-usage");
|
||||||
if (serverAddressesFeature == null) throw new ArgumentNullException();
|
Console.WriteLine("We are in a CI environment, so chrome headless mode has been enabled.");
|
||||||
|
|
||||||
this.BaseAddress = serverAddressesFeature.Addresses.First();
|
|
||||||
|
|
||||||
ChromeOptions chromeOptions = new();
|
|
||||||
if (Convert.ToBoolean(Environment.GetEnvironmentVariable("CI") ?? "false"))
|
|
||||||
{
|
|
||||||
chromeOptions.AddArgument("headless");
|
|
||||||
chromeOptions.AddArgument("no-sandbox");
|
|
||||||
chromeOptions.AddArgument("disable-dev-shm-usage");
|
|
||||||
Console.WriteLine("We are in a CI environment, so chrome headless mode has been enabled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Driver = new ChromeDriver(chromeOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
this.Driver = new ChromeDriver(chromeOptions);
|
||||||
{
|
}
|
||||||
this.Driver.Close();
|
|
||||||
this.Driver.Dispose();
|
|
||||||
this.WebHost.Dispose();
|
|
||||||
|
|
||||||
GC.SuppressFinalize(this);
|
public void Dispose()
|
||||||
}
|
{
|
||||||
|
this.Driver.Close();
|
||||||
|
this.Driver.Dispose();
|
||||||
|
this.WebHost.Dispose();
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,76 +9,75 @@ using Microsoft.EntityFrameworkCore;
|
||||||
using OpenQA.Selenium;
|
using OpenQA.Selenium;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace ProjectLighthouse.Tests.WebsiteTests
|
namespace ProjectLighthouse.Tests.WebsiteTests;
|
||||||
|
|
||||||
|
public class RegisterTests : LighthouseWebTest
|
||||||
{
|
{
|
||||||
public class RegisterTests : LighthouseWebTest
|
[DatabaseFact]
|
||||||
|
public async Task ShouldRegister()
|
||||||
{
|
{
|
||||||
[DatabaseFact]
|
await using Database database = new();
|
||||||
public async Task ShouldRegister()
|
|
||||||
{
|
|
||||||
await using Database database = new();
|
|
||||||
|
|
||||||
string username = "unitTestUser" + new Random().Next();
|
string username = "unitTestUser" + new Random().Next();
|
||||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||||
|
|
||||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("text")).SendKeys(username);
|
this.Driver.FindElement(By.Id("text")).SendKeys(username);
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
||||||
this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password);
|
this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password);
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("submit")).Click();
|
this.Driver.FindElement(By.Id("submit")).Click();
|
||||||
|
|
||||||
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||||
Assert.NotNull(user);
|
Assert.NotNull(user);
|
||||||
|
|
||||||
await database.RemoveUser(user);
|
await database.RemoveUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DatabaseFact]
|
[DatabaseFact]
|
||||||
public async Task ShouldNotRegisterWithMismatchingPasswords()
|
public async Task ShouldNotRegisterWithMismatchingPasswords()
|
||||||
{
|
{
|
||||||
await using Database database = new();
|
await using Database database = new();
|
||||||
|
|
||||||
string username = "unitTestUser" + new Random().Next();
|
string username = "unitTestUser" + new Random().Next();
|
||||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||||
|
|
||||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("text")).SendKeys(username);
|
this.Driver.FindElement(By.Id("text")).SendKeys(username);
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
||||||
this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password + "a");
|
this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password + "a");
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("submit")).Click();
|
this.Driver.FindElement(By.Id("submit")).Click();
|
||||||
|
|
||||||
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||||
Assert.Null(user);
|
Assert.Null(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DatabaseFact]
|
[DatabaseFact]
|
||||||
public async Task ShouldNotRegisterWithTakenUsername()
|
public async Task ShouldNotRegisterWithTakenUsername()
|
||||||
{
|
{
|
||||||
await using Database database = new();
|
await using Database database = new();
|
||||||
|
|
||||||
string username = "unitTestUser" + new Random().Next();
|
string username = "unitTestUser" + new Random().Next();
|
||||||
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
string password = HashHelper.Sha256Hash(HashHelper.GenerateRandomBytes(64).ToArray());
|
||||||
|
|
||||||
await database.CreateUser(username, HashHelper.BCryptHash(password));
|
await database.CreateUser(username, HashHelper.BCryptHash(password));
|
||||||
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
User? user = await database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||||
Assert.NotNull(user);
|
Assert.NotNull(user);
|
||||||
|
|
||||||
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
this.Driver.Navigate().GoToUrl(this.BaseAddress + "/register");
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("text")).SendKeys(username);
|
this.Driver.FindElement(By.Id("text")).SendKeys(username);
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
this.Driver.FindElement(By.Id("password")).SendKeys(password);
|
||||||
this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password);
|
this.Driver.FindElement(By.Id("confirmPassword")).SendKeys(password);
|
||||||
|
|
||||||
this.Driver.FindElement(By.Id("submit")).Click();
|
this.Driver.FindElement(By.Id("submit")).Click();
|
||||||
|
|
||||||
Assert.Contains("The username you've chosen is already taken.", this.Driver.PageSource);
|
Assert.Contains("The username you've chosen is already taken.", this.Driver.PageSource);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,28 +2,22 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Tests
|
namespace LBPUnion.ProjectLighthouse.Tests;
|
||||||
{
|
|
||||||
public sealed class DatabaseFactAttribute : FactAttribute
|
|
||||||
{
|
|
||||||
private static readonly object migrateLock = new();
|
|
||||||
|
|
||||||
public DatabaseFactAttribute()
|
public sealed class DatabaseFactAttribute : FactAttribute
|
||||||
{
|
{
|
||||||
ServerSettings.Instance = new ServerSettings();
|
private static readonly object migrateLock = new();
|
||||||
ServerSettings.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
|
|
||||||
if (!ServerStatics.DbConnected)
|
public DatabaseFactAttribute()
|
||||||
|
{
|
||||||
|
ServerSettings.Instance = new ServerSettings();
|
||||||
|
ServerSettings.Instance.DbConnectionString = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse";
|
||||||
|
if (!ServerStatics.DbConnected) this.Skip = "Database not available";
|
||||||
|
else
|
||||||
|
lock(migrateLock)
|
||||||
{
|
{
|
||||||
this.Skip = "Database not available";
|
using Database database = new();
|
||||||
|
database.Database.Migrate();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
lock(migrateLock)
|
|
||||||
{
|
|
||||||
using Database database = new();
|
|
||||||
database.Database.Migrate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,58 +4,57 @@ using System.Text;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Files;
|
using LBPUnion.ProjectLighthouse.Types.Files;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Tests
|
namespace LBPUnion.ProjectLighthouse.Tests;
|
||||||
|
|
||||||
|
public class FileTypeTests
|
||||||
{
|
{
|
||||||
public class FileTypeTests
|
[Fact]
|
||||||
|
public void ShouldRecognizeLevel()
|
||||||
{
|
{
|
||||||
[Fact]
|
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestLevel.lvl"));
|
||||||
public void ShouldRecognizeLevel()
|
Assert.True(file.FileType == LbpFileType.Level);
|
||||||
{
|
}
|
||||||
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestLevel.lvl"));
|
|
||||||
Assert.True(file.FileType == LbpFileType.Level);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldRecognizeScript()
|
public void ShouldRecognizeScript()
|
||||||
{
|
{
|
||||||
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestScript.ff"));
|
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestScript.ff"));
|
||||||
Assert.True(file.FileType == LbpFileType.Script);
|
Assert.True(file.FileType == LbpFileType.Script);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldRecognizeTexture()
|
public void ShouldRecognizeTexture()
|
||||||
{
|
{
|
||||||
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestTexture.tex"));
|
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestTexture.tex"));
|
||||||
Assert.True(file.FileType == LbpFileType.Texture);
|
Assert.True(file.FileType == LbpFileType.Texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldRecognizeFileArchive()
|
public void ShouldRecognizeFileArchive()
|
||||||
{
|
{
|
||||||
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc"));
|
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc"));
|
||||||
Assert.True(file.FileType == LbpFileType.FileArchive);
|
Assert.True(file.FileType == LbpFileType.FileArchive);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldNotRecognizeFileArchiveAsScript()
|
public void ShouldNotRecognizeFileArchiveAsScript()
|
||||||
{
|
{
|
||||||
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc"));
|
LbpFile file = new(File.ReadAllBytes("ExampleFiles/TestFarc.farc"));
|
||||||
Assert.False(file.FileType == LbpFileType.Script);
|
Assert.False(file.FileType == LbpFileType.Script);
|
||||||
Assert.True(file.FileType == LbpFileType.FileArchive);
|
Assert.True(file.FileType == LbpFileType.FileArchive);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldRecognizeNothingAsUnknown()
|
public void ShouldRecognizeNothingAsUnknown()
|
||||||
{
|
{
|
||||||
LbpFile file = new(Array.Empty<byte>());
|
LbpFile file = new(Array.Empty<byte>());
|
||||||
Assert.True(file.FileType == LbpFileType.Unknown);
|
Assert.True(file.FileType == LbpFileType.Unknown);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ShouldRecognizeGarbageAsUnknown()
|
public void ShouldRecognizeGarbageAsUnknown()
|
||||||
{
|
{
|
||||||
LbpFile file = new(Encoding.ASCII.GetBytes("free pc only $900"));
|
LbpFile file = new(Encoding.ASCII.GetBytes("free pc only $900"));
|
||||||
Assert.True(file.FileType == LbpFileType.Unknown);
|
Assert.True(file.FileType == LbpFileType.Unknown);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,89 +11,84 @@ using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.TestHost;
|
using Microsoft.AspNetCore.TestHost;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Tests
|
namespace LBPUnion.ProjectLighthouse.Tests;
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
|
public class LighthouseServerTest
|
||||||
{
|
{
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
public readonly HttpClient Client;
|
||||||
public class LighthouseServerTest
|
public readonly TestServer Server;
|
||||||
|
|
||||||
|
public LighthouseServerTest()
|
||||||
{
|
{
|
||||||
public readonly HttpClient Client;
|
this.Server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
|
||||||
public readonly TestServer Server;
|
this.Client = this.Server.CreateClient();
|
||||||
|
}
|
||||||
|
public async Task<HttpResponseMessage> AuthenticateResponse(int number = -1, bool createUser = true)
|
||||||
|
{
|
||||||
|
if (number == -1) number = new Random().Next();
|
||||||
|
|
||||||
public LighthouseServerTest()
|
const string username = "unitTestUser";
|
||||||
|
if (createUser)
|
||||||
{
|
{
|
||||||
this.Server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
|
await using Database database = new();
|
||||||
this.Client = this.Server.CreateClient();
|
if (await database.Users.FirstOrDefaultAsync(u => u.Username == $"{username}{number}") == null)
|
||||||
}
|
await database.CreateUser($"{username}{number}", HashHelper.BCryptHash($"unitTestPassword{number}"));
|
||||||
public async Task<HttpResponseMessage> AuthenticateResponse(int number = -1, bool createUser = true)
|
|
||||||
{
|
|
||||||
if (number == -1)
|
|
||||||
{
|
|
||||||
number = new Random().Next();
|
|
||||||
}
|
|
||||||
|
|
||||||
const string username = "unitTestUser";
|
|
||||||
if (createUser)
|
|
||||||
{
|
|
||||||
await using Database database = new();
|
|
||||||
if (await database.Users.FirstOrDefaultAsync(u => u.Username == $"{username}{number}") == null)
|
|
||||||
await database.CreateUser($"{username}{number}", HashHelper.BCryptHash($"unitTestPassword{number}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
string stringContent = $"{LoginData.UsernamePrefix}{username}{number}{(char)0x00}";
|
|
||||||
|
|
||||||
HttpResponseMessage response = await this.Client.PostAsync
|
|
||||||
($"/LITTLEBIGPLANETPS3_XML/login?titleID={GameVersionHelper.LittleBigPlanet2TitleIds[0]}", new StringContent(stringContent));
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LoginResult> Authenticate(int number = 0)
|
string stringContent = $"{LoginData.UsernamePrefix}{username}{number}{(char)0x00}";
|
||||||
{
|
|
||||||
HttpResponseMessage response = await this.AuthenticateResponse(number);
|
|
||||||
|
|
||||||
string responseContent = LbpSerializer.StringElement("loginResult", await response.Content.ReadAsStringAsync());
|
HttpResponseMessage response = await this.Client.PostAsync
|
||||||
|
($"/LITTLEBIGPLANETPS3_XML/login?titleID={GameVersionHelper.LittleBigPlanet2TitleIds[0]}", new StringContent(stringContent));
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
XmlSerializer serializer = new(typeof(LoginResult));
|
public async Task<LoginResult> Authenticate(int number = 0)
|
||||||
return (LoginResult)serializer.Deserialize(new StringReader(responseContent))!;
|
{
|
||||||
}
|
HttpResponseMessage response = await this.AuthenticateResponse(number);
|
||||||
|
|
||||||
public Task<HttpResponseMessage> AuthenticatedRequest(string endpoint, string mmAuth) => this.AuthenticatedRequest(endpoint, mmAuth, HttpMethod.Get);
|
string responseContent = LbpSerializer.StringElement("loginResult", await response.Content.ReadAsStringAsync());
|
||||||
|
|
||||||
public Task<HttpResponseMessage> AuthenticatedRequest(string endpoint, string mmAuth, HttpMethod method)
|
XmlSerializer serializer = new(typeof(LoginResult));
|
||||||
{
|
return (LoginResult)serializer.Deserialize(new StringReader(responseContent))!;
|
||||||
using HttpRequestMessage requestMessage = new(method, endpoint);
|
}
|
||||||
requestMessage.Headers.Add("Cookie", mmAuth);
|
|
||||||
|
|
||||||
return this.Client.SendAsync(requestMessage);
|
public Task<HttpResponseMessage> AuthenticatedRequest(string endpoint, string mmAuth) => this.AuthenticatedRequest(endpoint, mmAuth, HttpMethod.Get);
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> UploadFileEndpointRequest(string filePath)
|
public Task<HttpResponseMessage> AuthenticatedRequest(string endpoint, string mmAuth, HttpMethod method)
|
||||||
{
|
{
|
||||||
byte[] bytes = await File.ReadAllBytesAsync(filePath);
|
using HttpRequestMessage requestMessage = new(method, endpoint);
|
||||||
string hash = HashHelper.Sha1Hash(bytes).ToLower();
|
requestMessage.Headers.Add("Cookie", mmAuth);
|
||||||
|
|
||||||
return await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/upload/{hash}", new ByteArrayContent(bytes));
|
return this.Client.SendAsync(requestMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> UploadFileRequest(string endpoint, string filePath)
|
public async Task<HttpResponseMessage> UploadFileEndpointRequest(string filePath)
|
||||||
=> await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath)));
|
{
|
||||||
|
byte[] bytes = await File.ReadAllBytesAsync(filePath);
|
||||||
|
string hash = HashHelper.Sha1Hash(bytes).ToLower();
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> UploadDataRequest(string endpoint, byte[] data)
|
return await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/upload/{hash}", new ByteArrayContent(bytes));
|
||||||
=> await this.Client.PostAsync(endpoint, new ByteArrayContent(data));
|
}
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> AuthenticatedUploadFileRequest(string endpoint, string filePath, string mmAuth)
|
public async Task<HttpResponseMessage> UploadFileRequest(string endpoint, string filePath)
|
||||||
{
|
=> await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath)));
|
||||||
using HttpRequestMessage requestMessage = new(HttpMethod.Post, endpoint);
|
|
||||||
requestMessage.Headers.Add("Cookie", mmAuth);
|
|
||||||
requestMessage.Content = new StringContent(await File.ReadAllTextAsync(filePath));
|
|
||||||
return await this.Client.SendAsync(requestMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> AuthenticatedUploadDataRequest(string endpoint, byte[] data, string mmAuth)
|
public async Task<HttpResponseMessage> UploadDataRequest(string endpoint, byte[] data) => await this.Client.PostAsync(endpoint, new ByteArrayContent(data));
|
||||||
{
|
|
||||||
using HttpRequestMessage requestMessage = new(HttpMethod.Post, endpoint);
|
public async Task<HttpResponseMessage> AuthenticatedUploadFileRequest(string endpoint, string filePath, string mmAuth)
|
||||||
requestMessage.Headers.Add("Cookie", mmAuth);
|
{
|
||||||
requestMessage.Content = new ByteArrayContent(data);
|
using HttpRequestMessage requestMessage = new(HttpMethod.Post, endpoint);
|
||||||
return await this.Client.SendAsync(requestMessage);
|
requestMessage.Headers.Add("Cookie", mmAuth);
|
||||||
}
|
requestMessage.Content = new StringContent(await File.ReadAllTextAsync(filePath));
|
||||||
|
return await this.Client.SendAsync(requestMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<HttpResponseMessage> AuthenticatedUploadDataRequest(string endpoint, byte[] data, string mmAuth)
|
||||||
|
{
|
||||||
|
using HttpRequestMessage requestMessage = new(HttpMethod.Post, endpoint);
|
||||||
|
requestMessage.Headers.Add("Cookie", mmAuth);
|
||||||
|
requestMessage.Content = new ByteArrayContent(data);
|
||||||
|
return await this.Client.SendAsync(requestMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,42 +2,41 @@ using System.Collections.Generic;
|
||||||
using LBPUnion.ProjectLighthouse.Serialization;
|
using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Tests
|
namespace LBPUnion.ProjectLighthouse.Tests;
|
||||||
|
|
||||||
|
public class SerializerTests
|
||||||
{
|
{
|
||||||
public class SerializerTests
|
[Fact]
|
||||||
|
public void BlankElementWorks()
|
||||||
{
|
{
|
||||||
[Fact]
|
Assert.Equal("<test></test>", LbpSerializer.BlankElement("test"));
|
||||||
public void BlankElementWorks()
|
}
|
||||||
{
|
|
||||||
Assert.Equal("<test></test>", LbpSerializer.BlankElement("test"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void StringElementWorks()
|
public void StringElementWorks()
|
||||||
{
|
{
|
||||||
Assert.Equal("<test>asd</test>", LbpSerializer.StringElement("test", "asd"));
|
Assert.Equal("<test>asd</test>", LbpSerializer.StringElement("test", "asd"));
|
||||||
Assert.Equal("<test>asd</test>", LbpSerializer.StringElement(new KeyValuePair<string, object>("test", "asd")));
|
Assert.Equal("<test>asd</test>", LbpSerializer.StringElement(new KeyValuePair<string, object>("test", "asd")));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TaggedStringElementWorks()
|
public void TaggedStringElementWorks()
|
||||||
{
|
{
|
||||||
Assert.Equal("<test foo=\"bar\">asd</test>", LbpSerializer.TaggedStringElement("test", "asd", "foo", "bar"));
|
Assert.Equal("<test foo=\"bar\">asd</test>", LbpSerializer.TaggedStringElement("test", "asd", "foo", "bar"));
|
||||||
Assert.Equal
|
Assert.Equal
|
||||||
(
|
(
|
||||||
"<test foo=\"bar\">asd</test>",
|
"<test foo=\"bar\">asd</test>",
|
||||||
LbpSerializer.TaggedStringElement(new KeyValuePair<string, object>("test", "asd"), new KeyValuePair<string, object>("foo", "bar"))
|
LbpSerializer.TaggedStringElement(new KeyValuePair<string, object>("test", "asd"), new KeyValuePair<string, object>("foo", "bar"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ElementsWorks()
|
public void ElementsWorks()
|
||||||
{
|
{
|
||||||
Assert.Equal
|
Assert.Equal
|
||||||
(
|
(
|
||||||
"<test>asd</test><foo>bar</foo>",
|
"<test>asd</test><foo>bar</foo>",
|
||||||
LbpSerializer.Elements(new KeyValuePair<string, object>("test", "asd"), new KeyValuePair<string, object>("foo", "bar"))
|
LbpSerializer.Elements(new KeyValuePair<string, object>("test", "asd"), new KeyValuePair<string, object>("foo", "bar"))
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,46 +3,45 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/plain")]
|
||||||
|
public class ClientConfigurationController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
[HttpGet("network_settings.nws")]
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
[SuppressMessage("ReSharper", "StringLiteralTypo")]
|
||||||
[Produces("text/plain")]
|
public IActionResult NetworkSettings()
|
||||||
public class ClientConfigurationController : ControllerBase
|
|
||||||
{
|
{
|
||||||
[HttpGet("network_settings.nws")]
|
HostString hostname = this.Request.Host;
|
||||||
[SuppressMessage("ReSharper", "StringLiteralTypo")]
|
return this.Ok
|
||||||
public IActionResult NetworkSettings()
|
(
|
||||||
|
"ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels false\nAllowModeratedPoppetItems false\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 240.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 60.0\nTIMEOUT_DIVE_IN_TOTAL 300.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false\n" +
|
||||||
|
$"TelemetryServer {hostname}\n" +
|
||||||
|
$"CDNHostName {hostname}\n" +
|
||||||
|
$"ShowLevelBoos {ServerSettings.Instance.BooingEnabled.ToString().ToLower()}\n" +
|
||||||
|
$"AllowOnlineCreate {ServerSettings.Instance.VitaCreateMode.ToString().ToLower()}\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("t_conf")]
|
||||||
|
[Produces("text/json")]
|
||||||
|
public IActionResult Conf() => this.Ok("[{\"StatusCode\":200}]");
|
||||||
|
|
||||||
|
[HttpGet("farc_hashes")]
|
||||||
|
public IActionResult FarcHashes() => this.Ok();
|
||||||
|
|
||||||
|
[HttpGet("privacySettings")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public IActionResult PrivacySettings()
|
||||||
|
{
|
||||||
|
PrivacySettings ps = new()
|
||||||
{
|
{
|
||||||
HostString hostname = this.Request.Host;
|
LevelVisibility = "all",
|
||||||
return this.Ok
|
ProfileVisibility = "all",
|
||||||
(
|
};
|
||||||
"ProbabilityOfPacketDelay 0.0\nMinPacketDelayFrames 0\nMaxPacketDelayFrames 3\nProbabilityOfPacketDrop 0.0\nEnableFakeConditionsForLoopback true\nNumberOfFramesPredictionAllowedForNonLocalPlayer 1000\nEnablePrediction true\nMinPredictedFrames 0\nMaxPredictedFrames 10\nAllowGameRendCameraSplit true\nFramesBeforeAgressiveCatchup 30\nPredictionPadSides 200\nPredictionPadTop 200\nPredictionPadBottom 200\nShowErrorNumbers true\nAllowModeratedLevels false\nAllowModeratedPoppetItems false\nTIMEOUT_WAIT_FOR_JOIN_RESPONSE_FROM_PREV_PARTY_HOST 50.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_HOST 30.0\nTIMEOUT_WAIT_FOR_CHANGE_LEVEL_PARTY_MEMBER 45.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN_FRIEND 15.0\nTIMEOUT_WAIT_FOR_CONNECTION_FROM_HOST 30.0\nTIMEOUT_WAIT_FOR_ROOM_ID_TO_JOIN 60.0\nTIMEOUT_WAIT_FOR_GET_NUM_PLAYERS_ONLINE 60.0\nTIMEOUT_WAIT_FOR_SIGNALLING_CONNECTIONS 120.0\nTIMEOUT_WAIT_FOR_PARTY_DATA 60.0\nTIME_TO_WAIT_FOR_LEAVE_MESSAGE_TO_COME_BACK 20.0\nTIME_TO_WAIT_FOR_FOLLOWING_REQUESTS_TO_ARRIVE 30.0\nTIMEOUT_WAIT_FOR_FINISHED_MIGRATING_HOST 30.0\nTIMEOUT_WAIT_FOR_PARTY_LEADER_FINISH_JOINING 45.0\nTIMEOUT_WAIT_FOR_QUICKPLAY_LEVEL 60.0\nTIMEOUT_WAIT_FOR_PLAYERS_TO_JOIN 30.0\nTIMEOUT_WAIT_FOR_DIVE_IN_PLAYERS 240.0\nTIMEOUT_WAIT_FOR_FIND_BEST_ROOM 60.0\nTIMEOUT_DIVE_IN_TOTAL 300.0\nTIMEOUT_WAIT_FOR_SOCKET_CONNECTION 120.0\nTIMEOUT_WAIT_FOR_REQUEST_RESOURCE_MESSAGE 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_GET_RESOURCE_LIST 120.0\nTIMEOUT_WAIT_FOR_CLIENT_TO_LOAD_RESOURCES 120.0\nTIMEOUT_WAIT_FOR_LOCAL_CLIENT_TO_SAVE_GAME_STATE 30.0\nTIMEOUT_WAIT_FOR_ADD_PLAYERS_TO_TAKE 30.0\nTIMEOUT_WAIT_FOR_UPDATE_FROM_CLIENT 90.0\nTIMEOUT_WAIT_FOR_HOST_TO_GET_RESOURCE_LIST 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_SAVE_GAME_STATE 60.0\nTIMEOUT_WAIT_FOR_HOST_TO_ADD_US 30.0\nTIMEOUT_WAIT_FOR_UPDATE 60.0\nTIMEOUT_WAIT_FOR_REQUEST_JOIN 50.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_PRESENCE 60.0\nTIMEOUT_WAIT_FOR_AUTOJOIN_CONNECTION 120.0\nSECONDS_BETWEEN_PINS_AWARDED_UPLOADS 300.0\nEnableKeepAlive true\nAllowVoIPRecordingPlayback true\nOverheatingThresholdDisallowMidgameJoin 0.95\nMaxCatchupFrames 3\nMaxLagBeforeShowLoading 23\nMinLagBeforeHideLoading 30\nLagImprovementInflectionPoint -1.0\nFlickerThreshold 2.0\nClosedDemo2014Version 1\nClosedDemo2014Expired false\nEnablePlayedFilter true\nEnableCommunityDecorations true\nGameStateUpdateRate 10.0\nGameStateUpdateRateWithConsumers 1.0\nDisableDLCPublishCheck false\nEnableDiveIn true\nEnableHackChecks false\n" +
|
|
||||||
$"TelemetryServer {hostname}\n" +
|
|
||||||
$"CDNHostName {hostname}\n" +
|
|
||||||
$"ShowLevelBoos {ServerSettings.Instance.BooingEnabled.ToString().ToLower()}\n" +
|
|
||||||
$"AllowOnlineCreate {ServerSettings.Instance.VitaCreateMode.ToString().ToLower()}\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("t_conf")]
|
return this.Ok(ps.Serialize());
|
||||||
[Produces("text/json")]
|
|
||||||
public IActionResult Conf() => this.Ok("[{\"StatusCode\":200}]");
|
|
||||||
|
|
||||||
[HttpGet("farc_hashes")]
|
|
||||||
public IActionResult FarcHashes() => this.Ok();
|
|
||||||
|
|
||||||
[HttpGet("privacySettings")]
|
|
||||||
[Produces("text/xml")]
|
|
||||||
public IActionResult PrivacySettings()
|
|
||||||
{
|
|
||||||
PrivacySettings ps = new()
|
|
||||||
{
|
|
||||||
LevelVisibility = "all",
|
|
||||||
ProfileVisibility = "all",
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.Ok(ps.Serialize());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,110 +11,109 @@ using LBPUnion.ProjectLighthouse.Types.Categories;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class CollectionController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
|
||||||
[Produces("text/xml")]
|
public CollectionController(Database database)
|
||||||
public class CollectionController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public CollectionController(Database database)
|
[HttpGet("user/{username}/playlists")]
|
||||||
{
|
public IActionResult GetUserPlaylists(string username) => this.Ok();
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("user/{username}/playlists")]
|
[HttpGet("searches")]
|
||||||
public IActionResult GetUserPlaylists(string username) => this.Ok();
|
[HttpGet("genres")]
|
||||||
|
public async Task<IActionResult> GenresAndSearches()
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
[HttpGet("searches")]
|
string categoriesSerialized = CollectionHelper.Categories.Aggregate
|
||||||
[HttpGet("genres")]
|
(
|
||||||
public async Task<IActionResult> GenresAndSearches()
|
string.Empty,
|
||||||
{
|
(current, category) =>
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
{
|
||||||
if (user == null) return this.StatusCode(403, "");
|
string serialized;
|
||||||
|
|
||||||
string categoriesSerialized = CollectionHelper.Categories.Aggregate
|
if (category is CategoryWithUser categoryWithUser) serialized = categoryWithUser.Serialize(this.database, user);
|
||||||
|
else serialized = category.Serialize(this.database);
|
||||||
|
|
||||||
|
return current + serialized;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.Ok
|
||||||
|
(
|
||||||
|
LbpSerializer.TaggedStringElement
|
||||||
(
|
(
|
||||||
string.Empty,
|
"categories",
|
||||||
(current, category) =>
|
categoriesSerialized,
|
||||||
|
new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
string serialized;
|
{
|
||||||
|
"hint", ""
|
||||||
if (category is CategoryWithUser categoryWithUser) serialized = categoryWithUser.Serialize(this.database, user);
|
},
|
||||||
else serialized = category.Serialize(this.database);
|
{
|
||||||
|
"hint_start", 1
|
||||||
return current + serialized;
|
},
|
||||||
|
{
|
||||||
|
"total", CollectionHelper.Categories.Count
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return this.Ok
|
[HttpGet("searches/{endpointName}")]
|
||||||
(
|
public async Task<IActionResult> GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||||
LbpSerializer.TaggedStringElement
|
{
|
||||||
(
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
"categories",
|
if (user == null) return this.StatusCode(403, "");
|
||||||
categoriesSerialized,
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
"hint", ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hint_start", 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"total", CollectionHelper.Categories.Count
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("searches/{endpointName}")]
|
Category? category = CollectionHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName);
|
||||||
public async Task<IActionResult> GetCategorySlots(string endpointName, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
if (category == null) return this.NotFound();
|
||||||
|
|
||||||
|
Logger.Log("Found category " + category, LoggerLevelCategory.Instance);
|
||||||
|
|
||||||
|
List<Slot> slots;
|
||||||
|
int totalSlots;
|
||||||
|
|
||||||
|
if (category is CategoryWithUser categoryWithUser)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
slots = categoryWithUser.GetSlots(this.database, user, pageStart, pageSize).ToList();
|
||||||
if (user == null) return this.StatusCode(403, "");
|
totalSlots = categoryWithUser.GetTotalSlots(this.database, user);
|
||||||
|
|
||||||
Category? category = CollectionHelper.Categories.FirstOrDefault(c => c.Endpoint == endpointName);
|
|
||||||
if (category == null) return this.NotFound();
|
|
||||||
|
|
||||||
Logger.Log("Found category " + category, LoggerLevelCategory.Instance);
|
|
||||||
|
|
||||||
List<Slot> slots;
|
|
||||||
int totalSlots;
|
|
||||||
|
|
||||||
if (category is CategoryWithUser categoryWithUser)
|
|
||||||
{
|
|
||||||
slots = categoryWithUser.GetSlots(this.database, user, pageStart, pageSize).ToList();
|
|
||||||
totalSlots = categoryWithUser.GetTotalSlots(this.database, user);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
slots = category.GetSlots(this.database, pageStart, pageSize).ToList();
|
|
||||||
totalSlots = category.GetTotalSlots(this.database);
|
|
||||||
}
|
|
||||||
|
|
||||||
string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
|
||||||
|
|
||||||
return this.Ok
|
|
||||||
(
|
|
||||||
LbpSerializer.TaggedStringElement
|
|
||||||
(
|
|
||||||
"results",
|
|
||||||
slotsSerialized,
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
"total", totalSlots
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hint_start", pageStart + pageSize
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slots = category.GetSlots(this.database, pageStart, pageSize).ToList();
|
||||||
|
totalSlots = category.GetTotalSlots(this.database);
|
||||||
|
}
|
||||||
|
|
||||||
|
string slotsSerialized = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
||||||
|
|
||||||
|
return this.Ok
|
||||||
|
(
|
||||||
|
LbpSerializer.TaggedStringElement
|
||||||
|
(
|
||||||
|
"results",
|
||||||
|
slotsSerialized,
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"total", totalSlots
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hint_start", pageStart + pageSize
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,73 +11,72 @@ using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class CommentController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
public CommentController(Database database)
|
||||||
[Produces("text/xml")]
|
|
||||||
public class CommentController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
public CommentController(Database database)
|
}
|
||||||
{
|
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("userComments/{username}")]
|
[HttpGet("userComments/{username}")]
|
||||||
public async Task<IActionResult> GetComments(string username)
|
public async Task<IActionResult> GetComments(string username)
|
||||||
{
|
{
|
||||||
List<Comment> comments = await this.database.Comments.Include
|
List<Comment> comments = await this.database.Comments.Include
|
||||||
(c => c.Target)
|
(c => c.Target)
|
||||||
.Include(c => c.Poster)
|
.Include(c => c.Poster)
|
||||||
.Where(c => c.Target.Username == username)
|
.Where(c => c.Target.Username == username)
|
||||||
.OrderByDescending(c => c.Timestamp)
|
.OrderByDescending(c => c.Timestamp)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
string outputXml = comments.Aggregate(string.Empty, (current, comment) => current + comment.Serialize());
|
string outputXml = comments.Aggregate(string.Empty, (current, comment) => current + comment.Serialize());
|
||||||
return this.Ok(LbpSerializer.StringElement("comments", outputXml));
|
return this.Ok(LbpSerializer.StringElement("comments", outputXml));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("postUserComment/{username}")]
|
[HttpPost("postUserComment/{username}")]
|
||||||
public async Task<IActionResult> PostComment(string username)
|
public async Task<IActionResult> PostComment(string username)
|
||||||
{
|
{
|
||||||
this.Request.Body.Position = 0;
|
this.Request.Body.Position = 0;
|
||||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
XmlSerializer serializer = new(typeof(Comment));
|
XmlSerializer serializer = new(typeof(Comment));
|
||||||
Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString));
|
Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString));
|
||||||
|
|
||||||
User? poster = await this.database.UserFromGameRequest(this.Request);
|
User? poster = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (poster == null) return this.StatusCode(403, "");
|
if (poster == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
User? target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
User? target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||||
if (comment == null || target == null) return this.BadRequest();
|
if (comment == null || target == null) return this.BadRequest();
|
||||||
|
|
||||||
comment.PosterUserId = poster.UserId;
|
comment.PosterUserId = poster.UserId;
|
||||||
comment.TargetUserId = target.UserId;
|
comment.TargetUserId = target.UserId;
|
||||||
|
|
||||||
comment.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
comment.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
||||||
|
|
||||||
this.database.Comments.Add(comment);
|
this.database.Comments.Add(comment);
|
||||||
await this.database.SaveChangesAsync();
|
await this.database.SaveChangesAsync();
|
||||||
return this.Ok();
|
return this.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("deleteUserComment/{username}")]
|
[HttpPost("deleteUserComment/{username}")]
|
||||||
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string username)
|
public async Task<IActionResult> DeleteComment([FromQuery] int commentId, string username)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (user == null) return this.StatusCode(403, "");
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
Comment? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
||||||
if (comment == null) return this.NotFound();
|
if (comment == null) return this.NotFound();
|
||||||
|
|
||||||
if (comment.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) return this.StatusCode(403, "");
|
if (comment.TargetUserId != user.UserId && comment.PosterUserId != user.UserId) return this.StatusCode(403, "");
|
||||||
|
|
||||||
this.database.Comments.Remove(comment);
|
this.database.Comments.Remove(comment);
|
||||||
await this.database.SaveChangesAsync();
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
return this.Ok();
|
return this.Ok();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
public class DeveloperController : Controller
|
||||||
{
|
{
|
||||||
[ApiController]
|
[HttpGet("/developer_videos")]
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
public IActionResult DeveloperVideos() => this.Ok();
|
||||||
public class DeveloperController : Controller
|
|
||||||
{
|
|
||||||
[HttpGet("/developer_videos")]
|
|
||||||
public IActionResult DeveloperVideos() => this.Ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -7,123 +7,122 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
{
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
// [Produces("text/plain")]
|
// [Produces("text/plain")]
|
||||||
public class EnterLevelController : ControllerBase
|
public class EnterLevelController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly Database database;
|
||||||
|
|
||||||
|
public EnterLevelController(Database database)
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public EnterLevelController(Database database)
|
[HttpPost("play/user/{slotId}")]
|
||||||
|
public async Task<IActionResult> PlayLevel(int slotId)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||||
|
if (slot == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
GameVersion gameVersion = token.GameVersion;
|
||||||
|
|
||||||
|
IQueryable<VisitedLevel> visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == user.UserId);
|
||||||
|
VisitedLevel? v;
|
||||||
|
if (!visited.Any())
|
||||||
{
|
{
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("play/user/{slotId}")]
|
|
||||||
public async Task<IActionResult> PlayLevel(int slotId)
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
|
||||||
if (slot == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
|
||||||
if (token == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
GameVersion gameVersion = token.GameVersion;
|
|
||||||
|
|
||||||
IQueryable<VisitedLevel> visited = this.database.VisitedLevels.Where(s => s.SlotId == slotId && s.UserId == user.UserId);
|
|
||||||
VisitedLevel? v;
|
|
||||||
if (!visited.Any())
|
|
||||||
{
|
|
||||||
switch (gameVersion)
|
|
||||||
{
|
|
||||||
case GameVersion.LittleBigPlanet2:
|
|
||||||
slot.PlaysLBP2Unique++;
|
|
||||||
break;
|
|
||||||
case GameVersion.LittleBigPlanet3:
|
|
||||||
slot.PlaysLBP3Unique++;
|
|
||||||
break;
|
|
||||||
case GameVersion.LittleBigPlanetVita:
|
|
||||||
slot.PlaysLBPVitaUnique++;
|
|
||||||
break;
|
|
||||||
default: return this.BadRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
v = new VisitedLevel();
|
|
||||||
v.SlotId = slotId;
|
|
||||||
v.UserId = user.UserId;
|
|
||||||
this.database.VisitedLevels.Add(v);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
v = await visited.FirstOrDefaultAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v == null) return this.NotFound();
|
|
||||||
|
|
||||||
switch (gameVersion)
|
switch (gameVersion)
|
||||||
{
|
{
|
||||||
case GameVersion.LittleBigPlanet2:
|
case GameVersion.LittleBigPlanet2:
|
||||||
slot.PlaysLBP2++;
|
slot.PlaysLBP2Unique++;
|
||||||
v.PlaysLBP2++;
|
|
||||||
break;
|
break;
|
||||||
case GameVersion.LittleBigPlanet3:
|
case GameVersion.LittleBigPlanet3:
|
||||||
slot.PlaysLBP3++;
|
slot.PlaysLBP3Unique++;
|
||||||
v.PlaysLBP3++;
|
|
||||||
break;
|
break;
|
||||||
case GameVersion.LittleBigPlanetVita:
|
case GameVersion.LittleBigPlanetVita:
|
||||||
slot.PlaysLBPVita++;
|
slot.PlaysLBPVitaUnique++;
|
||||||
v.PlaysLBPVita++;
|
|
||||||
break;
|
break;
|
||||||
case GameVersion.LittleBigPlanetPSP: throw new NotImplementedException();
|
default: return this.BadRequest();
|
||||||
case GameVersion.Unknown:
|
|
||||||
default:
|
|
||||||
return this.BadRequest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
v = new VisitedLevel();
|
||||||
|
v.SlotId = slotId;
|
||||||
return this.Ok();
|
v.UserId = user.UserId;
|
||||||
|
this.database.VisitedLevels.Add(v);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Only used in LBP1
|
|
||||||
[HttpGet("enterLevel/{id:int}")]
|
|
||||||
public async Task<IActionResult> EnterLevel(int id)
|
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
v = await visited.FirstOrDefaultAsync();
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
IQueryable<VisitedLevel> visited = this.database.VisitedLevels.Where(s => s.SlotId == id && s.UserId == user.UserId);
|
|
||||||
VisitedLevel? v;
|
|
||||||
if (!visited.Any())
|
|
||||||
{
|
|
||||||
slot.PlaysLBP1Unique++;
|
|
||||||
|
|
||||||
v = new VisitedLevel();
|
|
||||||
v.SlotId = id;
|
|
||||||
v.UserId = user.UserId;
|
|
||||||
this.database.VisitedLevels.Add(v);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
v = await visited.FirstOrDefaultAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v == null) return this.NotFound();
|
|
||||||
|
|
||||||
slot.PlaysLBP1++;
|
|
||||||
v.PlaysLBP1++;
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (v == null) return this.NotFound();
|
||||||
|
|
||||||
|
switch (gameVersion)
|
||||||
|
{
|
||||||
|
case GameVersion.LittleBigPlanet2:
|
||||||
|
slot.PlaysLBP2++;
|
||||||
|
v.PlaysLBP2++;
|
||||||
|
break;
|
||||||
|
case GameVersion.LittleBigPlanet3:
|
||||||
|
slot.PlaysLBP3++;
|
||||||
|
v.PlaysLBP3++;
|
||||||
|
break;
|
||||||
|
case GameVersion.LittleBigPlanetVita:
|
||||||
|
slot.PlaysLBPVita++;
|
||||||
|
v.PlaysLBPVita++;
|
||||||
|
break;
|
||||||
|
case GameVersion.LittleBigPlanetPSP: throw new NotImplementedException();
|
||||||
|
case GameVersion.Unknown:
|
||||||
|
default:
|
||||||
|
return this.BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only used in LBP1
|
||||||
|
[HttpGet("enterLevel/{id:int}")]
|
||||||
|
public async Task<IActionResult> EnterLevel(int id)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
|
IQueryable<VisitedLevel> visited = this.database.VisitedLevels.Where(s => s.SlotId == id && s.UserId == user.UserId);
|
||||||
|
VisitedLevel? v;
|
||||||
|
if (!visited.Any())
|
||||||
|
{
|
||||||
|
slot.PlaysLBP1Unique++;
|
||||||
|
|
||||||
|
v = new VisitedLevel();
|
||||||
|
v.SlotId = id;
|
||||||
|
v.UserId = user.UserId;
|
||||||
|
this.database.VisitedLevels.Add(v);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v = await visited.FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v == null) return this.NotFound();
|
||||||
|
|
||||||
|
slot.PlaysLBP1++;
|
||||||
|
v.PlaysLBP1++;
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,88 +11,87 @@ using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
public class FriendsController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
|
||||||
public class FriendsController : ControllerBase
|
public FriendsController(Database database)
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public FriendsController(Database database)
|
[HttpPost("npdata")]
|
||||||
|
public async Task<IActionResult> NPData()
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
this.Request.Body.Position = 0;
|
||||||
|
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
|
XmlSerializer serializer = new(typeof(NPData));
|
||||||
|
NPData? npData = (NPData?)serializer.Deserialize(new StringReader(bodyString));
|
||||||
|
if (npData == null) return this.BadRequest();
|
||||||
|
|
||||||
|
List<User> friends = new();
|
||||||
|
foreach (string friendName in npData.Friends)
|
||||||
{
|
{
|
||||||
this.database = database;
|
User? friend = await this.database.Users.FirstOrDefaultAsync(u => u.Username == friendName);
|
||||||
|
if (friend == null) continue;
|
||||||
|
|
||||||
|
friends.Add(friend);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("npdata")]
|
List<int> blockedUsers = new();
|
||||||
public async Task<IActionResult> NPData()
|
foreach (string blockedUserName in npData.BlockedUsers)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == blockedUserName);
|
||||||
if (user == null) return this.StatusCode(403, "");
|
if (blockedUser == null) continue;
|
||||||
|
|
||||||
this.Request.Body.Position = 0;
|
blockedUsers.Add(blockedUser.UserId);
|
||||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
|
||||||
|
|
||||||
XmlSerializer serializer = new(typeof(NPData));
|
|
||||||
NPData? npData = (NPData?)serializer.Deserialize(new StringReader(bodyString));
|
|
||||||
if (npData == null) return this.BadRequest();
|
|
||||||
|
|
||||||
List<User> friends = new();
|
|
||||||
foreach (string friendName in npData.Friends)
|
|
||||||
{
|
|
||||||
User? friend = await this.database.Users.FirstOrDefaultAsync(u => u.Username == friendName);
|
|
||||||
if (friend == null) continue;
|
|
||||||
|
|
||||||
friends.Add(friend);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<int> blockedUsers = new();
|
|
||||||
foreach (string blockedUserName in npData.BlockedUsers)
|
|
||||||
{
|
|
||||||
User? blockedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == blockedUserName);
|
|
||||||
if (blockedUser == null) continue;
|
|
||||||
|
|
||||||
blockedUsers.Add(blockedUser.UserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FriendHelper.FriendIdsByUserId.ContainsKey(user.UserId))
|
|
||||||
{
|
|
||||||
FriendHelper.FriendIdsByUserId.Remove(user.UserId);
|
|
||||||
FriendHelper.BlockedIdsByUserId.Remove(user.UserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
FriendHelper.FriendIdsByUserId.Add(user.UserId, friends.Select(u => u.UserId).ToArray());
|
|
||||||
FriendHelper.BlockedIdsByUserId.Add(user.UserId, blockedUsers.ToArray());
|
|
||||||
|
|
||||||
string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username));
|
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.StringElement("npdata", LbpSerializer.StringElement("friends", friendsSerialized)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("myFriends")]
|
if (FriendHelper.FriendIdsByUserId.ContainsKey(user.UserId))
|
||||||
public async Task<IActionResult> MyFriends()
|
|
||||||
{
|
{
|
||||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
FriendHelper.FriendIdsByUserId.Remove(user.UserId);
|
||||||
|
FriendHelper.BlockedIdsByUserId.Remove(user.UserId);
|
||||||
if (userAndToken == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
|
||||||
User user = userAndToken.Value.Item1;
|
|
||||||
GameToken gameToken = userAndToken.Value.Item2;
|
|
||||||
|
|
||||||
if (!FriendHelper.FriendIdsByUserId.TryGetValue(user.UserId, out int[]? friendIds) || friendIds == null)
|
|
||||||
return this.Ok(LbpSerializer.BlankElement("myFriends"));
|
|
||||||
|
|
||||||
string friends = "";
|
|
||||||
foreach (int friendId in friendIds)
|
|
||||||
{
|
|
||||||
User? friend = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == friendId);
|
|
||||||
if (friend == null) continue;
|
|
||||||
|
|
||||||
friends += friend.Serialize(gameToken.GameVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.StringElement("myFriends", friends));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FriendHelper.FriendIdsByUserId.Add(user.UserId, friends.Select(u => u.UserId).ToArray());
|
||||||
|
FriendHelper.BlockedIdsByUserId.Add(user.UserId, blockedUsers.ToArray());
|
||||||
|
|
||||||
|
string friendsSerialized = friends.Aggregate(string.Empty, (current, user1) => current + LbpSerializer.StringElement("npHandle", user1.Username));
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.StringElement("npdata", LbpSerializer.StringElement("friends", friendsSerialized)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("myFriends")]
|
||||||
|
public async Task<IActionResult> MyFriends()
|
||||||
|
{
|
||||||
|
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||||
|
|
||||||
|
if (userAndToken == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
// ReSharper disable once PossibleInvalidOperationException
|
||||||
|
User user = userAndToken.Value.Item1;
|
||||||
|
GameToken gameToken = userAndToken.Value.Item2;
|
||||||
|
|
||||||
|
if (!FriendHelper.FriendIdsByUserId.TryGetValue(user.UserId, out int[]? friendIds) || friendIds == null)
|
||||||
|
return this.Ok(LbpSerializer.BlankElement("myFriends"));
|
||||||
|
|
||||||
|
string friends = "";
|
||||||
|
foreach (int friendId in friendIds)
|
||||||
|
{
|
||||||
|
User? friend = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == friendId);
|
||||||
|
if (friend == null) continue;
|
||||||
|
|
||||||
|
friends += friend.Serialize(gameToken.GameVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.StringElement("myFriends", friends));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,26 +2,25 @@ using System;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/tags")]
|
||||||
|
[Produces("text/plain")]
|
||||||
|
public class LevelTagsController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
[HttpGet]
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/tags")]
|
public IActionResult Get()
|
||||||
[Produces("text/plain")]
|
|
||||||
public class LevelTagsController : ControllerBase
|
|
||||||
{
|
{
|
||||||
[HttpGet]
|
string[] tags = Enum.GetNames(typeof(LevelTags));
|
||||||
public IActionResult Get()
|
|
||||||
|
int i = 0;
|
||||||
|
foreach (string tag in tags)
|
||||||
{
|
{
|
||||||
string[] tags = Enum.GetNames(typeof(LevelTags));
|
tags[i] = $"TAG_{tag.Replace("_", "-")}";
|
||||||
|
i++;
|
||||||
int i = 0;
|
|
||||||
foreach (string tag in tags)
|
|
||||||
{
|
|
||||||
tags[i] = $"TAG_{tag.Replace("_", "-")}";
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.Ok(string.Join(",", tags));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.Ok(string.Join(",", tags));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,209 +9,208 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class ListController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
public ListController(Database database)
|
||||||
[Produces("text/xml")]
|
|
||||||
public class ListController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
public ListController(Database database)
|
|
||||||
{
|
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Levels
|
|
||||||
|
|
||||||
#region Level Queue (lolcatftw)
|
|
||||||
|
|
||||||
[HttpGet("slots/lolcatftw/{username}")]
|
|
||||||
public async Task<IActionResult> GetLevelQueue(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
|
||||||
{
|
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
|
||||||
if (token == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
GameVersion gameVersion = token.GameVersion;
|
|
||||||
|
|
||||||
IEnumerable<QueuedLevel> queuedLevels = this.database.QueuedLevels.Include(q => q.User)
|
|
||||||
.Include(q => q.Slot)
|
|
||||||
.Include(q => q.Slot.Location)
|
|
||||||
.Include(q => q.Slot.Creator)
|
|
||||||
.Where(q => q.Slot.GameVersion <= gameVersion)
|
|
||||||
.Where(q => q.User.Username == username)
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, 30))
|
|
||||||
.AsEnumerable();
|
|
||||||
|
|
||||||
string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize());
|
|
||||||
|
|
||||||
return this.Ok
|
|
||||||
(
|
|
||||||
LbpSerializer.TaggedStringElement
|
|
||||||
("slots", response, "total", this.database.QueuedLevels.Include(q => q.User).Count(q => q.User.Username == username))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("lolcatftw/add/user/{id:int}")]
|
|
||||||
public async Task<IActionResult> AddQueuedLevel(int id)
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.QueueLevel(user, slot);
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("lolcatftw/remove/user/{id:int}")]
|
|
||||||
public async Task<IActionResult> RemoveQueuedLevel(int id)
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.UnqueueLevel(user, slot);
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("lolcatftw/clear")]
|
|
||||||
public async Task<IActionResult> ClearQueuedLevels()
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == user.UserId));
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Hearted Levels
|
|
||||||
|
|
||||||
[HttpGet("favouriteSlots/{username}")]
|
|
||||||
public async Task<IActionResult> GetFavouriteSlots(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
|
||||||
{
|
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
|
||||||
if (token == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
GameVersion gameVersion = token.GameVersion;
|
|
||||||
|
|
||||||
IEnumerable<HeartedLevel> heartedLevels = this.database.HeartedLevels.Include(q => q.User)
|
|
||||||
.Include(q => q.Slot)
|
|
||||||
.Include(q => q.Slot.Location)
|
|
||||||
.Include(q => q.Slot.Creator)
|
|
||||||
.Where(q => q.Slot.GameVersion <= gameVersion)
|
|
||||||
.Where(q => q.User.Username == username)
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, 30))
|
|
||||||
.AsEnumerable();
|
|
||||||
|
|
||||||
string response = heartedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize());
|
|
||||||
|
|
||||||
return this.Ok
|
|
||||||
(
|
|
||||||
LbpSerializer.TaggedStringElement
|
|
||||||
("favouriteSlots", response, "total", this.database.HeartedLevels.Include(q => q.User).Count(q => q.User.Username == username))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("favourite/slot/user/{id:int}")]
|
|
||||||
public async Task<IActionResult> AddFavouriteSlot(int id)
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.HeartLevel(user, slot);
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("unfavourite/slot/user/{id:int}")]
|
|
||||||
public async Task<IActionResult> RemoveFavouriteSlot(int id)
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.UnheartLevel(user, slot);
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#endregion Levels
|
|
||||||
|
|
||||||
#region Users
|
|
||||||
|
|
||||||
[HttpGet("favouriteUsers/{username}")]
|
|
||||||
public async Task<IActionResult> GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
|
||||||
{
|
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
|
||||||
if (token == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
IEnumerable<HeartedProfile> heartedProfiles = this.database.HeartedProfiles.Include
|
|
||||||
(q => q.User)
|
|
||||||
.Include(q => q.HeartedUser)
|
|
||||||
.Include(q => q.HeartedUser.Location)
|
|
||||||
.Where(q => q.User.Username == username)
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, 30))
|
|
||||||
.AsEnumerable();
|
|
||||||
|
|
||||||
string response = heartedProfiles.Aggregate(string.Empty, (current, q) => current + q.HeartedUser.Serialize(token.GameVersion));
|
|
||||||
|
|
||||||
return this.Ok
|
|
||||||
(
|
|
||||||
LbpSerializer.TaggedStringElement
|
|
||||||
("favouriteUsers", response, "total", this.database.HeartedProfiles.Include(q => q.User).Count(q => q.User.Username == username))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("favourite/user/{username}")]
|
|
||||||
public async Task<IActionResult> AddFavouriteUser(string username)
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
|
||||||
if (heartedUser == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.HeartUser(user, heartedUser);
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("unfavourite/user/{username}")]
|
|
||||||
public async Task<IActionResult> RemoveFavouriteUser(string username)
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
|
||||||
if (heartedUser == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.UnheartUser(user, heartedUser);
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Levels
|
||||||
|
|
||||||
|
#region Level Queue (lolcatftw)
|
||||||
|
|
||||||
|
[HttpGet("slots/lolcatftw/{username}")]
|
||||||
|
public async Task<IActionResult> GetLevelQueue(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
||||||
|
{
|
||||||
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
GameVersion gameVersion = token.GameVersion;
|
||||||
|
|
||||||
|
IEnumerable<QueuedLevel> queuedLevels = this.database.QueuedLevels.Include(q => q.User)
|
||||||
|
.Include(q => q.Slot)
|
||||||
|
.Include(q => q.Slot.Location)
|
||||||
|
.Include(q => q.Slot.Creator)
|
||||||
|
.Where(q => q.Slot.GameVersion <= gameVersion)
|
||||||
|
.Where(q => q.User.Username == username)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30))
|
||||||
|
.AsEnumerable();
|
||||||
|
|
||||||
|
string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize());
|
||||||
|
|
||||||
|
return this.Ok
|
||||||
|
(
|
||||||
|
LbpSerializer.TaggedStringElement
|
||||||
|
("slots", response, "total", this.database.QueuedLevels.Include(q => q.User).Count(q => q.User.Username == username))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("lolcatftw/add/user/{id:int}")]
|
||||||
|
public async Task<IActionResult> AddQueuedLevel(int id)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
|
await this.database.QueueLevel(user, slot);
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("lolcatftw/remove/user/{id:int}")]
|
||||||
|
public async Task<IActionResult> RemoveQueuedLevel(int id)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
|
await this.database.UnqueueLevel(user, slot);
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("lolcatftw/clear")]
|
||||||
|
public async Task<IActionResult> ClearQueuedLevels()
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == user.UserId));
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Hearted Levels
|
||||||
|
|
||||||
|
[HttpGet("favouriteSlots/{username}")]
|
||||||
|
public async Task<IActionResult> GetFavouriteSlots(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
||||||
|
{
|
||||||
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
GameVersion gameVersion = token.GameVersion;
|
||||||
|
|
||||||
|
IEnumerable<HeartedLevel> heartedLevels = this.database.HeartedLevels.Include(q => q.User)
|
||||||
|
.Include(q => q.Slot)
|
||||||
|
.Include(q => q.Slot.Location)
|
||||||
|
.Include(q => q.Slot.Creator)
|
||||||
|
.Where(q => q.Slot.GameVersion <= gameVersion)
|
||||||
|
.Where(q => q.User.Username == username)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30))
|
||||||
|
.AsEnumerable();
|
||||||
|
|
||||||
|
string response = heartedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize());
|
||||||
|
|
||||||
|
return this.Ok
|
||||||
|
(
|
||||||
|
LbpSerializer.TaggedStringElement
|
||||||
|
("favouriteSlots", response, "total", this.database.HeartedLevels.Include(q => q.User).Count(q => q.User.Username == username))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("favourite/slot/user/{id:int}")]
|
||||||
|
public async Task<IActionResult> AddFavouriteSlot(int id)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
|
await this.database.HeartLevel(user, slot);
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("unfavourite/slot/user/{id:int}")]
|
||||||
|
public async Task<IActionResult> RemoveFavouriteSlot(int id)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
|
await this.database.UnheartLevel(user, slot);
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion Levels
|
||||||
|
|
||||||
|
#region Users
|
||||||
|
|
||||||
|
[HttpGet("favouriteUsers/{username}")]
|
||||||
|
public async Task<IActionResult> GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
||||||
|
{
|
||||||
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
IEnumerable<HeartedProfile> heartedProfiles = this.database.HeartedProfiles.Include
|
||||||
|
(q => q.User)
|
||||||
|
.Include(q => q.HeartedUser)
|
||||||
|
.Include(q => q.HeartedUser.Location)
|
||||||
|
.Where(q => q.User.Username == username)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30))
|
||||||
|
.AsEnumerable();
|
||||||
|
|
||||||
|
string response = heartedProfiles.Aggregate(string.Empty, (current, q) => current + q.HeartedUser.Serialize(token.GameVersion));
|
||||||
|
|
||||||
|
return this.Ok
|
||||||
|
(
|
||||||
|
LbpSerializer.TaggedStringElement
|
||||||
|
("favouriteUsers", response, "total", this.database.HeartedProfiles.Include(q => q.User).Count(q => q.User.Username == username))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("favourite/user/{username}")]
|
||||||
|
public async Task<IActionResult> AddFavouriteUser(string username)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||||
|
if (heartedUser == null) return this.NotFound();
|
||||||
|
|
||||||
|
await this.database.HeartUser(user, heartedUser);
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("unfavourite/user/{username}")]
|
||||||
|
public async Task<IActionResult> RemoveFavouriteUser(string username)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||||
|
if (heartedUser == null) return this.NotFound();
|
||||||
|
|
||||||
|
await this.database.UnheartUser(user, heartedUser);
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
|
@ -11,144 +11,143 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
{
|
|
||||||
[ApiController]
|
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/login")]
|
|
||||||
[Produces("text/xml")]
|
|
||||||
public class LoginController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly Database database;
|
|
||||||
|
|
||||||
public LoginController(Database database)
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/login")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class LoginController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly Database database;
|
||||||
|
|
||||||
|
public LoginController(Database database)
|
||||||
|
{
|
||||||
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Login([FromQuery] string? titleId)
|
||||||
|
{
|
||||||
|
titleId ??= "";
|
||||||
|
|
||||||
|
string body = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
|
LoginData? loginData;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
this.database = database;
|
loginData = LoginData.CreateFromString(body);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
loginData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
if (loginData == null)
|
||||||
public async Task<IActionResult> Login([FromQuery] string? titleId)
|
|
||||||
{
|
{
|
||||||
titleId ??= "";
|
Logger.Log("loginData was null, rejecting login", LoggerLevelLogin.Instance);
|
||||||
|
return this.BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
string body = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
IPAddress? remoteIpAddress = this.HttpContext.Connection.RemoteIpAddress;
|
||||||
|
if (remoteIpAddress == null)
|
||||||
|
{
|
||||||
|
Logger.Log("unable to determine ip, rejecting login", LoggerLevelLogin.Instance);
|
||||||
|
return this.StatusCode(403, ""); // 403 probably isnt the best status code for this, but whatever
|
||||||
|
}
|
||||||
|
|
||||||
LoginData? loginData;
|
string ipAddress = remoteIpAddress.ToString();
|
||||||
try
|
|
||||||
|
// Get an existing token from the IP & username
|
||||||
|
GameToken? token = await this.database.GameTokens.Include
|
||||||
|
(t => t.User)
|
||||||
|
.FirstOrDefaultAsync(t => t.UserLocation == ipAddress && t.User.Username == loginData.Username && !t.Used);
|
||||||
|
|
||||||
|
if (token == null) // If we cant find an existing token, try to generate a new one
|
||||||
|
{
|
||||||
|
token = await this.database.AuthenticateUser(loginData, ipAddress, titleId);
|
||||||
|
if (token == null)
|
||||||
{
|
{
|
||||||
loginData = LoginData.CreateFromString(body);
|
Logger.Log("unable to find/generate a token, rejecting login", LoggerLevelLogin.Instance);
|
||||||
|
return this.StatusCode(403, ""); // If not, then 403.
|
||||||
}
|
}
|
||||||
catch
|
}
|
||||||
|
|
||||||
|
User? user = await this.database.UserFromGameToken(token, true);
|
||||||
|
|
||||||
|
if (user == null || user.Banned)
|
||||||
|
{
|
||||||
|
Logger.Log("unable to find a user from a token, rejecting login", LoggerLevelLogin.Instance);
|
||||||
|
return this.StatusCode(403, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ServerSettings.Instance.UseExternalAuth)
|
||||||
|
{
|
||||||
|
if (ServerSettings.Instance.BlockDeniedUsers)
|
||||||
{
|
{
|
||||||
loginData = null;
|
string ipAddressAndName = $"{token.UserLocation}|{user.Username}";
|
||||||
}
|
if (DeniedAuthenticationHelper.RecentlyDenied(ipAddressAndName) || DeniedAuthenticationHelper.GetAttempts(ipAddressAndName) > 3)
|
||||||
|
|
||||||
if (loginData == null)
|
|
||||||
{
|
|
||||||
Logger.Log("loginData was null, rejecting login", LoggerLevelLogin.Instance);
|
|
||||||
return this.BadRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
IPAddress? remoteIpAddress = this.HttpContext.Connection.RemoteIpAddress;
|
|
||||||
if (remoteIpAddress == null)
|
|
||||||
{
|
|
||||||
Logger.Log("unable to determine ip, rejecting login", LoggerLevelLogin.Instance);
|
|
||||||
return this.StatusCode(403, ""); // 403 probably isnt the best status code for this, but whatever
|
|
||||||
}
|
|
||||||
|
|
||||||
string ipAddress = remoteIpAddress.ToString();
|
|
||||||
|
|
||||||
// Get an existing token from the IP & username
|
|
||||||
GameToken? token = await this.database.GameTokens.Include
|
|
||||||
(t => t.User)
|
|
||||||
.FirstOrDefaultAsync(t => t.UserLocation == ipAddress && t.User.Username == loginData.Username && !t.Used);
|
|
||||||
|
|
||||||
if (token == null) // If we cant find an existing token, try to generate a new one
|
|
||||||
{
|
|
||||||
token = await this.database.AuthenticateUser(loginData, ipAddress, titleId);
|
|
||||||
if (token == null)
|
|
||||||
{
|
{
|
||||||
Logger.Log("unable to find/generate a token, rejecting login", LoggerLevelLogin.Instance);
|
this.database.AuthenticationAttempts.RemoveRange
|
||||||
return this.StatusCode(403, ""); // If not, then 403.
|
(this.database.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId));
|
||||||
|
|
||||||
|
DeniedAuthenticationHelper.AddAttempt(ipAddressAndName);
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
Logger.Log("too many denied logins, rejecting login", LoggerLevelLogin.Instance);
|
||||||
|
return this.StatusCode(403, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
User? user = await this.database.UserFromGameToken(token, true);
|
if (this.database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).Select(a => a.IpAddress).Contains(ipAddress))
|
||||||
|
|
||||||
if (user == null || user.Banned)
|
|
||||||
{
|
|
||||||
Logger.Log("unable to find a user from a token, rejecting login", LoggerLevelLogin.Instance);
|
|
||||||
return this.StatusCode(403, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ServerSettings.Instance.UseExternalAuth)
|
|
||||||
{
|
|
||||||
if (ServerSettings.Instance.BlockDeniedUsers)
|
|
||||||
{
|
|
||||||
string ipAddressAndName = $"{token.UserLocation}|{user.Username}";
|
|
||||||
if (DeniedAuthenticationHelper.RecentlyDenied(ipAddressAndName) || DeniedAuthenticationHelper.GetAttempts(ipAddressAndName) > 3)
|
|
||||||
{
|
|
||||||
this.database.AuthenticationAttempts.RemoveRange
|
|
||||||
(this.database.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId));
|
|
||||||
|
|
||||||
DeniedAuthenticationHelper.AddAttempt(ipAddressAndName);
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
Logger.Log("too many denied logins, rejecting login", LoggerLevelLogin.Instance);
|
|
||||||
return this.StatusCode(403, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).Select(a => a.IpAddress).Contains(ipAddress))
|
|
||||||
{
|
|
||||||
token.Approved = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AuthenticationAttempt authAttempt = new()
|
|
||||||
{
|
|
||||||
GameToken = token,
|
|
||||||
GameTokenId = token.TokenId,
|
|
||||||
Timestamp = TimestampHelper.Timestamp,
|
|
||||||
IPAddress = ipAddress,
|
|
||||||
Platform = token.GameVersion == GameVersion.LittleBigPlanetVita ? Platform.Vita : Platform.PS3, // TODO: properly identify RPCS3
|
|
||||||
};
|
|
||||||
|
|
||||||
this.database.AuthenticationAttempts.Add(authAttempt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
token.Approved = true;
|
token.Approved = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
if (!token.Approved)
|
|
||||||
{
|
{
|
||||||
Logger.Log("token unapproved, rejecting login", LoggerLevelLogin.Instance);
|
AuthenticationAttempt authAttempt = new()
|
||||||
return this.StatusCode(403, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Log($"Successfully logged in user {user.Username} as {token.GameVersion} client ({titleId})", LoggerLevelLogin.Instance);
|
|
||||||
// After this point we are now considering this session as logged in.
|
|
||||||
|
|
||||||
// We just logged in with the token. Mark it as used so someone else doesnt try to use it,
|
|
||||||
// and so we don't pick the same token up when logging in later.
|
|
||||||
token.Used = true;
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
// Create a new room on LBP2/3/Vita
|
|
||||||
if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user, token.GameVersion);
|
|
||||||
|
|
||||||
return this.Ok
|
|
||||||
(
|
|
||||||
new LoginResult
|
|
||||||
{
|
{
|
||||||
AuthTicket = "MM_AUTH=" + token.UserToken,
|
GameToken = token,
|
||||||
LbpEnvVer = ServerStatics.ServerName,
|
GameTokenId = token.TokenId,
|
||||||
}.Serialize()
|
Timestamp = TimestampHelper.Timestamp,
|
||||||
);
|
IPAddress = ipAddress,
|
||||||
|
Platform = token.GameVersion == GameVersion.LittleBigPlanetVita ? Platform.Vita : Platform.PS3, // TODO: properly identify RPCS3
|
||||||
|
};
|
||||||
|
|
||||||
|
this.database.AuthenticationAttempts.Add(authAttempt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token.Approved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
if (!token.Approved)
|
||||||
|
{
|
||||||
|
Logger.Log("token unapproved, rejecting login", LoggerLevelLogin.Instance);
|
||||||
|
return this.StatusCode(403, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Log($"Successfully logged in user {user.Username} as {token.GameVersion} client ({titleId})", LoggerLevelLogin.Instance);
|
||||||
|
// After this point we are now considering this session as logged in.
|
||||||
|
|
||||||
|
// We just logged in with the token. Mark it as used so someone else doesnt try to use it,
|
||||||
|
// and so we don't pick the same token up when logging in later.
|
||||||
|
token.Used = true;
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Create a new room on LBP2/3/Vita
|
||||||
|
if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user, token.GameVersion);
|
||||||
|
|
||||||
|
return this.Ok
|
||||||
|
(
|
||||||
|
new LoginResult
|
||||||
|
{
|
||||||
|
AuthTicket = "MM_AUTH=" + token.UserToken,
|
||||||
|
LbpEnvVer = ServerStatics.ServerName,
|
||||||
|
}.Serialize()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,96 +14,115 @@ using LBPUnion.ProjectLighthouse.Types.Match;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
{
|
|
||||||
[ApiController]
|
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
|
||||||
[Produces("text/xml")]
|
|
||||||
public class MatchController : ControllerBase
|
|
||||||
{
|
|
||||||
private readonly Database database;
|
|
||||||
|
|
||||||
public MatchController(Database database)
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class MatchController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly Database database;
|
||||||
|
|
||||||
|
public MatchController(Database database)
|
||||||
|
{
|
||||||
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("match")]
|
||||||
|
[Produces("text/plain")]
|
||||||
|
public async Task<IActionResult> Match()
|
||||||
|
{
|
||||||
|
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||||
|
|
||||||
|
if (userAndToken == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
// ReSharper disable once PossibleInvalidOperationException
|
||||||
|
User user = userAndToken.Value.Item1;
|
||||||
|
GameToken gameToken = userAndToken.Value.Item2;
|
||||||
|
|
||||||
|
#region Parse match data
|
||||||
|
|
||||||
|
// Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]]
|
||||||
|
|
||||||
|
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
|
if (bodyString.Length == 0 || bodyString[0] != '[') return this.BadRequest();
|
||||||
|
|
||||||
|
Logger.Log("Received match data: " + bodyString, LoggerLevelMatch.Instance);
|
||||||
|
|
||||||
|
IMatchData? matchData;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
this.database = database;
|
matchData = MatchHelper.Deserialize(bodyString);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
Logger.Log("Exception while parsing matchData: ", LoggerLevelMatch.Instance);
|
||||||
|
string[] lines = e.ToDetailedException().Split("\n");
|
||||||
|
foreach (string line in lines) Logger.Log(line, LoggerLevelMatch.Instance);
|
||||||
|
|
||||||
|
return this.BadRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("match")]
|
if (matchData == null)
|
||||||
[Produces("text/plain")]
|
|
||||||
public async Task<IActionResult> Match()
|
|
||||||
{
|
{
|
||||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
Logger.Log("Could not parse match data: matchData is null", LoggerLevelMatch.Instance);
|
||||||
|
return this.BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
if (userAndToken == null) return this.StatusCode(403, "");
|
Logger.Log($"Parsed match from {user.Username} (type: {matchData.GetType()})", LoggerLevelMatch.Instance);
|
||||||
|
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
#endregion
|
||||||
User user = userAndToken.Value.Item1;
|
|
||||||
GameToken gameToken = userAndToken.Value.Item2;
|
|
||||||
|
|
||||||
#region Parse match data
|
await LastContactHelper.SetLastContact(user, gameToken.GameVersion);
|
||||||
|
|
||||||
// Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]]
|
#region Process match data
|
||||||
|
|
||||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
if (matchData is UpdateMyPlayerData playerData)
|
||||||
|
{
|
||||||
|
MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation);
|
||||||
|
Room? room = RoomHelper.FindRoomByUser(user, gameToken.GameVersion, true);
|
||||||
|
|
||||||
if (bodyString.Length == 0 || bodyString[0] != '[') return this.BadRequest();
|
if (playerData.RoomState != null)
|
||||||
|
if (room != null && Equals(room.Host, user))
|
||||||
|
room.State = (RoomState)playerData.RoomState;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.Log("Received match data: " + bodyString, LoggerLevelMatch.Instance);
|
if (matchData is FindBestRoom && MatchHelper.UserLocations.Count > 1)
|
||||||
|
{
|
||||||
|
FindBestRoomResponse? response = RoomHelper.FindBestRoom(user, gameToken.GameVersion, gameToken.UserLocation);
|
||||||
|
|
||||||
IMatchData? matchData;
|
if (response == null) return this.NotFound();
|
||||||
try
|
|
||||||
|
string serialized = JsonSerializer.Serialize(response, typeof(FindBestRoomResponse));
|
||||||
|
foreach (Player player in response.Players) MatchHelper.AddUserRecentlyDivedIn(user.UserId, player.User.UserId);
|
||||||
|
|
||||||
|
return this.Ok($"[{{\"StatusCode\":200}},{serialized}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1)
|
||||||
|
{
|
||||||
|
List<User> users = new();
|
||||||
|
foreach (string playerUsername in createRoom.Players)
|
||||||
{
|
{
|
||||||
matchData = MatchHelper.Deserialize(bodyString);
|
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
||||||
}
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||||
catch(Exception e)
|
if (player != null) users.Add(player);
|
||||||
{
|
else return this.BadRequest();
|
||||||
Logger.Log("Exception while parsing matchData: ", LoggerLevelMatch.Instance);
|
|
||||||
string[] lines = e.ToDetailedException().Split("\n");
|
|
||||||
foreach (string line in lines) Logger.Log(line, LoggerLevelMatch.Instance);
|
|
||||||
|
|
||||||
return this.BadRequest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchData == null)
|
// Create a new one as requested
|
||||||
{
|
RoomHelper.CreateRoom(users, gameToken.GameVersion, createRoom.RoomSlot);
|
||||||
Logger.Log("Could not parse match data: matchData is null", LoggerLevelMatch.Instance);
|
}
|
||||||
return this.BadRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Log($"Parsed match from {user.Username} (type: {matchData.GetType()})", LoggerLevelMatch.Instance);
|
if (matchData is UpdatePlayersInRoom updatePlayersInRoom)
|
||||||
|
{
|
||||||
|
Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.Host == user);
|
||||||
|
|
||||||
#endregion
|
if (room != null)
|
||||||
|
|
||||||
await LastContactHelper.SetLastContact(user, gameToken.GameVersion);
|
|
||||||
|
|
||||||
#region Process match data
|
|
||||||
|
|
||||||
if (matchData is UpdateMyPlayerData playerData)
|
|
||||||
{
|
|
||||||
MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation);
|
|
||||||
Room? room = RoomHelper.FindRoomByUser(user, gameToken.GameVersion, true);
|
|
||||||
|
|
||||||
if (playerData.RoomState != null)
|
|
||||||
if (room != null && Equals(room.Host, user))
|
|
||||||
room.State = (RoomState)playerData.RoomState;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchData is FindBestRoom && MatchHelper.UserLocations.Count > 1)
|
|
||||||
{
|
|
||||||
FindBestRoomResponse? response = RoomHelper.FindBestRoom(user, gameToken.GameVersion, gameToken.UserLocation);
|
|
||||||
|
|
||||||
if (response == null) return this.NotFound();
|
|
||||||
|
|
||||||
string serialized = JsonSerializer.Serialize(response, typeof(FindBestRoomResponse));
|
|
||||||
foreach (Player player in response.Players) MatchHelper.AddUserRecentlyDivedIn(user.UserId, player.User.UserId);
|
|
||||||
|
|
||||||
return this.Ok($"[{{\"StatusCode\":200}},{serialized}]");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchData is CreateRoom createRoom && MatchHelper.UserLocations.Count >= 1)
|
|
||||||
{
|
{
|
||||||
List<User> users = new();
|
List<User> users = new();
|
||||||
foreach (string playerUsername in createRoom.Players)
|
foreach (string playerUsername in updatePlayersInRoom.Players)
|
||||||
{
|
{
|
||||||
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
||||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||||
|
@ -111,33 +130,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
||||||
else return this.BadRequest();
|
else return this.BadRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new one as requested
|
room.Players = users;
|
||||||
RoomHelper.CreateRoom(users, gameToken.GameVersion, createRoom.RoomSlot);
|
RoomHelper.CleanupRooms(null, room);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchData is UpdatePlayersInRoom updatePlayersInRoom)
|
|
||||||
{
|
|
||||||
Room? room = RoomHelper.Rooms.FirstOrDefault(r => r.Host == user);
|
|
||||||
|
|
||||||
if (room != null)
|
|
||||||
{
|
|
||||||
List<User> users = new();
|
|
||||||
foreach (string playerUsername in updatePlayersInRoom.Players)
|
|
||||||
{
|
|
||||||
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
|
|
||||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
|
||||||
if (player != null) users.Add(player);
|
|
||||||
else return this.BadRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
room.Players = users;
|
|
||||||
RoomHelper.CleanupRooms(null, room);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
return this.Ok("[{\"StatusCode\":200}]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
return this.Ok("[{\"StatusCode\":200}]");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,36 +8,36 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/plain")]
|
||||||
|
public class MessageController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
|
||||||
[Produces("text/plain")]
|
public MessageController(Database database)
|
||||||
public class MessageController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public MessageController(Database database)
|
[HttpGet("eula")]
|
||||||
{
|
public async Task<IActionResult> Eula()
|
||||||
this.database = database;
|
{
|
||||||
}
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
[HttpGet("eula")]
|
return this.Ok($"{EulaHelper.License}\n{ServerSettings.Instance.EulaText}");
|
||||||
public async Task<IActionResult> Eula()
|
}
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
return this.Ok($"{EulaHelper.License}\n{ServerSettings.Instance.EulaText}");
|
[HttpGet("announce")]
|
||||||
}
|
public async Task<IActionResult> Announce()
|
||||||
|
{
|
||||||
[HttpGet("announce")]
|
#if !DEBUG
|
||||||
public async Task<IActionResult> Announce()
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
{
|
if (user == null) return this.StatusCode(403, "");
|
||||||
#if !DEBUG
|
#else
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
#else
|
|
||||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||||
|
|
||||||
if (userAndToken == null) return this.StatusCode(403, "");
|
if (userAndToken == null) return this.StatusCode(403, "");
|
||||||
|
@ -45,17 +45,17 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
// ReSharper disable once PossibleInvalidOperationException
|
||||||
User user = userAndToken.Value.Item1;
|
User user = userAndToken.Value.Item1;
|
||||||
GameToken gameToken = userAndToken.Value.Item2;
|
GameToken gameToken = userAndToken.Value.Item2;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
string announceText = ServerSettings.Instance.AnnounceText;
|
string announceText = ServerSettings.Instance.AnnounceText;
|
||||||
|
|
||||||
announceText = announceText.Replace("%user", user.Username);
|
announceText = announceText.Replace("%user", user.Username);
|
||||||
announceText = announceText.Replace("%id", user.UserId.ToString());
|
announceText = announceText.Replace("%id", user.UserId.ToString());
|
||||||
|
|
||||||
return this.Ok
|
return this.Ok
|
||||||
(
|
(
|
||||||
announceText +
|
announceText +
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
"\n\n---DEBUG INFO---\n" +
|
"\n\n---DEBUG INFO---\n" +
|
||||||
$"user.UserId: {user.UserId}\n" +
|
$"user.UserId: {user.UserId}\n" +
|
||||||
$"token.Approved: {gameToken.Approved}\n" +
|
$"token.Approved: {gameToken.Approved}\n" +
|
||||||
|
@ -63,27 +63,26 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
||||||
$"token.UserLocation: {gameToken.UserLocation}\n" +
|
$"token.UserLocation: {gameToken.UserLocation}\n" +
|
||||||
$"token.GameVersion: {gameToken.GameVersion}\n" +
|
$"token.GameVersion: {gameToken.GameVersion}\n" +
|
||||||
"---DEBUG INFO---" +
|
"---DEBUG INFO---" +
|
||||||
#endif
|
#endif
|
||||||
"\n"
|
"\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("notification")]
|
[HttpGet("notification")]
|
||||||
public IActionResult Notification() => this.Ok();
|
public IActionResult Notification() => this.Ok();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Filters chat messages sent by a user.
|
/// Filters chat messages sent by a user.
|
||||||
/// The reponse sent is the text that will appear in-game.
|
/// The reponse sent is the text that will appear in-game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost("filter")]
|
[HttpPost("filter")]
|
||||||
public async Task<IActionResult> Filter()
|
public async Task<IActionResult> Filter()
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (user == null) return this.StatusCode(403, "");
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
string loggedText = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
string loggedText = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
Logger.Log($"{user.Username}: {loggedText}", LoggerLevelFilter.Instance);
|
Logger.Log($"{user.Username}: {loggedText}", LoggerLevelFilter.Instance);
|
||||||
return this.Ok(loggedText);
|
return this.Ok(loggedText);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,36 +2,35 @@ using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
using LBPUnion.ProjectLighthouse.Types.News;
|
using LBPUnion.ProjectLighthouse.Types.News;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
{
|
|
||||||
[ApiController]
|
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/news")]
|
|
||||||
[Produces("text/xml")]
|
|
||||||
public class NewsController : ControllerBase
|
|
||||||
{
|
|
||||||
[HttpGet]
|
|
||||||
public IActionResult Get()
|
|
||||||
{
|
|
||||||
string newsEntry = LbpSerializer.StringElement
|
|
||||||
(
|
|
||||||
"item",
|
|
||||||
new NewsEntry
|
|
||||||
{
|
|
||||||
Category = "no_category",
|
|
||||||
Summary = "test summary",
|
|
||||||
Image = new NewsImage
|
|
||||||
{
|
|
||||||
Hash = "4947269c5f7061b27225611ee58a9a91a8031bbe",
|
|
||||||
Alignment = "right",
|
|
||||||
},
|
|
||||||
Id = 1,
|
|
||||||
Title = "Test Title",
|
|
||||||
Text = "Test Text",
|
|
||||||
Date = 1348755214000,
|
|
||||||
}.Serialize()
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.StringElement("news", newsEntry));
|
[ApiController]
|
||||||
}
|
[Route("LITTLEBIGPLANETPS3_XML/news")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class NewsController : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Get()
|
||||||
|
{
|
||||||
|
string newsEntry = LbpSerializer.StringElement
|
||||||
|
(
|
||||||
|
"item",
|
||||||
|
new NewsEntry
|
||||||
|
{
|
||||||
|
Category = "no_category",
|
||||||
|
Summary = "test summary",
|
||||||
|
Image = new NewsImage
|
||||||
|
{
|
||||||
|
Hash = "4947269c5f7061b27225611ee58a9a91a8031bbe",
|
||||||
|
Alignment = "right",
|
||||||
|
},
|
||||||
|
Id = 1,
|
||||||
|
Title = "Test Title",
|
||||||
|
Text = "Test Text",
|
||||||
|
Date = 1348755214000,
|
||||||
|
}.Serialize()
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.StringElement("news", newsEntry));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,148 +13,142 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class PhotosController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
|
||||||
[Produces("text/xml")]
|
public PhotosController(Database database)
|
||||||
public class PhotosController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public PhotosController(Database database)
|
[HttpPost("uploadPhoto")]
|
||||||
|
public async Task<IActionResult> UploadPhoto()
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
if (user.PhotosByMe >= ServerSettings.Instance.PhotosQuota) return this.BadRequest();
|
||||||
|
|
||||||
|
this.Request.Body.Position = 0;
|
||||||
|
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
|
XmlSerializer serializer = new(typeof(Photo));
|
||||||
|
Photo? photo = (Photo?)serializer.Deserialize(new StringReader(bodyString));
|
||||||
|
if (photo == null) return this.BadRequest();
|
||||||
|
|
||||||
|
foreach (Photo p in this.database.Photos.Where(p => p.CreatorId == user.UserId))
|
||||||
{
|
{
|
||||||
this.database = database;
|
if (p.LargeHash == photo.LargeHash) return this.Ok(); // photo already uplaoded
|
||||||
|
if (p.MediumHash == photo.MediumHash) return this.Ok();
|
||||||
|
if (p.SmallHash == photo.SmallHash) return this.Ok();
|
||||||
|
if (p.PlanHash == photo.PlanHash) return this.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("uploadPhoto")]
|
photo.CreatorId = user.UserId;
|
||||||
public async Task<IActionResult> UploadPhoto()
|
photo.Creator = user;
|
||||||
|
|
||||||
|
if (photo.Subjects.Count > 4) return this.BadRequest();
|
||||||
|
|
||||||
|
foreach (PhotoSubject subject in photo.Subjects)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username);
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
if (user.PhotosByMe >= ServerSettings.Instance.PhotosQuota) return this.BadRequest();
|
if (subject.User == null) continue;
|
||||||
|
|
||||||
this.Request.Body.Position = 0;
|
subject.UserId = subject.User.UserId;
|
||||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
Logger.Log($"Adding PhotoSubject (userid {subject.UserId}) to db", LoggerLevelPhotos.Instance);
|
||||||
|
|
||||||
XmlSerializer serializer = new(typeof(Photo));
|
this.database.PhotoSubjects.Add(subject);
|
||||||
Photo? photo = (Photo?)serializer.Deserialize(new StringReader(bodyString));
|
|
||||||
if (photo == null) return this.BadRequest();
|
|
||||||
|
|
||||||
foreach (Photo p in this.database.Photos.Where(p => p.CreatorId == user.UserId))
|
|
||||||
{
|
|
||||||
if (p.LargeHash == photo.LargeHash) return this.Ok(); // photo already uplaoded
|
|
||||||
if (p.MediumHash == photo.MediumHash) return this.Ok();
|
|
||||||
if (p.SmallHash == photo.SmallHash) return this.Ok();
|
|
||||||
if (p.PlanHash == photo.PlanHash) return this.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
photo.CreatorId = user.UserId;
|
|
||||||
photo.Creator = user;
|
|
||||||
|
|
||||||
if (photo.Subjects.Count > 4)
|
|
||||||
{
|
|
||||||
return this.BadRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (PhotoSubject subject in photo.Subjects)
|
|
||||||
{
|
|
||||||
subject.User = await this.database.Users.FirstOrDefaultAsync(u => u.Username == subject.Username);
|
|
||||||
|
|
||||||
if (subject.User == null) continue;
|
|
||||||
|
|
||||||
subject.UserId = subject.User.UserId;
|
|
||||||
Logger.Log($"Adding PhotoSubject (userid {subject.UserId}) to db", LoggerLevelPhotos.Instance);
|
|
||||||
|
|
||||||
this.database.PhotoSubjects.Add(subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
// Check for duplicate photo subjects
|
|
||||||
List<int> subjectUserIds = new(4);
|
|
||||||
foreach (PhotoSubject subject in photo.Subjects)
|
|
||||||
{
|
|
||||||
if (subjectUserIds.Contains(subject.UserId))
|
|
||||||
{
|
|
||||||
return this.BadRequest();
|
|
||||||
}
|
|
||||||
subjectUserIds.Add(subject.UserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
photo.PhotoSubjectIds = photo.Subjects.Select(subject => subject.PhotoSubjectId.ToString()).ToArray();
|
|
||||||
|
|
||||||
// photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId);
|
|
||||||
|
|
||||||
Logger.Log($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LoggerLevelPhotos.Instance);
|
|
||||||
|
|
||||||
this.database.Photos.Add(photo);
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("photos/user/{id:int}")]
|
await this.database.SaveChangesAsync();
|
||||||
public async Task<IActionResult> SlotPhotos(int id)
|
|
||||||
|
// Check for duplicate photo subjects
|
||||||
|
List<int> subjectUserIds = new(4);
|
||||||
|
foreach (PhotoSubject subject in photo.Subjects)
|
||||||
{
|
{
|
||||||
List<Photo> photos = await this.database.Photos.Include(p => p.Creator).Take(10).ToListAsync();
|
if (subjectUserIds.Contains(subject.UserId)) return this.BadRequest();
|
||||||
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id));
|
|
||||||
return this.Ok(LbpSerializer.StringElement("photos", response));
|
subjectUserIds.Add(subject.UserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("photos/by")]
|
photo.PhotoSubjectIds = photo.Subjects.Select(subject => subject.PhotoSubjectId.ToString()).ToArray();
|
||||||
public async Task<IActionResult> UserPhotosBy([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user);
|
|
||||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
|
||||||
if (userFromQuery == null) return this.NotFound();
|
|
||||||
|
|
||||||
List<Photo> photos = await this.database.Photos.Include
|
// photo.Slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == photo.SlotId);
|
||||||
(p => p.Creator)
|
|
||||||
.Where(p => p.CreatorId == userFromQuery.UserId)
|
|
||||||
.OrderByDescending(s => s.Timestamp)
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, 30))
|
|
||||||
.ToListAsync();
|
|
||||||
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0));
|
|
||||||
return this.Ok(LbpSerializer.StringElement("photos", response));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("photos/with")]
|
Logger.Log($"Adding PhotoSubjectCollection ({photo.PhotoSubjectCollection}) to photo", LoggerLevelPhotos.Instance);
|
||||||
public async Task<IActionResult> UserPhotosWith([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user);
|
|
||||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
|
||||||
if (userFromQuery == null) return this.NotFound();
|
|
||||||
|
|
||||||
List<Photo> photos = new();
|
this.database.Photos.Add(photo);
|
||||||
foreach (Photo photo in this.database.Photos.Include
|
|
||||||
(p => p.Creator)) photos.AddRange(photo.Subjects.Where(subject => subject.User.UserId == userFromQuery.UserId).Select(_ => photo));
|
|
||||||
|
|
||||||
string response = photos.OrderByDescending
|
await this.database.SaveChangesAsync();
|
||||||
(s => s.Timestamp)
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, 30))
|
|
||||||
.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0));
|
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.StringElement("photos", response));
|
return this.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("deletePhoto/{id:int}")]
|
[HttpGet("photos/user/{id:int}")]
|
||||||
public async Task<IActionResult> DeletePhoto(int id)
|
public async Task<IActionResult> SlotPhotos(int id)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
List<Photo> photos = await this.database.Photos.Include(p => p.Creator).Take(10).ToListAsync();
|
||||||
if (user == null) return this.StatusCode(403, "");
|
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(id));
|
||||||
|
return this.Ok(LbpSerializer.StringElement("photos", response));
|
||||||
|
}
|
||||||
|
|
||||||
Photo? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id);
|
[HttpGet("photos/by")]
|
||||||
if (photo == null) return this.NotFound();
|
public async Task<IActionResult> UserPhotosBy([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||||
if (photo.CreatorId != user.UserId) return this.StatusCode(401, "");
|
{
|
||||||
|
User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user);
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||||
|
if (userFromQuery == null) return this.NotFound();
|
||||||
|
|
||||||
this.database.Photos.Remove(photo);
|
List<Photo> photos = await this.database.Photos.Include
|
||||||
await this.database.SaveChangesAsync();
|
(p => p.Creator)
|
||||||
return this.Ok();
|
.Where(p => p.CreatorId == userFromQuery.UserId)
|
||||||
}
|
.OrderByDescending(s => s.Timestamp)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30))
|
||||||
|
.ToListAsync();
|
||||||
|
string response = photos.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0));
|
||||||
|
return this.Ok(LbpSerializer.StringElement("photos", response));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("photos/with")]
|
||||||
|
public async Task<IActionResult> UserPhotosWith([FromQuery] string user, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||||
|
{
|
||||||
|
User? userFromQuery = await this.database.Users.FirstOrDefaultAsync(u => u.Username == user);
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||||
|
if (userFromQuery == null) return this.NotFound();
|
||||||
|
|
||||||
|
List<Photo> photos = new();
|
||||||
|
foreach (Photo photo in this.database.Photos.Include
|
||||||
|
(p => p.Creator)) photos.AddRange(photo.Subjects.Where(subject => subject.User.UserId == userFromQuery.UserId).Select(_ => photo));
|
||||||
|
|
||||||
|
string response = photos.OrderByDescending
|
||||||
|
(s => s.Timestamp)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30))
|
||||||
|
.Aggregate(string.Empty, (s, photo) => s + photo.Serialize(0));
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.StringElement("photos", response));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("deletePhoto/{id:int}")]
|
||||||
|
public async Task<IActionResult> DeletePhoto(int id)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Photo? photo = await this.database.Photos.FirstOrDefaultAsync(p => p.PhotoId == id);
|
||||||
|
if (photo == null) return this.NotFound();
|
||||||
|
if (photo.CreatorId != user.UserId) return this.StatusCode(401, "");
|
||||||
|
|
||||||
|
this.database.Photos.Remove(photo);
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
return this.Ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,138 +13,113 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class PublishController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
|
||||||
[Produces("text/xml")]
|
public PublishController(Database database)
|
||||||
public class PublishController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public PublishController(Database database)
|
/// <summary>
|
||||||
|
/// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("startPublish")]
|
||||||
|
public async Task<IActionResult> StartPublish()
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest();
|
||||||
|
|
||||||
|
Slot? slot = await this.GetSlotFromBody();
|
||||||
|
if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(slot.RootLevel)) return this.BadRequest();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(slot.ResourceCollection)) slot.ResourceCollection = slot.RootLevel;
|
||||||
|
|
||||||
|
// Republish logic
|
||||||
|
if (slot.SlotId != 0)
|
||||||
{
|
{
|
||||||
this.database = database;
|
Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
|
||||||
|
if (oldSlot == null) return this.NotFound();
|
||||||
|
if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
slot.ResourceCollection += "," + slot.IconHash; // tells LBP to upload icon after we process resources here
|
||||||
/// Endpoint the game uses to check what resources need to be uploaded and if the level can be uploaded
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("startPublish")]
|
|
||||||
public async Task<IActionResult> StartPublish()
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest();
|
string resources = slot.Resources.Where
|
||||||
|
(hash => !FileHelper.ResourceExists(hash))
|
||||||
|
.Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash));
|
||||||
|
|
||||||
Slot? slot = await this.GetSlotFromBody();
|
return this.Ok(LbpSerializer.TaggedStringElement("slot", resources, "type", "user"));
|
||||||
if (slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(slot.RootLevel)) return this.BadRequest();
|
/// <summary>
|
||||||
|
/// Endpoint actually used to publish a level
|
||||||
if (string.IsNullOrEmpty(slot.ResourceCollection)) slot.ResourceCollection = slot.RootLevel;
|
/// </summary>
|
||||||
|
[HttpPost("publish")]
|
||||||
// Republish logic
|
public async Task<IActionResult> Publish()
|
||||||
if (slot.SlotId != 0)
|
{
|
||||||
{
|
|
||||||
Slot? oldSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
|
|
||||||
if (oldSlot == null) return this.NotFound();
|
|
||||||
if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
slot.ResourceCollection += "," + slot.IconHash; // tells LBP to upload icon after we process resources here
|
|
||||||
|
|
||||||
string resources = slot.Resources.Where
|
|
||||||
(hash => !FileHelper.ResourceExists(hash))
|
|
||||||
.Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash));
|
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.TaggedStringElement("slot", resources, "type", "user"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Endpoint actually used to publish a level
|
|
||||||
/// </summary>
|
|
||||||
[HttpPost("publish")]
|
|
||||||
public async Task<IActionResult> Publish()
|
|
||||||
{
|
|
||||||
// User user = await this.database.UserFromGameRequest(this.Request);
|
// User user = await this.database.UserFromGameRequest(this.Request);
|
||||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||||
|
|
||||||
if (userAndToken == null) return this.StatusCode(403, "");
|
if (userAndToken == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
// ReSharper disable once PossibleInvalidOperationException
|
||||||
User user = userAndToken.Value.Item1;
|
User user = userAndToken.Value.Item1;
|
||||||
GameToken gameToken = userAndToken.Value.Item2;
|
GameToken gameToken = userAndToken.Value.Item2;
|
||||||
|
|
||||||
if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest();
|
if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest();
|
||||||
|
|
||||||
Slot? slot = await this.GetSlotFromBody();
|
Slot? slot = await this.GetSlotFromBody();
|
||||||
if (slot?.Location == null) return this.BadRequest();
|
if (slot?.Location == null) return this.BadRequest();
|
||||||
|
|
||||||
// Republish logic
|
// Republish logic
|
||||||
if (slot.SlotId != 0)
|
if (slot.SlotId != 0)
|
||||||
{
|
{
|
||||||
Slot? oldSlot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
|
Slot? oldSlot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slot.SlotId);
|
||||||
if (oldSlot == null) return this.NotFound();
|
if (oldSlot == null) return this.NotFound();
|
||||||
|
|
||||||
if (oldSlot.Location == null) throw new ArgumentNullException();
|
if (oldSlot.Location == null) throw new ArgumentNullException();
|
||||||
|
|
||||||
if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
|
if (oldSlot.CreatorId != user.UserId) return this.BadRequest();
|
||||||
|
|
||||||
oldSlot.Location.X = slot.Location.X;
|
oldSlot.Location.X = slot.Location.X;
|
||||||
oldSlot.Location.Y = slot.Location.Y;
|
oldSlot.Location.Y = slot.Location.Y;
|
||||||
|
|
||||||
slot.CreatorId = oldSlot.CreatorId;
|
slot.CreatorId = oldSlot.CreatorId;
|
||||||
slot.LocationId = oldSlot.LocationId;
|
slot.LocationId = oldSlot.LocationId;
|
||||||
slot.SlotId = oldSlot.SlotId;
|
slot.SlotId = oldSlot.SlotId;
|
||||||
|
|
||||||
slot.PlaysLBP1 = oldSlot.PlaysLBP1;
|
slot.PlaysLBP1 = oldSlot.PlaysLBP1;
|
||||||
slot.PlaysLBP1Complete = oldSlot.PlaysLBP1Complete;
|
slot.PlaysLBP1Complete = oldSlot.PlaysLBP1Complete;
|
||||||
slot.PlaysLBP1Unique = oldSlot.PlaysLBP1Unique;
|
slot.PlaysLBP1Unique = oldSlot.PlaysLBP1Unique;
|
||||||
|
|
||||||
slot.PlaysLBP2 = oldSlot.PlaysLBP2;
|
slot.PlaysLBP2 = oldSlot.PlaysLBP2;
|
||||||
slot.PlaysLBP2Complete = oldSlot.PlaysLBP2Complete;
|
slot.PlaysLBP2Complete = oldSlot.PlaysLBP2Complete;
|
||||||
slot.PlaysLBP2Unique = oldSlot.PlaysLBP2Unique;
|
slot.PlaysLBP2Unique = oldSlot.PlaysLBP2Unique;
|
||||||
|
|
||||||
slot.PlaysLBP3 = oldSlot.PlaysLBP3;
|
slot.PlaysLBP3 = oldSlot.PlaysLBP3;
|
||||||
slot.PlaysLBP3Complete = oldSlot.PlaysLBP3Complete;
|
slot.PlaysLBP3Complete = oldSlot.PlaysLBP3Complete;
|
||||||
slot.PlaysLBP3Unique = oldSlot.PlaysLBP3Unique;
|
slot.PlaysLBP3Unique = oldSlot.PlaysLBP3Unique;
|
||||||
|
|
||||||
slot.PlaysLBPVita = oldSlot.PlaysLBPVita;
|
slot.PlaysLBPVita = oldSlot.PlaysLBPVita;
|
||||||
slot.PlaysLBPVitaComplete = oldSlot.PlaysLBPVitaComplete;
|
slot.PlaysLBPVitaComplete = oldSlot.PlaysLBPVitaComplete;
|
||||||
slot.PlaysLBPVitaUnique = oldSlot.PlaysLBPVitaUnique;
|
slot.PlaysLBPVitaUnique = oldSlot.PlaysLBPVitaUnique;
|
||||||
|
|
||||||
slot.FirstUploaded = oldSlot.FirstUploaded;
|
slot.FirstUploaded = oldSlot.FirstUploaded;
|
||||||
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
|
|
||||||
|
|
||||||
slot.TeamPick = oldSlot.TeamPick;
|
|
||||||
|
|
||||||
slot.GameVersion = gameToken.GameVersion;
|
|
||||||
|
|
||||||
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
|
|
||||||
{
|
|
||||||
slot.MinimumPlayers = 1;
|
|
||||||
slot.MaximumPlayers = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.database.Entry(oldSlot).CurrentValues.SetValues(slot);
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
return this.Ok(oldSlot.Serialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: parse location in body
|
|
||||||
Location l = new()
|
|
||||||
{
|
|
||||||
X = slot.Location.X,
|
|
||||||
Y = slot.Location.Y,
|
|
||||||
};
|
|
||||||
this.database.Locations.Add(l);
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
slot.LocationId = l.Id;
|
|
||||||
slot.CreatorId = user.UserId;
|
|
||||||
slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds();
|
|
||||||
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
|
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
|
||||||
|
|
||||||
|
slot.TeamPick = oldSlot.TeamPick;
|
||||||
|
|
||||||
slot.GameVersion = gameToken.GameVersion;
|
slot.GameVersion = gameToken.GameVersion;
|
||||||
|
|
||||||
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
|
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
|
||||||
|
@ -153,42 +128,66 @@ namespace LBPUnion.ProjectLighthouse.Controllers
|
||||||
slot.MaximumPlayers = 4;
|
slot.MaximumPlayers = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.database.Slots.Add(slot);
|
this.database.Entry(oldSlot).CurrentValues.SetValues(slot);
|
||||||
await this.database.SaveChangesAsync();
|
await this.database.SaveChangesAsync();
|
||||||
|
return this.Ok(oldSlot.Serialize());
|
||||||
return this.Ok(slot.Serialize());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("unpublish/{id:int}")]
|
//TODO: parse location in body
|
||||||
public async Task<IActionResult> Unpublish(int id)
|
Location l = new()
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
X = slot.Location.X,
|
||||||
if (user == null) return this.StatusCode(403, "");
|
Y = slot.Location.Y,
|
||||||
|
};
|
||||||
|
this.database.Locations.Add(l);
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
slot.LocationId = l.Id;
|
||||||
|
slot.CreatorId = user.UserId;
|
||||||
|
slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds();
|
||||||
|
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
|
||||||
|
slot.GameVersion = gameToken.GameVersion;
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id);
|
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
if (slot.Location == null) throw new ArgumentNullException();
|
|
||||||
|
|
||||||
if (slot.CreatorId != user.UserId) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
this.database.Locations.Remove(slot.Location);
|
|
||||||
this.database.Slots.Remove(slot);
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Slot?> GetSlotFromBody()
|
|
||||||
{
|
{
|
||||||
this.Request.Body.Position = 0;
|
slot.MinimumPlayers = 1;
|
||||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
slot.MaximumPlayers = 4;
|
||||||
|
|
||||||
XmlSerializer serializer = new(typeof(Slot));
|
|
||||||
Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString));
|
|
||||||
|
|
||||||
return slot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.database.Slots.Add(slot);
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok(slot.Serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("unpublish/{id:int}")]
|
||||||
|
public async Task<IActionResult> Unpublish(int id)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
|
if (slot.Location == null) throw new ArgumentNullException();
|
||||||
|
|
||||||
|
if (slot.CreatorId != user.UserId) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
this.database.Locations.Remove(slot.Location);
|
||||||
|
this.database.Slots.Remove(slot);
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Slot?> GetSlotFromBody()
|
||||||
|
{
|
||||||
|
this.Request.Body.Position = 0;
|
||||||
|
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
|
XmlSerializer serializer = new(typeof(Slot));
|
||||||
|
Slot? slot = (Slot?)serializer.Deserialize(new StringReader(bodyString));
|
||||||
|
|
||||||
|
return slot;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,79 +11,78 @@ using LBPUnion.ProjectLighthouse.Types.Files;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using IOFile = System.IO.File;
|
using IOFile = System.IO.File;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML")]
|
||||||
|
public class ResourcesController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
[HttpPost("showModerated")]
|
||||||
[Produces("text/xml")]
|
public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources"));
|
||||||
[Route("LITTLEBIGPLANETPS3_XML")]
|
|
||||||
public class ResourcesController : ControllerBase
|
[HttpPost("filterResources")]
|
||||||
|
[HttpPost("showNotUploaded")]
|
||||||
|
public async Task<IActionResult> FilterResources()
|
||||||
{
|
{
|
||||||
[HttpPost("showModerated")]
|
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
public IActionResult ShowModerated() => this.Ok(LbpSerializer.BlankElement("resources"));
|
|
||||||
|
|
||||||
[HttpPost("filterResources")]
|
XmlSerializer serializer = new(typeof(ResourceList));
|
||||||
[HttpPost("showNotUploaded")]
|
ResourceList resourceList = (ResourceList)serializer.Deserialize(new StringReader(bodyString));
|
||||||
public async Task<IActionResult> FilterResources()
|
|
||||||
|
if (resourceList == null) return this.BadRequest();
|
||||||
|
|
||||||
|
string resources = resourceList.Resources.Where
|
||||||
|
(s => !FileHelper.ResourceExists(s))
|
||||||
|
.Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash));
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.StringElement("resources", resources));
|
||||||
|
}
|
||||||
|
|
||||||
|
[ResponseCache(Duration = 86400)]
|
||||||
|
[HttpGet("/gameAssets/{hash}")]
|
||||||
|
[HttpGet("r/{hash}")]
|
||||||
|
public IActionResult GetResource(string hash)
|
||||||
|
{
|
||||||
|
string path = FileHelper.GetResourcePath(hash);
|
||||||
|
|
||||||
|
if (FileHelper.ResourceExists(hash)) return this.File(IOFile.OpenRead(path), "application/octet-stream");
|
||||||
|
|
||||||
|
return this.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check if this is a valid hash
|
||||||
|
[HttpPost("upload/{hash}")]
|
||||||
|
public async Task<IActionResult> UploadResource(string hash)
|
||||||
|
{
|
||||||
|
string assetsDirectory = FileHelper.ResourcePath;
|
||||||
|
string path = FileHelper.GetResourcePath(hash);
|
||||||
|
|
||||||
|
FileHelper.EnsureDirectoryCreated(assetsDirectory);
|
||||||
|
if (FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded
|
||||||
|
|
||||||
|
Logger.Log($"Processing resource upload (hash: {hash})", LoggerLevelResources.Instance);
|
||||||
|
LbpFile file = new(await BinaryHelper.ReadFromPipeReader(this.Request.BodyReader));
|
||||||
|
|
||||||
|
if (!FileHelper.IsFileSafe(file))
|
||||||
{
|
{
|
||||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
Logger.Log($"File is unsafe (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
|
||||||
|
return this.UnprocessableEntity();
|
||||||
XmlSerializer serializer = new(typeof(ResourceList));
|
|
||||||
ResourceList resourceList = (ResourceList)serializer.Deserialize(new StringReader(bodyString));
|
|
||||||
|
|
||||||
if (resourceList == null) return this.BadRequest();
|
|
||||||
|
|
||||||
string resources = resourceList.Resources.Where
|
|
||||||
(s => !FileHelper.ResourceExists(s))
|
|
||||||
.Aggregate("", (current, hash) => current + LbpSerializer.StringElement("resource", hash));
|
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.StringElement("resources", resources));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[ResponseCache(Duration = 86400)]
|
string calculatedHash = HashHelper.Sha1Hash(file.Data).ToLower();
|
||||||
[HttpGet("/gameAssets/{hash}")]
|
if (calculatedHash != hash)
|
||||||
[HttpGet("r/{hash}")]
|
|
||||||
public IActionResult GetResource(string hash)
|
|
||||||
{
|
{
|
||||||
string path = FileHelper.GetResourcePath(hash);
|
Logger.Log
|
||||||
|
(
|
||||||
if (FileHelper.ResourceExists(hash)) return this.File(IOFile.OpenRead(path), "application/octet-stream");
|
$"File hash does not match the uploaded file! (hash: {hash}, calculatedHash: {calculatedHash}, type: {file.FileType})",
|
||||||
|
LoggerLevelResources.Instance
|
||||||
return this.NotFound();
|
);
|
||||||
|
return this.Conflict();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check if this is a valid hash
|
Logger.Log($"File is OK! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
|
||||||
[HttpPost("upload/{hash}")]
|
await IOFile.WriteAllBytesAsync(path, file.Data);
|
||||||
public async Task<IActionResult> UploadResource(string hash)
|
return this.Ok();
|
||||||
{
|
|
||||||
string assetsDirectory = FileHelper.ResourcePath;
|
|
||||||
string path = FileHelper.GetResourcePath(hash);
|
|
||||||
|
|
||||||
FileHelper.EnsureDirectoryCreated(assetsDirectory);
|
|
||||||
if (FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded
|
|
||||||
|
|
||||||
Logger.Log($"Processing resource upload (hash: {hash})", LoggerLevelResources.Instance);
|
|
||||||
LbpFile file = new(await BinaryHelper.ReadFromPipeReader(this.Request.BodyReader));
|
|
||||||
|
|
||||||
if (!FileHelper.IsFileSafe(file))
|
|
||||||
{
|
|
||||||
Logger.Log($"File is unsafe (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
|
|
||||||
return this.UnprocessableEntity();
|
|
||||||
}
|
|
||||||
|
|
||||||
string calculatedHash = HashHelper.Sha1Hash(file.Data).ToLower();
|
|
||||||
if (calculatedHash != hash)
|
|
||||||
{
|
|
||||||
Logger.Log
|
|
||||||
(
|
|
||||||
$"File hash does not match the uploaded file! (hash: {hash}, calculatedHash: {calculatedHash}, type: {file.FileType})",
|
|
||||||
LoggerLevelResources.Instance
|
|
||||||
);
|
|
||||||
return this.Conflict();
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Log($"File is OK! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
|
|
||||||
await IOFile.WriteAllBytesAsync(path, file.Data);
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,319 +13,318 @@ using LBPUnion.ProjectLighthouse.Types.Reviews;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/plain")]
|
||||||
|
public class ReviewController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
|
||||||
[Produces("text/plain")]
|
public ReviewController(Database database)
|
||||||
public class ReviewController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public ReviewController(Database database)
|
// LBP1 rating
|
||||||
|
[HttpPost("rate/user/{slotId}")]
|
||||||
|
public async Task<IActionResult> Rate(int slotId, [FromQuery] int rating)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||||
|
if (slot == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
|
||||||
|
if (ratedLevel == null)
|
||||||
{
|
{
|
||||||
this.database = database;
|
ratedLevel = new RatedLevel();
|
||||||
|
ratedLevel.SlotId = slotId;
|
||||||
|
ratedLevel.UserId = user.UserId;
|
||||||
|
ratedLevel.Rating = 0;
|
||||||
|
this.database.RatedLevels.Add(ratedLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LBP1 rating
|
ratedLevel.RatingLBP1 = Math.Max(Math.Min(5, rating), 0);
|
||||||
[HttpPost("rate/user/{slotId}")]
|
|
||||||
public async Task<IActionResult> Rate(int slotId, [FromQuery] int rating)
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// LBP2 and beyond rating
|
||||||
|
[HttpPost("dpadrate/user/{slotId:int}")]
|
||||||
|
public async Task<IActionResult> DPadRate(int slotId, [FromQuery] int rating)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||||
|
if (slot == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
|
||||||
|
if (ratedLevel == null)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
ratedLevel = new RatedLevel();
|
||||||
if (user == null) return this.StatusCode(403, "");
|
ratedLevel.SlotId = slotId;
|
||||||
|
ratedLevel.UserId = user.UserId;
|
||||||
Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId);
|
ratedLevel.RatingLBP1 = 0;
|
||||||
if (slot == null) return this.StatusCode(403, "");
|
this.database.RatedLevels.Add(ratedLevel);
|
||||||
|
|
||||||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
|
|
||||||
if (ratedLevel == null)
|
|
||||||
{
|
|
||||||
ratedLevel = new RatedLevel();
|
|
||||||
ratedLevel.SlotId = slotId;
|
|
||||||
ratedLevel.UserId = user.UserId;
|
|
||||||
ratedLevel.Rating = 0;
|
|
||||||
this.database.RatedLevels.Add(ratedLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
ratedLevel.RatingLBP1 = Math.Max(Math.Min(5, rating), 0);
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LBP2 and beyond rating
|
ratedLevel.Rating = Math.Max(Math.Min(1, rating), -1);
|
||||||
[HttpPost("dpadrate/user/{slotId:int}")]
|
|
||||||
public async Task<IActionResult> DPadRate(int slotId, [FromQuery] int rating)
|
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
|
||||||
|
if (review != null) review.Thumb = ratedLevel.Rating;
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("postReview/user/{slotId:int}")]
|
||||||
|
public async Task<IActionResult> PostReview(int slotId)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
|
||||||
|
Review? newReview = await this.GetReviewFromBody();
|
||||||
|
if (newReview == null) return this.BadRequest();
|
||||||
|
|
||||||
|
if (review == null)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
review = new Review();
|
||||||
if (user == null) return this.StatusCode(403, "");
|
review.SlotId = slotId;
|
||||||
|
review.ReviewerId = user.UserId;
|
||||||
|
review.DeletedBy = DeletedBy.None;
|
||||||
|
review.ThumbsUp = 0;
|
||||||
|
review.ThumbsDown = 0;
|
||||||
|
this.database.Reviews.Add(review);
|
||||||
|
}
|
||||||
|
review.Thumb = newReview.Thumb;
|
||||||
|
review.LabelCollection = newReview.LabelCollection;
|
||||||
|
review.Text = newReview.Text;
|
||||||
|
review.Deleted = false;
|
||||||
|
review.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.Include(s => s.Creator).Include(s => s.Location).FirstOrDefaultAsync(s => s.SlotId == slotId);
|
// sometimes the game posts/updates a review rating without also calling dpadrate/user/etc (why??)
|
||||||
if (slot == null) return this.StatusCode(403, "");
|
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
|
||||||
|
if (ratedLevel == null)
|
||||||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
|
{
|
||||||
if (ratedLevel == null)
|
ratedLevel = new RatedLevel();
|
||||||
{
|
ratedLevel.SlotId = slotId;
|
||||||
ratedLevel = new RatedLevel();
|
ratedLevel.UserId = user.UserId;
|
||||||
ratedLevel.SlotId = slotId;
|
ratedLevel.RatingLBP1 = 0;
|
||||||
ratedLevel.UserId = user.UserId;
|
this.database.RatedLevels.Add(ratedLevel);
|
||||||
ratedLevel.RatingLBP1 = 0;
|
|
||||||
this.database.RatedLevels.Add(ratedLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
ratedLevel.Rating = Math.Max(Math.Min(1, rating), -1);
|
|
||||||
|
|
||||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
|
|
||||||
if (review != null) review.Thumb = ratedLevel.Rating;
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("postReview/user/{slotId:int}")]
|
ratedLevel.Rating = newReview.Thumb;
|
||||||
public async Task<IActionResult> PostReview(int slotId)
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("reviewsFor/user/{slotId:int}")]
|
||||||
|
public async Task<IActionResult> ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
||||||
|
{
|
||||||
|
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||||
|
|
||||||
|
if (userAndToken == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
// ReSharper disable once PossibleInvalidOperationException
|
||||||
|
User user = userAndToken.Value.Item1;
|
||||||
|
GameToken gameToken = userAndToken.Value.Item2;
|
||||||
|
|
||||||
|
GameVersion gameVersion = gameToken.GameVersion;
|
||||||
|
|
||||||
|
Random rand = new();
|
||||||
|
|
||||||
|
Review? yourReview = await this.database.Reviews.FirstOrDefaultAsync
|
||||||
|
(r => r.ReviewerId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion);
|
||||||
|
|
||||||
|
VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync
|
||||||
|
(v => v.UserId == user.UserId && v.SlotId == slotId && v.Slot.GameVersion <= gameVersion);
|
||||||
|
|
||||||
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
||||||
|
if (slot == null) return this.BadRequest();
|
||||||
|
|
||||||
|
bool canNowReviewLevel = slot.CreatorId != user.UserId && visitedLevel != null && yourReview == null;
|
||||||
|
if (canNowReviewLevel)
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync
|
||||||
if (user == null) return this.StatusCode(403, "");
|
(r => r.UserId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion);
|
||||||
|
|
||||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId);
|
yourReview = new Review();
|
||||||
Review? newReview = await this.GetReviewFromBody();
|
yourReview.ReviewerId = user.UserId;
|
||||||
if (newReview == null) return this.BadRequest();
|
yourReview.Reviewer = user;
|
||||||
|
yourReview.Thumb = ratedLevel?.Rating == null ? 0 : ratedLevel.Rating;
|
||||||
if (review == null)
|
yourReview.Slot = slot;
|
||||||
{
|
yourReview.SlotId = slotId;
|
||||||
review = new Review();
|
yourReview.Deleted = false;
|
||||||
review.SlotId = slotId;
|
yourReview.DeletedBy = DeletedBy.None;
|
||||||
review.ReviewerId = user.UserId;
|
yourReview.Text = "You haven't reviewed this level yet. Edit this blank review to upload one!";
|
||||||
review.DeletedBy = DeletedBy.None;
|
yourReview.LabelCollection = "";
|
||||||
review.ThumbsUp = 0;
|
yourReview.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
||||||
review.ThumbsDown = 0;
|
|
||||||
this.database.Reviews.Add(review);
|
|
||||||
}
|
|
||||||
review.Thumb = newReview.Thumb;
|
|
||||||
review.LabelCollection = newReview.LabelCollection;
|
|
||||||
review.Text = newReview.Text;
|
|
||||||
review.Deleted = false;
|
|
||||||
review.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
|
||||||
|
|
||||||
// sometimes the game posts/updates a review rating without also calling dpadrate/user/etc (why??)
|
|
||||||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId);
|
|
||||||
if (ratedLevel == null)
|
|
||||||
{
|
|
||||||
ratedLevel = new RatedLevel();
|
|
||||||
ratedLevel.SlotId = slotId;
|
|
||||||
ratedLevel.UserId = user.UserId;
|
|
||||||
ratedLevel.RatingLBP1 = 0;
|
|
||||||
this.database.RatedLevels.Add(ratedLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
ratedLevel.Rating = newReview.Thumb;
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("reviewsFor/user/{slotId:int}")]
|
IQueryable<Review?> reviews = this.database.Reviews.Where(r => r.SlotId == slotId && r.Slot.GameVersion <= gameVersion)
|
||||||
public async Task<IActionResult> ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
.Include(r => r.Reviewer)
|
||||||
{
|
.Include(r => r.Slot)
|
||||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
.OrderByDescending(r => r.ThumbsUp)
|
||||||
|
.ThenByDescending(_ => EF.Functions.Random())
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(pageSize);
|
||||||
|
|
||||||
if (userAndToken == null) return this.StatusCode(403, "");
|
IEnumerable<Review?> prependedReviews;
|
||||||
|
if (canNowReviewLevel) // this can only be true if you have not posted a review but have visited the level
|
||||||
|
// prepend the fake review to the top of the list to be easily edited
|
||||||
|
prependedReviews = reviews.ToList().Prepend(yourReview);
|
||||||
|
else prependedReviews = reviews.ToList();
|
||||||
|
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
string inner = prependedReviews.Aggregate
|
||||||
User user = userAndToken.Value.Item1;
|
(
|
||||||
GameToken gameToken = userAndToken.Value.Item2;
|
string.Empty,
|
||||||
|
(current, review) =>
|
||||||
GameVersion gameVersion = gameToken.GameVersion;
|
|
||||||
|
|
||||||
Random rand = new();
|
|
||||||
|
|
||||||
Review? yourReview = await this.database.Reviews.FirstOrDefaultAsync
|
|
||||||
(r => r.ReviewerId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion);
|
|
||||||
|
|
||||||
VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync
|
|
||||||
(v => v.UserId == user.UserId && v.SlotId == slotId && v.Slot.GameVersion <= gameVersion);
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId);
|
|
||||||
if (slot == null) return this.BadRequest();
|
|
||||||
|
|
||||||
bool canNowReviewLevel = slot.CreatorId != user.UserId && visitedLevel != null && yourReview == null;
|
|
||||||
if (canNowReviewLevel)
|
|
||||||
{
|
{
|
||||||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync
|
if (review == null) return current;
|
||||||
(r => r.UserId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion);
|
|
||||||
|
|
||||||
yourReview = new Review();
|
return current + review.Serialize();
|
||||||
yourReview.ReviewerId = user.UserId;
|
|
||||||
yourReview.Reviewer = user;
|
|
||||||
yourReview.Thumb = ratedLevel?.Rating == null ? 0 : ratedLevel.Rating;
|
|
||||||
yourReview.Slot = slot;
|
|
||||||
yourReview.SlotId = slotId;
|
|
||||||
yourReview.Deleted = false;
|
|
||||||
yourReview.DeletedBy = DeletedBy.None;
|
|
||||||
yourReview.Text = "You haven't reviewed this level yet. Edit this blank review to upload one!";
|
|
||||||
yourReview.LabelCollection = "";
|
|
||||||
yourReview.Timestamp = TimeHelper.UnixTimeMilliseconds();
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
IQueryable<Review?> reviews = this.database.Reviews.Where(r => r.SlotId == slotId && r.Slot.GameVersion <= gameVersion)
|
string response = LbpSerializer.TaggedStringElement
|
||||||
.Include(r => r.Reviewer)
|
(
|
||||||
.Include(r => r.Slot)
|
"reviews",
|
||||||
.OrderByDescending(r => r.ThumbsUp)
|
inner,
|
||||||
.ThenByDescending(_ => EF.Functions.Random())
|
new Dictionary<string, object>
|
||||||
.Skip(pageStart - 1)
|
{
|
||||||
.Take(pageSize);
|
|
||||||
|
|
||||||
IEnumerable<Review?> prependedReviews;
|
|
||||||
if (canNowReviewLevel) // this can only be true if you have not posted a review but have visited the level
|
|
||||||
// prepend the fake review to the top of the list to be easily edited
|
|
||||||
prependedReviews = reviews.ToList().Prepend(yourReview);
|
|
||||||
else prependedReviews = reviews.ToList();
|
|
||||||
|
|
||||||
string inner = prependedReviews.Aggregate
|
|
||||||
(
|
|
||||||
string.Empty,
|
|
||||||
(current, review) =>
|
|
||||||
{
|
{
|
||||||
if (review == null) return current;
|
"hint_start", pageStart + pageSize
|
||||||
|
},
|
||||||
return current + review.Serialize();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
string response = LbpSerializer.TaggedStringElement
|
|
||||||
(
|
|
||||||
"reviews",
|
|
||||||
inner,
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
{
|
||||||
{
|
"hint", pageStart // not sure
|
||||||
"hint_start", pageStart + pageSize
|
},
|
||||||
},
|
|
||||||
{
|
|
||||||
"hint", pageStart // not sure
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return this.Ok(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("reviewsBy/{username}")]
|
|
||||||
public async Task<IActionResult> ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
|
||||||
{
|
|
||||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
|
||||||
|
|
||||||
if (userAndToken == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
|
||||||
User user = userAndToken.Value.Item1;
|
|
||||||
GameToken gameToken = userAndToken.Value.Item2;
|
|
||||||
|
|
||||||
GameVersion gameVersion = gameToken.GameVersion;
|
|
||||||
|
|
||||||
IEnumerable<Review> reviews = this.database.Reviews.Where(r => r.Reviewer.Username == username && r.Slot.GameVersion <= gameVersion)
|
|
||||||
.Include(r => r.Reviewer)
|
|
||||||
.Include(r => r.Slot)
|
|
||||||
.OrderByDescending(r => r.Timestamp)
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(pageSize);
|
|
||||||
|
|
||||||
string inner = reviews.Aggregate
|
|
||||||
(
|
|
||||||
string.Empty,
|
|
||||||
(current, review) =>
|
|
||||||
{
|
|
||||||
//RatedLevel? ratedLevel = this.database.RatedLevels.FirstOrDefault(r => r.SlotId == review.SlotId && r.UserId == user.UserId);
|
|
||||||
//RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
|
|
||||||
return current + review.Serialize( /*, ratedReview*/);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
string response = LbpSerializer.TaggedStringElement
|
|
||||||
(
|
|
||||||
"reviews",
|
|
||||||
inner,
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
"hint_start", pageStart
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hint", reviews.Last().Timestamp // Seems to be the timestamp of oldest
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.Ok(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("rateReview/user/{slotId:int}/{username}")]
|
|
||||||
public async Task<IActionResult> RateReview(int slotId, string username, [FromQuery] int rating = 0)
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
|
||||||
if (reviewer == null) return this.StatusCode(400, "");
|
|
||||||
|
|
||||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId);
|
|
||||||
if (review == null) return this.StatusCode(400, "");
|
|
||||||
|
|
||||||
RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
|
|
||||||
if (ratedReview == null)
|
|
||||||
{
|
|
||||||
ratedReview = new RatedReview();
|
|
||||||
ratedReview.ReviewId = review.ReviewId;
|
|
||||||
ratedReview.UserId = user.UserId;
|
|
||||||
ratedReview.Thumb = 0;
|
|
||||||
this.database.RatedReviews.Add(ratedReview);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
return this.Ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
int oldThumb = ratedReview.Thumb;
|
[HttpGet("reviewsBy/{username}")]
|
||||||
ratedReview.Thumb = Math.Max(Math.Min(1, rating), -1);
|
public async Task<IActionResult> ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
|
||||||
|
{
|
||||||
|
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||||
|
|
||||||
if (oldThumb != ratedReview.Thumb)
|
if (userAndToken == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
// ReSharper disable once PossibleInvalidOperationException
|
||||||
|
User user = userAndToken.Value.Item1;
|
||||||
|
GameToken gameToken = userAndToken.Value.Item2;
|
||||||
|
|
||||||
|
GameVersion gameVersion = gameToken.GameVersion;
|
||||||
|
|
||||||
|
IEnumerable<Review> reviews = this.database.Reviews.Where(r => r.Reviewer.Username == username && r.Slot.GameVersion <= gameVersion)
|
||||||
|
.Include(r => r.Reviewer)
|
||||||
|
.Include(r => r.Slot)
|
||||||
|
.OrderByDescending(r => r.Timestamp)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(pageSize);
|
||||||
|
|
||||||
|
string inner = reviews.Aggregate
|
||||||
|
(
|
||||||
|
string.Empty,
|
||||||
|
(current, review) =>
|
||||||
{
|
{
|
||||||
if (oldThumb == -1) review.ThumbsDown--;
|
//RatedLevel? ratedLevel = this.database.RatedLevels.FirstOrDefault(r => r.SlotId == review.SlotId && r.UserId == user.UserId);
|
||||||
else if (oldThumb == 1) review.ThumbsUp--;
|
//RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
|
||||||
|
return current + review.Serialize( /*, ratedReview*/);
|
||||||
if (ratedReview.Thumb == -1) review.ThumbsDown++;
|
|
||||||
else if (ratedReview.Thumb == 1) review.ThumbsUp++;
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
string response = LbpSerializer.TaggedStringElement
|
||||||
|
(
|
||||||
|
"reviews",
|
||||||
|
inner,
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"hint_start", pageStart
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hint", reviews.Last().Timestamp // Seems to be the timestamp of oldest
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return this.Ok();
|
return this.Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("deleteReview/user/{slotId:int}/{username}")]
|
[HttpPost("rateReview/user/{slotId:int}/{username}")]
|
||||||
public async Task<IActionResult> DeleteReview(int slotId, string username)
|
public async Task<IActionResult> RateReview(int slotId, string username, [FromQuery] int rating = 0)
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||||
|
if (reviewer == null) return this.StatusCode(400, "");
|
||||||
|
|
||||||
|
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId);
|
||||||
|
if (review == null) return this.StatusCode(400, "");
|
||||||
|
|
||||||
|
RatedReview? ratedReview = await this.database.RatedReviews.FirstOrDefaultAsync(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
|
||||||
|
if (ratedReview == null)
|
||||||
{
|
{
|
||||||
User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
ratedReview = new RatedReview();
|
||||||
if (reviewer == null) return this.StatusCode(403, "");
|
ratedReview.ReviewId = review.ReviewId;
|
||||||
|
ratedReview.UserId = user.UserId;
|
||||||
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId);
|
ratedReview.Thumb = 0;
|
||||||
if (review == null) return this.StatusCode(403, "");
|
this.database.RatedReviews.Add(ratedReview);
|
||||||
|
|
||||||
review.Deleted = true;
|
|
||||||
review.DeletedBy = DeletedBy.LevelAuthor;
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
return this.Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Review?> GetReviewFromBody()
|
int oldThumb = ratedReview.Thumb;
|
||||||
|
ratedReview.Thumb = Math.Max(Math.Min(1, rating), -1);
|
||||||
|
|
||||||
|
if (oldThumb != ratedReview.Thumb)
|
||||||
{
|
{
|
||||||
this.Request.Body.Position = 0;
|
if (oldThumb == -1) review.ThumbsDown--;
|
||||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
else if (oldThumb == 1) review.ThumbsUp--;
|
||||||
|
|
||||||
XmlSerializer serializer = new(typeof(Review));
|
if (ratedReview.Thumb == -1) review.ThumbsDown++;
|
||||||
Review? review = (Review?)serializer.Deserialize(new StringReader(bodyString));
|
else if (ratedReview.Thumb == 1) review.ThumbsUp++;
|
||||||
|
|
||||||
return review;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("deleteReview/user/{slotId:int}/{username}")]
|
||||||
|
public async Task<IActionResult> DeleteReview(int slotId, string username)
|
||||||
|
{
|
||||||
|
User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||||
|
if (reviewer == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == reviewer.UserId);
|
||||||
|
if (review == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
review.Deleted = true;
|
||||||
|
review.DeletedBy = DeletedBy.LevelAuthor;
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Review?> GetReviewFromBody()
|
||||||
|
{
|
||||||
|
this.Request.Body.Position = 0;
|
||||||
|
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
|
XmlSerializer serializer = new(typeof(Review));
|
||||||
|
Review? review = (Review?)serializer.Deserialize(new StringReader(bodyString));
|
||||||
|
|
||||||
|
return review;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,155 +11,151 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class ScoreController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
|
||||||
[Produces("text/xml")]
|
public ScoreController(Database database)
|
||||||
public class ScoreController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public ScoreController(Database database)
|
[HttpPost("scoreboard/user/{id:int}")]
|
||||||
|
public async Task<IActionResult> SubmitScore(int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false)
|
||||||
|
{
|
||||||
|
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
||||||
|
|
||||||
|
if (userAndToken == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
// ReSharper disable once PossibleInvalidOperationException
|
||||||
|
User user = userAndToken.Value.Item1;
|
||||||
|
GameToken gameToken = userAndToken.Value.Item2;
|
||||||
|
|
||||||
|
this.Request.Body.Position = 0;
|
||||||
|
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
|
XmlSerializer serializer = new(typeof(Score));
|
||||||
|
Score? score = (Score?)serializer.Deserialize(new StringReader(bodyString));
|
||||||
|
if (score == null) return this.BadRequest();
|
||||||
|
|
||||||
|
score.SlotId = id;
|
||||||
|
|
||||||
|
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
|
||||||
|
if (slot == null) return this.BadRequest();
|
||||||
|
|
||||||
|
switch (gameToken.GameVersion)
|
||||||
{
|
{
|
||||||
this.database = database;
|
case GameVersion.LittleBigPlanet1:
|
||||||
|
slot.PlaysLBP1Complete++;
|
||||||
|
break;
|
||||||
|
case GameVersion.LittleBigPlanet2:
|
||||||
|
slot.PlaysLBP2Complete++;
|
||||||
|
break;
|
||||||
|
case GameVersion.LittleBigPlanet3:
|
||||||
|
slot.PlaysLBP3Complete++;
|
||||||
|
break;
|
||||||
|
case GameVersion.LittleBigPlanetVita:
|
||||||
|
slot.PlaysLBPVitaComplete++;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("scoreboard/user/{id:int}")]
|
IQueryable<Score> existingScore = this.database.Scores.Where(s => s.SlotId == score.SlotId && s.PlayerIdCollection == score.PlayerIdCollection);
|
||||||
public async Task<IActionResult> SubmitScore(int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false)
|
|
||||||
|
if (existingScore.Any())
|
||||||
{
|
{
|
||||||
(User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
|
Score first = existingScore.First(s => s.SlotId == score.SlotId);
|
||||||
|
score.ScoreId = first.ScoreId;
|
||||||
if (userAndToken == null) return this.StatusCode(403, "");
|
score.Points = Math.Max(first.Points, score.Points);
|
||||||
|
this.database.Entry(first).CurrentValues.SetValues(score);
|
||||||
// ReSharper disable once PossibleInvalidOperationException
|
}
|
||||||
User user = userAndToken.Value.Item1;
|
else
|
||||||
GameToken gameToken = userAndToken.Value.Item2;
|
{
|
||||||
|
this.database.Scores.Add(score);
|
||||||
this.Request.Body.Position = 0;
|
|
||||||
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
|
||||||
|
|
||||||
XmlSerializer serializer = new(typeof(Score));
|
|
||||||
Score? score = (Score?)serializer.Deserialize(new StringReader(bodyString));
|
|
||||||
if (score == null) return this.BadRequest();
|
|
||||||
|
|
||||||
score.SlotId = id;
|
|
||||||
|
|
||||||
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
|
|
||||||
if (slot == null) return this.BadRequest();
|
|
||||||
|
|
||||||
switch (gameToken.GameVersion)
|
|
||||||
{
|
|
||||||
case GameVersion.LittleBigPlanet1:
|
|
||||||
slot.PlaysLBP1Complete++;
|
|
||||||
break;
|
|
||||||
case GameVersion.LittleBigPlanet2:
|
|
||||||
slot.PlaysLBP2Complete++;
|
|
||||||
break;
|
|
||||||
case GameVersion.LittleBigPlanet3:
|
|
||||||
slot.PlaysLBP3Complete++;
|
|
||||||
break;
|
|
||||||
case GameVersion.LittleBigPlanetVita:
|
|
||||||
slot.PlaysLBPVitaComplete++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
IQueryable<Score> existingScore = this.database.Scores.Where(s => s.SlotId == score.SlotId && s.PlayerIdCollection == score.PlayerIdCollection);
|
|
||||||
|
|
||||||
if (existingScore.Any())
|
|
||||||
{
|
|
||||||
Score first = existingScore.First(s => s.SlotId == score.SlotId);
|
|
||||||
score.ScoreId = first.ScoreId;
|
|
||||||
score.Points = Math.Max(first.Points, score.Points);
|
|
||||||
this.database.Entry(first).CurrentValues.SetValues(score);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.database.Scores.Add(score);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
string myRanking = this.GetScores(score.SlotId, score.Type, user);
|
|
||||||
|
|
||||||
return this.Ok(myRanking);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("friendscores/user/{slotId:int}/{type:int}")]
|
await this.database.SaveChangesAsync();
|
||||||
public IActionResult FriendScores(int slotId, int type)
|
|
||||||
//=> await TopScores(slotId, type);
|
|
||||||
=> this.Ok(LbpSerializer.BlankElement("scores"));
|
|
||||||
|
|
||||||
[HttpGet("topscores/user/{slotId:int}/{type:int}")]
|
string myRanking = this.GetScores(score.SlotId, score.Type, user);
|
||||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
|
||||||
public async Task<IActionResult> TopScores(int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
|
||||||
{
|
|
||||||
// Get username
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
return this.Ok(myRanking);
|
||||||
|
}
|
||||||
|
|
||||||
return this.Ok(this.GetScores(slotId, type, user, pageStart, pageSize));
|
[HttpGet("friendscores/user/{slotId:int}/{type:int}")]
|
||||||
}
|
public IActionResult FriendScores(int slotId, int type)
|
||||||
|
//=> await TopScores(slotId, type);
|
||||||
|
=> this.Ok(LbpSerializer.BlankElement("scores"));
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
[HttpGet("topscores/user/{slotId:int}/{type:int}")]
|
||||||
public string GetScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5)
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||||
{
|
public async Task<IActionResult> TopScores(int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
|
||||||
// This is hella ugly but it technically assigns the proper rank to a score
|
{
|
||||||
// var needed for Anonymous type returned from SELECT
|
// Get username
|
||||||
var rankedScores = this.database.Scores.Where(s => s.SlotId == slotId && s.Type == type)
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
.OrderByDescending(s => s.Points)
|
|
||||||
.ToList()
|
|
||||||
.Select
|
|
||||||
(
|
|
||||||
(s, rank) => new
|
|
||||||
{
|
|
||||||
Score = s,
|
|
||||||
Rank = rank + 1,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find your score, since even if you aren't in the top list your score is pinned
|
if (user == null) return this.StatusCode(403, "");
|
||||||
var myScore = rankedScores.Where
|
|
||||||
(rs => rs.Score.PlayerIdCollection.Contains(user.Username))
|
|
||||||
.OrderByDescending(rs => rs.Score.Points)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
// Paginated viewing: if not requesting pageStart, get results around user
|
return this.Ok(this.GetScores(slotId, type, user, pageStart, pageSize));
|
||||||
var pagedScores = rankedScores.Skip(pageStart != -1 || myScore == null ? pageStart - 1 : myScore.Rank - 3).Take(Math.Min(pageSize, 30));
|
}
|
||||||
|
|
||||||
string serializedScores = pagedScores.Aggregate
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||||
|
public string GetScores(int slotId, int type, User user, int pageStart = -1, int pageSize = 5)
|
||||||
|
{
|
||||||
|
// This is hella ugly but it technically assigns the proper rank to a score
|
||||||
|
// var needed for Anonymous type returned from SELECT
|
||||||
|
var rankedScores = this.database.Scores.Where(s => s.SlotId == slotId && s.Type == type)
|
||||||
|
.OrderByDescending(s => s.Points)
|
||||||
|
.ToList()
|
||||||
|
.Select
|
||||||
(
|
(
|
||||||
string.Empty,
|
(s, rank) => new
|
||||||
(current, rs) =>
|
|
||||||
{
|
{
|
||||||
rs.Score.Rank = rs.Rank;
|
Score = s,
|
||||||
return current + rs.Score.Serialize();
|
Rank = rank + 1,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
string res;
|
// Find your score, since even if you aren't in the top list your score is pinned
|
||||||
if (myScore == null) res = LbpSerializer.StringElement("scores", serializedScores);
|
var myScore = rankedScores.Where(rs => rs.Score.PlayerIdCollection.Contains(user.Username)).OrderByDescending(rs => rs.Score.Points).FirstOrDefault();
|
||||||
else
|
|
||||||
res = LbpSerializer.TaggedStringElement
|
|
||||||
(
|
|
||||||
"scores",
|
|
||||||
serializedScores,
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
"yourScore", myScore.Score.Points
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"yourRank", myScore.Rank
|
|
||||||
}, //This is the numerator of your position globally in the side menu.
|
|
||||||
{
|
|
||||||
"totalNumScores", rankedScores.Count()
|
|
||||||
}, // This is the denominator of your position globally in the side menu.
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return res;
|
// Paginated viewing: if not requesting pageStart, get results around user
|
||||||
}
|
var pagedScores = rankedScores.Skip(pageStart != -1 || myScore == null ? pageStart - 1 : myScore.Rank - 3).Take(Math.Min(pageSize, 30));
|
||||||
|
|
||||||
|
string serializedScores = pagedScores.Aggregate
|
||||||
|
(
|
||||||
|
string.Empty,
|
||||||
|
(current, rs) =>
|
||||||
|
{
|
||||||
|
rs.Score.Rank = rs.Rank;
|
||||||
|
return current + rs.Score.Serialize();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
string res;
|
||||||
|
if (myScore == null) res = LbpSerializer.StringElement("scores", serializedScores);
|
||||||
|
else
|
||||||
|
res = LbpSerializer.TaggedStringElement
|
||||||
|
(
|
||||||
|
"scores",
|
||||||
|
serializedScores,
|
||||||
|
new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"yourScore", myScore.Score.Points
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"yourRank", myScore.Rank
|
||||||
|
}, //This is the numerator of your position globally in the side menu.
|
||||||
|
{
|
||||||
|
"totalNumScores", rankedScores.Count()
|
||||||
|
}, // This is the denominator of your position globally in the side menu.
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,48 +7,47 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class SearchController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
public SearchController(Database database)
|
||||||
[Produces("text/xml")]
|
|
||||||
public class SearchController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
public SearchController(Database database)
|
}
|
||||||
{
|
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots/search")]
|
[HttpGet("slots/search")]
|
||||||
public async Task<IActionResult> SearchSlots([FromQuery] string query, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
public async Task<IActionResult> SearchSlots([FromQuery] string query, [FromQuery] int pageSize, [FromQuery] int pageStart)
|
||||||
{
|
{
|
||||||
if (query == null) return this.BadRequest();
|
if (query == null) return this.BadRequest();
|
||||||
|
|
||||||
query = query.ToLower();
|
query = query.ToLower();
|
||||||
|
|
||||||
string[] keywords = query.Split(" ");
|
string[] keywords = query.Split(" ");
|
||||||
|
|
||||||
IQueryable<Slot> dbQuery = this.database.Slots
|
IQueryable<Slot> dbQuery = this.database.Slots
|
||||||
.Include(s => s.Creator)
|
.Include(s => s.Creator)
|
||||||
.Include(s => s.Location)
|
.Include(s => s.Location)
|
||||||
.Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable
|
.Where(s => s.SlotId >= 0); // dumb query to conv into IQueryable
|
||||||
|
|
||||||
// ReSharper disable once LoopCanBeConvertedToQuery
|
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||||
foreach (string keyword in keywords)
|
foreach (string keyword in keywords)
|
||||||
dbQuery = dbQuery.Where
|
dbQuery = dbQuery.Where
|
||||||
(
|
(
|
||||||
s => s.Name.ToLower().Contains(keyword) ||
|
s => s.Name.ToLower().Contains(keyword) ||
|
||||||
s.Description.ToLower().Contains(keyword) ||
|
s.Description.ToLower().Contains(keyword) ||
|
||||||
s.Creator.Username.ToLower().Contains(keyword) ||
|
s.Creator.Username.ToLower().Contains(keyword) ||
|
||||||
s.SlotId.ToString().Equals(keyword)
|
s.SlotId.ToString().Equals(keyword)
|
||||||
);
|
);
|
||||||
|
|
||||||
List<Slot> slots = await dbQuery.Skip(pageStart - 1).Take(Math.Min(pageSize, 30)).ToListAsync();
|
List<Slot> slots = await dbQuery.Skip(pageStart - 1).Take(Math.Min(pageSize, 30)).ToListAsync();
|
||||||
|
|
||||||
string response = slots.Aggregate("", (current, slot) => current + slot.Serialize());
|
string response = slots.Aggregate("", (current, slot) => current + slot.Serialize());
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", dbQuery.Count()));
|
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", dbQuery.Count()));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,345 +11,340 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class SlotsController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
public SlotsController(Database database)
|
||||||
[Produces("text/xml")]
|
|
||||||
public class SlotsController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
public SlotsController(Database database)
|
}
|
||||||
{
|
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots/by")]
|
[HttpGet("slots/by")]
|
||||||
public async Task<IActionResult> SlotsBy([FromQuery] string u, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
public async Task<IActionResult> SlotsBy([FromQuery] string u, [FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||||
{
|
{
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
if (token == null) return this.StatusCode(403, "");
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
GameVersion gameVersion = token.GameVersion;
|
GameVersion gameVersion = token.GameVersion;
|
||||||
|
|
||||||
User? user = await this.database.Users.FirstOrDefaultAsync(dbUser => dbUser.Username == u);
|
User? user = await this.database.Users.FirstOrDefaultAsync(dbUser => dbUser.Username == u);
|
||||||
if (user == null) return this.NotFound();
|
if (user == null) return this.NotFound();
|
||||||
|
|
||||||
string response = Enumerable.Aggregate
|
string response = Enumerable.Aggregate
|
||||||
(
|
(
|
||||||
this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
||||||
.Include(s => s.Creator)
|
|
||||||
.Include(s => s.Location)
|
|
||||||
.Where(s => s.Creator!.Username == user.Username)
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)),
|
|
||||||
string.Empty,
|
|
||||||
(current, slot) => current + slot.Serialize()
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.Ok
|
|
||||||
(
|
|
||||||
LbpSerializer.TaggedStringElement
|
|
||||||
(
|
|
||||||
"slots",
|
|
||||||
response,
|
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"total", user.UsedSlots
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("s/user/{id:int}")]
|
|
||||||
public async Task<IActionResult> SUser(int id)
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
|
||||||
if (token == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
GameVersion gameVersion = token.GameVersion;
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
|
||||||
.Include(s => s.Creator)
|
.Include(s => s.Creator)
|
||||||
.Include(s => s.Location)
|
.Include(s => s.Location)
|
||||||
.FirstOrDefaultAsync(s => s.SlotId == id);
|
.Where(s => s.Creator!.Username == user.Username)
|
||||||
|
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId);
|
|
||||||
VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId);
|
|
||||||
return this.Ok(slot.Serialize(ratedLevel, visitedLevel));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots/cool")]
|
|
||||||
public async Task<IActionResult> Lbp1CoolSlots([FromQuery] int page)
|
|
||||||
{
|
|
||||||
const int pageSize = 30;
|
|
||||||
return await CoolSlots((page - 1) * pageSize, pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots/lbp2cool")]
|
|
||||||
public async Task<IActionResult> CoolSlots
|
|
||||||
(
|
|
||||||
[FromQuery] int pageStart,
|
|
||||||
[FromQuery] int pageSize,
|
|
||||||
[FromQuery] string? gameFilterType = null,
|
|
||||||
[FromQuery] int? players = null,
|
|
||||||
[FromQuery] bool? move = null,
|
|
||||||
[FromQuery] int? page = null
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int _pageStart = pageStart;
|
|
||||||
if (page != null) _pageStart = (int)page * 30;
|
|
||||||
// bit of a better placeholder until we can track average user interaction with /stream endpoint
|
|
||||||
return await ThumbsSlots(_pageStart, Math.Min(pageSize, 30), gameFilterType, players, move, "thisWeek");
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots")]
|
|
||||||
public async Task<IActionResult> NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
|
||||||
{
|
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
|
||||||
if (token == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
GameVersion gameVersion = token.GameVersion;
|
|
||||||
|
|
||||||
IQueryable<Slot> slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
|
||||||
.Include(s => s.Creator)
|
|
||||||
.Include(s => s.Location)
|
|
||||||
.OrderByDescending(s => s.FirstUploaded)
|
|
||||||
.Skip(pageStart - 1)
|
.Skip(pageStart - 1)
|
||||||
.Take(Math.Min(pageSize, 30));
|
.Take(Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)),
|
||||||
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize());
|
string.Empty,
|
||||||
|
(current, slot) => current + slot.Serialize()
|
||||||
|
);
|
||||||
|
|
||||||
return this.Ok
|
return this.Ok
|
||||||
|
(
|
||||||
|
LbpSerializer.TaggedStringElement
|
||||||
(
|
(
|
||||||
LbpSerializer.TaggedStringElement
|
"slots",
|
||||||
(
|
response,
|
||||||
"slots",
|
new Dictionary<string, object>
|
||||||
response,
|
{
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
{
|
||||||
{
|
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
|
||||||
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
|
},
|
||||||
},
|
{
|
||||||
{
|
"total", user.UsedSlots
|
||||||
"total", await StatisticsHelper.SlotCount()
|
},
|
||||||
},
|
}
|
||||||
}
|
)
|
||||||
)
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots/mmpicks")]
|
[HttpGet("s/user/{id:int}")]
|
||||||
public async Task<IActionResult> TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
public async Task<IActionResult> SUser(int id)
|
||||||
{
|
{
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
if (token == null) return this.StatusCode(403, "");
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
GameVersion gameVersion = token.GameVersion;
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
IQueryable<Slot> slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
GameVersion gameVersion = token.GameVersion;
|
||||||
.Where(s => s.TeamPick)
|
|
||||||
.Include(s => s.Creator)
|
|
||||||
.Include(s => s.Location)
|
|
||||||
.OrderByDescending(s => s.LastUpdated)
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, 30));
|
|
||||||
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize());
|
|
||||||
|
|
||||||
return this.Ok
|
Slot? slot = await this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
||||||
|
.Include(s => s.Creator)
|
||||||
|
.Include(s => s.Location)
|
||||||
|
.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
|
|
||||||
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
|
RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId);
|
||||||
|
VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId);
|
||||||
|
return this.Ok(slot.Serialize(ratedLevel, visitedLevel));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("slots/cool")]
|
||||||
|
public async Task<IActionResult> Lbp1CoolSlots([FromQuery] int page)
|
||||||
|
{
|
||||||
|
const int pageSize = 30;
|
||||||
|
return await this.CoolSlots((page - 1) * pageSize, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("slots/lbp2cool")]
|
||||||
|
public async Task<IActionResult> CoolSlots
|
||||||
|
(
|
||||||
|
[FromQuery] int pageStart,
|
||||||
|
[FromQuery] int pageSize,
|
||||||
|
[FromQuery] string? gameFilterType = null,
|
||||||
|
[FromQuery] int? players = null,
|
||||||
|
[FromQuery] bool? move = null,
|
||||||
|
[FromQuery] int? page = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int _pageStart = pageStart;
|
||||||
|
if (page != null) _pageStart = (int)page * 30;
|
||||||
|
// bit of a better placeholder until we can track average user interaction with /stream endpoint
|
||||||
|
return await this.ThumbsSlots(_pageStart, Math.Min(pageSize, 30), gameFilterType, players, move, "thisWeek");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("slots")]
|
||||||
|
public async Task<IActionResult> NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||||
|
{
|
||||||
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
GameVersion gameVersion = token.GameVersion;
|
||||||
|
|
||||||
|
IQueryable<Slot> slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
||||||
|
.Include(s => s.Creator)
|
||||||
|
.Include(s => s.Location)
|
||||||
|
.OrderByDescending(s => s.FirstUploaded)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30));
|
||||||
|
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize());
|
||||||
|
|
||||||
|
return this.Ok
|
||||||
|
(
|
||||||
|
LbpSerializer.TaggedStringElement
|
||||||
(
|
(
|
||||||
LbpSerializer.TaggedStringElement
|
"slots",
|
||||||
(
|
response,
|
||||||
"slots",
|
new Dictionary<string, object>
|
||||||
response,
|
{
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
{
|
||||||
{
|
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
|
||||||
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
|
},
|
||||||
},
|
{
|
||||||
{
|
"total", await StatisticsHelper.SlotCount()
|
||||||
"total", await StatisticsHelper.MMPicksCount()
|
},
|
||||||
},
|
}
|
||||||
}
|
)
|
||||||
)
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots/lbp2luckydip")]
|
[HttpGet("slots/mmpicks")]
|
||||||
public async Task<IActionResult> LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed)
|
public async Task<IActionResult> TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
|
||||||
{
|
{
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
if (token == null) return this.StatusCode(403, "");
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
GameVersion gameVersion = token.GameVersion;
|
GameVersion gameVersion = token.GameVersion;
|
||||||
|
|
||||||
IEnumerable<Slot> slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
IQueryable<Slot> slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
||||||
.Include(s => s.Creator)
|
.Where(s => s.TeamPick)
|
||||||
.Include(s => s.Location)
|
.Include(s => s.Creator)
|
||||||
.OrderBy(_ => EF.Functions.Random())
|
.Include(s => s.Location)
|
||||||
.Take(Math.Min(pageSize, 30));
|
.OrderByDescending(s => s.LastUpdated)
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30));
|
||||||
|
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize());
|
||||||
|
|
||||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
return this.Ok
|
||||||
|
(
|
||||||
return this.Ok
|
LbpSerializer.TaggedStringElement
|
||||||
(
|
(
|
||||||
LbpSerializer.TaggedStringElement
|
"slots",
|
||||||
(
|
response,
|
||||||
"slots",
|
new Dictionary<string, object>
|
||||||
response,
|
{
|
||||||
new Dictionary<string, object>
|
|
||||||
{
|
{
|
||||||
{
|
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
|
||||||
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
|
},
|
||||||
},
|
|
||||||
{
|
|
||||||
"total", await StatisticsHelper.SlotCount()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots/thumbs")]
|
|
||||||
public async Task<IActionResult> ThumbsSlots
|
|
||||||
(
|
|
||||||
[FromQuery] int pageStart,
|
|
||||||
[FromQuery] int pageSize,
|
|
||||||
[FromQuery] string? gameFilterType = null,
|
|
||||||
[FromQuery] int? players = null,
|
|
||||||
[FromQuery] bool? move = null,
|
|
||||||
[FromQuery] string? dateFilterType = null
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Random rand = new();
|
|
||||||
|
|
||||||
IEnumerable<Slot> slots = FilterByRequest(gameFilterType, dateFilterType)
|
|
||||||
.AsEnumerable()
|
|
||||||
.OrderByDescending(s => s.Thumbsup)
|
|
||||||
.ThenBy(_ => rand.Next())
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, 30));
|
|
||||||
|
|
||||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots/mostUniquePlays")]
|
|
||||||
public async Task<IActionResult> MostUniquePlaysSlots
|
|
||||||
(
|
|
||||||
[FromQuery] int pageStart,
|
|
||||||
[FromQuery] int pageSize,
|
|
||||||
[FromQuery] string? gameFilterType = null,
|
|
||||||
[FromQuery] int? players = null,
|
|
||||||
[FromQuery] bool? move = null,
|
|
||||||
[FromQuery] string? dateFilterType = null
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Random rand = new();
|
|
||||||
|
|
||||||
IEnumerable<Slot> slots = FilterByRequest(gameFilterType, dateFilterType)
|
|
||||||
.AsEnumerable()
|
|
||||||
.OrderByDescending
|
|
||||||
(
|
|
||||||
s =>
|
|
||||||
{
|
{
|
||||||
// probably not the best way to do this?
|
"total", await StatisticsHelper.MMPicksCount()
|
||||||
return GetGameFilter(gameFilterType) switch
|
},
|
||||||
{
|
}
|
||||||
GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique,
|
)
|
||||||
GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique,
|
);
|
||||||
GameVersion.LittleBigPlanet3 => s.PlaysLBP3Unique,
|
}
|
||||||
GameVersion.LittleBigPlanetVita => s.PlaysLBPVitaUnique,
|
|
||||||
_ => s.PlaysUnique,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.ThenBy(_ => rand.Next())
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, 30));
|
|
||||||
|
|
||||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
[HttpGet("slots/lbp2luckydip")]
|
||||||
|
public async Task<IActionResult> LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed)
|
||||||
|
{
|
||||||
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
|
GameVersion gameVersion = token.GameVersion;
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("slots/mostHearted")]
|
IEnumerable<Slot> slots = this.database.Slots.Where(s => s.GameVersion <= gameVersion)
|
||||||
public async Task<IActionResult> MostHeartedSlots
|
.Include(s => s.Creator)
|
||||||
|
.Include(s => s.Location)
|
||||||
|
.OrderBy(_ => EF.Functions.Random())
|
||||||
|
.Take(Math.Min(pageSize, 30));
|
||||||
|
|
||||||
|
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
||||||
|
|
||||||
|
return this.Ok
|
||||||
(
|
(
|
||||||
[FromQuery] int pageStart,
|
LbpSerializer.TaggedStringElement
|
||||||
[FromQuery] int pageSize,
|
(
|
||||||
[FromQuery] string? gameFilterType = null,
|
"slots",
|
||||||
[FromQuery] int? players = null,
|
response,
|
||||||
[FromQuery] bool? move = null,
|
new Dictionary<string, object>
|
||||||
[FromQuery] string? dateFilterType = null
|
{
|
||||||
)
|
{
|
||||||
|
"hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"total", await StatisticsHelper.SlotCount()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("slots/thumbs")]
|
||||||
|
public async Task<IActionResult> ThumbsSlots
|
||||||
|
(
|
||||||
|
[FromQuery] int pageStart,
|
||||||
|
[FromQuery] int pageSize,
|
||||||
|
[FromQuery] string? gameFilterType = null,
|
||||||
|
[FromQuery] int? players = null,
|
||||||
|
[FromQuery] bool? move = null,
|
||||||
|
[FromQuery] string? dateFilterType = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Random rand = new();
|
||||||
|
|
||||||
|
IEnumerable<Slot> slots = this.FilterByRequest(gameFilterType, dateFilterType)
|
||||||
|
.AsEnumerable()
|
||||||
|
.OrderByDescending(s => s.Thumbsup)
|
||||||
|
.ThenBy(_ => rand.Next())
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30));
|
||||||
|
|
||||||
|
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("slots/mostUniquePlays")]
|
||||||
|
public async Task<IActionResult> MostUniquePlaysSlots
|
||||||
|
(
|
||||||
|
[FromQuery] int pageStart,
|
||||||
|
[FromQuery] int pageSize,
|
||||||
|
[FromQuery] string? gameFilterType = null,
|
||||||
|
[FromQuery] int? players = null,
|
||||||
|
[FromQuery] bool? move = null,
|
||||||
|
[FromQuery] string? dateFilterType = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Random rand = new();
|
||||||
|
|
||||||
|
IEnumerable<Slot> slots = this.FilterByRequest(gameFilterType, dateFilterType)
|
||||||
|
.AsEnumerable()
|
||||||
|
.OrderByDescending
|
||||||
|
(
|
||||||
|
s =>
|
||||||
|
{
|
||||||
|
// probably not the best way to do this?
|
||||||
|
return this.GetGameFilter(gameFilterType) switch
|
||||||
|
{
|
||||||
|
GameVersion.LittleBigPlanet1 => s.PlaysLBP1Unique,
|
||||||
|
GameVersion.LittleBigPlanet2 => s.PlaysLBP2Unique,
|
||||||
|
GameVersion.LittleBigPlanet3 => s.PlaysLBP3Unique,
|
||||||
|
GameVersion.LittleBigPlanetVita => s.PlaysLBPVitaUnique,
|
||||||
|
_ => s.PlaysUnique,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.ThenBy(_ => rand.Next())
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30));
|
||||||
|
|
||||||
|
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("slots/mostHearted")]
|
||||||
|
public async Task<IActionResult> MostHeartedSlots
|
||||||
|
(
|
||||||
|
[FromQuery] int pageStart,
|
||||||
|
[FromQuery] int pageSize,
|
||||||
|
[FromQuery] string? gameFilterType = null,
|
||||||
|
[FromQuery] int? players = null,
|
||||||
|
[FromQuery] bool? move = null,
|
||||||
|
[FromQuery] string? dateFilterType = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Random rand = new();
|
||||||
|
|
||||||
|
IEnumerable<Slot> slots = this.FilterByRequest(gameFilterType, dateFilterType)
|
||||||
|
.AsEnumerable()
|
||||||
|
.OrderByDescending(s => s.Hearts)
|
||||||
|
.ThenBy(_ => rand.Next())
|
||||||
|
.Skip(pageStart - 1)
|
||||||
|
.Take(Math.Min(pageSize, 30));
|
||||||
|
|
||||||
|
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameVersion GetGameFilter(string? gameFilterType)
|
||||||
|
{
|
||||||
|
return gameFilterType switch
|
||||||
{
|
{
|
||||||
Random rand = new();
|
"lbp1" => GameVersion.LittleBigPlanet1,
|
||||||
|
"lbp2" => GameVersion.LittleBigPlanet2,
|
||||||
|
"lbp3" => GameVersion.LittleBigPlanet3,
|
||||||
|
"both" => GameVersion.LittleBigPlanet2, // LBP2 default option
|
||||||
|
null => GameVersion.LittleBigPlanet1,
|
||||||
|
_ => GameVersion.Unknown,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
IEnumerable<Slot> slots = FilterByRequest(gameFilterType, dateFilterType)
|
public IQueryable<Slot> FilterByRequest(string? gameFilterType, string? dateFilterType)
|
||||||
.AsEnumerable()
|
{
|
||||||
.OrderByDescending(s => s.Hearts)
|
string _dateFilterType = dateFilterType ?? "";
|
||||||
.ThenBy(_ => rand.Next())
|
|
||||||
.Skip(pageStart - 1)
|
|
||||||
.Take(Math.Min(pageSize, 30));
|
|
||||||
|
|
||||||
string response = slots.Aggregate(string.Empty, (current, slot) => current + slot.Serialize());
|
long oldestTime = _dateFilterType switch
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameVersion GetGameFilter(string? gameFilterType)
|
|
||||||
{
|
{
|
||||||
return gameFilterType switch
|
"thisWeek" => DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(),
|
||||||
{
|
"thisMonth" => DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(),
|
||||||
"lbp1" => GameVersion.LittleBigPlanet1,
|
_ => 0,
|
||||||
"lbp2" => GameVersion.LittleBigPlanet2,
|
};
|
||||||
"lbp3" => GameVersion.LittleBigPlanet3,
|
|
||||||
"both" => GameVersion.LittleBigPlanet2, // LBP2 default option
|
|
||||||
null => GameVersion.LittleBigPlanet1,
|
|
||||||
_ => GameVersion.Unknown,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public IQueryable<Slot> FilterByRequest(string? gameFilterType, string? dateFilterType)
|
GameVersion gameVersion = this.GetGameFilter(gameFilterType);
|
||||||
{
|
|
||||||
string _dateFilterType = dateFilterType ?? "";
|
|
||||||
|
|
||||||
long oldestTime = _dateFilterType switch
|
IQueryable<Slot> whereSlots;
|
||||||
{
|
|
||||||
"thisWeek" => DateTimeOffset.Now.AddDays(-7).ToUnixTimeMilliseconds(),
|
|
||||||
"thisMonth" => DateTimeOffset.Now.AddDays(-31).ToUnixTimeMilliseconds(),
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
GameVersion gameVersion = GetGameFilter(gameFilterType);
|
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
||||||
|
if (gameFilterType == "both")
|
||||||
|
// Get game versions less than the current version
|
||||||
|
// Needs support for LBP3 ("both" = LBP1+2)
|
||||||
|
whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime);
|
||||||
|
else
|
||||||
|
// Get game versions exactly equal to gamefiltertype
|
||||||
|
whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime);
|
||||||
|
|
||||||
IQueryable<Slot> whereSlots;
|
return whereSlots.Include(s => s.Creator).Include(s => s.Location);
|
||||||
|
|
||||||
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
|
|
||||||
if (gameFilterType == "both")
|
|
||||||
{
|
|
||||||
// Get game versions less than the current version
|
|
||||||
// Needs support for LBP3 ("both" = LBP1+2)
|
|
||||||
whereSlots = this.database.Slots.Where(s => s.GameVersion <= gameVersion && s.FirstUploaded >= oldestTime);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Get game versions exactly equal to gamefiltertype
|
|
||||||
whereSlots = this.database.Slots.Where(s => s.GameVersion == gameVersion && s.FirstUploaded >= oldestTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
return whereSlots.Include(s => s.Creator).Include(s => s.Location);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,37 +3,36 @@ using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
using LBPUnion.ProjectLighthouse.Serialization;
|
using LBPUnion.ProjectLighthouse.Serialization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/plain")]
|
||||||
|
public class StatisticsController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
public StatisticsController(Database database)
|
||||||
[Produces("text/plain")]
|
|
||||||
public class StatisticsController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
public StatisticsController(Database database)
|
|
||||||
{
|
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("playersInPodCount")]
|
|
||||||
[HttpGet("totalPlayerCount")]
|
|
||||||
public async Task<IActionResult> TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!);
|
|
||||||
|
|
||||||
[HttpGet("planetStats")]
|
|
||||||
public async Task<IActionResult> PlanetStats()
|
|
||||||
{
|
|
||||||
int totalSlotCount = await StatisticsHelper.SlotCount();
|
|
||||||
int mmPicksCount = await StatisticsHelper.MMPicksCount();
|
|
||||||
|
|
||||||
return this.Ok
|
|
||||||
(
|
|
||||||
LbpSerializer.StringElement
|
|
||||||
("planetStats", LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + LbpSerializer.StringElement("mmPicksCount", mmPicksCount))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("planetStats/totalLevelCount")]
|
|
||||||
public async Task<IActionResult> TotalLevelCount() => this.Ok((await StatisticsHelper.SlotCount()).ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("playersInPodCount")]
|
||||||
|
[HttpGet("totalPlayerCount")]
|
||||||
|
public async Task<IActionResult> TotalPlayerCount() => this.Ok((await StatisticsHelper.RecentMatches()).ToString()!);
|
||||||
|
|
||||||
|
[HttpGet("planetStats")]
|
||||||
|
public async Task<IActionResult> PlanetStats()
|
||||||
|
{
|
||||||
|
int totalSlotCount = await StatisticsHelper.SlotCount();
|
||||||
|
int mmPicksCount = await StatisticsHelper.MMPicksCount();
|
||||||
|
|
||||||
|
return this.Ok
|
||||||
|
(
|
||||||
|
LbpSerializer.StringElement
|
||||||
|
("planetStats", LbpSerializer.StringElement("totalSlotCount", totalSlotCount) + LbpSerializer.StringElement("mmPicksCount", mmPicksCount))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("planetStats/totalLevelCount")]
|
||||||
|
public async Task<IActionResult> TotalLevelCount() => this.Ok((await StatisticsHelper.SlotCount()).ToString());
|
||||||
}
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class StoreController : Controller
|
||||||
{
|
{
|
||||||
[ApiController]
|
[HttpGet("promotions")]
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
public IActionResult Promotions() => this.Ok();
|
||||||
[Produces("text/xml")]
|
|
||||||
public class StoreController : Controller
|
|
||||||
{
|
|
||||||
[HttpGet("promotions")]
|
|
||||||
public IActionResult Promotions() => this.Ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -12,181 +12,180 @@ using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers
|
namespace LBPUnion.ProjectLighthouse.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("LITTLEBIGPLANETPS3_XML/")]
|
||||||
|
[Produces("text/xml")]
|
||||||
|
public class UserController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("LITTLEBIGPLANETPS3_XML/")]
|
|
||||||
[Produces("text/xml")]
|
public UserController(Database database)
|
||||||
public class UserController : ControllerBase
|
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public UserController(Database database)
|
public async Task<string?> GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1)
|
||||||
|
{
|
||||||
|
User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username);
|
||||||
|
return user?.Serialize(gameVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("user/{username}")]
|
||||||
|
public async Task<IActionResult> GetUser(string username)
|
||||||
|
{
|
||||||
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
string? user = await this.GetSerializedUser(username, token.GameVersion);
|
||||||
|
if (user == null) return this.NotFound();
|
||||||
|
|
||||||
|
return this.Ok(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("users")]
|
||||||
|
public async Task<IActionResult> GetUserAlt([FromQuery] string[] u)
|
||||||
|
{
|
||||||
|
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
||||||
|
if (token == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
List<string?> serializedUsers = new();
|
||||||
|
foreach (string userId in u) serializedUsers.Add(await this.GetSerializedUser(userId, token.GameVersion));
|
||||||
|
|
||||||
|
string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user);
|
||||||
|
|
||||||
|
return this.Ok(LbpSerializer.StringElement("users", serialized));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("updateUser")]
|
||||||
|
public async Task<IActionResult> UpdateUser()
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
XmlReaderSettings settings = new()
|
||||||
{
|
{
|
||||||
this.database = database;
|
Async = true, // this is apparently not default
|
||||||
}
|
};
|
||||||
|
|
||||||
public async Task<string?> GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1)
|
bool locationChanged = false;
|
||||||
|
|
||||||
|
// this is an absolute mess, but necessary because LBP only sends what changed
|
||||||
|
//
|
||||||
|
// example for changing profile card location:
|
||||||
|
// <updateUser>
|
||||||
|
// <location>
|
||||||
|
// <x>1234</x>
|
||||||
|
// <y>1234</y>
|
||||||
|
// </location>
|
||||||
|
// </updateUser>
|
||||||
|
//
|
||||||
|
// example for changing biography:
|
||||||
|
// <updateUser>
|
||||||
|
// <biography>biography stuff</biography>
|
||||||
|
// </updateUser>
|
||||||
|
//
|
||||||
|
// if you find a way to make it not stupid feel free to replace this
|
||||||
|
using (XmlReader reader = XmlReader.Create(this.Request.Body, settings))
|
||||||
{
|
{
|
||||||
User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username);
|
List<string> path = new(); // you can think of this as a file path in the XML, like <updateUser> -> <location> -> <x>
|
||||||
return user?.Serialize(gameVersion);
|
while (await reader.ReadAsync()) // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||||
}
|
switch (reader.NodeType)
|
||||||
|
{
|
||||||
[HttpGet("user/{username}")]
|
case XmlNodeType.Element:
|
||||||
public async Task<IActionResult> GetUser(string username)
|
path.Add(reader.Name);
|
||||||
{
|
break;
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
case XmlNodeType.Text:
|
||||||
if (token == null) return this.StatusCode(403, "");
|
switch (path[1])
|
||||||
|
{
|
||||||
string? user = await this.GetSerializedUser(username, token.GameVersion);
|
case "biography":
|
||||||
if (user == null) return this.NotFound();
|
|
||||||
|
|
||||||
return this.Ok(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("users")]
|
|
||||||
public async Task<IActionResult> GetUserAlt([FromQuery] string[] u)
|
|
||||||
{
|
|
||||||
GameToken? token = await this.database.GameTokenFromRequest(this.Request);
|
|
||||||
if (token == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
List<string?> serializedUsers = new();
|
|
||||||
foreach (string userId in u) serializedUsers.Add(await this.GetSerializedUser(userId, token.GameVersion));
|
|
||||||
|
|
||||||
string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user);
|
|
||||||
|
|
||||||
return this.Ok(LbpSerializer.StringElement("users", serialized));
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("updateUser")]
|
|
||||||
public async Task<IActionResult> UpdateUser()
|
|
||||||
{
|
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
XmlReaderSettings settings = new()
|
|
||||||
{
|
|
||||||
Async = true, // this is apparently not default
|
|
||||||
};
|
|
||||||
|
|
||||||
bool locationChanged = false;
|
|
||||||
|
|
||||||
// this is an absolute mess, but necessary because LBP only sends what changed
|
|
||||||
//
|
|
||||||
// example for changing profile card location:
|
|
||||||
// <updateUser>
|
|
||||||
// <location>
|
|
||||||
// <x>1234</x>
|
|
||||||
// <y>1234</y>
|
|
||||||
// </location>
|
|
||||||
// </updateUser>
|
|
||||||
//
|
|
||||||
// example for changing biography:
|
|
||||||
// <updateUser>
|
|
||||||
// <biography>biography stuff</biography>
|
|
||||||
// </updateUser>
|
|
||||||
//
|
|
||||||
// if you find a way to make it not stupid feel free to replace this
|
|
||||||
using (XmlReader reader = XmlReader.Create(this.Request.Body, settings))
|
|
||||||
{
|
|
||||||
List<string> path = new(); // you can think of this as a file path in the XML, like <updateUser> -> <location> -> <x>
|
|
||||||
while (await reader.ReadAsync()) // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
|
||||||
switch (reader.NodeType)
|
|
||||||
{
|
|
||||||
case XmlNodeType.Element:
|
|
||||||
path.Add(reader.Name);
|
|
||||||
break;
|
|
||||||
case XmlNodeType.Text:
|
|
||||||
switch (path[1])
|
|
||||||
{
|
{
|
||||||
case "biography":
|
user.Biography = await reader.GetValueAsync();
|
||||||
{
|
break;
|
||||||
user.Biography = await reader.GetValueAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "location":
|
|
||||||
{
|
|
||||||
locationChanged = true; // if we're here then we're probably about to change the location.
|
|
||||||
// ReSharper disable once ConvertIfStatementToSwitchStatement
|
|
||||||
if (path[2] == "x")
|
|
||||||
user.Location.X = Convert.ToInt32
|
|
||||||
(await reader.GetValueAsync()); // GetValue only returns a string, i guess we just hope its a number lol
|
|
||||||
else if (path[2] == "y") user.Location.Y = Convert.ToInt32(await reader.GetValueAsync());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "icon":
|
|
||||||
{
|
|
||||||
user.IconHash = await reader.GetValueAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "planets":
|
|
||||||
{
|
|
||||||
user.PlanetHash = await reader.GetValueAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "yay2":
|
|
||||||
{
|
|
||||||
user.YayHash = await reader.GetValueAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "boo2":
|
|
||||||
{
|
|
||||||
user.BooHash = await reader.GetValueAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "meh2":
|
|
||||||
{
|
|
||||||
user.MehHash = await reader.GetValueAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
case "location":
|
||||||
|
{
|
||||||
|
locationChanged = true; // if we're here then we're probably about to change the location.
|
||||||
|
// ReSharper disable once ConvertIfStatementToSwitchStatement
|
||||||
|
if (path[2] == "x")
|
||||||
|
user.Location.X = Convert.ToInt32
|
||||||
|
(await reader.GetValueAsync()); // GetValue only returns a string, i guess we just hope its a number lol
|
||||||
|
else if (path[2] == "y") user.Location.Y = Convert.ToInt32(await reader.GetValueAsync());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "icon":
|
||||||
|
{
|
||||||
|
user.IconHash = await reader.GetValueAsync();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "planets":
|
||||||
|
{
|
||||||
|
user.PlanetHash = await reader.GetValueAsync();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "yay2":
|
||||||
|
{
|
||||||
|
user.YayHash = await reader.GetValueAsync();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "boo2":
|
||||||
|
{
|
||||||
|
user.BooHash = await reader.GetValueAsync();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "meh2":
|
||||||
|
{
|
||||||
|
user.MehHash = await reader.GetValueAsync();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case XmlNodeType.EndElement:
|
case XmlNodeType.EndElement:
|
||||||
path.RemoveAt(path.Count - 1);
|
path.RemoveAt(path.Count - 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// the way location on a user card works is stupid and will not save with the way below as-is, so we do the following:
|
|
||||||
if (locationChanged) // only modify the database if we modify here
|
|
||||||
{
|
|
||||||
Location? l = await this.database.Locations.FirstOrDefaultAsync(l => l.Id == user.LocationId); // find the location in the database again
|
|
||||||
|
|
||||||
if (l == null) throw new Exception("this shouldn't happen ever but we handle this");
|
|
||||||
|
|
||||||
// set the location in the database to the one we modified above
|
|
||||||
l.X = user.Location.X;
|
|
||||||
l.Y = user.Location.Y;
|
|
||||||
|
|
||||||
// now both are in sync, and will update in the database.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.database.ChangeTracker.HasChanges()) await this.database.SaveChangesAsync(); // save the user to the database if we changed anything
|
|
||||||
return this.Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("update_my_pins")]
|
// the way location on a user card works is stupid and will not save with the way below as-is, so we do the following:
|
||||||
public async Task<IActionResult> UpdateMyPins()
|
if (locationChanged) // only modify the database if we modify here
|
||||||
{
|
{
|
||||||
User? user = await this.database.UserFromGameRequest(this.Request);
|
Location? l = await this.database.Locations.FirstOrDefaultAsync(l => l.Id == user.LocationId); // find the location in the database again
|
||||||
if (user == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
string pinsString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
if (l == null) throw new Exception("this shouldn't happen ever but we handle this");
|
||||||
Pins? pinJson = JsonSerializer.Deserialize<Pins>(pinsString);
|
|
||||||
if (pinJson == null) return this.BadRequest();
|
|
||||||
|
|
||||||
// Sometimes the update gets called periodically as pin progress updates via playing,
|
// set the location in the database to the one we modified above
|
||||||
// may not affect equipped profile pins however, so check before setting it.
|
l.X = user.Location.X;
|
||||||
string currentPins = user.Pins;
|
l.Y = user.Location.Y;
|
||||||
string newPins = string.Join(",", pinJson.ProfilePins);
|
|
||||||
|
|
||||||
if (string.Equals(currentPins, newPins)) return this.Ok("[{\"StatusCode\":200}]");
|
// now both are in sync, and will update in the database.
|
||||||
|
|
||||||
user.Pins = newPins;
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Ok("[{\"StatusCode\":200}]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.database.ChangeTracker.HasChanges()) await this.database.SaveChangesAsync(); // save the user to the database if we changed anything
|
||||||
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("update_my_pins")]
|
||||||
|
public async Task<IActionResult> UpdateMyPins()
|
||||||
|
{
|
||||||
|
User? user = await this.database.UserFromGameRequest(this.Request);
|
||||||
|
if (user == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
string pinsString = await new StreamReader(this.Request.Body).ReadToEndAsync();
|
||||||
|
Pins? pinJson = JsonSerializer.Deserialize<Pins>(pinsString);
|
||||||
|
if (pinJson == null) return this.BadRequest();
|
||||||
|
|
||||||
|
// Sometimes the update gets called periodically as pin progress updates via playing,
|
||||||
|
// may not affect equipped profile pins however, so check before setting it.
|
||||||
|
string currentPins = user.Pins;
|
||||||
|
string newPins = string.Join(",", pinJson.ProfilePins);
|
||||||
|
|
||||||
|
if (string.Equals(currentPins, newPins)) return this.Ok("[{\"StatusCode\":200}]");
|
||||||
|
|
||||||
|
user.Pins = newPins;
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Ok("[{\"StatusCode\":200}]");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,68 +6,67 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
|
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("admin/slot/{id:int}")]
|
||||||
|
public class AdminSlotController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("admin/slot/{id:int}")]
|
|
||||||
public class AdminSlotController : ControllerBase
|
public AdminSlotController(Database database)
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public AdminSlotController(Database database)
|
[HttpGet("teamPick")]
|
||||||
{
|
public async Task<IActionResult> TeamPick([FromRoute] int id)
|
||||||
this.database = database;
|
{
|
||||||
}
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null || !user.IsAdmin) return this.StatusCode(403, "");
|
||||||
|
|
||||||
[HttpGet("teamPick")]
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
public async Task<IActionResult> TeamPick([FromRoute] int id)
|
if (slot == null) return this.NotFound();
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null || !user.IsAdmin) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
slot.TeamPick = true;
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
slot.TeamPick = true;
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
return this.Ok();
|
[HttpGet("removeTeamPick")]
|
||||||
}
|
public async Task<IActionResult> RemoveTeamPick([FromRoute] int id)
|
||||||
|
{
|
||||||
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null || !user.IsAdmin) return this.StatusCode(403, "");
|
||||||
|
|
||||||
[HttpGet("removeTeamPick")]
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
public async Task<IActionResult> RemoveTeamPick([FromRoute] int id)
|
if (slot == null) return this.NotFound();
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null || !user.IsAdmin) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
slot.TeamPick = false;
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
slot.TeamPick = false;
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
return this.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
return this.Ok();
|
[HttpGet("delete")]
|
||||||
}
|
public async Task<IActionResult> DeleteLevel([FromRoute] int id)
|
||||||
|
{
|
||||||
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null || !user.IsAdmin) return this.StatusCode(403, "");
|
||||||
|
|
||||||
[HttpGet("delete")]
|
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
public async Task<IActionResult> DeleteLevel([FromRoute] int id)
|
if (slot == null) return this.Ok();
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null || !user.IsAdmin) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
if (slot.Location == null) throw new ArgumentNullException();
|
||||||
if (slot == null) return this.Ok();
|
|
||||||
|
|
||||||
if (slot.Location == null) throw new ArgumentNullException();
|
this.database.Locations.Remove(slot.Location);
|
||||||
|
this.database.Slots.Remove(slot);
|
||||||
|
|
||||||
this.database.Locations.Remove(slot.Location);
|
await this.database.SaveChangesAsync();
|
||||||
this.database.Slots.Remove(slot);
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
return this.Ok();
|
||||||
|
|
||||||
return this.Ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,34 +4,33 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
|
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("admin/user/{id:int}")]
|
||||||
|
public class AdminUserController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("admin/user/{id:int}")]
|
|
||||||
public class AdminUserController : ControllerBase
|
public AdminUserController(Database database)
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public AdminUserController(Database database)
|
[HttpGet("unban")]
|
||||||
{
|
public async Task<IActionResult> UnbanUser([FromRoute] int id)
|
||||||
this.database = database;
|
{
|
||||||
}
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null || !user.IsAdmin) return this.NotFound();
|
||||||
|
|
||||||
[HttpGet("unban")]
|
User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
||||||
public async Task<IActionResult> UnbanUser([FromRoute] int id)
|
;
|
||||||
{
|
if (targetedUser == null) return this.NotFound();
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null || !user.IsAdmin) return this.NotFound();
|
|
||||||
|
|
||||||
User? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
targetedUser.Banned = false;
|
||||||
;
|
targetedUser.BannedReason = null;
|
||||||
if (targetedUser == null) return this.NotFound();
|
|
||||||
|
|
||||||
targetedUser.Banned = false;
|
await this.database.SaveChangesAsync();
|
||||||
targetedUser.BannedReason = null;
|
return this.Redirect($"/user/{targetedUser.UserId}");
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
return this.Redirect($"/user/{targetedUser.UserId}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,5 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LBPUnion.ProjectLighthouse.Helpers;
|
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Debug;
|
namespace LBPUnion.ProjectLighthouse.Controllers.Website.Debug;
|
||||||
|
|
||||||
|
|
|
@ -7,85 +7,84 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth
|
namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("/authentication")]
|
||||||
|
public class AuthenticationController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("/authentication")]
|
|
||||||
public class AuthenticationController : ControllerBase
|
public AuthenticationController(Database database)
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public AuthenticationController(Database database)
|
[HttpGet("approve/{id:int}")]
|
||||||
|
public async Task<IActionResult> Approve(int id)
|
||||||
|
{
|
||||||
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null) return this.Redirect("/login");
|
||||||
|
|
||||||
|
AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include
|
||||||
|
(a => a.GameToken)
|
||||||
|
.FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id);
|
||||||
|
if (authAttempt == null) return this.NotFound();
|
||||||
|
|
||||||
|
if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
authAttempt.GameToken.Approved = true;
|
||||||
|
this.database.AuthenticationAttempts.Remove(authAttempt);
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Redirect("~/authentication");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("deny/{id:int}")]
|
||||||
|
public async Task<IActionResult> Deny(int id)
|
||||||
|
{
|
||||||
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null) return this.Redirect("/login");
|
||||||
|
|
||||||
|
AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include
|
||||||
|
(a => a.GameToken)
|
||||||
|
.FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id);
|
||||||
|
if (authAttempt == null) return this.NotFound();
|
||||||
|
|
||||||
|
if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, "");
|
||||||
|
|
||||||
|
this.database.GameTokens.Remove(authAttempt.GameToken);
|
||||||
|
this.database.AuthenticationAttempts.Remove(authAttempt);
|
||||||
|
|
||||||
|
DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}");
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Redirect("~/authentication");
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("denyAll")]
|
||||||
|
public async Task<IActionResult> DenyAll()
|
||||||
|
{
|
||||||
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null) return this.Redirect("/login");
|
||||||
|
|
||||||
|
List<AuthenticationAttempt> authAttempts = await this.database.AuthenticationAttempts.Include
|
||||||
|
(a => a.GameToken)
|
||||||
|
.Where(a => a.GameToken.UserId == user.UserId)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
foreach (AuthenticationAttempt authAttempt in authAttempts)
|
||||||
{
|
{
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("approve/{id:int}")]
|
|
||||||
public async Task<IActionResult> Approve(int id)
|
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("/login");
|
|
||||||
|
|
||||||
AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include
|
|
||||||
(a => a.GameToken)
|
|
||||||
.FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id);
|
|
||||||
if (authAttempt == null) return this.NotFound();
|
|
||||||
|
|
||||||
if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
authAttempt.GameToken.Approved = true;
|
|
||||||
this.database.AuthenticationAttempts.Remove(authAttempt);
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Redirect("~/authentication");
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("deny/{id:int}")]
|
|
||||||
public async Task<IActionResult> Deny(int id)
|
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("/login");
|
|
||||||
|
|
||||||
AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include
|
|
||||||
(a => a.GameToken)
|
|
||||||
.FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id);
|
|
||||||
if (authAttempt == null) return this.NotFound();
|
|
||||||
|
|
||||||
if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
this.database.GameTokens.Remove(authAttempt.GameToken);
|
this.database.GameTokens.Remove(authAttempt.GameToken);
|
||||||
this.database.AuthenticationAttempts.Remove(authAttempt);
|
this.database.AuthenticationAttempts.Remove(authAttempt);
|
||||||
|
|
||||||
DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}");
|
DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}");
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Redirect("~/authentication");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("denyAll")]
|
await this.database.SaveChangesAsync();
|
||||||
public async Task<IActionResult> DenyAll()
|
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("/login");
|
|
||||||
|
|
||||||
List<AuthenticationAttempt> authAttempts = await this.database.AuthenticationAttempts.Include
|
return this.Redirect("~/authentication");
|
||||||
(a => a.GameToken)
|
|
||||||
.Where(a => a.GameToken.UserId == user.UserId)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
foreach (AuthenticationAttempt authAttempt in authAttempts)
|
|
||||||
{
|
|
||||||
this.database.GameTokens.Remove(authAttempt.GameToken);
|
|
||||||
this.database.AuthenticationAttempts.Remove(authAttempt);
|
|
||||||
|
|
||||||
DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}");
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Redirect("~/authentication");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,64 +4,63 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth
|
namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("/authentication")]
|
||||||
|
public class AutoApprovalController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("/authentication")]
|
|
||||||
public class AutoApprovalController : ControllerBase
|
public AutoApprovalController(Database database)
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public AutoApprovalController(Database database)
|
[HttpGet("autoApprove/{id:int}")]
|
||||||
|
public async Task<IActionResult> AutoApprove([FromRoute] int id)
|
||||||
|
{
|
||||||
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null) return this.Redirect("/login");
|
||||||
|
|
||||||
|
AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include
|
||||||
|
(a => a.GameToken)
|
||||||
|
.FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id);
|
||||||
|
|
||||||
|
if (authAttempt == null) return this.BadRequest();
|
||||||
|
if (authAttempt.GameToken.UserId != user.UserId) return this.Redirect("/login");
|
||||||
|
|
||||||
|
authAttempt.GameToken.Approved = true;
|
||||||
|
|
||||||
|
UserApprovedIpAddress approvedIpAddress = new()
|
||||||
{
|
{
|
||||||
this.database = database;
|
UserId = user.UserId,
|
||||||
}
|
User = user,
|
||||||
|
IpAddress = authAttempt.IPAddress,
|
||||||
|
};
|
||||||
|
|
||||||
[HttpGet("autoApprove/{id:int}")]
|
this.database.UserApprovedIpAddresses.Add(approvedIpAddress);
|
||||||
public async Task<IActionResult> AutoApprove([FromRoute] int id)
|
this.database.AuthenticationAttempts.Remove(authAttempt);
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("/login");
|
|
||||||
|
|
||||||
AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include
|
await this.database.SaveChangesAsync();
|
||||||
(a => a.GameToken)
|
|
||||||
.FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id);
|
|
||||||
|
|
||||||
if (authAttempt == null) return this.BadRequest();
|
return this.Redirect("/authentication");
|
||||||
if (authAttempt.GameToken.UserId != user.UserId) return this.Redirect("/login");
|
}
|
||||||
|
|
||||||
authAttempt.GameToken.Approved = true;
|
[HttpGet("revokeAutoApproval/{id:int}")]
|
||||||
|
public async Task<IActionResult> RevokeAutoApproval([FromRoute] int id)
|
||||||
|
{
|
||||||
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null) return this.Redirect("/login");
|
||||||
|
|
||||||
UserApprovedIpAddress approvedIpAddress = new()
|
UserApprovedIpAddress? approvedIpAddress = await this.database.UserApprovedIpAddresses.FirstOrDefaultAsync(a => a.UserApprovedIpAddressId == id);
|
||||||
{
|
if (approvedIpAddress == null) return this.BadRequest();
|
||||||
UserId = user.UserId,
|
if (approvedIpAddress.UserId != user.UserId) return this.Redirect("/login");
|
||||||
User = user,
|
|
||||||
IpAddress = authAttempt.IPAddress,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.database.UserApprovedIpAddresses.Add(approvedIpAddress);
|
this.database.UserApprovedIpAddresses.Remove(approvedIpAddress);
|
||||||
this.database.AuthenticationAttempts.Remove(authAttempt);
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
return this.Redirect("/authentication");
|
return this.Redirect("/authentication/autoApprovals");
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("revokeAutoApproval/{id:int}")]
|
|
||||||
public async Task<IActionResult> RevokeAutoApproval([FromRoute] int id)
|
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("/login");
|
|
||||||
|
|
||||||
UserApprovedIpAddress? approvedIpAddress = await this.database.UserApprovedIpAddresses.FirstOrDefaultAsync(a => a.UserApprovedIpAddressId == id);
|
|
||||||
if (approvedIpAddress == null) return this.BadRequest();
|
|
||||||
if (approvedIpAddress.UserId != user.UserId) return this.Redirect("/login");
|
|
||||||
|
|
||||||
this.database.UserApprovedIpAddresses.Remove(approvedIpAddress);
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Redirect("/authentication/autoApprovals");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,81 +10,80 @@ using Microsoft.EntityFrameworkCore;
|
||||||
// TODO: Clean up this file
|
// TODO: Clean up this file
|
||||||
// - jvyden
|
// - jvyden
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers.Website
|
namespace LBPUnion.ProjectLighthouse.Controllers.Website;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("slot/{id:int}")]
|
||||||
|
public class SlotPageController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("slot/{id:int}")]
|
|
||||||
public class SlotPageController : ControllerBase
|
public SlotPageController(Database database)
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public SlotPageController(Database database)
|
[HttpGet("heart")]
|
||||||
{
|
public async Task<IActionResult> HeartLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
|
||||||
this.database = database;
|
{
|
||||||
}
|
if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
|
||||||
|
|
||||||
[HttpGet("heart")]
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
public async Task<IActionResult> HeartLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
|
if (user == null) return this.Redirect("~/login");
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
|
|
||||||
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
if (user == null) return this.Redirect("~/login");
|
if (heartedSlot == null) return this.NotFound();
|
||||||
|
|
||||||
Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
await this.database.HeartLevel(user, heartedSlot);
|
||||||
if (heartedSlot == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.HeartLevel(user, heartedSlot);
|
return this.Redirect(callbackUrl);
|
||||||
|
}
|
||||||
|
|
||||||
return this.Redirect(callbackUrl);
|
[HttpGet("unheart")]
|
||||||
}
|
public async Task<IActionResult> UnheartLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
|
||||||
|
|
||||||
[HttpGet("unheart")]
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
public async Task<IActionResult> UnheartLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
|
if (user == null) return this.Redirect("~/login");
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
|
|
||||||
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
if (user == null) return this.Redirect("~/login");
|
if (heartedSlot == null) return this.NotFound();
|
||||||
|
|
||||||
Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
await this.database.UnheartLevel(user, heartedSlot);
|
||||||
if (heartedSlot == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.UnheartLevel(user, heartedSlot);
|
return this.Redirect(callbackUrl);
|
||||||
|
}
|
||||||
|
|
||||||
return this.Redirect(callbackUrl);
|
[HttpGet("queue")]
|
||||||
}
|
public async Task<IActionResult> QueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
|
||||||
|
|
||||||
[HttpGet("queue")]
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
public async Task<IActionResult> QueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
|
if (user == null) return this.Redirect("~/login");
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
|
|
||||||
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
if (user == null) return this.Redirect("~/login");
|
if (queuedSlot == null) return this.NotFound();
|
||||||
|
|
||||||
Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
await this.database.QueueLevel(user, queuedSlot);
|
||||||
if (queuedSlot == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.QueueLevel(user, queuedSlot);
|
return this.Redirect(callbackUrl);
|
||||||
|
}
|
||||||
|
|
||||||
return this.Redirect(callbackUrl);
|
[HttpGet("unqueue")]
|
||||||
}
|
public async Task<IActionResult> UnqueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
|
||||||
|
|
||||||
[HttpGet("unqueue")]
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
public async Task<IActionResult> UnqueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
|
if (user == null) return this.Redirect("~/login");
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
|
|
||||||
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
if (user == null) return this.Redirect("~/login");
|
if (queuedSlot == null) return this.NotFound();
|
||||||
|
|
||||||
Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
await this.database.UnqueueLevel(user, queuedSlot);
|
||||||
if (queuedSlot == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.UnqueueLevel(user, queuedSlot);
|
return this.Redirect(callbackUrl);
|
||||||
|
|
||||||
return this.Redirect(callbackUrl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,45 +4,44 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Controllers.Website
|
namespace LBPUnion.ProjectLighthouse.Controllers.Website;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("user/{id:int}")]
|
||||||
|
public class UserPageController : ControllerBase
|
||||||
{
|
{
|
||||||
[ApiController]
|
private readonly Database database;
|
||||||
[Route("user/{id:int}")]
|
|
||||||
public class UserPageController : ControllerBase
|
public UserPageController(Database database)
|
||||||
{
|
{
|
||||||
private readonly Database database;
|
this.database = database;
|
||||||
|
}
|
||||||
|
|
||||||
public UserPageController(Database database)
|
[HttpGet("heart")]
|
||||||
{
|
public async Task<IActionResult> HeartUser([FromRoute] int id)
|
||||||
this.database = database;
|
{
|
||||||
}
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null) return this.Redirect("~/login");
|
||||||
|
|
||||||
[HttpGet("heart")]
|
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
||||||
public async Task<IActionResult> HeartUser([FromRoute] int id)
|
if (heartedUser == null) return this.NotFound();
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("~/login");
|
|
||||||
|
|
||||||
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
await this.database.HeartUser(user, heartedUser);
|
||||||
if (heartedUser == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.HeartUser(user, heartedUser);
|
return this.Redirect("~/user/" + id);
|
||||||
|
}
|
||||||
|
|
||||||
return this.Redirect("~/user/" + id);
|
[HttpGet("unheart")]
|
||||||
}
|
public async Task<IActionResult> UnheartUser([FromRoute] int id)
|
||||||
|
{
|
||||||
|
User? user = this.database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null) return this.Redirect("~/login");
|
||||||
|
|
||||||
[HttpGet("unheart")]
|
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
||||||
public async Task<IActionResult> UnheartUser([FromRoute] int id)
|
if (heartedUser == null) return this.NotFound();
|
||||||
{
|
|
||||||
User? user = this.database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("~/login");
|
|
||||||
|
|
||||||
User? heartedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
await this.database.UnheartUser(user, heartedUser);
|
||||||
if (heartedUser == null) return this.NotFound();
|
|
||||||
|
|
||||||
await this.database.UnheartUser(user, heartedUser);
|
return this.Redirect("~/user/" + id);
|
||||||
|
|
||||||
return this.Redirect("~/user/" + id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,287 +13,284 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse
|
namespace LBPUnion.ProjectLighthouse;
|
||||||
|
|
||||||
|
public class Database : DbContext
|
||||||
{
|
{
|
||||||
public class Database : DbContext
|
public DbSet<User> Users { get; set; }
|
||||||
|
public DbSet<Location> Locations { get; set; }
|
||||||
|
public DbSet<Slot> Slots { get; set; }
|
||||||
|
public DbSet<QueuedLevel> QueuedLevels { get; set; }
|
||||||
|
public DbSet<HeartedLevel> HeartedLevels { get; set; }
|
||||||
|
public DbSet<HeartedProfile> HeartedProfiles { get; set; }
|
||||||
|
public DbSet<Comment> Comments { get; set; }
|
||||||
|
public DbSet<GameToken> GameTokens { get; set; }
|
||||||
|
public DbSet<WebToken> WebTokens { get; set; }
|
||||||
|
public DbSet<Score> Scores { get; set; }
|
||||||
|
public DbSet<PhotoSubject> PhotoSubjects { get; set; }
|
||||||
|
public DbSet<Photo> Photos { get; set; }
|
||||||
|
public DbSet<LastContact> LastContacts { get; set; }
|
||||||
|
public DbSet<VisitedLevel> VisitedLevels { get; set; }
|
||||||
|
public DbSet<RatedLevel> RatedLevels { get; set; }
|
||||||
|
public DbSet<AuthenticationAttempt> AuthenticationAttempts { get; set; }
|
||||||
|
public DbSet<Review> Reviews { get; set; }
|
||||||
|
public DbSet<RatedReview> RatedReviews { get; set; }
|
||||||
|
public DbSet<UserApprovedIpAddress> UserApprovedIpAddresses { get; set; }
|
||||||
|
public DbSet<DatabaseCategory> CustomCategories { get; set; }
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||||
|
=> options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
|
||||||
|
|
||||||
|
public async Task<User> CreateUser(string username, string password)
|
||||||
{
|
{
|
||||||
public DbSet<User> Users { get; set; }
|
if (!password.StartsWith("$")) throw new ArgumentException(nameof(password) + " is not a BCrypt hash");
|
||||||
public DbSet<Location> Locations { get; set; }
|
|
||||||
public DbSet<Slot> Slots { get; set; }
|
|
||||||
public DbSet<QueuedLevel> QueuedLevels { get; set; }
|
|
||||||
public DbSet<HeartedLevel> HeartedLevels { get; set; }
|
|
||||||
public DbSet<HeartedProfile> HeartedProfiles { get; set; }
|
|
||||||
public DbSet<Comment> Comments { get; set; }
|
|
||||||
public DbSet<GameToken> GameTokens { get; set; }
|
|
||||||
public DbSet<WebToken> WebTokens { get; set; }
|
|
||||||
public DbSet<Score> Scores { get; set; }
|
|
||||||
public DbSet<PhotoSubject> PhotoSubjects { get; set; }
|
|
||||||
public DbSet<Photo> Photos { get; set; }
|
|
||||||
public DbSet<LastContact> LastContacts { get; set; }
|
|
||||||
public DbSet<VisitedLevel> VisitedLevels { get; set; }
|
|
||||||
public DbSet<RatedLevel> RatedLevels { get; set; }
|
|
||||||
public DbSet<AuthenticationAttempt> AuthenticationAttempts { get; set; }
|
|
||||||
public DbSet<Review> Reviews { get; set; }
|
|
||||||
public DbSet<RatedReview> RatedReviews { get; set; }
|
|
||||||
public DbSet<UserApprovedIpAddress> UserApprovedIpAddresses { get; set; }
|
|
||||||
public DbSet<DatabaseCategory> CustomCategories { get; set; }
|
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
User user;
|
||||||
=> options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
|
if ((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) return user;
|
||||||
|
|
||||||
public async Task<User> CreateUser(string username, string password)
|
Location l = new(); // store to get id after submitting
|
||||||
|
this.Locations.Add(l); // add to table
|
||||||
|
await this.SaveChangesAsync(); // saving to the database returns the id and sets it on this entity
|
||||||
|
|
||||||
|
user = new User
|
||||||
{
|
{
|
||||||
if (!password.StartsWith("$")) throw new ArgumentException(nameof(password) + " is not a BCrypt hash");
|
Username = username,
|
||||||
|
Password = password,
|
||||||
|
LocationId = l.Id,
|
||||||
|
Biography = username + " hasn't introduced themselves yet.",
|
||||||
|
};
|
||||||
|
this.Users.Add(user);
|
||||||
|
|
||||||
User user;
|
await this.SaveChangesAsync();
|
||||||
if ((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) return user;
|
|
||||||
|
|
||||||
Location l = new(); // store to get id after submitting
|
return user;
|
||||||
this.Locations.Add(l); // add to table
|
|
||||||
await this.SaveChangesAsync(); // saving to the database returns the id and sets it on this entity
|
|
||||||
|
|
||||||
user = new User
|
|
||||||
{
|
|
||||||
Username = username,
|
|
||||||
Password = password,
|
|
||||||
LocationId = l.Id,
|
|
||||||
Biography = username + " hasn't introduced themselves yet.",
|
|
||||||
};
|
|
||||||
this.Users.Add(user);
|
|
||||||
|
|
||||||
await this.SaveChangesAsync();
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nullable enable
|
|
||||||
public async Task<GameToken?> AuthenticateUser(LoginData loginData, string userLocation, string titleId = "")
|
|
||||||
{
|
|
||||||
User? user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username);
|
|
||||||
if (user == null) return null;
|
|
||||||
|
|
||||||
GameToken gameToken = new()
|
|
||||||
{
|
|
||||||
UserToken = HashHelper.GenerateAuthToken(),
|
|
||||||
User = user,
|
|
||||||
UserId = user.UserId,
|
|
||||||
UserLocation = userLocation,
|
|
||||||
GameVersion = GameVersionHelper.FromTitleId(titleId),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (gameToken.GameVersion == GameVersion.Unknown)
|
|
||||||
{
|
|
||||||
Logger.Log($"Unknown GameVersion for TitleId {titleId}", LoggerLevelLogin.Instance);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.GameTokens.Add(gameToken);
|
|
||||||
await this.SaveChangesAsync();
|
|
||||||
|
|
||||||
return gameToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Hearts & Queues
|
|
||||||
|
|
||||||
public async Task HeartUser(User user, User heartedUser)
|
|
||||||
{
|
|
||||||
HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync
|
|
||||||
(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId);
|
|
||||||
if (heartedProfile != null) return;
|
|
||||||
|
|
||||||
this.HeartedProfiles.Add
|
|
||||||
(
|
|
||||||
new HeartedProfile
|
|
||||||
{
|
|
||||||
HeartedUserId = heartedUser.UserId,
|
|
||||||
UserId = user.UserId,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UnheartUser(User user, User heartedUser)
|
|
||||||
{
|
|
||||||
HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync
|
|
||||||
(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId);
|
|
||||||
if (heartedProfile != null) this.HeartedProfiles.Remove(heartedProfile);
|
|
||||||
|
|
||||||
await this.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task HeartLevel(User user, Slot heartedSlot)
|
|
||||||
{
|
|
||||||
HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId);
|
|
||||||
if (heartedLevel != null) return;
|
|
||||||
|
|
||||||
this.HeartedLevels.Add
|
|
||||||
(
|
|
||||||
new HeartedLevel
|
|
||||||
{
|
|
||||||
SlotId = heartedSlot.SlotId,
|
|
||||||
UserId = user.UserId,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UnheartLevel(User user, Slot heartedSlot)
|
|
||||||
{
|
|
||||||
HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId);
|
|
||||||
if (heartedLevel != null) this.HeartedLevels.Remove(heartedLevel);
|
|
||||||
|
|
||||||
await this.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task QueueLevel(User user, Slot queuedSlot)
|
|
||||||
{
|
|
||||||
QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId);
|
|
||||||
if (queuedLevel != null) return;
|
|
||||||
|
|
||||||
this.QueuedLevels.Add
|
|
||||||
(
|
|
||||||
new QueuedLevel
|
|
||||||
{
|
|
||||||
SlotId = queuedSlot.SlotId,
|
|
||||||
UserId = user.UserId,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UnqueueLevel(User user, Slot queuedSlot)
|
|
||||||
{
|
|
||||||
QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId);
|
|
||||||
if (queuedLevel != null) this.QueuedLevels.Remove(queuedLevel);
|
|
||||||
|
|
||||||
await this.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Game Token Shenanigans
|
|
||||||
|
|
||||||
public async Task<User?> UserFromMMAuth(string authToken, bool allowUnapproved = false)
|
|
||||||
{
|
|
||||||
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
|
||||||
GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == authToken);
|
|
||||||
|
|
||||||
if (token == null) return null;
|
|
||||||
if (!allowUnapproved && !token.Approved) return null;
|
|
||||||
|
|
||||||
return await this.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == token.UserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<User?> UserFromGameToken
|
|
||||||
(GameToken gameToken, bool allowUnapproved = false)
|
|
||||||
=> await this.UserFromMMAuth(gameToken.UserToken, allowUnapproved);
|
|
||||||
|
|
||||||
public async Task<User?> UserFromGameRequest(HttpRequest request, bool allowUnapproved = false)
|
|
||||||
{
|
|
||||||
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
|
||||||
if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
|
|
||||||
|
|
||||||
return await this.UserFromMMAuth(mmAuth, allowUnapproved);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<GameToken?> GameTokenFromRequest(HttpRequest request, bool allowUnapproved = false)
|
|
||||||
{
|
|
||||||
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
|
||||||
if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
|
|
||||||
|
|
||||||
GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth);
|
|
||||||
|
|
||||||
if (token == null) return null;
|
|
||||||
if (!allowUnapproved && !token.Approved) return null;
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<(User, GameToken)?> UserAndGameTokenFromRequest(HttpRequest request, bool allowUnapproved = false)
|
|
||||||
{
|
|
||||||
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
|
||||||
if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
|
|
||||||
|
|
||||||
GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth);
|
|
||||||
if (token == null) return null;
|
|
||||||
if (!allowUnapproved && !token.Approved) return null;
|
|
||||||
|
|
||||||
User? user = await this.UserFromGameToken(token);
|
|
||||||
|
|
||||||
if (user == null) return null;
|
|
||||||
|
|
||||||
return (user, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Web Token Shenanigans
|
|
||||||
|
|
||||||
public User? UserFromLighthouseToken(string lighthouseToken)
|
|
||||||
{
|
|
||||||
WebToken? token = this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken);
|
|
||||||
if (token == null) return null;
|
|
||||||
|
|
||||||
return this.Users.Include(u => u.Location).FirstOrDefault(u => u.UserId == token.UserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public User? UserFromWebRequest(HttpRequest request)
|
|
||||||
{
|
|
||||||
if (!request.Cookies.TryGetValue("LighthouseToken", out string? lighthouseToken) || lighthouseToken == null) return null;
|
|
||||||
|
|
||||||
return this.UserFromLighthouseToken(lighthouseToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WebToken? WebTokenFromRequest(HttpRequest request)
|
|
||||||
{
|
|
||||||
if (!request.Cookies.TryGetValue("LighthouseToken", out string? lighthouseToken) || lighthouseToken == null) return null;
|
|
||||||
|
|
||||||
return this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public async Task<Photo?> PhotoFromSubject(PhotoSubject subject)
|
|
||||||
=> await this.Photos.FirstOrDefaultAsync(p => p.PhotoSubjectIds.Contains(subject.PhotoSubjectId.ToString()));
|
|
||||||
public async Task RemoveUser(User? user)
|
|
||||||
{
|
|
||||||
if (user == null) return;
|
|
||||||
|
|
||||||
if (user.Location != null) this.Locations.Remove(user.Location);
|
|
||||||
LastContact? lastContact = await this.LastContacts.FirstOrDefaultAsync(l => l.UserId == user.UserId);
|
|
||||||
if (lastContact != null) this.LastContacts.Remove(lastContact);
|
|
||||||
|
|
||||||
foreach (Slot slot in this.Slots.Where(s => s.CreatorId == user.UserId)) await this.RemoveSlot(slot, false);
|
|
||||||
|
|
||||||
this.AuthenticationAttempts.RemoveRange(this.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId));
|
|
||||||
this.HeartedProfiles.RemoveRange(this.HeartedProfiles.Where(h => h.UserId == user.UserId));
|
|
||||||
this.PhotoSubjects.RemoveRange(this.PhotoSubjects.Where(s => s.UserId == user.UserId));
|
|
||||||
this.HeartedLevels.RemoveRange(this.HeartedLevels.Where(h => h.UserId == user.UserId));
|
|
||||||
this.VisitedLevels.RemoveRange(this.VisitedLevels.Where(v => v.UserId == user.UserId));
|
|
||||||
this.RatedReviews.RemoveRange(this.RatedReviews.Where(r => r.UserId == user.UserId));
|
|
||||||
this.QueuedLevels.RemoveRange(this.QueuedLevels.Where(q => q.UserId == user.UserId));
|
|
||||||
this.RatedLevels.RemoveRange(this.RatedLevels.Where(r => r.UserId == user.UserId));
|
|
||||||
this.GameTokens.RemoveRange(this.GameTokens.Where(t => t.UserId == user.UserId));
|
|
||||||
this.WebTokens.RemoveRange(this.WebTokens.Where(t => t.UserId == user.UserId));
|
|
||||||
this.Comments.RemoveRange(this.Comments.Where(c => c.PosterUserId == user.UserId));
|
|
||||||
this.Reviews.RemoveRange(this.Reviews.Where(r => r.ReviewerId == user.UserId));
|
|
||||||
this.Photos.RemoveRange(this.Photos.Where(p => p.CreatorId == user.UserId));
|
|
||||||
|
|
||||||
this.Users.Remove(user);
|
|
||||||
|
|
||||||
await this.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RemoveSlot(Slot slot, bool saveChanges = true)
|
|
||||||
{
|
|
||||||
if (slot.Location != null) this.Locations.Remove(slot.Location);
|
|
||||||
this.Slots.Remove(slot);
|
|
||||||
|
|
||||||
if (saveChanges) await this.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
#nullable disable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
public async Task<GameToken?> AuthenticateUser(LoginData loginData, string userLocation, string titleId = "")
|
||||||
|
{
|
||||||
|
User? user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username);
|
||||||
|
if (user == null) return null;
|
||||||
|
|
||||||
|
GameToken gameToken = new()
|
||||||
|
{
|
||||||
|
UserToken = HashHelper.GenerateAuthToken(),
|
||||||
|
User = user,
|
||||||
|
UserId = user.UserId,
|
||||||
|
UserLocation = userLocation,
|
||||||
|
GameVersion = GameVersionHelper.FromTitleId(titleId),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (gameToken.GameVersion == GameVersion.Unknown)
|
||||||
|
{
|
||||||
|
Logger.Log($"Unknown GameVersion for TitleId {titleId}", LoggerLevelLogin.Instance);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.GameTokens.Add(gameToken);
|
||||||
|
await this.SaveChangesAsync();
|
||||||
|
|
||||||
|
return gameToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Hearts & Queues
|
||||||
|
|
||||||
|
public async Task HeartUser(User user, User heartedUser)
|
||||||
|
{
|
||||||
|
HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId);
|
||||||
|
if (heartedProfile != null) return;
|
||||||
|
|
||||||
|
this.HeartedProfiles.Add
|
||||||
|
(
|
||||||
|
new HeartedProfile
|
||||||
|
{
|
||||||
|
HeartedUserId = heartedUser.UserId,
|
||||||
|
UserId = user.UserId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UnheartUser(User user, User heartedUser)
|
||||||
|
{
|
||||||
|
HeartedProfile? heartedProfile = await this.HeartedProfiles.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId);
|
||||||
|
if (heartedProfile != null) this.HeartedProfiles.Remove(heartedProfile);
|
||||||
|
|
||||||
|
await this.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HeartLevel(User user, Slot heartedSlot)
|
||||||
|
{
|
||||||
|
HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId);
|
||||||
|
if (heartedLevel != null) return;
|
||||||
|
|
||||||
|
this.HeartedLevels.Add
|
||||||
|
(
|
||||||
|
new HeartedLevel
|
||||||
|
{
|
||||||
|
SlotId = heartedSlot.SlotId,
|
||||||
|
UserId = user.UserId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UnheartLevel(User user, Slot heartedSlot)
|
||||||
|
{
|
||||||
|
HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == heartedSlot.SlotId);
|
||||||
|
if (heartedLevel != null) this.HeartedLevels.Remove(heartedLevel);
|
||||||
|
|
||||||
|
await this.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task QueueLevel(User user, Slot queuedSlot)
|
||||||
|
{
|
||||||
|
QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId);
|
||||||
|
if (queuedLevel != null) return;
|
||||||
|
|
||||||
|
this.QueuedLevels.Add
|
||||||
|
(
|
||||||
|
new QueuedLevel
|
||||||
|
{
|
||||||
|
SlotId = queuedSlot.SlotId,
|
||||||
|
UserId = user.UserId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UnqueueLevel(User user, Slot queuedSlot)
|
||||||
|
{
|
||||||
|
QueuedLevel? queuedLevel = await this.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == queuedSlot.SlotId);
|
||||||
|
if (queuedLevel != null) this.QueuedLevels.Remove(queuedLevel);
|
||||||
|
|
||||||
|
await this.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Game Token Shenanigans
|
||||||
|
|
||||||
|
public async Task<User?> UserFromMMAuth(string authToken, bool allowUnapproved = false)
|
||||||
|
{
|
||||||
|
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
||||||
|
GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == authToken);
|
||||||
|
|
||||||
|
if (token == null) return null;
|
||||||
|
if (!allowUnapproved && !token.Approved) return null;
|
||||||
|
|
||||||
|
return await this.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == token.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<User?> UserFromGameToken
|
||||||
|
(GameToken gameToken, bool allowUnapproved = false)
|
||||||
|
=> await this.UserFromMMAuth(gameToken.UserToken, allowUnapproved);
|
||||||
|
|
||||||
|
public async Task<User?> UserFromGameRequest(HttpRequest request, bool allowUnapproved = false)
|
||||||
|
{
|
||||||
|
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
||||||
|
if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
|
||||||
|
|
||||||
|
return await this.UserFromMMAuth(mmAuth, allowUnapproved);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GameToken?> GameTokenFromRequest(HttpRequest request, bool allowUnapproved = false)
|
||||||
|
{
|
||||||
|
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
||||||
|
if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
|
||||||
|
|
||||||
|
GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth);
|
||||||
|
|
||||||
|
if (token == null) return null;
|
||||||
|
if (!allowUnapproved && !token.Approved) return null;
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<(User, GameToken)?> UserAndGameTokenFromRequest(HttpRequest request, bool allowUnapproved = false)
|
||||||
|
{
|
||||||
|
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
|
||||||
|
if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
|
||||||
|
|
||||||
|
GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth);
|
||||||
|
if (token == null) return null;
|
||||||
|
if (!allowUnapproved && !token.Approved) return null;
|
||||||
|
|
||||||
|
User? user = await this.UserFromGameToken(token);
|
||||||
|
|
||||||
|
if (user == null) return null;
|
||||||
|
|
||||||
|
return (user, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Web Token Shenanigans
|
||||||
|
|
||||||
|
public User? UserFromLighthouseToken(string lighthouseToken)
|
||||||
|
{
|
||||||
|
WebToken? token = this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken);
|
||||||
|
if (token == null) return null;
|
||||||
|
|
||||||
|
return this.Users.Include(u => u.Location).FirstOrDefault(u => u.UserId == token.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User? UserFromWebRequest(HttpRequest request)
|
||||||
|
{
|
||||||
|
if (!request.Cookies.TryGetValue("LighthouseToken", out string? lighthouseToken) || lighthouseToken == null) return null;
|
||||||
|
|
||||||
|
return this.UserFromLighthouseToken(lighthouseToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebToken? WebTokenFromRequest(HttpRequest request)
|
||||||
|
{
|
||||||
|
if (!request.Cookies.TryGetValue("LighthouseToken", out string? lighthouseToken) || lighthouseToken == null) return null;
|
||||||
|
|
||||||
|
return this.WebTokens.FirstOrDefault(t => t.UserToken == lighthouseToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public async Task<Photo?> PhotoFromSubject(PhotoSubject subject)
|
||||||
|
=> await this.Photos.FirstOrDefaultAsync(p => p.PhotoSubjectIds.Contains(subject.PhotoSubjectId.ToString()));
|
||||||
|
public async Task RemoveUser(User? user)
|
||||||
|
{
|
||||||
|
if (user == null) return;
|
||||||
|
|
||||||
|
if (user.Location != null) this.Locations.Remove(user.Location);
|
||||||
|
LastContact? lastContact = await this.LastContacts.FirstOrDefaultAsync(l => l.UserId == user.UserId);
|
||||||
|
if (lastContact != null) this.LastContacts.Remove(lastContact);
|
||||||
|
|
||||||
|
foreach (Slot slot in this.Slots.Where(s => s.CreatorId == user.UserId)) await this.RemoveSlot(slot, false);
|
||||||
|
|
||||||
|
this.AuthenticationAttempts.RemoveRange(this.AuthenticationAttempts.Include(a => a.GameToken).Where(a => a.GameToken.UserId == user.UserId));
|
||||||
|
this.HeartedProfiles.RemoveRange(this.HeartedProfiles.Where(h => h.UserId == user.UserId));
|
||||||
|
this.PhotoSubjects.RemoveRange(this.PhotoSubjects.Where(s => s.UserId == user.UserId));
|
||||||
|
this.HeartedLevels.RemoveRange(this.HeartedLevels.Where(h => h.UserId == user.UserId));
|
||||||
|
this.VisitedLevels.RemoveRange(this.VisitedLevels.Where(v => v.UserId == user.UserId));
|
||||||
|
this.RatedReviews.RemoveRange(this.RatedReviews.Where(r => r.UserId == user.UserId));
|
||||||
|
this.QueuedLevels.RemoveRange(this.QueuedLevels.Where(q => q.UserId == user.UserId));
|
||||||
|
this.RatedLevels.RemoveRange(this.RatedLevels.Where(r => r.UserId == user.UserId));
|
||||||
|
this.GameTokens.RemoveRange(this.GameTokens.Where(t => t.UserId == user.UserId));
|
||||||
|
this.WebTokens.RemoveRange(this.WebTokens.Where(t => t.UserId == user.UserId));
|
||||||
|
this.Comments.RemoveRange(this.Comments.Where(c => c.PosterUserId == user.UserId));
|
||||||
|
this.Reviews.RemoveRange(this.Reviews.Where(r => r.ReviewerId == user.UserId));
|
||||||
|
this.Photos.RemoveRange(this.Photos.Where(p => p.CreatorId == user.UserId));
|
||||||
|
|
||||||
|
this.Users.Remove(user);
|
||||||
|
|
||||||
|
await this.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RemoveSlot(Slot slot, bool saveChanges = true)
|
||||||
|
{
|
||||||
|
if (slot.Location != null) this.Locations.Remove(slot.Location);
|
||||||
|
this.Slots.Remove(slot);
|
||||||
|
|
||||||
|
if (saveChanges) await this.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
#nullable disable
|
||||||
}
|
}
|
|
@ -2,23 +2,22 @@ using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse
|
namespace LBPUnion.ProjectLighthouse;
|
||||||
|
|
||||||
|
public class FakeRemoteIPAddressMiddleware
|
||||||
{
|
{
|
||||||
public class FakeRemoteIPAddressMiddleware
|
private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.0.0.1");
|
||||||
|
private readonly RequestDelegate next;
|
||||||
|
|
||||||
|
public FakeRemoteIPAddressMiddleware(RequestDelegate next)
|
||||||
{
|
{
|
||||||
private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.0.0.1");
|
this.next = next;
|
||||||
private readonly RequestDelegate next;
|
}
|
||||||
|
|
||||||
public FakeRemoteIPAddressMiddleware(RequestDelegate next)
|
public async Task Invoke(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
this.next = next;
|
httpContext.Connection.RemoteIpAddress = this.fakeIpAddress;
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Invoke(HttpContext httpContext)
|
await this.next(httpContext);
|
||||||
{
|
|
||||||
httpContext.Connection.RemoteIpAddress = this.fakeIpAddress;
|
|
||||||
|
|
||||||
await this.next(httpContext);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,60 +6,59 @@ using System.IO.Pipelines;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class BinaryHelper
|
||||||
{
|
{
|
||||||
public static class BinaryHelper
|
public static string ReadString(BinaryReader reader)
|
||||||
{
|
{
|
||||||
public static string ReadString(BinaryReader reader)
|
List<byte> readBytes = new();
|
||||||
|
|
||||||
|
byte readByte;
|
||||||
|
do readBytes.Add(readByte = reader.ReadByte());
|
||||||
|
while (readByte != 0x00);
|
||||||
|
|
||||||
|
return Encoding.UTF8.GetString(readBytes.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReadUntilByte(BinaryReader reader, byte byteToReadTo)
|
||||||
|
{
|
||||||
|
byte readByte;
|
||||||
|
do readByte = reader.ReadByte();
|
||||||
|
while (readByte != byteToReadTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] ReadLastBytes(BinaryReader reader, int count, bool restoreOldPosition = true)
|
||||||
|
{
|
||||||
|
long oldPosition = reader.BaseStream.Position;
|
||||||
|
|
||||||
|
if (reader.BaseStream.Length < count) return Array.Empty<byte>();
|
||||||
|
|
||||||
|
reader.BaseStream.Position = reader.BaseStream.Length - count;
|
||||||
|
byte[] data = reader.ReadBytes(count);
|
||||||
|
|
||||||
|
if (restoreOldPosition) reader.BaseStream.Position = oldPosition;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Written with reference from
|
||||||
|
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-response?view=aspnetcore-5.0
|
||||||
|
// Surprisingly doesn't take seconds. (67ms for a 100kb file)
|
||||||
|
public static async Task<byte[]> ReadFromPipeReader(PipeReader reader)
|
||||||
|
{
|
||||||
|
List<byte> data = new();
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
List<byte> readBytes = new();
|
ReadResult readResult = await reader.ReadAsync();
|
||||||
|
ReadOnlySequence<byte> buffer = readResult.Buffer;
|
||||||
|
|
||||||
byte readByte;
|
if (readResult.IsCompleted && buffer.Length > 0) data.AddRange(buffer.ToArray());
|
||||||
do readBytes.Add(readByte = reader.ReadByte());
|
|
||||||
while (readByte != 0x00);
|
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(readBytes.ToArray());
|
reader.AdvanceTo(buffer.Start, buffer.End);
|
||||||
|
|
||||||
|
if (readResult.IsCompleted) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ReadUntilByte(BinaryReader reader, byte byteToReadTo)
|
return data.ToArray();
|
||||||
{
|
|
||||||
byte readByte;
|
|
||||||
do readByte = reader.ReadByte();
|
|
||||||
while (readByte != byteToReadTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] ReadLastBytes(BinaryReader reader, int count, bool restoreOldPosition = true)
|
|
||||||
{
|
|
||||||
long oldPosition = reader.BaseStream.Position;
|
|
||||||
|
|
||||||
if (reader.BaseStream.Length < count) return Array.Empty<byte>();
|
|
||||||
|
|
||||||
reader.BaseStream.Position = reader.BaseStream.Length - count;
|
|
||||||
byte[] data = reader.ReadBytes(count);
|
|
||||||
|
|
||||||
if (restoreOldPosition) reader.BaseStream.Position = oldPosition;
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Written with reference from
|
|
||||||
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/request-response?view=aspnetcore-5.0
|
|
||||||
// Surprisingly doesn't take seconds. (67ms for a 100kb file)
|
|
||||||
public static async Task<byte[]> ReadFromPipeReader(PipeReader reader)
|
|
||||||
{
|
|
||||||
List<byte> data = new();
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ReadResult readResult = await reader.ReadAsync();
|
|
||||||
ReadOnlySequence<byte> buffer = readResult.Buffer;
|
|
||||||
|
|
||||||
if (readResult.IsCompleted && buffer.Length > 0) data.AddRange(buffer.ToArray());
|
|
||||||
|
|
||||||
reader.AdvanceTo(buffer.Start, buffer.End);
|
|
||||||
|
|
||||||
if (readResult.IsCompleted) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.ToArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,21 +1,20 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Categories;
|
using LBPUnion.ProjectLighthouse.Types.Categories;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class CollectionHelper
|
||||||
{
|
{
|
||||||
public static class CollectionHelper
|
public static readonly List<Category> Categories = new();
|
||||||
|
|
||||||
|
static CollectionHelper()
|
||||||
{
|
{
|
||||||
public static readonly List<Category> Categories = new();
|
Categories.Add(new TeamPicksCategory());
|
||||||
|
Categories.Add(new NewestLevelsCategory());
|
||||||
|
Categories.Add(new QueueCategory());
|
||||||
|
Categories.Add(new HeartedCategory());
|
||||||
|
|
||||||
static CollectionHelper()
|
using Database database = new();
|
||||||
{
|
foreach (DatabaseCategory category in database.CustomCategories) Categories.Add(new CustomCategory(category));
|
||||||
Categories.Add(new TeamPicksCategory());
|
|
||||||
Categories.Add(new NewestLevelsCategory());
|
|
||||||
Categories.Add(new QueueCategory());
|
|
||||||
Categories.Add(new HeartedCategory());
|
|
||||||
|
|
||||||
using Database database = new();
|
|
||||||
foreach (DatabaseCategory category in database.CustomCategories) Categories.Add(new CustomCategory(category));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,38 +1,37 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class DeniedAuthenticationHelper
|
||||||
{
|
{
|
||||||
public static class DeniedAuthenticationHelper
|
public static readonly Dictionary<string, long> IPAddressAndNameDeniedAt = new();
|
||||||
|
public static readonly Dictionary<string, int> AttemptsByIPAddressAndName = new();
|
||||||
|
|
||||||
|
public static void SetDeniedAt(string ipAddressAndName, long timestamp = 0)
|
||||||
{
|
{
|
||||||
public static readonly Dictionary<string, long> IPAddressAndNameDeniedAt = new();
|
if (timestamp == 0) timestamp = TimestampHelper.Timestamp;
|
||||||
public static readonly Dictionary<string, int> AttemptsByIPAddressAndName = new();
|
|
||||||
|
|
||||||
public static void SetDeniedAt(string ipAddressAndName, long timestamp = 0)
|
if (IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long _)) IPAddressAndNameDeniedAt.Remove(ipAddressAndName);
|
||||||
{
|
IPAddressAndNameDeniedAt.Add(ipAddressAndName, timestamp);
|
||||||
if (timestamp == 0) timestamp = TimestampHelper.Timestamp;
|
}
|
||||||
|
|
||||||
if (IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long _)) IPAddressAndNameDeniedAt.Remove(ipAddressAndName);
|
public static bool RecentlyDenied(string ipAddressAndName)
|
||||||
IPAddressAndNameDeniedAt.Add(ipAddressAndName, timestamp);
|
{
|
||||||
}
|
if (!IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long timestamp)) return false;
|
||||||
|
|
||||||
public static bool RecentlyDenied(string ipAddressAndName)
|
return TimestampHelper.Timestamp < timestamp + 300;
|
||||||
{
|
}
|
||||||
if (!IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long timestamp)) return false;
|
|
||||||
|
|
||||||
return TimestampHelper.Timestamp < timestamp + 300;
|
public static void AddAttempt(string ipAddressAndName)
|
||||||
}
|
{
|
||||||
|
if (AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) AttemptsByIPAddressAndName.Remove(ipAddressAndName);
|
||||||
|
AttemptsByIPAddressAndName.Add(ipAddressAndName, attempts + 1);
|
||||||
|
}
|
||||||
|
|
||||||
public static void AddAttempt(string ipAddressAndName)
|
public static int GetAttempts(string ipAddressAndName)
|
||||||
{
|
{
|
||||||
if (AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) AttemptsByIPAddressAndName.Remove(ipAddressAndName);
|
if (!AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) return 0;
|
||||||
AttemptsByIPAddressAndName.Add(ipAddressAndName, attempts + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetAttempts(string ipAddressAndName)
|
return attempts;
|
||||||
{
|
|
||||||
if (!AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) return 0;
|
|
||||||
|
|
||||||
return attempts;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class EulaHelper
|
||||||
{
|
{
|
||||||
public static class EulaHelper
|
public const string License = @"
|
||||||
{
|
|
||||||
public const string License = @"
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as
|
it under the terms of the GNU Affero General Public License as
|
||||||
published by the Free Software Foundation, either version 3 of the
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
@ -15,5 +15,4 @@ GNU Affero General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU Affero General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -3,26 +3,25 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers.Extensions
|
namespace LBPUnion.ProjectLighthouse.Helpers.Extensions;
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/8039737
|
||||||
|
public static class ExceptionExtensions
|
||||||
{
|
{
|
||||||
// https://stackoverflow.com/a/8039737
|
public static string ToDetailedException(this Exception exception)
|
||||||
public static class ExceptionExtensions
|
|
||||||
{
|
{
|
||||||
public static string ToDetailedException(this Exception exception)
|
PropertyInfo[] properties = exception.GetType().GetProperties();
|
||||||
{
|
|
||||||
PropertyInfo[] properties = exception.GetType().GetProperties();
|
|
||||||
|
|
||||||
IEnumerable<string> fields = properties.Select
|
IEnumerable<string> fields = properties.Select
|
||||||
(
|
(
|
||||||
property => new
|
property => new
|
||||||
{
|
{
|
||||||
property.Name,
|
property.Name,
|
||||||
Value = property.GetValue(exception, null),
|
Value = property.GetValue(exception, null),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.Select(x => $"{x.Name} = {(x.Value != null ? x.Value.ToString() : string.Empty)}");
|
.Select(x => $"{x.Name} = {(x.Value != null ? x.Value.ToString() : string.Empty)}");
|
||||||
|
|
||||||
return string.Join("\n", fields);
|
return string.Join("\n", fields);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,14 +2,13 @@ using System.Text.RegularExpressions;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers.Extensions
|
namespace LBPUnion.ProjectLighthouse.Helpers.Extensions;
|
||||||
{
|
|
||||||
// yoinked and adapted from https://stackoverflow.com/a/68641796
|
|
||||||
public static class RequestExtensions
|
|
||||||
{
|
|
||||||
private static readonly Regex mobileCheck = new
|
|
||||||
("Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled);
|
|
||||||
|
|
||||||
public static bool IsMobile(this HttpRequest request) => mobileCheck.IsMatch(request.Headers[HeaderNames.UserAgent].ToString());
|
// yoinked and adapted from https://stackoverflow.com/a/68641796
|
||||||
}
|
public static class RequestExtensions
|
||||||
|
{
|
||||||
|
private static readonly Regex mobileCheck = new
|
||||||
|
("Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Compiled);
|
||||||
|
|
||||||
|
public static bool IsMobile(this HttpRequest request) => mobileCheck.IsMatch(request.Headers[HeaderNames.UserAgent].ToString());
|
||||||
}
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers.Extensions
|
namespace LBPUnion.ProjectLighthouse.Helpers.Extensions;
|
||||||
|
|
||||||
|
public static class StringExtensions
|
||||||
{
|
{
|
||||||
public static class StringExtensions
|
public static string ToFileName(this string text) => Path.GetInvalidFileNameChars().Aggregate(text, (current, c) => current.Replace(c.ToString(), ""));
|
||||||
{
|
|
||||||
public static string ToFileName(this string text) => Path.GetInvalidFileNameChars().Aggregate(text, (current, c) => current.Replace(c.ToString(), ""));
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -5,99 +5,98 @@ using System.Text;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Files;
|
using LBPUnion.ProjectLighthouse.Types.Files;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class FileHelper
|
||||||
{
|
{
|
||||||
public static class FileHelper
|
public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r");
|
||||||
|
|
||||||
|
public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash);
|
||||||
|
|
||||||
|
public static bool IsFileSafe(LbpFile file)
|
||||||
{
|
{
|
||||||
public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r");
|
if (!ServerSettings.Instance.CheckForUnsafeFiles) return true;
|
||||||
|
|
||||||
public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash);
|
if (file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data);
|
||||||
|
|
||||||
public static bool IsFileSafe(LbpFile file)
|
return file.FileType switch
|
||||||
{
|
{
|
||||||
if (!ServerSettings.Instance.CheckForUnsafeFiles) return true;
|
LbpFileType.FileArchive => false,
|
||||||
|
LbpFileType.Painting => true,
|
||||||
if (file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data);
|
LbpFileType.Unknown => false,
|
||||||
|
LbpFileType.Texture => true,
|
||||||
return file.FileType switch
|
LbpFileType.Script => false,
|
||||||
{
|
LbpFileType.Level => true,
|
||||||
LbpFileType.FileArchive => false,
|
LbpFileType.Voice => true,
|
||||||
LbpFileType.Painting => true,
|
LbpFileType.Plan => true,
|
||||||
LbpFileType.Unknown => false,
|
LbpFileType.Jpeg => true,
|
||||||
LbpFileType.Texture => true,
|
LbpFileType.Png => true,
|
||||||
LbpFileType.Script => false,
|
#if DEBUG
|
||||||
LbpFileType.Level => true,
|
|
||||||
LbpFileType.Voice => true,
|
|
||||||
LbpFileType.Plan => true,
|
|
||||||
LbpFileType.Jpeg => true,
|
|
||||||
LbpFileType.Png => true,
|
|
||||||
#if DEBUG
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(file), $"Unhandled file type ({file.FileType}) in FileHelper.IsFileSafe()"),
|
_ => throw new ArgumentOutOfRangeException(nameof(file), $"Unhandled file type ({file.FileType}) in FileHelper.IsFileSafe()"),
|
||||||
#else
|
#else
|
||||||
_ => false,
|
_ => false,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
public static LbpFileType DetermineFileType(byte[] data)
|
|
||||||
{
|
|
||||||
if (data.Length == 0) return LbpFileType.Unknown; // Can't be anything if theres no data.
|
|
||||||
|
|
||||||
using MemoryStream ms = new(data);
|
|
||||||
using BinaryReader reader = new(ms);
|
|
||||||
|
|
||||||
// Determine if file is a FARC (File Archive).
|
|
||||||
// Needs to be done before anything else that determines the type by the header
|
|
||||||
// because this determines the type by the footer.
|
|
||||||
string footer = Encoding.ASCII.GetString(BinaryHelper.ReadLastBytes(reader, 4));
|
|
||||||
if (footer == "FARC") return LbpFileType.FileArchive;
|
|
||||||
|
|
||||||
byte[] header = reader.ReadBytes(3);
|
|
||||||
|
|
||||||
return Encoding.ASCII.GetString(header) switch
|
|
||||||
{
|
|
||||||
"PTG" => LbpFileType.Painting,
|
|
||||||
"TEX" => LbpFileType.Texture,
|
|
||||||
"FSH" => LbpFileType.Script,
|
|
||||||
"VOP" => LbpFileType.Voice,
|
|
||||||
"LVL" => LbpFileType.Level,
|
|
||||||
"PLN" => LbpFileType.Plan,
|
|
||||||
_ => determineFileTypePartTwoWeirdName(reader),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LbpFileType determineFileTypePartTwoWeirdName(BinaryReader reader)
|
|
||||||
{
|
|
||||||
reader.BaseStream.Position = 0;
|
|
||||||
|
|
||||||
// Determine if file is JPEG/PNG
|
|
||||||
byte[] header = reader.ReadBytes(9);
|
|
||||||
|
|
||||||
if (header[0] == 0xFF && header[1] == 0xD8 && header[2] == 0xFF && header[3] == 0xE0) return LbpFileType.Jpeg;
|
|
||||||
if (header[0] == 0x89 && header[1] == 0x50 && header[2] == 0x4E && header[3] == 0x47) return LbpFileType.Png;
|
|
||||||
|
|
||||||
return LbpFileType.Unknown; // Still unknown.
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash));
|
|
||||||
|
|
||||||
public static int ResourceSize(string hash)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return (int)new FileInfo(GetResourcePath(hash)).Length;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EnsureDirectoryCreated(string path)
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string[] ResourcesNotUploaded(params string[] hashes) => hashes.Where(hash => !ResourceExists(hash)).ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LbpFileType DetermineFileType(byte[] data)
|
||||||
|
{
|
||||||
|
if (data.Length == 0) return LbpFileType.Unknown; // Can't be anything if theres no data.
|
||||||
|
|
||||||
|
using MemoryStream ms = new(data);
|
||||||
|
using BinaryReader reader = new(ms);
|
||||||
|
|
||||||
|
// Determine if file is a FARC (File Archive).
|
||||||
|
// Needs to be done before anything else that determines the type by the header
|
||||||
|
// because this determines the type by the footer.
|
||||||
|
string footer = Encoding.ASCII.GetString(BinaryHelper.ReadLastBytes(reader, 4));
|
||||||
|
if (footer == "FARC") return LbpFileType.FileArchive;
|
||||||
|
|
||||||
|
byte[] header = reader.ReadBytes(3);
|
||||||
|
|
||||||
|
return Encoding.ASCII.GetString(header) switch
|
||||||
|
{
|
||||||
|
"PTG" => LbpFileType.Painting,
|
||||||
|
"TEX" => LbpFileType.Texture,
|
||||||
|
"FSH" => LbpFileType.Script,
|
||||||
|
"VOP" => LbpFileType.Voice,
|
||||||
|
"LVL" => LbpFileType.Level,
|
||||||
|
"PLN" => LbpFileType.Plan,
|
||||||
|
_ => determineFileTypePartTwoWeirdName(reader),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LbpFileType determineFileTypePartTwoWeirdName(BinaryReader reader)
|
||||||
|
{
|
||||||
|
reader.BaseStream.Position = 0;
|
||||||
|
|
||||||
|
// Determine if file is JPEG/PNG
|
||||||
|
byte[] header = reader.ReadBytes(9);
|
||||||
|
|
||||||
|
if (header[0] == 0xFF && header[1] == 0xD8 && header[2] == 0xFF && header[3] == 0xE0) return LbpFileType.Jpeg;
|
||||||
|
if (header[0] == 0x89 && header[1] == 0x50 && header[2] == 0x4E && header[3] == 0x47) return LbpFileType.Png;
|
||||||
|
|
||||||
|
return LbpFileType.Unknown; // Still unknown.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash));
|
||||||
|
|
||||||
|
public static int ResourceSize(string hash)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (int)new FileInfo(GetResourcePath(hash)).Length;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnsureDirectoryCreated(string path)
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string[] ResourcesNotUploaded(params string[] hashes) => hashes.Where(hash => !ResourceExists(hash)).ToArray();
|
||||||
}
|
}
|
|
@ -2,13 +2,12 @@ using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")]
|
||||||
|
public static class FriendHelper
|
||||||
{
|
{
|
||||||
[NotMapped]
|
public static readonly Dictionary<int, int[]> FriendIdsByUserId = new();
|
||||||
[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")]
|
public static readonly Dictionary<int, int[]> BlockedIdsByUserId = new();
|
||||||
public static class FriendHelper
|
|
||||||
{
|
|
||||||
public static readonly Dictionary<int, int[]> FriendIdsByUserId = new();
|
|
||||||
public static readonly Dictionary<int, int[]> BlockedIdsByUserId = new();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,102 +1,101 @@
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public class GameVersionHelper
|
||||||
{
|
{
|
||||||
public class GameVersionHelper
|
// https://www.serialstation.com/games/b89b4eb4-4e4c-4e54-b72b-f7f9dbfac125
|
||||||
|
public static readonly string[] LittleBigPlanet1TitleIds =
|
||||||
{
|
{
|
||||||
// https://www.serialstation.com/games/b89b4eb4-4e4c-4e54-b72b-f7f9dbfac125
|
"BCES00141",
|
||||||
public static readonly string[] LittleBigPlanet1TitleIds =
|
"BCAS20091",
|
||||||
{
|
"BCUS98208",
|
||||||
"BCES00141",
|
"BCAS20078",
|
||||||
"BCAS20091",
|
"BCJS70009",
|
||||||
"BCUS98208",
|
"BCES00611",
|
||||||
"BCAS20078",
|
"BCUS98148",
|
||||||
"BCJS70009",
|
"BCAS20058",
|
||||||
"BCES00611",
|
"BCJS30018",
|
||||||
"BCUS98148",
|
"UCAS40262",
|
||||||
"BCAS20058",
|
"BCET70011",
|
||||||
"BCJS30018",
|
"BCUS98199",
|
||||||
"UCAS40262",
|
"BCJB95003",
|
||||||
"BCET70011",
|
"NPUA70045",
|
||||||
"BCUS98199",
|
"NPEA00241",
|
||||||
"BCJB95003",
|
"NPEA00147",
|
||||||
"NPUA70045",
|
"NPHG00033",
|
||||||
"NPEA00241",
|
"NPHG00035",
|
||||||
"NPEA00147",
|
};
|
||||||
"NPHG00033",
|
|
||||||
"NPHG00035",
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://serialstation.com/games/35e69aba-1872-4fd7-9d39-11ce75924040
|
// https://serialstation.com/games/35e69aba-1872-4fd7-9d39-11ce75924040
|
||||||
public static readonly string[] LittleBigPlanet2TitleIds =
|
public static readonly string[] LittleBigPlanet2TitleIds =
|
||||||
{
|
{
|
||||||
"BCUS98249",
|
"BCUS98249",
|
||||||
"BCES01086",
|
"BCES01086",
|
||||||
"BCAS20113",
|
"BCAS20113",
|
||||||
"BCJS70024",
|
"BCJS70024",
|
||||||
"BCAS20201",
|
"BCAS20201",
|
||||||
"BCUS98245",
|
"BCUS98245",
|
||||||
"BCES01345",
|
"BCES01345",
|
||||||
"BCJS30058",
|
"BCJS30058",
|
||||||
"BCUS98372",
|
"BCUS98372",
|
||||||
"BCES00850",
|
"BCES00850",
|
||||||
"BCES01346",
|
"BCES01346",
|
||||||
"BCUS90260",
|
"BCUS90260",
|
||||||
"BCET70023",
|
"BCET70023",
|
||||||
"BCES01694",
|
"BCES01694",
|
||||||
"NPUA80662",
|
"NPUA80662",
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://www.serialstation.com/games/b62d53d9-fdff-4463-8134-64b81e1cbd50
|
// https://www.serialstation.com/games/b62d53d9-fdff-4463-8134-64b81e1cbd50
|
||||||
// includes PS4 games
|
// includes PS4 games
|
||||||
public static readonly string[] LittleBigPlanet3TitleIds =
|
public static readonly string[] LittleBigPlanet3TitleIds =
|
||||||
{
|
{
|
||||||
"CUSA00063",
|
"CUSA00063",
|
||||||
"CUSA00693",
|
"CUSA00693",
|
||||||
"CUSA00473",
|
"CUSA00473",
|
||||||
"CUSA00810",
|
"CUSA00810",
|
||||||
"CUSA00473",
|
"CUSA00473",
|
||||||
"CUSA01072",
|
"CUSA01072",
|
||||||
"CUSA00738",
|
"CUSA00738",
|
||||||
"PCJS50003",
|
"PCJS50003",
|
||||||
"BCES02068",
|
"BCES02068",
|
||||||
"BCAS20322",
|
"BCAS20322",
|
||||||
"BCJS30095",
|
"BCJS30095",
|
||||||
"BCES01663",
|
"BCES01663",
|
||||||
"CUSA00063",
|
"CUSA00063",
|
||||||
"BCUS98362",
|
"BCUS98362",
|
||||||
"PCKS90007",
|
"PCKS90007",
|
||||||
"PCAS00012",
|
"PCAS00012",
|
||||||
"CUSA00601",
|
"CUSA00601",
|
||||||
"CUSA00810",
|
"CUSA00810",
|
||||||
"CUSA00762",
|
"CUSA00762",
|
||||||
"PCAS20007",
|
"PCAS20007",
|
||||||
"CUSA00473",
|
"CUSA00473",
|
||||||
"CUSA01077",
|
"CUSA01077",
|
||||||
"CUSA01304",
|
"CUSA01304",
|
||||||
"NPUA81116",
|
"NPUA81116",
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly string[] LittleBigPlanetVitaTitleIds =
|
public static readonly string[] LittleBigPlanetVitaTitleIds =
|
||||||
{
|
{
|
||||||
"PCSF00021", "PCSA00017", "PCSC00013", "PCSD00006", "PCSA00549", "PCSF00516",
|
"PCSF00021", "PCSA00017", "PCSC00013", "PCSD00006", "PCSA00549", "PCSF00516",
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly string[] LittleBigPlanetPSPTitleIds =
|
public static readonly string[] LittleBigPlanetPSPTitleIds =
|
||||||
{
|
{
|
||||||
"NPWR00500", "UCAS40262", "UCES01264", "UCUS98744", "UCJS10107",
|
"NPWR00500", "UCAS40262", "UCES01264", "UCUS98744", "UCJS10107",
|
||||||
};
|
};
|
||||||
|
|
||||||
public static GameVersion FromTitleId(string titleId)
|
public static GameVersion FromTitleId(string titleId)
|
||||||
{
|
{
|
||||||
if (LittleBigPlanet1TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet1;
|
if (LittleBigPlanet1TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet1;
|
||||||
if (LittleBigPlanet2TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet2;
|
if (LittleBigPlanet2TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet2;
|
||||||
if (LittleBigPlanet3TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet3;
|
if (LittleBigPlanet3TitleIds.Contains(titleId)) return GameVersion.LittleBigPlanet3;
|
||||||
if (LittleBigPlanetVitaTitleIds.Contains(titleId)) return GameVersion.LittleBigPlanetVita;
|
if (LittleBigPlanetVitaTitleIds.Contains(titleId)) return GameVersion.LittleBigPlanetVita;
|
||||||
if (LittleBigPlanetPSPTitleIds.Contains(titleId)) return GameVersion.LittleBigPlanetPSP;
|
if (LittleBigPlanetPSPTitleIds.Contains(titleId)) return GameVersion.LittleBigPlanetPSP;
|
||||||
|
|
||||||
return GameVersion.LittleBigPlanet1;
|
return GameVersion.LittleBigPlanet1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,78 +6,77 @@ using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
|
public static class HashHelper
|
||||||
{
|
{
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
private static readonly SHA1 sha1 = SHA1.Create();
|
||||||
public static class HashHelper
|
private static readonly SHA256 sha256 = SHA256.Create();
|
||||||
|
private static readonly Random random = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a specified amount of random bytes in an array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The amount of bytes to generate.</param>
|
||||||
|
/// <returns>The bytes generated</returns>
|
||||||
|
public static IEnumerable<byte> GenerateRandomBytes(int count)
|
||||||
{
|
{
|
||||||
private static readonly SHA1 sha1 = SHA1.Create();
|
byte[] b = new byte[count];
|
||||||
private static readonly SHA256 sha256 = SHA256.Create();
|
random.NextBytes(b);
|
||||||
private static readonly Random random = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a specified amount of random bytes in an array.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="count">The amount of bytes to generate.</param>
|
|
||||||
/// <returns>The bytes generated</returns>
|
|
||||||
public static IEnumerable<byte> GenerateRandomBytes(int count)
|
|
||||||
{
|
|
||||||
byte[] b = new byte[count];
|
|
||||||
random.NextBytes(b);
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates a random SHA256 & BCrypted token
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The token as a string.</returns>
|
|
||||||
public static string GenerateAuthToken()
|
|
||||||
{
|
|
||||||
byte[] bytes = (byte[])GenerateRandomBytes(256);
|
|
||||||
|
|
||||||
return BCryptHash(Sha256Hash(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<string> ComputeDigest(string path, string authCookie, Stream body, string digestKey)
|
|
||||||
{
|
|
||||||
MemoryStream memoryStream = new();
|
|
||||||
|
|
||||||
byte[] pathBytes = Encoding.UTF8.GetBytes(path);
|
|
||||||
byte[] cookieBytes = string.IsNullOrEmpty(authCookie) ? Array.Empty<byte>() : Encoding.UTF8.GetBytes(authCookie);
|
|
||||||
byte[] keyBytes = Encoding.UTF8.GetBytes(digestKey);
|
|
||||||
|
|
||||||
await body.CopyToAsync(memoryStream);
|
|
||||||
|
|
||||||
byte[] bodyBytes = memoryStream.ToArray();
|
|
||||||
|
|
||||||
using IncrementalHash sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
|
|
||||||
sha1.AppendData(bodyBytes);
|
|
||||||
if (cookieBytes.Length > 0) sha1.AppendData(cookieBytes);
|
|
||||||
sha1.AppendData(pathBytes);
|
|
||||||
sha1.AppendData(keyBytes);
|
|
||||||
|
|
||||||
byte[] digestBytes = sha1.GetHashAndReset();
|
|
||||||
string digestString = Convert.ToHexString(digestBytes).ToLower();
|
|
||||||
|
|
||||||
return digestString;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Hash Functions
|
|
||||||
|
|
||||||
public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str));
|
|
||||||
|
|
||||||
public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", "").ToLower();
|
|
||||||
|
|
||||||
public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str));
|
|
||||||
|
|
||||||
public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(sha1.ComputeHash(bytes)).Replace("-", "");
|
|
||||||
|
|
||||||
public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str);
|
|
||||||
|
|
||||||
public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes));
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a random SHA256 & BCrypted token
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The token as a string.</returns>
|
||||||
|
public static string GenerateAuthToken()
|
||||||
|
{
|
||||||
|
byte[] bytes = (byte[])GenerateRandomBytes(256);
|
||||||
|
|
||||||
|
return BCryptHash(Sha256Hash(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<string> ComputeDigest(string path, string authCookie, Stream body, string digestKey)
|
||||||
|
{
|
||||||
|
MemoryStream memoryStream = new();
|
||||||
|
|
||||||
|
byte[] pathBytes = Encoding.UTF8.GetBytes(path);
|
||||||
|
byte[] cookieBytes = string.IsNullOrEmpty(authCookie) ? Array.Empty<byte>() : Encoding.UTF8.GetBytes(authCookie);
|
||||||
|
byte[] keyBytes = Encoding.UTF8.GetBytes(digestKey);
|
||||||
|
|
||||||
|
await body.CopyToAsync(memoryStream);
|
||||||
|
|
||||||
|
byte[] bodyBytes = memoryStream.ToArray();
|
||||||
|
|
||||||
|
using IncrementalHash sha1 = IncrementalHash.CreateHash(HashAlgorithmName.SHA1);
|
||||||
|
sha1.AppendData(bodyBytes);
|
||||||
|
if (cookieBytes.Length > 0) sha1.AppendData(cookieBytes);
|
||||||
|
sha1.AppendData(pathBytes);
|
||||||
|
sha1.AppendData(keyBytes);
|
||||||
|
|
||||||
|
byte[] digestBytes = sha1.GetHashAndReset();
|
||||||
|
string digestString = Convert.ToHexString(digestBytes).ToLower();
|
||||||
|
|
||||||
|
return digestString;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Hash Functions
|
||||||
|
|
||||||
|
public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str));
|
||||||
|
|
||||||
|
public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", "").ToLower();
|
||||||
|
|
||||||
|
public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str));
|
||||||
|
|
||||||
|
public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(sha1.ComputeHash(bytes)).Replace("-", "");
|
||||||
|
|
||||||
|
public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str);
|
||||||
|
|
||||||
|
public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
|
@ -6,45 +6,44 @@ using Kettu;
|
||||||
using LBPUnion.ProjectLighthouse.Logging;
|
using LBPUnion.ProjectLighthouse.Logging;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class InfluxHelper
|
||||||
{
|
{
|
||||||
public static class InfluxHelper
|
public static readonly InfluxDBClient Client = InfluxDBClientFactory.Create(ServerSettings.Instance.InfluxUrl, ServerSettings.Instance.InfluxToken);
|
||||||
|
|
||||||
|
public static async void Log()
|
||||||
{
|
{
|
||||||
public static readonly InfluxDBClient Client = InfluxDBClientFactory.Create(ServerSettings.Instance.InfluxUrl, ServerSettings.Instance.InfluxToken);
|
using WriteApi writeApi = Client.GetWriteApi();
|
||||||
|
PointData point = PointData.Measurement("lighthouse")
|
||||||
|
.Field("playerCount", await StatisticsHelper.RecentMatches())
|
||||||
|
.Field("slotCount", await StatisticsHelper.SlotCount());
|
||||||
|
|
||||||
public static async void Log()
|
writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point);
|
||||||
{
|
|
||||||
using WriteApi writeApi = Client.GetWriteApi();
|
|
||||||
PointData point = PointData.Measurement("lighthouse")
|
|
||||||
.Field("playerCount", await StatisticsHelper.RecentMatches())
|
|
||||||
.Field("slotCount", await StatisticsHelper.SlotCount());
|
|
||||||
|
|
||||||
writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point);
|
writeApi.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
writeApi.Flush();
|
public static async Task StartLogging()
|
||||||
}
|
{
|
||||||
|
await Client.ReadyAsync();
|
||||||
public static async Task StartLogging()
|
Logger.Log("InfluxDB is now ready.", LoggerLevelInflux.Instance);
|
||||||
{
|
Thread t = new
|
||||||
await Client.ReadyAsync();
|
(
|
||||||
Logger.Log("InfluxDB is now ready.", LoggerLevelInflux.Instance);
|
delegate()
|
||||||
Thread t = new
|
{
|
||||||
(
|
while (true)
|
||||||
delegate()
|
|
||||||
{
|
{
|
||||||
while (true)
|
#pragma warning disable CS4014
|
||||||
{
|
Log();
|
||||||
#pragma warning disable CS4014
|
#pragma warning restore CS4014
|
||||||
Log();
|
|
||||||
#pragma warning restore CS4014
|
|
||||||
// Logger.Log("Logged.", LoggerLevelInflux.Instance);
|
// Logger.Log("Logged.", LoggerLevelInflux.Instance);
|
||||||
Thread.Sleep(60000);
|
Thread.Sleep(60000);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
t.IsBackground = true;
|
);
|
||||||
t.Name = "InfluxDB Logger";
|
t.IsBackground = true;
|
||||||
t.Start();
|
t.Name = "InfluxDB Logger";
|
||||||
}
|
t.Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,31 +5,30 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class LastContactHelper
|
||||||
{
|
{
|
||||||
public static class LastContactHelper
|
private static readonly Database database = new();
|
||||||
|
|
||||||
|
public static async Task SetLastContact(User user, GameVersion gameVersion)
|
||||||
{
|
{
|
||||||
private static readonly Database database = new();
|
LastContact? lastContact = await database.LastContacts.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync();
|
||||||
|
|
||||||
public static async Task SetLastContact(User user, GameVersion gameVersion)
|
// below makes it not look like trash
|
||||||
|
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression
|
||||||
|
if (lastContact == null)
|
||||||
{
|
{
|
||||||
LastContact? lastContact = await database.LastContacts.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync();
|
lastContact = new LastContact
|
||||||
|
|
||||||
// below makes it not look like trash
|
|
||||||
// ReSharper disable once ConvertIfStatementToNullCoalescingExpression
|
|
||||||
if (lastContact == null)
|
|
||||||
{
|
{
|
||||||
lastContact = new LastContact
|
UserId = user.UserId,
|
||||||
{
|
};
|
||||||
UserId = user.UserId,
|
database.LastContacts.Add(lastContact);
|
||||||
};
|
|
||||||
database.LastContacts.Add(lastContact);
|
|
||||||
}
|
|
||||||
|
|
||||||
lastContact.Timestamp = TimestampHelper.Timestamp;
|
|
||||||
lastContact.GameVersion = gameVersion;
|
|
||||||
|
|
||||||
await database.SaveChangesAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastContact.Timestamp = TimestampHelper.Timestamp;
|
||||||
|
lastContact.GameVersion = gameVersion;
|
||||||
|
|
||||||
|
await database.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,65 +6,61 @@ using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LBPUnion.ProjectLighthouse.Maintenance;
|
using LBPUnion.ProjectLighthouse.Maintenance;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class MaintenanceHelper
|
||||||
{
|
{
|
||||||
public static class MaintenanceHelper
|
|
||||||
|
static MaintenanceHelper()
|
||||||
{
|
{
|
||||||
|
Commands = getListOfInterfaceObjects<ICommand>();
|
||||||
|
MaintenanceJobs = getListOfInterfaceObjects<IMaintenanceJob>();
|
||||||
|
}
|
||||||
|
public static List<ICommand> Commands { get; }
|
||||||
|
|
||||||
static MaintenanceHelper()
|
public static List<IMaintenanceJob> MaintenanceJobs { get; }
|
||||||
|
|
||||||
|
private static List<T> getListOfInterfaceObjects<T>() where T : class
|
||||||
|
{
|
||||||
|
return Assembly.GetExecutingAssembly()
|
||||||
|
.GetTypes()
|
||||||
|
.Where(t => t.GetInterfaces().Contains(typeof(T)) && t.GetConstructor(Type.EmptyTypes) != null)
|
||||||
|
.Select(t => Activator.CreateInstance(t) as T)
|
||||||
|
.ToList()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task RunCommand(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 1)
|
||||||
|
throw new Exception
|
||||||
|
("This should never happen. " + "If it did, its because you tried to run a command before validating that the user actually wants to run one.");
|
||||||
|
|
||||||
|
string baseCmd = args[0];
|
||||||
|
args = args.Skip(1).ToArray();
|
||||||
|
|
||||||
|
IEnumerable<ICommand> suitableCommands = Commands.Where
|
||||||
|
(command => command.Aliases().Any(a => a.ToLower() == baseCmd.ToLower()))
|
||||||
|
.Where(command => args.Length >= command.RequiredArgs());
|
||||||
|
foreach (ICommand command in suitableCommands)
|
||||||
{
|
{
|
||||||
Commands = getListOfInterfaceObjects<ICommand>();
|
Console.WriteLine("Running command " + command.Name());
|
||||||
MaintenanceJobs = getListOfInterfaceObjects<IMaintenanceJob>();
|
await command.Run(args);
|
||||||
}
|
return;
|
||||||
public static List<ICommand> Commands { get; }
|
|
||||||
|
|
||||||
public static List<IMaintenanceJob> MaintenanceJobs { get; }
|
|
||||||
|
|
||||||
private static List<T> getListOfInterfaceObjects<T>() where T : class
|
|
||||||
{
|
|
||||||
return Assembly.GetExecutingAssembly()
|
|
||||||
.GetTypes()
|
|
||||||
.Where(t => t.GetInterfaces().Contains(typeof(T)) && t.GetConstructor(Type.EmptyTypes) != null)
|
|
||||||
.Select(t => Activator.CreateInstance(t) as T)
|
|
||||||
.ToList()!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task RunCommand(string[] args)
|
Console.WriteLine("Command not found.");
|
||||||
{
|
}
|
||||||
if (args.Length < 1)
|
|
||||||
throw new Exception
|
|
||||||
(
|
|
||||||
"This should never happen. " +
|
|
||||||
"If it did, its because you tried to run a command before validating that the user actually wants to run one."
|
|
||||||
);
|
|
||||||
|
|
||||||
string baseCmd = args[0];
|
public static async Task RunMaintenanceJob(string jobName)
|
||||||
args = args.Skip(1).ToArray();
|
{
|
||||||
|
IMaintenanceJob? job = MaintenanceJobs.FirstOrDefault(j => j.GetType().Name == jobName);
|
||||||
|
if (job == null) throw new ArgumentNullException();
|
||||||
|
|
||||||
IEnumerable<ICommand> suitableCommands = Commands.Where
|
await RunMaintenanceJob(job);
|
||||||
(command => command.Aliases().Any(a => a.ToLower() == baseCmd.ToLower()))
|
}
|
||||||
.Where(command => args.Length >= command.RequiredArgs());
|
|
||||||
foreach (ICommand command in suitableCommands)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Running command " + command.Name());
|
|
||||||
await command.Run(args);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("Command not found.");
|
public static async Task RunMaintenanceJob(IMaintenanceJob job)
|
||||||
}
|
{
|
||||||
|
await job.Run();
|
||||||
public static async Task RunMaintenanceJob(string jobName)
|
|
||||||
{
|
|
||||||
IMaintenanceJob? job = MaintenanceJobs.FirstOrDefault(j => j.GetType().Name == jobName);
|
|
||||||
if (job == null) throw new ArgumentNullException();
|
|
||||||
|
|
||||||
await RunMaintenanceJob(job);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task RunMaintenanceJob(IMaintenanceJob job)
|
|
||||||
{
|
|
||||||
await job.Run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,70 +7,69 @@ using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Match;
|
using LBPUnion.ProjectLighthouse.Types.Match;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class MatchHelper
|
||||||
{
|
{
|
||||||
public static class MatchHelper
|
public static readonly Dictionary<int, string?> UserLocations = new();
|
||||||
|
public static readonly Dictionary<int, List<int>?> UserRecentlyDivedIn = new();
|
||||||
|
|
||||||
|
public static void SetUserLocation(int userId, string location)
|
||||||
{
|
{
|
||||||
public static readonly Dictionary<int, string?> UserLocations = new();
|
if (UserLocations.TryGetValue(userId, out string? _)) UserLocations.Remove(userId);
|
||||||
public static readonly Dictionary<int, List<int>?> UserRecentlyDivedIn = new();
|
UserLocations.Add(userId, location);
|
||||||
|
}
|
||||||
|
|
||||||
public static void SetUserLocation(int userId, string location)
|
public static void AddUserRecentlyDivedIn(int userId, int otherUserId)
|
||||||
|
{
|
||||||
|
if (!UserRecentlyDivedIn.TryGetValue(userId, out List<int>? recentlyDivedIn)) UserRecentlyDivedIn.Add(userId, recentlyDivedIn = new List<int>());
|
||||||
|
|
||||||
|
Debug.Assert(recentlyDivedIn != null, nameof(recentlyDivedIn) + " is null, somehow.");
|
||||||
|
|
||||||
|
recentlyDivedIn.Add(otherUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool DidUserRecentlyDiveInWith(int userId, int otherUserId)
|
||||||
|
{
|
||||||
|
if (!UserRecentlyDivedIn.TryGetValue(userId, out List<int>? recentlyDivedIn) || recentlyDivedIn == null) return false;
|
||||||
|
|
||||||
|
return recentlyDivedIn.Contains(otherUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the function used to show people how laughably awful LBP's protocol is. Beware.
|
||||||
|
public static IMatchData? Deserialize(string data)
|
||||||
|
{
|
||||||
|
string matchType = "";
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
if (UserLocations.TryGetValue(userId, out string? _)) UserLocations.Remove(userId);
|
if (data[i] == ',') break;
|
||||||
UserLocations.Add(userId, location);
|
|
||||||
|
matchType += data[i];
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddUserRecentlyDivedIn(int userId, int otherUserId)
|
string matchData = $"{{{string.Concat(data.Skip(matchType.Length + 3).SkipLast(2))}}}"; // unfuck formatting so we can parse it as json
|
||||||
|
|
||||||
|
// JSON does not like the hex value that location comes in (0x7f000001) so, convert it to int
|
||||||
|
matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{8}", m => Convert.ToInt32(m.Value, 16).ToString());
|
||||||
|
// oh, but it gets better than that! LBP also likes to send hex values with an uneven amount of digits (0xa000064, 7 digits). in any case, we handle it here:
|
||||||
|
matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{7}", m => Convert.ToInt32(m.Value, 16).ToString());
|
||||||
|
// i'm actually crying about it.
|
||||||
|
|
||||||
|
return Deserialize(matchType, matchData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IMatchData? Deserialize(string matchType, string matchData)
|
||||||
|
{
|
||||||
|
return matchType switch
|
||||||
{
|
{
|
||||||
if (!UserRecentlyDivedIn.TryGetValue(userId, out List<int>? recentlyDivedIn)) UserRecentlyDivedIn.Add(userId, recentlyDivedIn = new List<int>());
|
"UpdateMyPlayerData" => JsonSerializer.Deserialize<UpdateMyPlayerData>(matchData),
|
||||||
|
"UpdatePlayersInRoom" => JsonSerializer.Deserialize<UpdatePlayersInRoom>(matchData),
|
||||||
Debug.Assert(recentlyDivedIn != null, nameof(recentlyDivedIn) + " is null, somehow.");
|
"CreateRoom" => JsonSerializer.Deserialize<CreateRoom>(matchData),
|
||||||
|
"FindBestRoom" => JsonSerializer.Deserialize<FindBestRoom>(matchData),
|
||||||
recentlyDivedIn.Add(otherUserId);
|
_ => null,
|
||||||
}
|
};
|
||||||
|
|
||||||
public static bool DidUserRecentlyDiveInWith(int userId, int otherUserId)
|
|
||||||
{
|
|
||||||
if (!UserRecentlyDivedIn.TryGetValue(userId, out List<int>? recentlyDivedIn) || recentlyDivedIn == null) return false;
|
|
||||||
|
|
||||||
return recentlyDivedIn.Contains(otherUserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the function used to show people how laughably awful LBP's protocol is. Beware.
|
|
||||||
public static IMatchData? Deserialize(string data)
|
|
||||||
{
|
|
||||||
string matchType = "";
|
|
||||||
|
|
||||||
int i = 1;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (data[i] == ',') break;
|
|
||||||
|
|
||||||
matchType += data[i];
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
string matchData = $"{{{string.Concat(data.Skip(matchType.Length + 3).SkipLast(2))}}}"; // unfuck formatting so we can parse it as json
|
|
||||||
|
|
||||||
// JSON does not like the hex value that location comes in (0x7f000001) so, convert it to int
|
|
||||||
matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{8}", m => Convert.ToInt32(m.Value, 16).ToString());
|
|
||||||
// oh, but it gets better than that! LBP also likes to send hex values with an uneven amount of digits (0xa000064, 7 digits). in any case, we handle it here:
|
|
||||||
matchData = Regex.Replace(matchData, @"0x[a-fA-F0-9]{7}", m => Convert.ToInt32(m.Value, 16).ToString());
|
|
||||||
// i'm actually crying about it.
|
|
||||||
|
|
||||||
return Deserialize(matchType, matchData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMatchData? Deserialize(string matchType, string matchData)
|
|
||||||
{
|
|
||||||
return matchType switch
|
|
||||||
{
|
|
||||||
"UpdateMyPlayerData" => JsonSerializer.Deserialize<UpdateMyPlayerData>(matchData),
|
|
||||||
"UpdatePlayersInRoom" => JsonSerializer.Deserialize<UpdatePlayersInRoom>(matchData),
|
|
||||||
"CreateRoom" => JsonSerializer.Deserialize<CreateRoom>(matchData),
|
|
||||||
"FindBestRoom" => JsonSerializer.Deserialize<FindBestRoom>(matchData),
|
|
||||||
_ => null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,184 +8,177 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Match;
|
using LBPUnion.ProjectLighthouse.Types.Match;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public class RoomHelper
|
||||||
{
|
{
|
||||||
public class RoomHelper
|
public static readonly List<Room> Rooms = new();
|
||||||
|
|
||||||
|
public static readonly RoomSlot PodSlot = new()
|
||||||
{
|
{
|
||||||
public static readonly List<Room> Rooms = new();
|
SlotType = SlotType.Pod,
|
||||||
|
SlotId = 0,
|
||||||
|
};
|
||||||
|
|
||||||
public static readonly RoomSlot PodSlot = new()
|
private static int roomIdIncrement;
|
||||||
|
|
||||||
|
internal static int RoomIdIncrement => roomIdIncrement++;
|
||||||
|
|
||||||
|
public static FindBestRoomResponse? FindBestRoom(User? user, GameVersion roomVersion, string? location)
|
||||||
|
{
|
||||||
|
if (roomVersion == GameVersion.LittleBigPlanet1 || roomVersion == GameVersion.LittleBigPlanetPSP)
|
||||||
{
|
{
|
||||||
SlotType = SlotType.Pod,
|
Logger.Log($"Returning null for FindBestRoom, game ({roomVersion}) does not support dive in", LoggerLevelMatch.Instance);
|
||||||
SlotId = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
private static int roomIdIncrement;
|
|
||||||
|
|
||||||
internal static int RoomIdIncrement => roomIdIncrement++;
|
|
||||||
|
|
||||||
public static FindBestRoomResponse? FindBestRoom(User? user, GameVersion roomVersion, string? location)
|
|
||||||
{
|
|
||||||
if (roomVersion == GameVersion.LittleBigPlanet1 || roomVersion == GameVersion.LittleBigPlanetPSP)
|
|
||||||
{
|
|
||||||
Logger.Log($"Returning null for FindBestRoom, game ({roomVersion}) does not support dive in", LoggerLevelMatch.Instance);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool anyRoomsLookingForPlayers;
|
|
||||||
List<Room> rooms;
|
|
||||||
|
|
||||||
lock(Rooms)
|
|
||||||
{
|
|
||||||
anyRoomsLookingForPlayers = Rooms.Any(r => r.IsLookingForPlayers);
|
|
||||||
rooms = anyRoomsLookingForPlayers ? Rooms.Where(r => anyRoomsLookingForPlayers && r.IsLookingForPlayers).ToList() : Rooms;
|
|
||||||
}
|
|
||||||
|
|
||||||
rooms = rooms.Where(r => r.RoomVersion == roomVersion).ToList();
|
|
||||||
|
|
||||||
foreach (Room room in rooms)
|
|
||||||
// Look for rooms looking for players before moving on to rooms that are idle.
|
|
||||||
{
|
|
||||||
if (user != null && MatchHelper.DidUserRecentlyDiveInWith(user.UserId, room.Host.UserId)) continue;
|
|
||||||
|
|
||||||
Dictionary<int, string> relevantUserLocations = new();
|
|
||||||
|
|
||||||
// Determine if all players in a room have UserLocations stored, also store the relevant userlocations while we're at it
|
|
||||||
bool allPlayersHaveLocations = room.Players.All
|
|
||||||
(
|
|
||||||
p =>
|
|
||||||
{
|
|
||||||
bool gotValue = MatchHelper.UserLocations.TryGetValue(p.UserId, out string? value);
|
|
||||||
|
|
||||||
if (gotValue && value != null) relevantUserLocations.Add(p.UserId, value);
|
|
||||||
return gotValue;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// If we don't have all locations then the game won't know how to communicate. Thus, it's not a valid room.
|
|
||||||
if (!allPlayersHaveLocations) continue;
|
|
||||||
|
|
||||||
// If we got here then it should be a valid room.
|
|
||||||
|
|
||||||
FindBestRoomResponse response = new();
|
|
||||||
response.RoomId = room.RoomId;
|
|
||||||
|
|
||||||
response.Players = new List<Player>();
|
|
||||||
response.Locations = new List<string>();
|
|
||||||
foreach (User player in room.Players)
|
|
||||||
{
|
|
||||||
response.Players.Add
|
|
||||||
(
|
|
||||||
new Player
|
|
||||||
{
|
|
||||||
MatchingRes = 0,
|
|
||||||
User = player,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
response.Locations.Add(relevantUserLocations.GetValueOrDefault(player.UserId)); // Already validated to exist
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
response.Players.Add
|
|
||||||
(
|
|
||||||
new Player
|
|
||||||
{
|
|
||||||
MatchingRes = 1,
|
|
||||||
User = user,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location == null)
|
|
||||||
{
|
|
||||||
response.Locations.Add(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
response.Slots = new List<List<int>>
|
|
||||||
{
|
|
||||||
new()
|
|
||||||
{
|
|
||||||
(int)room.Slot.SlotType,
|
|
||||||
room.Slot.SlotId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Logger.Log($"Found a room (id: {room.RoomId}) for user {user?.Username ?? "null"} (id: {user?.UserId ?? -1})", LoggerLevelMatch.Instance);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Room CreateRoom(User user, GameVersion roomVersion, RoomSlot? slot = null)
|
bool anyRoomsLookingForPlayers;
|
||||||
=> CreateRoom
|
List<Room> rooms;
|
||||||
(
|
|
||||||
new List<User>
|
lock(Rooms)
|
||||||
{
|
|
||||||
user,
|
|
||||||
},
|
|
||||||
roomVersion,
|
|
||||||
slot
|
|
||||||
);
|
|
||||||
public static Room CreateRoom(List<User> users, GameVersion roomVersion, RoomSlot? slot = null)
|
|
||||||
{
|
{
|
||||||
Room room = new()
|
anyRoomsLookingForPlayers = Rooms.Any(r => r.IsLookingForPlayers);
|
||||||
|
rooms = anyRoomsLookingForPlayers ? Rooms.Where(r => anyRoomsLookingForPlayers && r.IsLookingForPlayers).ToList() : Rooms;
|
||||||
|
}
|
||||||
|
|
||||||
|
rooms = rooms.Where(r => r.RoomVersion == roomVersion).ToList();
|
||||||
|
|
||||||
|
foreach (Room room in rooms)
|
||||||
|
// Look for rooms looking for players before moving on to rooms that are idle.
|
||||||
|
{
|
||||||
|
if (user != null && MatchHelper.DidUserRecentlyDiveInWith(user.UserId, room.Host.UserId)) continue;
|
||||||
|
|
||||||
|
Dictionary<int, string> relevantUserLocations = new();
|
||||||
|
|
||||||
|
// Determine if all players in a room have UserLocations stored, also store the relevant userlocations while we're at it
|
||||||
|
bool allPlayersHaveLocations = room.Players.All
|
||||||
|
(
|
||||||
|
p =>
|
||||||
|
{
|
||||||
|
bool gotValue = MatchHelper.UserLocations.TryGetValue(p.UserId, out string? value);
|
||||||
|
|
||||||
|
if (gotValue && value != null) relevantUserLocations.Add(p.UserId, value);
|
||||||
|
return gotValue;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// If we don't have all locations then the game won't know how to communicate. Thus, it's not a valid room.
|
||||||
|
if (!allPlayersHaveLocations) continue;
|
||||||
|
|
||||||
|
// If we got here then it should be a valid room.
|
||||||
|
|
||||||
|
FindBestRoomResponse response = new();
|
||||||
|
response.RoomId = room.RoomId;
|
||||||
|
|
||||||
|
response.Players = new List<Player>();
|
||||||
|
response.Locations = new List<string>();
|
||||||
|
foreach (User player in room.Players)
|
||||||
{
|
{
|
||||||
RoomId = RoomIdIncrement,
|
response.Players.Add
|
||||||
Players = users,
|
(
|
||||||
State = RoomState.Idle,
|
new Player
|
||||||
Slot = slot ?? PodSlot,
|
{
|
||||||
RoomVersion = roomVersion,
|
MatchingRes = 0,
|
||||||
|
User = player,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
response.Locations.Add(relevantUserLocations.GetValueOrDefault(player.UserId)); // Already validated to exist
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user != null)
|
||||||
|
response.Players.Add
|
||||||
|
(
|
||||||
|
new Player
|
||||||
|
{
|
||||||
|
MatchingRes = 1,
|
||||||
|
User = user,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (location == null) response.Locations.Add(location);
|
||||||
|
|
||||||
|
response.Slots = new List<List<int>>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
(int)room.Slot.SlotType,
|
||||||
|
room.Slot.SlotId,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
CleanupRooms(room.Host, room);
|
Logger.Log($"Found a room (id: {room.RoomId}) for user {user?.Username ?? "null"} (id: {user?.UserId ?? -1})", LoggerLevelMatch.Instance);
|
||||||
lock(Rooms) Rooms.Add(room);
|
|
||||||
Logger.Log($"Created room (id: {room.RoomId}) for host {room.Host.Username} (id: {room.Host.UserId})", LoggerLevelMatch.Instance);
|
|
||||||
|
|
||||||
return room;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Room? FindRoomByUser(User user, GameVersion roomVersion, bool createIfDoesNotExist = false)
|
return null;
|
||||||
{
|
}
|
||||||
lock(Rooms)
|
|
||||||
|
public static Room CreateRoom(User user, GameVersion roomVersion, RoomSlot? slot = null)
|
||||||
|
=> CreateRoom
|
||||||
|
(
|
||||||
|
new List<User>
|
||||||
{
|
{
|
||||||
foreach (Room room in Rooms.Where(room => room.Players.Any(player => user == player))) return room;
|
user,
|
||||||
}
|
},
|
||||||
|
roomVersion,
|
||||||
return createIfDoesNotExist ? CreateRoom(user, roomVersion) : null;
|
slot
|
||||||
}
|
);
|
||||||
|
public static Room CreateRoom(List<User> users, GameVersion roomVersion, RoomSlot? slot = null)
|
||||||
[SuppressMessage("ReSharper", "InvertIf")]
|
{
|
||||||
public static void CleanupRooms(User? host = null, Room? newRoom = null)
|
Room room = new()
|
||||||
{
|
{
|
||||||
lock(Rooms)
|
RoomId = RoomIdIncrement,
|
||||||
{
|
Players = users,
|
||||||
// Delete old rooms based on host
|
State = RoomState.Idle,
|
||||||
if (host != null)
|
Slot = slot ?? PodSlot,
|
||||||
try
|
RoomVersion = roomVersion,
|
||||||
{
|
};
|
||||||
Rooms.RemoveAll(r => r.Host == host);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// TODO: detect the room that failed and remove it
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove players in this new room from other rooms
|
CleanupRooms(room.Host, room);
|
||||||
if (newRoom != null)
|
lock(Rooms) Rooms.Add(room);
|
||||||
foreach (Room room in Rooms)
|
Logger.Log($"Created room (id: {room.RoomId}) for host {room.Host.Username} (id: {room.Host.UserId})", LoggerLevelMatch.Instance);
|
||||||
{
|
|
||||||
if (room == newRoom) continue;
|
|
||||||
|
|
||||||
foreach (User newRoomPlayer in newRoom.Players) room.Players.RemoveAll(p => p == newRoomPlayer);
|
return room;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rooms.RemoveAll(r => r.Players.Count == 0); // Remove empty rooms
|
public static Room? FindRoomByUser(User user, GameVersion roomVersion, bool createIfDoesNotExist = false)
|
||||||
Rooms.RemoveAll(r => r.Players.Count > 4); // Remove obviously bogus rooms
|
{
|
||||||
}
|
lock(Rooms)
|
||||||
|
foreach (Room room in Rooms.Where(room => room.Players.Any(player => user == player)))
|
||||||
|
return room;
|
||||||
|
|
||||||
|
return createIfDoesNotExist ? CreateRoom(user, roomVersion) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "InvertIf")]
|
||||||
|
public static void CleanupRooms(User? host = null, Room? newRoom = null)
|
||||||
|
{
|
||||||
|
lock(Rooms)
|
||||||
|
{
|
||||||
|
// Delete old rooms based on host
|
||||||
|
if (host != null)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Rooms.RemoveAll(r => r.Host == host);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// TODO: detect the room that failed and remove it
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove players in this new room from other rooms
|
||||||
|
if (newRoom != null)
|
||||||
|
foreach (Room room in Rooms)
|
||||||
|
{
|
||||||
|
if (room == newRoom) continue;
|
||||||
|
|
||||||
|
foreach (User newRoomPlayer in newRoom.Players) room.Players.RemoveAll(p => p == newRoomPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rooms.RemoveAll(r => r.Players.Count == 0); // Remove empty rooms
|
||||||
|
Rooms.RemoveAll(r => r.Players.Count > 4); // Remove obviously bogus rooms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,18 +2,17 @@ using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class StatisticsHelper
|
||||||
{
|
{
|
||||||
public static class StatisticsHelper
|
private static readonly Database database = new();
|
||||||
{
|
|
||||||
private static readonly Database database = new();
|
|
||||||
|
|
||||||
public static async Task<int> RecentMatches() => await database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync();
|
public static async Task<int> RecentMatches() => await database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync();
|
||||||
|
|
||||||
public static async Task<int> SlotCount() => await database.Slots.CountAsync();
|
public static async Task<int> SlotCount() => await database.Slots.CountAsync();
|
||||||
|
|
||||||
public static async Task<int> MMPicksCount() => await database.Slots.CountAsync(s => s.TeamPick);
|
public static async Task<int> MMPicksCount() => await database.Slots.CountAsync(s => s.TeamPick);
|
||||||
|
|
||||||
public static async Task<int> PhotoCount() => await database.Photos.CountAsync();
|
public static async Task<int> PhotoCount() => await database.Photos.CountAsync();
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class TimeHelper
|
||||||
{
|
{
|
||||||
public static class TimeHelper
|
public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||||
{
|
public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds();
|
||||||
public static long UnixTimeMilliseconds() => DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
|
||||||
public static long UnixTimeSeconds() => DateTimeOffset.Now.ToUnixTimeSeconds();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1397109686193
|
// 1397109686193
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
{
|
|
||||||
public static class TimestampHelper
|
|
||||||
{
|
|
||||||
public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
|
||||||
|
|
||||||
public static long TimestampMillis => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
|
public static class TimestampHelper
|
||||||
}
|
{
|
||||||
|
public static long Timestamp => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
||||||
|
|
||||||
|
public static long TimestampMillis => (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalMilliseconds;
|
||||||
}
|
}
|
|
@ -5,79 +5,78 @@ using Kettu;
|
||||||
using LBPUnion.ProjectLighthouse.Logging;
|
using LBPUnion.ProjectLighthouse.Logging;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Helpers
|
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||||
|
|
||||||
|
public static class VersionHelper
|
||||||
{
|
{
|
||||||
public static class VersionHelper
|
static VersionHelper()
|
||||||
{
|
{
|
||||||
static VersionHelper()
|
try
|
||||||
{
|
{
|
||||||
try
|
CommitHash = readManifestFile("gitVersion.txt");
|
||||||
{
|
Branch = readManifestFile("gitBranch.txt");
|
||||||
CommitHash = readManifestFile("gitVersion.txt");
|
|
||||||
Branch = readManifestFile("gitBranch.txt");
|
|
||||||
|
|
||||||
string remotesFile = readManifestFile("gitRemotes.txt");
|
string remotesFile = readManifestFile("gitRemotes.txt");
|
||||||
|
|
||||||
string[] lines = remotesFile.Split('\n');
|
string[] lines = remotesFile.Split('\n');
|
||||||
|
|
||||||
// line[0] line[1] line[2]
|
// line[0] line[1] line[2]
|
||||||
// origin git@github.com:LBPUnion/project-lighthouse.git (fetch)
|
// origin git@github.com:LBPUnion/project-lighthouse.git (fetch)
|
||||||
|
|
||||||
// linq is a serious and painful catastrophe but its useful so i'm gonna keep using it
|
// linq is a serious and painful catastrophe but its useful so i'm gonna keep using it
|
||||||
Remotes = lines.Select(line => line.Split("\t")[1]).ToArray();
|
Remotes = lines.Select(line => line.Split("\t")[1]).ToArray();
|
||||||
|
|
||||||
CommitsOutOfDate = readManifestFile("gitUnpushed.txt").Split('\n').Length;
|
CommitsOutOfDate = readManifestFile("gitUnpushed.txt").Split('\n').Length;
|
||||||
|
|
||||||
CanCheckForUpdates = true;
|
CanCheckForUpdates = true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
Logger.Log
|
Logger.Log
|
||||||
(
|
(
|
||||||
"Project Lighthouse was built incorrectly. Please make sure git is available when building. " +
|
"Project Lighthouse was built incorrectly. Please make sure git is available when building. " +
|
||||||
"Because of this, you will not be notified of updates.",
|
"Because of this, you will not be notified of updates.",
|
||||||
LoggerLevelStartup.Instance
|
LoggerLevelStartup.Instance
|
||||||
);
|
);
|
||||||
CommitHash = "invalid";
|
CommitHash = "invalid";
|
||||||
Branch = "invalid";
|
Branch = "invalid";
|
||||||
CanCheckForUpdates = false;
|
CanCheckForUpdates = false;
|
||||||
}
|
|
||||||
|
|
||||||
if (IsDirty)
|
|
||||||
{
|
|
||||||
Logger.Log
|
|
||||||
(
|
|
||||||
"This is a modified version of Project Lighthouse. " +
|
|
||||||
"Please make sure you are properly disclosing the source code to any users who may be using this instance.",
|
|
||||||
LoggerLevelStartup.Instance
|
|
||||||
);
|
|
||||||
CanCheckForUpdates = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string readManifestFile(string fileName)
|
if (IsDirty)
|
||||||
{
|
{
|
||||||
using Stream stream = typeof(Program).Assembly.GetManifestResourceStream($"{typeof(Program).Namespace}.{fileName}");
|
Logger.Log
|
||||||
using StreamReader reader = new(stream ?? throw new Exception("The assembly or manifest resource is null."));
|
(
|
||||||
|
"This is a modified version of Project Lighthouse. " +
|
||||||
return reader.ReadToEnd().Trim();
|
"Please make sure you are properly disclosing the source code to any users who may be using this instance.",
|
||||||
|
LoggerLevelStartup.Instance
|
||||||
|
);
|
||||||
|
CanCheckForUpdates = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string CommitHash { get; set; }
|
|
||||||
public static string Branch { get; set; }
|
|
||||||
public static string FullVersion => $"{ServerStatics.ServerName} {Branch}@{CommitHash} {Build}";
|
|
||||||
public static bool IsDirty => CommitHash.EndsWith("-dirty") || CommitsOutOfDate != 1 || CommitHash == "invalid" || Branch == "invalid";
|
|
||||||
public static int CommitsOutOfDate { get; set; }
|
|
||||||
public static bool CanCheckForUpdates { get; set; }
|
|
||||||
public static string[] Remotes { get; set; }
|
|
||||||
|
|
||||||
public const string Build =
|
|
||||||
#if DEBUG
|
|
||||||
"Debug";
|
|
||||||
#elif RELEASE
|
|
||||||
"Release";
|
|
||||||
#else
|
|
||||||
"Unknown";
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string readManifestFile(string fileName)
|
||||||
|
{
|
||||||
|
using Stream stream = typeof(Program).Assembly.GetManifestResourceStream($"{typeof(Program).Namespace}.{fileName}");
|
||||||
|
using StreamReader reader = new(stream ?? throw new Exception("The assembly or manifest resource is null."));
|
||||||
|
|
||||||
|
return reader.ReadToEnd().Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CommitHash { get; set; }
|
||||||
|
public static string Branch { get; set; }
|
||||||
|
public static string FullVersion => $"{ServerStatics.ServerName} {Branch}@{CommitHash} {Build}";
|
||||||
|
public static bool IsDirty => CommitHash.EndsWith("-dirty") || CommitsOutOfDate != 1 || CommitHash == "invalid" || Branch == "invalid";
|
||||||
|
public static int CommitsOutOfDate { get; set; }
|
||||||
|
public static bool CanCheckForUpdates { get; set; }
|
||||||
|
public static string[] Remotes { get; set; }
|
||||||
|
|
||||||
|
public const string Build =
|
||||||
|
#if DEBUG
|
||||||
|
"Debug";
|
||||||
|
#elif RELEASE
|
||||||
|
"Release";
|
||||||
|
#else
|
||||||
|
"Unknown";
|
||||||
|
#endif
|
||||||
}
|
}
|
|
@ -3,22 +3,21 @@ using Kettu;
|
||||||
using LBPUnion.ProjectLighthouse.Helpers.Extensions;
|
using LBPUnion.ProjectLighthouse.Helpers.Extensions;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Logging
|
namespace LBPUnion.ProjectLighthouse.Logging;
|
||||||
|
|
||||||
|
public class AspNetToKettuLogger : ILogger
|
||||||
{
|
{
|
||||||
public class AspNetToKettuLogger : ILogger
|
public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;
|
||||||
|
public bool IsEnabled(LogLevel logLevel) => true;
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||||
{
|
{
|
||||||
public IDisposable BeginScope<TState>(TState state) => NullScope.Instance;
|
LoggerLevel loggerLevel = new LoggerLevelAspNet(logLevel);
|
||||||
public bool IsEnabled(LogLevel logLevel) => true;
|
|
||||||
|
|
||||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
Logger.Log(state.ToString(), loggerLevel);
|
||||||
{
|
if (exception == null) return;
|
||||||
LoggerLevel loggerLevel = new LoggerLevelAspNet(logLevel);
|
|
||||||
|
|
||||||
Logger.Log(state.ToString(), loggerLevel);
|
string[] lines = exception.ToDetailedException().Replace("\r", "").Split("\n");
|
||||||
if (exception == null) return;
|
foreach (string line in lines) Logger.Log(line, loggerLevel);
|
||||||
|
|
||||||
string[] lines = exception.ToDetailedException().Replace("\r", "").Split("\n");
|
|
||||||
foreach (string line in lines) Logger.Log(line, loggerLevel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,16 +1,15 @@
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Logging
|
namespace LBPUnion.ProjectLighthouse.Logging;
|
||||||
{
|
|
||||||
[ProviderAlias("Kettu")]
|
|
||||||
public class AspNetToKettuLoggerProvider : ILoggerProvider, IDisposable
|
|
||||||
{
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ILogger CreateLogger(string categoryName) => new AspNetToKettuLogger();
|
[ProviderAlias("Kettu")]
|
||||||
|
public class AspNetToKettuLoggerProvider : ILoggerProvider, IDisposable
|
||||||
|
{
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ILogger CreateLogger(string categoryName) => new AspNetToKettuLogger();
|
||||||
}
|
}
|
|
@ -4,24 +4,23 @@ using Kettu;
|
||||||
using LBPUnion.ProjectLighthouse.Helpers;
|
using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Settings;
|
using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Logging
|
namespace LBPUnion.ProjectLighthouse.Logging;
|
||||||
|
|
||||||
|
public class InfluxLogger : LoggerBase
|
||||||
{
|
{
|
||||||
public class InfluxLogger : LoggerBase
|
public override bool AllowMultiple => false;
|
||||||
|
|
||||||
|
public override void Send(LoggerLine line)
|
||||||
{
|
{
|
||||||
public override bool AllowMultiple => false;
|
string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] ";
|
||||||
|
|
||||||
public override void Send(LoggerLine line)
|
string level = $"{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}";
|
||||||
{
|
string content = line.LineData;
|
||||||
string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] ";
|
|
||||||
|
|
||||||
string level = $"{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}";
|
using WriteApi writeApi = InfluxHelper.Client.GetWriteApi();
|
||||||
string content = line.LineData;
|
|
||||||
|
|
||||||
using WriteApi writeApi = InfluxHelper.Client.GetWriteApi();
|
PointData point = PointData.Measurement("lighthouseLog").Field("level", level).Field("content", content);
|
||||||
|
|
||||||
PointData point = PointData.Measurement("lighthouseLog").Field("level", level).Field("content", content);
|
writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point);
|
||||||
|
|
||||||
writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,29 +4,28 @@ using Kettu;
|
||||||
using LBPUnion.ProjectLighthouse.Helpers;
|
using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
using LBPUnion.ProjectLighthouse.Helpers.Extensions;
|
using LBPUnion.ProjectLighthouse.Helpers.Extensions;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Logging
|
namespace LBPUnion.ProjectLighthouse.Logging;
|
||||||
|
|
||||||
|
public class LighthouseFileLogger : LoggerBase
|
||||||
{
|
{
|
||||||
public class LighthouseFileLogger : LoggerBase
|
private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs");
|
||||||
|
public override bool AllowMultiple => false;
|
||||||
|
|
||||||
|
public override void Send(LoggerLine line)
|
||||||
{
|
{
|
||||||
private static readonly string logsDirectory = Path.Combine(Environment.CurrentDirectory, "logs");
|
FileHelper.EnsureDirectoryCreated(logsDirectory);
|
||||||
public override bool AllowMultiple => false;
|
|
||||||
|
|
||||||
public override void Send(LoggerLine line)
|
string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] ";
|
||||||
|
|
||||||
|
string contentFile = $"{channel}{line.LineData}\n";
|
||||||
|
string contentAll = $"[{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}] {line.LineData}\n";
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
FileHelper.EnsureDirectoryCreated(logsDirectory);
|
File.AppendAllText(Path.Combine(logsDirectory, line.LoggerLevel.Name.ToFileName() + ".log"), contentFile);
|
||||||
|
File.AppendAllText(Path.Combine(logsDirectory, "all.log"), contentAll);
|
||||||
string channel = string.IsNullOrEmpty(line.LoggerLevel.Channel) ? "" : $"[{line.LoggerLevel.Channel}] ";
|
|
||||||
|
|
||||||
string contentFile = $"{channel}{line.LineData}\n";
|
|
||||||
string contentAll = $"[{$"{line.LoggerLevel.Name} {channel}".TrimEnd()}] {line.LineData}\n";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File.AppendAllText(Path.Combine(logsDirectory, line.LoggerLevel.Name.ToFileName() + ".log"), contentFile);
|
|
||||||
File.AppendAllText(Path.Combine(logsDirectory, "all.log"), contentAll);
|
|
||||||
}
|
|
||||||
catch(IOException) {} // windows, ya goofed
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
catch(IOException) {} // windows, ya goofed
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,81 +1,80 @@
|
||||||
using Kettu;
|
using Kettu;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Logging
|
namespace LBPUnion.ProjectLighthouse.Logging;
|
||||||
|
|
||||||
|
public class LoggerLevelStartup : LoggerLevel
|
||||||
{
|
{
|
||||||
public class LoggerLevelStartup : LoggerLevel
|
public static readonly LoggerLevelStartup Instance = new();
|
||||||
{
|
public override string Name => "Startup";
|
||||||
public static readonly LoggerLevelStartup Instance = new();
|
}
|
||||||
public override string Name => "Startup";
|
|
||||||
}
|
public class LoggerLevelDatabase : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelDatabase : LoggerLevel
|
public static readonly LoggerLevelDatabase Instance = new();
|
||||||
{
|
public override string Name => "Database";
|
||||||
public static readonly LoggerLevelDatabase Instance = new();
|
}
|
||||||
public override string Name => "Database";
|
|
||||||
}
|
public class LoggerLevelHttp : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelHttp : LoggerLevel
|
public static readonly LoggerLevelHttp Instance = new();
|
||||||
{
|
public override string Name => "HTTP";
|
||||||
public static readonly LoggerLevelHttp Instance = new();
|
}
|
||||||
public override string Name => "HTTP";
|
|
||||||
}
|
public class LoggerLevelFilter : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelFilter : LoggerLevel
|
public static readonly LoggerLevelFilter Instance = new();
|
||||||
{
|
public override string Name => "Filter";
|
||||||
public static readonly LoggerLevelFilter Instance = new();
|
}
|
||||||
public override string Name => "Filter";
|
|
||||||
}
|
public class LoggerLevelLogin : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelLogin : LoggerLevel
|
public static readonly LoggerLevelLogin Instance = new();
|
||||||
{
|
public override string Name => "Login";
|
||||||
public static readonly LoggerLevelLogin Instance = new();
|
}
|
||||||
public override string Name => "Login";
|
|
||||||
}
|
public class LoggerLevelResources : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelResources : LoggerLevel
|
public static readonly LoggerLevelResources Instance = new();
|
||||||
{
|
public override string Name => "Resources";
|
||||||
public static readonly LoggerLevelResources Instance = new();
|
}
|
||||||
public override string Name => "Resources";
|
|
||||||
}
|
public class LoggerLevelMatch : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelMatch : LoggerLevel
|
public static readonly LoggerLevelMatch Instance = new();
|
||||||
{
|
public override string Name => "Match";
|
||||||
public static readonly LoggerLevelMatch Instance = new();
|
}
|
||||||
public override string Name => "Match";
|
|
||||||
}
|
public class LoggerLevelPhotos : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelPhotos : LoggerLevel
|
public static readonly LoggerLevelPhotos Instance = new();
|
||||||
{
|
public override string Name => "Photos";
|
||||||
public static readonly LoggerLevelPhotos Instance = new();
|
}
|
||||||
public override string Name => "Photos";
|
|
||||||
}
|
public class LoggerLevelConfig : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelConfig : LoggerLevel
|
public static readonly LoggerLevelConfig Instance = new();
|
||||||
{
|
public override string Name => "Config";
|
||||||
public static readonly LoggerLevelConfig Instance = new();
|
}
|
||||||
public override string Name => "Config";
|
|
||||||
}
|
public class LoggerLevelInflux : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelInflux : LoggerLevel
|
public static readonly LoggerLevelInflux Instance = new();
|
||||||
{
|
public override string Name => "Influx";
|
||||||
public static readonly LoggerLevelInflux Instance = new();
|
}
|
||||||
public override string Name => "Influx";
|
|
||||||
}
|
public class LoggerLevelAspNet : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelAspNet : LoggerLevel
|
|
||||||
{
|
public LoggerLevelAspNet(LogLevel level)
|
||||||
|
{
|
||||||
public LoggerLevelAspNet(LogLevel level)
|
this.Channel = level.ToString();
|
||||||
{
|
}
|
||||||
this.Channel = level.ToString();
|
public override string Name => "AspNet";
|
||||||
}
|
}
|
||||||
public override string Name => "AspNet";
|
|
||||||
}
|
public class LoggerLevelCategory : LoggerLevel
|
||||||
|
{
|
||||||
public class LoggerLevelCategory : LoggerLevel
|
public static readonly LoggerLevelCategory Instance = new();
|
||||||
{
|
public override string Name => "Category";
|
||||||
public static readonly LoggerLevelCategory Instance = new();
|
|
||||||
public override string Name => "Category";
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,17 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Logging
|
namespace LBPUnion.ProjectLighthouse.Logging;
|
||||||
|
|
||||||
|
public class NullScope : IDisposable
|
||||||
{
|
{
|
||||||
public class NullScope : IDisposable
|
|
||||||
|
private NullScope()
|
||||||
|
{}
|
||||||
|
public static NullScope Instance { get; } = new();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
private NullScope()
|
|
||||||
{}
|
|
||||||
public static NullScope Instance { get; } = new();
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,48 +7,47 @@ using LBPUnion.ProjectLighthouse.Logging;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
|
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class CreateUserCommand : ICommand
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
private readonly Database _database = new();
|
||||||
public class CreateUserCommand : ICommand
|
|
||||||
|
public async Task Run(string[] args)
|
||||||
{
|
{
|
||||||
private readonly Database _database = new();
|
string onlineId = args[0];
|
||||||
|
string password = args[1];
|
||||||
|
|
||||||
public async Task Run(string[] args)
|
password = HashHelper.Sha256Hash(password);
|
||||||
|
|
||||||
|
User? user = await this._database.Users.FirstOrDefaultAsync(u => u.Username == onlineId);
|
||||||
|
if (user == null)
|
||||||
{
|
{
|
||||||
string onlineId = args[0];
|
user = await this._database.CreateUser(onlineId, HashHelper.BCryptHash(password));
|
||||||
string password = args[1];
|
Logger.Log($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LoggerLevelLogin.Instance);
|
||||||
|
|
||||||
password = HashHelper.Sha256Hash(password);
|
user.PasswordResetRequired = true;
|
||||||
|
Logger.Log("This user will need to reset their password when they log in.", LoggerLevelLogin.Instance);
|
||||||
|
|
||||||
User? user = await this._database.Users.FirstOrDefaultAsync(u => u.Username == onlineId);
|
await this._database.SaveChangesAsync();
|
||||||
if (user == null)
|
Logger.Log("Database changes saved.", LoggerLevelDatabase.Instance);
|
||||||
{
|
}
|
||||||
user = await this._database.CreateUser(onlineId, HashHelper.BCryptHash(password));
|
else
|
||||||
Logger.Log($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LoggerLevelLogin.Instance);
|
{
|
||||||
|
Logger.Log("A user with this username already exists.", LoggerLevelLogin.Instance);
|
||||||
user.PasswordResetRequired = true;
|
|
||||||
Logger.Log("This user will need to reset their password when they log in.", LoggerLevelLogin.Instance);
|
|
||||||
|
|
||||||
await this._database.SaveChangesAsync();
|
|
||||||
Logger.Log("Database changes saved.", LoggerLevelDatabase.Instance);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Log("A user with this username already exists.", LoggerLevelLogin.Instance);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name() => "Create New User";
|
|
||||||
|
|
||||||
public string[] Aliases()
|
|
||||||
=> new[]
|
|
||||||
{
|
|
||||||
"useradd", "adduser", "newuser", "createUser",
|
|
||||||
};
|
|
||||||
|
|
||||||
public string Arguments() => "<OnlineID> <Password>";
|
|
||||||
|
|
||||||
public int RequiredArgs() => 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string Name() => "Create New User";
|
||||||
|
|
||||||
|
public string[] Aliases()
|
||||||
|
=> new[]
|
||||||
|
{
|
||||||
|
"useradd", "adduser", "newuser", "createUser",
|
||||||
|
};
|
||||||
|
|
||||||
|
public string Arguments() => "<OnlineID> <Password>";
|
||||||
|
|
||||||
|
public int RequiredArgs() => 2;
|
||||||
}
|
}
|
|
@ -5,36 +5,35 @@ using JetBrains.Annotations;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
|
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands;
|
||||||
{
|
|
||||||
[UsedImplicitly]
|
|
||||||
public class DeleteUserCommand : ICommand
|
|
||||||
{
|
|
||||||
private readonly Database database = new();
|
|
||||||
public string Name() => "Delete User";
|
|
||||||
public string[] Aliases()
|
|
||||||
=> new[]
|
|
||||||
{
|
|
||||||
"deleteUser", "wipeUser",
|
|
||||||
};
|
|
||||||
public string Arguments() => "<username/userId>";
|
|
||||||
public int RequiredArgs() => 1;
|
|
||||||
public async Task Run(string[] args)
|
|
||||||
{
|
|
||||||
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]);
|
|
||||||
if (user == null)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0]));
|
|
||||||
if (user == null) throw new Exception();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Could not find user by parameter '{args[0]}'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.database.RemoveUser(user);
|
[UsedImplicitly]
|
||||||
}
|
public class DeleteUserCommand : ICommand
|
||||||
|
{
|
||||||
|
private readonly Database database = new();
|
||||||
|
public string Name() => "Delete User";
|
||||||
|
public string[] Aliases()
|
||||||
|
=> new[]
|
||||||
|
{
|
||||||
|
"deleteUser", "wipeUser",
|
||||||
|
};
|
||||||
|
public string Arguments() => "<username/userId>";
|
||||||
|
public int RequiredArgs() => 1;
|
||||||
|
public async Task Run(string[] args)
|
||||||
|
{
|
||||||
|
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]);
|
||||||
|
if (user == null)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0]));
|
||||||
|
if (user == null) throw new Exception();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Could not find user by parameter '{args[0]}'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.database.RemoveUser(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,41 +5,40 @@ using JetBrains.Annotations;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
|
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class MakeUserAdminCommand : ICommand
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
private readonly Database database = new();
|
||||||
public class MakeUserAdminCommand : ICommand
|
|
||||||
{
|
|
||||||
private readonly Database database = new();
|
|
||||||
|
|
||||||
public string Name() => "Make User Admin";
|
public string Name() => "Make User Admin";
|
||||||
public string[] Aliases()
|
public string[] Aliases()
|
||||||
=> new[]
|
=> new[]
|
||||||
{
|
|
||||||
"makeAdmin",
|
|
||||||
};
|
|
||||||
public string Arguments() => "<username/userId>";
|
|
||||||
public int RequiredArgs() => 1;
|
|
||||||
|
|
||||||
public async Task Run(string[] args)
|
|
||||||
{
|
{
|
||||||
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]);
|
"makeAdmin",
|
||||||
if (user == null)
|
};
|
||||||
try
|
public string Arguments() => "<username/userId>";
|
||||||
{
|
public int RequiredArgs() => 1;
|
||||||
user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0]));
|
|
||||||
if (user == null) throw new Exception();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Could not find user by parameter '{args[0]}'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.IsAdmin = true;
|
public async Task Run(string[] args)
|
||||||
await this.database.SaveChangesAsync();
|
{
|
||||||
|
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]);
|
||||||
|
if (user == null)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0]));
|
||||||
|
if (user == null) throw new Exception();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Could not find user by parameter '{args[0]}'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine($"The user {user.Username} (id: {user.UserId}) is now an admin.");
|
user.IsAdmin = true;
|
||||||
}
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
|
Console.WriteLine($"The user {user.Username} (id: {user.UserId}) is now an admin.");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,44 +6,43 @@ using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
|
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class ResetPasswordCommand : ICommand
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
private readonly Database database = new();
|
||||||
public class ResetPasswordCommand : ICommand
|
public string Name() => "Reset Password";
|
||||||
{
|
public string[] Aliases()
|
||||||
private readonly Database database = new();
|
=> new[]
|
||||||
public string Name() => "Reset Password";
|
|
||||||
public string[] Aliases()
|
|
||||||
=> new[]
|
|
||||||
{
|
|
||||||
"setPassword", "resetPassword", "passwd", "password",
|
|
||||||
};
|
|
||||||
public string Arguments() => "<username/userId> <sha256/plaintext>";
|
|
||||||
public int RequiredArgs() => 2;
|
|
||||||
|
|
||||||
public async Task Run(string[] args)
|
|
||||||
{
|
{
|
||||||
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]);
|
"setPassword", "resetPassword", "passwd", "password",
|
||||||
if (user == null)
|
};
|
||||||
try
|
public string Arguments() => "<username/userId> <sha256/plaintext>";
|
||||||
{
|
public int RequiredArgs() => 2;
|
||||||
user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0]));
|
|
||||||
if (user == null) throw new Exception();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Could not find user by parameter '{args[0]}'");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
string password = args[1];
|
|
||||||
if (password.Length != 64) password = HashHelper.Sha256Hash(password);
|
|
||||||
|
|
||||||
user.Password = HashHelper.BCryptHash(password);
|
public async Task Run(string[] args)
|
||||||
user.PasswordResetRequired = true;
|
{
|
||||||
|
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]);
|
||||||
|
if (user == null)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0]));
|
||||||
|
if (user == null) throw new Exception();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Could not find user by parameter '{args[0]}'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
string password = args[1];
|
||||||
|
if (password.Length != 64) password = HashHelper.Sha256Hash(password);
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
user.Password = HashHelper.BCryptHash(password);
|
||||||
|
user.PasswordResetRequired = true;
|
||||||
|
|
||||||
Console.WriteLine($"The password for user {user.Username} (id: {user.UserId}) has been reset.");
|
await this.database.SaveChangesAsync();
|
||||||
}
|
|
||||||
|
Console.WriteLine($"The password for user {user.Username} (id: {user.UserId}) has been reset.");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,41 +5,40 @@ using System.Threading.Tasks;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
|
namespace LBPUnion.ProjectLighthouse.Maintenance.Commands;
|
||||||
|
|
||||||
|
public class WipeTokensForUserCommand : ICommand
|
||||||
{
|
{
|
||||||
public class WipeTokensForUserCommand : ICommand
|
private readonly Database database = new();
|
||||||
{
|
|
||||||
private readonly Database database = new();
|
|
||||||
|
|
||||||
public string Name() => "Wipe tokens for user";
|
public string Name() => "Wipe tokens for user";
|
||||||
public string[] Aliases()
|
public string[] Aliases()
|
||||||
=> new[]
|
=> new[]
|
||||||
{
|
|
||||||
"wipeTokens", "wipeToken", "deleteTokens", "deleteToken", "removeTokens", "removeToken",
|
|
||||||
};
|
|
||||||
public string Arguments() => "<username/userId>";
|
|
||||||
public int RequiredArgs() => 1;
|
|
||||||
public async Task Run(string[] args)
|
|
||||||
{
|
{
|
||||||
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]);
|
"wipeTokens", "wipeToken", "deleteTokens", "deleteToken", "removeTokens", "removeToken",
|
||||||
if (user == null)
|
};
|
||||||
try
|
public string Arguments() => "<username/userId>";
|
||||||
{
|
public int RequiredArgs() => 1;
|
||||||
user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0]));
|
public async Task Run(string[] args)
|
||||||
if (user == null) throw new Exception();
|
{
|
||||||
}
|
User? user = await this.database.Users.FirstOrDefaultAsync(u => u.Username == args[0]);
|
||||||
catch
|
if (user == null)
|
||||||
{
|
try
|
||||||
Console.WriteLine($"Could not find user by parameter '{args[0]}'");
|
{
|
||||||
return;
|
user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == Convert.ToInt32(args[0]));
|
||||||
}
|
if (user == null) throw new Exception();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Could not find user by parameter '{args[0]}'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.database.GameTokens.RemoveRange(this.database.GameTokens.Where(t => t.UserId == user.UserId));
|
this.database.GameTokens.RemoveRange(this.database.GameTokens.Where(t => t.UserId == user.UserId));
|
||||||
this.database.WebTokens.RemoveRange(this.database.WebTokens.Where(t => t.UserId == user.UserId));
|
this.database.WebTokens.RemoveRange(this.database.WebTokens.Where(t => t.UserId == user.UserId));
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
await this.database.SaveChangesAsync();
|
||||||
|
|
||||||
Console.WriteLine($"Deleted all tokens for {user.Username} (id: {user.UserId}).");
|
Console.WriteLine($"Deleted all tokens for {user.Username} (id: {user.UserId}).");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,19 +1,18 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance
|
namespace LBPUnion.ProjectLighthouse.Maintenance;
|
||||||
|
|
||||||
|
public interface ICommand
|
||||||
{
|
{
|
||||||
public interface ICommand
|
|
||||||
{
|
|
||||||
|
|
||||||
public string FirstAlias => this.Aliases()[0];
|
public string FirstAlias => this.Aliases()[0];
|
||||||
public Task Run(string[] args);
|
public Task Run(string[] args);
|
||||||
|
|
||||||
public string Name();
|
public string Name();
|
||||||
|
|
||||||
public string[] Aliases();
|
public string[] Aliases();
|
||||||
|
|
||||||
public string Arguments();
|
public string Arguments();
|
||||||
|
|
||||||
public int RequiredArgs();
|
public int RequiredArgs();
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance
|
namespace LBPUnion.ProjectLighthouse.Maintenance;
|
||||||
|
|
||||||
|
public interface IMaintenanceJob
|
||||||
{
|
{
|
||||||
public interface IMaintenanceJob
|
public Task Run();
|
||||||
{
|
|
||||||
public Task Run();
|
|
||||||
|
|
||||||
public string Name();
|
public string Name();
|
||||||
|
|
||||||
public string Description();
|
public string Description();
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -7,86 +7,85 @@ using LBPUnion.ProjectLighthouse.Helpers;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Files;
|
using LBPUnion.ProjectLighthouse.Types.Files;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
|
namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs;
|
||||||
|
|
||||||
|
public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob
|
||||||
{
|
{
|
||||||
public class CleanupBrokenPhotosMaintenanceJob : IMaintenanceJob
|
private readonly Database database = new();
|
||||||
|
public string Name() => "Cleanup Broken Photos";
|
||||||
|
public string Description() => "Deletes all photos that have missing assets or invalid photo subjects.";
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")]
|
||||||
|
public async Task Run()
|
||||||
{
|
{
|
||||||
private readonly Database database = new();
|
foreach (Photo photo in this.database.Photos)
|
||||||
public string Name() => "Cleanup Broken Photos";
|
|
||||||
public string Description() => "Deletes all photos that have missing assets or invalid photo subjects.";
|
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")]
|
|
||||||
public async Task Run()
|
|
||||||
{
|
{
|
||||||
foreach (Photo photo in this.database.Photos)
|
bool hashNullOrEmpty = false;
|
||||||
|
bool noHashesExist = false;
|
||||||
|
bool largeHashIsInvalidFile = false;
|
||||||
|
bool tooManyPhotoSubjects = false;
|
||||||
|
bool duplicatePhotoSubjects = false;
|
||||||
|
|
||||||
|
hashNullOrEmpty = string.IsNullOrEmpty
|
||||||
|
(photo.LargeHash) ||
|
||||||
|
string.IsNullOrEmpty(photo.MediumHash) ||
|
||||||
|
string.IsNullOrEmpty(photo.SmallHash) ||
|
||||||
|
string.IsNullOrEmpty(photo.PlanHash);
|
||||||
|
if (hashNullOrEmpty) goto removePhoto;
|
||||||
|
|
||||||
|
List<string> hashes = new()
|
||||||
{
|
{
|
||||||
bool hashNullOrEmpty = false;
|
photo.LargeHash,
|
||||||
bool noHashesExist = false;
|
photo.MediumHash,
|
||||||
bool largeHashIsInvalidFile = false;
|
photo.SmallHash,
|
||||||
bool tooManyPhotoSubjects = false;
|
photo.PlanHash,
|
||||||
bool duplicatePhotoSubjects = false;
|
};
|
||||||
|
|
||||||
hashNullOrEmpty = string.IsNullOrEmpty
|
noHashesExist = FileHelper.ResourcesNotUploaded(hashes.ToArray()).Length != 0;
|
||||||
(photo.LargeHash) ||
|
if (noHashesExist) goto removePhoto;
|
||||||
string.IsNullOrEmpty(photo.MediumHash) ||
|
|
||||||
string.IsNullOrEmpty(photo.SmallHash) ||
|
|
||||||
string.IsNullOrEmpty(photo.PlanHash);
|
|
||||||
if (hashNullOrEmpty) goto removePhoto;
|
|
||||||
|
|
||||||
List<string> hashes = new()
|
LbpFile? file = LbpFile.FromHash(photo.LargeHash);
|
||||||
{
|
|
||||||
photo.LargeHash,
|
|
||||||
photo.MediumHash,
|
|
||||||
photo.SmallHash,
|
|
||||||
photo.PlanHash,
|
|
||||||
};
|
|
||||||
|
|
||||||
noHashesExist = FileHelper.ResourcesNotUploaded(hashes.ToArray()).Length != 0;
|
|
||||||
if (noHashesExist) goto removePhoto;
|
|
||||||
|
|
||||||
LbpFile? file = LbpFile.FromHash(photo.LargeHash);
|
|
||||||
// Console.WriteLine(file.FileType, );
|
// Console.WriteLine(file.FileType, );
|
||||||
if (file == null || file.FileType != LbpFileType.Jpeg && file.FileType != LbpFileType.Png)
|
if (file == null || file.FileType != LbpFileType.Jpeg && file.FileType != LbpFileType.Png)
|
||||||
{
|
{
|
||||||
largeHashIsInvalidFile = true;
|
largeHashIsInvalidFile = true;
|
||||||
goto removePhoto;
|
goto removePhoto;
|
||||||
}
|
|
||||||
|
|
||||||
if (photo.Subjects.Count > 4)
|
|
||||||
{
|
|
||||||
tooManyPhotoSubjects = true;
|
|
||||||
goto removePhoto;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<int> subjectUserIds = new(4);
|
|
||||||
foreach (PhotoSubject subject in photo.Subjects)
|
|
||||||
{
|
|
||||||
if (subjectUserIds.Contains(subject.UserId))
|
|
||||||
{
|
|
||||||
duplicatePhotoSubjects = true;
|
|
||||||
goto removePhoto;
|
|
||||||
}
|
|
||||||
subjectUserIds.Add(subject.UserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
|
|
||||||
removePhoto:
|
|
||||||
|
|
||||||
Console.WriteLine
|
|
||||||
(
|
|
||||||
$"Removing photo (id: {photo.PhotoId}): " +
|
|
||||||
$"{nameof(hashNullOrEmpty)}: {hashNullOrEmpty}, " +
|
|
||||||
$"{nameof(noHashesExist)}: {noHashesExist}, " +
|
|
||||||
$"{nameof(largeHashIsInvalidFile)}: {largeHashIsInvalidFile}, " +
|
|
||||||
$"{nameof(tooManyPhotoSubjects)}: {tooManyPhotoSubjects}" +
|
|
||||||
$"{nameof(duplicatePhotoSubjects)}: {duplicatePhotoSubjects}"
|
|
||||||
);
|
|
||||||
|
|
||||||
this.database.Photos.Remove(photo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
if (photo.Subjects.Count > 4)
|
||||||
|
{
|
||||||
|
tooManyPhotoSubjects = true;
|
||||||
|
goto removePhoto;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<int> subjectUserIds = new(4);
|
||||||
|
foreach (PhotoSubject subject in photo.Subjects)
|
||||||
|
{
|
||||||
|
if (subjectUserIds.Contains(subject.UserId))
|
||||||
|
{
|
||||||
|
duplicatePhotoSubjects = true;
|
||||||
|
goto removePhoto;
|
||||||
|
}
|
||||||
|
subjectUserIds.Add(subject.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
|
||||||
|
removePhoto:
|
||||||
|
|
||||||
|
Console.WriteLine
|
||||||
|
(
|
||||||
|
$"Removing photo (id: {photo.PhotoId}): " +
|
||||||
|
$"{nameof(hashNullOrEmpty)}: {hashNullOrEmpty}, " +
|
||||||
|
$"{nameof(noHashesExist)}: {noHashesExist}, " +
|
||||||
|
$"{nameof(largeHashIsInvalidFile)}: {largeHashIsInvalidFile}, " +
|
||||||
|
$"{nameof(tooManyPhotoSubjects)}: {tooManyPhotoSubjects}" +
|
||||||
|
$"{nameof(duplicatePhotoSubjects)}: {duplicatePhotoSubjects}"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.database.Photos.Remove(photo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,30 +4,29 @@ using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
|
namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs;
|
||||||
|
|
||||||
|
public class CleanupUnusedLocationsMaintenanceJob : IMaintenanceJob
|
||||||
{
|
{
|
||||||
public class CleanupUnusedLocationsMaintenanceJob : IMaintenanceJob
|
private readonly Database database = new();
|
||||||
|
public string Name() => "Cleanup Unused Locations";
|
||||||
|
public string Description() => "Cleanup unused locations in the database.";
|
||||||
|
|
||||||
|
public async Task Run()
|
||||||
{
|
{
|
||||||
private readonly Database database = new();
|
List<int> usedLocationIds = new();
|
||||||
public string Name() => "Cleanup Unused Locations";
|
|
||||||
public string Description() => "Cleanup unused locations in the database.";
|
|
||||||
|
|
||||||
public async Task Run()
|
usedLocationIds.AddRange(this.database.Slots.Select(slot => slot.LocationId));
|
||||||
|
usedLocationIds.AddRange(this.database.Users.Select(user => user.LocationId));
|
||||||
|
|
||||||
|
IQueryable<Location> locationsToRemove = this.database.Locations.Where(l => !usedLocationIds.Contains(l.Id));
|
||||||
|
|
||||||
|
foreach (Location location in locationsToRemove)
|
||||||
{
|
{
|
||||||
List<int> usedLocationIds = new();
|
Console.WriteLine("Removing location " + location.Id);
|
||||||
|
this.database.Locations.Remove(location);
|
||||||
usedLocationIds.AddRange(this.database.Slots.Select(slot => slot.LocationId));
|
|
||||||
usedLocationIds.AddRange(this.database.Users.Select(user => user.LocationId));
|
|
||||||
|
|
||||||
IQueryable<Location> locationsToRemove = this.database.Locations.Where(l => !usedLocationIds.Contains(l.Id));
|
|
||||||
|
|
||||||
foreach (Location location in locationsToRemove)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Removing location " + location.Id);
|
|
||||||
this.database.Locations.Remove(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.database.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,22 +1,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
|
namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs;
|
||||||
|
|
||||||
|
public class DeleteAllTokensMaintenanceJob : IMaintenanceJob
|
||||||
{
|
{
|
||||||
public class DeleteAllTokensMaintenanceJob : IMaintenanceJob
|
private readonly Database database = new();
|
||||||
|
|
||||||
|
public string Name() => "Delete ALL Tokens";
|
||||||
|
public string Description() => "Deletes ALL game tokens and web tokens.";
|
||||||
|
public async Task Run()
|
||||||
{
|
{
|
||||||
private readonly Database database = new();
|
this.database.GameTokens.RemoveRange(this.database.GameTokens);
|
||||||
|
this.database.WebTokens.RemoveRange(this.database.WebTokens);
|
||||||
|
|
||||||
public string Name() => "Delete ALL Tokens";
|
await this.database.SaveChangesAsync();
|
||||||
public string Description() => "Deletes ALL game tokens and web tokens.";
|
|
||||||
public async Task Run()
|
|
||||||
{
|
|
||||||
this.database.GameTokens.RemoveRange(this.database.GameTokens);
|
|
||||||
this.database.WebTokens.RemoveRange(this.database.WebTokens);
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
Console.WriteLine("Deleted ALL tokens.");
|
||||||
|
|
||||||
Console.WriteLine("Deleted ALL tokens.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,30 +2,29 @@ using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
|
namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs;
|
||||||
|
|
||||||
|
public class FixAllBrokenPlayerRequirementsMaintenanceJob : IMaintenanceJob
|
||||||
{
|
{
|
||||||
public class FixAllBrokenPlayerRequirementsMaintenanceJob : IMaintenanceJob
|
private readonly Database database = new();
|
||||||
|
|
||||||
|
public string Name() => "Fix All Broken Player Requirements";
|
||||||
|
public string Description() => "Some LBP1 levels may report that they are designed for 0 players. This job will fix that.";
|
||||||
|
public async Task Run()
|
||||||
{
|
{
|
||||||
private readonly Database database = new();
|
int count = 0;
|
||||||
|
await foreach (Slot slot in this.database.Slots)
|
||||||
|
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
|
||||||
|
{
|
||||||
|
slot.MinimumPlayers = 1;
|
||||||
|
slot.MaximumPlayers = 4;
|
||||||
|
|
||||||
public string Name() => "Fix All Broken Player Requirements";
|
Console.WriteLine($"Fixed slotId {slot.SlotId}");
|
||||||
public string Description() => "Some LBP1 levels may report that they are designed for 0 players. This job will fix that.";
|
count++;
|
||||||
public async Task Run()
|
}
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
await foreach (Slot slot in this.database.Slots)
|
|
||||||
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
|
|
||||||
{
|
|
||||||
slot.MinimumPlayers = 1;
|
|
||||||
slot.MaximumPlayers = 4;
|
|
||||||
|
|
||||||
Console.WriteLine($"Fixed slotId {slot.SlotId}");
|
await this.database.SaveChangesAsync();
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.database.SaveChangesAsync();
|
Console.WriteLine($"Fixed {count} broken player requirements.");
|
||||||
|
|
||||||
Console.WriteLine($"Fixed {count} broken player requirements.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,10 +10,10 @@ namespace LBPUnion.ProjectLighthouse.Pages.Admin;
|
||||||
|
|
||||||
public class AdminBanUserPage : BaseLayout
|
public class AdminBanUserPage : BaseLayout
|
||||||
{
|
{
|
||||||
public AdminBanUserPage(Database database) : base(database)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public User? TargetedUser;
|
public User? TargetedUser;
|
||||||
|
public AdminBanUserPage(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet([FromRoute] int id)
|
public async Task<IActionResult> OnGet([FromRoute] int id)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,12 +11,12 @@ namespace LBPUnion.ProjectLighthouse.Pages.Admin;
|
||||||
|
|
||||||
public class AdminPanelUsersPage : BaseLayout
|
public class AdminPanelUsersPage : BaseLayout
|
||||||
{
|
{
|
||||||
public AdminPanelUsersPage(Database database) : base(database)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public int UserCount;
|
public int UserCount;
|
||||||
|
|
||||||
public List<User> Users;
|
public List<User> Users;
|
||||||
|
public AdminPanelUsersPage(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet()
|
public async Task<IActionResult> OnGet()
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,31 +9,30 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth
|
namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth;
|
||||||
|
|
||||||
|
public class AuthenticationPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class AuthenticationPage : BaseLayout
|
|
||||||
|
public List<AuthenticationAttempt> AuthenticationAttempts;
|
||||||
|
|
||||||
|
public IPAddress? IpAddress;
|
||||||
|
public AuthenticationPage(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnGet()
|
||||||
{
|
{
|
||||||
|
if (!ServerSettings.Instance.UseExternalAuth) return this.NotFound();
|
||||||
|
if (this.User == null) return this.StatusCode(403, "");
|
||||||
|
|
||||||
public List<AuthenticationAttempt> AuthenticationAttempts;
|
this.IpAddress = this.HttpContext.Connection.RemoteIpAddress;
|
||||||
|
|
||||||
public IPAddress? IpAddress;
|
this.AuthenticationAttempts = this.Database.AuthenticationAttempts.Include
|
||||||
public AuthenticationPage(Database database) : base(database)
|
(a => a.GameToken)
|
||||||
{}
|
.Where(a => a.GameToken.UserId == this.User.UserId)
|
||||||
|
.OrderByDescending(a => a.Timestamp)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet()
|
return this.Page();
|
||||||
{
|
|
||||||
if (!ServerSettings.Instance.UseExternalAuth) return this.NotFound();
|
|
||||||
if (this.User == null) return this.StatusCode(403, "");
|
|
||||||
|
|
||||||
this.IpAddress = this.HttpContext.Connection.RemoteIpAddress;
|
|
||||||
|
|
||||||
this.AuthenticationAttempts = this.Database.AuthenticationAttempts.Include
|
|
||||||
(a => a.GameToken)
|
|
||||||
.Where(a => a.GameToken.UserId == this.User.UserId)
|
|
||||||
.OrderByDescending(a => a.Timestamp)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,23 +7,22 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth
|
namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth;
|
||||||
|
|
||||||
|
public class ManageUserApprovedIpAddressesPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class ManageUserApprovedIpAddressesPage : BaseLayout
|
|
||||||
|
public List<UserApprovedIpAddress> ApprovedIpAddresses;
|
||||||
|
public ManageUserApprovedIpAddressesPage(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnGet()
|
||||||
{
|
{
|
||||||
public ManageUserApprovedIpAddressesPage(Database database) : base(database)
|
User? user = this.Database.UserFromWebRequest(this.Request);
|
||||||
{}
|
if (user == null) return this.Redirect("/login");
|
||||||
|
|
||||||
public List<UserApprovedIpAddress> ApprovedIpAddresses;
|
this.ApprovedIpAddresses = await this.Database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).ToListAsync();
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet()
|
return this.Page();
|
||||||
{
|
|
||||||
User? user = this.Database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("/login");
|
|
||||||
|
|
||||||
this.ApprovedIpAddresses = await this.Database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).ToListAsync();
|
|
||||||
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,37 +9,34 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class LandingPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class LandingPage : BaseLayout
|
|
||||||
|
public int AuthenticationAttemptsCount;
|
||||||
|
public List<User> PlayersOnline;
|
||||||
|
|
||||||
|
public int PlayersOnlineCount;
|
||||||
|
public LandingPage(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public async Task<IActionResult> OnGet()
|
||||||
{
|
{
|
||||||
public List<User> PlayersOnline;
|
User? user = this.Database.UserFromWebRequest(this.Request);
|
||||||
|
if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
|
||||||
|
|
||||||
public int PlayersOnlineCount;
|
this.PlayersOnlineCount = await StatisticsHelper.RecentMatches();
|
||||||
|
|
||||||
public int AuthenticationAttemptsCount;
|
if (user != null)
|
||||||
public LandingPage(Database database) : base(database)
|
this.AuthenticationAttemptsCount = await this.Database.AuthenticationAttempts.Include
|
||||||
{}
|
(a => a.GameToken)
|
||||||
|
.CountAsync(a => a.GameToken.UserId == user.UserId);
|
||||||
|
|
||||||
[UsedImplicitly]
|
List<int> userIds = await this.Database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync();
|
||||||
public async Task<IActionResult> OnGet()
|
|
||||||
{
|
|
||||||
User? user = this.Database.UserFromWebRequest(this.Request);
|
|
||||||
if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
|
|
||||||
|
|
||||||
this.PlayersOnlineCount = await StatisticsHelper.RecentMatches();
|
this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync();
|
||||||
|
return this.Page();
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
this.AuthenticationAttemptsCount = await this.Database.AuthenticationAttempts.Include
|
|
||||||
(a => a.GameToken)
|
|
||||||
.CountAsync(a => a.GameToken.UserId == user.UserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<int> userIds = await this.Database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync();
|
|
||||||
|
|
||||||
this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync();
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,42 +3,41 @@ using System.Collections.Generic;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages.Layouts
|
namespace LBPUnion.ProjectLighthouse.Pages.Layouts;
|
||||||
|
|
||||||
|
public class BaseLayout : PageModel
|
||||||
{
|
{
|
||||||
public class BaseLayout : PageModel
|
|
||||||
|
public readonly Database Database;
|
||||||
|
|
||||||
|
public readonly List<PageNavigationItem> NavigationItems = new()
|
||||||
{
|
{
|
||||||
public BaseLayout(Database database)
|
new PageNavigationItem("Home", "/", "home"),
|
||||||
{
|
new PageNavigationItem("Photos", "/photos/0", "camera"),
|
||||||
this.Database = database;
|
new PageNavigationItem("Levels", "/slots/0", "certificate"),
|
||||||
}
|
};
|
||||||
|
|
||||||
public bool IsMobile;
|
public readonly List<PageNavigationItem> NavigationItemsRight = new();
|
||||||
|
public string Description = string.Empty;
|
||||||
public readonly Database Database;
|
|
||||||
|
public bool IsMobile;
|
||||||
public readonly List<PageNavigationItem> NavigationItems = new()
|
|
||||||
{
|
public bool ShowTitleInPage = true;
|
||||||
new PageNavigationItem("Home", "/", "home"),
|
|
||||||
new PageNavigationItem("Photos", "/photos/0", "camera"),
|
public string Title = string.Empty;
|
||||||
new PageNavigationItem("Levels", "/slots/0", "certificate"),
|
|
||||||
};
|
private User? user;
|
||||||
|
public BaseLayout(Database database)
|
||||||
public readonly List<PageNavigationItem> NavigationItemsRight = new();
|
{
|
||||||
|
this.Database = database;
|
||||||
public bool ShowTitleInPage = true;
|
}
|
||||||
|
|
||||||
public string Title = string.Empty;
|
public new User? User {
|
||||||
public string Description = string.Empty;
|
get {
|
||||||
|
if (this.user != null) return this.user;
|
||||||
private User? user;
|
|
||||||
|
return this.user = this.Database.UserFromWebRequest(this.Request);
|
||||||
public new User? User {
|
|
||||||
get {
|
|
||||||
if (this.user != null) return this.user;
|
|
||||||
|
|
||||||
return this.user = this.Database.UserFromWebRequest(this.Request);
|
|
||||||
}
|
|
||||||
set => this.user = value;
|
|
||||||
}
|
}
|
||||||
|
set => this.user = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,75 +9,74 @@ using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class LoginForm : BaseLayout
|
||||||
{
|
{
|
||||||
public class LoginForm : BaseLayout
|
public LoginForm(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public string Error { get; private set; }
|
||||||
|
|
||||||
|
public bool WasLoginRequest { get; private set; }
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public async Task<IActionResult> OnPost(string username, string password)
|
||||||
{
|
{
|
||||||
public LoginForm(Database database) : base(database)
|
if (string.IsNullOrWhiteSpace(username))
|
||||||
{}
|
|
||||||
|
|
||||||
public string Error { get; private set; }
|
|
||||||
|
|
||||||
public bool WasLoginRequest { get; private set; }
|
|
||||||
|
|
||||||
[UsedImplicitly]
|
|
||||||
public async Task<IActionResult> OnPost(string username, string password)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(username))
|
this.Error = "The username field is required.";
|
||||||
{
|
|
||||||
this.Error = "The username field is required.";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(password))
|
|
||||||
{
|
|
||||||
this.Error = "The password field is required.";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
Logger.Log($"User {username} failed to login on web due to invalid username", LoggerLevelLogin.Instance);
|
|
||||||
this.Error = "The username or password you entered is invalid.";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BCrypt.Net.BCrypt.Verify(password, user.Password))
|
|
||||||
{
|
|
||||||
Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LoggerLevelLogin.Instance);
|
|
||||||
this.Error = "The username or password you entered is invalid.";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.Banned)
|
|
||||||
{
|
|
||||||
Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LoggerLevelLogin.Instance);
|
|
||||||
this.Error = "You have been banned. Please contact an administrator for more information.\nReason: " + user.BannedReason;
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
WebToken webToken = new()
|
|
||||||
{
|
|
||||||
UserId = user.UserId,
|
|
||||||
UserToken = HashHelper.GenerateAuthToken(),
|
|
||||||
};
|
|
||||||
|
|
||||||
this.Database.WebTokens.Add(webToken);
|
|
||||||
await this.Database.SaveChangesAsync();
|
|
||||||
|
|
||||||
this.Response.Cookies.Append("LighthouseToken", webToken.UserToken);
|
|
||||||
|
|
||||||
if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
|
|
||||||
|
|
||||||
return this.RedirectToPage(nameof(LandingPage));
|
|
||||||
}
|
|
||||||
|
|
||||||
[UsedImplicitly]
|
|
||||||
public async Task<IActionResult> OnGet()
|
|
||||||
{
|
|
||||||
this.Error = string.Empty;
|
|
||||||
return this.Page();
|
return this.Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(password))
|
||||||
|
{
|
||||||
|
this.Error = "The password field is required.";
|
||||||
|
return this.Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username);
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
Logger.Log($"User {username} failed to login on web due to invalid username", LoggerLevelLogin.Instance);
|
||||||
|
this.Error = "The username or password you entered is invalid.";
|
||||||
|
return this.Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BCrypt.Net.BCrypt.Verify(password, user.Password))
|
||||||
|
{
|
||||||
|
Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to invalid password", LoggerLevelLogin.Instance);
|
||||||
|
this.Error = "The username or password you entered is invalid.";
|
||||||
|
return this.Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.Banned)
|
||||||
|
{
|
||||||
|
Logger.Log($"User {user.Username} (id: {user.UserId}) failed to login on web due to being banned", LoggerLevelLogin.Instance);
|
||||||
|
this.Error = "You have been banned. Please contact an administrator for more information.\nReason: " + user.BannedReason;
|
||||||
|
return this.Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
WebToken webToken = new()
|
||||||
|
{
|
||||||
|
UserId = user.UserId,
|
||||||
|
UserToken = HashHelper.GenerateAuthToken(),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Database.WebTokens.Add(webToken);
|
||||||
|
await this.Database.SaveChangesAsync();
|
||||||
|
|
||||||
|
this.Response.Cookies.Append("LighthouseToken", webToken.UserToken);
|
||||||
|
|
||||||
|
if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired");
|
||||||
|
|
||||||
|
return this.RedirectToPage(nameof(LandingPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public async Task<IActionResult> OnGet()
|
||||||
|
{
|
||||||
|
this.Error = string.Empty;
|
||||||
|
return this.Page();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,23 +4,22 @@ using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class LogoutPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class LogoutPage : BaseLayout
|
public LogoutPage(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
public async Task<IActionResult> OnGet()
|
||||||
{
|
{
|
||||||
public LogoutPage(Database database) : base(database)
|
WebToken? token = this.Database.WebTokenFromRequest(this.Request);
|
||||||
{}
|
if (token == null) return this.BadRequest();
|
||||||
public async Task<IActionResult> OnGet()
|
|
||||||
{
|
|
||||||
WebToken? token = this.Database.WebTokenFromRequest(this.Request);
|
|
||||||
if (token == null) return this.BadRequest();
|
|
||||||
|
|
||||||
this.Database.WebTokens.Remove(token);
|
this.Database.WebTokens.Remove(token);
|
||||||
await this.Database.SaveChangesAsync();
|
await this.Database.SaveChangesAsync();
|
||||||
|
|
||||||
this.Response.Cookies.Delete("LighthouseToken");
|
this.Response.Cookies.Delete("LighthouseToken");
|
||||||
|
|
||||||
return this.Page();
|
return this.Page();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,48 +6,47 @@ using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class PasswordResetPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class PasswordResetPage : BaseLayout
|
public PasswordResetPage(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public string Error { get; private set; }
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public async Task<IActionResult> OnPost(string password, string confirmPassword)
|
||||||
{
|
{
|
||||||
public PasswordResetPage(Database database) : base(database)
|
User? user = this.Database.UserFromWebRequest(this.Request);
|
||||||
{}
|
if (user == null) return this.Redirect("~/login");
|
||||||
|
|
||||||
public string Error { get; private set; }
|
if (string.IsNullOrWhiteSpace(password))
|
||||||
|
|
||||||
[UsedImplicitly]
|
|
||||||
public async Task<IActionResult> OnPost(string password, string confirmPassword)
|
|
||||||
{
|
{
|
||||||
User? user = this.Database.UserFromWebRequest(this.Request);
|
this.Error = "The password field is required.";
|
||||||
if (user == null) return this.Redirect("~/login");
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(password))
|
|
||||||
{
|
|
||||||
this.Error = "The password field is required.";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password != confirmPassword)
|
|
||||||
{
|
|
||||||
this.Error = "Passwords do not match!";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
user.Password = HashHelper.BCryptHash(password);
|
|
||||||
user.PasswordResetRequired = false;
|
|
||||||
|
|
||||||
await this.Database.SaveChangesAsync();
|
|
||||||
|
|
||||||
return this.Redirect("~/");
|
|
||||||
}
|
|
||||||
|
|
||||||
[UsedImplicitly]
|
|
||||||
public IActionResult OnGet()
|
|
||||||
{
|
|
||||||
User? user = this.Database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("~/login");
|
|
||||||
|
|
||||||
return this.Page();
|
return this.Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (password != confirmPassword)
|
||||||
|
{
|
||||||
|
this.Error = "Passwords do not match!";
|
||||||
|
return this.Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Password = HashHelper.BCryptHash(password);
|
||||||
|
user.PasswordResetRequired = false;
|
||||||
|
|
||||||
|
await this.Database.SaveChangesAsync();
|
||||||
|
|
||||||
|
return this.Redirect("~/");
|
||||||
|
}
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public IActionResult OnGet()
|
||||||
|
{
|
||||||
|
User? user = this.Database.UserFromWebRequest(this.Request);
|
||||||
|
if (user == null) return this.Redirect("~/login");
|
||||||
|
|
||||||
|
return this.Page();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,22 +5,21 @@ using LBPUnion.ProjectLighthouse.Pages.Layouts;
|
||||||
using LBPUnion.ProjectLighthouse.Types;
|
using LBPUnion.ProjectLighthouse.Types;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class PasswordResetRequiredPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class PasswordResetRequiredPage : BaseLayout
|
public PasswordResetRequiredPage([NotNull] Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public bool WasResetRequest { get; private set; }
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnGet()
|
||||||
{
|
{
|
||||||
public PasswordResetRequiredPage([NotNull] Database database) : base(database)
|
User? user = this.Database.UserFromWebRequest(this.Request);
|
||||||
{}
|
if (user == null) return this.Redirect("~/login");
|
||||||
|
if (!user.PasswordResetRequired) return this.Redirect("~/passwordReset");
|
||||||
|
|
||||||
public bool WasResetRequest { get; private set; }
|
return this.Page();
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet()
|
|
||||||
{
|
|
||||||
User? user = this.Database.UserFromWebRequest(this.Request);
|
|
||||||
if (user == null) return this.Redirect("~/login");
|
|
||||||
if (!user.PasswordResetRequired) return this.Redirect("~/passwordReset");
|
|
||||||
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,41 +10,37 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class PhotosPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class PhotosPage : BaseLayout
|
|
||||||
|
public int PageAmount;
|
||||||
|
|
||||||
|
public int PageNumber;
|
||||||
|
|
||||||
|
public int PhotoCount;
|
||||||
|
|
||||||
|
public List<Photo> Photos;
|
||||||
|
public PhotosPage([NotNull] Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnGet([FromRoute] int pageNumber)
|
||||||
{
|
{
|
||||||
|
this.PhotoCount = await StatisticsHelper.PhotoCount();
|
||||||
|
|
||||||
public int PageNumber;
|
this.PageNumber = pageNumber;
|
||||||
|
this.PageAmount = (int)Math.Ceiling((double)this.PhotoCount / ServerStatics.PageSize);
|
||||||
|
|
||||||
public int PhotoCount;
|
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/photos/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
||||||
|
|
||||||
public int PageAmount;
|
this.Photos = await this.Database.Photos.Include
|
||||||
|
(p => p.Creator)
|
||||||
|
.OrderByDescending(p => p.Timestamp)
|
||||||
|
.Skip(pageNumber * ServerStatics.PageSize)
|
||||||
|
.Take(ServerStatics.PageSize)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
public List<Photo> Photos;
|
return this.Page();
|
||||||
public PhotosPage([NotNull] Database database) : base(database)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet([FromRoute] int pageNumber)
|
|
||||||
{
|
|
||||||
this.PhotoCount = await StatisticsHelper.PhotoCount();
|
|
||||||
|
|
||||||
this.PageNumber = pageNumber;
|
|
||||||
this.PageAmount = (int)Math.Ceiling((double)this.PhotoCount / ServerStatics.PageSize);
|
|
||||||
|
|
||||||
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount)
|
|
||||||
{
|
|
||||||
return this.Redirect($"/photos/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Photos = await this.Database.Photos.Include
|
|
||||||
(p => p.Creator)
|
|
||||||
.OrderByDescending(p => p.Timestamp)
|
|
||||||
.Skip(pageNumber * ServerStatics.PageSize)
|
|
||||||
.Take(ServerStatics.PageSize)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,71 +8,70 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class RegisterForm : BaseLayout
|
||||||
{
|
{
|
||||||
public class RegisterForm : BaseLayout
|
public RegisterForm(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public string Error { get; private set; }
|
||||||
|
public bool WasRegisterRequest { get; private set; }
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
[SuppressMessage("ReSharper", "SpecifyStringComparison")]
|
||||||
|
public async Task<IActionResult> OnPost(string username, string password, string confirmPassword)
|
||||||
{
|
{
|
||||||
public RegisterForm(Database database) : base(database)
|
if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound();
|
||||||
{}
|
|
||||||
|
|
||||||
public string Error { get; private set; }
|
if (string.IsNullOrWhiteSpace(username))
|
||||||
public bool WasRegisterRequest { get; private set; }
|
|
||||||
|
|
||||||
[UsedImplicitly]
|
|
||||||
[SuppressMessage("ReSharper", "SpecifyStringComparison")]
|
|
||||||
public async Task<IActionResult> OnPost(string username, string password, string confirmPassword)
|
|
||||||
{
|
{
|
||||||
if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound();
|
this.Error = "The username field is blank.";
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(username))
|
|
||||||
{
|
|
||||||
this.Error = "The username field is blank.";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(password))
|
|
||||||
{
|
|
||||||
this.Error = "Password field is required.";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password != confirmPassword)
|
|
||||||
{
|
|
||||||
this.Error = "Passwords do not match!";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool userExists = await this.Database.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()) != null;
|
|
||||||
if (userExists)
|
|
||||||
{
|
|
||||||
this.Error = "The username you've chosen is already taken.";
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
|
|
||||||
User user = await this.Database.CreateUser(username, HashHelper.BCryptHash(password));
|
|
||||||
|
|
||||||
WebToken webToken = new()
|
|
||||||
{
|
|
||||||
UserId = user.UserId,
|
|
||||||
UserToken = HashHelper.GenerateAuthToken(),
|
|
||||||
};
|
|
||||||
|
|
||||||
this.Database.WebTokens.Add(webToken);
|
|
||||||
await this.Database.SaveChangesAsync();
|
|
||||||
|
|
||||||
this.Response.Cookies.Append("LighthouseToken", webToken.UserToken);
|
|
||||||
|
|
||||||
return this.RedirectToPage(nameof(LandingPage));
|
|
||||||
}
|
|
||||||
|
|
||||||
[UsedImplicitly]
|
|
||||||
[SuppressMessage("ReSharper", "SpecifyStringComparison")]
|
|
||||||
public IActionResult OnGet()
|
|
||||||
{
|
|
||||||
this.Error = string.Empty;
|
|
||||||
if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound();
|
|
||||||
|
|
||||||
return this.Page();
|
return this.Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(password))
|
||||||
|
{
|
||||||
|
this.Error = "Password field is required.";
|
||||||
|
return this.Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password != confirmPassword)
|
||||||
|
{
|
||||||
|
this.Error = "Passwords do not match!";
|
||||||
|
return this.Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool userExists = await this.Database.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()) != null;
|
||||||
|
if (userExists)
|
||||||
|
{
|
||||||
|
this.Error = "The username you've chosen is already taken.";
|
||||||
|
return this.Page();
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = await this.Database.CreateUser(username, HashHelper.BCryptHash(password));
|
||||||
|
|
||||||
|
WebToken webToken = new()
|
||||||
|
{
|
||||||
|
UserId = user.UserId,
|
||||||
|
UserToken = HashHelper.GenerateAuthToken(),
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Database.WebTokens.Add(webToken);
|
||||||
|
await this.Database.SaveChangesAsync();
|
||||||
|
|
||||||
|
this.Response.Cookies.Append("LighthouseToken", webToken.UserToken);
|
||||||
|
|
||||||
|
return this.RedirectToPage(nameof(LandingPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
[SuppressMessage("ReSharper", "SpecifyStringComparison")]
|
||||||
|
public IActionResult OnGet()
|
||||||
|
{
|
||||||
|
this.Error = string.Empty;
|
||||||
|
if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound();
|
||||||
|
|
||||||
|
return this.Page();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,23 +6,22 @@ using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class SlotPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class SlotPage : BaseLayout
|
|
||||||
|
public Slot Slot;
|
||||||
|
public SlotPage([NotNull] Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnGet([FromRoute] int id)
|
||||||
{
|
{
|
||||||
|
Slot? slot = await this.Database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id);
|
||||||
|
if (slot == null) return this.NotFound();
|
||||||
|
|
||||||
public Slot Slot;
|
this.Slot = slot;
|
||||||
public SlotPage([NotNull] Database database) : base(database)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet([FromRoute] int id)
|
return this.Page();
|
||||||
{
|
|
||||||
Slot? slot = await this.Database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id);
|
|
||||||
if (slot == null) return this.NotFound();
|
|
||||||
|
|
||||||
this.Slot = slot;
|
|
||||||
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,41 +10,37 @@ using LBPUnion.ProjectLighthouse.Types.Settings;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class SlotsPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class SlotsPage : BaseLayout
|
|
||||||
|
public int PageAmount;
|
||||||
|
|
||||||
|
public int PageNumber;
|
||||||
|
|
||||||
|
public int SlotCount;
|
||||||
|
|
||||||
|
public List<Slot> Slots;
|
||||||
|
public SlotsPage([NotNull] Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnGet([FromRoute] int pageNumber)
|
||||||
{
|
{
|
||||||
|
this.SlotCount = await StatisticsHelper.SlotCount();
|
||||||
|
|
||||||
public int PageNumber;
|
this.PageNumber = pageNumber;
|
||||||
|
this.PageAmount = (int)Math.Ceiling((double)this.SlotCount / ServerStatics.PageSize);
|
||||||
|
|
||||||
public int SlotCount;
|
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
||||||
|
|
||||||
public int PageAmount;
|
this.Slots = await this.Database.Slots.Include
|
||||||
|
(p => p.Creator)
|
||||||
|
.OrderByDescending(p => p.FirstUploaded)
|
||||||
|
.Skip(pageNumber * ServerStatics.PageSize)
|
||||||
|
.Take(ServerStatics.PageSize)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
public List<Slot> Slots;
|
return this.Page();
|
||||||
public SlotsPage([NotNull] Database database) : base(database)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet([FromRoute] int pageNumber)
|
|
||||||
{
|
|
||||||
this.SlotCount = await StatisticsHelper.SlotCount();
|
|
||||||
|
|
||||||
this.PageNumber = pageNumber;
|
|
||||||
this.PageAmount = (int)Math.Ceiling((double)this.SlotCount / ServerStatics.PageSize);
|
|
||||||
|
|
||||||
if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount)
|
|
||||||
{
|
|
||||||
return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.Slots = await this.Database.Slots.Include
|
|
||||||
(p => p.Creator)
|
|
||||||
.OrderByDescending(p => p.FirstUploaded)
|
|
||||||
.Skip(pageNumber * ServerStatics.PageSize)
|
|
||||||
.Take(ServerStatics.PageSize)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -125,9 +125,9 @@
|
||||||
@foreach (Comment comment in Model.Comments!)
|
@foreach (Comment comment in Model.Comments!)
|
||||||
{
|
{
|
||||||
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000);
|
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000);
|
||||||
StringWriter messageWriter = new StringWriter();
|
StringWriter messageWriter = new();
|
||||||
HttpUtility.HtmlDecode(comment.Message, messageWriter);
|
HttpUtility.HtmlDecode(comment.Message, messageWriter);
|
||||||
String decodedMessage = messageWriter.ToString();
|
string decodedMessage = messageWriter.ToString();
|
||||||
<div>
|
<div>
|
||||||
<b><a href="/user/@comment.PosterUserId">@comment.Poster.Username</a>: </b>
|
<b><a href="/user/@comment.PosterUserId">@comment.Poster.Username</a>: </b>
|
||||||
<span>@decodedMessage</span>
|
<span>@decodedMessage</span>
|
||||||
|
|
|
@ -8,40 +8,39 @@ using LBPUnion.ProjectLighthouse.Types.Profiles;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Pages
|
namespace LBPUnion.ProjectLighthouse.Pages;
|
||||||
|
|
||||||
|
public class UserPage : BaseLayout
|
||||||
{
|
{
|
||||||
public class UserPage : BaseLayout
|
public List<Comment>? Comments;
|
||||||
|
|
||||||
|
public bool IsProfileUserHearted;
|
||||||
|
|
||||||
|
public List<Photo>? Photos;
|
||||||
|
|
||||||
|
public User? ProfileUser;
|
||||||
|
public UserPage(Database database) : base(database)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public async Task<IActionResult> OnGet([FromRoute] int userId)
|
||||||
{
|
{
|
||||||
public List<Comment>? Comments;
|
this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId);
|
||||||
|
if (this.ProfileUser == null) return this.NotFound();
|
||||||
|
|
||||||
public bool IsProfileUserHearted;
|
this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(5).ToListAsync();
|
||||||
|
this.Comments = await this.Database.Comments.Include
|
||||||
|
(p => p.Poster)
|
||||||
|
.Include(p => p.Target)
|
||||||
|
.OrderByDescending(p => p.Timestamp)
|
||||||
|
.Where(p => p.TargetUserId == userId)
|
||||||
|
.Take(50)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
public List<Photo>? Photos;
|
if (this.User != null)
|
||||||
|
this.IsProfileUserHearted = await this.Database.HeartedProfiles.FirstOrDefaultAsync
|
||||||
|
(u => u.UserId == this.User.UserId && u.HeartedUserId == this.ProfileUser.UserId) !=
|
||||||
|
null;
|
||||||
|
|
||||||
public User? ProfileUser;
|
return this.Page();
|
||||||
public UserPage(Database database) : base(database)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public async Task<IActionResult> OnGet([FromRoute] int userId)
|
|
||||||
{
|
|
||||||
this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId);
|
|
||||||
if (this.ProfileUser == null) return this.NotFound();
|
|
||||||
|
|
||||||
this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(5).ToListAsync();
|
|
||||||
this.Comments = await this.Database.Comments.Include
|
|
||||||
(p => p.Poster)
|
|
||||||
.Include(p => p.Target)
|
|
||||||
.OrderByDescending(p => p.Timestamp)
|
|
||||||
.Where(p => p.TargetUserId == userId)
|
|
||||||
.Take(50)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
if (this.User != null)
|
|
||||||
this.IsProfileUserHearted = await this.Database.HeartedProfiles.FirstOrDefaultAsync
|
|
||||||
(u => u.UserId == this.User.UserId && u.HeartedUserId == this.ProfileUser.UserId) !=
|
|
||||||
null;
|
|
||||||
|
|
||||||
return this.Page();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,55 +12,55 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse
|
namespace LBPUnion.ProjectLighthouse;
|
||||||
|
|
||||||
|
public static class Program
|
||||||
{
|
{
|
||||||
public static class Program
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
if (args.Length != 0 && args[0] == "--wait-for-debugger")
|
||||||
{
|
{
|
||||||
if (args.Length != 0 && args[0] == "--wait-for-debugger")
|
Console.WriteLine("Waiting for a debugger to be attached...");
|
||||||
{
|
while (!Debugger.IsAttached) Thread.Sleep(100);
|
||||||
Console.WriteLine("Waiting for a debugger to be attached...");
|
Console.WriteLine("Debugger attached.");
|
||||||
while (!Debugger.IsAttached) Thread.Sleep(100);
|
}
|
||||||
Console.WriteLine("Debugger attached.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log startup time
|
// Log startup time
|
||||||
Stopwatch stopwatch = new();
|
Stopwatch stopwatch = new();
|
||||||
stopwatch.Start();
|
stopwatch.Start();
|
||||||
|
|
||||||
// Setup logging
|
// Setup logging
|
||||||
|
|
||||||
Logger.StartLogging();
|
Logger.StartLogging();
|
||||||
Logger.UpdateRate /= 2;
|
Logger.UpdateRate /= 2;
|
||||||
LoggerLine.LogFormat = "[{0}] {1}";
|
LoggerLine.LogFormat = "[{0}] {1}";
|
||||||
Logger.AddLogger(new ConsoleLogger());
|
Logger.AddLogger(new ConsoleLogger());
|
||||||
Logger.AddLogger(new LighthouseFileLogger());
|
Logger.AddLogger(new LighthouseFileLogger());
|
||||||
|
|
||||||
Logger.Log("Welcome to Project Lighthouse!", LoggerLevelStartup.Instance);
|
Logger.Log("Welcome to Project Lighthouse!", LoggerLevelStartup.Instance);
|
||||||
Logger.Log($"Running {VersionHelper.FullVersion}", LoggerLevelStartup.Instance);
|
Logger.Log($"Running {VersionHelper.FullVersion}", LoggerLevelStartup.Instance);
|
||||||
|
|
||||||
// This loads the config, see ServerSettings.cs for more information
|
// This loads the config, see ServerSettings.cs for more information
|
||||||
Logger.Log("Loaded config file version " + ServerSettings.Instance.ConfigVersion, LoggerLevelStartup.Instance);
|
Logger.Log("Loaded config file version " + ServerSettings.Instance.ConfigVersion, LoggerLevelStartup.Instance);
|
||||||
|
|
||||||
Logger.Log("Determining if the database is available...", LoggerLevelStartup.Instance);
|
Logger.Log("Determining if the database is available...", LoggerLevelStartup.Instance);
|
||||||
bool dbConnected = ServerStatics.DbConnected;
|
bool dbConnected = ServerStatics.DbConnected;
|
||||||
Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance);
|
Logger.Log(dbConnected ? "Connected to the database." : "Database unavailable! Exiting.", LoggerLevelStartup.Instance);
|
||||||
|
|
||||||
if (!dbConnected) Environment.Exit(1);
|
if (!dbConnected) Environment.Exit(1);
|
||||||
using Database database = new();
|
using Database database = new();
|
||||||
|
|
||||||
Logger.Log("Migrating database...", LoggerLevelDatabase.Instance);
|
Logger.Log("Migrating database...", LoggerLevelDatabase.Instance);
|
||||||
MigrateDatabase(database);
|
MigrateDatabase(database);
|
||||||
|
|
||||||
if (ServerSettings.Instance.InfluxEnabled)
|
if (ServerSettings.Instance.InfluxEnabled)
|
||||||
{
|
{
|
||||||
Logger.Log("Influx logging is enabled. Starting influx logging...", LoggerLevelStartup.Instance);
|
Logger.Log("Influx logging is enabled. Starting influx logging...", LoggerLevelStartup.Instance);
|
||||||
InfluxHelper.StartLogging().Wait();
|
InfluxHelper.StartLogging().Wait();
|
||||||
if (ServerSettings.Instance.InfluxLoggingEnabled) Logger.AddLogger(new InfluxLogger());
|
if (ServerSettings.Instance.InfluxLoggingEnabled) Logger.AddLogger(new InfluxLogger());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Logger.Log
|
Logger.Log
|
||||||
(
|
(
|
||||||
"This is a debug build, so performance may suffer! " +
|
"This is a debug build, so performance may suffer! " +
|
||||||
|
@ -69,48 +69,47 @@ namespace LBPUnion.ProjectLighthouse
|
||||||
LoggerLevelStartup.Instance
|
LoggerLevelStartup.Instance
|
||||||
);
|
);
|
||||||
Logger.Log("You can do so by running any dotnet command with the flag: \"-c Release\". ", LoggerLevelStartup.Instance);
|
Logger.Log("You can do so by running any dotnet command with the flag: \"-c Release\". ", LoggerLevelStartup.Instance);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (args.Length != 0)
|
if (args.Length != 0)
|
||||||
{
|
|
||||||
MaintenanceHelper.RunCommand(args).Wait();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stopwatch.Stop();
|
|
||||||
Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance);
|
|
||||||
|
|
||||||
CreateHostBuilder(args).Build().Run();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MigrateDatabase(Database database)
|
|
||||||
{
|
{
|
||||||
Stopwatch stopwatch = new();
|
MaintenanceHelper.RunCommand(args).Wait();
|
||||||
stopwatch.Start();
|
return;
|
||||||
|
|
||||||
database.Database.MigrateAsync().Wait();
|
|
||||||
|
|
||||||
stopwatch.Stop();
|
|
||||||
Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IHostBuilder CreateHostBuilder(string[] args)
|
stopwatch.Stop();
|
||||||
=> Host.CreateDefaultBuilder(args)
|
Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance);
|
||||||
.ConfigureWebHostDefaults
|
|
||||||
(
|
CreateHostBuilder(args).Build().Run();
|
||||||
webBuilder =>
|
|
||||||
{
|
|
||||||
webBuilder.UseStartup<Startup>();
|
|
||||||
webBuilder.UseWebRoot("StaticFiles");
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.ConfigureLogging
|
|
||||||
(
|
|
||||||
logging =>
|
|
||||||
{
|
|
||||||
logging.ClearProviders();
|
|
||||||
logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, AspNetToKettuLoggerProvider>());
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void MigrateDatabase(Database database)
|
||||||
|
{
|
||||||
|
Stopwatch stopwatch = new();
|
||||||
|
stopwatch.Start();
|
||||||
|
|
||||||
|
database.Database.MigrateAsync().Wait();
|
||||||
|
|
||||||
|
stopwatch.Stop();
|
||||||
|
Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IHostBuilder CreateHostBuilder(string[] args)
|
||||||
|
=> Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureWebHostDefaults
|
||||||
|
(
|
||||||
|
webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
webBuilder.UseWebRoot("StaticFiles");
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.ConfigureLogging
|
||||||
|
(
|
||||||
|
logging =>
|
||||||
|
{
|
||||||
|
logging.ClearProviders();
|
||||||
|
logging.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, AspNetToKettuLoggerProvider>());
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Serialization
|
namespace LBPUnion.ProjectLighthouse.Serialization;
|
||||||
|
|
||||||
|
public class JsonOutputFormatter : StringOutputFormatter
|
||||||
{
|
{
|
||||||
public class JsonOutputFormatter : StringOutputFormatter
|
public JsonOutputFormatter()
|
||||||
{
|
{
|
||||||
public JsonOutputFormatter()
|
this.SupportedMediaTypes.Add("text/json");
|
||||||
{
|
this.SupportedMediaTypes.Add("application/json");
|
||||||
this.SupportedMediaTypes.Add("text/json");
|
|
||||||
this.SupportedMediaTypes.Add("application/json");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,35 +2,34 @@ using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LBPUnion.ProjectLighthouse.Serialization
|
namespace LBPUnion.ProjectLighthouse.Serialization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// LBP doesn't like the XML serializer by C# that much, and it cant be controlled that much (cant have two root
|
||||||
|
/// elements),
|
||||||
|
/// so I wrote my own crappy one.
|
||||||
|
/// </summary>
|
||||||
|
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||||
|
public static class LbpSerializer
|
||||||
{
|
{
|
||||||
/// <summary>
|
public static string BlankElement(string key) => $"<{key}></{key}>";
|
||||||
/// LBP doesn't like the XML serializer by C# that much, and it cant be controlled that much (cant have two root
|
|
||||||
/// elements),
|
|
||||||
/// so I wrote my own crappy one.
|
|
||||||
/// </summary>
|
|
||||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
|
||||||
public static class LbpSerializer
|
|
||||||
{
|
|
||||||
public static string BlankElement(string key) => $"<{key}></{key}>";
|
|
||||||
|
|
||||||
public static string StringElement(KeyValuePair<string, object> pair) => $"<{pair.Key}>{pair.Value}</{pair.Key}>";
|
public static string StringElement(KeyValuePair<string, object> pair) => $"<{pair.Key}>{pair.Value}</{pair.Key}>";
|
||||||
|
|
||||||
public static string StringElement(string key, bool value) => $"<{key}>{value.ToString().ToLower()}</{key}>";
|
public static string StringElement(string key, bool value) => $"<{key}>{value.ToString().ToLower()}</{key}>";
|
||||||
|
|
||||||
public static string StringElement(string key, object value) => $"<{key}>{value}</{key}>";
|
public static string StringElement(string key, object value) => $"<{key}>{value}</{key}>";
|
||||||
|
|
||||||
public static string TaggedStringElement
|
public static string TaggedStringElement
|
||||||
(KeyValuePair<string, object> pair, KeyValuePair<string, object> tagPair)
|
(KeyValuePair<string, object> pair, KeyValuePair<string, object> tagPair)
|
||||||
=> $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}</{pair.Key}>";
|
=> $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}</{pair.Key}>";
|
||||||
|
|
||||||
public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}</{key}>";
|
public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}</{key}>";
|
||||||
|
|
||||||
public static string TaggedStringElement(string key, object value, Dictionary<string, object> attrKeyValuePairs)
|
public static string TaggedStringElement(string key, object value, Dictionary<string, object> attrKeyValuePairs)
|
||||||
=> $"<{key} " + attrKeyValuePairs.Aggregate(string.Empty, (current, kvp) => current + $"{kvp.Key}=\"{kvp.Value}\" ") + $">{value}</{key}>";
|
=> $"<{key} " + attrKeyValuePairs.Aggregate(string.Empty, (current, kvp) => current + $"{kvp.Key}=\"{kvp.Value}\" ") + $">{value}</{key}>";
|
||||||
|
|
||||||
public static string Elements
|
public static string Elements
|
||||||
(params KeyValuePair<string, object>[] pairs)
|
(params KeyValuePair<string, object>[] pairs)
|
||||||
=> pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair));
|
=> pairs.Aggregate(string.Empty, (current, pair) => current + StringElement(pair));
|
||||||
}
|
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue