Merge branch 'main' of github.com:LumaLivy/project-lighthouse

This commit is contained in:
LumaLivy 2021-12-10 23:59:39 -05:00
commit bf2fa7f807
56 changed files with 1949 additions and 553 deletions

10
.gitignore vendored
View file

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

View file

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

View file

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

View file

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

View file

@ -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<string, object>

View file

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

View file

@ -29,8 +29,7 @@ namespace LBPUnion.ProjectLighthouse.Controllers
public async Task<string?> GetSerializedUser(string username, GameVersion gameVersion = GameVersion.LittleBigPlanet1)
{
User? user = await this.database.Users.Include(u => u.Location).FirstOrDefaultAsync(u => u.Username == username);
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<string?> 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;

View file

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

View file

@ -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<Score> Scores { get; set; }
public DbSet<PhotoSubject> PhotoSubjects { get; set; }
public DbSet<Photo> Photos { get; set; }
public DbSet<LastMatch> LastMatches { get; set; }
public DbSet<LastContact> LastContacts { get; set; }
public DbSet<VisitedLevel> VisitedLevels { get; set; }
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)
@ -63,7 +60,7 @@ namespace LBPUnion.ProjectLighthouse
return user;
}
#nullable enable
#nullable enable
public async Task<GameToken?> 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<Photo?> PhotoFromSubject(PhotoSubject subject)
=> await this.Photos.FirstOrDefaultAsync(p => p.PhotoSubjectIds.Contains(subject.PhotoSubjectId.ToString()));
#nullable disable
#nullable disable
}
}

View file

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

View file

@ -8,7 +8,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers
{
private static readonly Database database = new();
public static async Task<int> RecentMatches() => await database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync();
public static async Task<int> RecentMatches() => await database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).CountAsync();
public static async Task<int> SlotCount() => await database.Slots.CountAsync();

View file

@ -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() => "<OnlineID> <Password>";
public int RequiredArgs() => 2;
}
}

View file

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

View file

@ -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<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
ReviewerId = table.Column<int>(type: "int", nullable: false),
SlotId = table.Column<int>(type: "int", nullable: false),
Timestamp = table.Column<long>(type: "bigint", nullable: false),
LabelCollection = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Deleted = table.Column<bool>(type: "tinyint(1)", nullable: false),
DeletedBy = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Text = table.Column<string>(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<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<int>(type: "int", nullable: false),
ReviewId = table.Column<int>(type: "int", nullable: false),
Thumb = table.Column<int>(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");
}
}
}

View file

@ -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<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")
@ -362,6 +413,9 @@ namespace ProjectLighthouse.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
@ -387,69 +441,6 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Locations");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Reviews.RatedReview", b =>
{
b.Property<int>("RatedReviewId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("ReviewId")
.HasColumnType("int");
b.Property<int>("Thumb")
.HasColumnType("int");
b.Property<int>("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<int>("ReviewId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<bool>("Deleted")
.HasColumnType("tinyint(1)");
b.Property<string>("DeletedBy")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("LabelCollection")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("ReviewerId")
.HasColumnType("int");
b.Property<int>("SlotId")
.HasColumnType("int");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("longtext");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("ReviewId");
b.HasIndex("ReviewerId");
b.HasIndex("SlotId");
b.ToTable("Reviews");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Score", b =>
{
b.Property<int>("ScoreId")
@ -475,29 +466,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")
@ -507,15 +475,30 @@ namespace ProjectLighthouse.Migrations
b.Property<string>("Biography")
.HasColumnType("longtext");
b.Property<string>("BooHash")
.HasColumnType("longtext");
b.Property<int>("Game")
.HasColumnType("int");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<bool>("IsAdmin")
.HasColumnType("tinyint(1)");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<string>("MehHash")
.HasColumnType("longtext");
b.Property<string>("Password")
.HasColumnType("longtext");
b.Property<bool>("PasswordResetRequired")
.HasColumnType("tinyint(1)");
b.Property<string>("Pins")
.HasColumnType("longtext");
@ -525,6 +508,9 @@ namespace ProjectLighthouse.Migrations
b.Property<string>("Username")
.HasColumnType("longtext");
b.Property<string>("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<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")
@ -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")

View file

@ -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<string>(
name: "BooHash",
table: "Users",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "MehHash",
table: "Users",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
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");
}
}
}

View file

@ -0,0 +1,728 @@
// <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("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<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.LastContact", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("GameVersion")
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("UserId");
b.ToTable("LastContacts");
});
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<string>("BooHash")
.HasColumnType("longtext");
b.Property<int>("Game")
.HasColumnType("int");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<bool>("IsAdmin")
.HasColumnType("tinyint(1)");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<string>("MehHash")
.HasColumnType("longtext");
b.Property<string>("Password")
.HasColumnType("longtext");
b.Property<bool>("PasswordResetRequired")
.HasColumnType("tinyint(1)");
b.Property<string>("Pins")
.HasColumnType("longtext");
b.Property<string>("PlanetHash")
.HasColumnType("longtext");
b.Property<string>("Username")
.HasColumnType("longtext");
b.Property<string>("YayHash")
.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,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");
}
}
}

View file

