diff --git a/.gitignore b/.gitignore index 15bd2de8..bb08bcee 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ +# LBP resources. +r/ + +# rider +.idea/ + r/ # cdn resources folder bin/ obj/ @@ -9,11 +15,9 @@ riderModule.iml /.idea/.idea.ProjectLighthouse/.idea/dataSources.local.xml *.sln.DotSettings.user /ProjectLighthouse/r/* -logs +/ProjectLighthouse/logs/* /ProjectLighthouse/ProjectLighthouse.csproj.user .vs/ -.vscode/ -.editorconfig lighthouse.config.json gitBranch.txt gitVersion.txt diff --git a/ProjectLighthouse/Controllers/EnterLevelController.cs b/ProjectLighthouse/Controllers/EnterLevelController.cs index 3cd45500..ba24c642 100644 --- a/ProjectLighthouse/Controllers/EnterLevelController.cs +++ b/ProjectLighthouse/Controllers/EnterLevelController.cs @@ -62,8 +62,11 @@ namespace LBPUnion.ProjectLighthouse.Controllers { v = await visited.FirstOrDefaultAsync(); } - - if (v == null) return this.NotFound(); + + if (v == null) + { + return this.NotFound(); + } switch (gameVersion) { @@ -116,7 +119,10 @@ namespace LBPUnion.ProjectLighthouse.Controllers v = await visited.FirstOrDefaultAsync(); } - if (v == null) return this.NotFound(); + if (v == null) + { + return this.NotFound(); + } slot.PlaysLBP1++; v.PlaysLBP1++; diff --git a/ProjectLighthouse/Controllers/MatchController.cs b/ProjectLighthouse/Controllers/MatchController.cs index 4315628e..b06a1218 100644 --- a/ProjectLighthouse/Controllers/MatchController.cs +++ b/ProjectLighthouse/Controllers/MatchController.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text.Json; using System.Threading.Tasks; using Kettu; @@ -10,7 +9,6 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Match; -using LBPUnion.ProjectLighthouse.Types.Profiles; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -72,27 +70,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers #endregion - #region Update LastMatch - - LastMatch? lastMatch = await this.database.LastMatches.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync(); - - // below makes it not look like trash - // ReSharper disable once ConvertIfStatementToNullCoalescingExpression - if (lastMatch == null) - { - lastMatch = new LastMatch - { - UserId = user.UserId, - }; - this.database.LastMatches.Add(lastMatch); - } - - lastMatch.Timestamp = TimestampHelper.Timestamp; - lastMatch.GameVersion = gameToken.GameVersion; - - await this.database.SaveChangesAsync(); - - #endregion + await LastContactHelper.SetLastContact(user, gameToken.GameVersion); #region Process match data diff --git a/ProjectLighthouse/Controllers/PublishController.cs b/ProjectLighthouse/Controllers/PublishController.cs index 0f6499dd..98f490e2 100644 --- a/ProjectLighthouse/Controllers/PublishController.cs +++ b/ProjectLighthouse/Controllers/PublishController.cs @@ -117,6 +117,12 @@ namespace LBPUnion.ProjectLighthouse.Controllers slot.GameVersion = gameToken.GameVersion; + if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) + { + slot.MinimumPlayers = 1; + slot.MaximumPlayers = 4; + } + this.database.Entry(oldSlot).CurrentValues.SetValues(slot); await this.database.SaveChangesAsync(); return this.Ok(oldSlot.Serialize()); diff --git a/ProjectLighthouse/Controllers/ReviewController.cs b/ProjectLighthouse/Controllers/ReviewController.cs index c6145e3e..65e007de 100644 --- a/ProjectLighthouse/Controllers/ReviewController.cs +++ b/ProjectLighthouse/Controllers/ReviewController.cs @@ -1,17 +1,17 @@ #nullable enable using System; using System.IO; -using System.Xml.Serialization; -using System.Collections.Generic; using System.Linq; +using System.Collections.Generic; using System.Threading.Tasks; +using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Helpers; -using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Reviews; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using LBPUnion.ProjectLighthouse.Serialization; namespace LBPUnion.ProjectLighthouse.Controllers { @@ -227,8 +227,8 @@ namespace LBPUnion.ProjectLighthouse.Controllers string inner = Enumerable.Aggregate(reviews, string.Empty, (current, review) => { RatedLevel? ratedLevel = this.database.RatedLevels.FirstOrDefault(r => r.SlotId == review.SlotId && r.UserId == user.UserId); - RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); - return current + review.Serialize(ratedLevel, ratedReview); + //RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); + return current + review.Serialize(ratedLevel/*, ratedReview*/); }); string response = LbpSerializer.TaggedStringElement("reviews", inner, new Dictionary diff --git a/ProjectLighthouse/Controllers/SlotsController.cs b/ProjectLighthouse/Controllers/SlotsController.cs index 6fb7f356..01ffe886 100644 --- a/ProjectLighthouse/Controllers/SlotsController.cs +++ b/ProjectLighthouse/Controllers/SlotsController.cs @@ -8,7 +8,6 @@ using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Settings; -using LBPUnion.ProjectLighthouse.Types.Reviews; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -87,8 +86,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId); VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(r => r.SlotId == id && r.UserId == user.UserId); - Review? yourReview = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == id && r.ReviewerId == user.UserId); - return this.Ok(slot.Serialize(ratedLevel, visitedLevel, yourReview)); + return this.Ok(slot.Serialize(ratedLevel, visitedLevel)); } [HttpGet("slots/lbp2cool")] diff --git a/ProjectLighthouse/Controllers/UserController.cs b/ProjectLighthouse/Controllers/UserController.cs index 03f3a783..71fb7fd4 100644 --- a/ProjectLighthouse/Controllers/UserController.cs +++ b/ProjectLighthouse/Controllers/UserController.cs @@ -29,8 +29,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers public async Task GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1) { User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username); - if (user == null) return ""; - return user.Serialize(gameVersion); + return user?.Serialize(gameVersion); } [HttpGet("user/{username}")] @@ -40,7 +39,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers if (token == null) return this.StatusCode(403, ""); string? user = await this.GetSerializedUser(username, token.GameVersion); - if (string.IsNullOrEmpty(user)) return this.NotFound(); + if (user == null) return this.NotFound(); return this.Ok(user); } @@ -52,10 +51,9 @@ namespace LBPUnion.ProjectLighthouse.Controllers if (token == null) return this.StatusCode(403, ""); List serializedUsers = new(); - foreach (string username in u) + foreach (string userId in u) { - string? serializedUser = await this.GetSerializedUser(username, token.GameVersion); - if (serializedUser != "") serializedUsers.Add(serializedUser); + serializedUsers.Add(await this.GetSerializedUser(userId, token.GameVersion)); } string serialized = serializedUsers.Aggregate(string.Empty, (current, user) => user == null ? current : current + user); @@ -132,6 +130,21 @@ namespace LBPUnion.ProjectLighthouse.Controllers user.PlanetHash = await reader.GetValueAsync(); break; } + case "yay2": + { + user.YayHash = await reader.GetValueAsync(); + break; + } + case "boo2": + { + user.BooHash = await reader.GetValueAsync(); + break; + } + case "meh2": + { + user.MehHash = await reader.GetValueAsync(); + break; + } } break; diff --git a/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs new file mode 100644 index 00000000..70c973ef --- /dev/null +++ b/ProjectLighthouse/Controllers/Website/Admin/AdminSlotController.cs @@ -0,0 +1,73 @@ +#nullable enable +using System; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Types.Levels; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Controllers.Website.Admin +{ + [ApiController] + [Route("admin/slot/{id:int}")] + public class AdminSlotController : ControllerBase + { + private readonly Database database; + + public AdminSlotController(Database database) + { + this.database = database; + } + + [Route("teamPick")] + public async Task TeamPick([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); + + slot.TeamPick = true; + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + [Route("removeTeamPick")] + public async Task RemoveTeamPick([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.NotFound(); + + slot.TeamPick = false; + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + [Route("delete")] + public async Task DeleteLevel([FromRoute] int id) + { + User? user = this.database.UserFromWebRequest(this.Request); + if (user == null || !user.IsAdmin) return this.StatusCode(403, ""); + + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id); + if (slot == null) return this.Ok(); + + if (slot.Location == null) throw new ArgumentNullException(); + + this.database.Locations.Remove(slot.Location); + this.database.Slots.Remove(slot); + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index e2134e58..3a5366cc 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -8,7 +8,6 @@ using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Levels; using LBPUnion.ProjectLighthouse.Types.Profiles; using LBPUnion.ProjectLighthouse.Types.Settings; -using LBPUnion.ProjectLighthouse.Types.Reviews; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; @@ -28,11 +27,9 @@ namespace LBPUnion.ProjectLighthouse public DbSet Scores { get; set; } public DbSet PhotoSubjects { get; set; } public DbSet Photos { get; set; } - public DbSet LastMatches { get; set; } + public DbSet LastContacts { get; set; } public DbSet VisitedLevels { get; set; } public DbSet RatedLevels { get; set; } - public DbSet Reviews { get; set; } - public DbSet RatedReviews { get; set; } public DbSet AuthenticationAttempts { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) @@ -63,7 +60,7 @@ namespace LBPUnion.ProjectLighthouse return user; } -#nullable enable + #nullable enable public async Task AuthenticateUser(LoginData loginData, string userLocation, string titleId = "") { // TODO: don't use psn name to authenticate @@ -255,6 +252,6 @@ namespace LBPUnion.ProjectLighthouse public async Task PhotoFromSubject(PhotoSubject subject) => await this.Photos.FirstOrDefaultAsync(p => p.PhotoSubjectIds.Contains(subject.PhotoSubjectId.ToString())); -#nullable disable + #nullable disable } } \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/LastContactHelper.cs b/ProjectLighthouse/Helpers/LastContactHelper.cs new file mode 100644 index 00000000..fa06e84b --- /dev/null +++ b/ProjectLighthouse/Helpers/LastContactHelper.cs @@ -0,0 +1,35 @@ +#nullable enable +using System.Linq; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Types; +using LBPUnion.ProjectLighthouse.Types.Profiles; +using Microsoft.EntityFrameworkCore; + +namespace LBPUnion.ProjectLighthouse.Helpers +{ + public static class LastContactHelper + { + private static readonly Database database = new(); + + public static async Task SetLastContact(User user, GameVersion gameVersion) + { + LastContact? lastContact = await database.LastContacts.Where(l => l.UserId == user.UserId).FirstOrDefaultAsync(); + + // below makes it not look like trash + // ReSharper disable once ConvertIfStatementToNullCoalescingExpression + if (lastContact == null) + { + lastContact = new LastContact + { + UserId = user.UserId, + }; + database.LastContacts.Add(lastContact); + } + + lastContact.Timestamp = TimestampHelper.Timestamp; + lastContact.GameVersion = gameVersion; + + await database.SaveChangesAsync(); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Helpers/StatisticsHelper.cs b/ProjectLighthouse/Helpers/StatisticsHelper.cs index 53d51710..97091d22 100644 --- a/ProjectLighthouse/Helpers/StatisticsHelper.cs +++ b/ProjectLighthouse/Helpers/StatisticsHelper.cs @@ -8,7 +8,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers { private static readonly Database database = new(); - public static async Task RecentMatches() => await database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync(); + public static async Task RecentMatches() => await database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync(); public static async Task SlotCount() => await database.Slots.CountAsync(); diff --git a/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs b/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs new file mode 100644 index 00000000..b3db6c4a --- /dev/null +++ b/ProjectLighthouse/Maintenance/Commands/CreateUserCommand.cs @@ -0,0 +1,60 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Kettu; +using LBPUnion.ProjectLighthouse.Helpers; +using LBPUnion.ProjectLighthouse.Logging; +using Microsoft.EntityFrameworkCore; +using LBPUnion.ProjectLighthouse.Types; + +namespace LBPUnion.ProjectLighthouse.Maintenance.Commands +{ + [UsedImplicitly] + public class CreateUserCommand : ICommand + { + private readonly Database _database = new(); + + public async Task Run(string[] args) + { + string onlineId = args[0]; + string password = args[1]; + + password = HashHelper.Sha256Hash(password); + + User? user = await this._database.Users.FirstOrDefaultAsync(u => u.Username == onlineId); + if (user == null) + { + user = await this._database.CreateUser(onlineId, + HashHelper.BCryptHash(password)); + Logger.Log( + $"Created user {user.UserId} with online ID (username) {user.Username} and the specified password.", LoggerLevelLogin.Instance); + + user.PasswordResetRequired = true; + Logger.Log("This user will need to reset their password when they log in.", + LoggerLevelLogin.Instance); + + await this._database.SaveChangesAsync(); + Logger.Log("Database changes saved.", + LoggerLevelDatabase.Instance); + } + else + { + Logger.Log("A user with this username already exists.", + LoggerLevelLogin.Instance); + } + } + + public string Name() => "Create New User"; + + public string[] Aliases() => + new[] + { + "useradd", "adduser", "newuser", "createUser" + }; + + public string Arguments() => " "; + + public int RequiredArgs() => 2; + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs b/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs new file mode 100644 index 00000000..4fae2b5e --- /dev/null +++ b/ProjectLighthouse/Maintenance/MaintenanceJobs/FixAllBrokenPlayerRequirementsMaintenanceJob.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading.Tasks; +using LBPUnion.ProjectLighthouse.Types.Levels; + +namespace LBPUnion.ProjectLighthouse.Maintenance.MaintenanceJobs +{ + public class FixAllBrokenPlayerRequirementsMaintenanceJob : IMaintenanceJob + { + private readonly Database database = new(); + + public string Name() => "Fix All Broken Player Requirements"; + public string Description() => "Some LBP1 levels may report that they are designed for 0 players. This job will fix that."; + public async Task Run() + { + int count = 0; + await foreach (Slot slot in this.database.Slots) + { + if (slot.MinimumPlayers == 0 || slot.MaximumPlayers == 0) + { + slot.MinimumPlayers = 1; + slot.MaximumPlayers = 4; + + Console.WriteLine($"Fixed slotId {slot.SlotId}"); + count++; + } + } + + await this.database.SaveChangesAsync(); + + Console.WriteLine($"Fixed {count} broken player requirements."); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Migrations/20211120025513_LevelReviews.cs b/ProjectLighthouse/Migrations/20211120025513_LevelReviews.cs deleted file mode 100644 index 07d2e4c5..00000000 --- a/ProjectLighthouse/Migrations/20211120025513_LevelReviews.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace ProjectLighthouse.Migrations -{ - public partial class LevelReviews : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Reviews", - columns: table => new - { - ReviewId = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - ReviewerId = table.Column(type: "int", nullable: false), - SlotId = table.Column(type: "int", nullable: false), - Timestamp = table.Column(type: "bigint", nullable: false), - LabelCollection = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Deleted = table.Column(type: "tinyint(1)", nullable: false), - DeletedBy = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Text = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK_Reviews", x => x.ReviewId); - table.ForeignKey( - name: "FK_Reviews_Slots_SlotId", - column: x => x.SlotId, - principalTable: "Slots", - principalColumn: "SlotId", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_Reviews_Users_ReviewerId", - column: x => x.ReviewerId, - principalTable: "Users", - principalColumn: "UserId", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "RatedReviews", - columns: table => new - { - RatedReviewId = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "int", nullable: false), - ReviewId = table.Column(type: "int", nullable: false), - Thumb = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_RatedReviews", x => x.RatedReviewId); - table.ForeignKey( - name: "FK_RatedReviews_Reviews_ReviewId", - column: x => x.ReviewId, - principalTable: "Reviews", - principalColumn: "ReviewId", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_RatedReviews_Users_UserId", - column: x => x.UserId, - principalTable: "Users", - principalColumn: "UserId", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX_RatedReviews_ReviewId", - table: "RatedReviews", - column: "ReviewId"); - - migrationBuilder.CreateIndex( - name: "IX_RatedReviews_UserId", - table: "RatedReviews", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_Reviews_ReviewerId", - table: "Reviews", - column: "ReviewerId"); - - migrationBuilder.CreateIndex( - name: "IX_Reviews_SlotId", - table: "Reviews", - column: "SlotId"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "RatedReviews"); - - migrationBuilder.DropTable( - name: "Reviews"); - } - } -} diff --git a/ProjectLighthouse/Migrations/20211120025513_LevelReviews.Designer.cs b/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.Designer.cs similarity index 90% rename from ProjectLighthouse/Migrations/20211120025513_LevelReviews.Designer.cs rename to ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.Designer.cs index 64eaac27..8c618602 100644 --- a/ProjectLighthouse/Migrations/20211120025513_LevelReviews.Designer.cs +++ b/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.Designer.cs @@ -10,8 +10,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace ProjectLighthouse.Migrations { [DbContext(typeof(Database))] - [Migration("20211120025513_LevelReviews")] - partial class LevelReviews + [Migration("20211130190200_AddYayBooMehHashesToUser")] + partial class AddYayBooMehHashesToUser { protected override void BuildTargetModel(ModelBuilder modelBuilder) { @@ -20,6 +20,57 @@ namespace ProjectLighthouse.Migrations .HasAnnotation("ProductVersion", "6.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => { b.Property("HeartedProfileId") @@ -362,6 +413,9 @@ namespace ProjectLighthouse.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("GameVersion") + .HasColumnType("int"); + b.Property("Timestamp") .HasColumnType("bigint"); @@ -387,69 +441,6 @@ namespace ProjectLighthouse.Migrations b.ToTable("Locations"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => - { - b.Property("RatedReviewId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("ReviewId") - .HasColumnType("int"); - - b.Property("Thumb") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.HasKey("RatedReviewId"); - - b.HasIndex("ReviewId"); - - b.HasIndex("UserId"); - - b.ToTable("RatedReviews"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => - { - b.Property("ReviewId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("Deleted") - .HasColumnType("tinyint(1)"); - - b.Property("DeletedBy") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("LabelCollection") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ReviewerId") - .HasColumnType("int"); - - b.Property("SlotId") - .HasColumnType("int"); - - b.Property("Text") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("Timestamp") - .HasColumnType("bigint"); - - b.HasKey("ReviewId"); - - b.HasIndex("ReviewerId"); - - b.HasIndex("SlotId"); - - b.ToTable("Reviews"); - }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => { b.Property("ScoreId") @@ -475,29 +466,6 @@ namespace ProjectLighthouse.Migrations b.ToTable("Scores"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Token", b => - { - b.Property("TokenId") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - b.Property("GameVersion") - .HasColumnType("int"); - - b.Property("UserId") - .HasColumnType("int"); - - b.Property("UserLocation") - .HasColumnType("longtext"); - - b.Property("UserToken") - .HasColumnType("longtext"); - - b.HasKey("TokenId"); - - b.ToTable("Tokens"); - }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => { b.Property("UserId") @@ -507,15 +475,30 @@ namespace ProjectLighthouse.Migrations b.Property("Biography") .HasColumnType("longtext"); + b.Property("BooHash") + .HasColumnType("longtext"); + b.Property("Game") .HasColumnType("int"); b.Property("IconHash") .HasColumnType("longtext"); + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + b.Property("LocationId") .HasColumnType("int"); + b.Property("MehHash") + .HasColumnType("longtext"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("PasswordResetRequired") + .HasColumnType("tinyint(1)"); + b.Property("Pins") .HasColumnType("longtext"); @@ -525,6 +508,9 @@ namespace ProjectLighthouse.Migrations b.Property("Username") .HasColumnType("longtext"); + b.Property("YayHash") + .HasColumnType("longtext"); + b.HasKey("UserId"); b.HasIndex("LocationId"); @@ -532,6 +518,34 @@ namespace ProjectLighthouse.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => { b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") @@ -687,44 +701,6 @@ namespace ProjectLighthouse.Migrations b.Navigation("Target"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Reviews.Review", "Review") - .WithMany() - .HasForeignKey("ReviewId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Review"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Slot"); - }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => { b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") diff --git a/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.cs b/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.cs new file mode 100644 index 00000000..26ae44f3 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211130190200_AddYayBooMehHashesToUser.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class AddYayBooMehHashesToUser : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "BooHash", + table: "Users", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "MehHash", + table: "Users", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "YayHash", + table: "Users", + type: "longtext", + nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "BooHash", + table: "Users"); + + migrationBuilder.DropColumn( + name: "MehHash", + table: "Users"); + + migrationBuilder.DropColumn( + name: "YayHash", + table: "Users"); + } + } +} diff --git a/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.Designer.cs b/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.Designer.cs new file mode 100644 index 00000000..ef8e4d93 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.Designer.cs @@ -0,0 +1,728 @@ +// +using LBPUnion.ProjectLighthouse; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20211202235932_RenameLastMatchesToLastContacts")] + partial class RenameLastMatchesToLastContacts + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.Property("AuthenticationAttemptId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameTokenId") + .HasColumnType("int"); + + b.Property("IPAddress") + .HasColumnType("longtext"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("AuthenticationAttemptId"); + + b.HasIndex("GameTokenId"); + + b.ToTable("AuthenticationAttempts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.GameToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserLocation") + .HasColumnType("longtext"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("GameTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.Property("HeartedProfileId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("HeartedUserId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedProfileId"); + + b.HasIndex("HeartedUserId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedProfiles"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.Property("HeartedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.Property("QueuedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("QueuedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("QueuedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.Property("RatedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("RatingLBP1") + .HasColumnType("double"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("RatedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("RatedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.Property("SlotId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuthorLabels") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("BackgroundHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FirstUploaded") + .HasColumnType("bigint"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("IconHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InitiallyLocked") + .HasColumnType("tinyint(1)"); + + b.Property("LastUpdated") + .HasColumnType("bigint"); + + b.Property("Lbp1Only") + .HasColumnType("tinyint(1)"); + + b.Property("LevelType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MaximumPlayers") + .HasColumnType("int"); + + b.Property("MinimumPlayers") + .HasColumnType("int"); + + b.Property("MoveRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlaysLBP1") + .HasColumnType("int"); + + b.Property("PlaysLBP1Complete") + .HasColumnType("int"); + + b.Property("PlaysLBP1Unique") + .HasColumnType("int"); + + b.Property("PlaysLBP2") + .HasColumnType("int"); + + b.Property("PlaysLBP2Complete") + .HasColumnType("int"); + + b.Property("PlaysLBP2Unique") + .HasColumnType("int"); + + b.Property("PlaysLBP3") + .HasColumnType("int"); + + b.Property("PlaysLBP3Complete") + .HasColumnType("int"); + + b.Property("PlaysLBP3Unique") + .HasColumnType("int"); + + b.Property("PlaysLBPVita") + .HasColumnType("int"); + + b.Property("PlaysLBPVitaComplete") + .HasColumnType("int"); + + b.Property("PlaysLBPVitaUnique") + .HasColumnType("int"); + + b.Property("ResourceCollection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RootLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Shareable") + .HasColumnType("int"); + + b.Property("SubLevel") + .HasColumnType("tinyint(1)"); + + b.Property("TeamPick") + .HasColumnType("tinyint(1)"); + + b.HasKey("SlotId"); + + b.HasIndex("CreatorId"); + + b.HasIndex("LocationId"); + + b.ToTable("Slots"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.Property("VisitedLevelId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlaysLBP1") + .HasColumnType("int"); + + b.Property("PlaysLBP2") + .HasColumnType("int"); + + b.Property("PlaysLBP3") + .HasColumnType("int"); + + b.Property("PlaysLBPVita") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("VisitedLevelId"); + + b.HasIndex("SlotId"); + + b.HasIndex("UserId"); + + b.ToTable("VisitedLevels"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.Property("PhotoId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("LargeHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MediumHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PhotoSubjectCollection") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlanHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SmallHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("PhotoId"); + + b.HasIndex("CreatorId"); + + b.ToTable("Photos"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.Property("PhotoSubjectId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Bounds") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("PhotoSubjectId"); + + b.HasIndex("UserId"); + + b.ToTable("PhotoSubjects"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.Property("CommentId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("PosterUserId") + .HasColumnType("int"); + + b.Property("TargetUserId") + .HasColumnType("int"); + + b.Property("ThumbsDown") + .HasColumnType("int"); + + b.Property("ThumbsUp") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("CommentId"); + + b.HasIndex("PosterUserId"); + + b.HasIndex("TargetUserId"); + + b.ToTable("Comments"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastContact", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("GameVersion") + .HasColumnType("int"); + + b.Property("Timestamp") + .HasColumnType("bigint"); + + b.HasKey("UserId"); + + b.ToTable("LastContacts"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Locations"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.Property("ScoreId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlayerIdCollection") + .HasColumnType("longtext"); + + b.Property("Points") + .HasColumnType("int"); + + b.Property("SlotId") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("ScoreId"); + + b.HasIndex("SlotId"); + + b.ToTable("Scores"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("BooHash") + .HasColumnType("longtext"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("IsAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MehHash") + .HasColumnType("longtext"); + + b.Property("Password") + .HasColumnType("longtext"); + + b.Property("PasswordResetRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.Property("YayHash") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.WebToken", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("WebTokens"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.AuthenticationAttempt", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.GameToken", "GameToken") + .WithMany() + .HasForeignKey("GameTokenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameToken"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.HeartedProfile", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "HeartedUser") + .WithMany() + .HasForeignKey("HeartedUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("HeartedUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.HeartedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.QueuedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.RatedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.Slot", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Levels.VisitedLevel", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Photo", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.PhotoSubject", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Comment", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.User", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Types.Profiles.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.cs b/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.cs new file mode 100644 index 00000000..0029a287 --- /dev/null +++ b/ProjectLighthouse/Migrations/20211202235932_RenameLastMatchesToLastContacts.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ProjectLighthouse.Migrations +{ + public partial class RenameLastMatchesToLastContacts : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameTable(name: "LastMatches", newName: "LastContacts"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameTable(name: "LastContacts", newName: "LastMatches"); + } + } +} diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index d4f8cfe9..70f91a28 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -405,7 +405,7 @@ namespace ProjectLighthouse.Migrations b.ToTable("Comments"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastMatch", b => + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.LastContact", b => { b.Property("UserId") .ValueGeneratedOnAdd() @@ -419,7 +419,7 @@ namespace ProjectLighthouse.Migrations b.HasKey("UserId"); - b.ToTable("LastMatches"); + b.ToTable("LastContacts"); }); modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Profiles.Location", b => @@ -544,6 +544,9 @@ namespace ProjectLighthouse.Migrations b.Property("Biography") .HasColumnType("longtext"); + b.Property("BooHash") + .HasColumnType("longtext"); + b.Property("Game") .HasColumnType("int"); @@ -556,6 +559,9 @@ namespace ProjectLighthouse.Migrations b.Property("LocationId") .HasColumnType("int"); + b.Property("MehHash") + .HasColumnType("longtext"); + b.Property("Password") .HasColumnType("longtext"); @@ -571,6 +577,9 @@ namespace ProjectLighthouse.Migrations b.Property("Username") .HasColumnType("longtext"); + b.Property("YayHash") + .HasColumnType("longtext"); + b.HasKey("UserId"); b.HasIndex("LocationId"); @@ -761,44 +770,6 @@ namespace ProjectLighthouse.Migrations b.Navigation("Target"); }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.Reviews.Review", "Review") - .WithMany() - .HasForeignKey("ReviewId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Review"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.Review", b => - { - b.HasOne("LBPUnion.ProjectLighthouse.Types.User", "Reviewer") - .WithMany() - .HasForeignKey("ReviewerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") - .WithMany() - .HasForeignKey("SlotId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Reviewer"); - - b.Navigation("Slot"); - }); - modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b => { b.HasOne("LBPUnion.ProjectLighthouse.Types.Levels.Slot", "Slot") diff --git a/ProjectLighthouse/Pages/AdminPanelPage.cshtml b/ProjectLighthouse/Pages/AdminPanelPage.cshtml index f9c92c5c..891e8891 100644 --- a/ProjectLighthouse/Pages/AdminPanelPage.cshtml +++ b/ProjectLighthouse/Pages/AdminPanelPage.cshtml @@ -5,10 +5,9 @@ @{ Layout = "Layouts/BaseLayout"; + Model.Title = "Admin Panel"; } -

