diff --git a/ProjectLighthouse.sln.DotSettings b/ProjectLighthouse.sln.DotSettings index 7443a6e3..f6af5eef 100644 --- a/ProjectLighthouse.sln.DotSettings +++ b/ProjectLighthouse.sln.DotSettings @@ -11,5 +11,6 @@ True True True + True True True \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/LevelListController.cs b/ProjectLighthouse/Controllers/LevelListController.cs new file mode 100644 index 00000000..2088280a --- /dev/null +++ b/ProjectLighthouse/Controllers/LevelListController.cs @@ -0,0 +1,119 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using ProjectLighthouse.Serialization; +using ProjectLighthouse.Types; + +namespace ProjectLighthouse.Controllers { + [ApiController] + [Route("LITTLEBIGPLANETPS3_XML/")] + [Produces("text/xml")] + public class LevelListController : ControllerBase { + private readonly Database database; + public LevelListController(Database database) { + this.database = database; + } + + #region Level Queue (lolcatftw) + + [HttpGet("slots/lolcatftw/{username}")] + public IActionResult GetLevelQueue(string username) { + IEnumerable queuedLevels = new Database().QueuedLevels + .Include(q => q.User) + .Include(q => q.Slot) + .Include(q => q.Slot.Location) + .Where(q => q.User.Username == username) + .AsEnumerable(); + + string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize()); + + return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", 1)); + } + + [HttpPost("lolcatftw/remove/user/{id:int}")] + public async Task RemoveQueuedLevel(int id) { + User? user = await this.database.UserFromRequest(this.Request); + if(user == null) return this.StatusCode(403, ""); + + QueuedLevel queuedLevel = await this.database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); + if(queuedLevel != null) this.database.QueuedLevels.Remove(queuedLevel); + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + [HttpPost("lolcatftw/add/user/{id:int}")] + public async Task AddQueuedLevel(int id) { + User? user = await this.database.UserFromRequest(this.Request); + if(user == null) return this.StatusCode(403, ""); + + QueuedLevel queuedLevel = await this.database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); + if(queuedLevel != null) return this.Ok(); + + this.database.QueuedLevels.Add(new QueuedLevel { + SlotId = id, + UserId = user.UserId, + }); + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + #endregion + + #region Hearted Levels + + [HttpGet("favouriteSlots/{username}")] + public async Task GetFavouriteSlots(string username) { + IEnumerable heartedLevels = new Database().HeartedLevels + .Include(q => q.User) + .Include(q => q.Slot) + .Include(q => q.Slot.Location) + .Where(q => q.User.Username == username) + .AsEnumerable(); + + string response = heartedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize()); + + return this.Ok(LbpSerializer.TaggedStringElement("favouriteSlots", response, "total", 1)); + } + + [HttpPost("favourite/slot/user/{id:int}")] + public async Task AddFavourite(int id) { + User? user = await this.database.UserFromRequest(this.Request); + if(user == null) return this.StatusCode(403, ""); + + HeartedLevel heartedLevel = await this.database.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); + if(heartedLevel != null) return this.Ok(); + + this.database.HeartedLevels.Add(new HeartedLevel { + SlotId = id, + UserId = user.UserId, + }); + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + [HttpPost("unfavourite/slot/user/{id:int}")] + public async Task RemoveFavourite(int id) { + User? user = await this.database.UserFromRequest(this.Request); + if(user == null) return this.StatusCode(403, ""); + + HeartedLevel heartedLevel = await this.database.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); + if(heartedLevel != null) this.database.HeartedLevels.Remove(heartedLevel); + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + + #endregion + + } +} \ No newline at end of file diff --git a/ProjectLighthouse/Controllers/LevelQueueController.cs b/ProjectLighthouse/Controllers/LevelQueueController.cs deleted file mode 100644 index 85d464c8..00000000 --- a/ProjectLighthouse/Controllers/LevelQueueController.cs +++ /dev/null @@ -1,63 +0,0 @@ -#nullable enable -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using ProjectLighthouse.Serialization; -using ProjectLighthouse.Types; - -namespace ProjectLighthouse.Controllers { - [ApiController] - [Route("LITTLEBIGPLANETPS3_XML/")] - [Produces("text/xml")] - public class LevelQueueController : ControllerBase { - [HttpGet("slots/lolcatftw/{username}")] - public IActionResult GetLevelQueue(string username) { - IEnumerable queuedLevels = new Database().QueuedLevels - .Include(q => q.User) - .Include(q => q.Slot) - .Where(q => q.User.Username == username) - .AsEnumerable(); - - string response = queuedLevels.Aggregate(string.Empty, (current, q) => current + q.Slot.Serialize()); - - return this.Ok(LbpSerializer.TaggedStringElement("slots", response, "total", 1)); - } - - [HttpPost("lolcatftw/remove/user/{id:int}")] - public async Task RemoveQueuedLevel(int id) { - await using Database database = new(); - - User? user = await database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); - - QueuedLevel queuedLevel = await database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); - if(queuedLevel != null) database.QueuedLevels.Remove(queuedLevel); - - await database.SaveChangesAsync(); - - return this.Ok(); - } - - [HttpPost("lolcatftw/add/user/{id:int}")] - public async Task AddQueuedLevel(int id) { - await using Database database = new(); - - User? user = await database.UserFromRequest(this.Request); - if(user == null) return this.StatusCode(403, ""); - - QueuedLevel queuedLevel = await database.QueuedLevels.FirstOrDefaultAsync(q => q.UserId == user.UserId && q.SlotId == id); - if(queuedLevel != null) return this.Ok(); - - database.QueuedLevels.Add(new QueuedLevel { - SlotId = id, - UserId = user.UserId - }); - - await database.SaveChangesAsync(); - - return this.Ok(); - } - } -} \ No newline at end of file diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 295ddcd5..2182c8ec 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -10,6 +10,7 @@ namespace ProjectLighthouse { public DbSet Locations { get; set; } public DbSet Slots { get; set; } public DbSet QueuedLevels { get; set; } + public DbSet HeartedLevels { get; set; } public DbSet Comments { get; set; } public DbSet Tokens { get; set; } diff --git a/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.Designer.cs b/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.Designer.cs new file mode 100644 index 00000000..47f0519f --- /dev/null +++ b/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.Designer.cs @@ -0,0 +1,364 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ProjectLighthouse; + +namespace ProjectLighthouse.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20211019031221_HeartedLevels")] + partial class HeartedLevels + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.11"); + + modelBuilder.Entity("ProjectLighthouse.Types.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("ProjectLighthouse.Types.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("ProjectLighthouse.Types.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("ProjectLighthouse.Types.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("ProjectLighthouse.Types.Slot", b => + { + b.Property("SlotId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("AuthorLabels") + .HasColumnType("longtext"); + + b.Property("BackgroundHash") + .HasColumnType("longtext"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("InitiallyLocked") + .HasColumnType("tinyint(1)"); + + b.Property("Lbp1Only") + .HasColumnType("tinyint(1)"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("MaximumPlayers") + .HasColumnType("int"); + + b.Property("MinimumPlayers") + .HasColumnType("int"); + + b.Property("MoveRequired") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("Resource") + .HasColumnType("longtext"); + + b.Property("RootLevel") + .HasColumnType("longtext"); + + b.Property("Shareable") + .HasColumnType("int"); + + b.Property("SubLevel") + .HasColumnType("tinyint(1)"); + + b.HasKey("SlotId"); + + b.HasIndex("CreatorId"); + + b.HasIndex("LocationId"); + + b.ToTable("Slots"); + }); + + modelBuilder.Entity("ProjectLighthouse.Types.Token", b => + { + b.Property("TokenId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.Property("UserToken") + .HasColumnType("longtext"); + + b.HasKey("TokenId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("ProjectLighthouse.Types.User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("Biography") + .HasColumnType("longtext"); + + b.Property("BooHash") + .HasColumnType("longtext"); + + b.Property("CommentCount") + .HasColumnType("int"); + + b.Property("CommentsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("FavouriteSlotCount") + .HasColumnType("int"); + + b.Property("FavouriteUserCount") + .HasColumnType("int"); + + b.Property("Game") + .HasColumnType("int"); + + b.Property("HeartCount") + .HasColumnType("int"); + + b.Property("IconHash") + .HasColumnType("longtext"); + + b.Property("Lists") + .HasColumnType("int"); + + b.Property("LocationId") + .HasColumnType("int"); + + b.Property("LolCatFtwCount") + .HasColumnType("int"); + + b.Property("PhotosByMeCount") + .HasColumnType("int"); + + b.Property("PhotosWithMeCount") + .HasColumnType("int"); + + b.Property("Pins") + .HasColumnType("longtext"); + + b.Property("PlanetHash") + .HasColumnType("longtext"); + + b.Property("ReviewCount") + .HasColumnType("int"); + + b.Property("StaffChallengeBronzeCount") + .HasColumnType("int"); + + b.Property("StaffChallengeGoldCount") + .HasColumnType("int"); + + b.Property("StaffChallengeSilverCount") + .HasColumnType("int"); + + b.Property("UsedSlots") + .HasColumnType("int"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.Property("YayHash") + .HasColumnType("longtext"); + + b.HasKey("UserId"); + + b.HasIndex("LocationId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ProjectLighthouse.Types.Comment", b => + { + b.HasOne("ProjectLighthouse.Types.User", "Poster") + .WithMany() + .HasForeignKey("PosterUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ProjectLighthouse.Types.User", "Target") + .WithMany() + .HasForeignKey("TargetUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Poster"); + + b.Navigation("Target"); + }); + + modelBuilder.Entity("ProjectLighthouse.Types.HeartedLevel", b => + { + b.HasOne("ProjectLighthouse.Types.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectLighthouse.Types.QueuedLevel", b => + { + b.HasOne("ProjectLighthouse.Types.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("ProjectLighthouse.Types.Slot", b => + { + b.HasOne("ProjectLighthouse.Types.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ProjectLighthouse.Types.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + + b.Navigation("Location"); + }); + + modelBuilder.Entity("ProjectLighthouse.Types.User", b => + { + b.HasOne("ProjectLighthouse.Types.Location", "Location") + .WithMany() + .HasForeignKey("LocationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Location"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.cs b/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.cs new file mode 100644 index 00000000..7c19023e --- /dev/null +++ b/ProjectLighthouse/Migrations/20211019031221_HeartedLevels.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace ProjectLighthouse.Migrations +{ + public partial class HeartedLevels : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "HeartedLevels", + columns: table => new + { + HeartedLevelId = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "int", nullable: false), + SlotId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_HeartedLevels", x => x.HeartedLevelId); + table.ForeignKey( + name: "FK_HeartedLevels_Slots_SlotId", + column: x => x.SlotId, + principalTable: "Slots", + principalColumn: "SlotId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_HeartedLevels_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "UserId", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_HeartedLevels_SlotId", + table: "HeartedLevels", + column: "SlotId"); + + migrationBuilder.CreateIndex( + name: "IX_HeartedLevels_UserId", + table: "HeartedLevels", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "HeartedLevels"); + } + } +} diff --git a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 98961477..3d40b0e8 100644 --- a/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -49,6 +49,27 @@ namespace ProjectLighthouse.Migrations b.ToTable("Comments"); }); + modelBuilder.Entity("ProjectLighthouse.Types.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("ProjectLighthouse.Types.Location", b => { b.Property("Id") @@ -268,6 +289,25 @@ namespace ProjectLighthouse.Migrations b.Navigation("Target"); }); + modelBuilder.Entity("ProjectLighthouse.Types.HeartedLevel", b => + { + b.HasOne("ProjectLighthouse.Types.Slot", "Slot") + .WithMany() + .HasForeignKey("SlotId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ProjectLighthouse.Types.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Slot"); + + b.Navigation("User"); + }); + modelBuilder.Entity("ProjectLighthouse.Types.QueuedLevel", b => { b.HasOne("ProjectLighthouse.Types.Slot", "Slot") diff --git a/ProjectLighthouse/Types/HeartedLevel.cs b/ProjectLighthouse/Types/HeartedLevel.cs new file mode 100644 index 00000000..09b99c0b --- /dev/null +++ b/ProjectLighthouse/Types/HeartedLevel.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace ProjectLighthouse.Types { + public class HeartedLevel { + [Key] public int HeartedLevelId { get; set; } + + public int UserId { get; set; } + + [ForeignKey(nameof(UserId))] public User User { get; set; } + + public int SlotId { get; set; } + + [ForeignKey(nameof(SlotId))] public Slot Slot { get; set; } + } +} \ No newline at end of file