This commit is contained in:
LumaLivy 2021-11-22 19:44:22 -05:00
commit 6eefac7bd0
95 changed files with 3922 additions and 156 deletions

View file

@ -8,5 +8,12 @@
<jdbc-url>jdbc:mysql://localhost:3306/lighthouse</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
<data-source source="LOCAL" name="Lighthouse Production" uuid="b323608d-d984-40d0-942e-2c2ea35006d8">
<driver-ref>mariadb</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mariadb://localhost:3306</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{fomantic-ui, sha256}" />
</component>
</project>

View file

@ -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<HttpResponseMessage> AuthenticateResponse(int number = 0)
public async Task<HttpResponseMessage> 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}";

View file

@ -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]

View file

@ -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);

View file

@ -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<int> 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);

View file

@ -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()
{

View file

@ -78,7 +78,9 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MM/@EntryIndexedValue">MM</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NAT/@EntryIndexedValue">NAT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NP/@EntryIndexedValue">NP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PS/@EntryIndexedValue">PS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PSP/@EntryIndexedValue">PSP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RPCS/@EntryIndexedValue">RPCS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Method/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>

View file

@ -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,7 +66,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("deleteUserComment/{username}")]
public async Task<IActionResult> 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);

View file

@ -24,13 +24,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("play/user/{slotId}")]
public async Task<IActionResult> 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;
@ -94,7 +94,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("enterLevel/{id:int}")]
public async Task<IActionResult> 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);

View file

@ -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.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<IActionResult> Approve(int id)
{
User? user = this.database.UserFromWebRequest(this.Request);
if (user == null) return this.Redirect("/login");
AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include
(a => a.GameToken)
.FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id);
if (authAttempt == null) return this.NotFound();
if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, "");
authAttempt.GameToken.Approved = true;
this.database.AuthenticationAttempts.Remove(authAttempt);
await this.database.SaveChangesAsync();
return this.Redirect("~/authentication");
}
[HttpGet("deny/{id:int}")]
public async Task<IActionResult> Deny(int id)
{
User? user = this.database.UserFromWebRequest(this.Request);
if (user == null) return this.Redirect("/login");
AuthenticationAttempt? authAttempt = await this.database.AuthenticationAttempts.Include
(a => a.GameToken)
.FirstOrDefaultAsync(a => a.AuthenticationAttemptId == id);
if (authAttempt == null) return this.NotFound();
if (authAttempt.GameToken.UserId != user.UserId) return this.StatusCode(403, "");
this.database.GameTokens.Remove(authAttempt.GameToken);
this.database.AuthenticationAttempts.Remove(authAttempt);
DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}");
await this.database.SaveChangesAsync();
return this.Redirect("~/authentication");
}
[HttpGet("denyAll")]
public async Task<IActionResult> DenyAll()
{
User? user = this.database.UserFromWebRequest(this.Request);
if (user == null) return this.Redirect("/login");
List<AuthenticationAttempt> authAttempts = await this.database.AuthenticationAttempts.Include
(a => a.GameToken)
.Where(a => a.GameToken.UserId == user.UserId)
.ToListAsync();
foreach (AuthenticationAttempt authAttempt in authAttempts)
{
this.database.GameTokens.Remove(authAttempt.GameToken);
this.database.AuthenticationAttempts.Remove(authAttempt);
DeniedAuthenticationHelper.SetDeniedAt($"{authAttempt.IPAddress}|{user.Username}");
}
await this.database.SaveChangesAsync();
return this.Redirect("~/authentication");
}
}
}

View file

@ -26,7 +26,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("npdata")]
public async Task<IActionResult> 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,13 +69,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("myFriends")]
public async Task<IActionResult> 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)
{
@ -88,7 +88,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));

View file

@ -29,7 +29,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("slots/lolcatftw/{username}")]
public async Task<IActionResult> 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,7 +56,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("lolcatftw/add/user/{id:int}")]
public async Task<IActionResult> 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);
@ -79,7 +79,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("lolcatftw/remove/user/{id:int}")]
public async Task<IActionResult> 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);
@ -93,7 +93,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("lolcatftw/clear")]
public async Task<IActionResult> 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 +110,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("favouriteSlots/{username}")]
public async Task<IActionResult> 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,7 +137,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("favourite/slot/user/{id:int}")]
public async Task<IActionResult> 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);
@ -160,7 +160,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("unfavourite/slot/user/{id:int}")]
public async Task<IActionResult> 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);
@ -180,7 +180,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("favouriteUsers/{username}")]
public async Task<IActionResult> 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<HeartedProfile> heartedProfiles = this.database.HeartedProfiles.Include
@ -204,7 +204,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("favourite/user/{username}")]
public async Task<IActionResult> 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);
@ -231,7 +231,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("unfavourite/user/{username}")]
public async Task<IActionResult> 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);

View file

@ -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,12 +48,44 @@ 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

View file

@ -32,13 +32,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[Produces("text/plain")]
public async Task<IActionResult> 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
@ -97,7 +97,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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)
@ -108,7 +108,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
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();

View file

@ -27,10 +27,17 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("announce")]
public async Task<IActionResult> 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);
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
);
}
[HttpGet("notification")]
@ -42,7 +49,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("filter")]
public async Task<IActionResult> 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();

View file

@ -29,7 +29,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("uploadPhoto")]
public async Task<IActionResult> UploadPhoto()
{
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;
@ -120,7 +120,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("deletePhoto/{id:int}")]
public async Task<IActionResult> 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);