Admin Panel

-

Commands

@foreach (ICommand command in MaintenanceHelper.Commands) diff --git a/ProjectLighthouse/Pages/LandingPage.cshtml b/ProjectLighthouse/Pages/LandingPage.cshtml index 312f9d1d..fc67aaae 100644 --- a/ProjectLighthouse/Pages/LandingPage.cshtml +++ b/ProjectLighthouse/Pages/LandingPage.cshtml @@ -4,6 +4,7 @@ @{ Layout = "Layouts/BaseLayout"; + Model.ShowTitleInPage = false; }

Welcome to Project Lighthouse!

diff --git a/ProjectLighthouse/Pages/LandingPage.cshtml.cs b/ProjectLighthouse/Pages/LandingPage.cshtml.cs index b02d3897..64fcd9dd 100644 --- a/ProjectLighthouse/Pages/LandingPage.cshtml.cs +++ b/ProjectLighthouse/Pages/LandingPage.cshtml.cs @@ -27,7 +27,7 @@ namespace LBPUnion.ProjectLighthouse.Pages this.PlayersOnlineCount = await StatisticsHelper.RecentMatches(); - List userIds = await this.Database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync(); + List userIds = await this.Database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync(); this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync(); return this.Page(); diff --git a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml index 2e5ee465..2295cea3 100644 --- a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml +++ b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml @@ -32,9 +32,38 @@ - Project Lighthouse + @if (Model.Title == string.Empty) + { + Project Lighthouse + } + else + { + Project Lighthouse - @Model.Title + } + + @* Favicon *@ + + + + + + + + + @if (ServerSettings.Instance.GoogleAnalyticsEnabled) + { + + + + }
@@ -84,6 +113,10 @@

