diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs
index 3c25974ef..54d304dd6 100644
--- a/API/Controllers/AccountController.cs
+++ b/API/Controllers/AccountController.cs
@@ -78,6 +78,8 @@ namespace API.Controllers
user.LastActive = DateTime.Now;
_userRepository.Update(user);
await _userRepository.SaveAllAsync();
+
+ _logger.LogInformation($"{user.UserName} logged in at {user.LastActive}");
return new UserDto
{
diff --git a/API/Controllers/LibraryController.cs b/API/Controllers/LibraryController.cs
index e06cba530..b72094f17 100644
--- a/API/Controllers/LibraryController.cs
+++ b/API/Controllers/LibraryController.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading.Tasks;
using API.DTOs;
using API.Entities;
@@ -35,6 +36,48 @@ namespace API.Controllers
_taskScheduler = taskScheduler;
_seriesRepository = seriesRepository;
}
+
+ ///
+ /// Creates a new Library. Upon library creation, adds new library to all Admin accounts.
+ ///
+ ///
+ ///
+ [Authorize(Policy = "RequireAdminRole")]
+ [HttpPost("create")]
+ public async Task AddLibrary(CreateLibraryDto createLibraryDto)
+ {
+ if (await _libraryRepository.LibraryExists(createLibraryDto.Name))
+ {
+ return BadRequest("Library name already exists. Please choose a unique name to the server.");
+ }
+
+ var admins = (await _userRepository.GetAdminUsersAsync()).ToList();
+
+ var library = new Library
+ {
+ Name = createLibraryDto.Name,
+ Type = createLibraryDto.Type,
+ AppUsers = admins,
+ Folders = createLibraryDto.Folders.Select(x => new FolderPath {Path = x}).ToList()
+ };
+
+ foreach (var admin in admins)
+ {
+ // If user is null, then set it
+ admin.Libraries ??= new List();
+ admin.Libraries.Add(library);
+ }
+
+
+ if (await _userRepository.SaveAllAsync())
+ {
+ var createdLibrary = await _libraryRepository.GetLibraryForNameAsync(library.Name);
+ BackgroundJob.Enqueue(() => _directoryService.ScanLibrary(createdLibrary.Id));
+ return Ok();
+ }
+
+ return BadRequest("There was a critical issue. Please try again.");
+ }
///
/// Returns a list of directories for a given path. If path is empty, returns root drives.
@@ -85,28 +128,30 @@ namespace API.Controllers
}
[Authorize(Policy = "RequireAdminRole")]
- [HttpGet("scan")]
- public async Task ScanLibrary(int libraryId)
+ [HttpPost("scan")]
+ public ActionResult ScanLibrary(int libraryId)
{
- var library = await _libraryRepository.GetLibraryDtoForIdAsync(libraryId);
-
- // We have to send a json encoded Library (aka a DTO) to the Background Job thread.
- // Because we use EF, we have circular dependencies back to Library and it will crap out
- BackgroundJob.Enqueue(() => _directoryService.ScanLibrary(library));
+ BackgroundJob.Enqueue(() => _directoryService.ScanLibrary(libraryId));
return Ok();
}
[HttpGet("libraries-for")]
public async Task>> GetLibrariesForUser(string username)
{
- return Ok(await _libraryRepository.GetLibrariesForUsernameAysnc(username));
+ return Ok(await _libraryRepository.GetLibrariesDtoForUsernameAsync(username));
}
[HttpGet("series")]
public async Task>> GetSeriesForLibrary(int libraryId)
{
- return Ok(await _seriesRepository.GetSeriesForLibraryIdAsync(libraryId));
-
+ return Ok(await _seriesRepository.GetSeriesDtoForLibraryIdAsync(libraryId));
}
+
+ [Authorize(Policy = "RequireAdminRole")]
+ [HttpDelete("delete")]
+ public async Task> DeleteLibrary(int libraryId)
+ {
+ return Ok(await _libraryRepository.DeleteLibrary(libraryId));
+ }
}
}
\ No newline at end of file
diff --git a/API/Controllers/SeriesController.cs b/API/Controllers/SeriesController.cs
index dbdfaf93e..4c5e66a41 100644
--- a/API/Controllers/SeriesController.cs
+++ b/API/Controllers/SeriesController.cs
@@ -27,13 +27,13 @@ namespace API.Controllers
[HttpGet("{seriesId}")]
public async Task> GetSeries(int seriesId)
{
- return Ok(await _seriesRepository.GetSeriesByIdAsync(seriesId));
+ return Ok(await _seriesRepository.GetSeriesDtoByIdAsync(seriesId));
}
[HttpGet("volumes")]
public async Task>> GetVolumes(int seriesId)
{
- return Ok(await _seriesRepository.GetVolumesAsync(seriesId));
+ return Ok(await _seriesRepository.GetVolumesDtoAsync(seriesId));
}
}
}
\ No newline at end of file
diff --git a/API/Controllers/UsersController.cs b/API/Controllers/UsersController.cs
index db81af51d..535345b75 100644
--- a/API/Controllers/UsersController.cs
+++ b/API/Controllers/UsersController.cs
@@ -2,7 +2,6 @@
using System.Linq;
using System.Threading.Tasks;
using API.DTOs;
-using API.Entities;
using API.Extensions;
using API.Interfaces;
using Microsoft.AspNetCore.Authorization;
@@ -22,47 +21,6 @@ namespace API.Controllers
_libraryRepository = libraryRepository;
}
- [Authorize(Policy = "RequireAdminRole")]
- [HttpPost("add-library")]
- public async Task AddLibrary(CreateLibraryDto createLibraryDto)
- {
- // NOTE: I think we should move this into library controller because it gets added to all admins
-
- var user = await _userRepository.GetUserByUsernameAsync(User.GetUsername());
-
- if (user == null) return BadRequest("Could not validate user");
-
-
- if (await _libraryRepository.LibraryExists(createLibraryDto.Name))
- {
- return BadRequest("Library name already exists. Please choose a unique name to the server.");
- }
-
- var library = new Library
- {
- Name = createLibraryDto.Name.ToLower(),
- Type = createLibraryDto.Type,
- AppUsers = new List() { user }
- };
-
- library.Folders = createLibraryDto.Folders.Select(x => new FolderPath
- {
- Path = x,
- Library = library
- }).ToList();
-
- user.Libraries ??= new List(); // If user is null, then set it
-
- user.Libraries.Add(library);
-
- if (await _userRepository.SaveAllAsync())
- {
- return Ok();
- }
-
- return BadRequest("Not implemented");
- }
-
[Authorize(Policy = "RequireAdminRole")]
[HttpDelete("delete-user")]
public async Task DeleteUser(string username)
@@ -84,5 +42,17 @@ namespace API.Controllers
{
return Ok(await _userRepository.GetMembersAsync());
}
+
+ [HttpGet("has-library-access")]
+ public async Task> HasLibraryAccess(int libraryId)
+ {
+ var user = await _userRepository.GetUserByUsernameAsync(User.GetUsername());
+
+ if (user == null) return BadRequest("Could not validate user");
+
+ var libs = await _libraryRepository.GetLibrariesDtoForUsernameAsync(user.UserName);
+
+ return Ok(libs.Any(x => x.Id == libraryId));
+ }
}
}
\ No newline at end of file
diff --git a/API/DTOs/VolumeDto.cs b/API/DTOs/VolumeDto.cs
index 1753d4679..b3dab2834 100644
--- a/API/DTOs/VolumeDto.cs
+++ b/API/DTOs/VolumeDto.cs
@@ -5,7 +5,8 @@ namespace API.DTOs
public class VolumeDto
{
public int Id { get; set; }
- public string Number { get; set; }
+ public int Number { get; set; }
+ public string Name { get; set; }
public string CoverImage { get; set; }
public ICollection Files { get; set; }
}
diff --git a/API/Data/DataContext.cs b/API/Data/DataContext.cs
index a83a76e9e..7a75ad138 100644
--- a/API/Data/DataContext.cs
+++ b/API/Data/DataContext.cs
@@ -1,16 +1,21 @@
-using API.Entities;
+using System;
+using API.Entities;
+using API.Entities.Interfaces;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace API.Data
{
- public class DataContext : IdentityDbContext, AppUserRole, IdentityUserLogin,
IdentityRoleClaim, IdentityUserToken>
{
public DataContext(DbContextOptions options) : base(options)
{
+ ChangeTracker.Tracked += OnEntityTracked;
+ ChangeTracker.StateChanged += OnEntityStateChanged;
}
public DbSet Library { get; set; }
@@ -33,12 +38,18 @@ namespace API.Data
.WithOne(u => u.Role)
.HasForeignKey(ur => ur.RoleId)
.IsRequired();
-
- // builder.Entity()
- // .HasMany(s => s.Series)
- // .WithOne(l => l.Library)
- // .HasForeignKey(x => x.Id)
-
+ }
+
+ void OnEntityTracked(object sender, EntityTrackedEventArgs e)
+ {
+ if (!e.FromQuery && e.Entry.State == EntityState.Added && e.Entry.Entity is IEntityDate entity)
+ entity.Created = DateTime.Now;
+ }
+
+ void OnEntityStateChanged(object sender, EntityStateChangedEventArgs e)
+ {
+ if (e.NewState == EntityState.Modified && e.Entry.Entity is IEntityDate entity)
+ entity.LastModified = DateTime.Now;
}
}
}
\ No newline at end of file
diff --git a/API/Data/LibraryRepository.cs b/API/Data/LibraryRepository.cs
index ce986279f..9c2c74b29 100644
--- a/API/Data/LibraryRepository.cs
+++ b/API/Data/LibraryRepository.cs
@@ -36,16 +36,7 @@ namespace API.Data
return _context.SaveChanges() > 0;
}
- public Library GetLibraryForName(string libraryName)
- {
- return _context.Library
- .Where(x => x.Name == libraryName)
- .Include(f => f.Folders)
- .Include(s => s.Series)
- .Single();
- }
-
- public async Task> GetLibrariesForUsernameAysnc(string userName)
+ public async Task> GetLibrariesDtoForUsernameAsync(string userName)
{
return await _context.Library
.Include(l => l.AppUsers)
@@ -53,21 +44,29 @@ namespace API.Data
.ProjectTo(_mapper.ConfigurationProvider).ToListAsync();
}
+ public async Task GetLibraryForNameAsync(string libraryName)
+ {
+ return await _context.Library
+ .Where(x => x.Name == libraryName)
+ .Include(f => f.Folders)
+ .Include(s => s.Series)
+ .SingleAsync();
+ }
+
+ public async Task DeleteLibrary(int libraryId)
+ {
+ var library = await GetLibraryForIdAsync(libraryId);
+ _context.Library.Remove(library);
+ return await _context.SaveChangesAsync() > 0;
+ }
+
public async Task> GetLibrariesAsync()
{
return await _context.Library
.Include(f => f.Folders)
.ProjectTo(_mapper.ConfigurationProvider).ToListAsync();
}
-
- public async Task GetLibraryDtoForIdAsync(int libraryId)
- {
- return await _context.Library
- .Where(x => x.Id == libraryId)
- .Include(f => f.Folders)
- .ProjectTo(_mapper.ConfigurationProvider).SingleAsync();
- }
-
+
public async Task GetLibraryForIdAsync(int libraryId)
{
return await _context.Library
diff --git a/API/Data/Migrations/20210102165536_EntityTimestamps.Designer.cs b/API/Data/Migrations/20210102165536_EntityTimestamps.Designer.cs
new file mode 100644
index 000000000..de4910b51
--- /dev/null
+++ b/API/Data/Migrations/20210102165536_EntityTimestamps.Designer.cs
@@ -0,0 +1,509 @@
+//
+using System;
+using API.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace API.Data.Migrations
+{
+ [DbContext(typeof(DataContext))]
+ [Migration("20210102165536_EntityTimestamps")]
+ partial class EntityTimestamps
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "5.0.1");
+
+ modelBuilder.Entity("API.Entities.AppRole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex");
+
+ b.ToTable("AspNetRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsAdmin")
+ .HasColumnType("INTEGER");
+
+ b.Property("LastActive")
+ .HasColumnType("TEXT");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("PasswordHash")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("TEXT");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex");
+
+ b.ToTable("AspNetUsers");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.Property("RoleId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.FolderPath", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("LibraryId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Path")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LibraryId");
+
+ b.ToTable("FolderPath");
+ });
+
+ modelBuilder.Entity("API.Entities.Library", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CoverImage")
+ .HasColumnType("TEXT");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.ToTable("Library");
+ });
+
+ modelBuilder.Entity("API.Entities.MangaFile", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("FilePath")
+ .HasColumnType("TEXT");
+
+ b.Property("VolumeId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("VolumeId");
+
+ b.ToTable("MangaFile");
+ });
+
+ modelBuilder.Entity("API.Entities.Series", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CoverImage")
+ .HasColumnType("TEXT");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("LibraryId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("OriginalName")
+ .HasColumnType("TEXT");
+
+ b.Property("SortName")
+ .HasColumnType("TEXT");
+
+ b.Property("Summary")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LibraryId");
+
+ b.ToTable("Series");
+ });
+
+ modelBuilder.Entity("API.Entities.Volume", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("Number")
+ .HasColumnType("TEXT");
+
+ b.Property("SeriesId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SeriesId");
+
+ b.ToTable("Volume");
+ });
+
+ modelBuilder.Entity("AppUserLibrary", b =>
+ {
+ b.Property("AppUsersId")
+ .HasColumnType("INTEGER");
+
+ b.Property("LibrariesId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("AppUsersId", "LibrariesId");
+
+ b.HasIndex("LibrariesId");
+
+ b.ToTable("AppUserLibrary");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("RoleId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasColumnType("TEXT");
+
+ b.Property("ProviderKey")
+ .HasColumnType("TEXT");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.Property("LoginProvider")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .HasColumnType("TEXT");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUserRole", b =>
+ {
+ b.HasOne("API.Entities.AppRole", "Role")
+ .WithMany("UserRoles")
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Entities.AppUser", "User")
+ .WithMany("UserRoles")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Role");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("API.Entities.FolderPath", b =>
+ {
+ b.HasOne("API.Entities.Library", "Library")
+ .WithMany("Folders")
+ .HasForeignKey("LibraryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Library");
+ });
+
+ modelBuilder.Entity("API.Entities.MangaFile", b =>
+ {
+ b.HasOne("API.Entities.Volume", "Volume")
+ .WithMany("Files")
+ .HasForeignKey("VolumeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Volume");
+ });
+
+ modelBuilder.Entity("API.Entities.Series", b =>
+ {
+ b.HasOne("API.Entities.Library", "Library")
+ .WithMany("Series")
+ .HasForeignKey("LibraryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Library");
+ });
+
+ modelBuilder.Entity("API.Entities.Volume", b =>
+ {
+ b.HasOne("API.Entities.Series", "Series")
+ .WithMany("Volumes")
+ .HasForeignKey("SeriesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Series");
+ });
+
+ modelBuilder.Entity("AppUserLibrary", b =>
+ {
+ b.HasOne("API.Entities.AppUser", null)
+ .WithMany()
+ .HasForeignKey("AppUsersId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Entities.Library", null)
+ .WithMany()
+ .HasForeignKey("LibrariesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.HasOne("API.Entities.AppRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.HasOne("API.Entities.AppUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.HasOne("API.Entities.AppUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.HasOne("API.Entities.AppUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("API.Entities.AppRole", b =>
+ {
+ b.Navigation("UserRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUser", b =>
+ {
+ b.Navigation("UserRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.Library", b =>
+ {
+ b.Navigation("Folders");
+
+ b.Navigation("Series");
+ });
+
+ modelBuilder.Entity("API.Entities.Series", b =>
+ {
+ b.Navigation("Volumes");
+ });
+
+ modelBuilder.Entity("API.Entities.Volume", b =>
+ {
+ b.Navigation("Files");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/API/Data/Migrations/20210102165536_EntityTimestamps.cs b/API/Data/Migrations/20210102165536_EntityTimestamps.cs
new file mode 100644
index 000000000..2ed6041f0
--- /dev/null
+++ b/API/Data/Migrations/20210102165536_EntityTimestamps.cs
@@ -0,0 +1,80 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace API.Data.Migrations
+{
+ public partial class EntityTimestamps : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "Created",
+ table: "Volume",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+
+ migrationBuilder.AddColumn(
+ name: "LastModified",
+ table: "Volume",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+
+ migrationBuilder.AddColumn(
+ name: "Created",
+ table: "Series",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+
+ migrationBuilder.AddColumn(
+ name: "LastModified",
+ table: "Series",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+
+ migrationBuilder.AddColumn(
+ name: "Created",
+ table: "Library",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+
+ migrationBuilder.AddColumn(
+ name: "LastModified",
+ table: "Library",
+ type: "TEXT",
+ nullable: false,
+ defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Created",
+ table: "Volume");
+
+ migrationBuilder.DropColumn(
+ name: "LastModified",
+ table: "Volume");
+
+ migrationBuilder.DropColumn(
+ name: "Created",
+ table: "Series");
+
+ migrationBuilder.DropColumn(
+ name: "LastModified",
+ table: "Series");
+
+ migrationBuilder.DropColumn(
+ name: "Created",
+ table: "Library");
+
+ migrationBuilder.DropColumn(
+ name: "LastModified",
+ table: "Library");
+ }
+ }
+}
diff --git a/API/Data/Migrations/20210102173326_VolumeNumberRefactor.Designer.cs b/API/Data/Migrations/20210102173326_VolumeNumberRefactor.Designer.cs
new file mode 100644
index 000000000..1102111fc
--- /dev/null
+++ b/API/Data/Migrations/20210102173326_VolumeNumberRefactor.Designer.cs
@@ -0,0 +1,512 @@
+//
+using System;
+using API.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace API.Data.Migrations
+{
+ [DbContext(typeof(DataContext))]
+ [Migration("20210102173326_VolumeNumberRefactor")]
+ partial class VolumeNumberRefactor
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "5.0.1");
+
+ modelBuilder.Entity("API.Entities.AppRole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex");
+
+ b.ToTable("AspNetRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsAdmin")
+ .HasColumnType("INTEGER");
+
+ b.Property("LastActive")
+ .HasColumnType("TEXT");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("PasswordHash")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("TEXT");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex");
+
+ b.ToTable("AspNetUsers");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.Property("RoleId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.FolderPath", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("LibraryId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Path")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LibraryId");
+
+ b.ToTable("FolderPath");
+ });
+
+ modelBuilder.Entity("API.Entities.Library", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CoverImage")
+ .HasColumnType("TEXT");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.ToTable("Library");
+ });
+
+ modelBuilder.Entity("API.Entities.MangaFile", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("FilePath")
+ .HasColumnType("TEXT");
+
+ b.Property("VolumeId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("VolumeId");
+
+ b.ToTable("MangaFile");
+ });
+
+ modelBuilder.Entity("API.Entities.Series", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CoverImage")
+ .HasColumnType("TEXT");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("LibraryId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("OriginalName")
+ .HasColumnType("TEXT");
+
+ b.Property("SortName")
+ .HasColumnType("TEXT");
+
+ b.Property("Summary")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LibraryId");
+
+ b.ToTable("Series");
+ });
+
+ modelBuilder.Entity("API.Entities.Volume", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("Number")
+ .HasColumnType("INTEGER");
+
+ b.Property("SeriesId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SeriesId");
+
+ b.ToTable("Volume");
+ });
+
+ modelBuilder.Entity("AppUserLibrary", b =>
+ {
+ b.Property("AppUsersId")
+ .HasColumnType("INTEGER");
+
+ b.Property("LibrariesId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("AppUsersId", "LibrariesId");
+
+ b.HasIndex("LibrariesId");
+
+ b.ToTable("AppUserLibrary");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("RoleId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasColumnType("TEXT");
+
+ b.Property("ProviderKey")
+ .HasColumnType("TEXT");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.Property("LoginProvider")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .HasColumnType("TEXT");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUserRole", b =>
+ {
+ b.HasOne("API.Entities.AppRole", "Role")
+ .WithMany("UserRoles")
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Entities.AppUser", "User")
+ .WithMany("UserRoles")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Role");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("API.Entities.FolderPath", b =>
+ {
+ b.HasOne("API.Entities.Library", "Library")
+ .WithMany("Folders")
+ .HasForeignKey("LibraryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Library");
+ });
+
+ modelBuilder.Entity("API.Entities.MangaFile", b =>
+ {
+ b.HasOne("API.Entities.Volume", "Volume")
+ .WithMany("Files")
+ .HasForeignKey("VolumeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Volume");
+ });
+
+ modelBuilder.Entity("API.Entities.Series", b =>
+ {
+ b.HasOne("API.Entities.Library", "Library")
+ .WithMany("Series")
+ .HasForeignKey("LibraryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Library");
+ });
+
+ modelBuilder.Entity("API.Entities.Volume", b =>
+ {
+ b.HasOne("API.Entities.Series", "Series")
+ .WithMany("Volumes")
+ .HasForeignKey("SeriesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Series");
+ });
+
+ modelBuilder.Entity("AppUserLibrary", b =>
+ {
+ b.HasOne("API.Entities.AppUser", null)
+ .WithMany()
+ .HasForeignKey("AppUsersId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("API.Entities.Library", null)
+ .WithMany()
+ .HasForeignKey("LibrariesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.HasOne("API.Entities.AppRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.HasOne("API.Entities.AppUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.HasOne("API.Entities.AppUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.HasOne("API.Entities.AppUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("API.Entities.AppRole", b =>
+ {
+ b.Navigation("UserRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUser", b =>
+ {
+ b.Navigation("UserRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.Library", b =>
+ {
+ b.Navigation("Folders");
+
+ b.Navigation("Series");
+ });
+
+ modelBuilder.Entity("API.Entities.Series", b =>
+ {
+ b.Navigation("Volumes");
+ });
+
+ modelBuilder.Entity("API.Entities.Volume", b =>
+ {
+ b.Navigation("Files");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/API/Data/Migrations/20210102173326_VolumeNumberRefactor.cs b/API/Data/Migrations/20210102173326_VolumeNumberRefactor.cs
new file mode 100644
index 000000000..21cc8d42c
--- /dev/null
+++ b/API/Data/Migrations/20210102173326_VolumeNumberRefactor.cs
@@ -0,0 +1,41 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace API.Data.Migrations
+{
+ public partial class VolumeNumberRefactor : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterColumn(
+ name: "Number",
+ table: "Volume",
+ type: "INTEGER",
+ nullable: false,
+ defaultValue: 0,
+ oldClrType: typeof(string),
+ oldType: "TEXT",
+ oldNullable: true);
+
+ migrationBuilder.AddColumn(
+ name: "Name",
+ table: "Volume",
+ type: "TEXT",
+ nullable: true);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Name",
+ table: "Volume");
+
+ migrationBuilder.AlterColumn(
+ name: "Number",
+ table: "Volume",
+ type: "TEXT",
+ nullable: true,
+ oldClrType: typeof(int),
+ oldType: "INTEGER");
+ }
+ }
+}
diff --git a/API/Data/Migrations/20210103201043_RemoveUserIsAdmin.Designer.cs b/API/Data/Migrations/20210103201043_RemoveUserIsAdmin.Designer.cs
new file mode 100644
index 000000000..4288a9878
--- /dev/null
+++ b/API/Data/Migrations/20210103201043_RemoveUserIsAdmin.Designer.cs
@@ -0,0 +1,509 @@
+//
+using System;
+using API.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+namespace API.Data.Migrations
+{
+ [DbContext(typeof(DataContext))]
+ [Migration("20210103201043_RemoveUserIsAdmin")]
+ partial class RemoveUserIsAdmin
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "5.0.1");
+
+ modelBuilder.Entity("API.Entities.AppRole", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex");
+
+ b.ToTable("AspNetRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("LastActive")
+ .HasColumnType("TEXT");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("PasswordHash")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("TEXT");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex");
+
+ b.ToTable("AspNetUsers");
+ });
+
+ modelBuilder.Entity("API.Entities.AppUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("INTEGER");
+
+ b.Property("RoleId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles");
+ });
+
+ modelBuilder.Entity("API.Entities.FolderPath", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("LibraryId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Path")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LibraryId");
+
+ b.ToTable("FolderPath");
+ });
+
+ modelBuilder.Entity("API.Entities.Library", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CoverImage")
+ .HasColumnType("TEXT");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.ToTable("Library");
+ });
+
+ modelBuilder.Entity("API.Entities.MangaFile", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("FilePath")
+ .HasColumnType("TEXT");
+
+ b.Property("VolumeId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("VolumeId");
+
+ b.ToTable("MangaFile");
+ });
+
+ modelBuilder.Entity("API.Entities.Series", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CoverImage")
+ .HasColumnType("TEXT");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("LibraryId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("OriginalName")
+ .HasColumnType("TEXT");
+
+ b.Property("SortName")
+ .HasColumnType("TEXT");
+
+ b.Property("Summary")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LibraryId");
+
+ b.ToTable("Series");
+ });
+
+ modelBuilder.Entity("API.Entities.Volume", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Created")
+ .HasColumnType("TEXT");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("Number")
+ .HasColumnType("INTEGER");
+
+ b.Property("SeriesId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SeriesId");
+
+ b.ToTable("Volume");
+ });
+
+ modelBuilder.Entity("AppUserLibrary", b =>
+ {
+ b.Property("AppUsersId")
+ .HasColumnType("INTEGER");
+
+ b.Property("LibrariesId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("AppUsersId", "LibrariesId");
+
+ b.HasIndex("LibrariesId");
+
+ b.ToTable("AppUserLibrary");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("RoleId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property