@ -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<int>("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<string>("Biography")
.HasColumnType("longtext");
b.Property<string>("BooHash")
.HasColumnType("longtext");
b.Property<int>("Game")
.HasColumnType("int");
@ -556,6 +559,9 @@ namespace ProjectLighthouse.Migrations
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<string>("MehHash")
.HasColumnType("longtext");
b.Property<string>("Password")
.HasColumnType("longtext");
@ -571,6 +577,9 @@ namespace ProjectLighthouse.Migrations
b.Property<string>("Username")
.HasColumnType("longtext");
b.Property<string>("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")

View file

@ -5,10 +5,9 @@
@{
Layout = "Layouts/BaseLayout";
Model.Title = "Admin Panel";
}
<h1>Admin Panel</h1>
<h2>Commands</h2>
<div class="ui grid">
@foreach (ICommand command in MaintenanceHelper.Commands)

View file

@ -4,6 +4,7 @@
@{
Layout = "Layouts/BaseLayout";
Model.ShowTitleInPage = false;
}
<h1>Welcome to <b>Project Lighthouse</b>!</h1>

View file

@ -27,7 +27,7 @@ namespace LBPUnion.ProjectLighthouse.Pages
this.PlayersOnlineCount = await StatisticsHelper.RecentMatches();
List<int> userIds = await this.Database.LastMatches.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync();
List<int> userIds = await this.Database.LastContacts.Where(l => TimestampHelper.Timestamp - l.Timestamp < 300).Select(l => l.UserId).ToListAsync();
this.PlayersOnline = await this.Database.Users.Where(u => userIds.Contains(u.UserId)).ToListAsync();
return this.Page();

View file

@ -32,9 +32,38 @@
<html lang="en">
<head>
<title>Project Lighthouse</title>
@if (Model.Title == string.Empty)
{
<title>Project Lighthouse</title>
}
else
{
<title>Project Lighthouse - @Model.Title</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">
@* Favicon *@
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
@if (ServerSettings.Instance.GoogleAnalyticsEnabled)
{
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=@ServerSettings.Instance.GoogleAnalyticsId"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '@ServerSettings.Instance.GoogleAnalyticsId');
</script>
}
</head>
<body>
<div class="pageContainer">
@ -84,6 +113,10 @@
<div class="main">
<div class="ui container">
<br>
@if (Model.ShowTitleInPage)
{
<h1>@Model.Title</h1>
}
@RenderBody()
<div style="height: 50px;"></div> @* makes it look nicer *@
</div>

View file

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

View file

@ -3,6 +3,7 @@
@{
Layout = "Layouts/BaseLayout";
Model.Title = "Log in";
}
<script src="https://geraintluff.github.io/sha256/sha256.min.js"></script>
@ -15,10 +16,21 @@
return true;
}
</script>
</script>
@if (!string.IsNullOrWhiteSpace(Model.Error))
{
<div class="ui negative message">
<div class="header">
Uh oh!
</div>
<p>@Model.Error</p>
</div>
}
<form onsubmit="return onSubmit(this)" method="post">
@Html.AntiForgeryToken()
<h1>Log in</h1>
<form onsubmit="return onSubmit(this)">
<div class="ui left labeled input">
<label for="text" class="ui blue label">Username: </label>
<input type="text" name="username" id="text">

View file

@ -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<IActionResult> OnGet([FromQuery] string username, [FromQuery] string password)
public async Task<IActionResult> 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<IActionResult> OnGet()
{
Error = string.Empty;
return this.Page();
}
}

View file

@ -3,6 +3,7 @@
@{
Layout = "Layouts/BaseLayout";
Model.Title = "Logged out";
}
<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>

View file

@ -0,0 +1,22 @@
@using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Types.Photo
<img src="/gameAssets/@Model.LargeHash" style="width: 100%; height: auto; border-radius: .28571429rem;">
<br>
<p>
<i>
Taken by
<b>
<a href="/user/@Model.Creator?.UserId">@Model.Creator?.Username</a>
</b>
</i>
</p>
<p>
<b>Photo contains @Model.Subjects.Count @(Model.Subjects.Count == 1 ? "person" : "people"):</b>
</p>
@foreach (PhotoSubject subject in Model.Subjects)
{
<a href="/user/@subject.UserId">@subject.User.Username</a>
}

View file

@ -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;
}
<div class="ui grid">
<div class="eight wide column">
@if (showLink)
{
<h2 style="margin-bottom: 2px;">
<a href="~/slot/@Model.SlotId">@slotName</a>
</h2>
}
else
{
<h1 style="margin-bottom: 2px;">
@slotName
</h1>
}
<div class="statsUnderTitle" style="margin-bottom: 10px;">
<i class="pink heart icon" title="Hearts"></i> <span>@Model.Hearts</span>
<i class="blue play icon" title="Plays"></i> <span>@Model.Plays</span>
<i class="green thumbs up icon" title="Yays"></i> <span>@Model.Thumbsup</span>
<i class="red thumbs down icon" title="Boos"></i> <span>@Model.Thumbsdown</span>
@if (Model.GameVersion == GameVersion.LittleBigPlanet1)
{
<i class="yellow star icon" title="LBP1 Stars"></i>
<span>@Model.RatingLBP1</span>
}
</div>
<p>
<i>Created by <a href="/user/@Model.Creator?.UserId">@Model.Creator?.Username</a></i>
</p>
</div>
<div class="eight wide right aligned column">
@if (user != null)
{
if (isHearted)
{
<a class="ui pink tiny button" href="/slot/@Model.SlotId/unheart?callbackUrl=@callbackUrl" title="Unheart">
<i class="broken heart icon" style="margin: 0"></i>
</a>
}
else
{
<a class="ui pink tiny button" href="/slot/@Model.SlotId/heart?callbackUrl=@callbackUrl" title="Heart">
<i class="heart icon" style="margin: 0"></i>
</a>
}
if (isQueued)
{
<a class="ui yellow tiny button" href="/slot/@Model.SlotId/unqueue?callbackUrl=@callbackUrl" title="Unqueue">
<i class="bell slash icon" style="margin: 0"></i>
</a>
}
else
{
<a class="ui yellow tiny button" href="/slot/@Model.SlotId/queue?callbackUrl=@callbackUrl" title="Queue">
<i class="bell icon" style="margin: 0"></i>
</a>
}
}
</div>
</div>

View file

@ -3,6 +3,7 @@
@{
Layout = "Layouts/BaseLayout";
Model.Title = "Password Reset";
}
<script src="https://geraintluff.github.io/sha256/sha256.min.js"></script>
@ -18,10 +19,20 @@
return true;
}
</script>
@if (!string.IsNullOrWhiteSpace(Model.Error))
{
<div class="ui negative message">
<div class="header">
Uh oh!
</div>
<p>@Model.Error</p>
</div>
}
<h1>Password Reset</h1>
<form onsubmit="return onSubmit(this)" method="post">
@Html.AntiForgeryToken()
<form onsubmit="return onSubmit(this)">
<div class="ui left labeled input">
<label for="password" class="ui blue label">Password: </label>
<input type="password" name="password" id="password">

View file