+ @if (Model.ShowTitleInPage) + { +

@Model.Title

+ } @RenderBody()
@* makes it look nicer *@
diff --git a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs index e77b3f11..5046b568 100644 --- a/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs +++ b/ProjectLighthouse/Pages/Layouts/BaseLayout.cshtml.cs @@ -9,6 +9,9 @@ namespace LBPUnion.ProjectLighthouse.Pages.Layouts { public readonly Database Database; + public string Title = string.Empty; + public bool ShowTitleInPage = true; + private User? user; public new User? User { diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml b/ProjectLighthouse/Pages/LoginForm.cshtml index b5b4b82d..6781af70 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml +++ b/ProjectLighthouse/Pages/LoginForm.cshtml @@ -3,6 +3,7 @@ @{ Layout = "Layouts/BaseLayout"; + Model.Title = "Log in"; } @@ -15,10 +16,21 @@ return true; } - + + +@if (!string.IsNullOrWhiteSpace(Model.Error)) +{ +
+
+ Uh oh! +
+

@Model.Error

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

Log in

-
diff --git a/ProjectLighthouse/Pages/LoginForm.cshtml.cs b/ProjectLighthouse/Pages/LoginForm.cshtml.cs index 332f1448..4f54e05c 100644 --- a/ProjectLighthouse/Pages/LoginForm.cshtml.cs +++ b/ProjectLighthouse/Pages/LoginForm.cshtml.cs @@ -12,38 +12,63 @@ namespace LBPUnion.ProjectLighthouse.Pages public class LoginForm : BaseLayout { public LoginForm(Database database) : base(database) - {} + { } + + public string Error { get; private set; } public bool WasLoginRequest { get; private set; } [UsedImplicitly] - public async Task OnGet([FromQuery] string username, [FromQuery] string password) + public async Task OnPost(string username, string password) { - this.WasLoginRequest = !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password); - - if (this.WasLoginRequest) + if (string.IsNullOrWhiteSpace(username)) { - 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); - - if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); - - return this.RedirectToPage(nameof(LandingPage)); + this.Error = "The username field is required."; + return this.Page(); } + if (string.IsNullOrWhiteSpace(password)) + { + this.Error = "The password field is required."; + return this.Page(); + } + + User? user = await this.Database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (user == null) + { + this.Error = "The username or password you entered is invalid."; + return this.Page(); + } + + if (!BCrypt.Net.BCrypt.Verify(password, + user.Password)) + { + this.Error = "The username or password you entered is invalid."; + return this.Page(); + } + + WebToken webToken = new() + { + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; + + this.Database.WebTokens.Add(webToken); + await this.Database.SaveChangesAsync(); + + this.Response.Cookies.Append("LighthouseToken", + webToken.UserToken); + + if (user.PasswordResetRequired) return this.Redirect("~/passwordResetRequired"); + + return this.RedirectToPage(nameof(LandingPage)); + } + + + [UsedImplicitly] + public async Task OnGet() + { + Error = string.Empty; return this.Page(); } } diff --git a/ProjectLighthouse/Pages/LogoutPage.cshtml b/ProjectLighthouse/Pages/LogoutPage.cshtml index ac65ce91..589ed4f5 100644 --- a/ProjectLighthouse/Pages/LogoutPage.cshtml +++ b/ProjectLighthouse/Pages/LogoutPage.cshtml @@ -3,6 +3,7 @@ @{ Layout = "Layouts/BaseLayout"; + Model.Title = "Logged out"; }

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