View file

@ -32,7 +32,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("startPublish")]
public async Task<IActionResult> StartPublish()
{
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.GetSlotFromBody();
@ -65,14 +65,14 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("publish")]
public async Task<IActionResult> 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;
Slot? slot = await this.GetSlotFromBody();
if (slot == null || slot.Location == null) return this.BadRequest();
@ -95,7 +95,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
slot.SlotId = oldSlot.SlotId;
slot.FirstUploaded = oldSlot.FirstUploaded;
slot.LastUpdated = TimeHelper.UnixTimeMilliseconds();
slot.GameVersion = token.GameVersion;
slot.GameVersion = gameToken.GameVersion;
this.database.Entry(oldSlot).CurrentValues.SetValues(slot);
await this.database.SaveChangesAsync();
@ -114,7 +114,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 +131,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("unpublish/{id:int}")]
public async Task<IActionResult> 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);

View file

@ -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)
{

View file

@ -31,7 +31,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("rate/user/{slotId}")]
public async Task<IActionResult> 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<IActionResult> 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);

View file

@ -28,13 +28,13 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("scoreboard/user/{id:int}")]
public async Task<IActionResult> SubmitScore(int id, [FromQuery] bool lbp1 = false, [FromQuery] bool lbp2 = false, [FromQuery] bool lbp3 = false)
{
(User, 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++;
@ -95,7 +95,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
public async Task<IActionResult> 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, "");

View file

@ -27,7 +27,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("slots/by")]
public async Task<IActionResult> 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;
@ -69,10 +69,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("s/user/{id:int}")]
public async Task<IActionResult> 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;
@ -97,7 +97,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("slots")]
public async Task<IActionResult> 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,7 +116,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("slots/mmpicks")]
public async Task<IActionResult> 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,7 +136,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("slots/lbp2luckydip")]
public async Task<IActionResult> 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;

View file

@ -36,7 +36,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("user/{username}")]
public async Task<IActionResult> 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);
@ -48,7 +48,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpGet("users")]
public async Task<IActionResult> 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<string?> serializedUsers = new();
@ -69,7 +69,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("updateUser")]
public async Task<IActionResult> 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()
@ -165,7 +165,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
[HttpPost("update_my_pins")]
public async Task<IActionResult> 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();

View file

@ -1,3 +1,4 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Kettu;
@ -22,7 +23,8 @@ namespace LBPUnion.ProjectLighthouse
public DbSet<HeartedLevel> HeartedLevels { get; set; }
public DbSet<HeartedProfile> HeartedProfiles { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Token> Tokens { get; set; }
public DbSet<GameToken> GameTokens { get; set; }
public DbSet<WebToken> WebTokens { get; set; }
public DbSet<Score> Scores { get; set; }
public DbSet<PhotoSubject> PhotoSubjects { get; set; }
public DbSet<Photo> Photos { get; set; }
@ -31,12 +33,15 @@ namespace LBPUnion.ProjectLighthouse
public DbSet<RatedLevel> RatedLevels { get; set; }
public DbSet<Review> Reviews { get; set; }
public DbSet<RatedReview> RatedReviews { get; set; }
public DbSet<AuthenticationAttempt> AuthenticationAttempts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseMySql(ServerSettings.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion);
public async Task<User> CreateUser(string username)
public async Task<User> CreateUser(string username, string password)
{
if (!password.StartsWith("$2a")) 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.",
};
@ -57,13 +63,14 @@ namespace LBPUnion.ProjectLighthouse
return user;
}
#nullable enable
public async Task<Token?> AuthenticateUser(LoginData loginData, string userLocation, string titleId = "")
#nullable enable
public async Task<GameToken?> 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,102 @@ 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<User?> UserFromAuthToken(string authToken)
#region Game Token Shenanigans
public async Task<User?> UserFromMMAuth(string authToken, bool allowUnapproved = false)
{
Token? token = await this.Tokens.FirstOrDefaultAsync(t => t.UserToken == authToken);
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
GameToken? token = await this.GameTokens.FirstOrDefaultAsync(t => t.UserToken == authToken);
if (token == null) return null;
if (!allowUnapproved && !token.Approved) return null;
return await this.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.UserId == token.UserId);
}
public async Task<User?> UserFromToken(Token token) => await this.UserFromAuthToken(token.UserToken);
public async Task<User?> UserFromGameToken
(GameToken gameToken, bool allowUnapproved = false)
=> await this.UserFromMMAuth(gameToken.UserToken, allowUnapproved);
public async Task<User?> UserFromRequest(HttpRequest request)
public async Task<User?> UserFromGameRequest(HttpRequest request, bool allowUnapproved = false)
{
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
return await this.UserFromAuthToken(mmAuth);
return await this.UserFromMMAuth(mmAuth, allowUnapproved);
}
public async Task<Token?> TokenFromRequest(HttpRequest request)
public async Task<GameToken?> GameTokenFromRequest(HttpRequest request, bool allowUnapproved = false)
{
if (ServerStatics.IsUnitTesting) allowUnapproved = true;
if (!request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return null;
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<Photo?> PhotoFromSubject(PhotoSubject subject)
=> await this.Photos.FirstOrDefaultAsync(p => p.PhotoSubjectIds.Contains(subject.PhotoSubjectId.ToString()));
#nullable disable
#nullable disable
}
}

View file

