Add database changes for allowing multiple resources

This commit is contained in:
jvyden 2021-10-20 18:09:34 -04:00
parent 685f57fd28
commit e63a4b812a
No known key found for this signature in database
GPG key ID: 18BCF2BE0262B278
8 changed files with 442 additions and 13 deletions

View file

@ -25,7 +25,7 @@ namespace ProjectLighthouse.Controllers {
Slot slot = await this.GetSlotFromBody(); Slot slot = await this.GetSlotFromBody();
if(slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded if(slot == null) return this.BadRequest(); // if the level cant be parsed then it obviously cant be uploaded
string resource = LbpSerializer.StringElement("resource", slot.Resource); string resource = LbpSerializer.StringElement("resource", slot.Resources);
return this.Ok(LbpSerializer.TaggedStringElement("slot", resource, "type", "user")); return this.Ok(LbpSerializer.TaggedStringElement("slot", resource, "type", "user"));
} }
@ -69,7 +69,7 @@ namespace ProjectLighthouse.Controllers {
return this.Ok(); return this.Ok();
} }
public async Task<Slot> GetSlotFromBody() { public async Task<Slot> GetSlotFromBody() {
Request.Body.Position = 0; Request.Body.Position = 0;
string bodyString = await new StreamReader(Request.Body).ReadToEndAsync(); string bodyString = await new StreamReader(Request.Body).ReadToEndAsync();

View file

@ -1,4 +1,3 @@
using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -26,10 +25,10 @@ namespace ProjectLighthouse.Controllers {
[HttpGet("r/{hash}")] [HttpGet("r/{hash}")]
public IActionResult GetResource(string hash) { public IActionResult GetResource(string hash) {
string path = Path.Combine(Environment.CurrentDirectory, "r", hash); string path = FileHelper.GetResourcePath(hash);
if(IOFile.Exists(path)) { if(FileHelper.ResourceExists(hash)) {
return this.File(IOFile.OpenRead(path), "image/jpg"); return this.File(IOFile.OpenRead(path), "application/octet-stream");
} }
return this.NotFound(); return this.NotFound();
} }
@ -37,11 +36,11 @@ namespace ProjectLighthouse.Controllers {
// TODO: check if this is a valid hash // TODO: check if this is a valid hash
[HttpPost("upload/{hash}")] [HttpPost("upload/{hash}")]
public async Task<IActionResult> UploadResource(string hash) { public async Task<IActionResult> UploadResource(string hash) {
string assetsDirectory = Path.Combine(Environment.CurrentDirectory, "r"); string assetsDirectory = FileHelper.ResourcePath;
string path = Path.Combine(assetsDirectory, hash); string path = FileHelper.GetResourcePath(hash);
if(!Directory.Exists(assetsDirectory)) Directory.CreateDirectory(assetsDirectory); FileHelper.EnsureDirectoryCreated(assetsDirectory);
if(IOFile.Exists(path)) this.Ok(); // no reason to fail if it's already uploaded if(FileHelper.ResourceExists(hash)) this.Ok(); // no reason to fail if it's already uploaded
LbpFile file = new(Encoding.ASCII.GetBytes(await new StreamReader(Request.Body).ReadToEndAsync())); LbpFile file = new(Encoding.ASCII.GetBytes(await new StreamReader(Request.Body).ReadToEndAsync()));

View file

@ -22,6 +22,14 @@ namespace ProjectLighthouse {
MySqlServerVersion.LatestSupportedServerVersion MySqlServerVersion.LatestSupportedServerVersion
); );
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Slot>()
.Property<string>(nameof(Slot.ResourceCollection))
.HasField(nameof(Slot.ResourceCollection));
}
public async Task<User> CreateUser(string username) { public async Task<User> CreateUser(string username) {
User user; User user;
if((user = await Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null) if((user = await Users.Where(u => u.Username == username).FirstOrDefaultAsync()) != null)

View file

@ -1,10 +1,16 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using ProjectLighthouse.Types.Files; using ProjectLighthouse.Types.Files;
namespace ProjectLighthouse.Helpers { namespace ProjectLighthouse.Helpers {
public static class FileHelper { public static class FileHelper {
public static readonly string ResourcePath = Path.Combine(Environment.CurrentDirectory, "r");
public static string GetResourcePath(string hash) => Path.Combine(ResourcePath, hash);
public static bool IsFileSafe(LbpFile file) { public static bool IsFileSafe(LbpFile file) {
if(file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data); if(file.FileType == LbpFileType.Unknown) file.FileType = DetermineFileType(file.Data);
@ -38,5 +44,13 @@ namespace ProjectLighthouse.Helpers {
_ => LbpFileType.Unknown, _ => LbpFileType.Unknown,
}; };
} }
public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash));
public static void EnsureDirectoryCreated(string path) {
if(!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path)));
}
public static string[] ResourcesNotUploaded(params string[] hashes) => hashes.Where(hash => !ResourceExists(hash)).ToArray();
} }
} }

View file

@ -0,0 +1,378 @@
// <auto-generated />
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("20211020220840_ResourceList")]
partial class ResourceList
{
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<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("ProjectLighthouse.Types.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("ProjectLighthouse.Types.LastMatch", b =>
{
b.Property<int>("UserId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<long>("Timestamp")
.HasColumnType("bigint");
b.HasKey("UserId");
b.ToTable("LastMatches");
});
modelBuilder.Entity("ProjectLighthouse.Types.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("ProjectLighthouse.Types.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("ProjectLighthouse.Types.Slot", b =>
{
b.Property<int>("SlotId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("AuthorLabels")
.HasColumnType("longtext");
b.Property<string>("BackgroundHash")
.HasColumnType("longtext");
b.Property<int>("CreatorId")
.HasColumnType("int");
b.Property<string>("Description")
.HasColumnType("longtext");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<bool>("InitiallyLocked")
.HasColumnType("tinyint(1)");
b.Property<bool>("Lbp1Only")
.HasColumnType("tinyint(1)");
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")
.HasColumnType("longtext");
b.Property<string>("ResourceCollection")
.HasColumnType("longtext");
b.Property<string>("RootLevel")
.HasColumnType("longtext");
b.Property<int>("Shareable")
.HasColumnType("int");
b.Property<bool>("SubLevel")
.HasColumnType("tinyint(1)");
b.HasKey("SlotId");
b.HasIndex("CreatorId");
b.HasIndex("LocationId");
b.ToTable("Slots");
});
modelBuilder.Entity("ProjectLighthouse.Types.Token", 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("Tokens");
});
modelBuilder.Entity("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>("CommentCount")
.HasColumnType("int");
b.Property<bool>("CommentsEnabled")
.HasColumnType("tinyint(1)");
b.Property<int>("FavouriteSlotCount")
.HasColumnType("int");
b.Property<int>("FavouriteUserCount")
.HasColumnType("int");
b.Property<int>("Game")
.HasColumnType("int");
b.Property<int>("HeartCount")
.HasColumnType("int");
b.Property<string>("IconHash")
.HasColumnType("longtext");
b.Property<int>("Lists")
.HasColumnType("int");
b.Property<int>("LocationId")
.HasColumnType("int");
b.Property<int>("LolCatFtwCount")
.HasColumnType("int");
b.Property<int>("PhotosByMeCount")
.HasColumnType("int");
b.Property<int>("PhotosWithMeCount")
.HasColumnType("int");
b.Property<string>("Pins")
.HasColumnType("longtext");
b.Property<string>("PlanetHash")
.HasColumnType("longtext");
b.Property<int>("ReviewCount")
.HasColumnType("int");
b.Property<int>("StaffChallengeBronzeCount")
.HasColumnType("int");
b.Property<int>("StaffChallengeGoldCount")
.HasColumnType("int");
b.Property<int>("StaffChallengeSilverCount")
.HasColumnType("int");
b.Property<int>("UsedSlots")
.HasColumnType("int");
b.Property<string>("Username")
.HasColumnType("longtext");
b.Property<string>("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
}
}
}

View file

@ -0,0 +1,23 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace ProjectLighthouse.Migrations
{
public partial class ResourceList : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Resource",
table: "Slots",
newName: "ResourceCollection");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "ResourceCollection",
table: "Slots",
newName: "Resource");
}
}
}

