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("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211120045239_AddPasswordToUser.cs b/ProjectLighthouse/Migrations/20211120045239_AddPasswordToUser.cs new file mode 100644 index 00000000..e962eee8 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211120045239_AddPasswordToUser.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class AddPasswordToUser : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Password", + table: "Users", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Password", + table: "Users"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211120052549_RenameTokensToGameTokens.Designer.cs b/ProjectLighthouse/Migrations/20211120052549_RenameTokensToGameTokens.Designer.cs new file mode 100644 index 00000000..f0e1ea83 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211120052549_RenameTokensToGameTokens.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("20211120052549_RenameTokensToGameTokens")] + partial class RenameTokensToGameTokens + { + 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.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + + 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("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211120052549_RenameTokensToGameTokens.cs b/ProjectLighthouse/Migrations/20211120052549_RenameTokensToGameTokens.cs new file mode 100644 index 00000000..59150955 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211120052549_RenameTokensToGameTokens.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class RenameTokensToGameTokens : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_Tokens", + table: "Tokens"); + + migrationBuilder.RenameTable( + name: "Tokens", + newName: "GameTokens"); + + migrationBuilder.AddPrimaryKey( + name: "PK_GameTokens", + table: "GameTokens", + column: "TokenId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_GameTokens", + table: "GameTokens"); + + migrationBuilder.RenameTable( + name: "GameTokens", + newName: "Tokens"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Tokens", + table: "Tokens", + column: "TokenId"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211120053654_AddWebTokens.Designer.cs b/ProjectLighthouse/Migrations/20211120053654_AddWebTokens.Designer.cs new file mode 100644 index 00000000..fe402f9f --- /dev/null +++ b/ProjectLighthouse/Migrations/20211120053654_AddWebTokens.Designer.cs @@ -0,0 +1,671 @@ +// +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("20211120053654_AddWebTokens")] + partial class AddWebTokens + { + 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.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + + 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("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211120053654_AddWebTokens.cs b/ProjectLighthouse/Migrations/20211120053654_AddWebTokens.cs new file mode 100644 index 00000000..7fe503b6 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211120053654_AddWebTokens.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class AddWebTokens : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "WebTokens", + columns: table => new + { + TokenId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "int", nullable: false), + UserToken = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_WebTokens", x => x.TokenId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "WebTokens"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211122002000_AddAuthenticationAttempts.Designer.cs b/ProjectLighthouse/Migrations/20211122002000_AddAuthenticationAttempts.Designer.cs new file mode 100644 index 00000000..3122c70a --- /dev/null +++ b/ProjectLighthouse/Migrations/20211122002000_AddAuthenticationAttempts.Designer.cs @@ -0,0 +1,710 @@ +// +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("20211122002000_AddAuthenticationAttempts")] + partial class AddAuthenticationAttempts + { + 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.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + + 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("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211122002000_AddAuthenticationAttempts.cs b/ProjectLighthouse/Migrations/20211122002000_AddAuthenticationAttempts.cs new file mode 100644 index 00000000..d63e4918 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211122002000_AddAuthenticationAttempts.cs @@ -0,0 +1,59 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class AddAuthenticationAttempts : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Approved", + table: "GameTokens", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "AuthenticationAttempts", + columns: table => new + { + AuthenticationAttemptId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Timestamp = table.Column(type: "bigint", nullable: false), + Platform = table.Column(type: "int", nullable: false), + IPAddress = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + GameTokenId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AuthenticationAttempts", x => x.AuthenticationAttemptId); + table.ForeignKey( + name: "FK_AuthenticationAttempts_GameTokens_GameTokenId", + column: x => x.GameTokenId, + principalTable: "GameTokens", + principalColumn: "TokenId", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_AuthenticationAttempts_GameTokenId", + table: "AuthenticationAttempts", + column: "GameTokenId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AuthenticationAttempts"); + + migrationBuilder.DropColumn( + name: "Approved", + table: "GameTokens"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211123224001_AddIsAdminToUser.Designer.cs b/ProjectLighthouse/Migrations/20211123224001_AddIsAdminToUser.Designer.cs new file mode 100644 index 00000000..2ceca95c --- /dev/null +++ b/ProjectLighthouse/Migrations/20211123224001_AddIsAdminToUser.Designer.cs @@ -0,0 +1,713 @@ +// +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("20211123224001_AddIsAdminToUser")] + partial class AddIsAdminToUser + { + 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.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + + 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("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211123224001_AddIsAdminToUser.cs b/ProjectLighthouse/Migrations/20211123224001_AddIsAdminToUser.cs new file mode 100644 index 00000000..4678bb64 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211123224001_AddIsAdminToUser.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class AddIsAdminToUser : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsAdmin", + table: "Users", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsAdmin", + table: "Users"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211125052035_AddGameVersionToLastMatch.Designer.cs b/ProjectLighthouse/Migrations/20211125052035_AddGameVersionToLastMatch.Designer.cs new file mode 100644 index 00000000..1793ca1b --- /dev/null +++ b/ProjectLighthouse/Migrations/20211125052035_AddGameVersionToLastMatch.Designer.cs @@ -0,0 +1,716 @@ +// +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("20211125052035_AddGameVersionToLastMatch")] + partial class AddGameVersionToLastMatch + { + 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.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + + 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("GameVersion") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211125052035_AddGameVersionToLastMatch.cs b/ProjectLighthouse/Migrations/20211125052035_AddGameVersionToLastMatch.cs new file mode 100644 index 00000000..23c6e128 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211125052035_AddGameVersionToLastMatch.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class AddGameVersionToLastMatch : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "GameVersion", + table: "LastMatches", + type: "int", + nullable: false, + defaultValue: 0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "GameVersion", + table: "LastMatches"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211127201738_AddPasswordResetRequiredToUser.Designer.cs b/ProjectLighthouse/Migrations/20211127201738_AddPasswordResetRequiredToUser.Designer.cs new file mode 100644 index 00000000..1c8e0d20 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211127201738_AddPasswordResetRequiredToUser.Designer.cs @@ -0,0 +1,719 @@ +// +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("20211127201738_AddPasswordResetRequiredToUser")] + partial class AddPasswordResetRequiredToUser + { + 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.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + + 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("GameVersion") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastMatches"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("PasswordResetRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211127201738_AddPasswordResetRequiredToUser.cs b/ProjectLighthouse/Migrations/20211127201738_AddPasswordResetRequiredToUser.cs new file mode 100644 index 00000000..0fd106d6 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211127201738_AddPasswordResetRequiredToUser.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class AddPasswordResetRequiredToUser : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PasswordResetRequired", + table: "Users", + type: "tinyint(1)", + nullable: false, + defaultValue: false); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PasswordResetRequired", + table: "Users"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211120025513_LevelReviews.Designer.cs b/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.Designer.cs similarity index 90% rename from ProjectLighthouse/Migrations/20211120025513_LevelReviews.Designer.cs rename to ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.Designer.cs index 64eaac27..8c618602 100644 --- a/ProjectLighthouse/Migrations/20211120025513_LevelReviews.Designer.cs +++ b/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.Designer.cs @@ -10,8 +10,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace ProjectLighthouse.Migrations { [DbContext(typeof(Database))] - [Migration("20211120025513_LevelReviews")] - partial class LevelReviews + [Migration("20211130190200_AddYayBooMehHashesToUser")] + partial class AddYayBooMehHashesToUser { protected override void BuildTargetModel(ModelBuilder modelBuilder) { @@ -20,6 +20,57 @@ namespace ProjectLighthouse.Migrations .HasAnnotation("ProductVersion", "6.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => { b.Property("HeartedProfileId") @@ -362,6 +413,9 @@ namespace ProjectLighthouse.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("GameVersion") + .HasColumnType("int"); + b.Property("Timestamp") .HasColumnType("bigint"); @@ -387,69 +441,6 @@ namespace ProjectLighthouse.Migrations b.ToTable("Locations"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => - { - b.Property("RatedReviewId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("ReviewId") - .HasColumnType("int"); - - b.Property("Thumb") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.HasKey("RatedReviewId"); - - b.HasIndex("ReviewId"); - - b.HasIndex("UserId"); - - b.ToTable("RatedReviews"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => - { - b.Property("ReviewId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Deleted") - .HasColumnType("tinyint(1)"); - - b.Property("DeletedBy") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("LabelCollection") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ReviewerId") - .HasColumnType("int"); - - b.Property("SlotId") - .HasColumnType("int"); - - b.Property("Text") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Timestamp") - .HasColumnType("bigint"); - - b.HasKey("ReviewId"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("SlotId"); - - b.ToTable("Reviews"); - }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => { b.Property("ScoreId") @@ -475,29 +466,6 @@ namespace ProjectLighthouse.Migrations b.ToTable("Scores"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b => - { - b.Property("TokenId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("GameVersion") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.Property("UserLocation") - .HasColumnType("longtext"); - - b.Property("UserToken") - .HasColumnType("longtext"); - - b.HasKey("TokenId"); - - b.ToTable("Tokens"); - }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => { b.Property("UserId") @@ -507,15 +475,30 @@ namespace ProjectLighthouse.Migrations b.Property("Biography") .HasColumnType("longtext"); + b.Property("BooHash") + .HasColumnType("longtext"); + b.Property("Game") .HasColumnType("int"); b.Property("IconHash") .HasColumnType("longtext"); + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + b.Property("LocationId") .HasColumnType("int"); + b.Property("MehHash") + .HasColumnType("longtext"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("PasswordResetRequired") + .HasColumnType("tinyint(1)"); + b.Property("Pins") .HasColumnType("longtext"); @@ -525,6 +508,9 @@ namespace ProjectLighthouse.Migrations b.Property("Username") .HasColumnType("longtext"); + b.Property("YayHash") + .HasColumnType("longtext"); + b.HasKey("UserId"); b.HasIndex("LocationId"); @@ -532,6 +518,34 @@ namespace ProjectLighthouse.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => { b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") @@ -687,44 +701,6 @@ namespace ProjectLighthouse.Migrations b.Navigation("Target"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Reviews.Review", "Review") - .WithMany() - .HasForeignKey("ReviewId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Review"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Slot"); - }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => { b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") diff --git a/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.cs b/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.cs new file mode 100644 index 00000000..26ae44f3 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class AddYayBooMehHashesToUser : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BooHash", + table: "Users", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "MehHash", + table: "Users", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "YayHash", + table: "Users", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "BooHash", + table: "Users"); + + migrationBuilder.DropColumn( + name: "MehHash", + table: "Users"); + + migrationBuilder.DropColumn( + name: "YayHash", + table: "Users"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.Designer.cs b/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.Designer.cs new file mode 100644 index 00000000..ef8e4d93 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.Designer.cs @@ -0,0 +1,728 @@ +// +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("20211202235932_RenameLastMatchesToLastContacts")] + partial class RenameLastMatchesToLastContacts + { + 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.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + + 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.LastContact", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastContacts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("BooHash") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MehHash") + .HasColumnType("longtext"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("PasswordResetRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.Property("YayHash") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.cs b/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.cs new file mode 100644 index 00000000..0029a287 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class RenameLastMatchesToLastContacts : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameTable(name: "LastMatches", newName: "LastContacts"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameTable(name: "LastContacts", newName: "LastMatches"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211211045823_AddLevelReviews.Designer.cs b/ProjectLighthouse/Migrations/20211211045823_AddLevelReviews.Designer.cs new file mode 100644 index 00000000..98610a6c --- /dev/null +++ b/ProjectLighthouse/Migrations/20211211045823_AddLevelReviews.Designer.cs @@ -0,0 +1,837 @@ +// +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("20211211045823_AddLevelReviews")] + partial class AddLevelReviews + { + 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.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + + 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.LastContact", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastContacts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => + { + b.Property("RatedReviewId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("ReviewId") + .HasColumnType("int"); + + b.Property("Thumb") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("RatedReviewId"); + + b.HasIndex("ReviewId"); + + b.HasIndex("UserId"); + + b.ToTable("RatedReviews"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => + { + b.Property("ReviewId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Deleted") + .HasColumnType("tinyint(1)"); + + b.Property("DeletedBy") + .HasColumnType("int"); + + b.Property("LabelCollection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ReviewerId") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Thumb") + .HasColumnType("int"); + + b.Property("ThumbsDown") + .HasColumnType("int"); + + b.Property("ThumbsUp") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("ReviewId"); + + b.HasIndex("ReviewerId"); + + b.HasIndex("SlotId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("BooHash") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MehHash") + .HasColumnType("longtext"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("PasswordResetRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.Property("YayHash") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Reviews.Review", "Review") + .WithMany() + .HasForeignKey("ReviewId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Review"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Reviewer") + .WithMany() + .HasForeignKey("ReviewerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Reviewer"); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211120025513_LevelReviews.cs b/ProjectLighthouse/Migrations/20211211045823_AddLevelReviews.cs similarity index 92% rename from ProjectLighthouse/Migrations/20211120025513_LevelReviews.cs rename to ProjectLighthouse/Migrations/20211211045823_AddLevelReviews.cs index 07d2e4c5..ccdf6c2c 100644 --- a/ProjectLighthouse/Migrations/20211120025513_LevelReviews.cs +++ b/ProjectLighthouse/Migrations/20211211045823_AddLevelReviews.cs @@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore.Migrations; namespace ProjectLighthouse.Migrations { - public partial class LevelReviews : Migration + public partial class AddLevelReviews : Migration { protected override void Up(MigrationBuilder migrationBuilder) { @@ -21,10 +21,12 @@ namespace ProjectLighthouse.Migrations LabelCollection = table.Column(type: "longtext", nullable: false) .Annotation("MySql:CharSet", "utf8mb4"), Deleted = table.Column(type: "tinyint(1)", nullable: false), - DeletedBy = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), + DeletedBy = table.Column(type: "int", nullable: false), Text = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4") + .Annotation("MySql:CharSet", "utf8mb4"), + Thumb = table.Column(type: "int", nullable: false), + ThumbsUp = table.Column(type: "int", nullable: false), + ThumbsDown = table.Column(type: "int", nullable: false) }, constraints: table => { diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index a21d10d6..4afc7b51 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -18,6 +18,57 @@ namespace ProjectLighthouse.Migrations .HasAnnotation("ProductVersion", "6.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => { b.Property("HeartedProfileId") @@ -354,18 +405,21 @@ namespace ProjectLighthouse.Migrations b.ToTable("Comments"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastMatch", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastContact", b => { b.Property("UserId") .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("GameVersion") + .HasColumnType("int"); + b.Property("Timestamp") .HasColumnType("bigint"); b.HasKey("UserId"); - b.ToTable("LastMatches"); + b.ToTable("LastContacts"); }); modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => @@ -418,9 +472,8 @@ namespace ProjectLighthouse.Migrations b.Property("Deleted") .HasColumnType("tinyint(1)"); - b.Property("DeletedBy") - .IsRequired() - .HasColumnType("longtext"); + b.Property("DeletedBy") + .HasColumnType("int"); b.Property("LabelCollection") .IsRequired() @@ -436,6 +489,15 @@ namespace ProjectLighthouse.Migrations .IsRequired() .HasColumnType("longtext"); + b.Property("Thumb") + .HasColumnType("int"); + + b.Property("ThumbsDown") + .HasColumnType("int"); + + b.Property("ThumbsUp") + .HasColumnType("int"); + b.Property("Timestamp") .HasColumnType("bigint"); @@ -473,29 +535,6 @@ namespace ProjectLighthouse.Migrations b.ToTable("Scores"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b => - { - b.Property("TokenId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("GameVersion") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.Property("UserLocation") - .HasColumnType("longtext"); - - b.Property("UserToken") - .HasColumnType("longtext"); - - b.HasKey("TokenId"); - - b.ToTable("Tokens"); - }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => { b.Property("UserId") @@ -505,15 +544,30 @@ namespace ProjectLighthouse.Migrations b.Property("Biography") .HasColumnType("longtext"); + b.Property("BooHash") + .HasColumnType("longtext"); + b.Property("Game") .HasColumnType("int"); b.Property("IconHash") .HasColumnType("longtext"); + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + b.Property("LocationId") .HasColumnType("int"); + b.Property("MehHash") + .HasColumnType("longtext"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("PasswordResetRequired") + .HasColumnType("tinyint(1)"); + b.Property("Pins") .HasColumnType("longtext"); @@ -523,6 +577,9 @@ namespace ProjectLighthouse.Migrations b.Property("Username") .HasColumnType("longtext"); + b.Property("YayHash") + .HasColumnType("longtext"); + b.HasKey("UserId"); b.HasIndex("LocationId"); @@ -530,6 +587,34 @@ namespace ProjectLighthouse.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => { b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") diff --git a/ProjectLighthouse/Pages/AdminPanelPage.cshtml b/ProjectLighthouse/Pages/AdminPanelPage.cshtml new file mode 100644 index 00000000..891e8891 --- /dev/null +++ b/ProjectLighthouse/Pages/AdminPanelPage.cshtml @@ -0,0 +1,54 @@ +@page "/admin" +@using LBPUnion.ProjectLighthouse.Helpers +@using LBPUnion.ProjectLighthouse.Maintenance +@model LBPUnion.ProjectLighthouse.Pages.AdminPanelPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Admin Panel"; +} + +

Commands

+
+ @foreach (ICommand command in MaintenanceHelper.Commands) + { +
+
+

@command.Name()

+
+
+ +


+ + +
+
+
+ } +
+

Maintenance Jobs

+

+ Warning: Interrupting Lighthouse during maintenance may leave the database in an unclean state. +

+ +
+ @foreach (IMaintenanceJob job in MaintenanceHelper.MaintenanceJobs) + { +
+
+

@job.Name()

+

@job.Description()

+
+ + +
+
+
+ } +
\ No newline at end of file diff --git a/ProjectLighthouse/Pages/AdminPanelPage.cshtml.cs b/ProjectLighthouse/Pages/AdminPanelPage.cshtml.cs new file mode 100644 index 00000000..e12a2b4a --- /dev/null +++ b/ProjectLighthouse/Pages/AdminPanelPage.cshtml.cs @@ -0,0 +1,43 @@ +#nullable enable +using System.Collections.Generic; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Maintenance; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class AdminPanelPage : BaseLayout + { + + public List Commands = MaintenanceHelper.Commands; + public AdminPanelPage(Database database) : base(database) + {} + + public async Task OnGet([FromQuery] string? args, [FromQuery] string? command, [FromQuery] string? maintenanceJob) + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); + if (!user.IsAdmin) return this.NotFound(); + + if (!string.IsNullOrEmpty(command)) + { + args ??= ""; + args = command + " " + args; + string[] split = args.Split(" "); + await MaintenanceHelper.RunCommand(split); + return this.Redirect("~/admin"); + } + + if (!string.IsNullOrEmpty(maintenanceJob)) + { + await MaintenanceHelper.RunMaintenanceJob(maintenanceJob); + return this.Redirect("~/admin"); + } + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml new file mode 100644 index 00000000..58521e46 --- /dev/null +++ b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml @@ -0,0 +1,36 @@ +@page "/authentication" +@using LBPUnion.ProjectLighthouse.Types +@model LBPUnion.ProjectLighthouse.Pages.ExternalAuth.AuthenticationPage + +@{ + Layout = "Layouts/BaseLayout"; +} +

Authentication

+ +@if (Model.AuthenticationAttempts.Count == 0) +{ +

You have no pending authentication attempts.

+} +else +{ +

You have @Model.AuthenticationAttempts.Count authentication attempts pending.

+ + + +} + +@foreach (AuthenticationAttempt authAttempt in Model.AuthenticationAttempts) +{ + DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(authAttempt.Timestamp); +
+

A @authAttempt.Platform authentication request was logged at @timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC from the IP address @authAttempt.IPAddress.

+ +
+} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs new file mode 100644 index 00000000..13a1f5eb --- /dev/null +++ b/ProjectLighthouse/Pages/ExternalAuth/AuthenticationPage.cshtml.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Types.Settings; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Pages.ExternalAuth +{ + public class AuthenticationPage : BaseLayout + { + + public List AuthenticationAttempts; + public AuthenticationPage(Database database) : base(database) + {} + + public async Task OnGet() + { + if (!ServerSettings.Instance.UseExternalAuth) return this.NotFound(); + + this.AuthenticationAttempts = this.Database.AuthenticationAttempts.Include + (a => a.GameToken) + .Where(a => a.GameToken.UserId == this.User.UserId) + .OrderByDescending(a => a.Timestamp) + .ToList(); + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LandingPage.cshtml b/ProjectLighthouse/Pages/LandingPage.cshtml new file mode 100644 index 00000000..fc67aaae --- /dev/null +++ b/ProjectLighthouse/Pages/LandingPage.cshtml @@ -0,0 +1,35 @@ +@page "/" +@using LBPUnion.ProjectLighthouse.Types +@model LBPUnion.ProjectLighthouse.Pages.LandingPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.ShowTitleInPage = false; +} +

Welcome to Project Lighthouse!

+ +@if (Model.User != null) +{ +

You are currently logged in as @Model.User.Username.

+} + +@if (Model.PlayersOnlineCount == 1) +{ +

There is 1 user currently online:

+ @foreach (User user in Model.PlayersOnline) + { + @user.Username + } +} +else if (Model.PlayersOnlineCount == 0) +{ +

There are no users online. Why not hop on?

+} +else +{ +

There are currently @Model.PlayersOnlineCount users online:

+ @foreach (User user in Model.PlayersOnline) + { + @user.Username + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LandingPage.cshtml.cs b/ProjectLighthouse/Pages/LandingPage.cshtml.cs new file mode 100644 index 00000000..23289463 --- /dev/null +++ b/ProjectLighthouse/Pages/LandingPage.cshtml.cs @@ -0,0 +1,36 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class LandingPage : BaseLayout + { + public List PlayersOnline; + + public int PlayersOnlineCount; + public LandingPage(Database database) : base(database) + {} + + [UsedImplicitly] + public async Task OnGet() + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user != null && user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); + + this.PlayersOnlineCount = await StatisticsHelper.RecentMatches(); + + List userIds = await this.Database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync(); + + this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync(); + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml new file mode 100644 index 00000000..2295cea3 --- /dev/null +++ b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml @@ -0,0 +1,138 @@ +@using LBPUnion.ProjectLighthouse.Helpers +@using LBPUnion.ProjectLighthouse.Types +@using LBPUnion.ProjectLighthouse.Types.Settings +@model LBPUnion.ProjectLighthouse.Pages.Layouts.BaseLayout + +@{ + if (Model!.User == null) + { + Model.NavigationItemsRight.Add(new PageNavigationItem("Log in", "/login", "user alternate")); + if (ServerSettings.Instance.RegistrationEnabled) + { + Model.NavigationItemsRight.Add(new PageNavigationItem("Register", "/register", "user alternate edit")); + } + } + else + { + if (ServerSettings.Instance.UseExternalAuth) + { + Model.NavigationItems.Add(new PageNavigationItem("Authentication", "/authentication", "key")); + } + Model.NavigationItemsRight.Add(new PageNavigationItem("Profile", "/user/" + Model.User.UserId, "user alternate")); + + @if (Model.User.IsAdmin) + { + Model.NavigationItemsRight.Add(new PageNavigationItem("Admin Panel", "/admin", "cogs")); + } + Model.NavigationItemsRight.Add(new PageNavigationItem("Log out", "/logout", "user alternate slash")); // should always be last + } +} + + + + + + @if (Model.Title == string.Empty) + { + Project Lighthouse + } + else + { + Project Lighthouse - @Model.Title + } + + + + @* Favicon *@ + + + + + + + + + @if (ServerSettings.Instance.GoogleAnalyticsEnabled) + { + + + + } + + +
+
+ + +
+
+
+
+ @if (Model.ShowTitleInPage) + { +

@Model.Title

+ } + @RenderBody() +
@* makes it look nicer *@ +
+
+
+
+
+ +

Page generated by @VersionHelper.FullVersion.

+ @if (VersionHelper.IsDirty) + { +

This page was generated using a modified version of Project Lighthouse. Please make sure you are properly disclosing the source code to any users who may be using this instance.

+ } +
+
+
+
+ + \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs new file mode 100644 index 00000000..f7f14670 --- /dev/null +++ b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs @@ -0,0 +1,41 @@ +#nullable enable +using System.Collections.Generic; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace LBPUnion.ProjectLighthouse.Pages.Layouts +{ + public class BaseLayout : PageModel + { + public readonly Database Database; + + public readonly List NavigationItems = new() + { + new PageNavigationItem("Home", "/", "home"), + new PageNavigationItem("Photos", "/photos/0", "camera"), + new PageNavigationItem("Levels", "/slots/0", "certificate"), + }; + + public readonly List NavigationItemsRight = new(); + + public bool ShowTitleInPage = true; + + public string Title = string.Empty; + + private User? user; + + public BaseLayout(Database database) + { + this.Database = database; + } + + public new User? User { + get { + if (this.user != null) return this.user; + + return this.user = this.Database.UserFromWebRequest(this.Request); + } + set => this.user = value; + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml b/ProjectLighthouse/Pages/LoginForm.cshtml new file mode 100644 index 00000000..5967a096 --- /dev/null +++ b/ProjectLighthouse/Pages/LoginForm.cshtml @@ -0,0 +1,45 @@ +@page "/login" +@model LBPUnion.ProjectLighthouse.Pages.LoginForm + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Log in"; +} + + + + + +@if (!string.IsNullOrWhiteSpace(Model.Error)) +{ +
+
+ Uh oh! +
+

@Model.Error

+
+} + +
+ @Html.AntiForgeryToken() + +
+ + +


+ +
+ + +



+ +
+
\ No newline at end of file diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml.cs b/ProjectLighthouse/Pages/LoginForm.cshtml.cs new file mode 100644 index 00000000..c7871109 --- /dev/null +++ b/ProjectLighthouse/Pages/LoginForm.cshtml.cs @@ -0,0 +1,72 @@ +#nullable enable +using System.Threading.Tasks; +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class LoginForm : BaseLayout + { + public LoginForm(Database database) : base(database) + {} + + public string Error { get; private set; } + + public bool WasLoginRequest { get; private set; } + + [UsedImplicitly] + public async Task OnPost(string username, string password) + { + if (string.IsNullOrWhiteSpace(username)) + { + this.Error = "The username field is required."; + return this.Page(); + } + + if (string.IsNullOrWhiteSpace(password)) + { + this.Error = "The password field is required."; + return this.Page(); + } + + User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (user == null) + { + this.Error = "The username or password you entered is invalid."; + return this.Page(); + } + + if (!BCrypt.Net.BCrypt.Verify(password, user.Password)) + { + this.Error = "The username or password you entered is invalid."; + return this.Page(); + } + + WebToken webToken = new() + { + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; + + this.Database.WebTokens.Add(webToken); + await this.Database.SaveChangesAsync(); + + this.Response.Cookies.Append("LighthouseToken", webToken.UserToken); + + if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); + + return this.RedirectToPage(nameof(LandingPage)); + } + + [UsedImplicitly] + public async Task OnGet() + { + this.Error = string.Empty; + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LogoutPage.cshtml b/ProjectLighthouse/Pages/LogoutPage.cshtml new file mode 100644 index 00000000..589ed4f5 --- /dev/null +++ b/ProjectLighthouse/Pages/LogoutPage.cshtml @@ -0,0 +1,10 @@ +@page "/logout" +@model LBPUnion.ProjectLighthouse.Pages.LogoutPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Logged out"; +} + +

You have been successfully logged out. You will be redirected in 5 seconds, or you may click here to do so manually.

+ \ No newline at end of file diff --git a/ProjectLighthouse/Pages/LogoutPage.cshtml.cs b/ProjectLighthouse/Pages/LogoutPage.cshtml.cs new file mode 100644 index 00000000..792947be --- /dev/null +++ b/ProjectLighthouse/Pages/LogoutPage.cshtml.cs @@ -0,0 +1,26 @@ +#nullable enable +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class LogoutPage : BaseLayout + { + public LogoutPage(Database database) : base(database) + {} + public async Task OnGet() + { + WebToken? token = this.Database.WebTokenFromRequest(this.Request); + if (token == null) return this.BadRequest(); + + this.Database.WebTokens.Remove(token); + await this.Database.SaveChangesAsync(); + + this.Response.Cookies.Delete("LighthouseToken"); + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Partials/PhotoPartial.cshtml b/ProjectLighthouse/Pages/Partials/PhotoPartial.cshtml new file mode 100644 index 00000000..33a8fbc1 --- /dev/null +++ b/ProjectLighthouse/Pages/Partials/PhotoPartial.cshtml @@ -0,0 +1,22 @@ +@using LBPUnion.ProjectLighthouse.Types +@model LBPUnion.ProjectLighthouse.Types.Photo + + +
+ +

+ + Taken by + + @Model.Creator?.Username + + +

+ +

+ Photo contains @Model.Subjects.Count @(Model.Subjects.Count == 1 ? "person" : "people"): +

+@foreach (PhotoSubject subject in Model.Subjects) +{ + @subject.User.Username +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml b/ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml new file mode 100644 index 00000000..1d64b996 --- /dev/null +++ b/ProjectLighthouse/Pages/Partials/SlotCardPartial.cshtml @@ -0,0 +1,86 @@ +@using LBPUnion.ProjectLighthouse +@using LBPUnion.ProjectLighthouse.Types +@using Microsoft.EntityFrameworkCore +@model LBPUnion.ProjectLighthouse.Types.Levels.Slot + +@{ + User user = (User)ViewData["User"]; + + await using Database database = new(); + + string slotName = string.IsNullOrEmpty(Model.Name) ? "Unnamed Level" : Model.Name; + + bool isQueued = false; + bool isHearted = false; + + if (user != null) + { + isQueued = await database.QueuedLevels.FirstOrDefaultAsync(h => h.SlotId == Model.SlotId && h.UserId == user.UserId) != null; + + isHearted = await database.HeartedLevels.FirstOrDefaultAsync(h => h.SlotId == Model.SlotId && h.UserId == user.UserId) != null; + } + + string callbackUrl = (string)ViewData["CallbackUrl"]; + bool showLink = (bool?)ViewData["ShowLink"] ?? false; +} +
+
+ @if (showLink) + { +

+ @slotName +

+ } + else + { +

+ @slotName +

+ } +
+ @Model.Hearts + @Model.Plays + @Model.Thumbsup + @Model.Thumbsdown + + @if (Model.GameVersion == GameVersion.LittleBigPlanet1) + { + + @Model.RatingLBP1 + } +
+

+ Created by @Model.Creator?.Username +

+
+
+ @if (user != null) + { + if (isHearted) + { + + + + } + else + { + + + + } + + if (isQueued) + { + + + + } + else + { + + + + } + } +
+
\ No newline at end of file diff --git a/ProjectLighthouse/Pages/PasswordResetPage.cshtml b/ProjectLighthouse/Pages/PasswordResetPage.cshtml new file mode 100644 index 00000000..7cc087e1 --- /dev/null +++ b/ProjectLighthouse/Pages/PasswordResetPage.cshtml @@ -0,0 +1,47 @@ +@page "/passwordReset" +@model LBPUnion.ProjectLighthouse.Pages.PasswordResetPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Password Reset"; +} + + + + + +@if (!string.IsNullOrWhiteSpace(Model.Error)) +{ +
+
+ Uh oh! +
+

@Model.Error

+
+} + +
+ @Html.AntiForgeryToken() + +
+ + +


+ +
+ + +



+ +
+
\ No newline at end of file diff --git a/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs b/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs new file mode 100644 index 00000000..8f844f7f --- /dev/null +++ b/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs @@ -0,0 +1,53 @@ +#nullable enable +using System.Threading.Tasks; +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class PasswordResetPage : BaseLayout + { + public PasswordResetPage(Database database) : base(database) + {} + + public string Error { get; private set; } + + [UsedImplicitly] + public async Task OnPost(string password, string confirmPassword) + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); + + if (string.IsNullOrWhiteSpace(password)) + { + this.Error = "The password field is required."; + return this.Page(); + } + + if (password != confirmPassword) + { + this.Error = "Passwords do not match!"; + return this.Page(); + } + + user.Password = HashHelper.BCryptHash(password); + user.PasswordResetRequired = false; + + await this.Database.SaveChangesAsync(); + + return this.Redirect("~/"); + } + + [UsedImplicitly] + public IActionResult OnGet() + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml new file mode 100644 index 00000000..616d2f8a --- /dev/null +++ b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml @@ -0,0 +1,13 @@ +@page "/passwordResetRequired" +@model LBPUnion.ProjectLighthouse.Pages.PasswordResetRequiredPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Password Reset Required"; +} + +

An admin has deemed it necessary that you reset your password. Please do so.

+ + +
Reset Password
+
\ No newline at end of file diff --git a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs new file mode 100644 index 00000000..55c07595 --- /dev/null +++ b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml.cs @@ -0,0 +1,26 @@ +#nullable enable +using System.Threading.Tasks; +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using Microsoft.AspNetCore.Mvc; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class PasswordResetRequiredPage : BaseLayout + { + public PasswordResetRequiredPage([NotNull] Database database) : base(database) + {} + + public bool WasResetRequest { get; private set; } + + public async Task OnGet() + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); + if (!user.PasswordResetRequired) return this.Redirect("~/passwordReset"); + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/PhotosPage.cshtml b/ProjectLighthouse/Pages/PhotosPage.cshtml new file mode 100644 index 00000000..86e16661 --- /dev/null +++ b/ProjectLighthouse/Pages/PhotosPage.cshtml @@ -0,0 +1,27 @@ +@page "/photos/{pageNumber:int}" +@using LBPUnion.ProjectLighthouse.Types +@model LBPUnion.ProjectLighthouse.Pages.PhotosPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Photos"; +} + +

There are @Model.PhotoCount total photos!

+ +@foreach (Photo photo in Model.Photos) +{ +
+ @await Html.PartialAsync("Partials/PhotoPartial", photo) +
+} + +@if (Model.PageNumber != 0) +{ + Previous Page +} +@(Model.PageNumber + 1) / @(Model.PageAmount) +@if (Model.PageNumber < Model.PageAmount - 1) +{ + Next Page +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/PhotosPage.cshtml.cs b/ProjectLighthouse/Pages/PhotosPage.cshtml.cs new file mode 100644 index 00000000..f5e132b0 --- /dev/null +++ b/ProjectLighthouse/Pages/PhotosPage.cshtml.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Types.Settings; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class PhotosPage : BaseLayout + { + + public int PageNumber; + + public int PhotoCount; + + public int PageAmount; + + public List Photos; + public PhotosPage([NotNull] Database database) : base(database) + {} + + public async Task OnGet([FromRoute] int pageNumber) + { + this.PhotoCount = await StatisticsHelper.PhotoCount(); + + this.PageNumber = pageNumber; + this.PageAmount = (int)Math.Ceiling((double)this.PhotoCount / ServerStatics.PageSize); + + if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) + { + return this.Redirect($"/photos/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); + } + + this.Photos = await this.Database.Photos.Include + (p => p.Creator) + .OrderByDescending(p => p.Timestamp) + .Skip(pageNumber * ServerStatics.PageSize) + .Take(ServerStatics.PageSize) + .ToListAsync(); + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/RegisterForm.cshtml b/ProjectLighthouse/Pages/RegisterForm.cshtml new file mode 100644 index 00000000..e7c670ae --- /dev/null +++ b/ProjectLighthouse/Pages/RegisterForm.cshtml @@ -0,0 +1,52 @@ +@page "/register" +@model LBPUnion.ProjectLighthouse.Pages.RegisterForm + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Register"; +} + + + + + +@if (!string.IsNullOrWhiteSpace(Model.Error)) +{ +
+
+ Uh oh! +
+

@Model.Error

+
+} + +
+
+ @Html.AntiForgeryToken() + + + +


+ +
+ + +


+ +
+ + +



+ +
+
\ No newline at end of file diff --git a/ProjectLighthouse/Pages/RegisterForm.cshtml.cs b/ProjectLighthouse/Pages/RegisterForm.cshtml.cs new file mode 100644 index 00000000..e09aa17e --- /dev/null +++ b/ProjectLighthouse/Pages/RegisterForm.cshtml.cs @@ -0,0 +1,78 @@ +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Types.Settings; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class RegisterForm : BaseLayout + { + public RegisterForm(Database database) : base(database) + {} + + public string Error { get; private set; } + public bool WasRegisterRequest { get; private set; } + + [UsedImplicitly] + [SuppressMessage("ReSharper", "SpecifyStringComparison")] + public async Task OnPost(string username, string password, string confirmPassword) + { + if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); + + if (string.IsNullOrWhiteSpace(username)) + { + this.Error = "The username field is blank."; + return this.Page(); + } + + if (string.IsNullOrWhiteSpace(password)) + { + this.Error = "Password field is required."; + return this.Page(); + } + + if (password != confirmPassword) + { + this.Error = "Passwords do not match!"; + return this.Page(); + } + + bool userExists = await this.Database.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()) != null; + if (userExists) + { + this.Error = "The username you've chosen is already taken."; + return this.Page(); + } + + User user = await this.Database.CreateUser(username, HashHelper.BCryptHash(password)); + + WebToken webToken = new() + { + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; + + this.Database.WebTokens.Add(webToken); + await this.Database.SaveChangesAsync(); + + this.Response.Cookies.Append("LighthouseToken", webToken.UserToken); + + return this.RedirectToPage(nameof(LandingPage)); + } + + [UsedImplicitly] + [SuppressMessage("ReSharper", "SpecifyStringComparison")] + public IActionResult OnGet() + { + this.Error = string.Empty; + if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/SlotPage.cshtml b/ProjectLighthouse/Pages/SlotPage.cshtml new file mode 100644 index 00000000..221d99f2 --- /dev/null +++ b/ProjectLighthouse/Pages/SlotPage.cshtml @@ -0,0 +1,81 @@ +@page "/slot/{id:int}" +@model LBPUnion.ProjectLighthouse.Pages.SlotPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = Model.Slot.Name; + Model.ShowTitleInPage = false; +} + +@await Html.PartialAsync("Partials/SlotCardPartial", Model.Slot, new ViewDataDictionary(ViewData) +{ + { + "User", Model.User + }, + { + "CallbackUrl", $"~/slot/{Model.Slot.SlotId}" + }, + { + "ShowLink", false + }, +}) + +
+
+
+

Description

+

@(string.IsNullOrEmpty(Model.Slot.Description) ? "This level has no description." : Model.Slot.Description)

+
+
+
+
+

Tags

+ @{ + string[] authorLabels = Model.Slot.AuthorLabels.Split(","); + if (authorLabels.Length == 1) // ..?? ok c# + { +

This level has no tags.

+ } + else + { + foreach (string label in authorLabels.Where(label => !string.IsNullOrEmpty(label))) + { +
@label.Replace("LABEL_", "")
+ } + } + } +
+
+
+@if (Model.User != null && Model.User.IsAdmin) +{ +
+

Admin Settings

+ + @if (Model.Slot.TeamPick) + { + +
+ + Remove Team Pick +
+
+ } + else + { + +
+ + Team Pick +
+
+ } + + +
+ + Delete +
+
+
+} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/SlotPage.cshtml.cs b/ProjectLighthouse/Pages/SlotPage.cshtml.cs new file mode 100644 index 00000000..a0c65f17 --- /dev/null +++ b/ProjectLighthouse/Pages/SlotPage.cshtml.cs @@ -0,0 +1,28 @@ +#nullable enable +using System.Threading.Tasks; +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types.Levels; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class SlotPage : BaseLayout + { + + public Slot Slot; + public SlotPage([NotNull] Database database) : base(database) + {} + + public async Task OnGet([FromRoute] int id) + { + Slot? slot = await this.Database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); + + this.Slot = slot; + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/SlotsPage.cshtml b/ProjectLighthouse/Pages/SlotsPage.cshtml new file mode 100644 index 00000000..89ba207a --- /dev/null +++ b/ProjectLighthouse/Pages/SlotsPage.cshtml @@ -0,0 +1,38 @@ +@page "/slots/{pageNumber:int}" +@using LBPUnion.ProjectLighthouse.Types.Levels +@model LBPUnion.ProjectLighthouse.Pages.SlotsPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = "Levels"; +} + +

There are @Model.SlotCount total levels!

+ +@foreach (Slot slot in Model.Slots) +{ +
+ @await Html.PartialAsync("Partials/SlotCardPartial", slot, new ViewDataDictionary(ViewData) + { + { + "User", Model.User + }, + { + "CallbackUrl", $"~/slots/{Model.PageNumber}" + }, + { + "ShowLink", true + }, + }) +
+} + +@if (Model.PageNumber != 0) +{ + Previous Page +} +@(Model.PageNumber + 1) / @(Model.PageAmount) +@if (Model.PageNumber < Model.PageAmount - 1) +{ + Next Page +} diff --git a/ProjectLighthouse/Pages/SlotsPage.cshtml.cs b/ProjectLighthouse/Pages/SlotsPage.cshtml.cs new file mode 100644 index 00000000..12c7432e --- /dev/null +++ b/ProjectLighthouse/Pages/SlotsPage.cshtml.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using JetBrains.Annotations; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types.Levels; +using LBPUnion.ProjectLighthouse.Types.Settings; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class SlotsPage : BaseLayout + { + + public int PageNumber; + + public int SlotCount; + + public int PageAmount; + + public List Slots; + public SlotsPage([NotNull] Database database) : base(database) + {} + + public async Task OnGet([FromRoute] int pageNumber) + { + this.SlotCount = await StatisticsHelper.SlotCount(); + + this.PageNumber = pageNumber; + this.PageAmount = (int)Math.Ceiling((double)this.SlotCount / ServerStatics.PageSize); + + if (this.PageNumber < 0 || this.PageNumber >= this.PageAmount) + { + return this.Redirect($"/slots/{Math.Clamp(this.PageNumber, 0, this.PageAmount - 1)}"); + } + + this.Slots = await this.Database.Slots.Include + (p => p.Creator) + .OrderByDescending(p => p.FirstUploaded) + .Skip(pageNumber * ServerStatics.PageSize) + .Take(ServerStatics.PageSize) + .ToListAsync(); + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Pages/UserPage.cshtml b/ProjectLighthouse/Pages/UserPage.cshtml new file mode 100644 index 00000000..128a7883 --- /dev/null +++ b/ProjectLighthouse/Pages/UserPage.cshtml @@ -0,0 +1,104 @@ +@page "/user/{userId:int}" +@using LBPUnion.ProjectLighthouse.Types +@using LBPUnion.ProjectLighthouse.Types.Profiles +@using LBPUnion.ProjectLighthouse.Types.Settings +@model LBPUnion.ProjectLighthouse.Pages.UserPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = Model.ProfileUser!.Username + "'s user page"; + Model.ShowTitleInPage = false; +} + +
+
+

@Model.Title

+

+ @Model.ProfileUser!.Status +

+
+ @Model.ProfileUser.Hearts + @Model.ProfileUser.Comments + @Model.ProfileUser.UsedSlots / @ServerSettings.Instance.EntitledSlots + @Model.ProfileUser.PhotosByMe +
+
+
+
+ @if (Model.ProfileUser != Model.User && Model.User != null) + { + if (!Model.IsProfileUserHearted) + { + + + Heart + + } + else + { + + + Unheart + + } + } + @if (Model.ProfileUser == Model.User) + { + + + Reset Password + + } +
+
+
+

Biography

+

@Model.ProfileUser.Biography

+
+
+
+
+

Recent Activity

+

Coming soon!

+
+
+
+ + +@if (Model.Photos != null && Model.Photos.Count != 0) +{ +
+

Most recent photos

+ +
+ @foreach (Photo photo in Model.Photos) + { +
+ @await Html.PartialAsync("Partials/PhotoPartial", photo); +
+ } +
+
+} + + +
+

Comments

+ @if (Model.ProfileUser.Comments == 0) + { +

There are no comments.

+ } + + @foreach (Comment comment in Model.Comments!) + { + DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000); +
+ @comment.Poster.Username: + @comment.Message +

+ @timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC +

+
+
+ } +
\ No newline at end of file diff --git a/ProjectLighthouse/Pages/UserPage.cshtml.cs b/ProjectLighthouse/Pages/UserPage.cshtml.cs new file mode 100644 index 00000000..0240ab59 --- /dev/null +++ b/ProjectLighthouse/Pages/UserPage.cshtml.cs @@ -0,0 +1,47 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Pages.Layouts; +using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Types.Profiles; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Pages +{ + public class UserPage : BaseLayout + { + public List? Comments; + + public bool IsProfileUserHearted; + + public List? Photos; + + public User? ProfileUser; + public UserPage(Database database) : base(database) + {} + + public async Task OnGet([FromRoute] int userId) + { + this.ProfileUser = await this.Database.Users.FirstOrDefaultAsync(u => u.UserId == userId); + if (this.ProfileUser == null) return this.NotFound(); + + this.Photos = await this.Database.Photos.OrderByDescending(p => p.Timestamp).Where(p => p.CreatorId == userId).Take(5).ToListAsync(); + this.Comments = await this.Database.Comments.Include + (p => p.Poster) + .Include(p => p.Target) + .OrderByDescending(p => p.Timestamp) + .Where(p => p.TargetUserId == userId) + .Take(50) + .ToListAsync(); + + if (this.User != null) + this.IsProfileUserHearted = await this.Database.HeartedProfiles.FirstOrDefaultAsync + (u => u.UserId == this.User.UserId && u.HeartedUserId == this.ProfileUser.UserId) != + null; + + return this.Page(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Program.cs b/ProjectLighthouse/Program.cs index 695e2470..934cb837 100644 --- a/ProjectLighthouse/Program.cs +++ b/ProjectLighthouse/Program.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Threading; using Kettu; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; @@ -17,6 +18,13 @@ namespace LBPUnion.ProjectLighthouse { public static void Main(string[] args) { + if (args.Length != 0 && args[0] == "--wait-for-debugger") + { + Console.WriteLine("Waiting for a debugger to be attached..."); + while (!Debugger.IsAttached) Thread.Sleep(100); + Console.WriteLine("Debugger attached."); + } + // Log startup time Stopwatch stopwatch = new(); stopwatch.Start(); @@ -24,12 +32,13 @@ namespace LBPUnion.ProjectLighthouse // Setup logging Logger.StartLogging(); + Logger.UpdateRate /= 2; LoggerLine.LogFormat = "[{0}] {1}"; Logger.AddLogger(new ConsoleLogger()); Logger.AddLogger(new LighthouseFileLogger()); Logger.Log("Welcome to Project Lighthouse!", LoggerLevelStartup.Instance); - Logger.Log($"Running {ServerStatics.ServerName} {GitVersionHelper.CommitHash}@{GitVersionHelper.Branch}", LoggerLevelStartup.Instance); + Logger.Log($"Running {VersionHelper.FullVersion}", LoggerLevelStartup.Instance); // This loads the config, see ServerSettings.cs for more information Logger.Log("Loaded config file version " + ServerSettings.Instance.ConfigVersion, LoggerLevelStartup.Instance); @@ -47,12 +56,27 @@ namespace LBPUnion.ProjectLighthouse if (ServerSettings.Instance.InfluxEnabled) { Logger.Log("Influx logging is enabled. Starting influx logging...", LoggerLevelStartup.Instance); - #pragma warning disable CS4014 - InfluxHelper.StartLogging(); - #pragma warning restore CS4014 + InfluxHelper.StartLogging().Wait(); if (ServerSettings.Instance.InfluxLoggingEnabled) Logger.AddLogger(new InfluxLogger()); } + #if DEBUG + Logger.Log + ( + "This is a debug build, so performance may suffer! " + + "If you are running Lighthouse in a production environment, " + + "it is highly recommended to run a release build. ", + LoggerLevelStartup.Instance + ); + Logger.Log("You can do so by running any dotnet command with the flag: \"-c Release\". ", LoggerLevelStartup.Instance); + #endif + + if (args.Length != 0) + { + MaintenanceHelper.RunCommand(args).Wait(); + return; + } + stopwatch.Stop(); Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance); @@ -64,7 +88,7 @@ namespace LBPUnion.ProjectLighthouse Stopwatch stopwatch = new(); stopwatch.Start(); - database.Database.Migrate(); + database.Database.MigrateAsync().Wait(); stopwatch.Stop(); Logger.Log($"Migration took {stopwatch.ElapsedMilliseconds}ms.", LoggerLevelDatabase.Instance); @@ -77,6 +101,7 @@ namespace LBPUnion.ProjectLighthouse webBuilder => { webBuilder.UseStartup(); + webBuilder.UseWebRoot("StaticFiles"); } ) .ConfigureLogging diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index b576a35c..0fef627e 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -9,10 +9,11 @@ - + + all @@ -32,6 +33,10 @@ + + <_ContentIncludedByDefault Remove="Pages\Admin\Index.cshtml"/> + + diff --git a/ProjectLighthouse/Startup.cs b/ProjectLighthouse/Startup.cs index 93e41f3d..fe68aea5 100644 --- a/ProjectLighthouse/Startup.cs +++ b/ProjectLighthouse/Startup.cs @@ -1,17 +1,17 @@ -using System; using System.Diagnostics; using System.IO; using Kettu; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Serialization; +using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Primitives; namespace LBPUnion.ProjectLighthouse @@ -29,6 +29,11 @@ namespace LBPUnion.ProjectLighthouse public void ConfigureServices(IServiceCollection services) { services.AddControllers(); + #if DEBUG + services.AddRazorPages().WithRazorPagesAtContentRoot().AddRazorRuntimeCompilation(); + #else + services.AddRazorPages().WithRazorPagesAtContentRoot(); + #endif services.AddMvc ( @@ -54,19 +59,21 @@ namespace LBPUnion.ProjectLighthouse public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env) { bool computeDigests = true; - string serverDigestKey = Environment.GetEnvironmentVariable("SERVER_DIGEST_KEY"); - if (string.IsNullOrWhiteSpace(serverDigestKey)) + string serverDigestKey = ServerSettings.Instance.ServerDigestKey; + if (string.IsNullOrEmpty(serverDigestKey)) { Logger.Log ( - "The SERVER_DIGEST_KEY environment variable wasn't set, so digest headers won't be set or verified. This will prevent LBP 1 and LBP 3 from working. " + + "The serverDigestKey configuration option wasn't set, so digest headers won't be set or verified. This will also prevent LBP 1, LBP 2, and LBP Vita from working. " + "To increase security, it is recommended that you find and set this variable.", LoggerLevelStartup.Instance ); computeDigests = false; } - if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); + #if DEBUG + app.UseDeveloperExceptionPage(); + #endif app.UseForwardedHeaders(); @@ -90,7 +97,7 @@ namespace LBPUnion.ProjectLighthouse string digestPath = context.Request.Path; Stream body = context.Request.Body; - if (computeDigests) + if (computeDigests && digestPath.StartsWith("/LITTLEBIGPLANETPS3_XML")) { string clientRequestDigest = await HashHelper.ComputeDigest(digestPath, authCookie, body, serverDigestKey); @@ -129,11 +136,23 @@ namespace LBPUnion.ProjectLighthouse // Copy the buffered response to the actual respose stream. responseBuffer.Position = 0; - await responseBuffer.CopyToAsync(oldResponseStream); - context.Response.Body = oldResponseStream; + #nullable enable + // Log LastContact for LBP1. This is done on LBP2/3/V on a Match request. + if (context.Request.Path.ToString().StartsWith("/LITTLEBIGPLANETPS3_XML")) + { + // We begin by grabbing a token from the request, if this is a LBPPS3_XML request of course. + await using Database database = new(); // Gets nuked at the end of the scope + GameToken? gameToken = await database.GameTokenFromRequest(context.Request); + + if (gameToken != null && gameToken.GameVersion == GameVersion.LittleBigPlanet1) + // Ignore UserFromGameToken null because user must exist for a token to exist + await LastContactHelper.SetLastContact((await database.UserFromGameToken(gameToken))!, GameVersion.LittleBigPlanet1); + } + #nullable disable + requestStopwatch.Stop(); Logger.Log @@ -152,7 +171,10 @@ namespace LBPUnion.ProjectLighthouse app.UseRouting(); + app.UseStaticFiles(); + app.UseEndpoints(endpoints => endpoints.MapControllers()); + app.UseEndpoints(endpoints => endpoints.MapRazorPages()); } } } \ No newline at end of file diff --git a/ProjectLighthouse/StaticFiles/android-chrome-192x192.png b/ProjectLighthouse/StaticFiles/android-chrome-192x192.png new file mode 100644 index 00000000..9b3eab52 Binary files /dev/null and b/ProjectLighthouse/StaticFiles/android-chrome-192x192.png differ diff --git a/ProjectLighthouse/StaticFiles/android-chrome-512x512.png b/ProjectLighthouse/StaticFiles/android-chrome-512x512.png new file mode 100644 index 00000000..09f9e579 Binary files /dev/null and b/ProjectLighthouse/StaticFiles/android-chrome-512x512.png differ diff --git a/ProjectLighthouse/StaticFiles/apple-touch-icon.png b/ProjectLighthouse/StaticFiles/apple-touch-icon.png new file mode 100644 index 00000000..e87773fa Binary files /dev/null and b/ProjectLighthouse/StaticFiles/apple-touch-icon.png differ diff --git a/ProjectLighthouse/StaticFiles/browserconfig.xml b/ProjectLighthouse/StaticFiles/browserconfig.xml new file mode 100644 index 00000000..b3930d0f --- /dev/null +++ b/ProjectLighthouse/StaticFiles/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/ProjectLighthouse/StaticFiles/css/styles.css b/ProjectLighthouse/StaticFiles/css/styles.css new file mode 100644 index 00000000..53cbfaa5 --- /dev/null +++ b/ProjectLighthouse/StaticFiles/css/styles.css @@ -0,0 +1,18 @@ +div.pageContainer { + display: flex; + flex-direction: column; + min-height: 100vh; +} + +div.main { + flex: 1; +} + +div.statsUnderTitle > i { + margin-right: 2px; +} + +div.statsUnderTitle > span { + margin-right: 5px; +} + diff --git a/ProjectLighthouse/StaticFiles/favicon-16x16.png b/ProjectLighthouse/StaticFiles/favicon-16x16.png new file mode 100644 index 00000000..e3ef0781 Binary files /dev/null and b/ProjectLighthouse/StaticFiles/favicon-16x16.png differ diff --git a/ProjectLighthouse/StaticFiles/favicon-32x32.png b/ProjectLighthouse/StaticFiles/favicon-32x32.png new file mode 100644 index 00000000..bc34a6c1 Binary files /dev/null and b/ProjectLighthouse/StaticFiles/favicon-32x32.png differ diff --git a/ProjectLighthouse/StaticFiles/favicon.ico b/ProjectLighthouse/StaticFiles/favicon.ico new file mode 100644 index 00000000..af563a7c Binary files /dev/null and b/ProjectLighthouse/StaticFiles/favicon.ico differ diff --git a/ProjectLighthouse/StaticFiles/mstile-150x150.png b/ProjectLighthouse/StaticFiles/mstile-150x150.png new file mode 100644 index 00000000..1825f73d Binary files /dev/null and b/ProjectLighthouse/StaticFiles/mstile-150x150.png differ diff --git a/ProjectLighthouse/StaticFiles/safari-pinned-tab.svg b/ProjectLighthouse/StaticFiles/safari-pinned-tab.svg new file mode 100644 index 00000000..51454694 --- /dev/null +++ b/ProjectLighthouse/StaticFiles/safari-pinned-tab.svg @@ -0,0 +1,223 @@ + + + + + Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + + + + + + + + + diff --git a/ProjectLighthouse/StaticFiles/site.webmanifest b/ProjectLighthouse/StaticFiles/site.webmanifest new file mode 100644 index 00000000..b20abb7c --- /dev/null +++ b/ProjectLighthouse/StaticFiles/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/ProjectLighthouse/Types/AuthenticationAttempt.cs b/ProjectLighthouse/Types/AuthenticationAttempt.cs new file mode 100644 index 00000000..57a10cd8 --- /dev/null +++ b/ProjectLighthouse/Types/AuthenticationAttempt.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace LBPUnion.ProjectLighthouse.Types +{ + public class AuthenticationAttempt + { + [Key] + public int AuthenticationAttemptId { get; set; } + + public long Timestamp { get; set; } + public Platform Platform { get; set; } + public string IPAddress { get; set; } + + public int GameTokenId { get; set; } + + [ForeignKey(nameof(GameTokenId))] + public GameToken GameToken { get; set; } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/DeletedBy.cs b/ProjectLighthouse/Types/DeletedBy.cs new file mode 100644 index 00000000..964b9e53 --- /dev/null +++ b/ProjectLighthouse/Types/DeletedBy.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace LBPUnion.ProjectLighthouse.Types +{ + [XmlRoot("deleted_by")] + public enum DeletedBy + { + [XmlEnum(Name = "none")] + None, + + [XmlEnum(Name = "moderator")] + Moderator, + + [XmlEnum(Name = "level_author")] + LevelAuthor, + // TODO: deletion types for comments (profile etc) + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Files/LbpFile.cs b/ProjectLighthouse/Types/Files/LbpFile.cs index b226276c..d0c98314 100644 --- a/ProjectLighthouse/Types/Files/LbpFile.cs +++ b/ProjectLighthouse/Types/Files/LbpFile.cs @@ -1,3 +1,5 @@ +#nullable enable +using System.IO; using LBPUnion.ProjectLighthouse.Helpers; namespace LBPUnion.ProjectLighthouse.Types.Files @@ -20,5 +22,15 @@ namespace LBPUnion.ProjectLighthouse.Types.Files this.Data = data; this.FileType = FileHelper.DetermineFileType(this.Data); } + + public static LbpFile? FromHash(string hash) + { + string path = FileHelper.GetResourcePath(hash); + if (!File.Exists(path)) return null; + + byte[] data = File.ReadAllBytes(path); + + return new LbpFile(data); + } } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Token.cs b/ProjectLighthouse/Types/GameToken.cs similarity index 83% rename from ProjectLighthouse/Types/Token.cs rename to ProjectLighthouse/Types/GameToken.cs index fc448c8c..4fdca27d 100644 --- a/ProjectLighthouse/Types/Token.cs +++ b/ProjectLighthouse/Types/GameToken.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; namespace LBPUnion.ProjectLighthouse.Types { - public class Token + public class GameToken { // ReSharper disable once UnusedMember.Global [Key] @@ -15,5 +15,7 @@ namespace LBPUnion.ProjectLighthouse.Types public string UserLocation { get; set; } public GameVersion GameVersion { get; set; } + + public bool Approved { get; set; } = false; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/GameVersion.cs b/ProjectLighthouse/Types/GameVersion.cs index c0530d9c..2ad35019 100644 --- a/ProjectLighthouse/Types/GameVersion.cs +++ b/ProjectLighthouse/Types/GameVersion.cs @@ -9,4 +9,9 @@ namespace LBPUnion.ProjectLighthouse.Types LittleBigPlanetPSP = 4, Unknown = -1, } + + public static class GameVersionExtensions + { + public static string ToPrettyString(this GameVersion gameVersion) => gameVersion.ToString().Replace("LittleBigPlanet", "LittleBigPlanet "); + } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Types/Levels/Slot.cs index 42353c32..faab5eee 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Types/Levels/Slot.cs @@ -192,9 +192,6 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels } } - [XmlElement("leveltype")] - public string LevelType { get; set; } = ""; - [NotMapped] [XmlElement("reviewCount")] public int ReviewCount { @@ -205,6 +202,9 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels } } + [XmlElement("leveltype")] + public string LevelType { get; set; } = ""; + public string SerializeResources() { return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)) + @@ -259,12 +259,13 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels LbpSerializer.StringElement("yourLBP1PlayCount", yourVisitedStats?.PlaysLBP1) + LbpSerializer.StringElement("yourLBP2PlayCount", yourVisitedStats?.PlaysLBP2) + LbpSerializer.StringElement("yourLBP3PlayCount", yourVisitedStats?.PlaysLBP3) + - LbpSerializer.StringElement("yourLBPVitaPlayCount", yourVisitedStats?.PlaysLBPVita) + // i doubt this is the right name but we'll go with it + LbpSerializer.StringElement + ("yourLBPVitaPlayCount", yourVisitedStats?.PlaysLBPVita) + // i doubt this is the right name but we'll go with it yourReview?.Serialize("yourReview") + LbpSerializer.StringElement("reviewsEnabled", true) + LbpSerializer.StringElement("commentsEnabled", false) + LbpSerializer.StringElement("reviewCount", this.ReviewCount); - + return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user"); } } diff --git a/ProjectLighthouse/Types/Match/CreateRoom.cs b/ProjectLighthouse/Types/Match/CreateRoom.cs index 1dc569c8..3edeb8a0 100644 --- a/ProjectLighthouse/Types/Match/CreateRoom.cs +++ b/ProjectLighthouse/Types/Match/CreateRoom.cs @@ -8,6 +8,17 @@ namespace LBPUnion.ProjectLighthouse.Types.Match [SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] public class CreateRoom : IMatchData { + public int BuildVersion; + public int HostMood; + public int Language; + public List Location; + + public List NAT; + public int PassedNoJoinPoint; + public RoomState RoomState; + + public string Search; + //[CreateRoom,["Players":["LumaLivy"],"Reservations":["0"],"NAT":[2],"Slots":[[1,3]],"RoomState":0,"HostMood":1,"PassedNoJoinPoint":0,"Location":[0x7f000001],"Language":1,"BuildVersion":289,"Search":""]] public List Players { get; set; } @@ -25,17 +36,8 @@ namespace LBPUnion.ProjectLighthouse.Types.Match public RoomSlot RoomSlot => new() { - SlotType = (SlotType)Slots[0][0], - SlotId = Slots[0][1], + SlotType = (SlotType)this.Slots[0][0], + SlotId = this.Slots[0][1], }; - - public List NAT; - public RoomState RoomState; - public int HostMood; - public int PassedNoJoinPoint; - public List Location; - public int Language; - public int BuildVersion; - public string Search; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/FindBestRoom.cs b/ProjectLighthouse/Types/Match/FindBestRoom.cs index 7e93f804..9b73b657 100644 --- a/ProjectLighthouse/Types/Match/FindBestRoom.cs +++ b/ProjectLighthouse/Types/Match/FindBestRoom.cs @@ -8,6 +8,15 @@ namespace LBPUnion.ProjectLighthouse.Types.Match [SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] public class FindBestRoom : IMatchData { + public int BuildVersion; + public int HostMood; + public int Language; + public List Location; + + public List NAT; + public int PassedNoJoinPoint; + public RoomState RoomState; + public string Search; public List Players { get; set; } public List Reservations { get; set; } @@ -15,14 +24,5 @@ namespace LBPUnion.ProjectLighthouse.Types.Match [JsonIgnore] public IEnumerable FirstSlot => this.Slots[0]; - - public List NAT; - public RoomState RoomState; - public int HostMood; - public int PassedNoJoinPoint; - public List Location; - public int Language; - public int BuildVersion; - public string Search; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/Player.cs b/ProjectLighthouse/Types/Match/Player.cs index 95c18ec6..a0d57ccc 100644 --- a/ProjectLighthouse/Types/Match/Player.cs +++ b/ProjectLighthouse/Types/Match/Player.cs @@ -11,7 +11,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Match public User User { get; set; } [SuppressMessage("ReSharper", "UnusedMember.Global")] - public string PlayerId => User.Username; + public string PlayerId => this.User.Username; [JsonPropertyName("matching_res")] public int MatchingRes { get; set; } diff --git a/ProjectLighthouse/Types/Match/Room.cs b/ProjectLighthouse/Types/Match/Room.cs index 4c6b93d5..18bb962f 100644 --- a/ProjectLighthouse/Types/Match/Room.cs +++ b/ProjectLighthouse/Types/Match/Room.cs @@ -6,13 +6,13 @@ namespace LBPUnion.ProjectLighthouse.Types.Match { public class Room { - public int RoomId; public List Players; - public RoomState State; + public int RoomId; public RoomSlot Slot; + public RoomState State; - public bool IsInPod => Slot.SlotType == SlotType.Pod; + public bool IsInPod => this.Slot.SlotType == SlotType.Pod; public bool IsLookingForPlayers => this.State == RoomState.DivingIntoLevel || this.State == RoomState.DivingInWaiting; public User Host => this.Players[0]; @@ -20,7 +20,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Match #nullable enable public override bool Equals(object? obj) { - if (obj is Room room) return room.RoomId == RoomId; + if (obj is Room room) return room.RoomId == this.RoomId; return false; } diff --git a/ProjectLighthouse/Types/Match/RoomSlot.cs b/ProjectLighthouse/Types/Match/RoomSlot.cs index 438fa763..e4609b93 100644 --- a/ProjectLighthouse/Types/Match/RoomSlot.cs +++ b/ProjectLighthouse/Types/Match/RoomSlot.cs @@ -4,7 +4,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Match { public class RoomSlot { - public SlotType SlotType; public int SlotId; + public SlotType SlotType; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Match/RoomState.cs b/ProjectLighthouse/Types/Match/RoomState.cs index 5d949ac0..c674a7fc 100644 --- a/ProjectLighthouse/Types/Match/RoomState.cs +++ b/ProjectLighthouse/Types/Match/RoomState.cs @@ -6,27 +6,27 @@ namespace LBPUnion.ProjectLighthouse.Types.Match public enum RoomState { /// - /// The room isn't doing anything in particular. + /// The room isn't doing anything in particular. /// Idle = 0, /// - /// The room is looking to join an existing room playing a specific slot. + /// The room is looking to join an existing room playing a specific slot. /// DivingIntoLevel = 1, /// - /// ??? + /// ??? /// Unknown = 2, /// - /// The room is looking for other rooms to join. + /// The room is looking for other rooms to join. /// DivingIn = 3, /// - /// The room is waiting for players to join their room. + /// The room is waiting for players to join their room. /// DivingInWaiting = 4, } diff --git a/ProjectLighthouse/Types/PageNavigationItem.cs b/ProjectLighthouse/Types/PageNavigationItem.cs new file mode 100644 index 00000000..b832fa70 --- /dev/null +++ b/ProjectLighthouse/Types/PageNavigationItem.cs @@ -0,0 +1,17 @@ +#nullable enable + +namespace LBPUnion.ProjectLighthouse.Types +{ + public class PageNavigationItem + { + public PageNavigationItem(string name, string url, string? icon = null) + { + this.Name = name; + this.Url = url; + this.Icon = icon; + } + public string Name { get; set; } + public string Url { get; set; } + public string? Icon { get; set; } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Photo.cs b/ProjectLighthouse/Types/Photo.cs index 317f0b09..d5c9b4f3 100644 --- a/ProjectLighthouse/Types/Photo.cs +++ b/ProjectLighthouse/Types/Photo.cs @@ -14,6 +14,15 @@ namespace LBPUnion.ProjectLighthouse.Types [XmlType("photo")] public class Photo { + + [NotMapped] + private List? _subjects; + + [NotMapped] + [XmlArray("subjects")] + [XmlArrayItem("subject")] + public List? SubjectsXmlDontUseLiterallyEver; + [Key] public int PhotoId { get; set; } @@ -33,14 +42,6 @@ namespace LBPUnion.ProjectLighthouse.Types [XmlElement("plan")] public string PlanHash { get; set; } = ""; - [NotMapped] - private List? _subjects; - - [NotMapped] - [XmlArray("subjects")] - [XmlArrayItem("subject")] - public List? SubjectsXmlDontUseLiterallyEver; - [NotMapped] public List Subjects { get { @@ -94,7 +95,7 @@ namespace LBPUnion.ProjectLighthouse.Types LbpSerializer.StringElement("subjects", subjectsAggregate) + slot; - return LbpSerializer.TaggedStringElement("photo", photo, "timestamp", Timestamp * 1000); + return LbpSerializer.TaggedStringElement("photo", photo, "timestamp", this.Timestamp * 1000); } } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/PhotoSubject.cs b/ProjectLighthouse/Types/PhotoSubject.cs index 04512010..99be0fb7 100644 --- a/ProjectLighthouse/Types/PhotoSubject.cs +++ b/ProjectLighthouse/Types/PhotoSubject.cs @@ -31,9 +31,9 @@ namespace LBPUnion.ProjectLighthouse.Types public string Serialize() { - string response = LbpSerializer.StringElement("npHandle", User.Username) + - LbpSerializer.StringElement("displayName", User.Username) + - LbpSerializer.StringElement("bounds", Bounds); + string response = LbpSerializer.StringElement("npHandle", this.User.Username) + + LbpSerializer.StringElement("displayName", this.User.Username) + + LbpSerializer.StringElement("bounds", this.Bounds); return LbpSerializer.StringElement("subject", response); } diff --git a/ProjectLighthouse/Types/Platform.cs b/ProjectLighthouse/Types/Platform.cs new file mode 100644 index 00000000..3711848a --- /dev/null +++ b/ProjectLighthouse/Types/Platform.cs @@ -0,0 +1,9 @@ +namespace LBPUnion.ProjectLighthouse.Types +{ + public enum Platform + { + PS3 = 0, + RPCS3 = 1, + Vita = 2, + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Types/Profiles/LastMatch.cs b/ProjectLighthouse/Types/Profiles/LastContact.cs similarity index 66% rename from ProjectLighthouse/Types/Profiles/LastMatch.cs rename to ProjectLighthouse/Types/Profiles/LastContact.cs index ec637c14..295250b8 100644 --- a/ProjectLighthouse/Types/Profiles/LastMatch.cs +++ b/ProjectLighthouse/Types/Profiles/LastContact.cs @@ -2,11 +2,13 @@ using System.ComponentModel.DataAnnotations; namespace LBPUnion.ProjectLighthouse.Types.Profiles { - public class LastMatch + public class LastContact { [Key] public int UserId { get; set; } public long Timestamp { get; set; } + + public GameVersion GameVersion { get; set; } = GameVersion.Unknown; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Reviews/Review.cs b/ProjectLighthouse/Types/Reviews/Review.cs index a2ee79d0..362d2e17 100644 --- a/ProjectLighthouse/Types/Reviews/Review.cs +++ b/ProjectLighthouse/Types/Reviews/Review.cs @@ -1,11 +1,11 @@ #nullable enable -using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using LBPUnion.ProjectLighthouse.Types.Levels; -using LBPUnion.ProjectLighthouse.Serialization; +using System.IO; +using System.Xml; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Serialization; +using LBPUnion.ProjectLighthouse.Types.Levels; namespace LBPUnion.ProjectLighthouse.Types.Reviews { @@ -43,51 +43,45 @@ namespace LBPUnion.ProjectLighthouse.Types.Reviews } [XmlElement("deleted")] - public Boolean Deleted { get; set; } + public bool Deleted { get; set; } [XmlElement("deleted_by")] - public string DeletedBy { get; set; } // enum ? Needs testing e.g. Moderated/Author/Level Author? etc. + public DeletedBy DeletedBy { get; set; } [XmlElement("text")] public string Text { get; set; } - [NotMapped] [XmlElement("thumb")] - public int Thumb { get; set; } // (unused) -- temp value for getting thumb from review upload body for updating level rating - - [NotMapped] + public int Thumb { get; set; } + [XmlElement("thumbsup")] - public int ThumbsUp { - get { - using Database database = new(); + public int ThumbsUp { get; set; } - return database.RatedReviews.Count(r => r.ReviewId == this.ReviewId && r.Thumb == 1); - } - } - [NotMapped] [XmlElement("thumbsdown")] - public int ThumbsDown { - get { - using Database database = new(); + public int ThumbsDown { get; set; } - return database.RatedReviews.Count(r => r.ReviewId == this.ReviewId && r.Thumb == -1); - } - } - - public string Serialize(RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) { - return this.Serialize("review", yourLevelRating, yourRatingStats); - } + public string Serialize + (RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) + => this.Serialize("review", yourLevelRating, yourRatingStats); public string Serialize(string elementOverride, RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) { + XmlWriterSettings settings = new(); + settings.OmitXmlDeclaration = true; + + XmlSerializer serializer = new(typeof(DeletedBy)); + StringWriter stringWriter = new(); + using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings)) serializer.Serialize(xmlWriter, this.DeletedBy); + string deletedBy = stringWriter.ToString(); + string reviewData = LbpSerializer.TaggedStringElement("slot_id", this.SlotId, "type", this.Slot.Type) + LbpSerializer.StringElement("reviewer", this.Reviewer.Username) + - LbpSerializer.StringElement("thumb", yourLevelRating?.Rating) + + LbpSerializer.StringElement("thumb", this.Thumb) + LbpSerializer.StringElement("timestamp", this.Timestamp) + LbpSerializer.StringElement("labels", this.LabelCollection) + LbpSerializer.StringElement("deleted", this.Deleted) + - LbpSerializer.StringElement("deleted_by", this.DeletedBy) + + deletedBy + LbpSerializer.StringElement("text", this.Text) + LbpSerializer.StringElement("thumbsup", this.ThumbsUp) + LbpSerializer.StringElement("thumbsdown", this.ThumbsDown) + @@ -97,5 +91,4 @@ namespace LBPUnion.ProjectLighthouse.Types.Reviews } } - } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ServerSettings.cs b/ProjectLighthouse/Types/Settings/ServerSettings.cs index 405e90db..ef1943e3 100644 --- a/ProjectLighthouse/Types/Settings/ServerSettings.cs +++ b/ProjectLighthouse/Types/Settings/ServerSettings.cs @@ -11,6 +11,8 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings [Serializable] public class ServerSettings { + + public const int CurrentConfigVersion = 12; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! static ServerSettings() { if (ServerStatics.IsUnitTesting) return; // Unit testing, we don't want to read configurations here since the tests will provide their own @@ -63,20 +65,6 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings } } - #region Meta - - [NotNull] - public static ServerSettings Instance; - - public const int CurrentConfigVersion = 4; - - [JsonPropertyName("ConfigVersionDoNotModifyOrYouWillBeSlapped")] - public int ConfigVersion { get; set; } = CurrentConfigVersion; - - public const string ConfigFileName = "lighthouse.config.json"; - - #endregion Meta - public bool InfluxEnabled { get; set; } public bool InfluxLoggingEnabled { get; set; } public string InfluxOrg { get; set; } = "lighthouse"; @@ -87,5 +75,39 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings public string EulaText { get; set; } = ""; public string DbConnectionString { get; set; } = "server=127.0.0.1;uid=root;pwd=lighthouse;database=lighthouse"; + + public string ExternalUrl { get; set; } = "http://localhost:10060"; + public string ServerDigestKey { get; set; } + public bool UseExternalAuth { get; set; } + + public bool CheckForUnsafeFiles { get; set; } = true; + + public bool RegistrationEnabled { get; set; } = true; + + /// + /// The maximum amount of slots allowed on users' earth + /// + public int EntitledSlots { get; set; } = 50; + + public int ListsQuota { get; set; } = 50; + + public int PhotosQuota { get; set; } = 500; + + public bool GoogleAnalyticsEnabled { get; set; } + + public string GoogleAnalyticsId { get; set; } = ""; + + #region Meta + + [NotNull] + public static ServerSettings Instance; + + [JsonPropertyName("ConfigVersionDoNotModifyOrYouWillBeSlapped")] + public int ConfigVersion { get; set; } = CurrentConfigVersion; + + public const string ConfigFileName = "lighthouse.config.json"; + + #endregion Meta + } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Settings/ServerStatics.cs b/ProjectLighthouse/Types/Settings/ServerStatics.cs index a6b29548..cea385e4 100644 --- a/ProjectLighthouse/Types/Settings/ServerStatics.cs +++ b/ProjectLighthouse/Types/Settings/ServerStatics.cs @@ -8,15 +8,10 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings { public static class ServerStatics { - /// - /// The maximum amount of slots allowed on users' earth - /// - public const int EntitledSlots = 50; - - public const int ListsQuota = 50; - public const string ServerName = "ProjectLighthouse"; + public const int PageSize = 20; + public static bool DbConnected { get { try diff --git a/ProjectLighthouse/Types/User.cs b/ProjectLighthouse/Types/User.cs index 9346fc87..726c706e 100644 --- a/ProjectLighthouse/Types/User.cs +++ b/ProjectLighthouse/Types/User.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics.CodeAnalysis; using System.Linq; +using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Settings; @@ -12,6 +13,7 @@ namespace LBPUnion.ProjectLighthouse.Types public readonly ClientsConnected ClientsConnected = new(); public int UserId { get; set; } public string Username { get; set; } + public string Password { get; set; } public string IconHash { get; set; } public int Game { get; set; } @@ -99,13 +101,38 @@ namespace LBPUnion.ProjectLighthouse.Types } } + public bool IsAdmin { get; set; } = false; + + public bool PasswordResetRequired { get; set; } + + public string YayHash { get; set; } = ""; + public string BooHash { get; set; } = ""; + public string MehHash { get; set; } = ""; + + #nullable enable + [NotMapped] + public string Status { + get { + using Database database = new(); + LastContact? lastMatch = database.LastContacts.Where + (l => l.UserId == this.UserId) + .FirstOrDefault(l => TimestampHelper.Timestamp - l.Timestamp < 300); + + if (lastMatch == null) return "Offline"; + + return "Currently online on " + lastMatch.GameVersion.ToPrettyString(); + } + } + #nullable disable + public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1) { string user = LbpSerializer.TaggedStringElement("npHandle", this.Username, "icon", this.IconHash) + LbpSerializer.StringElement("game", this.Game) + this.SerializeSlots(gameVersion == GameVersion.LittleBigPlanetVita) + LbpSerializer.StringElement("lists", this.Lists) + - LbpSerializer.StringElement("lists_quota", ServerStatics.ListsQuota) + // technically not a part of the user but LBP expects it + LbpSerializer.StringElement + ("lists_quota", ServerSettings.Instance.ListsQuota) + // technically not a part of the user but LBP expects it LbpSerializer.StringElement("biography", this.Biography) + LbpSerializer.StringElement("reviewCount", this.Reviews) + LbpSerializer.StringElement("commentCount", this.Comments) + @@ -119,8 +146,11 @@ namespace LBPUnion.ProjectLighthouse.Types LbpSerializer.StringElement("pins", this.Pins) + LbpSerializer.StringElement("planets", this.PlanetHash) + LbpSerializer.BlankElement("photos") + - LbpSerializer.StringElement("heartCount", this.Hearts) - + this.ClientsConnected.Serialize(); + LbpSerializer.StringElement("heartCount", this.Hearts) + + LbpSerializer.StringElement("yay2", this.YayHash) + + LbpSerializer.StringElement("boo2", this.YayHash) + + LbpSerializer.StringElement("meh2", this.YayHash); + this.ClientsConnected.Serialize(); return LbpSerializer.TaggedStringElement("user", user, "type", "user"); } @@ -147,7 +177,7 @@ namespace LBPUnion.ProjectLighthouse.Types /// /// The number of slots remaining on the earth /// - public int FreeSlots => ServerStatics.EntitledSlots - this.UsedSlots; + public int FreeSlots => ServerSettings.Instance.EntitledSlots - this.UsedSlots; private static readonly string[] slotTypes = { @@ -177,12 +207,12 @@ namespace LBPUnion.ProjectLighthouse.Types slotTypesLocal = slotTypes; } - slots += LbpSerializer.StringElement("entitledSlots", ServerStatics.EntitledSlots); + slots += LbpSerializer.StringElement("entitledSlots", ServerSettings.Instance.EntitledSlots); slots += LbpSerializer.StringElement("freeSlots", this.FreeSlots); foreach (string slotType in slotTypesLocal) { - slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerStatics.EntitledSlots); + slots += LbpSerializer.StringElement(slotType + "EntitledSlots", ServerSettings.Instance.EntitledSlots); // ReSharper disable once StringLiteralTypo slots += LbpSerializer.StringElement(slotType + slotType == "crossControl" ? "PurchsedSlots" : "PurchasedSlots", 0); slots += LbpSerializer.StringElement(slotType + "FreeSlots", this.FreeSlots); @@ -196,7 +226,7 @@ namespace LBPUnion.ProjectLighthouse.Types #nullable enable public override bool Equals(object? obj) { - if (obj is User user) return user.UserId == UserId; + if (obj is User user) return user.UserId == this.UserId; return false; } diff --git a/ProjectLighthouse/Types/WebToken.cs b/ProjectLighthouse/Types/WebToken.cs new file mode 100644 index 00000000..2bf8fe9c --- /dev/null +++ b/ProjectLighthouse/Types/WebToken.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace LBPUnion.ProjectLighthouse.Types +{ + public class WebToken + { + // ReSharper disable once UnusedMember.Global + [Key] + public int TokenId { get; set; } + + public int UserId { get; set; } + + public string UserToken { get; set; } + } +} \ No newline at end of file diff --git a/README.md b/README.md index f3ff44b4..e68ac31e 100644 --- a/README.md +++ b/README.md @@ -80,11 +80,15 @@ Keep in mind while running database tests you need to have `LIGHTHOUSE_DB_CONNEC ## Compatibility across games and platforms -| Game | Console (PS3/Vita) | Emulator (RPCS3) | Next-Gen (PS4/PS5) | -|----------|-------------------------------------|------------------------------------------------|--------------------| -| LBP1 | Compatible | Incompatible, crashes on entering pod computer | N/A | -| LBP2 | Compatible | Compatible with patched RPCS3 | N/A | -| LBP3 | Somewhat compatible | Somewhat compatible with workaround | Incompatible | -| LBP Vita | Compatible | N/A | N/A | +| Game | Console (PS3/Vita/PSP) | Emulator (RPCS3/Vita3k/PPSSPP) | Next-Gen (PS4/PS5) | +|----------|---------------------------------------|----------------------------------------------------------|------------------------| +| LBP1 | Compatible | Incompatible, crashes on entering pod computer | N/A | +| LBP2 | Compatible | Compatible with patched RPCS3 | N/A | +| LBP3 | Somewhat compatible, frequent crashes | Somewhat compatible with patched RPCS3, frequent crashes | Incompatible | +| LBP Vita | Compatible | Incompatible, marked as "bootable" on Vita3k | N/A | +| LBP PSP | Potentially compatible | Incompatible, PSN not supported on PPSSPP | Potentially Compatible | -Project Lighthouse is still a heavy work in progress, so this is subject to change at any point. +While LBP Vita and LBP PSP can be supported, they are not properly seperated from the mainline games at this time. We +recommend you run seperate instances for these games to avoid problems. + +Project Lighthouse is still a heavy work in progress, so this chart is subject to change at any point.