@ -0,0 +1,38 @@
using System.Collections.Generic;
namespace LBPUnion.ProjectLighthouse.Helpers
{
public static class DeniedAuthenticationHelper
{
public static readonly Dictionary<string, long> IPAddressAndNameDeniedAt = new();
public static readonly Dictionary<string, int> AttemptsByIPAddressAndName = new();
public static void SetDeniedAt(string ipAddressAndName, long timestamp = 0)
{
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;
}
}
}

View file

@ -2,6 +2,7 @@ using System;
using System.IO;
using Kettu;
using LBPUnion.ProjectLighthouse.Logging;
using LBPUnion.ProjectLighthouse.Types.Settings;
namespace LBPUnion.ProjectLighthouse.Helpers
{
@ -50,6 +51,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
public static string CommitHash { get; set; }
public static string Branch { get; set; }
public static string FullVersion => $"{ServerStatics.ServerName} {Branch}@{CommitHash}";
public static bool IsDirty => CommitHash.EndsWith("-dirty");
public static bool CanCheckForUpdates { get; set; }
}

View file

@ -139,7 +139,14 @@ 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

View file

@ -13,5 +13,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
public static async Task<int> SlotCount() => await database.Slots.CountAsync();
public static async Task<int> MMPicksCount() => await database.Slots.CountAsync(s => s.TeamPick);
public static async Task<int> PhotoCount() => await database.Photos.CountAsync();
}
}

View file