@ -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<IActionResult> OnGet([FromQuery] string password, [FromQuery] string confirmPassword)
public string Error { get; private set; }
[UsedImplicitly]
public async Task<IActionResult> 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();
}
}

View file

@ -3,9 +3,9 @@
@{
Layout = "Layouts/BaseLayout";
Model.Title = "Password Reset Required";
}
<h1>Password Reset Required</h1>
<p>An admin has deemed it necessary that you reset your password. Please do so.</p>
<a href="/passwordReset">

View file

@ -4,33 +4,15 @@
@{
Layout = "Layouts/BaseLayout";
Model.Title = "Photos";
}
<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="/user/@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="/user/@subject.UserId">@subject.User.Username</a>
}
@await Html.PartialAsync("Partials/PhotoPartial", photo)
</div>
}

View file

@ -3,6 +3,7 @@
@{
Layout = "Layouts/BaseLayout";
Model.Title = "Register";
}
<script src="https://geraintluff.github.io/sha256/sha256.min.js"></script>
@ -19,10 +20,20 @@
}
</script>
<h1>Register</h1>
@if (!string.IsNullOrWhiteSpace(Model.Error))
{
<div class="ui negative message">
<div class="header">
Uh oh!
</div>
<p>@Model.Error</p>
</div>
}
<form onsubmit="return onSubmit(this)">
<form onsubmit="return onSubmit(this)" method="post">
<div class="ui left labeled input">
@Html.AntiForgeryToken()
<label for="text" class="ui blue label">Username: </label>
<input type="text" name="username" id="text">
</div><br><br>

View file

@ -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<IActionResult> OnGet([FromQuery] string username, [FromQuery] string password, [FromQuery] string confirmPassword)
[SuppressMessage("ReSharper",
"SpecifyStringComparison")]
public async Task<IActionResult> 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();
}
}

View file

@ -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
},
})
<div class="ui grid">
<div class="eight wide column">
<div class="ui blue segment">
<h2>Description</h2>
<p>@(string.IsNullOrEmpty(Model.Slot.Description) ? "This level has no description." : Model.Slot.Description)</p>
</div>
</div>
<div class="eight wide column">
<div class="ui red segment">
<h2>Tags</h2>
@{
string[] authorLabels = Model.Slot.AuthorLabels.Split(",");
if (authorLabels.Length == 1) // ..?? ok c#
{
<p>This level has no tags.</p>
}
else
{
foreach (string label in authorLabels.Where(label => !string.IsNullOrEmpty(label)))
{
<div class="ui blue label">@label.Replace("LABEL_", "")</div>
}
}
}
</div>
</div>
</div>
@if (Model.User != null && Model.User.IsAdmin)
{
<div class="ui yellow segment">
<h2>Admin Settings</h2>
@if (Model.Slot.TeamPick)
{
<a href="/admin/slot/@Model.Slot.SlotId/removeTeamPick">
<div class="ui pink button">
<i class="ribbon icon"></i>
<span>Remove Team Pick</span>
</div>
</a>
}
else
{
<a href="/admin/slot/@Model.Slot.SlotId/teamPick">
<div class="ui pink button">
<i class="ribbon icon"></i>
<span>Team Pick</span>
</div>
</a>
}
<a href="/admin/slot/@Model.Slot.SlotId/delete">
<div class="ui red button">
<i class="trash icon"></i>
<span>Delete</span>
</div>
</a>
</div>
}

View file

@ -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<IActionResult> OnGet([FromRoute] int id)
{
Slot? slot = await this.Database.Slots.Include(s => s.Creator).FirstOrDefaultAsync(s => s.SlotId == id);
if (slot == null) return this.NotFound();
this.Slot = slot;
return this.Page();
}
}
}

View file

@ -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";
}
<h1>Levels</h1>
<p>There are @Model.SlotCount total levels!</p>
@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;
}
<div class="ui segment">
<div class="ui grid">
<div class="eight wide column">
<h2 style="margin-bottom: 2px;">@slotName</h2>
<div class="statsUnderTitle" style="margin-bottom: 10px;">
<i class="pink heart icon" title="Hearts"></i> <span>@slot.Hearts</span>
<i class="blue play icon" title="Plays"></i> <span>@slot.Plays</span>
<i class="green thumbs up icon" title="Yays"></i> <span>@slot.Thumbsup</span>
<i class="red thumbs down icon" title="Boos"></i> <span>@slot.Thumbsdown</span>
@if (slot.GameVersion == GameVersion.LittleBigPlanet1)
{
<i class="yellow star icon" title="LBP1 Stars"></i>
<span>@slot.RatingLBP1</span>
}
</div>
<p>
<i>Created by <a href="/user/@slot.Creator?.UserId">@slot.Creator?.Username</a></i>
</p>
</div>
<div class="eight wide right aligned column">
@if (Model.User != null)
{
if (isHearted)
{
<a class="ui pink tiny button" href="/slot/@slot.SlotId/unheart?callbackUrl=~/slots/@Model.PageNumber" title="Unheart">
<i class="broken heart icon" style="margin: 0"></i>
</a>
}
else
{
<a class="ui pink tiny button" href="/slot/@slot.SlotId/heart?callbackUrl=~/slots/@Model.PageNumber" title="Heart">
<i class="heart icon" style="margin: 0"></i>
</a>
}
if (isQueued)
{
<a class="ui yellow tiny button" href="/slot/@slot.SlotId/unqueue?callbackUrl=~/slots/@Model.PageNumber" title="Unqueue">
<i class="bell slash icon" style="margin: 0"></i>
</a>
}
else
{
<a class="ui yellow tiny button" href="/slot/@slot.SlotId/queue?callbackUrl=~/slots/@Model.PageNumber" title="Queue">
<i class="bell icon" style="margin: 0"></i>
</a>
}
}
</div>
</div>
@await Html.PartialAsync("Partials/SlotCardPartial", slot, new ViewDataDictionary(ViewData)
{
{
"User", Model.User
},
{
"CallbackUrl", $"~/slots/{Model.PageNumber}"
},
{
"ShowLink", true
},
})
</div>
}

View file

