diff --git a/API/API.csproj b/API/API.csproj index 1ccee1124..cdd2f9198 100644 --- a/API/API.csproj +++ b/API/API.csproj @@ -8,6 +8,9 @@ + + + diff --git a/API/Controllers/AccountController.cs b/API/Controllers/AccountController.cs index 3610925e9..3c25974ef 100644 --- a/API/Controllers/AccountController.cs +++ b/API/Controllers/AccountController.cs @@ -49,8 +49,6 @@ namespace API.Controllers if (!result.Succeeded) return BadRequest(result.Errors); - - // TODO: Need a way to store Roles in enum and configure from there var role = registerDto.IsAdmin ? PolicyConstants.AdminRole : PolicyConstants.PlebRole; var roleResult = await _userManager.AddToRoleAsync(user, role); diff --git a/API/Controllers/AdminController.cs b/API/Controllers/AdminController.cs index 173961a48..fa495b62e 100644 --- a/API/Controllers/AdminController.cs +++ b/API/Controllers/AdminController.cs @@ -1,9 +1,6 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using API.DTOs; +using System.Threading.Tasks; using API.Entities; using API.Interfaces; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; diff --git a/API/Controllers/LibraryController.cs b/API/Controllers/LibraryController.cs index f60365b60..82e88afb0 100644 --- a/API/Controllers/LibraryController.cs +++ b/API/Controllers/LibraryController.cs @@ -1,14 +1,13 @@ -using System.Collections.Generic; -using System.Collections.Immutable; +using System; +using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; using API.Data; using API.DTOs; using API.Entities; -using API.Extensions; using API.Interfaces; using AutoMapper; +using Hangfire; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; @@ -18,23 +17,23 @@ namespace API.Controllers [Authorize] public class LibraryController : BaseApiController { - private readonly DataContext _context; private readonly IDirectoryService _directoryService; private readonly ILibraryRepository _libraryRepository; private readonly ILogger _logger; private readonly IUserRepository _userRepository; private readonly IMapper _mapper; + private readonly ITaskScheduler _taskScheduler; - public LibraryController(DataContext context, IDirectoryService directoryService, + public LibraryController(IDirectoryService directoryService, ILibraryRepository libraryRepository, ILogger logger, IUserRepository userRepository, - IMapper mapper) + IMapper mapper, ITaskScheduler taskScheduler) { - _context = context; _directoryService = directoryService; _libraryRepository = libraryRepository; _logger = logger; _userRepository = userRepository; _mapper = mapper; + _taskScheduler = taskScheduler; } /// @@ -90,8 +89,8 @@ namespace API.Controllers public async Task ScanLibrary(int libraryId) { var library = await _libraryRepository.GetLibraryForIdAsync(libraryId); - - _directoryService.ScanLibrary(library); + + BackgroundJob.Enqueue(() => _directoryService.ScanLibrary(library)); return Ok(); } diff --git a/API/Controllers/UsersController.cs b/API/Controllers/UsersController.cs index 28d1a3916..6102cc5e5 100644 --- a/API/Controllers/UsersController.cs +++ b/API/Controllers/UsersController.cs @@ -14,13 +14,11 @@ namespace API.Controllers [Authorize] public class UsersController : BaseApiController { - private readonly DataContext _context; private readonly IUserRepository _userRepository; private readonly ILibraryRepository _libraryRepository; - public UsersController(DataContext context, IUserRepository userRepository, ILibraryRepository libraryRepository) + public UsersController(IUserRepository userRepository, ILibraryRepository libraryRepository) { - _context = context; _userRepository = userRepository; _libraryRepository = libraryRepository; } @@ -43,7 +41,7 @@ namespace API.Controllers // TODO: We probably need to clean the folders before we insert var library = new Library { - Name = createLibraryDto.Name, // TODO: Ensure code handles Library name always being lowercase + Name = createLibraryDto.Name.ToLower(), Type = createLibraryDto.Type, AppUsers = new List() { user } }; diff --git a/API/DTOs/CreateLibraryDto.cs b/API/DTOs/CreateLibraryDto.cs index 3e3263d48..5dcdbf4e3 100644 --- a/API/DTOs/CreateLibraryDto.cs +++ b/API/DTOs/CreateLibraryDto.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Text.Json.Serialization; using API.Entities; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; namespace API.DTOs { diff --git a/API/DTOs/MemberDto.cs b/API/DTOs/MemberDto.cs index 6f09f1fc3..b404af389 100644 --- a/API/DTOs/MemberDto.cs +++ b/API/DTOs/MemberDto.cs @@ -1,7 +1,5 @@ using System; -using System.Collections; using System.Collections.Generic; -using API.Entities; namespace API.DTOs { diff --git a/API/Data/DataContext.cs b/API/Data/DataContext.cs index e29f5758a..9772251b7 100644 --- a/API/Data/DataContext.cs +++ b/API/Data/DataContext.cs @@ -1,5 +1,4 @@ -using System; -using API.Entities; +using API.Entities; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; diff --git a/API/Data/Migrations/20201224155621_MiscCleanup.cs b/API/Data/Migrations/20201224155621_MiscCleanup.cs index 20e0a4dc9..78e66aea8 100644 --- a/API/Data/Migrations/20201224155621_MiscCleanup.cs +++ b/API/Data/Migrations/20201224155621_MiscCleanup.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; namespace API.Data.Migrations { diff --git a/API/Data/Migrations/DataContextModelSnapshot.cs b/API/Data/Migrations/DataContextModelSnapshot.cs index fd6f137ac..b7088bacf 100644 --- a/API/Data/Migrations/DataContextModelSnapshot.cs +++ b/API/Data/Migrations/DataContextModelSnapshot.cs @@ -1,9 +1,7 @@ // using System; -using API.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace API.Data.Migrations { diff --git a/API/Data/Seed.cs b/API/Data/Seed.cs index cd90243c4..8d7f619c3 100644 --- a/API/Data/Seed.cs +++ b/API/Data/Seed.cs @@ -12,8 +12,8 @@ namespace API.Data { var roles = new List { - new AppRole {Name = PolicyConstants.AdminRole}, - new AppRole {Name = PolicyConstants.PlebRole} + new() {Name = PolicyConstants.AdminRole}, + new() {Name = PolicyConstants.PlebRole} }; foreach (var role in roles) diff --git a/API/Extensions/ApplicationServiceExtensions.cs b/API/Extensions/ApplicationServiceExtensions.cs index 454c7c2f2..08077b5a1 100644 --- a/API/Extensions/ApplicationServiceExtensions.cs +++ b/API/Extensions/ApplicationServiceExtensions.cs @@ -3,6 +3,8 @@ using API.Helpers; using API.Interfaces; using API.Services; using AutoMapper; +using Hangfire; +using Hangfire.LiteDB; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -14,6 +16,7 @@ namespace API.Extensions public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration config) { services.AddAutoMapper(typeof(AutoMapperProfiles).Assembly); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -23,6 +26,14 @@ namespace API.Extensions options.UseSqlite(config.GetConnectionString("DefaultConnection")); }); + services.AddHangfire(configuration => configuration + .UseSimpleAssemblyNameTypeSerializer() + .UseRecommendedSerializerSettings() + .UseLiteDbStorage()); + + // Add the processing server as IHostedService + services.AddHangfireServer(); + return services; } } diff --git a/API/Hangfire-log.db b/API/Hangfire-log.db new file mode 100644 index 000000000..d8fc774c2 Binary files /dev/null and b/API/Hangfire-log.db differ diff --git a/API/Hangfire.db b/API/Hangfire.db new file mode 100644 index 000000000..db5987848 Binary files /dev/null and b/API/Hangfire.db differ diff --git a/API/Helpers/AutoMapperProfiles.cs b/API/Helpers/AutoMapperProfiles.cs index 4e203ab04..08e16b68a 100644 --- a/API/Helpers/AutoMapperProfiles.cs +++ b/API/Helpers/AutoMapperProfiles.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using API.DTOs; using API.Entities; using AutoMapper; diff --git a/API/Interfaces/IDirectoryService.cs b/API/Interfaces/IDirectoryService.cs index 3c7151794..818aa9451 100644 --- a/API/Interfaces/IDirectoryService.cs +++ b/API/Interfaces/IDirectoryService.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; -using System.Threading.Tasks; using API.DTOs; -using API.Entities; namespace API.Interfaces { diff --git a/API/Interfaces/ITaskScheduler.cs b/API/Interfaces/ITaskScheduler.cs new file mode 100644 index 000000000..7f0a6312b --- /dev/null +++ b/API/Interfaces/ITaskScheduler.cs @@ -0,0 +1,7 @@ +namespace API.Interfaces +{ + public interface ITaskScheduler + { + + } +} \ No newline at end of file diff --git a/API/Interfaces/IUserRepository.cs b/API/Interfaces/IUserRepository.cs index 601913c89..31be6e52e 100644 --- a/API/Interfaces/IUserRepository.cs +++ b/API/Interfaces/IUserRepository.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using API.DTOs; using API.Entities; diff --git a/API/Services/DirectoryService.cs b/API/Services/DirectoryService.cs index 1109f2305..8f09750b2 100644 --- a/API/Services/DirectoryService.cs +++ b/API/Services/DirectoryService.cs @@ -29,7 +29,7 @@ namespace API.Services /// List of folder names public IEnumerable ListDirectory(string rootPath) { - // TODO: Put some checks in here along with API to ensure that we aren't passed a file, folder exists, etc. + if (!Directory.Exists(rootPath)) return ImmutableList.Empty; var di = new DirectoryInfo(rootPath); var dirs = di.GetDirectories() @@ -137,7 +137,7 @@ namespace API.Services else { Parallel.ForEach(files, () => 0, (file, loopState, localCount) => { action(file); - return (int) ++localCount; + return ++localCount; }, (c) => { Interlocked.Add(ref fileCount, c); diff --git a/API/Services/TaskScheduler.cs b/API/Services/TaskScheduler.cs new file mode 100644 index 000000000..b76d10e40 --- /dev/null +++ b/API/Services/TaskScheduler.cs @@ -0,0 +1,17 @@ +using API.Interfaces; +using Hangfire; + +namespace API.Services +{ + public class TaskScheduler : ITaskScheduler + { + private readonly BackgroundJobServer _client; + + public TaskScheduler() + { + _client = new BackgroundJobServer(); + } + + + } +} \ No newline at end of file diff --git a/API/Startup.cs b/API/Startup.cs index c98d4064d..4ad56e85a 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -1,5 +1,7 @@ +using System; using API.Extensions; using API.Middleware; +using Hangfire; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -33,7 +35,7 @@ namespace API } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure(IApplicationBuilder app, IBackgroundJobClient backgroundJobs, IWebHostEnvironment env) { app.UseMiddleware(); @@ -42,6 +44,9 @@ namespace API app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "API v1")); } + + app.UseHangfireDashboard(); + backgroundJobs.Enqueue(() => Console.WriteLine("Hello world from Hangfire!")); app.UseHttpsRedirection(); @@ -57,6 +62,7 @@ namespace API app.UseEndpoints(endpoints => { endpoints.MapControllers(); + endpoints.MapHangfireDashboard(); }); } } diff --git a/API/appsettings.Development.json b/API/appsettings.Development.json index 280e18a03..5d8c460c5 100644 --- a/API/appsettings.Development.json +++ b/API/appsettings.Development.json @@ -1,13 +1,15 @@ { "ConnectionStrings": { - "DefaultConnection": "Data source=kavita.db" + "DefaultConnection": "Data source=kavita.db", + "HangfireConnection": "Data source=hangfire.db" }, "TokenKey": "super secret unguessable key", "Logging": { "LogLevel": { "Default": "Debug", "Microsoft": "Information", - "Microsoft.Hosting.Lifetime": "Information" + "Microsoft.Hosting.Lifetime": "Information", + "Hangfire": "Information" } } }