diff --git a/.gitignore b/.gitignore
index ff16b3ae..d0391f6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,9 @@
+# LBP resources.
+r/
+
+# rider
+.idea/
+
r/ # cdn resources folder
bin/
obj/
@@ -12,8 +18,14 @@ riderModule.iml
/ProjectLighthouse/logs/*
/ProjectLighthouse/ProjectLighthouse.csproj.user
.vs/
-.vscode/
-.editorconfig
lighthouse.config.json
gitBranch.txt
gitVersion.txt
+ProjectLighthouse/.vscode/tasks.json
+ProjectLighthouse/.vscode/launch.json
+logs/Startup.log
+logs/LoggerInfo.log
+logs/all.log
+.vscode/tasks.json
+.vscode/launch.json
+.editorconfig
diff --git a/.idea/.idea.ProjectLighthouse/.idea/dataSources.xml b/.idea/.idea.ProjectLighthouse/.idea/dataSources.xml
index 4b88ef02..702fe04b 100644
--- a/.idea/.idea.ProjectLighthouse/.idea/dataSources.xml
+++ b/.idea/.idea.ProjectLighthouse/.idea/dataSources.xml
@@ -4,9 +4,16 @@
mysql.8
true
- com.mysql.cj.jdbc.Driver
+ com.mysql.cj.jdbc.NonRegisteringDriver
jdbc:mysql://localhost:3306/lighthouse
$ProjectFileDir$
+
+ mariadb
+ true
+ org.mariadb.jdbc.Driver
+ jdbc:mariadb://localhost:3306
+ $ProjectFileDir$
+
\ No newline at end of file
diff --git a/.idea/.idea.ProjectLighthouse/.idea/jsLibraryMappings.xml b/.idea/.idea.ProjectLighthouse/.idea/jsLibraryMappings.xml
new file mode 100644
index 00000000..e0f60e14
--- /dev/null
+++ b/.idea/.idea.ProjectLighthouse/.idea/jsLibraryMappings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ProjectLighthouse.Tests/LighthouseTest.cs b/ProjectLighthouse.Tests/LighthouseTest.cs
index 3efb3684..22babd5c 100644
--- a/ProjectLighthouse.Tests/LighthouseTest.cs
+++ b/ProjectLighthouse.Tests/LighthouseTest.cs
@@ -9,6 +9,7 @@ using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
+using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Tests
{
@@ -25,9 +26,15 @@ namespace LBPUnion.ProjectLighthouse.Tests
this.Client = this.Server.CreateClient();
}
- public async Task AuthenticateResponse(int number = 0)
+ public async Task AuthenticateResponse(int number = 0, bool createUser = true)
{
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}";
diff --git a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs b/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs
index 346b957b..d7f5fd19 100644
--- a/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs
+++ b/ProjectLighthouse.Tests/Tests/AuthenticationTests.cs
@@ -49,11 +49,10 @@ namespace LBPUnion.ProjectLighthouse.Tests
{
LoginResult loginResult = await this.Authenticate();
- HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/announce", loginResult.AuthTicket);
+ HttpResponseMessage response = await this.AuthenticatedRequest("/LITTLEBIGPLANETPS3_XML/enterLevel/1", loginResult.AuthTicket);
string responseContent = await response.Content.ReadAsStringAsync();
- Assert.True(response.IsSuccessStatusCode);
- Assert.Contains("You are now logged in", responseContent);
+ Assert.False(response.StatusCode == HttpStatusCode.Forbidden);
}
[DatabaseFact]
diff --git a/ProjectLighthouse.Tests/Tests/DatabaseTests.cs b/ProjectLighthouse.Tests/Tests/DatabaseTests.cs
index f2071792..01d49d1f 100644
--- a/ProjectLighthouse.Tests/Tests/DatabaseTests.cs
+++ b/ProjectLighthouse.Tests/Tests/DatabaseTests.cs
@@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Types;
namespace LBPUnion.ProjectLighthouse.Tests
@@ -12,8 +13,8 @@ namespace LBPUnion.ProjectLighthouse.Tests
await using Database database = new();
int rand = new Random().Next();
- User userA = await database.CreateUser("createUserTwiceTest" + rand);
- User userB = await database.CreateUser("createUserTwiceTest" + rand);
+ User userA = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken());
+ User userB = await database.CreateUser("createUserTwiceTest" + rand, HashHelper.GenerateAuthToken());
database.Users.Remove(userA);
database.Users.Remove(userB);
diff --git a/ProjectLighthouse.Tests/Tests/MatchTests.cs b/ProjectLighthouse.Tests/Tests/MatchTests.cs
index 5c52f9f5..721dc941 100644
--- a/ProjectLighthouse.Tests/Tests/MatchTests.cs
+++ b/ProjectLighthouse.Tests/Tests/MatchTests.cs
@@ -3,6 +3,7 @@ using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Types;
using Xunit;
@@ -36,7 +37,6 @@ namespace LBPUnion.ProjectLighthouse.Tests
semaphore.Release();
Assert.True(result.IsSuccessStatusCode);
}
- public async Task GetPlayerCount() => Convert.ToInt32(await this.Client.GetStringAsync("LITTLEBIGPLANETPS3_XML/totalPlayerCount"));
[DatabaseFact]
public async Task ShouldIncrementPlayerCount()
@@ -45,14 +45,14 @@ namespace LBPUnion.ProjectLighthouse.Tests
await semaphore.WaitAsync();
- int oldPlayerCount = await this.GetPlayerCount();
+ int oldPlayerCount = await StatisticsHelper.RecentMatches();
HttpResponseMessage result = await this.AuthenticatedUploadDataRequest
("LITTLEBIGPLANETPS3_XML/match", Encoding.ASCII.GetBytes("[UpdateMyPlayerData,[\"Player\":\"1984\"]]"), loginResult.AuthTicket);
Assert.True(result.IsSuccessStatusCode);
- int playerCount = await this.GetPlayerCount();
+ int playerCount = await StatisticsHelper.RecentMatches();
semaphore.Release();
Assert.Equal(oldPlayerCount + 1, playerCount);
diff --git a/ProjectLighthouse.Tests/Tests/SlotTests.cs b/ProjectLighthouse.Tests/Tests/SlotTests.cs
index 52f4d690..7af3d264 100644
--- a/ProjectLighthouse.Tests/Tests/SlotTests.cs
+++ b/ProjectLighthouse.Tests/Tests/SlotTests.cs
@@ -1,5 +1,6 @@
using System.Net.Http;
using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Profiles;
@@ -14,8 +15,8 @@ namespace LBPUnion.ProjectLighthouse.Tests
{
await using Database database = new();
- User userA = await database.CreateUser("unitTestUser0");
- User userB = await database.CreateUser("unitTestUser1");
+ User userA = await database.CreateUser("unitTestUser0", HashHelper.GenerateAuthToken());
+ User userB = await database.CreateUser("unitTestUser1", HashHelper.GenerateAuthToken());
Location l = new()
{
diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings
index 34c3a59a..385c8365 100644
--- a/ProjectLighthouse.sln.DotSettings
+++ b/ProjectLighthouse.sln.DotSettings
@@ -78,7 +78,9 @@
MM
NAT
NP
+ PS
PSP
+ RPCS
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy>
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
@@ -126,5 +128,6 @@
True
True
True
+ True
True
True
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/CommentController.cs b/ProjectLighthouse/Controllers/CommentController.cs
index fd7cbbbf..c4e83394 100644
--- a/ProjectLighthouse/Controllers/CommentController.cs
+++ b/ProjectLighthouse/Controllers/CommentController.cs
@@ -47,7 +47,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
XmlSerializer serializer = new(typeof(Comment));
Comment? comment = (Comment?)serializer.Deserialize(new StringReader(bodyString));
- User? poster = await this.database.UserFromRequest(this.Request);
+ User? poster = await this.database.UserFromGameRequest(this.Request);
if (poster == null) return this.StatusCode(403, "");
User? target = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
@@ -66,14 +66,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("deleteUserComment/{username}")]
public async Task DeleteComment([FromQuery] int commentId, string username)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
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, "");
diff --git a/ProjectLighthouse/Controllers/EnterLevelController.cs b/ProjectLighthouse/Controllers/EnterLevelController.cs
index af799c02..8d45be74 100644
--- a/ProjectLighthouse/Controllers/EnterLevelController.cs
+++ b/ProjectLighthouse/Controllers/EnterLevelController.cs
@@ -24,13 +24,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("play/user/{slotId}")]
public async Task PlayLevel(int slotId)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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, "");
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
@@ -62,7 +62,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
v = await visited.FirstOrDefaultAsync();
}
-
+
if (v == null) return this.NotFound();
switch (gameVersion)
@@ -94,7 +94,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("enterLevel/{id:int}")]
public async Task EnterLevel(int id)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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);
diff --git a/ProjectLighthouse/Controllers/FriendsController.cs b/ProjectLighthouse/Controllers/FriendsController.cs
index e4b2ef6a..32c8b81a 100644
--- a/ProjectLighthouse/Controllers/FriendsController.cs
+++ b/ProjectLighthouse/Controllers/FriendsController.cs
@@ -26,7 +26,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("npdata")]
public async Task NPData()
{
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
this.Request.Body.Position = 0;
@@ -69,18 +69,16 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("myFriends")]
public async Task MyFriends()
{
- (User, Token)? userAndToken = await this.database.UserAndTokenFromRequest(this.Request);
+ (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;
- Token token = userAndToken.Value.Item2;
+ 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)
@@ -88,7 +86,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
User? friend = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == friendId);
if (friend == null) continue;
- friends += friend.Serialize(token.GameVersion);
+ friends += friend.Serialize(gameToken.GameVersion);
}
return this.Ok(LbpSerializer.StringElement("myFriends", friends));
diff --git a/ProjectLighthouse/Controllers/ListController.cs b/ProjectLighthouse/Controllers/ListController.cs
index f4139dc1..8accb958 100644
--- a/ProjectLighthouse/Controllers/ListController.cs
+++ b/ProjectLighthouse/Controllers/ListController.cs
@@ -29,7 +29,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("slots/lolcatftw/{username}")]
public async Task GetLevelQueue(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
{
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
@@ -56,22 +56,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("lolcatftw/add/user/{id:int}")]
public async Task AddQueuedLevel(int id)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
- QueuedLevel? queuedLevel = await this.database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id);
- if (queuedLevel != null) return this.Ok();
+ Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
+ if (slot == null) return this.NotFound();
- this.database.QueuedLevels.Add
- (
- new QueuedLevel
- {
- SlotId = id,
- UserId = user.UserId,
- }
- );
-
- await this.database.SaveChangesAsync();
+ await this.database.QueueLevel(user, slot);
return this.Ok();
}
@@ -79,13 +70,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("lolcatftw/remove/user/{id:int}")]
public async Task RemoveQueuedLevel(int id)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
- QueuedLevel? queuedLevel = await this.database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id);
- if (queuedLevel != null) this.database.QueuedLevels.Remove(queuedLevel);
+ Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
+ if (slot == null) return this.NotFound();
- await this.database.SaveChangesAsync();
+ await this.database.UnqueueLevel(user, slot);
return this.Ok();
}
@@ -93,7 +84,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("lolcatftw/clear")]
public async Task ClearQueuedLevels()
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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));
@@ -110,7 +101,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("favouriteSlots/{username}")]
public async Task GetFavouriteSlots(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
{
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
@@ -137,22 +128,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("favourite/slot/user/{id:int}")]
public async Task AddFavouriteSlot(int id)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
- HeartedLevel? heartedLevel = await this.database.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id);
- if (heartedLevel != null) return this.Ok();
+ Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
+ if (slot == null) return this.NotFound();
- this.database.HeartedLevels.Add
- (
- new HeartedLevel
- {
- SlotId = id,
- UserId = user.UserId,
- }
- );
-
- await this.database.SaveChangesAsync();
+ await this.database.HeartLevel(user, slot);
return this.Ok();
}
@@ -160,13 +142,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("unfavourite/slot/user/{id:int}")]
public async Task RemoveFavouriteSlot(int id)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
- HeartedLevel? heartedLevel = await this.database.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id);
- if (heartedLevel != null) this.database.HeartedLevels.Remove(heartedLevel);
+ Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
+ if (slot == null) return this.NotFound();
- await this.database.SaveChangesAsync();
+ await this.database.UnheartLevel(user, slot);
return this.Ok();
}
@@ -180,7 +162,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("favouriteUsers/{username}")]
public async Task GetFavouriteUsers(string username, [FromQuery] int pageSize, [FromQuery] int pageStart)
{
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
IEnumerable heartedProfiles = this.database.HeartedProfiles.Include
@@ -204,26 +186,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("favourite/user/{username}")]
public async Task AddFavouriteUser(string username)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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();
- HeartedProfile? heartedProfile = await this.database.HeartedProfiles.FirstOrDefaultAsync
- (q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId);
- if (heartedProfile != null) return this.Ok();
-
- this.database.HeartedProfiles.Add
- (
- new HeartedProfile
- {
- HeartedUserId = heartedUser.UserId,
- UserId = user.UserId,
- }
- );
-
- await this.database.SaveChangesAsync();
+ await this.database.HeartUser(user, heartedUser);
return this.Ok();
}
@@ -231,17 +200,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("unfavourite/user/{username}")]
public async Task RemoveFavouriteUser(string username)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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();
- HeartedProfile? heartedProfile = await this.database.HeartedProfiles.FirstOrDefaultAsync
- (q => q.UserId == user.UserId && q.HeartedUserId == heartedUser.UserId);
- if (heartedProfile != null) this.database.HeartedProfiles.Remove(heartedProfile);
-
- await this.database.SaveChangesAsync();
+ await this.database.UnheartUser(user, heartedUser);
return this.Ok();
}
diff --git a/ProjectLighthouse/Controllers/LoginController.cs b/ProjectLighthouse/Controllers/LoginController.cs
index 6bce4863..a8126a94 100644
--- a/ProjectLighthouse/Controllers/LoginController.cs
+++ b/ProjectLighthouse/Controllers/LoginController.cs
@@ -1,5 +1,6 @@
#nullable enable
using System.IO;
+using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Kettu;
@@ -8,6 +9,7 @@ using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Controllers
{
@@ -46,19 +48,48 @@ namespace LBPUnion.ProjectLighthouse.Controllers
string userLocation = ipAddress.ToString();
- Token? token = await this.database.AuthenticateUser(loginData, userLocation, titleId);
+ GameToken? token = await this.database.AuthenticateUser(loginData, userLocation, titleId);
if (token == null) return this.StatusCode(403, "");
- User? user = await this.database.UserFromToken(token);
+ User? user = await this.database.UserFromGameToken(token, true);
if (user == null) return this.StatusCode(403, "");
+ if (ServerSettings.Instance.UseExternalAuth)
+ {
+ 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();
+ return this.StatusCode(403, "");
+ }
+
+ AuthenticationAttempt authAttempt = new()
+ {
+ GameToken = token,
+ GameTokenId = token.TokenId,
+ Timestamp = TimestampHelper.Timestamp,
+ IPAddress = userLocation,
+ 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();
+
Logger.Log($"Successfully logged in user {user.Username} as {token.GameVersion} client ({titleId})", LoggerLevelLogin.Instance);
// Create a new room on LBP2+/Vita
- if (token.GameVersion != GameVersion.LittleBigPlanet1)
- {
- RoomHelper.CreateRoom(user);
- }
+ if (token.GameVersion != GameVersion.LittleBigPlanet1) RoomHelper.CreateRoom(user);
return this.Ok
(
diff --git a/ProjectLighthouse/Controllers/MatchController.cs b/ProjectLighthouse/Controllers/MatchController.cs
index 94f4fff5..42ddd75e 100644
--- a/ProjectLighthouse/Controllers/MatchController.cs
+++ b/ProjectLighthouse/Controllers/MatchController.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Kettu;
@@ -10,7 +9,6 @@ using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Match;
-using LBPUnion.ProjectLighthouse.Types.Profiles;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -32,13 +30,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[Produces("text/plain")]
public async Task Match()
{
- (User, Token)? userAndToken = await this.database.UserAndTokenFromRequest(this.Request);
+ (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;
- Token token = userAndToken.Value.Item2;
+ GameToken gameToken = userAndToken.Value.Item2;
#region Parse match data
@@ -72,51 +70,28 @@ namespace LBPUnion.ProjectLighthouse.Controllers
#endregion
- #region Update LastMatch
-
- LastMatch? lastMatch = await this.database.LastMatches.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync();
-
- // below makes it not look like trash
- // ReSharper disable once ConvertIfStatementToNullCoalescingExpression
- if (lastMatch == null)
- {
- lastMatch = new LastMatch
- {
- UserId = user.UserId,
- };
- this.database.LastMatches.Add(lastMatch);
- }
-
- lastMatch.Timestamp = TimestampHelper.Timestamp;
-
- await this.database.SaveChangesAsync();
-
- #endregion
+ await LastContactHelper.SetLastContact(user, gameToken.GameVersion);
#region Process match data
if (matchData is UpdateMyPlayerData playerData)
{
- MatchHelper.SetUserLocation(user.UserId, token.UserLocation);
+ MatchHelper.SetUserLocation(user.UserId, gameToken.UserLocation);
Room? room = RoomHelper.FindRoomByUser(user, true);
if (playerData.RoomState != null)
- {
- if (room != null && Equals(room.Host, user)) room.State = (RoomState)playerData.RoomState;
- }
+ 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, token.UserLocation);
+ FindBestRoomResponse? response = RoomHelper.FindBestRoom(user, 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);
- }
+ foreach (Player player in response.Players) MatchHelper.AddUserRecentlyDivedIn(user.UserId, player.User.UserId);
return this.Ok($"[{{\"StatusCode\":200}},{serialized}]");
}
@@ -128,10 +103,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
User? player = await this.database.Users.FirstOrDefaultAsync(u => u.Username == playerUsername);
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
- if (player != null)
- {
- users.Add(player);
- }
+ if (player != null) users.Add(player);
else return this.BadRequest();
}
diff --git a/ProjectLighthouse/Controllers/MessageController.cs b/ProjectLighthouse/Controllers/MessageController.cs
index beab0d93..80851bbe 100644
--- a/ProjectLighthouse/Controllers/MessageController.cs
+++ b/ProjectLighthouse/Controllers/MessageController.cs
@@ -27,10 +27,20 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("announce")]
public async Task Announce()
{
- User user = await this.database.UserFromRequest(this.Request);
+ User user = await this.database.UserFromGameRequest(this.Request, true);
if (user == null) return this.StatusCode(403, "");
- return this.Ok($"You are now logged in as user {user.Username} (id {user.UserId}).\n\n" + ServerSettings.Instance.EulaText);
+ if (ServerSettings.Instance.UseExternalAuth)
+ return this.Ok
+ (
+ "Please stay on this screen.\n" +
+ $"Before continuing, you must approve this session at {ServerSettings.Instance.ExternalUrl}.\n" +
+ "Please keep in mind that if the session is denied you may have to wait up to 5-10 minutes to try logging in again.\n" +
+ "Once approved, you may press X and continue.\n\n" +
+ ServerSettings.Instance.EulaText
+ );
+
+ return this.Ok($"You are now logged in as {user.Username} (id: {user.UserId}).\n\n" + ServerSettings.Instance.EulaText);
}
[HttpGet("notification")]
@@ -42,7 +52,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("filter")]
public async Task Filter()
{
- User user = await this.database.UserFromRequest(this.Request);
+ User user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
string loggedText = await new StreamReader(this.Request.Body).ReadToEndAsync();
diff --git a/ProjectLighthouse/Controllers/PhotosController.cs b/ProjectLighthouse/Controllers/PhotosController.cs
index 6dce4228..59c5df91 100644
--- a/ProjectLighthouse/Controllers/PhotosController.cs
+++ b/ProjectLighthouse/Controllers/PhotosController.cs
@@ -9,6 +9,7 @@ using Kettu;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
+using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -29,9 +30,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("uploadPhoto")]
public async Task UploadPhoto()
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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();
@@ -39,6 +42,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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;
@@ -103,10 +114,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers
if (userFromQuery == null) return this.NotFound();
List 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));
- }
+ 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)
@@ -120,7 +129,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("deletePhoto/{id:int}")]
public async Task DeletePhoto(int id)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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);
diff --git a/ProjectLighthouse/Controllers/PublishController.cs b/ProjectLighthouse/Controllers/PublishController.cs
index d7094138..ecf7a66d 100644
--- a/ProjectLighthouse/Controllers/PublishController.cs
+++ b/ProjectLighthouse/Controllers/PublishController.cs
@@ -9,6 +9,7 @@ using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Profiles;
+using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -32,9 +33,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("startPublish")]
public async Task StartPublish()
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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
@@ -65,17 +68,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("publish")]
public async Task Publish()
{
-// User user = await this.database.UserFromRequest(this.Request);
- (User, Token)? userAndToken = await this.database.UserAndTokenFromRequest(this.Request);
+// User user = await this.database.UserFromGameRequest(this.Request);
+ (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;
- Token token = userAndToken.Value.Item2;
+ GameToken gameToken = userAndToken.Value.Item2;
+
+ if (user.UsedSlots >= ServerSettings.Instance.EntitledSlots) return this.BadRequest();
Slot? slot = await this.GetSlotFromBody();
- if (slot == null || slot.Location == null) return this.BadRequest();
+ if (slot?.Location == null) return this.BadRequest();
// Republish logic
if (slot.SlotId != 0)
@@ -93,9 +98,35 @@ namespace LBPUnion.ProjectLighthouse.Controllers
slot.CreatorId = oldSlot.CreatorId;
slot.LocationId = oldSlot.LocationId;
slot.SlotId = oldSlot.SlotId;
+
+ slot.PlaysLBP1 = oldSlot.PlaysLBP1;
+ slot.PlaysLBP1Complete = oldSlot.PlaysLBP1Complete;
+ slot.PlaysLBP1Unique = oldSlot.PlaysLBP1Unique;
+
+ slot.PlaysLBP2 = oldSlot.PlaysLBP2;
+ slot.PlaysLBP2Complete = oldSlot.PlaysLBP2Complete;
+ slot.PlaysLBP2Unique = oldSlot.PlaysLBP2Unique;
+
+ slot.PlaysLBP3 = oldSlot.PlaysLBP3;
+ slot.PlaysLBP3Complete = oldSlot.PlaysLBP3Complete;
+ slot.PlaysLBP3Unique = oldSlot.PlaysLBP3Unique;
+
+ slot.PlaysLBPVita = oldSlot.PlaysLBPVita;
+ slot.PlaysLBPVitaComplete = oldSlot.PlaysLBPVitaComplete;
+ slot.PlaysLBPVitaUnique = oldSlot.PlaysLBPVitaUnique;
+
slot.FirstUploaded = oldSlot.FirstUploaded;
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
- slot.GameVersion = token.GameVersion;
+
+ 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();
@@ -114,7 +145,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
slot.CreatorId = user.UserId;
slot.FirstUploaded = TimeHelper.UnixTimeMilliseconds();
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
- slot.GameVersion = token.GameVersion;
+ slot.GameVersion = gameToken.GameVersion;
if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0)
{
@@ -131,7 +162,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("unpublish/{id:int}")]
public async Task Unpublish(int id)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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);
diff --git a/ProjectLighthouse/Controllers/ResourcesController.cs b/ProjectLighthouse/Controllers/ResourcesController.cs
index adb7734b..1b645471 100644
--- a/ProjectLighthouse/Controllers/ResourcesController.cs
+++ b/ProjectLighthouse/Controllers/ResourcesController.cs
@@ -14,8 +14,8 @@ using IOFile = System.IO.File;
namespace LBPUnion.ProjectLighthouse.Controllers
{
[ApiController]
- [Route("LITTLEBIGPLANETPS3_XML/")]
[Produces("text/xml")]
+ [Route("LITTLEBIGPLANETPS3_XML")]
public class ResourcesController : ControllerBase
{
[HttpPost("showModerated")]
@@ -39,6 +39,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers
return this.Ok(LbpSerializer.StringElement("resources", resources));
}
+ [ResponseCache(Duration = 86400)]
+ [HttpGet("/gameAssets/{hash}")]
[HttpGet("r/{hash}")]
public IActionResult GetResource(string hash)
{
@@ -68,9 +70,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers
return this.UnprocessableEntity();
}
- if (HashHelper.Sha1Hash(file.Data) != hash)
+ string calculatedHash = HashHelper.Sha1Hash(file.Data).ToLower();
+ if (calculatedHash != hash)
{
- Logger.Log($"File hash does not match the uploaded file! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance);
+ Logger.Log
+ (
+ $"File hash does not match the uploaded file! (hash: {hash}, calculatedHash: {calculatedHash}, type: {file.FileType})",
+ LoggerLevelResources.Instance
+ );
return this.Conflict();
}
diff --git a/ProjectLighthouse/Controllers/ReviewController.cs b/ProjectLighthouse/Controllers/ReviewController.cs
index dc04da77..50234b25 100644
--- a/ProjectLighthouse/Controllers/ReviewController.cs
+++ b/ProjectLighthouse/Controllers/ReviewController.cs
@@ -1,10 +1,10 @@
#nullable enable
using System;
-using System.IO;
-using System.Xml.Serialization;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading.Tasks;
+using System.Xml.Serialization;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
@@ -31,7 +31,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("rate/user/{slotId}")]
public async Task Rate(int slotId, [FromQuery] int rating)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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);
@@ -58,7 +58,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("dpadrate/user/{slotId:int}")]
public async Task DPadRate(int slotId, [FromQuery] int rating)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ 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);
@@ -76,32 +76,41 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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}")]
- public async Task PostReview(int slotId) {
- User? user = await this.database.UserFromRequest(this.Request);
+ public async Task 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) {
- review = new();
+
+ if (review == null)
+ {
+ review = new Review();
review.SlotId = slotId;
review.ReviewerId = user.UserId;
- review.DeletedBy = "none";
-
+ 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();
-
- // sometimes the game posts a review without also calling dpadrate/user/etc (why??)
+
+ // 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)
{
@@ -113,7 +122,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
ratedLevel.Rating = newReview.Thumb;
-
await this.database.SaveChangesAsync();
@@ -121,94 +129,150 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
[HttpGet("reviewsFor/user/{slotId:int}")]
- public async Task ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
+ public async Task ReviewsFor(int slotId, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
{
- User? user = await this.database.UserFromRequest(this.Request);
- if (user == null) return this.StatusCode(403, "");
+ (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
- Token? token = await this.database.TokenFromRequest(this.Request);
- if (token == null) return this.StatusCode(403, "");
+ if (userAndToken == null) return this.StatusCode(403, "");
- GameVersion gameVersion = token.GameVersion;
+ // ReSharper disable once PossibleInvalidOperationException
+ User user = userAndToken.Value.Item1;
+ GameToken gameToken = userAndToken.Value.Item2;
+
+ GameVersion gameVersion = gameToken.GameVersion;
Random rand = new();
- IEnumerable reviews = this.database.Reviews.Where(r => r.SlotId == slotId && r.Slot.GameVersion <= gameVersion)
+ 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
+ (r => r.UserId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion);
+
+ yourReview = new Review();
+ 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 reviews = this.database.Reviews.Where(r => r.SlotId == slotId && r.Slot.GameVersion <= gameVersion)
.Include(r => r.Reviewer)
.Include(r => r.Slot)
- .AsEnumerable() // performance? Needed for next line (ThumbsUp is not in DB)
.OrderByDescending(r => r.ThumbsUp)
- .ThenByDescending(_ => rand.Next())
+ .ThenByDescending(_ => EF.Functions.Random())
.Skip(pageStart - 1)
.Take(pageSize);
-
- string inner = Enumerable.Aggregate(reviews, string.Empty, (current, review) => {
- RatedLevel? ratedLevel = this.database.RatedLevels.FirstOrDefault(r => r.SlotId == slotId && r.UserId == review.ReviewerId);
- RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId);
-
- return current + review.Serialize(ratedLevel, ratedReview);
- });
- string response = LbpSerializer.TaggedStringElement("reviews", inner, new Dictionary
+ IEnumerable 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;
+
+ return current + review.Serialize();
+ }
+ );
+
+ string response = LbpSerializer.TaggedStringElement
+ (
+ "reviews",
+ inner,
+ new Dictionary
+ {
{
- {
- "hint_start", pageStart + pageSize
- },
- {
- "hint", pageStart // not sure
- },
- });
+ "hint_start", pageStart + pageSize
+ },
+ {
+ "hint", pageStart // not sure
+ },
+ }
+ );
return this.Ok(response);
}
[HttpGet("reviewsBy/{username}")]
- public async Task ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
+ public async Task ReviewsBy(string username, [FromQuery] int pageStart = 1, [FromQuery] int pageSize = 10)
{
- User? user = await this.database.UserFromRequest(this.Request);
- if (user == null) return this.StatusCode(403, "");
+ (User, GameToken)? userAndToken = await this.database.UserAndGameTokenFromRequest(this.Request);
- Token? token = await this.database.TokenFromRequest(this.Request);
- if (token == null) return this.StatusCode(403, "");
+ if (userAndToken == null) return this.StatusCode(403, "");
- GameVersion gameVersion = token.GameVersion;
+ // ReSharper disable once PossibleInvalidOperationException
+ User user = userAndToken.Value.Item1;
+ GameToken gameToken = userAndToken.Value.Item2;
+
+ GameVersion gameVersion = gameToken.GameVersion;
IEnumerable reviews = this.database.Reviews.Where(r => r.Reviewer.Username == username && r.Slot.GameVersion <= gameVersion)
.Include(r => r.Reviewer)
.Include(r => r.Slot)
- .AsEnumerable() // performance?
.OrderByDescending(r => r.Timestamp)
.Skip(pageStart - 1)
.Take(pageSize);
-
- string inner = Enumerable.Aggregate(reviews, 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(ratedLevel, ratedReview);
- });
- string response = LbpSerializer.TaggedStringElement("reviews", inner, new Dictionary
+ 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
+ {
{
- {
- "hint_start", pageStart
- },
- {
- "hint", reviews.Last().Timestamp // Seems to be the timestamp of oldest
- },
- });
+ "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 RateReview(int slotId, string username, [FromQuery] int rating = 0) {
- User? user = await this.database.UserFromRequest(this.Request);
+ public async Task 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(403, "");
+ 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(403, "");
+ 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)
@@ -220,15 +284,26 @@ namespace LBPUnion.ProjectLighthouse.Controllers
this.database.RatedReviews.Add(ratedReview);
}
+ int oldThumb = ratedReview.Thumb;
ratedReview.Thumb = Math.Max(Math.Min(1, rating), -1);
+ if (oldThumb != ratedReview.Thumb)
+ {
+ if (oldThumb == -1) review.ThumbsDown--;
+ else if (oldThumb == 1) review.ThumbsUp--;
+
+ if (ratedReview.Thumb == -1) review.ThumbsDown++;
+ else if (ratedReview.Thumb == 1) review.ThumbsUp++;
+ }
+
await this.database.SaveChangesAsync();
return this.Ok();
}
[HttpPost("deleteReview/user/{slotId:int}/{username}")]
- public async Task DeleteReview(int slotId, string username) {
+ public async Task DeleteReview(int slotId, string username)
+ {
User? reviewer = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username);
if (reviewer == null) return this.StatusCode(403, "");
@@ -236,7 +311,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
if (review == null) return this.StatusCode(403, "");
review.Deleted = true;
- review.DeletedBy = "level_author"; // other value is "moderator"
+ review.DeletedBy = DeletedBy.LevelAuthor;
await this.database.SaveChangesAsync();
return this.Ok();
@@ -252,6 +327,5 @@ namespace LBPUnion.ProjectLighthouse.Controllers
return review;
}
-
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/ScoreController.cs b/ProjectLighthouse/Controllers/ScoreController.cs
index 6f75e4d1..8b08c214 100644
--- a/ProjectLighthouse/Controllers/ScoreController.cs
+++ b/ProjectLighthouse/Controllers/ScoreController.cs
@@ -28,13 +28,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("scoreboard/user/{id:int}")]
public async Task SubmitScore(int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false)
{
- (User, Token)? userAndToken = await this.database.UserAndTokenFromRequest(this.Request);
+ (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;
- Token token = userAndToken.Value.Item2;
+ GameToken gameToken = userAndToken.Value.Item2;
this.Request.Body.Position = 0;
string bodyString = await new StreamReader(this.Request.Body).ReadToEndAsync();
@@ -48,7 +48,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
Slot? slot = this.database.Slots.FirstOrDefault(s => s.SlotId == score.SlotId);
if (slot == null) return this.BadRequest();
- switch (token.GameVersion)
+ switch (gameToken.GameVersion)
{
case GameVersion.LittleBigPlanet1:
slot.PlaysLBP1Complete++;
@@ -80,7 +80,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
await this.database.SaveChangesAsync();
- string myRanking = GetScores(score.SlotId, score.Type, user);
+ string myRanking = this.GetScores(score.SlotId, score.Type, user);
return this.Ok(myRanking);
}
@@ -95,11 +95,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
public async Task TopScores(int slotId, int type, [FromQuery] int pageStart = -1, [FromQuery] int pageSize = 5)
{
// Get username
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
- return this.Ok(GetScores(slotId, type, user, pageStart, pageSize));
+ return this.Ok(this.GetScores(slotId, type, user, pageStart, pageSize));
}
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
@@ -139,17 +139,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
);
string res;
- if (myScore == null)
- {
- res = LbpSerializer.StringElement("scores", serializedScores);
- }
+ if (myScore == null) res = LbpSerializer.StringElement("scores", serializedScores);
else
- {
res = LbpSerializer.TaggedStringElement
(
"scores",
serializedScores,
- new Dictionary()
+ new Dictionary
{
{
"yourScore", myScore.Score.Points
@@ -162,7 +158,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}, // This is the denominator of your position globally in the side menu.
}
);
- }
return res;
}
diff --git a/ProjectLighthouse/Controllers/SearchController.cs b/ProjectLighthouse/Controllers/SearchController.cs
index 3c7708ba..42b1d3c1 100644
--- a/ProjectLighthouse/Controllers/SearchController.cs
+++ b/ProjectLighthouse/Controllers/SearchController.cs
@@ -1,7 +1,7 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using System;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types.Levels;
using Microsoft.AspNetCore.Mvc;
@@ -44,14 +44,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
s.SlotId.ToString().Equals(keyword)
);
- List slots = await dbQuery
- .Skip(pageStart - 1)
- .Take(Math.Min(pageSize, 30))
- .ToListAsync();
+ List slots = await dbQuery.Skip(pageStart - 1).Take(Math.Min(pageSize, 30)).ToListAsync();
string response = slots.Aggregate("", (current, slot) => current + slot.Serialize());
return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", dbQuery.Count()));
}
}
-}
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs
index a8be7e87..6e104235 100644
--- a/ProjectLighthouse/Controllers/SlotsController.cs
+++ b/ProjectLighthouse/Controllers/SlotsController.cs
@@ -3,11 +3,11 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Serialization;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Settings;
-using LBPUnion.ProjectLighthouse.Types.Reviews;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -27,7 +27,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("slots/by")]
public async Task SlotsBy([FromQuery] string u, [FromQuery] int pageStart, [FromQuery] int pageSize)
{
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
@@ -42,7 +42,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
.Include(s => s.Location)
.Where(s => s.Creator!.Username == user.Username)
.Skip(pageStart - 1)
- .Take(Math.Min(pageSize, ServerStatics.EntitledSlots)),
+ .Take(Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)),
string.Empty,
(current, slot) => current + slot.Serialize()
);
@@ -56,7 +56,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
new Dictionary
{
{
- "hint_start", pageStart + Math.Min(pageSize, ServerStatics.EntitledSlots)
+ "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
},
{
"total", user.UsedSlots
@@ -69,10 +69,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("s/user/{id:int}")]
public async Task SUser(int id)
{
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
@@ -86,13 +86,12 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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);
- Review? yourReview = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == id && r.ReviewerId == user.UserId);
- return this.Ok(slot.Serialize(ratedLevel, visitedLevel, yourReview));
+ return this.Ok(slot.Serialize(ratedLevel, visitedLevel));
}
[HttpGet("slots/lbp2cool")]
[HttpGet("slots/cool")]
- public async Task CoolSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] int? page = null)
+ public async Task CoolSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] int? page = null)
{
int _pageStart = pageStart;
if (page != null) _pageStart = (int)page * 30;
@@ -103,7 +102,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("slots")]
public async Task NewestSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
{
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
@@ -116,13 +115,29 @@ namespace LBPUnion.ProjectLighthouse.Controllers
.Take(Math.Min(pageSize, 30));
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize());
- return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
+ return this.Ok
+ (
+ LbpSerializer.TaggedStringElement
+ (
+ "slots",
+ response,
+ new Dictionary
+ {
+ {
+ "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
+ },
+ {
+ "total", await StatisticsHelper.SlotCount()
+ },
+ }
+ )
+ );
}
[HttpGet("slots/mmpicks")]
public async Task TeamPickedSlots([FromQuery] int pageStart, [FromQuery] int pageSize)
{
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
@@ -136,13 +151,29 @@ namespace LBPUnion.ProjectLighthouse.Controllers
.Take(Math.Min(pageSize, 30));
string response = Enumerable.Aggregate(slots, string.Empty, (current, slot) => current + slot.Serialize());
- return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "hint_start", pageStart + Math.Min(pageSize, 30)));
+ return this.Ok
+ (
+ LbpSerializer.TaggedStringElement
+ (
+ "slots",
+ response,
+ new Dictionary
+ {
+ {
+ "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
+ },
+ {
+ "total", await StatisticsHelper.MMPicksCount()
+ },
+ }
+ )
+ );
}
[HttpGet("slots/lbp2luckydip")]
public async Task LuckyDipSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] int seed)
{
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
GameVersion gameVersion = token.GameVersion;
@@ -155,15 +186,31 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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)));
+ return this.Ok
+ (
+ LbpSerializer.TaggedStringElement
+ (
+ "slots",
+ response,
+ new Dictionary
+ {
+ {
+ "hint_start", pageStart + Math.Min(pageSize, ServerSettings.Instance.EntitledSlots)
+ },
+ {
+ "total", await StatisticsHelper.SlotCount()
+ },
+ }
+ )
+ );
}
[HttpGet("slots/thumbs")]
- public async Task ThumbsSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null)
+ public async Task ThumbsSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null)
{
- // v--- not sure of API in LBP3 here, needs testing
+ // v--- not sure of API in LBP3 here, needs testing
GameVersion gameVersion = gameFilterType == "both" ? GameVersion.LittleBigPlanet2 : GameVersion.LittleBigPlanet1;
-
+
long oldestTime;
string _dateFilterType = dateFilterType != null ? dateFilterType : "";
@@ -198,11 +245,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
[HttpGet("slots/mostUniquePlays")]
- public async Task MostUniquePlaysSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null)
+ public async Task MostUniquePlaysSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null)
{
- // v--- not sure of API in LBP3 here, needs testing
+ // v--- not sure of API in LBP3 here, needs testing
GameVersion gameVersion = gameFilterType == "both" ? GameVersion.LittleBigPlanet2 : GameVersion.LittleBigPlanet1;
-
+
long oldestTime;
string _dateFilterType = dateFilterType != null ? dateFilterType : "";
@@ -226,9 +273,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
.Include(s => s.Creator)
.Include(s => s.Location)
.AsEnumerable()
- .OrderByDescending(s => {
+ .OrderByDescending(s =>
+ {
// probably not the best way to do this
- switch (gameVersion) {
+ switch (gameVersion)
+ {
case GameVersion.LittleBigPlanet1:
return s.PlaysLBP1Unique;
case GameVersion.LittleBigPlanet2:
@@ -251,11 +300,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
}
[HttpGet("slots/mostHearted")]
- public async Task MostHeartedSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null)
+ public async Task MostHeartedSlots([FromQuery] int pageStart, [FromQuery] int pageSize, [FromQuery] string gameFilterType, [FromQuery] int players, [FromQuery] Boolean move, [FromQuery] string? dateFilterType = null)
{
- // v--- not sure of API in LBP3 here, needs testing
+ // v--- not sure of API in LBP3 here, needs testing
GameVersion gameVersion = gameFilterType == "both" ? GameVersion.LittleBigPlanet2 : GameVersion.LittleBigPlanet1;
-
+
long oldestTime;
string _dateFilterType = dateFilterType != null ? dateFilterType : "";
diff --git a/ProjectLighthouse/Controllers/UserController.cs b/ProjectLighthouse/Controllers/UserController.cs
index 2e6fc452..02bf2355 100644
--- a/ProjectLighthouse/Controllers/UserController.cs
+++ b/ProjectLighthouse/Controllers/UserController.cs
@@ -29,18 +29,17 @@ namespace LBPUnion.ProjectLighthouse.Controllers
public async Task GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1)
{
User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username);
- if (user == null) return "";
- return user.Serialize(gameVersion);
+ return user?.Serialize(gameVersion);
}
[HttpGet("user/{username}")]
public async Task GetUser(string username)
{
- Token? token = await this.database.TokenFromRequest(this.Request);
+ 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 == "") return this.NotFound();
+ if (user == null) return this.NotFound();
return this.Ok(user);
}
@@ -48,15 +47,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("users")]
public async Task GetUserAlt([FromQuery] string[] u)
{
- Token? token = await this.database.TokenFromRequest(this.Request);
+ GameToken? token = await this.database.GameTokenFromRequest(this.Request);
if (token == null) return this.StatusCode(403, "");
List serializedUsers = new();
- foreach (string username in u)
- {
- string? serializedUser = await this.GetSerializedUser(username, token.GameVersion);
- if (serializedUser != "") serializedUsers.Add(serializedUser);
- }
+ 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);
@@ -69,7 +64,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("updateUser")]
public async Task UpdateUser()
{
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
XmlReaderSettings settings = new()
@@ -132,6 +127,21 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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;
@@ -146,10 +156,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
{
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");
- }
+ 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;
@@ -165,7 +172,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("update_my_pins")]
public async Task UpdateMyPins()
{
- User? user = await this.database.UserFromRequest(this.Request);
+ User? user = await this.database.UserFromGameRequest(this.Request);
if (user == null) return this.StatusCode(403, "");
string pinsString = await new StreamReader(this.Request.Body).ReadToEndAsync();
diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs
new file mode 100644
index 00000000..70c973ef
--- /dev/null
+++ b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs
@@ -0,0 +1,73 @@
+#nullable enable
+using System;
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Types;
+using LBPUnion.ProjectLighthouse.Types.Levels;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin
+{
+ [ApiController]
+ [Route("admin/slot/{id:int}")]
+ public class AdminSlotController : ControllerBase
+ {
+ private readonly Database database;
+
+ public AdminSlotController(Database database)
+ {
+ this.database = database;
+ }
+
+ [Route("teamPick")]
+ public async Task TeamPick([FromRoute] int id)
+ {
+ 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 == null) return this.NotFound();
+
+ slot.TeamPick = true;
+
+ await this.database.SaveChangesAsync();
+
+ return this.Ok();
+ }
+
+ [Route("removeTeamPick")]
+ public async Task RemoveTeamPick([FromRoute] int id)
+ {
+ 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 == null) return this.NotFound();
+
+ slot.TeamPick = false;
+
+ await this.database.SaveChangesAsync();
+
+ return this.Ok();
+ }
+
+ [Route("delete")]
+ public async Task DeleteLevel([FromRoute] int id)
+ {
+ 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 == null) return this.Ok();
+
+ if (slot.Location == null) throw new ArgumentNullException();
+
+ this.database.Locations.Remove(slot.Location);
+ this.database.Slots.Remove(slot);
+
+ await this.database.SaveChangesAsync();
+
+ return this.Ok();
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs b/ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs
new file mode 100644
index 00000000..045d4cd4
--- /dev/null
+++ b/ProjectLighthouse/Controllers/Website/ExternalAuth/AuthenticationController.cs
@@ -0,0 +1,91 @@
+#nullable enable
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Helpers;
+using LBPUnion.ProjectLighthouse.Types;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Controllers.Website.ExternalAuth
+{
+ [ApiController]
+ [Route("/authentication")]
+ public class AuthenticationController : ControllerBase
+ {
+ private readonly Database database;
+
+ public AuthenticationController(Database database)
+ {
+ this.database = database;
+ }
+
+ [HttpGet("approve/{id:int}")]
+ public async Task 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 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 DenyAll()
+ {
+ User? user = this.database.UserFromWebRequest(this.Request);
+ if (user == null) return this.Redirect("/login");
+
+ List authAttempts = await this.database.AuthenticationAttempts.Include
+ (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");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/Website/SlotPageController.cs b/ProjectLighthouse/Controllers/Website/SlotPageController.cs
new file mode 100644
index 00000000..e69697c7
--- /dev/null
+++ b/ProjectLighthouse/Controllers/Website/SlotPageController.cs
@@ -0,0 +1,90 @@
+#nullable enable
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Types;
+using LBPUnion.ProjectLighthouse.Types.Levels;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+// I would like to apologize in advance for anyone dealing with this file.
+// Theres probably a better way to do this with delegates but I'm tired.
+// TODO: Clean up this file
+// - jvyden
+
+namespace LBPUnion.ProjectLighthouse.Controllers.Website
+{
+ [ApiController]
+ [Route("slot/{id:int}")]
+ public class SlotPageController : ControllerBase
+ {
+ private readonly Database database;
+
+ public SlotPageController(Database database)
+ {
+ this.database = database;
+ }
+
+ [HttpGet("heart")]
+ public async Task HeartLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
+ {
+ if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
+
+ User? user = this.database.UserFromWebRequest(this.Request);
+ if (user == null) return this.Redirect("~/login");
+
+ Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
+ if (heartedSlot == null) return this.NotFound();
+
+ await this.database.HeartLevel(user, heartedSlot);
+
+ return this.Redirect(callbackUrl);
+ }
+
+ [HttpGet("unheart")]
+ public async Task UnheartLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
+ {
+ if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
+
+ User? user = this.database.UserFromWebRequest(this.Request);
+ if (user == null) return this.Redirect("~/login");
+
+ Slot? heartedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
+ if (heartedSlot == null) return this.NotFound();
+
+ await this.database.UnheartLevel(user, heartedSlot);
+
+ return this.Redirect(callbackUrl);
+ }
+
+ [HttpGet("queue")]
+ public async Task QueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
+ {
+ if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
+
+ User? user = this.database.UserFromWebRequest(this.Request);
+ if (user == null) return this.Redirect("~/login");
+
+ Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
+ if (queuedSlot == null) return this.NotFound();
+
+ await this.database.QueueLevel(user, queuedSlot);
+
+ return this.Redirect(callbackUrl);
+ }
+
+ [HttpGet("unqueue")]
+ public async Task UnqueueLevel([FromRoute] int id, [FromQuery] string? callbackUrl)
+ {
+ if (string.IsNullOrEmpty(callbackUrl)) callbackUrl = "~/slot/" + id;
+
+ User? user = this.database.UserFromWebRequest(this.Request);
+ if (user == null) return this.Redirect("~/login");
+
+ Slot? queuedSlot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
+ if (queuedSlot == null) return this.NotFound();
+
+ await this.database.UnqueueLevel(user, queuedSlot);
+
+ return this.Redirect(callbackUrl);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Controllers/Website/UserPageController.cs b/ProjectLighthouse/Controllers/Website/UserPageController.cs
new file mode 100644
index 00000000..43e37725
--- /dev/null
+++ b/ProjectLighthouse/Controllers/Website/UserPageController.cs
@@ -0,0 +1,48 @@
+#nullable enable
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Types;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Controllers.Website
+{
+ [ApiController]
+ [Route("user/{id:int}")]
+ public class UserPageController : ControllerBase
+ {
+ private readonly Database database;
+
+ public UserPageController(Database database)
+ {
+ this.database = database;
+ }
+
+ [HttpGet("heart")]
+ public async Task HeartUser([FromRoute] int id)
+ {
+ 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);
+ if (heartedUser == null) return this.NotFound();
+
+ await this.database.HeartUser(user, heartedUser);
+
+ return this.Redirect("~/user/" + id);
+ }
+
+ [HttpGet("unheart")]
+ public async Task UnheartUser([FromRoute] int id)
+ {
+ 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);
+ if (heartedUser == null) return this.NotFound();
+
+ await this.database.UnheartUser(user, heartedUser);
+
+ return this.Redirect("~/user/" + id);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs
index 2d1bfbeb..2af08e30 100644
--- a/ProjectLighthouse/Database.cs
+++ b/ProjectLighthouse/Database.cs
@@ -1,3 +1,4 @@
+using System;
using System.Linq;
using System.Threading.Tasks;
using Kettu;
@@ -6,8 +7,8 @@ using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types;
using LBPUnion.ProjectLighthouse.Types.Levels;
using LBPUnion.ProjectLighthouse.Types.Profiles;
-using LBPUnion.ProjectLighthouse.Types.Settings;
using LBPUnion.ProjectLighthouse.Types.Reviews;
+using LBPUnion.ProjectLighthouse.Types.Settings;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
@@ -22,21 +23,25 @@ namespace LBPUnion.ProjectLighthouse
public DbSet HeartedLevels { get; set; }
public DbSet HeartedProfiles { get; set; }
public DbSet Comments { get; set; }
- public DbSet Tokens { get; set; }
+ public DbSet GameTokens { get; set; }
+ public DbSet WebTokens { get; set; }
public DbSet Scores { get; set; }
public DbSet PhotoSubjects { get; set; }
public DbSet Photos { get; set; }
- public DbSet LastMatches { get; set; }
+ public DbSet LastContacts { get; set; }
public DbSet VisitedLevels { get; set; }
public DbSet RatedLevels { get; set; }
+ public DbSet AuthenticationAttempts { get; set; }
public DbSet Reviews { get; set; }
public DbSet RatedReviews { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
- public async Task CreateUser(string username)
+ public async Task CreateUser(string username, string password)
{
+ if (!password.StartsWith("$")) throw new ArgumentException(nameof(password) + " is not a BCrypt hash");
+
User user;
if ((user = await this.Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) return user;
@@ -47,6 +52,7 @@ namespace LBPUnion.ProjectLighthouse
user = new User
{
Username = username,
+ Password = password,
LocationId = l.Id,
Biography = username + " hasn't introduced themselves yet.",
};
@@ -58,12 +64,13 @@ namespace LBPUnion.ProjectLighthouse
}
#nullable enable
- public async Task AuthenticateUser(LoginData loginData, string userLocation, string titleId = "")
+ public async Task AuthenticateUser(LoginData loginData, string userLocation, string titleId = "")
{
// TODO: don't use psn name to authenticate
- User user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username) ?? await this.CreateUser(loginData.Username);
+ User? user = await this.Users.FirstOrDefaultAsync(u => u.Username == loginData.Username);
+ if (user == null) return null;
- Token token = new()
+ GameToken gameToken = new()
{
UserToken = HashHelper.GenerateAuthToken(),
UserId = user.UserId,
@@ -71,58 +78,214 @@ namespace LBPUnion.ProjectLighthouse
GameVersion = GameVersionHelper.FromTitleId(titleId),
};
- if (token.GameVersion == GameVersion.Unknown)
+ if (gameToken.GameVersion == GameVersion.Unknown)
{
Logger.Log($"Unknown GameVersion for TitleId {titleId}", LoggerLevelLogin.Instance);
return null;
}
- this.Tokens.Add(token);
+ this.GameTokens.Add(gameToken);
await this.SaveChangesAsync();
- return token;
+ return gameToken;
}
- public async Task UserFromAuthToken(string authToken)
+ #region Hearts & Queues
+
+ public async Task HeartUser(User user, User heartedUser)
{
- Token? token = await this.Tokens.FirstOrDefaultAsync(t => t.UserToken == authToken);
+ 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 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 UserFromToken(Token token) => await this.UserFromAuthToken(token.UserToken);
+ public async Task UserFromGameToken
+ (GameToken gameToken, bool allowUnapproved = false)
+ => await this.UserFromMMAuth(gameToken.UserToken, allowUnapproved);
- public async Task UserFromRequest(HttpRequest request)
+ public async Task 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.UserFromAuthToken(mmAuth);
+ return await this.UserFromMMAuth(mmAuth, allowUnapproved);
}
- public async Task TokenFromRequest(HttpRequest request)
+ public async Task GameTokenFromRequest(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.Tokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth);
- }
+ GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth);
- public async Task<(User, Token)?> UserAndTokenFromRequest(HttpRequest request)
- {
- if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
-
- Token? token = await this.Tokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth);
if (token == null) return null;
+ if (!allowUnapproved && !token.Approved) return null;
- User? user = await this.UserFromToken(token);
+ 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 PhotoFromSubject(PhotoSubject subject)
=> await this.Photos.FirstOrDefaultAsync(p => p.PhotoSubjectIds.Contains(subject.PhotoSubjectId.ToString()));
+
+ public async Task RemoveUser(User user)
+ {
+ 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.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.Photos.RemoveRange(this.Photos.Where(p => p.CreatorId == user.UserId));
+
+ 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
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/FakeRemoteIPAddressMiddleware.cs b/ProjectLighthouse/FakeRemoteIPAddressMiddleware.cs
index 6a5a200e..58018157 100644
--- a/ProjectLighthouse/FakeRemoteIPAddressMiddleware.cs
+++ b/ProjectLighthouse/FakeRemoteIPAddressMiddleware.cs
@@ -6,8 +6,8 @@ namespace LBPUnion.ProjectLighthouse
{
public class FakeRemoteIPAddressMiddleware
{
- private readonly RequestDelegate next;
private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.0.0.1");
+ private readonly RequestDelegate next;
public FakeRemoteIPAddressMiddleware(RequestDelegate next)
{
diff --git a/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs b/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs
new file mode 100644
index 00000000..1dc990a0
--- /dev/null
+++ b/ProjectLighthouse/Helpers/DeniedAuthenticationHelper.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+
+namespace LBPUnion.ProjectLighthouse.Helpers
+{
+ public static class DeniedAuthenticationHelper
+ {
+ public static readonly Dictionary IPAddressAndNameDeniedAt = new();
+ public static readonly Dictionary AttemptsByIPAddressAndName = new();
+
+ public static void SetDeniedAt(string ipAddressAndName, long timestamp = 0)
+ {
+ if (timestamp == 0) timestamp = TimestampHelper.Timestamp;
+
+ if (IPAddressAndNameDeniedAt.TryGetValue(ipAddressAndName, out long _)) IPAddressAndNameDeniedAt.Remove(ipAddressAndName);
+ IPAddressAndNameDeniedAt.Add(ipAddressAndName, timestamp);
+ }
+
+ public static bool RecentlyDenied(string ipAddressAndName)
+ {
+ 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 int GetAttempts(string ipAddressAndName)
+ {
+ if (!AttemptsByIPAddressAndName.TryGetValue(ipAddressAndName, out int attempts)) return 0;
+
+ return attempts;
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Helpers/FileHelper.cs
index f85b3113..ce33cff1 100644
--- a/ProjectLighthouse/Helpers/FileHelper.cs
+++ b/ProjectLighthouse/Helpers/FileHelper.cs
@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using System.Text;
using LBPUnion.ProjectLighthouse.Types.Files;
+using LBPUnion.ProjectLighthouse.Types.Settings;
namespace LBPUnion.ProjectLighthouse.Helpers
{
@@ -14,6 +15,8 @@ namespace LBPUnion.ProjectLighthouse.Helpers
public static bool IsFileSafe(LbpFile file)
{
+ if (!ServerSettings.Instance.CheckForUnsafeFiles) return true;
+
if (file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data);
return file.FileType switch
diff --git a/ProjectLighthouse/Helpers/GameVersionHelper.cs b/ProjectLighthouse/Helpers/GameVersionHelper.cs
index c3e63be2..748b5cf7 100644
--- a/ProjectLighthouse/Helpers/GameVersionHelper.cs
+++ b/ProjectLighthouse/Helpers/GameVersionHelper.cs
@@ -44,6 +44,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
"BCES01346",
"BCUS90260",
"BCET70023",
+ "BCES01694",
"NPUA80662",
};
diff --git a/ProjectLighthouse/Helpers/HashHelper.cs b/ProjectLighthouse/Helpers/HashHelper.cs
index 838a22a4..88012439 100644
--- a/ProjectLighthouse/Helpers/HashHelper.cs
+++ b/ProjectLighthouse/Helpers/HashHelper.cs
@@ -67,7 +67,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str));
- public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", "");
+ public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", "").ToLower();
public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str));
diff --git a/ProjectLighthouse/Helpers/LastContactHelper.cs b/ProjectLighthouse/Helpers/LastContactHelper.cs
new file mode 100644
index 00000000..fa06e84b
--- /dev/null
+++ b/ProjectLighthouse/Helpers/LastContactHelper.cs
@@ -0,0 +1,35 @@
+#nullable enable
+using System.Linq;
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Types;
+using LBPUnion.ProjectLighthouse.Types.Profiles;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Helpers
+{
+ public static class LastContactHelper
+ {
+ private static readonly Database database = new();
+
+ public static async Task SetLastContact(User user, GameVersion gameVersion)
+ {
+ LastContact? lastContact = await database.LastContacts.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync();
+
+ // below makes it not look like trash
+ // ReSharper disable once ConvertIfStatementToNullCoalescingExpression
+ if (lastContact == null)
+ {
+ lastContact = new LastContact
+ {
+ UserId = user.UserId,
+ };
+ database.LastContacts.Add(lastContact);
+ }
+
+ lastContact.Timestamp = TimestampHelper.Timestamp;
+ lastContact.GameVersion = gameVersion;
+
+ await database.SaveChangesAsync();
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Helpers/MaintenanceHelper.cs b/ProjectLighthouse/Helpers/MaintenanceHelper.cs
new file mode 100644
index 00000000..a2794e57
--- /dev/null
+++ b/ProjectLighthouse/Helpers/MaintenanceHelper.cs
@@ -0,0 +1,70 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Maintenance;
+
+namespace LBPUnion.ProjectLighthouse.Helpers
+{
+ public static class MaintenanceHelper
+ {
+
+ static MaintenanceHelper()
+ {
+ Commands = getListOfInterfaceObjects();
+ MaintenanceJobs = getListOfInterfaceObjects();
+ }
+ public static List Commands { get; }
+
+ public static List MaintenanceJobs { get; }
+
+ private static List getListOfInterfaceObjects() 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 suitableCommands = Commands.Where
+ (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(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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Helpers/MatchHelper.cs b/ProjectLighthouse/Helpers/MatchHelper.cs
index 30f07616..c6379435 100644
--- a/ProjectLighthouse/Helpers/MatchHelper.cs
+++ b/ProjectLighthouse/Helpers/MatchHelper.cs
@@ -22,10 +22,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
public static void AddUserRecentlyDivedIn(int userId, int otherUserId)
{
- if (!UserRecentlyDivedIn.TryGetValue(userId, out List? recentlyDivedIn))
- {
- UserRecentlyDivedIn.Add(userId, recentlyDivedIn = new List());
- }
+ if (!UserRecentlyDivedIn.TryGetValue(userId, out List? recentlyDivedIn)) UserRecentlyDivedIn.Add(userId, recentlyDivedIn = new List());
Debug.Assert(recentlyDivedIn != null, nameof(recentlyDivedIn) + " is null, somehow.");
diff --git a/ProjectLighthouse/Helpers/RoomHelper.cs b/ProjectLighthouse/Helpers/RoomHelper.cs
index 285e2b93..e89979fa 100644
--- a/ProjectLighthouse/Helpers/RoomHelper.cs
+++ b/ProjectLighthouse/Helpers/RoomHelper.cs
@@ -43,10 +43,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
{
bool gotValue = MatchHelper.UserLocations.TryGetValue(p.UserId, out string? value);
- if (gotValue && value != null)
- {
- relevantUserLocations.Add(p.UserId, value);
- }
+ if (gotValue && value != null) relevantUserLocations.Add(p.UserId, value);
return gotValue;
}
);
@@ -138,20 +135,23 @@ namespace LBPUnion.ProjectLighthouse.Helpers
{
// Delete old rooms based on host
if (host != null)
- {
- Rooms.RemoveAll(r => r.Host == host);
- }
+ 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);
}
- }
}
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Helpers/StatisticsHelper.cs b/ProjectLighthouse/Helpers/StatisticsHelper.cs
index 3a43ed81..97091d22 100644
--- a/ProjectLighthouse/Helpers/StatisticsHelper.cs
+++ b/ProjectLighthouse/Helpers/StatisticsHelper.cs
@@ -8,10 +8,12 @@ namespace LBPUnion.ProjectLighthouse.Helpers
{
private static readonly Database database = new();
- public static async Task RecentMatches() => await database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync();
+ public static async Task RecentMatches() => await database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync();
public static async Task SlotCount() => await database.Slots.CountAsync();
public static async Task MMPicksCount() => await database.Slots.CountAsync(s => s.TeamPick);
+
+ public static async Task PhotoCount() => await database.Photos.CountAsync();
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Helpers/GitVersionHelper.cs b/ProjectLighthouse/Helpers/VersionHelper.cs
similarity index 82%
rename from ProjectLighthouse/Helpers/GitVersionHelper.cs
rename to ProjectLighthouse/Helpers/VersionHelper.cs
index 857728fe..13bae320 100644
--- a/ProjectLighthouse/Helpers/GitVersionHelper.cs
+++ b/ProjectLighthouse/Helpers/VersionHelper.cs
@@ -2,12 +2,13 @@ using System;
using System.IO;
using Kettu;
using LBPUnion.ProjectLighthouse.Logging;
+using LBPUnion.ProjectLighthouse.Types.Settings;
namespace LBPUnion.ProjectLighthouse.Helpers
{
- public static class GitVersionHelper
+ public static class VersionHelper
{
- static GitVersionHelper()
+ static VersionHelper()
{
try
{
@@ -50,7 +51,17 @@ namespace LBPUnion.ProjectLighthouse.Helpers
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");
public static bool CanCheckForUpdates { get; set; }
+
+ public const string Build =
+ #if DEBUG
+ "Debug";
+ #elif RELEASE
+ "Release";
+ #else
+ "Unknown";
+ #endif
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Logging/InfluxLogger.cs b/ProjectLighthouse/Logging/InfluxLogger.cs
index a4ab28e9..02c881f6 100644
--- a/ProjectLighthouse/Logging/InfluxLogger.cs
+++ b/ProjectLighthouse/Logging/InfluxLogger.cs
@@ -8,6 +8,7 @@ namespace LBPUnion.ProjectLighthouse.Logging
{
public class InfluxLogger : LoggerBase
{
+ public override bool AllowMultiple => false;
public override void Send(LoggerLine line)
{
@@ -22,6 +23,5 @@ namespace LBPUnion.ProjectLighthouse.Logging
writeApi.WritePoint(ServerSettings.Instance.InfluxBucket, ServerSettings.Instance.InfluxOrg, point);
}
- public override bool AllowMultiple => false;
}
}
\ No newline at end of file
diff --git a/ProjectLighthouse/Logging/LighthouseFileLogger.cs b/ProjectLighthouse/Logging/LighthouseFileLogger.cs
index 387ca881..cc276f5b 100644
--- a/ProjectLighthouse/Logging/LighthouseFileLogger.cs
+++ b/ProjectLighthouse/Logging/LighthouseFileLogger.cs
@@ -25,7 +25,7 @@ namespace LBPUnion.ProjectLighthouse.Logging
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
}
}
diff --git a/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs b/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs
new file mode 100644
index 00000000..36846952
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs
@@ -0,0 +1,54 @@
+#nullable enable
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using Kettu;
+using LBPUnion.ProjectLighthouse.Helpers;
+using LBPUnion.ProjectLighthouse.Logging;
+using LBPUnion.ProjectLighthouse.Types;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
+{
+ [UsedImplicitly]
+ public class CreateUserCommand : ICommand
+ {
+ private readonly Database _database = new();
+
+ public async Task Run(string[] args)
+ {
+ string onlineId = args[0];
+ string password = args[1];
+
+ password = HashHelper.Sha256Hash(password);
+
+ User? user = await this._database.Users.FirstOrDefaultAsync(u => u.Username == onlineId);
+ if (user == null)
+ {
+ user = await this._database.CreateUser(onlineId, HashHelper.BCryptHash(password));
+ Logger.Log($"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", 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() => " ";
+
+ public int RequiredArgs() => 2;
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs b/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs
new file mode 100644
index 00000000..d7c7802c
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/Commands/DeleteUserCommand.cs
@@ -0,0 +1,40 @@
+#nullable enable
+using System;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using LBPUnion.ProjectLighthouse.Types;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
+{
+ [UsedImplicitly]
+ public class DeleteUserCommand : ICommand
+ {
+ private readonly Database database = new();
+ public string Name() => "Delete/Ban User";
+ public string[] Aliases()
+ => new[]
+ {
+ "deleteUser", "wipeUser", "banUser",
+ };
+ public string Arguments() => "";
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs b/ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs
new file mode 100644
index 00000000..df22f928
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/Commands/MakeUserAdminCommand.cs
@@ -0,0 +1,45 @@
+#nullable enable
+using System;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using LBPUnion.ProjectLighthouse.Types;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
+{
+ [UsedImplicitly]
+ public class MakeUserAdminCommand : ICommand
+ {
+ private readonly Database database = new();
+
+ public string Name() => "Make User Admin";
+ public string[] Aliases()
+ => new[]
+ {
+ "makeAdmin",
+ };
+ public string Arguments() => "";
+ 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;
+ }
+
+ user.IsAdmin = true;
+ await this.database.SaveChangesAsync();
+
+ Console.WriteLine($"The user {user.Username} (id: {user.UserId}) is now an admin.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs b/ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs
new file mode 100644
index 00000000..1f22afa5
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/Commands/ResetPasswordCommand.cs
@@ -0,0 +1,49 @@
+#nullable enable
+using System;
+using System.Threading.Tasks;
+using JetBrains.Annotations;
+using LBPUnion.ProjectLighthouse.Helpers;
+using LBPUnion.ProjectLighthouse.Types;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
+{
+ [UsedImplicitly]
+ public class ResetPasswordCommand : ICommand
+ {
+ private readonly Database database = new();
+ public string Name() => "Reset Password";
+ public string[] Aliases()
+ => new[]
+ {
+ "setPassword", "resetPassword", "passwd", "password",
+ };
+ public string Arguments() => " ";
+ public int RequiredArgs() => 2;
+
+ 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;
+ }
+ string password = args[1];
+ if (password.Length != 64) password = HashHelper.Sha256Hash(password);
+
+ user.Password = HashHelper.BCryptHash(password);
+ user.PasswordResetRequired = true;
+
+ await this.database.SaveChangesAsync();
+
+ Console.WriteLine($"The password for user {user.Username} (id: {user.UserId}) has been reset.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs b/ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs
new file mode 100644
index 00000000..d9ec15c8
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/Commands/WipeTokensForUserCommand.cs
@@ -0,0 +1,45 @@
+#nullable enable
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Types;
+using Microsoft.EntityFrameworkCore;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance.Commands
+{
+ public class WipeTokensForUserCommand : ICommand
+ {
+ private readonly Database database = new();
+
+ public string Name() => "Wipe tokens for user";
+ public string[] Aliases()
+ => new[]
+ {
+ "wipeTokens", "wipeToken", "deleteTokens", "deleteToken", "removeTokens", "removeToken",
+ };
+ public string Arguments() => "";
+ 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;
+ }
+
+ 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));
+
+ await this.database.SaveChangesAsync();
+
+ Console.WriteLine($"Deleted all tokens for {user.Username} (id: {user.UserId}).");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/ICommand.cs b/ProjectLighthouse/Maintenance/ICommand.cs
new file mode 100644
index 00000000..a12f26a8
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/ICommand.cs
@@ -0,0 +1,19 @@
+using System.Threading.Tasks;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance
+{
+ public interface ICommand
+ {
+
+ public string FirstAlias => this.Aliases()[0];
+ public Task Run(string[] args);
+
+ public string Name();
+
+ public string[] Aliases();
+
+ public string Arguments();
+
+ public int RequiredArgs();
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/IMaintenanceJob.cs b/ProjectLighthouse/Maintenance/IMaintenanceJob.cs
new file mode 100644
index 00000000..4abd1dcb
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/IMaintenanceJob.cs
@@ -0,0 +1,13 @@
+using System.Threading.Tasks;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance
+{
+ public interface IMaintenanceJob
+ {
+ public Task Run();
+
+ public string Name();
+
+ public string Description();
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs
new file mode 100644
index 00000000..78d45fde
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupBrokenPhotosMaintenanceJob.cs
@@ -0,0 +1,71 @@
+#nullable enable
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Helpers;
+using LBPUnion.ProjectLighthouse.Types;
+using LBPUnion.ProjectLighthouse.Types.Files;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
+{
+ 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.";
+
+ [SuppressMessage("ReSharper", "LoopCanBePartlyConvertedToQuery")]
+ public async Task Run()
+ {
+ foreach (Photo photo in this.database.Photos)
+ {
+ bool hashNullOrEmpty = false;
+ bool noHashesExist = false;
+ bool largeHashIsInvalidFile = false;
+
+ hashNullOrEmpty = string.IsNullOrEmpty
+ (photo.LargeHash) ||
+ string.IsNullOrEmpty(photo.MediumHash) ||
+ string.IsNullOrEmpty(photo.SmallHash) ||
+ string.IsNullOrEmpty(photo.PlanHash);
+ if (hashNullOrEmpty) goto removePhoto;
+
+ List hashes = new()
+ {
+ 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, );
+ if (file == null || file.FileType != LbpFileType.Jpeg && file.FileType != LbpFileType.Png)
+ {
+ largeHashIsInvalidFile = true;
+ goto removePhoto;
+ }
+
+ continue;
+
+ removePhoto:
+
+ Console.WriteLine
+ (
+ $"Removing photo (id: {photo.PhotoId}): " +
+ $"{nameof(hashNullOrEmpty)}: {hashNullOrEmpty}, " +
+ $"{nameof(noHashesExist)}: {noHashesExist}, " +
+ $"{nameof(largeHashIsInvalidFile)}: {largeHashIsInvalidFile}"
+ );
+
+ this.database.Photos.Remove(photo);
+ }
+
+ await this.database.SaveChangesAsync();
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs
new file mode 100644
index 00000000..5fb8f6ff
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/MaintenanceJobs/CleanupUnusedLocationsMaintenanceJob.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Types.Profiles;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
+{
+ 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()
+ {
+ List usedLocationIds = new();
+
+ usedLocationIds.AddRange(this.database.Slots.Select(slot => slot.LocationId));
+ usedLocationIds.AddRange(this.database.Users.Select(user => user.LocationId));
+
+ IQueryable 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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs
new file mode 100644
index 00000000..ca269fec
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/MaintenanceJobs/DeleteAllTokensMaintenanceJob.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Threading.Tasks;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
+{
+ 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()
+ {
+ this.database.GameTokens.RemoveRange(this.database.GameTokens);
+ this.database.WebTokens.RemoveRange(this.database.WebTokens);
+
+ await this.database.SaveChangesAsync();
+
+ Console.WriteLine("Deleted ALL tokens.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs
new file mode 100644
index 00000000..efd0460f
--- /dev/null
+++ b/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Threading.Tasks;
+using LBPUnion.ProjectLighthouse.Types.Levels;
+
+namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs
+{
+ 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()
+ {
+ 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}");
+ count++;
+ }
+
+ await this.database.SaveChangesAsync();
+
+ Console.WriteLine($"Fixed {count} broken player requirements.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ProjectLighthouse/Migrations/20211019021627_InitialCreate.Designer.cs b/ProjectLighthouse/Migrations/20211019021627_InitialCreate.Designer.cs
index 7fcae3a9..053a8eee 100644
--- a/ProjectLighthouse/Migrations/20211019021627_InitialCreate.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211019021627_InitialCreate.Designer.cs
@@ -154,7 +154,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Slots");
});
- modelBuilder.Entity("ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.Designer.cs b/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.Designer.cs
index d531cf75..43af2c8a 100644
--- a/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.Designer.cs
@@ -175,7 +175,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Slots");
});
- modelBuilder.Entity("ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211019203627_LastMatches.Designer.cs b/ProjectLighthouse/Migrations/20211019203627_LastMatches.Designer.cs
index aebb0f62..03647fc8 100644
--- a/ProjectLighthouse/Migrations/20211019203627_LastMatches.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211019203627_LastMatches.Designer.cs
@@ -189,7 +189,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Slots");
});
- modelBuilder.Entity("ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211020220840_ResourceList.Designer.cs b/ProjectLighthouse/Migrations/20211020220840_ResourceList.Designer.cs
index 36a02088..11880c79 100644
--- a/ProjectLighthouse/Migrations/20211020220840_ResourceList.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211020220840_ResourceList.Designer.cs
@@ -189,7 +189,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Slots");
});
- modelBuilder.Entity("ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211026010814_FavouriteUsers.Designer.cs b/ProjectLighthouse/Migrations/20211026010814_FavouriteUsers.Designer.cs
index 5c292a1e..5f0ff875 100644
--- a/ProjectLighthouse/Migrations/20211026010814_FavouriteUsers.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211026010814_FavouriteUsers.Designer.cs
@@ -208,7 +208,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Locations");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.Designer.cs b/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.Designer.cs
index 48beed7d..09a0a521 100644
--- a/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211028015915_AddSlotTimestamp.Designer.cs
@@ -211,7 +211,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Locations");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.Designer.cs b/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.Designer.cs
index d1ba6622..3cf2e85b 100644
--- a/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211028021513_AddSlotFirstUploadedAndLastUpdated.Designer.cs
@@ -214,7 +214,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Locations");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.Designer.cs b/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.Designer.cs
index 59a0dcc7..ef52f9e7 100644
--- a/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211029213334_RemoveUsedSlotsFromDb.Designer.cs
@@ -214,7 +214,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Locations");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.Designer.cs b/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.Designer.cs
index f9f84485..eeca745a 100644
--- a/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211030203837_AddMMPickToSlot.Designer.cs
@@ -217,7 +217,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Locations");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211031234245_AddScoresTable.Designer.cs b/ProjectLighthouse/Migrations/20211031234245_AddScoresTable.Designer.cs
index ce6e6792..14211110 100644
--- a/ProjectLighthouse/Migrations/20211031234245_AddScoresTable.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211031234245_AddScoresTable.Designer.cs
@@ -242,7 +242,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211102215859_RenameTeamPick.Designer.cs b/ProjectLighthouse/Migrations/20211102215859_RenameTeamPick.Designer.cs
index 34087d12..131c53ad 100644
--- a/ProjectLighthouse/Migrations/20211102215859_RenameTeamPick.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211102215859_RenameTeamPick.Designer.cs
@@ -242,7 +242,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211103194917_RemoveStartupMigrations.Designer.cs b/ProjectLighthouse/Migrations/20211103194917_RemoveStartupMigrations.Designer.cs
index f26e5451..ce336fce 100644
--- a/ProjectLighthouse/Migrations/20211103194917_RemoveStartupMigrations.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211103194917_RemoveStartupMigrations.Designer.cs
@@ -242,7 +242,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211104031327_AddGameVersionToToken.Designer.cs b/ProjectLighthouse/Migrations/20211104031327_AddGameVersionToToken.Designer.cs
index 9814b171..215e0608 100644
--- a/ProjectLighthouse/Migrations/20211104031327_AddGameVersionToToken.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211104031327_AddGameVersionToToken.Designer.cs
@@ -242,7 +242,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211104040509_AddGameVersionToSlots.Designer.cs b/ProjectLighthouse/Migrations/20211104040509_AddGameVersionToSlots.Designer.cs
index 0727a0e5..94070a76 100644
--- a/ProjectLighthouse/Migrations/20211104040509_AddGameVersionToSlots.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211104040509_AddGameVersionToSlots.Designer.cs
@@ -245,7 +245,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211104195812_AddPhotoSupport.Designer.cs b/ProjectLighthouse/Migrations/20211104195812_AddPhotoSupport.Designer.cs
index b848597f..8e738f6a 100644
--- a/ProjectLighthouse/Migrations/20211104195812_AddPhotoSupport.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211104195812_AddPhotoSupport.Designer.cs
@@ -293,7 +293,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211105205010_UpdatePhotoAndPhotoSubjectToDoStuffWeirdName.Designer.cs b/ProjectLighthouse/Migrations/20211105205010_UpdatePhotoAndPhotoSubjectToDoStuffWeirdName.Designer.cs
index 9dee3007..31f84ecc 100644
--- a/ProjectLighthouse/Migrations/20211105205010_UpdatePhotoAndPhotoSubjectToDoStuffWeirdName.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211105205010_UpdatePhotoAndPhotoSubjectToDoStuffWeirdName.Designer.cs
@@ -303,7 +303,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211105205239_DropPhotoSubjectParentPhoto.Designer.cs b/ProjectLighthouse/Migrations/20211105205239_DropPhotoSubjectParentPhoto.Designer.cs
index 5f463653..7ee30fe0 100644
--- a/ProjectLighthouse/Migrations/20211105205239_DropPhotoSubjectParentPhoto.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211105205239_DropPhotoSubjectParentPhoto.Designer.cs
@@ -298,7 +298,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211105205749_DropPhotoSlot.Designer.cs b/ProjectLighthouse/Migrations/20211105205749_DropPhotoSlot.Designer.cs
index 9cb364e3..9d153964 100644
--- a/ProjectLighthouse/Migrations/20211105205749_DropPhotoSlot.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211105205749_DropPhotoSlot.Designer.cs
@@ -293,7 +293,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211106010424_AddCreatorToPhoto.Designer.cs b/ProjectLighthouse/Migrations/20211106010424_AddCreatorToPhoto.Designer.cs
index dfaa00d8..a1804945 100644
--- a/ProjectLighthouse/Migrations/20211106010424_AddCreatorToPhoto.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211106010424_AddCreatorToPhoto.Designer.cs
@@ -298,7 +298,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211107023452_NoPhotosByMeOrWithMeInUser.Designer.cs b/ProjectLighthouse/Migrations/20211107023452_NoPhotosByMeOrWithMeInUser.Designer.cs
index 46f8d20f..22841820 100644
--- a/ProjectLighthouse/Migrations/20211107023452_NoPhotosByMeOrWithMeInUser.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211107023452_NoPhotosByMeOrWithMeInUser.Designer.cs
@@ -303,7 +303,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211108013443_RemoveCommentsEnabled.Designer.cs b/ProjectLighthouse/Migrations/20211108013443_RemoveCommentsEnabled.Designer.cs
index 67842c32..89324073 100644
--- a/ProjectLighthouse/Migrations/20211108013443_RemoveCommentsEnabled.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211108013443_RemoveCommentsEnabled.Designer.cs
@@ -303,7 +303,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211108015422_AddPlaysToSlot.Designer.cs b/ProjectLighthouse/Migrations/20211108015422_AddPlaysToSlot.Designer.cs
index 40001bd0..59bcc054 100644
--- a/ProjectLighthouse/Migrations/20211108015422_AddPlaysToSlot.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211108015422_AddPlaysToSlot.Designer.cs
@@ -306,7 +306,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211108054552_RemoveCountsFromDatabase.Designer.cs b/ProjectLighthouse/Migrations/20211108054552_RemoveCountsFromDatabase.Designer.cs
index 4d2d382f..30993275 100644
--- a/ProjectLighthouse/Migrations/20211108054552_RemoveCountsFromDatabase.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211108054552_RemoveCountsFromDatabase.Designer.cs
@@ -306,7 +306,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211108093616_GameSpecificPlayCounts.Designer.cs b/ProjectLighthouse/Migrations/20211108093616_GameSpecificPlayCounts.Designer.cs
index abf45882..c649e759 100644
--- a/ProjectLighthouse/Migrations/20211108093616_GameSpecificPlayCounts.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211108093616_GameSpecificPlayCounts.Designer.cs
@@ -330,7 +330,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211108114052_VisitedLevelsTable.Designer.cs b/ProjectLighthouse/Migrations/20211108114052_VisitedLevelsTable.Designer.cs
index f78f1d58..9ea4167f 100644
--- a/ProjectLighthouse/Migrations/20211108114052_VisitedLevelsTable.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211108114052_VisitedLevelsTable.Designer.cs
@@ -354,7 +354,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211108212022_BooYayRateLevels.Designer.cs b/ProjectLighthouse/Migrations/20211108212022_BooYayRateLevels.Designer.cs
index 4d1d9dbf..fc982fdc 100644
--- a/ProjectLighthouse/Migrations/20211108212022_BooYayRateLevels.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211108212022_BooYayRateLevels.Designer.cs
@@ -381,7 +381,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211109225543_AddLevelTypeToSlot.Designer.cs b/ProjectLighthouse/Migrations/20211109225543_AddLevelTypeToSlot.Designer.cs
index cc243392..f14eb761 100644
--- a/ProjectLighthouse/Migrations/20211109225543_AddLevelTypeToSlot.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211109225543_AddLevelTypeToSlot.Designer.cs
@@ -384,7 +384,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211113091631_AddUserLocationToToken.Designer.cs b/ProjectLighthouse/Migrations/20211113091631_AddUserLocationToToken.Designer.cs
index 76be44ce..02bde039 100644
--- a/ProjectLighthouse/Migrations/20211113091631_AddUserLocationToToken.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211113091631_AddUserLocationToToken.Designer.cs
@@ -384,7 +384,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211113215128_VisitedLevelPlayCounts.Designer.cs b/ProjectLighthouse/Migrations/20211113215128_VisitedLevelPlayCounts.Designer.cs
index 5d93e5a2..8385d1da 100644
--- a/ProjectLighthouse/Migrations/20211113215128_VisitedLevelPlayCounts.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211113215128_VisitedLevelPlayCounts.Designer.cs
@@ -401,7 +401,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211113220306_VisitedLevelDropGameVersion.Designer.cs b/ProjectLighthouse/Migrations/20211113220306_VisitedLevelDropGameVersion.Designer.cs
index b48129af..38304f22 100644
--- a/ProjectLighthouse/Migrations/20211113220306_VisitedLevelDropGameVersion.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211113220306_VisitedLevelDropGameVersion.Designer.cs
@@ -398,7 +398,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211114231343_UserRefactor.Designer.cs b/ProjectLighthouse/Migrations/20211114231343_UserRefactor.Designer.cs
index cf70b3b0..49a61ffa 100644
--- a/ProjectLighthouse/Migrations/20211114231343_UserRefactor.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211114231343_UserRefactor.Designer.cs
@@ -384,7 +384,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211115050553_UserAddDefaultsToNullableStrings.Designer.cs b/ProjectLighthouse/Migrations/20211115050553_UserAddDefaultsToNullableStrings.Designer.cs
index 556eb96f..3aad3cc8 100644
--- a/ProjectLighthouse/Migrations/20211115050553_UserAddDefaultsToNullableStrings.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211115050553_UserAddDefaultsToNullableStrings.Designer.cs
@@ -400,7 +400,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211115052941_SlotAddLbpVitaPlays.Designer.cs b/ProjectLighthouse/Migrations/20211115052941_SlotAddLbpVitaPlays.Designer.cs
index efb62ea6..d3531618 100644
--- a/ProjectLighthouse/Migrations/20211115052941_SlotAddLbpVitaPlays.Designer.cs
+++ b/ProjectLighthouse/Migrations/20211115052941_SlotAddLbpVitaPlays.Designer.cs
@@ -412,7 +412,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
- modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property("TokenId")
.ValueGeneratedOnAdd()
diff --git a/ProjectLighthouse/Migrations/20211120045239_AddPasswordToUser.Designer.cs b/ProjectLighthouse/Migrations/20211120045239_AddPasswordToUser.Designer.cs
new file mode 100644
index 00000000..1afd4976
--- /dev/null
+++ b/ProjectLighthouse/Migrations/20211120045239_AddPasswordToUser.Designer.cs
@@ -0,0 +1,654 @@
+//
+using LBPUnion.ProjectLighthouse;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace ProjectLighthouse.Migrations
+{
+ [DbContext(typeof(Database))]
+ [Migration("20211120045239_AddPasswordToUser")]
+ partial class AddPasswordToUser
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "6.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b =>
+ {
+ b.Property("HeartedProfileId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("HeartedUserId")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .HasColumnType("int");
+
+ b.HasKey("HeartedProfileId");
+
+ b.HasIndex("HeartedUserId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("HeartedProfiles");
+ });
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b =>
+ {
+ b.Property("HeartedLevelId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("SlotId")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .HasColumnType("int");
+
+ b.HasKey("HeartedLevelId");
+
+ b.HasIndex("SlotId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("HeartedLevels");
+ });
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b =>
+ {
+ b.Property("QueuedLevelId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("SlotId")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .HasColumnType("int");
+
+ b.HasKey("QueuedLevelId");
+
+ b.HasIndex("SlotId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("QueuedLevels");
+ });
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b =>
+ {
+ b.Property("RatedLevelId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Rating")
+ .HasColumnType("int");
+
+ b.Property("RatingLBP1")
+ .HasColumnType("double");
+
+ b.Property("SlotId")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .HasColumnType("int");
+
+ b.HasKey("RatedLevelId");
+
+ b.HasIndex("SlotId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("RatedLevels");
+ });
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b =>
+ {
+ b.Property("SlotId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("AuthorLabels")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("BackgroundHash")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("CreatorId")
+ .HasColumnType("int");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("FirstUploaded")
+ .HasColumnType("bigint");
+
+ b.Property("GameVersion")
+ .HasColumnType("int");
+
+ b.Property("IconHash")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("InitiallyLocked")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LastUpdated")
+ .HasColumnType("bigint");
+
+ b.Property("Lbp1Only")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LevelType")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("LocationId")
+ .HasColumnType("int");
+
+ b.Property("MaximumPlayers")
+ .HasColumnType("int");
+
+ b.Property("MinimumPlayers")
+ .HasColumnType("int");
+
+ b.Property("MoveRequired")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("PlaysLBP1")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP1Complete")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP1Unique")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP2")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP2Complete")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP2Unique")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP3")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP3Complete")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP3Unique")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBPVita")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBPVitaComplete")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBPVitaUnique")
+ .HasColumnType("int");
+
+ b.Property("ResourceCollection")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("RootLevel")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Shareable")
+ .HasColumnType("int");
+
+ b.Property("SubLevel")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("TeamPick")
+ .HasColumnType("tinyint(1)");
+
+ b.HasKey("SlotId");
+
+ b.HasIndex("CreatorId");
+
+ b.HasIndex("LocationId");
+
+ b.ToTable("Slots");
+ });
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b =>
+ {
+ b.Property("VisitedLevelId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP1")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP2")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBP3")
+ .HasColumnType("int");
+
+ b.Property("PlaysLBPVita")
+ .HasColumnType("int");
+
+ b.Property("SlotId")
+ .HasColumnType("int");
+
+ b.Property("UserId")
+ .HasColumnType("int");
+
+ b.HasKey("VisitedLevelId");
+
+ b.HasIndex("SlotId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("VisitedLevels");
+ });
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b =>
+ {
+ b.Property("PhotoId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("CreatorId")
+ .HasColumnType("int");
+
+ b.Property("LargeHash")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("MediumHash")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("PhotoSubjectCollection")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("PlanHash")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("SmallHash")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Timestamp")
+ .HasColumnType("bigint");
+
+ b.HasKey("PhotoId");
+
+ b.HasIndex("CreatorId");
+
+ b.ToTable("Photos");
+ });
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b =>
+ {
+ b.Property("PhotoSubjectId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Bounds")
+ .HasColumnType("longtext");
+
+ b.Property("UserId")
+ .HasColumnType("int");
+
+ b.HasKey("PhotoSubjectId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("PhotoSubjects");
+ });
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b =>
+ {
+ b.Property("CommentId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property("Message")
+ .HasColumnType("longtext");
+
+ b.Property("PosterUserId")
+ .HasColumnType("int");
+
+ b.Property("TargetUserId")
+ .HasColumnType("int");
+
+ b.Property("ThumbsDown")
+ .HasColumnType("int");
+
+ b.Property("ThumbsUp")
+ .HasColumnType("int");
+
+ b.Property("Timestamp")
+ .HasColumnType("bigint");
+
+ b.HasKey("CommentId");
+
+ b.HasIndex("PosterUserId");
+
+ b.HasIndex("TargetUserId");
+
+ b.ToTable("Comments");
+ });
+
+ modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastMatch", b =>
+ {
+ b.Property("UserId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ b.Property