@ -6,13 +6,15 @@
@{
Layout = "Layouts/BaseLayout";
Model.Title = Model.ProfileUser!.Username + "'s user page";
Model.ShowTitleInPage = false;
}
<div class="ui grid">
<div class="eight wide column">
<h1>@Model.ProfileUser!.Username's user page</h1>
<h1>@Model.Title</h1>
<p>
<i>@Model.ProfileUser.Status</i>
<i>@Model.ProfileUser!.Status</i>
</p>
<div class="statsUnderTitle">
<i class="pink heart icon" title="Hearts"></i> <span>@Model.ProfileUser.Hearts</span>
@ -72,37 +74,31 @@
@foreach (Photo photo in Model.Photos)
{
<div class="eight wide column">
<img src="/gameAssets/@photo.LargeHash" style="width: 100%; height: auto; border-radius: .28571429rem;">
<br>
<p>
<b>Photo contains @photo.Subjects.Count @(photo.Subjects.Count == 1 ? "person" : "people"):</b>
</p>
@foreach (PhotoSubject subject in photo.Subjects)
{
<a href="/user/@subject.UserId">@subject.User.Username</a>
}
@await Html.PartialAsync("Partials/PhotoPartial", photo);
</div>
}
</div>
</div>
}
@if (Model.ProfileUser.Comments > 0)
{
<div class="ui yellow segment">
<h1>Comments</h1>
@foreach (Comment comment in Model.Comments!)
{
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000);
<div>
<b><a href="/user/@comment.PosterUserId">@comment.Poster.Username</a>: </b>
<span>@comment.Message</span>
<p>
<i>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</i>
</p>
<div class="ui divider"></div>
</div>
}
</div>
}
<div class="ui yellow segment">
<h1>Comments</h1>
@if (Model.ProfileUser.Comments == 0)
{
<p>There are no comments.</p>
}
@foreach (Comment comment in Model.Comments!)
{
DateTimeOffset timestamp = DateTimeOffset.FromUnixTimeSeconds(comment.Timestamp / 1000);
<div>
<b><a href="/user/@comment.PosterUserId">@comment.Poster.Username</a>: </b>
<span>@comment.Message</span>
<p>
<i>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</i>
</p>
<div class="ui divider"></div>
</div>
}
</div>

View file

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

View file