View file

@ -164,7 +164,7 @@ namespace ProjectLighthouse.Migrations
b.Property<string>("Name") b.Property<string>("Name")
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<string>("Resource") b.Property<string>("ResourceCollection")
.HasColumnType("longtext"); .HasColumnType("longtext");
b.Property<string>("RootLevel") b.Property<string>("RootLevel")

View file

@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Xml.Serialization; using System.Xml.Serialization;
@ -29,9 +30,15 @@ namespace ProjectLighthouse.Types {
[XmlElement("rootLevel")] [XmlElement("rootLevel")]
public string RootLevel { get; set; } public string RootLevel { get; set; }
public string ResourceCollection;
[NotMapped]
[XmlElement("resource")] [XmlElement("resource")]
public string Resource { get; set; } public string[] Resources {
get => this.ResourceCollection.Split(",");
set => this.ResourceCollection = string.Join(',', value);
}
[XmlIgnore] [XmlIgnore]
public int LocationId { get; set; } public int LocationId { get; set; }
@ -84,7 +91,7 @@ namespace ProjectLighthouse.Types {
LbpSerializer.StringElement("description", Description) + LbpSerializer.StringElement("description", Description) +
LbpSerializer.StringElement("icon", IconHash) + LbpSerializer.StringElement("icon", IconHash) +
LbpSerializer.StringElement("rootLevel", RootLevel) + LbpSerializer.StringElement("rootLevel", RootLevel) +
LbpSerializer.StringElement("resource", Resource) + LbpSerializer.StringElement("resource", this.Resources) +
LbpSerializer.StringElement("location", Location.Serialize()) + LbpSerializer.StringElement("location", Location.Serialize()) +
LbpSerializer.StringElement("initiallyLocked", InitiallyLocked) + LbpSerializer.StringElement("initiallyLocked", InitiallyLocked) +
LbpSerializer.StringElement("isSubLevel", SubLevel) + LbpSerializer.StringElement("isSubLevel", SubLevel) +