diff --git a/ProjectLighthouse/Pages/Partials/PhotoPartial.cshtml b/ProjectLighthouse/Pages/Partials/PhotoPartial.cshtml new file mode 100644 index 00000000..33a8fbc1 --- /dev/null +++ b/ProjectLighthouse/Pages/Partials/PhotoPartial.cshtml @@ -0,0 +1,22 @@ +@using LBPUnion.ProjectLighthouse.Types +@model LBPUnion.ProjectLighthouse.Types.Photo + + +
+ +

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

+ +

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

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

+ @slotName +

+ } + else + { +

+ @slotName +

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

+ Created by @Model.Creator?.Username +

+
+
+ @if (user != null) + { + if (isHearted) + { + + + + } + else + { + + + + } + + if (isQueued) + { + + + + } + else + { + + + + } + } +
+
\ No newline at end of file diff --git a/ProjectLighthouse/Pages/PasswordResetPage.cshtml b/ProjectLighthouse/Pages/PasswordResetPage.cshtml index e069274e..d7b3ca36 100644 --- a/ProjectLighthouse/Pages/PasswordResetPage.cshtml +++ b/ProjectLighthouse/Pages/PasswordResetPage.cshtml @@ -3,6 +3,7 @@ @{ Layout = "Layouts/BaseLayout"; + Model.Title = "Password Reset"; } @@ -18,10 +19,20 @@ return true; } + +@if (!string.IsNullOrWhiteSpace(Model.Error)) +{ +
+
+ Uh oh! +
+

@Model.Error

+
+} -