@ -8,34 +8,38 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BCrypt.Net-Next" Version="4.0.2"/>
<PackageReference Include="InfluxDB.Client" Version="3.1.0"/>
<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="BCrypt.Net-Next" Version="4.0.2" />
<PackageReference Include="InfluxDB.Client" Version="3.2.0" />
<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>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.0"/>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<None Remove="gitVersion.txt"/>
<None Remove="gitVersion.txt" />
<EmbeddedResource Include="gitVersion.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
<None Remove="gitBranch.txt"/>
<None Remove="gitBranch.txt" />
<EmbeddedResource Include="gitBranch.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<_ContentIncludedByDefault Remove="Pages\Admin\Index.cshtml" />
</ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="git describe --long --always --dirty --exclude=\* --abbrev=8 &gt; &quot;$(ProjectDir)/gitVersion.txt&quot;"/>
<Exec Command="git branch --show-current &gt; &quot;$(ProjectDir)/gitBranch.txt&quot;"/>
<Exec Command="git describe --long --always --dirty --exclude=\* --abbrev=8 &gt; &quot;$(ProjectDir)/gitVersion.txt&quot;" />
<Exec Command="git branch --show-current &gt; &quot;$(ProjectDir)/gitBranch.txt&quot;" />
</Target>
</Project>

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#da532c</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,223 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="602.000000pt" height="602.000000pt" viewBox="0 0 602.000000 602.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,602.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2730 5987 c-3 -3 -36 -8 -74 -11 -38 -3 -78 -8 -90 -11 -11 -2 -37
-7 -56 -9 -19 -3 -59 -10 -88 -17 -30 -6 -59 -12 -65 -13 -19 -4 -98 -25 -157
-43 -81 -23 -297 -102 -358 -129 -29 -13 -55 -24 -57 -24 -23 0 -388 -198
-405 -220 -3 -3 -23 -17 -45 -30 -22 -13 -44 -28 -50 -32 -5 -4 -44 -33 -85
-65 -419 -316 -809 -823 -995 -1292 -20 -52 -38 -99 -39 -105 -1 -6 -17 -56
-35 -111 -29 -89 -67 -235 -76 -290 -2 -11 -11 -63 -19 -115 -24 -148 -31
-248 -31 -480 0 -250 9 -358 42 -520 8 -41 17 -86 19 -100 3 -14 9 -38 14 -55
5 -16 25 -83 44 -147 19 -65 40 -126 46 -138 6 -11 8 -20 6 -20 -3 0 9 -33 25
-72 17 -40 36 -86 42 -103 7 -16 31 -68 54 -116 24 -48 43 -92 43 -98 0 -6 5
-11 10 -11 6 0 10 -5 10 -11 0 -14 130 -233 142 -237 4 -2 8 -8 8 -13 0 -16
175 -253 257 -346 210 -242 498 -461 813 -619 324 -163 816 -314 1135 -350 39
-5 162 -8 275 -9 211 0 311 7 448 30 17 3 44 7 60 9 46 7 203 46 277 68 39 12
75 22 80 24 33 6 248 79 349 118 245 95 519 254 704 410 210 176 279 250 514
550 13 16 26 38 30 48 4 10 12 18 18 18 5 0 10 5 10 11 0 5 18 37 40 69 22 32
40 61 40 64 0 3 13 24 29 48 39 58 152 276 196 379 20 46 40 93 45 104 42 94
128 383 144 485 3 19 8 46 11 60 4 25 8 49 22 165 4 33 7 159 8 280 0 242 -5
301 -41 505 -8 47 -17 99 -20 115 -3 17 -7 37 -10 45 -3 8 -8 26 -10 40 -15
83 -92 333 -134 435 -7 17 -16 39 -21 50 -24 60 -127 269 -161 330 -187 325
-447 639 -683 825 -271 214 -594 392 -913 504 -86 31 -316 88 -417 105 -79 13
-109 18 -145 25 -32 5 -101 15 -165 21 -27 3 -68 8 -90 11 -54 7 -470 16 -475
11z m513 -101 c15 -2 60 -7 100 -11 40 -4 81 -9 92 -11 11 -2 40 -6 65 -9 104
-12 314 -54 395 -79 6 -1 30 -8 55 -15 157 -43 424 -161 589 -261 115 -69 285
-190 344 -243 20 -19 37 -33 37 -31 0 6 141 -129 205 -196 194 -203 408 -502
521 -730 30 -60 103 -232 108 -255 1 -5 19 -62 40 -125 21 -63 39 -122 41
-130 2 -8 11 -44 20 -80 9 -36 19 -76 21 -90 3 -14 9 -50 14 -80 26 -148 33
-263 33 -570 0 -146 -2 -299 -6 -340 -11 -118 -19 -185 -23 -191 -1 -3 -6 -28
-10 -55 -3 -27 -8 -52 -10 -55 -2 -3 -6 -20 -9 -37 -7 -43 -85 -279 -123 -372
-39 -97 -104 -225 -162 -320 -51 -83 -142 -222 -150 -230 -3 -3 -51 -66 -107
-140 -166 -221 -300 -361 -503 -525 -87 -70 -276 -196 -360 -241 -30 -16 -56
-32 -58 -36 -2 -5 -8 -8 -12 -8 -5 0 -42 -15 -82 -34 -219 -103 -597 -217
-798 -241 -25 -3 -58 -8 -75 -10 -171 -24 -628 -24 -824 1 -46 5 -233 39 -266
48 -21 6 -86 23 -120 32 -29 8 -38 10 -55 16 -8 3 -51 18 -95 33 -44 15 -87
29 -95 33 -8 3 -17 6 -20 7 -3 1 -18 7 -35 14 -16 8 -39 17 -50 21 -33 13
-250 115 -255 120 -3 3 -30 18 -60 34 -81 42 -322 204 -420 282 -168 134 -337
316 -528 569 -4 6 -23 35 -42 65 -19 30 -37 57 -40 60 -28 25 -232 428 -224
442 3 4 2 8 -3 8 -4 0 -13 15 -19 33 -6 17 -32 93 -57 168 -42 123 -90 291
-102 358 -3 14 -7 35 -9 46 -2 11 -7 40 -11 65 -3 25 -9 59 -11 75 -17 104
-26 555 -11 555 4 0 5 4 2 10 -3 5 -1 45 4 87 6 43 13 101 16 128 4 28 8 58
11 67 5 16 10 41 19 88 2 14 12 52 21 85 8 33 17 67 19 75 8 36 96 292 125
365 39 98 141 309 152 313 4 2 8 8 8 13 0 21 163 263 247 368 200 249 403 437
663 612 123 83 153 101 280 165 130 65 195 94 212 94 8 0 18 4 23 9 12 11 278
97 325 105 19 3 45 8 58 11 53 12 68 15 92 20 40 7 94 15 131 20 19 2 52 6 74
9 87 11 133 16 210 22 44 3 81 7 83 9 5 4 350 -4 385 -9z"/>
<path d="M2839 5856 c-2 -2 -40 -6 -84 -10 -44 -4 -93 -9 -110 -11 -37 -6 -85
-12 -150 -20 -66 -8 -169 -26 -237 -41 -15 -3 -30 -5 -33 -5 -2 0 -23 -6 -45
-14 -22 -8 -41 -14 -43 -13 -1 0 -29 -7 -62 -17 -33 -10 -64 -19 -70 -21 -29
-6 -248 -100 -332 -143 -127 -63 -142 -72 -180 -98 -17 -13 -34 -23 -37 -23
-8 0 -131 -84 -134 -92 -2 -5 -10 -8 -17 -8 -7 0 -15 -3 -17 -7 -1 -5 -32 -30
-68 -58 -36 -27 -81 -63 -100 -80 -46 -39 -265 -259 -309 -310 -88 -101 -291
-391 -291 -415 0 -6 -4 -10 -9 -10 -5 0 -11 -8 -14 -17 -3 -10 -25 -52 -48
-93 -82 -148 -222 -511 -245 -636 -3 -21 -10 -46 -14 -54 -5 -8 -11 -32 -14
-54 -4 -21 -8 -42 -11 -46 -5 -8 -13 -54 -20 -120 -2 -19 -7 -51 -10 -70 -25
-161 -25 -590 0 -690 2 -8 6 -42 10 -75 10 -90 69 -331 115 -465 14 -42 58
-163 71 -195 7 -16 13 -35 14 -40 5 -23 112 -235 153 -305 100 -168 254 -385
347 -486 66 -73 166 -172 226 -224 41 -36 76 -67 79 -70 18 -18 180 -137 255
-187 100 -67 254 -153 360 -203 85 -39 289 -120 304 -120 6 0 19 -4 29 -10 32
-18 238 -74 352 -95 153 -29 138 -27 275 -41 150 -15 640 -8 697 11 7 2 36 6
63 9 103 12 302 57 444 100 119 37 328 118 379 147 18 10 38 19 43 19 32 0
377 227 499 328 68 57 261 250 315 315 22 27 45 54 50 60 6 7 39 50 73 97 35
47 70 94 79 104 32 40 183 266 183 275 0 5 6 16 14 24 12 12 123 235 146 292
4 11 15 38 24 60 29 73 75 229 100 340 24 108 28 132 43 265 9 82 9 643 -1
730 -4 36 -11 91 -17 123 -6 33 -12 73 -14 90 -2 18 -8 48 -13 67 -5 19 -10
40 -11 45 -11 54 -30 133 -51 210 -7 29 -68 198 -93 260 -31 77 -149 300 -207
390 -63 98 -224 316 -284 384 -132 149 -298 300 -476 430 -75 55 -334 215
-349 216 -3 1 -17 7 -31 15 -14 8 -29 15 -35 16 -5 2 -53 22 -105 44 -52 23
-99 43 -105 45 -5 1 -17 5 -25 8 -49 19 -243 75 -289 83 -14 3 -42 9 -61 14
-19 4 -69 14 -110 20 -41 6 -88 14 -105 17 -16 2 -46 6 -65 8 -19 2 -60 6 -90
9 -110 13 -120 13 -125 6 -2 -4 2 -38 10 -76 9 -38 24 -111 35 -162 11 -52 22
-97 24 -100 2 -4 7 -24 10 -44 3 -20 15 -77 25 -125 11 -48 22 -99 25 -113 3
-14 7 -34 10 -45 12 -51 25 -112 30 -135 14 -70 55 -257 60 -270 3 -8 7 -31
10 -51 3 -20 8 -42 10 -50 3 -8 8 -26 11 -41 6 -35 9 -50 35 -168 12 -52 28
-126 36 -165 8 -38 19 -86 24 -105 5 -19 12 -53 16 -75 3 -22 8 -43 9 -46 2
-3 13 -50 25 -105 12 -54 23 -108 26 -119 2 -11 7 -33 9 -50 3 -16 8 -38 11
-47 3 -9 7 -25 9 -35 2 -10 13 -63 25 -118 12 -55 23 -111 26 -125 3 -14 9
-41 14 -60 6 -19 10 -46 10 -59 0 -23 0 -23 -22 5 -38 49 -41 52 -108 119 -79
79 -126 120 -162 142 -16 10 -28 21 -28 25 0 4 -5 8 -11 8 -21 0 -68 40 -67
58 0 9 -2 40 -6 67 -3 28 -8 73 -11 100 -3 28 -7 66 -10 85 -2 19 -7 60 -10
90 -3 30 -8 66 -10 80 -2 14 -7 57 -11 95 -3 39 -8 79 -10 90 -3 11 -7 43 -9
70 -8 76 -16 147 -21 177 -5 32 -12 97 -19 184 -3 33 -8 71 -10 85 -3 13 -8
47 -10 74 -7 69 -14 128 -20 167 -2 18 -7 58 -10 88 -5 55 -9 83 -19 173 -6
51 -12 101 -21 187 -3 30 -8 69 -11 85 -2 17 -7 53 -10 80 -2 28 -7 73 -10
100 -3 28 -6 56 -6 63 -1 6 -6 12 -11 12 -9 0 -5 -113 8 -225 6 -46 14 -136
20 -215 2 -30 6 -73 9 -95 3 -22 8 -71 11 -110 3 -38 8 -89 11 -112 2 -23 7
-68 10 -100 14 -162 25 -269 30 -318 5 -42 10 -91 17 -185 5 -50 10 -105 23
-217 2 -23 7 -68 9 -100 3 -32 7 -76 9 -98 3 -22 8 -71 11 -110 4 -38 9 -86
11 -105 2 -19 7 -66 10 -103 6 -69 6 -69 -18 -61 -12 4 -47 12 -77 18 -42 9
-55 16 -57 31 -4 27 -16 245 -29 535 -3 74 -7 173 -10 220 -2 47 -7 141 -10
210 -6 142 -12 261 -20 435 -4 66 -8 163 -10 215 -11 286 -16 374 -20 379 -3
2 -6 -13 -6 -35 -1 -21 -3 -59 -4 -84 -2 -25 -6 -115 -9 -200 -9 -193 -15
-321 -31 -645 -2 -44 -6 -136 -9 -205 -4 -69 -8 -168 -11 -220 -3 -52 -7 -135
-10 -185 -2 -49 -7 -148 -11 -220 -3 -71 -7 -149 -8 -173 -3 -51 -9 -58 -61
-65 -22 -3 -53 -10 -68 -16 -26 -10 -31 -6 -29 19 3 33 15 164 18 195 6 64 13
135 19 200 5 58 8 92 20 200 3 28 7 75 10 105 3 30 7 75 10 100 8 74 14 141
20 210 3 36 7 79 9 95 2 17 7 68 11 115 4 47 8 92 10 100 2 8 6 53 10 99 4 47
8 92 10 100 1 9 6 52 10 96 16 186 23 259 30 315 3 19 7 70 9 113 3 43 8 82
11 87 3 6 -1 10 -9 10 -10 0 -16 -9 -17 -22 0 -13 -2 -36 -5 -53 -4 -30 -12
-107 -19 -175 -2 -19 -6 -51 -9 -70 -3 -19 -8 -60 -11 -90 -13 -119 -16 -146
-20 -170 -2 -14 -7 -52 -10 -85 -2 -33 -7 -71 -10 -85 -2 -14 -7 -48 -9 -75
-7 -77 -16 -150 -32 -278 -8 -65 -17 -139 -19 -165 -6 -63 -13 -126 -20 -187
-3 -27 -8 -68 -11 -90 -2 -22 -7 -56 -9 -75 -3 -19 -7 -60 -10 -90 -3 -30 -7
-71 -10 -90 -3 -19 -7 -55 -10 -80 -3 -25 -7 -61 -10 -80 -5 -45 -14 -123 -20
-180 -3 -25 -9 -46 -13 -48 -53 -17 -290 -223 -344 -298 -31 -44 -56 -59 -43
-26 4 9 9 28 11 42 8 47 16 82 44 210 11 52 22 104 24 115 2 11 23 106 46 210
23 105 44 199 46 210 2 11 6 30 8 43 3 12 7 25 9 30 3 4 8 28 11 55 4 26 11
60 17 75 6 15 10 28 9 30 -1 1 1 15 4 30 12 51 17 77 22 102 3 14 14 63 24
110 11 47 22 96 24 110 3 14 7 34 10 45 5 19 45 202 51 235 2 8 4 15 5 15 1 0
3 11 5 25 2 14 16 77 30 140 15 63 29 129 31 145 3 17 12 55 19 85 8 31 17 73
21 94 3 22 9 49 14 60 4 12 8 24 8 29 1 6 15 78 27 132 3 18 18 90 21 107 3
16 -8 32 -17 24z m-139 -135 c0 -8 -14 -83 -24 -126 -2 -11 -12 -56 -21 -100
-9 -44 -18 -87 -20 -95 -2 -8 -9 -37 -15 -65 -6 -27 -14 -61 -16 -75 -3 -14
-11 -53 -19 -88 -8 -34 -17 -77 -20 -95 -4 -17 -10 -50 -16 -72 -9 -39 -14
-61 -23 -107 -3 -13 -7 -32 -10 -43 -2 -11 -14 -63 -25 -115 -11 -52 -27 -124
-35 -160 -30 -136 -65 -296 -69 -320 -3 -14 -10 -43 -15 -65 -6 -22 -13 -53
-16 -70 -3 -16 -7 -39 -10 -50 -2 -11 -14 -65 -26 -120 -12 -55 -23 -104 -25
-110 -2 -11 -18 -86 -20 -95 -1 -3 -7 -31 -15 -62 l-13 -57 -51 4 c-28 2 -62
6 -76 9 -14 2 -47 7 -75 10 -27 3 -61 8 -75 11 -27 5 -81 13 -145 21 -22 3
-47 7 -55 9 -8 3 -32 7 -54 11 -21 3 -48 8 -60 10 -26 5 -133 25 -166 30 -14
3 -34 8 -45 11 -11 4 -29 7 -40 9 -158 20 -533 130 -692 204 -24 11 -45 20
-48 20 -2 0 -34 13 -70 30 -36 17 -70 30 -75 30 -5 0 -18 10 -30 22 -25 27
-24 31 51 183 62 126 70 140 106 192 13 17 23 34 23 37 0 15 149 219 238 326
322 387 781 695 1227 821 39 11 79 22 90 24 11 2 62 13 113 25 51 11 97 18
102 15 5 -3 11 -1 15 5 4 6 9 9 13 7 4 -2 43 2 87 8 98 16 145 18 145 6z m781
-11 c30 -5 70 -12 89 -15 19 -3 46 -7 60 -10 14 -3 41 -7 60 -10 195 -31 532
-159 730 -278 373 -223 660 -502 901 -875 37 -59 72 -112 76 -118 25 -32 171
-321 178 -349 1 -5 5 -17 8 -25 28 -67 28 -66 -16 -81 -23 -8 -44 -14 -47 -15
-3 0 -16 -5 -30 -11 -106 -44 -489 -165 -568 -179 -15 -3 -46 -9 -70 -15 -23
-5 -59 -12 -78 -15 -35 -5 -47 -7 -104 -18 -14 -3 -43 -8 -65 -11 -22 -3 -53
-7 -70 -10 -55 -9 -101 -14 -161 -20 -32 -4 -77 -9 -99 -12 -22 -3 -56 -6 -75
-8 -42 -4 -101 -11 -190 -21 -36 -4 -94 -8 -129 -8 l-64 -1 -14 65 c-7 36 -15
75 -17 86 -2 12 -16 76 -30 141 -15 66 -28 128 -30 139 -3 10 -10 42 -16 69
-6 28 -13 61 -15 75 -2 14 -6 34 -9 45 -13 57 -95 437 -102 475 -3 14 -16 77
-30 140 -14 63 -27 124 -29 135 -3 20 -33 161 -42 200 -2 11 -11 49 -18 85 -8
36 -17 76 -19 90 -3 14 -18 81 -32 150 -14 69 -28 134 -31 145 -16 73 -16 75
15 75 15 0 53 -4 83 -10z m-261 -2103 c59 -15 83 -24 144 -55 153 -80 330
-323 362 -497 23 -127 -9 -284 -64 -314 -17 -9 -72 -7 -142 5 -28 4 -51 -2
-118 -36 -135 -68 -250 -198 -267 -302 -5 -28 -13 -87 -20 -130 -2 -18 -7 -58
-10 -88 -3 -30 -8 -71 -10 -90 -8 -52 -9 -238 -1 -290 10 -77 19 -119 41 -210
9 -36 19 -76 21 -89 3 -13 6 -28 7 -34 1 -7 -32 -12 -93 -13 -52 -2 -141 -4
-197 -5 -57 -2 -105 0 -108 3 -3 3 6 57 20 119 14 63 28 124 30 136 19 84 97
310 156 456 82 198 94 267 59 344 -28 64 -68 84 -155 79 -103 -7 -355 -97
-355 -126 0 -14 -57 -31 -83 -24 -65 16 -133 161 -155 329 -35 265 98 536 341
692 109 71 256 129 352 140 28 3 52 7 54 9 4 4 170 -4 191 -9z m-971 -1072
c109 -139 140 -184 135 -199 -10 -31 -157 -248 -215 -319 -131 -158 -298 -334
-441 -463 l-56 -51 -103 3 c-57 2 -193 5 -302 8 -175 4 -222 8 -206 19 59 39
451 420 584 567 103 115 185 219 235 300 53 85 132 240 163 320 l17 44 24 -24
c13 -14 87 -106 165 -205z m1670 193 c24 -64 151 -314 171 -336 11 -11 20 -25
20 -30 0 -4 8 -17 18 -28 10 -10 29 -35 42 -54 13 -19 29 -40 35 -46 5 -6 39
-44 75 -85 159 -179 425 -445 600 -598 l35 -31 -30 1 c-16 1 -157 -2 -313 -6
l-282 -7 -53 49 c-78 71 -287 282 -332 334 -22 25 -58 67 -80 94 -22 26 -45
53 -51 59 -20 21 -142 201 -173 257 -31 55 -31 55 -11 80 10 13 65 83 121 154
56 72 109 137 118 147 9 9 27 30 40 47 13 17 27 31 31 31 4 0 13 -15 19 -32z
m-1183 -375 c16 -10 20 -22 18 -51 -1 -20 -5 -109 -9 -197 -3 -88 -8 -194 -10
-235 -3 -41 -7 -133 -10 -204 -3 -71 -10 -134 -15 -141 -5 -6 -20 -11 -32 -9
-13 1 -42 3 -65 3 l-41 1 2 175 c1 120 -2 171 -9 165 -5 -6 -66 -86 -135 -178
l-126 -167 -59 1 c-33 1 -93 2 -134 3 l-73 1 18 28 c10 15 24 33 32 41 8 8 40
49 71 90 31 42 58 78 61 81 7 6 93 127 137 193 35 52 114 206 127 247 26 82
53 195 55 227 1 12 151 -44 197 -74z m690 60 c3 -16 8 -34 10 -42 3 -8 7 -30
10 -50 8 -45 50 -162 82 -226 41 -82 147 -245 219 -335 89 -113 164 -213 166
-222 2 -13 -19 -15 -161 -19 -82 -2 -94 0 -106 17 -20 28 -152 206 -170 229
-9 11 -30 39 -47 63 -17 23 -35 42 -40 42 -6 0 -8 -4 -5 -9 7 -11 10 -317 3
-328 -7 -11 -137 -17 -138 -6 -7 59 -11 136 -34 680 l-6 142 58 27 c105 48
138 63 145 63 4 1 10 -11 14 -26z m-735 -1158 c33 -10 59 -35 59 -56 0 -32
-20 -31 -53 3 -30 32 -35 33 -80 28 -50 -6 -71 -21 -81 -57 -9 -28 21 -56 77
-72 74 -22 148 -61 156 -83 18 -48 -4 -105 -49 -132 -76 -44 -184 -15 -223 60
-14 28 -15 34 -2 44 16 14 35 5 35 -16 0 -57 132 -93 180 -49 23 21 27 77 6
94 -7 6 -44 22 -82 35 -38 13 -81 29 -96 36 -72 33 -52 147 30 170 30 8 87 6
123 -5z m769 -12 c0 -15 -6 -23 -17 -24 -10 0 -35 -1 -56 -2 -20 -1 -37 1 -37
5 0 4 -7 5 -16 1 -9 -3 -28 -6 -42 -7 -25 -1 -27 -4 -28 -53 -1 -29 1 -53 5
-54 3 0 44 -2 91 -5 105 -5 114 -8 101 -32 -9 -16 -21 -18 -104 -16 l-94 2 1
-66 1 -67 90 -2 c105 -3 114 -6 111 -30 -3 -16 -15 -18 -115 -18 -62 0 -117 3
-123 6 -7 4 -11 72 -12 189 -1 145 2 183 13 189 7 5 62 8 122 8 107 -1 109 -1
109 -24z m-1409 -24 c0 -10 1 -53 1 -94 0 -177 1 -173 -36 -208 -18 -18 -49
-37 -67 -43 -67 -20 -162 16 -187 71 -6 13 -11 68 -12 122 -2 174 -2 173 20
173 11 0 20 -3 21 -7 0 -5 1 -65 2 -136 3 -127 3 -128 32 -157 28 -27 56 -36
107 -31 9 0 29 14 45 30 29 28 29 30 31 162 2 73 5 136 8 140 8 14 34 -2 35
-22z m1875 14 c3 -10 10 -27 15 -38 4 -11 25 -60 45 -110 20 -49 39 -94 41
-99 7 -10 21 21 70 154 32 86 43 106 61 108 12 2 22 0 22 -5 0 -12 -47 -143
-54 -150 -3 -4 -6 -12 -6 -19 0 -26 -73 -184 -85 -184 -7 0 -19 10 -27 23 -20
32 -128 303 -128 322 0 21 39 19 46 -2z"/>
<path d="M2290 5390 c-7 -16 -18 -30 -26 -30 -25 0 -25 -20 0 -30 14 -5 31
-18 38 -30 15 -23 28 -26 28 -6 0 8 12 24 26 35 17 13 21 21 12 21 -15 0 -66
48 -60 57 2 2 2 7 -1 9 -2 3 -10 -9 -17 -26z"/>
<path d="M2357 5229 c-15 -17 -15 -19 6 -34 19 -13 22 -13 30 0 11 20 10 23
-6 40 -12 11 -16 11 -30 -6z"/>
<path d="M1910 5151 c0 -25 6 -27 13 -6 4 8 2 17 -3 20 -6 4 -10 -3 -10 -14z"/>
<path d="M1244 4883 c4 -8 2 -13 -5 -13 -10 0 -10 -3 -1 -12 8 -8 15 -9 22 -2
8 8 6 15 -6 25 -12 9 -15 10 -10 2z"/>
<path d="M1365 4660 c3 -5 8 -10 11 -10 2 0 4 5 4 10 0 6 -5 10 -11 10 -5 0
-7 -4 -4 -10z"/>
<path d="M1165 4630 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
-8 -4 -11 -10z"/>
<path d="M1114 4199 c-17 -18 -16 -20 3 -39 20 -18 21 -18 38 0 16 17 15 20
-3 38 -20 20 -21 20 -38 1z"/>
<path d="M1465 4160 c3 -5 8 -10 11 -10 2 0 4 5 4 10 0 6 -5 10 -11 10 -5 0
-7 -4 -4 -10z"/>
<path d="M4096 5151 c-4 -5 -2 -12 3 -15 5 -4 12 -2 15 3 4 5 2 12 -3 15 -5 4
-12 2 -15 -3z"/>
<path d="M4766 4873 c-11 -11 -6 -23 9 -23 8 0 15 4 15 9 0 13 -16 22 -24 14z"/>
<path d="M3815 4725 c-5 -11 -20 -30 -34 -41 l-25 -21 22 -7 c12 -4 30 -18 41
-32 19 -24 20 -24 26 -4 3 11 15 25 26 30 10 6 19 15 19 21 0 6 -4 8 -9 5 -9
-6 -46 31 -54 54 -4 12 -6 11 -12 -5z"/>
<path d="M4545 4160 c-3 -5 -1 -10 4 -10 6 0 11 5 11 10 0 6 -2 10 -4 10 -3 0
-8 -4 -11 -10z"/>
<path d="M4921 4116 c-11 -13 -10 -17 3 -22 22 -8 30 1 19 21 -9 15 -11 15
-22 1z"/>
<path d="M3037 2874 c-1 -1 -20 -5 -41 -8 -22 -4 -41 -8 -43 -10 -2 -1 8 -16
23 -32 32 -37 65 -101 94 -184 13 -36 27 -69 31 -74 14 -16 49 82 49 137 0 29
-10 80 -22 115 -21 61 -23 62 -55 60 -19 -1 -35 -3 -36 -4z"/>
<path d="M2810 2823 c-34 -13 -73 -48 -105 -96 -39 -58 -45 -83 -17 -69 14 7
116 25 217 38 29 3 29 3 44 13 11 7 10 14 -5 37 -41 65 -90 93 -134 77z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View file

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

View file

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

View file

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

View file

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

View file

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