@ -154,7 +154,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Slots");
});
modelBuilder.Entity("ProjectLighthouse.Types.Token", b =>
modelBuilder.Entity("ProjectLighthouse.Types.GameToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -175,7 +175,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Slots");
});
modelBuilder.Entity("ProjectLighthouse.Types.Token", b =>
modelBuilder.Entity("ProjectLighthouse.Types.GameToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -189,7 +189,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Slots");
});
modelBuilder.Entity("ProjectLighthouse.Types.Token", b =>
modelBuilder.Entity("ProjectLighthouse.Types.GameToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -189,7 +189,7 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Slots");
});
modelBuilder.Entity("ProjectLighthouse.Types.Token", b =>
modelBuilder.Entity("ProjectLighthouse.Types.GameToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -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<int>("TokenId")
.ValueGeneratedOnAdd()

View file

@ -0,0 +1,654 @@
// <auto-generated />
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<int>("HeartedProfileId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("HeartedUserId")
.HasColumnType("int");
b.Property<int>("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<int>("HeartedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("QueuedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("RatedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("Rating")
.HasColumnType("int");
b.Property<double>("RatingLBP1")
.HasColumnType("double");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("SlotId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("AuthorLabels")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("BackgroundHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<long>("FirstUploaded")
.HasColumnType("bigint");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<string>("IconHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("InitiallyLocked")
.HasColumnType("tinyint(1)");
b.Property<long>("LastUpdated")
.HasColumnType("bigint");
b.Property<bool>("Lbp1Only")
.HasColumnType("tinyint(1)");
b.Property<string>("LevelType")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<int>("MaximumPlayers")
.HasColumnType("int");
b.Property<int>("MinimumPlayers")
.HasColumnType("int");
b.Property<bool>("MoveRequired")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("PlaysLBP1")
.HasColumnType("int");
b.Property<int>("PlaysLBP1Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP1Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBP2")
.HasColumnType("int");
b.Property<int>("PlaysLBP2Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP2Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBP3")
.HasColumnType("int");
b.Property<int>("PlaysLBP3Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP3Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBPVita")
.HasColumnType("int");
b.Property<int>("PlaysLBPVitaComplete")
.HasColumnType("int");
b.Property<int>("PlaysLBPVitaUnique")
.HasColumnType("int");
b.Property<string>("ResourceCollection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("RootLevel")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("Shareable")
.HasColumnType("int");
b.Property<bool>("SubLevel")
.HasColumnType("tinyint(1)");
b.Property<bool>("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<int>("VisitedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("PlaysLBP1")
.HasColumnType("int");
b.Property<int>("PlaysLBP2")
.HasColumnType("int");
b.Property<int>("PlaysLBP3")
.HasColumnType("int");
b.Property<int>("PlaysLBPVita")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("VisitedLevelId");
b.HasIndex("SlotId");
b.HasIndex("UserId");
b.ToTable("VisitedLevels");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b =>
{
b.Property<int>("PhotoId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("LargeHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("MediumHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("PhotoSubjectCollection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("PlanHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("SmallHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("PhotoId");
b.HasIndex("CreatorId");
b.ToTable("Photos");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b =>
{
b.Property<int>("PhotoSubjectId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Bounds")
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("PhotoSubjectId");
b.HasIndex("UserId");
b.ToTable("PhotoSubjects");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b =>
{
b.Property<int>("CommentId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Message")
.HasColumnType("longtext");
b.Property<int>("PosterUserId")
.HasColumnType("int");
b.Property<int>("TargetUserId")
.HasColumnType("int");
b.Property<int>("ThumbsDown")
.HasColumnType("int");
b.Property<int>("ThumbsUp")
.HasColumnType("int");
b.Property<long>("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<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("UserId");
b.ToTable("LastMatches");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("X")
.HasColumnType("int");
b.Property<int>("Y")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Locations");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b =>
{
b.Property<int>("ScoreId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("PlayerIdCollection")
.HasColumnType("longtext");
b.Property<int>("Points")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("ScoreId");
b.HasIndex("SlotId");
b.ToTable("Scores");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("UserLocation")
.HasColumnType("longtext");
b.Property<string>("UserToken")
.HasColumnType("longtext");
b.HasKey("TokenId");
b.ToTable("Tokens");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Biography")
.HasColumnType("longtext");
b.Property<int>("Game")
.HasColumnType("int");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<string>("Password")
.HasColumnType("longtext");
b.Property<string>("Pins")
.HasColumnType("longtext");
b.Property<string>("PlanetHash")
.HasColumnType("longtext");
b.Property<string>("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
}
}
}

View file

@ -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<string>(
name: "Password",
table: "Users",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Password",
table: "Users");
}
}
}

View file

@ -0,0 +1,654 @@
// <auto-generated />
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<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("UserLocation")
.HasColumnType("longtext");
b.Property<string>("UserToken")
.HasColumnType("longtext");
b.HasKey("TokenId");
b.ToTable("GameTokens");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b =>
{
b.Property<int>("HeartedProfileId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("HeartedUserId")
.HasColumnType("int");
b.Property<int>("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<int>("HeartedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("QueuedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("RatedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("Rating")
.HasColumnType("int");
b.Property<double>("RatingLBP1")
.HasColumnType("double");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("SlotId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("AuthorLabels")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("BackgroundHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<long>("FirstUploaded")
.HasColumnType("bigint");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<string>("IconHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("InitiallyLocked")
.HasColumnType("tinyint(1)");
b.Property<long>("LastUpdated")
.HasColumnType("bigint");
b.Property<bool>("Lbp1Only")
.HasColumnType("tinyint(1)");
b.Property<string>("LevelType")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<int>("MaximumPlayers")
.HasColumnType("int");
b.Property<int>("MinimumPlayers")
.HasColumnType("int");
b.Property<bool>("MoveRequired")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("PlaysLBP1")
.HasColumnType("int");
b.Property<int>("PlaysLBP1Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP1Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBP2")
.HasColumnType("int");
b.Property<int>("PlaysLBP2Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP2Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBP3")
.HasColumnType("int");
b.Property<int>("PlaysLBP3Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP3Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBPVita")
.HasColumnType("int");
b.Property<int>("PlaysLBPVitaComplete")
.HasColumnType("int");
b.Property<int>("PlaysLBPVitaUnique")
.HasColumnType("int");
b.Property<string>("ResourceCollection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("RootLevel")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("Shareable")
.HasColumnType("int");
b.Property<bool>("SubLevel")
.HasColumnType("tinyint(1)");
b.Property<bool>("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<int>("VisitedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("PlaysLBP1")
.HasColumnType("int");
b.Property<int>("PlaysLBP2")
.HasColumnType("int");
b.Property<int>("PlaysLBP3")
.HasColumnType("int");
b.Property<int>("PlaysLBPVita")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("VisitedLevelId");
b.HasIndex("SlotId");
b.HasIndex("UserId");
b.ToTable("VisitedLevels");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b =>
{
b.Property<int>("PhotoId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("LargeHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("MediumHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("PhotoSubjectCollection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("PlanHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("SmallHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("PhotoId");
b.HasIndex("CreatorId");
b.ToTable("Photos");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b =>
{
b.Property<int>("PhotoSubjectId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Bounds")
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("PhotoSubjectId");
b.HasIndex("UserId");
b.ToTable("PhotoSubjects");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b =>
{
b.Property<int>("CommentId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Message")
.HasColumnType("longtext");
b.Property<int>("PosterUserId")
.HasColumnType("int");
b.Property<int>("TargetUserId")
.HasColumnType("int");
b.Property<int>("ThumbsDown")
.HasColumnType("int");
b.Property<int>("ThumbsUp")
.HasColumnType("int");
b.Property<long>("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<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("UserId");
b.ToTable("LastMatches");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("X")
.HasColumnType("int");
b.Property<int>("Y")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Locations");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b =>
{
b.Property<int>("ScoreId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("PlayerIdCollection")
.HasColumnType("longtext");
b.Property<int>("Points")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("ScoreId");
b.HasIndex("SlotId");
b.ToTable("Scores");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Biography")
.HasColumnType("longtext");
b.Property<int>("Game")
.HasColumnType("int");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<string>("Password")
.HasColumnType("longtext");
b.Property<string>("Pins")
.HasColumnType("longtext");
b.Property<string>("PlanetHash")
.HasColumnType("longtext");
b.Property<string>("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
}
}
}

View file

@ -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");
}
}
}

View file

@ -0,0 +1,671 @@
// <auto-generated />
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<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("UserLocation")
.HasColumnType("longtext");
b.Property<string>("UserToken")
.HasColumnType("longtext");
b.HasKey("TokenId");
b.ToTable("GameTokens");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b =>
{
b.Property<int>("HeartedProfileId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("HeartedUserId")
.HasColumnType("int");
b.Property<int>("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<int>("HeartedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("QueuedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("RatedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("Rating")
.HasColumnType("int");
b.Property<double>("RatingLBP1")
.HasColumnType("double");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("SlotId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("AuthorLabels")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("BackgroundHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<long>("FirstUploaded")
.HasColumnType("bigint");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<string>("IconHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("InitiallyLocked")
.HasColumnType("tinyint(1)");
b.Property<long>("LastUpdated")
.HasColumnType("bigint");
b.Property<bool>("Lbp1Only")
.HasColumnType("tinyint(1)");
b.Property<string>("LevelType")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<int>("MaximumPlayers")
.HasColumnType("int");
b.Property<int>("MinimumPlayers")
.HasColumnType("int");
b.Property<bool>("MoveRequired")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("PlaysLBP1")
.HasColumnType("int");
b.Property<int>("PlaysLBP1Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP1Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBP2")
.HasColumnType("int");
b.Property<int>("PlaysLBP2Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP2Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBP3")
.HasColumnType("int");
b.Property<int>("PlaysLBP3Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP3Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBPVita")
.HasColumnType("int");
b.Property<int>("PlaysLBPVitaComplete")
.HasColumnType("int");
b.Property<int>("PlaysLBPVitaUnique")
.HasColumnType("int");
b.Property<string>("ResourceCollection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("RootLevel")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("Shareable")
.HasColumnType("int");
b.Property<bool>("SubLevel")
.HasColumnType("tinyint(1)");
b.Property<bool>("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<int>("VisitedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("PlaysLBP1")
.HasColumnType("int");
b.Property<int>("PlaysLBP2")
.HasColumnType("int");
b.Property<int>("PlaysLBP3")
.HasColumnType("int");
b.Property<int>("PlaysLBPVita")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("VisitedLevelId");
b.HasIndex("SlotId");
b.HasIndex("UserId");
b.ToTable("VisitedLevels");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b =>
{
b.Property<int>("PhotoId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("LargeHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("MediumHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("PhotoSubjectCollection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("PlanHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("SmallHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("PhotoId");
b.HasIndex("CreatorId");
b.ToTable("Photos");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b =>
{
b.Property<int>("PhotoSubjectId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Bounds")
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("PhotoSubjectId");
b.HasIndex("UserId");
b.ToTable("PhotoSubjects");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b =>
{
b.Property<int>("CommentId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Message")
.HasColumnType("longtext");
b.Property<int>("PosterUserId")
.HasColumnType("int");
b.Property<int>("TargetUserId")
.HasColumnType("int");
b.Property<int>("ThumbsDown")
.HasColumnType("int");
b.Property<int>("ThumbsUp")
.HasColumnType("int");
b.Property<long>("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<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("UserId");
b.ToTable("LastMatches");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("X")
.HasColumnType("int");
b.Property<int>("Y")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Locations");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b =>
{
b.Property<int>("ScoreId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("PlayerIdCollection")
.HasColumnType("longtext");
b.Property<int>("Points")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("ScoreId");
b.HasIndex("SlotId");
b.ToTable("Scores");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Biography")
.HasColumnType("longtext");
b.Property<int>("Game")
.HasColumnType("int");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<string>("Password")
.HasColumnType("longtext");
b.Property<string>("Pins")
.HasColumnType("longtext");
b.Property<string>("PlanetHash")
.HasColumnType("longtext");
b.Property<string>("Username")
.HasColumnType("longtext");
b.HasKey("UserId");
b.HasIndex("LocationId");
b.ToTable("Users");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("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
}
}
}

View file

@ -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<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<int>(type: "int", nullable: false),
UserToken = table.Column<string>(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");
}
}
}

View file

@ -0,0 +1,710 @@
// <auto-generated />
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<int>("AuthenticationAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("GameTokenId")
.HasColumnType("int");
b.Property<string>("IPAddress")
.HasColumnType("longtext");
b.Property<int>("Platform")
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("AuthenticationAttemptId");
b.HasIndex("GameTokenId");
b.ToTable("AuthenticationAttempts");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<bool>("Approved")
.HasColumnType("tinyint(1)");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("UserLocation")
.HasColumnType("longtext");
b.Property<string>("UserToken")
.HasColumnType("longtext");
b.HasKey("TokenId");
b.ToTable("GameTokens");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b =>
{
b.Property<int>("HeartedProfileId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("HeartedUserId")
.HasColumnType("int");
b.Property<int>("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<int>("HeartedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("QueuedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("RatedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("Rating")
.HasColumnType("int");
b.Property<double>("RatingLBP1")
.HasColumnType("double");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("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<int>("SlotId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("AuthorLabels")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("BackgroundHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<long>("FirstUploaded")
.HasColumnType("bigint");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<string>("IconHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("InitiallyLocked")
.HasColumnType("tinyint(1)");
b.Property<long>("LastUpdated")
.HasColumnType("bigint");
b.Property<bool>("Lbp1Only")
.HasColumnType("tinyint(1)");
b.Property<string>("LevelType")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<int>("MaximumPlayers")
.HasColumnType("int");
b.Property<int>("MinimumPlayers")
.HasColumnType("int");
b.Property<bool>("MoveRequired")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("PlaysLBP1")
.HasColumnType("int");
b.Property<int>("PlaysLBP1Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP1Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBP2")
.HasColumnType("int");
b.Property<int>("PlaysLBP2Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP2Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBP3")
.HasColumnType("int");
b.Property<int>("PlaysLBP3Complete")
.HasColumnType("int");
b.Property<int>("PlaysLBP3Unique")
.HasColumnType("int");
b.Property<int>("PlaysLBPVita")
.HasColumnType("int");
b.Property<int>("PlaysLBPVitaComplete")
.HasColumnType("int");
b.Property<int>("PlaysLBPVitaUnique")
.HasColumnType("int");
b.Property<string>("ResourceCollection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("RootLevel")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("Shareable")
.HasColumnType("int");
b.Property<bool>("SubLevel")
.HasColumnType("tinyint(1)");
b.Property<bool>("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<int>("VisitedLevelId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("PlaysLBP1")
.HasColumnType("int");
b.Property<int>("PlaysLBP2")
.HasColumnType("int");
b.Property<int>("PlaysLBP3")
.HasColumnType("int");
b.Property<int>("PlaysLBPVita")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("VisitedLevelId");
b.HasIndex("SlotId");
b.HasIndex("UserId");
b.ToTable("VisitedLevels");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b =>
{
b.Property<int>("PhotoId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("LargeHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("MediumHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("PhotoSubjectCollection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("PlanHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("SmallHash")
.IsRequired()
.HasColumnType("longtext");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("PhotoId");
b.HasIndex("CreatorId");
b.ToTable("Photos");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b =>
{
b.Property<int>("PhotoSubjectId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Bounds")
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("PhotoSubjectId");
b.HasIndex("UserId");
b.ToTable("PhotoSubjects");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b =>
{
b.Property<int>("CommentId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Message")
.HasColumnType("longtext");
b.Property<int>("PosterUserId")
.HasColumnType("int");
b.Property<int>("TargetUserId")
.HasColumnType("int");
b.Property<int>("ThumbsDown")
.HasColumnType("int");
b.Property<int>("ThumbsUp")
.HasColumnType("int");
b.Property<long>("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<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("UserId");
b.ToTable("LastMatches");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("X")
.HasColumnType("int");
b.Property<int>("Y")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Locations");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b =>
{
b.Property<int>("ScoreId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("PlayerIdCollection")
.HasColumnType("longtext");
b.Property<int>("Points")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("ScoreId");
b.HasIndex("SlotId");
b.ToTable("Scores");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Biography")
.HasColumnType("longtext");
b.Property<int>("Game")
.HasColumnType("int");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<string>("Password")
.HasColumnType("longtext");
b.Property<string>("Pins")
.HasColumnType("longtext");
b.Property<string>("PlanetHash")
.HasColumnType("longtext");
b.Property<string>("Username")
.HasColumnType("longtext");
b.HasKey("UserId");
b.HasIndex("LocationId");
b.ToTable("Users");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("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
}
}
}

View file

@ -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<bool>(
name: "Approved",
table: "GameTokens",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
migrationBuilder.CreateTable(
name: "AuthenticationAttempts",
columns: table => new
{
AuthenticationAttemptId = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Timestamp = table.Column<long>(type: "bigint", nullable: false),
Platform = table.Column<int>(type: "int", nullable: false),
IPAddress = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
GameTokenId = table.Column<int>(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");
}
}
}

View file

@ -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<int>("AuthenticationAttemptId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("GameTokenId")
.HasColumnType("int");
b.Property<string>("IPAddress")
.HasColumnType("longtext");
b.Property<int>("Platform")
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("AuthenticationAttemptId");
b.HasIndex("GameTokenId");
b.ToTable("AuthenticationAttempts");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<bool>("Approved")
.HasColumnType("tinyint(1)");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("UserLocation")
.HasColumnType("longtext");
b.Property<string>("UserToken")
.HasColumnType("longtext");
b.HasKey("TokenId");
b.ToTable("GameTokens");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b =>
{
b.Property<int>("HeartedProfileId")
@ -473,29 +524,6 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Scores");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("UserLocation")
.HasColumnType("longtext");
b.Property<string>("UserToken")
.HasColumnType("longtext");
b.HasKey("TokenId");
b.ToTable("Tokens");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b =>
{
b.Property<int>("UserId")
@ -514,6 +542,9 @@ namespace ProjectLighthouse.Migrations
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<string>("Password")
.HasColumnType("longtext");
b.Property<string>("Pins")
.HasColumnType("longtext");
@ -530,6 +561,34 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Users");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b =>
{
b.Property<int>("TokenId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.Property<string>("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")

View file

@ -0,0 +1,36 @@
@page "/authentication"
@using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Pages.ExternalAuth.AuthenticationPage
@{
Layout = "Layouts/BaseLayout";
}
<h1>Authentication</h1>
@if (Model.AuthenticationAttempts.Count == 0)
{
<p>You have no pending authentication attempts.</p>
}
else
{
<p>You have @Model.AuthenticationAttempts.Count authentication attempts pending.</p>
<a href="/authentication/denyAll">
<button class="ui small red button">Deny all</button>
</a>
}
@foreach (AuthenticationAttempt authAttempt in Model.AuthenticationAttempts)
{
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(authAttempt.Timestamp);
<div class="ui red segment">
<p>A <b>@authAttempt.Platform</b> authentication request was logged at <b>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</b> from the IP address <b>@authAttempt.IPAddress</b>.</p>
<div>
<a href="/authentication/approve/@authAttempt.AuthenticationAttemptId">
<button class="ui tiny green button">Approve</button>
</a>
<a href="/authentication/deny/@authAttempt.AuthenticationAttemptId">
<button class="ui tiny red button">Deny</button>
</a>
</div>
</div>
}

View file

@ -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 AuthenticationPage(Database database) : base(database)
{}
public List<AuthenticationAttempt> AuthenticationAttempts;
public async Task<IActionResult> 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();
}
}
}

View file

@ -0,0 +1,25 @@
@page "/"
@model LBPUnion.ProjectLighthouse.Pages.LandingPage
@{
Layout = "Layouts/BaseLayout";
}
<h1>Welcome to <b>Project Lighthouse</b>!</h1>
@if (Model.User != null)
{
<p>You are currently logged in as <b>@Model.User.Username</b>.</p>
}
@if (Model.PlayersOnline == 1)
{
<p>There is 1 user currently online.</p>
}
else if (Model.PlayersOnline == 0)
{
<p>There are no users online. Why not hop on?</p>
}
else
{
<p>There are currently @Model.PlayersOnline users online.</p>
}

View file

@ -0,0 +1,24 @@
#nullable enable
using System.Threading.Tasks;
using JetBrains.Annotations;
using LBPUnion.ProjectLighthouse.Helpers;
using LBPUnion.ProjectLighthouse.Pages.Layouts;
using Microsoft.AspNetCore.Mvc;
namespace LBPUnion.ProjectLighthouse.Pages
{
public class LandingPage : BaseLayout
{
public LandingPage(Database database) : base(database)
{}
public int PlayersOnline;
[UsedImplicitly]
public async Task<IActionResult> OnGet()
{
this.PlayersOnline = await StatisticsHelper.RecentMatches();
return this.Page();
}
}
}

View file

@ -0,0 +1,64 @@
@using LBPUnion.ProjectLighthouse.Helpers
@using LBPUnion.ProjectLighthouse.Types
@using LBPUnion.ProjectLighthouse.Types.Settings
@model LBPUnion.ProjectLighthouse.Pages.Layouts.BaseLayout
@{
if (Model!.User == null)
{
Model.NavigationItems.Add(new PageNavigationItem("Log in", "/login", "user alternate"));
Model.NavigationItems.Add(new PageNavigationItem("Register", "/register", "user alternate edit"));
}
else
{
if (ServerSettings.Instance.UseExternalAuth)
{
Model.NavigationItems.Add(new PageNavigationItem("Authentication", "/authentication", "key"));
}
Model.NavigationItems.Add(new PageNavigationItem("Log out", "/logout", "user alternate slash")); // should always be last
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Project Lighthouse</title>
<link rel="stylesheet" type="text/css" href="~/css/styles.css">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.8/dist/semantic.min.css">
</head>
<header class="lighthouse-header">
<div class="ui attached menu">
<div class="ui container">
@foreach (PageNavigationItem navigationItem in Model!.NavigationItems)
{
<a class="item" href="@navigationItem.Url">
@if (navigationItem.Icon != null)
{
<i class="@navigationItem.Icon icon"></i>
}
@navigationItem.Name
</a>
}
</div>
</div>
</header>
<body>
<div class="ui container">
<br>
@RenderBody()
</div>
</body>
<footer class="lighthouse-footer">
<div class="ui black attached inverted segment">
<div class="ui container">
<p>Page generated by @GitVersionHelper.FullVersion.</p>
@if (GitVersionHelper.IsDirty)
{
<p>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.</p>
}
</div>
</div>
</footer>
</html>

View file

@ -0,0 +1,35 @@
#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;
private User? user;
public new User? User {
get {
if (this.user != null) return this.user;
return user = Database.UserFromWebRequest(this.Request);
}
set => this.user = value;
}
public BaseLayout(Database database)
{
this.Database = database;
}
public readonly List<PageNavigationItem> NavigationItems = new()
{
new PageNavigationItem("Home", "/", "home"),
new PageNavigationItem("Photos", "/photos/0", "camera"),
};
}
}

View file

@ -0,0 +1,33 @@
@page "/login"
@model LBPUnion.ProjectLighthouse.Pages.LoginForm
@{
Layout = "Layouts/BaseLayout";
}
<script src="https://geraintluff.github.io/sha256/sha256.min.js"></script>
<script>
function onSubmit(form) {
const password = form['password'];
password.value = sha256(password.value);
return true;
}
</script>
<h1>Log in</h1>
<form onsubmit="return onSubmit(this)">
<div class="ui left labeled input">
<label for="text" class="ui label">Username: </label>
<input type="text" name="username" id="text">
</div><br>
<div class="ui left labeled input">
<label for="password" class="ui label">Password: </label>
<input type="password" name="password" id="password">
</div><br><br>
<input type="submit" value="Log in" id="submit" class="ui button"><br>
</form>

View file

@ -0,0 +1,48 @@
#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 bool WasLoginRequest { get; private set; }
[UsedImplicitly]
public async Task<IActionResult> OnGet([FromQuery] string username, [FromQuery] string password)
{
this.WasLoginRequest = !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password);
if (this.WasLoginRequest)
{
User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username);
if (user == null) return this.StatusCode(403, "");
if (!BCrypt.Net.BCrypt.Verify(password, user.Password)) return this.StatusCode(403, "");
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));
}
return this.Page();
}
}
}

View file

@ -0,0 +1,9 @@
@page "/logout"
@model LBPUnion.ProjectLighthouse.Pages.LogoutPage
@{
Layout = "Layouts/BaseLayout";
}
<p>You have been successfully logged out. You will be redirected in 5 seconds, or you may click <a href="/">here</a> to do so manually.</p>
<meta http-equiv="refresh" content="5; url=/"/>

View file

@ -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<IActionResult> 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();
}
}
}

View file

@ -0,0 +1,43 @@
@page "/photos/{pageNumber:int}"
@using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Pages.PhotosPage
@{
Layout = "Layouts/BaseLayout";
}
<h1>Photos</h1>
<p>There are @Model.PhotoCount total photos!</p>
@foreach (Photo photo in Model.Photos)
{
<div class="ui segment">
<img src="/gameAssets/@photo.LargeHash" style="width: 100%; height: auto; border-radius: .28571429rem;">
<br>
<p>
<i>
Taken by
<b>
<a href="/users/@photo.Creator!.UserId">@photo.Creator.Username</a>
</b>
</i>
</p>
<p>
<b>Photo contains @photo.Subjects.Count @(photo.Subjects.Count == 1 ? "person" : "people"):</b>
</p>
@foreach (PhotoSubject subject in photo.Subjects)
{
<a href="/users/@subject.UserId">@subject.User.Username</a>
}
</div>
}
@if (Model.PageNumber != 0)
{
<a href="/photos/@(Model.PageNumber - 1)">Previous Page</a>
}
<a href="/photos/@(Model.PageNumber + 1)">Next Page</a>
<div style="height: 100px; width: 1px;"></div> @* solves quirk with footer *@

View file

@ -0,0 +1,41 @@
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 PhotosPage : BaseLayout
{
public PhotosPage([NotNull] Database database) : base(database)
{}
public int PhotoCount;
public List<Photo> Photos;
public int PageNumber;
public async Task<IActionResult> OnGet([FromRoute] int pageNumber)
{
const int pageSize = 20;
this.PhotoCount = await StatisticsHelper.PhotoCount();
this.PageNumber = pageNumber;
this.Photos = await this.Database.Photos.Include
(p => p.Creator)
.OrderByDescending(p => p.Timestamp)
.Skip(pageNumber * pageSize)
.Take(pageSize)
.ToListAsync();
return this.Page();
}
}
}

View file

@ -0,0 +1,38 @@
@page "/register"
@model LBPUnion.ProjectLighthouse.Pages.RegisterForm
@{
Layout = "Layouts/BaseLayout";
}
<script src="https://geraintluff.github.io/sha256/sha256.min.js"></script>
<script>
function onSubmit(form) {
const password = form['password'];
const confirmPassword = form['confirmPassword'];
password.value = sha256(password.value);
confirmPassword.value = sha256(confirmPassword.value);
return true;
}
</script>
<h1>Register</h1>
<form onsubmit="return onSubmit(this)">
<div class="ui left labeled input">
<label for="text" class="ui label">Username: </label>
<input type="text" name="username" id="text">
</div><br>
<div class="ui left labeled input">
<label for="password" class="ui label">Password: </label>
<input type="password" name="password" id="password">
</div><br>
<div class="ui left labeled input">
<label for="password" class="ui label">Confirm Password: </label>
<input type="password" name="confirmPassword" id="confirmPassword">
</div><br><br>
<input type="submit" value="Register" id="submit" class="ui button"><br>
</form>

View file

@ -0,0 +1,51 @@
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 Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Pages
{
public class RegisterForm : BaseLayout
{
public RegisterForm(Database database) : base(database)
{}
public bool WasRegisterRequest { get; private set; }
[UsedImplicitly]
[SuppressMessage("ReSharper", "SpecifyStringComparison")]
public async Task<IActionResult> OnGet([FromQuery] string username, [FromQuery] string password, [FromQuery] string confirmPassword)
{
this.WasRegisterRequest = !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password) && !string.IsNullOrEmpty(confirmPassword);
if (this.WasRegisterRequest)
{
if (password != confirmPassword) return this.BadRequest();
bool userExists = await this.Database.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()) != null;
if (userExists) return this.BadRequest();
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));
}
return this.Page();
}
}
}

View file

@ -29,7 +29,7 @@ namespace LBPUnion.ProjectLighthouse
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 {GitVersionHelper.FullVersion}", LoggerLevelStartup.Instance);
// This loads the config, see ServerSettings.cs for more information
Logger.Log("Loaded config file version " + ServerSettings.Instance.ConfigVersion, LoggerLevelStartup.Instance);
@ -53,6 +53,17 @@ namespace LBPUnion.ProjectLighthouse
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
stopwatch.Stop();
Logger.Log($"Ready! Startup took {stopwatch.ElapsedMilliseconds}ms. Passing off control to ASP.NET...", LoggerLevelStartup.Instance);
@ -77,6 +88,7 @@ namespace LBPUnion.ProjectLighthouse
webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseWebRoot("StaticFiles");
}
)
.ConfigureLogging

View file

@ -13,6 +13,7 @@
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0"/>
<PackageReference Include="Kettu" Version="1.2.1"/>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="6.0.0"/>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>

View file

@ -1,17 +1,16 @@
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.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 +28,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 +58,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 +96,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);
@ -152,7 +158,10 @@ namespace LBPUnion.ProjectLighthouse
app.UseRouting();
app.UseStaticFiles();
app.UseEndpoints(endpoints => endpoints.MapControllers());
app.UseEndpoints(endpoints => endpoints.MapRazorPages());
}
}
}

View file

@ -0,0 +1,5 @@
footer.lighthouse-footer {
width: 100%;
bottom: 0;
position: fixed;
}

View file

@ -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; }
}
}

View file

@ -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;
}
}

View file

@ -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; }
}
}

View file

@ -0,0 +1,9 @@
namespace LBPUnion.ProjectLighthouse.Types
{
public enum Platform
{
PS3 = 0,
RPCS3 = 1,
Vita = 2,
}
}

View file

@ -68,7 +68,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings
[NotNull]
public static ServerSettings Instance;
public const int CurrentConfigVersion = 4;
public const int CurrentConfigVersion = 7;
[JsonPropertyName("ConfigVersionDoNotModifyOrYouWillBeSlapped")]
public int ConfigVersion { get; set; } = CurrentConfigVersion;
@ -87,5 +87,9 @@ 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; }
}
}

View file

@ -12,6 +12,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; }

View file

@ -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; }
}
}