Password Reset

+ + @Html.AntiForgeryToken() -
diff --git a/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs b/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs index e0c473d3..1b1e893a 100644 --- a/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs +++ b/ProjectLighthouse/Pages/PasswordResetPage.cshtml.cs @@ -1,5 +1,6 @@ #nullable enable using System.Threading.Tasks; +using JetBrains.Annotations; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Pages.Layouts; using LBPUnion.ProjectLighthouse.Types; @@ -12,26 +13,40 @@ namespace LBPUnion.ProjectLighthouse.Pages public PasswordResetPage(Database database) : base(database) {} - public bool WasResetRequest { get; private set; } - public async Task OnGet([FromQuery] string password, [FromQuery] string confirmPassword) + + public string Error { get; private set; } + + [UsedImplicitly] + public async Task OnPost(string password, string confirmPassword) { User? user = this.Database.UserFromWebRequest(this.Request); if (user == null) return this.Redirect("~/login"); - this.WasResetRequest = !string.IsNullOrEmpty(password) && !string.IsNullOrEmpty(confirmPassword); - - if (this.WasResetRequest) + if (string.IsNullOrWhiteSpace(password)) { - if (password != confirmPassword) return this.BadRequest(); - - user.Password = HashHelper.BCryptHash(password); - user.PasswordResetRequired = false; - - await this.Database.SaveChangesAsync(); - - return this.Redirect("~/"); + this.Error = "The password field is required."; + return this.Page(); } + if (password != confirmPassword) + { + this.Error = "Passwords do not match!"; + return this.Page(); + } + + user.Password = HashHelper.BCryptHash(password); + user.PasswordResetRequired = false; + + await this.Database.SaveChangesAsync(); + + return this.Redirect("~/"); + } + + [UsedImplicitly] + public IActionResult OnGet() + { + User? user = this.Database.UserFromWebRequest(this.Request); + if (user == null) return this.Redirect("~/login"); return this.Page(); } } diff --git a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml index 5dc62ef7..616d2f8a 100644 --- a/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml +++ b/ProjectLighthouse/Pages/PasswordResetRequiredPage.cshtml @@ -3,9 +3,9 @@ @{ Layout = "Layouts/BaseLayout"; + Model.Title = "Password Reset Required"; } -

Password Reset Required

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

diff --git a/ProjectLighthouse/Pages/PhotosPage.cshtml b/ProjectLighthouse/Pages/PhotosPage.cshtml index 9cd07040..e579cbc3 100644 --- a/ProjectLighthouse/Pages/PhotosPage.cshtml +++ b/ProjectLighthouse/Pages/PhotosPage.cshtml @@ -4,33 +4,15 @@ @{ Layout = "Layouts/BaseLayout"; + Model.Title = "Photos"; } -

Photos

There are @Model.PhotoCount total photos!

@foreach (Photo photo in Model.Photos) {
- -
- -

- - Taken by - - @photo.Creator.Username - - -

- -

- Photo contains @photo.Subjects.Count @(photo.Subjects.Count == 1 ? "person" : "people"): -

- @foreach (PhotoSubject subject in photo.Subjects) - { - @subject.User.Username - } + @await Html.PartialAsync("Partials/PhotoPartial", photo)
} diff --git a/ProjectLighthouse/Pages/RegisterForm.cshtml b/ProjectLighthouse/Pages/RegisterForm.cshtml index 175e6741..ab315267 100644 --- a/ProjectLighthouse/Pages/RegisterForm.cshtml +++ b/ProjectLighthouse/Pages/RegisterForm.cshtml @@ -3,6 +3,7 @@ @{ Layout = "Layouts/BaseLayout"; + Model.Title = "Register"; } @@ -19,10 +20,20 @@ } -

Register

+@if (!string.IsNullOrWhiteSpace(Model.Error)) +{ +
+
+ Uh oh! +
+

@Model.Error

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


diff --git a/ProjectLighthouse/Pages/RegisterForm.cshtml.cs b/ProjectLighthouse/Pages/RegisterForm.cshtml.cs index 91a22719..44883a40 100644 --- a/ProjectLighthouse/Pages/RegisterForm.cshtml.cs +++ b/ProjectLighthouse/Pages/RegisterForm.cshtml.cs @@ -14,40 +14,68 @@ namespace LBPUnion.ProjectLighthouse.Pages { public RegisterForm(Database database) : base(database) {} - + + public string Error { get; private set; } public bool WasRegisterRequest { get; private set; } [UsedImplicitly] - [SuppressMessage("ReSharper", "SpecifyStringComparison")] - public async Task OnGet([FromQuery] string username, [FromQuery] string password, [FromQuery] string confirmPassword) + [SuppressMessage("ReSharper", + "SpecifyStringComparison")] + public async Task OnPost(string username, string password, string confirmPassword) { if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); - this.WasRegisterRequest = !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password) && !string.IsNullOrEmpty(confirmPassword); - - if (this.WasRegisterRequest) + if (string.IsNullOrWhiteSpace(username)) { - 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)); + this.Error = "The username field is blank."; + return this.Page(); } + if (string.IsNullOrWhiteSpace(password)) + { + this.Error = "Password field is required."; + return this.Page(); + } + + if (password != confirmPassword) + { + this.Error = "Passwords do not match!"; + return this.Page(); + } + + bool userExists = + await this.Database.Users.FirstOrDefaultAsync(u => u.Username.ToLower() == username.ToLower()) != null; + if (userExists) + { + this.Error = "The username you've chosen is already taken."; + return this.Page(); + } + + User user = await this.Database.CreateUser(username, + HashHelper.BCryptHash(password)); + + WebToken webToken = new() + { + UserId = user.UserId, + UserToken = HashHelper.GenerateAuthToken(), + }; + + this.Database.WebTokens.Add(webToken); + await this.Database.SaveChangesAsync(); + + this.Response.Cookies.Append("LighthouseToken", + webToken.UserToken); + + return this.RedirectToPage(nameof(LandingPage)); + } + + [UsedImplicitly] + [SuppressMessage("ReSharper", "SpecifyStringComparison")] + public IActionResult OnGet() + { + Error = string.Empty; + if (!ServerSettings.Instance.RegistrationEnabled) return this.NotFound(); + return this.Page(); } } diff --git a/ProjectLighthouse/Pages/SlotPage.cshtml b/ProjectLighthouse/Pages/SlotPage.cshtml new file mode 100644 index 00000000..221d99f2 --- /dev/null +++ b/ProjectLighthouse/Pages/SlotPage.cshtml @@ -0,0 +1,81 @@ +@page "/slot/{id:int}" +@model LBPUnion.ProjectLighthouse.Pages.SlotPage + +@{ + Layout = "Layouts/BaseLayout"; + Model.Title = Model.Slot.Name; + Model.ShowTitleInPage = false; +} + +@await Html.PartialAsync("Partials/SlotCardPartial", Model.Slot, new ViewDataDictionary(ViewData) +{ + { + "User", Model.User + }, + { + "CallbackUrl", $"~/slot/{Model.Slot.SlotId}" + }, + { + "ShowLink", false + }, +}) + +
+
+
+

Description

+

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

+
+
+
+
+

Tags

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

This level has no tags.

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

Admin Settings

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

Levels

There are @Model.SlotCount total levels!

@foreach (Slot slot in Model.Slots) { - string slotName = string.IsNullOrEmpty(slot.Name) ? "Unnamed Level" : slot.Name; - - bool isQueued = false; - bool isHearted = false; - - if (Model.User != null) - { - isQueued = await Model.Database.QueuedLevels.FirstOrDefaultAsync(h => h.SlotId == slot.SlotId && h.UserId == Model.User.UserId) != null; - - isHearted = await Model.Database.HeartedLevels.FirstOrDefaultAsync(h => h.SlotId == slot.SlotId && h.UserId == Model.User.UserId) != null; - } -
-
-
-

@slotName

-
- @slot.Hearts - @slot.Plays - @slot.Thumbsup - @slot.Thumbsdown - - @if (slot.GameVersion == GameVersion.LittleBigPlanet1) - { - - @slot.RatingLBP1 - } -
-

- Created by @slot.Creator?.Username -

-
-
- @if (Model.User != null) - { - if (isHearted) - { - - - - } - else - { - - - - } - - if (isQueued) - { - - - - } - else - { - - - - } - } -
-
+ @await Html.PartialAsync("Partials/SlotCardPartial", slot, new ViewDataDictionary(ViewData) + { + { + "User", Model.User + }, + { + "CallbackUrl", $"~/slots/{Model.PageNumber}" + }, + { + "ShowLink", true + }, + })
} diff --git a/ProjectLighthouse/Pages/UserPage.cshtml b/ProjectLighthouse/Pages/UserPage.cshtml index d9e127e3..128a7883 100644 --- a/ProjectLighthouse/Pages/UserPage.cshtml +++ b/ProjectLighthouse/Pages/UserPage.cshtml @@ -6,13 +6,15 @@ @{ Layout = "Layouts/BaseLayout"; + Model.Title = Model.ProfileUser!.Username + "'s user page"; + Model.ShowTitleInPage = false; }
-

@Model.ProfileUser!.Username's user page

+

@Model.Title

- @Model.ProfileUser.Status + @Model.ProfileUser!.Status

@Model.ProfileUser.Hearts @@ -72,37 +74,31 @@ @foreach (Photo photo in Model.Photos) {
- -
- -

- Photo contains @photo.Subjects.Count @(photo.Subjects.Count == 1 ? "person" : "people"): -

- @foreach (PhotoSubject subject in photo.Subjects) - { - @subject.User.Username - } + @await Html.PartialAsync("Partials/PhotoPartial", photo);
}
} -@if (Model.ProfileUser.Comments > 0) -{ -
-

Comments

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

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

-
-
- } -
-} \ No newline at end of file + +
+

Comments

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

There are no comments.

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

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

+
+
+ } +
\ No newline at end of file diff --git a/ProjectLighthouse/Program.cs b/ProjectLighthouse/Program.cs index 1ef82df1..cfef9200 100644 --- a/ProjectLighthouse/Program.cs +++ b/ProjectLighthouse/Program.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Threading; using Kettu; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; @@ -17,6 +18,16 @@ namespace LBPUnion.ProjectLighthouse { public static void Main(string[] args) { + if (args.Length != 0 && args[0] == "--wait-for-debugger") + { + Console.WriteLine("Waiting for a debugger to be attached..."); + while (!Debugger.IsAttached) + { + Thread.Sleep(100); + } + Console.WriteLine("Debugger attached."); + } + // Log startup time Stopwatch stopwatch = new(); stopwatch.Start(); diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index a4e601cc..6240dece 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -8,34 +8,38 @@ - - - - - - - + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + Always - + Always + + <_ContentIncludedByDefault Remove="Pages\Admin\Index.cshtml" /> + + - - + + diff --git a/ProjectLighthouse/Startup.cs b/ProjectLighthouse/Startup.cs index 0048c96c..27071604 100644 --- a/ProjectLighthouse/Startup.cs +++ b/ProjectLighthouse/Startup.cs @@ -4,6 +4,7 @@ using Kettu; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Logging; using LBPUnion.ProjectLighthouse.Serialization; +using LBPUnion.ProjectLighthouse.Types; using LBPUnion.ProjectLighthouse.Types.Settings; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; @@ -135,11 +136,25 @@ namespace LBPUnion.ProjectLighthouse // Copy the buffered response to the actual respose stream. responseBuffer.Position = 0; - await responseBuffer.CopyToAsync(oldResponseStream); - context.Response.Body = oldResponseStream; + #nullable enable + // Log LastContact for LBP1. This is done on LBP2/3/V on a Match request. + if (context.Request.Path.ToString().StartsWith("/LITTLEBIGPLANETPS3_XML")) + { + // We begin by grabbing a token from the request, if this is a LBPPS3_XML request of course. + await using Database database = new(); // Gets nuked at the end of the scope + GameToken? gameToken = await database.GameTokenFromRequest(context.Request); + + if (gameToken != null && gameToken.GameVersion == GameVersion.LittleBigPlanet1) + { + // Ignore UserFromGameToken null because user must exist for a token to exist + await LastContactHelper.SetLastContact((await database.UserFromGameToken(gameToken))!, GameVersion.LittleBigPlanet1); + } + } + #nullable disable + requestStopwatch.Stop(); Logger.Log diff --git a/ProjectLighthouse/StaticFiles/android-chrome-192x192.png b/ProjectLighthouse/StaticFiles/android-chrome-192x192.png new file mode 100644 index 00000000..9b3eab52 Binary files /dev/null and b/ProjectLighthouse/StaticFiles/android-chrome-192x192.png differ diff --git a/ProjectLighthouse/StaticFiles/android-chrome-512x512.png b/ProjectLighthouse/StaticFiles/android-chrome-512x512.png new file mode 100644 index 00000000..09f9e579 Binary files /dev/null and b/ProjectLighthouse/StaticFiles/android-chrome-512x512.png differ diff --git a/ProjectLighthouse/StaticFiles/apple-touch-icon.png b/ProjectLighthouse/StaticFiles/apple-touch-icon.png new file mode 100644 index 00000000..e87773fa Binary files /dev/null and b/ProjectLighthouse/StaticFiles/apple-touch-icon.png differ diff --git a/ProjectLighthouse/StaticFiles/browserconfig.xml b/ProjectLighthouse/StaticFiles/browserconfig.xml new file mode 100644 index 00000000..b3930d0f --- /dev/null +++ b/ProjectLighthouse/StaticFiles/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/ProjectLighthouse/StaticFiles/favicon-16x16.png b/ProjectLighthouse/StaticFiles/favicon-16x16.png new file mode 100644 index 00000000..e3ef0781 Binary files /dev/null and b/ProjectLighthouse/StaticFiles/favicon-16x16.png differ diff --git a/ProjectLighthouse/StaticFiles/favicon-32x32.png b/ProjectLighthouse/StaticFiles/favicon-32x32.png new file mode 100644 index 00000000..bc34a6c1 Binary files /dev/null and b/ProjectLighthouse/StaticFiles/favicon-32x32.png differ diff --git a/ProjectLighthouse/StaticFiles/favicon.ico b/ProjectLighthouse/StaticFiles/favicon.ico new file mode 100644 index 00000000..af563a7c Binary files /dev/null and b/ProjectLighthouse/StaticFiles/favicon.ico differ diff --git a/ProjectLighthouse/StaticFiles/mstile-150x150.png b/ProjectLighthouse/StaticFiles/mstile-150x150.png new file mode 100644 index 00000000..1825f73d Binary files /dev/null and b/ProjectLighthouse/StaticFiles/mstile-150x150.png differ diff --git a/ProjectLighthouse/StaticFiles/safari-pinned-tab.svg b/ProjectLighthouse/StaticFiles/safari-pinned-tab.svg new file mode 100644 index 00000000..51454694 --- /dev/null +++ b/ProjectLighthouse/StaticFiles/safari-pinned-tab.svg @@ -0,0 +1,223 @@ + + + + + Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + + + + + + + + + + + + + + diff --git a/ProjectLighthouse/StaticFiles/site.webmanifest b/ProjectLighthouse/StaticFiles/site.webmanifest new file mode 100644 index 00000000..b20abb7c --- /dev/null +++ b/ProjectLighthouse/StaticFiles/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Types/Levels/Slot.cs index 42353c32..ff0c580e 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Types/Levels/Slot.cs @@ -6,7 +6,6 @@ using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Profiles; -using LBPUnion.ProjectLighthouse.Types.Reviews; namespace LBPUnion.ProjectLighthouse.Types.Levels { @@ -195,23 +194,13 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels [XmlElement("leveltype")] public string LevelType { get; set; } = ""; - [NotMapped] - [XmlElement("reviewCount")] - public int ReviewCount { - get { - using Database database = new(); - - return database.Reviews.Count(r => r.SlotId == this.SlotId); - } - } - public string SerializeResources() { return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)) + LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize)); } - public string Serialize(RatedLevel? yourRatingStats = null, VisitedLevel? yourVisitedStats = null, Review? yourReview = null) + public string Serialize(RatedLevel? yourRatingStats = null, VisitedLevel? yourVisitedStats = null) { string slotData = LbpSerializer.StringElement("name", this.Name) + @@ -259,12 +248,9 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels LbpSerializer.StringElement("yourLBP1PlayCount", yourVisitedStats?.PlaysLBP1) + LbpSerializer.StringElement("yourLBP2PlayCount", yourVisitedStats?.PlaysLBP2) + LbpSerializer.StringElement("yourLBP3PlayCount", yourVisitedStats?.PlaysLBP3) + - LbpSerializer.StringElement("yourLBPVitaPlayCount", yourVisitedStats?.PlaysLBPVita) + // i doubt this is the right name but we'll go with it - yourReview?.Serialize("yourReview") + - LbpSerializer.StringElement("reviewsEnabled", true) + - LbpSerializer.StringElement("commentsEnabled", false) + - LbpSerializer.StringElement("reviewCount", this.ReviewCount); - + LbpSerializer.StringElement + ("yourLBPVitaPlayCount", yourVisitedStats?.PlaysLBPVita); // i doubt this is the right name but we'll go with it + return LbpSerializer.TaggedStringElement("slot", slotData, "type", "user"); } } diff --git a/ProjectLighthouse/Types/Profiles/LastMatch.cs b/ProjectLighthouse/Types/Profiles/LastContact.cs similarity index 90% rename from ProjectLighthouse/Types/Profiles/LastMatch.cs rename to ProjectLighthouse/Types/Profiles/LastContact.cs index 832430cd..295250b8 100644 --- a/ProjectLighthouse/Types/Profiles/LastMatch.cs +++ b/ProjectLighthouse/Types/Profiles/LastContact.cs @@ -2,7 +2,7 @@ using System.ComponentModel.DataAnnotations; namespace LBPUnion.ProjectLighthouse.Types.Profiles { - public class LastMatch + public class LastContact { [Key] public int UserId { get; set; } diff --git a/ProjectLighthouse/Types/Settings/ServerSettings.cs b/ProjectLighthouse/Types/Settings/ServerSettings.cs index eabf41c4..e4caf307 100644 --- a/ProjectLighthouse/Types/Settings/ServerSettings.cs +++ b/ProjectLighthouse/Types/Settings/ServerSettings.cs @@ -63,7 +63,7 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings } } - public const int CurrentConfigVersion = 10; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! + public const int CurrentConfigVersion = 11; // MUST BE INCREMENTED FOR EVERY CONFIG CHANGE! #region Meta @@ -102,5 +102,9 @@ namespace LBPUnion.ProjectLighthouse.Types.Settings public int EntitledSlots { get; set; } = 50; public int ListsQuota { get; set; } = 50; + + public bool GoogleAnalyticsEnabled { get; set; } = false; + + public string GoogleAnalyticsId { get; set; } = ""; } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/User.cs b/ProjectLighthouse/Types/User.cs index dcd8dd97..6f24a385 100644 --- a/ProjectLighthouse/Types/User.cs +++ b/ProjectLighthouse/Types/User.cs @@ -26,12 +26,7 @@ namespace LBPUnion.ProjectLighthouse.Types public string Biography { get; set; } [NotMapped] - public int Reviews { - get { - using Database database = new(); - return database.Reviews.Count(r => r.ReviewerId == this.UserId); - } - } + public int Reviews => 0; [NotMapped] public int Comments { @@ -105,12 +100,16 @@ namespace LBPUnion.ProjectLighthouse.Types public bool PasswordResetRequired { get; set; } + public string YayHash { get; set; } = ""; + public string BooHash { get; set; } = ""; + public string MehHash { get; set; } = ""; + #nullable enable [NotMapped] public string Status { get { using Database database = new(); - LastMatch? lastMatch = database.LastMatches.Where + LastContact? lastMatch = database.LastContacts.Where (l => l.UserId == this.UserId) .FirstOrDefault(l => TimestampHelper.Timestamp - l.Timestamp < 300); @@ -142,8 +141,11 @@ namespace LBPUnion.ProjectLighthouse.Types LbpSerializer.StringElement("pins", this.Pins) + LbpSerializer.StringElement("planets", this.PlanetHash) + LbpSerializer.BlankElement("photos") + - LbpSerializer.StringElement("heartCount", this.Hearts) - + this.ClientsConnected.Serialize(); + LbpSerializer.StringElement("heartCount", this.Hearts) + + LbpSerializer.StringElement("yay2", YayHash) + + LbpSerializer.StringElement("boo2", YayHash) + + LbpSerializer.StringElement("meh2", YayHash); + this.ClientsConnected.Serialize(); return LbpSerializer.TaggedStringElement("user", user, "type", "user"); }