mirror of
				https://github.com/Kareadita/Kavita.git
				synced 2025-11-04 03:27:05 -05:00 
			
		
		
		
	* Refactored all files to have Interfaces within the same file. Started moving over to file-scoped namespaces. * Refactored common methods for getting underlying file's cover, pages, and extracting into 1 interface. * More refactoring around removing dependence on explicit filetype testing for getting information. * Code is buildable, tests are broken. Huge refactor (not completed) which makes most of DirectoryService testable with a mock filesystem (and thus the services that utilize it). * Finished porting DirectoryService to use mocked filesystem implementation. * Added a null check * Added a null check * Finished all unit tests for DirectoryService. * Some misc cleanup on the code * Fixed up some bugs from refactoring scan loop. * Implemented CleanupService testing and refactored more of DirectoryService to be non-static. Fixed a bug where cover file cleanup wasn't properly finding files due to a regex bug. * Fixed an issue in CleanupBackup() where we weren't properly selecting database files older than 30 days. Finished CleanupService Tests. * Refactored Flatten and RemoveNonImages to directory service to allow CacheService to be testable. * Finally have CacheService tested. Rewrote GetCachedPagePath() to be much more straightforward & performant. * Updated DefaultParserTests.cs to contain all existing tests and follow new test layout format. * All tests fixed up
		
			
				
	
	
		
			282 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Linq;
 | 
						|
using System.Threading.Tasks;
 | 
						|
using API.Constants;
 | 
						|
using API.DTOs;
 | 
						|
using API.DTOs.Reader;
 | 
						|
using API.Entities;
 | 
						|
using AutoMapper;
 | 
						|
using AutoMapper.QueryableExtensions;
 | 
						|
using Microsoft.AspNetCore.Identity;
 | 
						|
using Microsoft.EntityFrameworkCore;
 | 
						|
 | 
						|
namespace API.Data.Repositories;
 | 
						|
 | 
						|
[Flags]
 | 
						|
public enum AppUserIncludes
 | 
						|
{
 | 
						|
    None = 1,
 | 
						|
    Progress = 2,
 | 
						|
    Bookmarks = 4,
 | 
						|
    ReadingLists = 8,
 | 
						|
    Ratings = 16
 | 
						|
}
 | 
						|
 | 
						|
public interface IUserRepository
 | 
						|
{
 | 
						|
    void Update(AppUser user);
 | 
						|
    void Update(AppUserPreferences preferences);
 | 
						|
    void Update(AppUserBookmark bookmark);
 | 
						|
    public void Delete(AppUser user);
 | 
						|
    Task<IEnumerable<MemberDto>>  GetMembersAsync();
 | 
						|
    Task<IEnumerable<AppUser>> GetAdminUsersAsync();
 | 
						|
    Task<IEnumerable<AppUser>> GetNonAdminUsersAsync();
 | 
						|
    Task<bool> IsUserAdmin(AppUser user);
 | 
						|
    Task<AppUserRating> GetUserRating(int seriesId, int userId);
 | 
						|
    Task<AppUserPreferences> GetPreferencesAsync(string username);
 | 
						|
    Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForSeries(int userId, int seriesId);
 | 
						|
    Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForVolume(int userId, int volumeId);
 | 
						|
    Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForChapter(int userId, int chapterId);
 | 
						|
    Task<IEnumerable<BookmarkDto>> GetAllBookmarkDtos(int userId);
 | 
						|
    Task<AppUserBookmark> GetBookmarkForPage(int page, int chapterId, int userId);
 | 
						|
    Task<int> GetUserIdByApiKeyAsync(string apiKey);
 | 
						|
    Task<AppUser> GetUserByUsernameAsync(string username, AppUserIncludes includeFlags = AppUserIncludes.None);
 | 
						|
    Task<AppUser> GetUserByIdAsync(int userId, AppUserIncludes includeFlags = AppUserIncludes.None);
 | 
						|
    Task<int> GetUserIdByUsernameAsync(string username);
 | 
						|
    Task<AppUser> GetUserWithReadingListsByUsernameAsync(string username);
 | 
						|
}
 | 
						|
 | 
						|
public class UserRepository : IUserRepository
 | 
						|
{
 | 
						|
    private readonly DataContext _context;
 | 
						|
    private readonly UserManager<AppUser> _userManager;
 | 
						|
    private readonly IMapper _mapper;
 | 
						|
 | 
						|
    public UserRepository(DataContext context, UserManager<AppUser> userManager, IMapper mapper)
 | 
						|
    {
 | 
						|
        _context = context;
 | 
						|
        _userManager = userManager;
 | 
						|
        _mapper = mapper;
 | 
						|
    }
 | 
						|
 | 
						|
    public void Update(AppUser user)
 | 
						|
    {
 | 
						|
        _context.Entry(user).State = EntityState.Modified;
 | 
						|
    }
 | 
						|
 | 
						|
    public void Update(AppUserPreferences preferences)
 | 
						|
    {
 | 
						|
        _context.Entry(preferences).State = EntityState.Modified;
 | 
						|
    }
 | 
						|
 | 
						|
    public void Update(AppUserBookmark bookmark)
 | 
						|
    {
 | 
						|
        _context.Entry(bookmark).State = EntityState.Modified;
 | 
						|
    }
 | 
						|
 | 
						|
    public void Delete(AppUser user)
 | 
						|
    {
 | 
						|
        _context.AppUser.Remove(user);
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// A one stop shop to get a tracked AppUser instance with any number of JOINs generated by passing bitwise flags.
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="username"></param>
 | 
						|
    /// <param name="includeFlags">Includes() you want. Pass multiple with flag1 | flag2 </param>
 | 
						|
    /// <returns></returns>
 | 
						|
    public async Task<AppUser> GetUserByUsernameAsync(string username, AppUserIncludes includeFlags = AppUserIncludes.None)
 | 
						|
    {
 | 
						|
        var query = _context.Users
 | 
						|
            .Where(x => x.UserName == username);
 | 
						|
 | 
						|
        query = AddIncludesToQuery(query, includeFlags);
 | 
						|
 | 
						|
        return await query.SingleOrDefaultAsync();
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// A one stop shop to get a tracked AppUser instance with any number of JOINs generated by passing bitwise flags.
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="userId"></param>
 | 
						|
    /// <param name="includeFlags">Includes() you want. Pass multiple with flag1 | flag2 </param>
 | 
						|
    /// <returns></returns>
 | 
						|
    public async Task<AppUser> GetUserByIdAsync(int userId, AppUserIncludes includeFlags = AppUserIncludes.None)
 | 
						|
    {
 | 
						|
        var query = _context.Users
 | 
						|
            .Where(x => x.Id == userId);
 | 
						|
 | 
						|
        query = AddIncludesToQuery(query, includeFlags);
 | 
						|
 | 
						|
        return await query.SingleOrDefaultAsync();
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<AppUserBookmark> GetBookmarkForPage(int page, int chapterId, int userId)
 | 
						|
    {
 | 
						|
        return await _context.AppUserBookmark
 | 
						|
            .Where(b => b.Page == page && b.ChapterId == chapterId && b.AppUserId == userId)
 | 
						|
            .SingleOrDefaultAsync();
 | 
						|
    }
 | 
						|
 | 
						|
    private static IQueryable<AppUser> AddIncludesToQuery(IQueryable<AppUser> query, AppUserIncludes includeFlags)
 | 
						|
    {
 | 
						|
        if (includeFlags.HasFlag(AppUserIncludes.Bookmarks))
 | 
						|
        {
 | 
						|
            query = query.Include(u => u.Bookmarks);
 | 
						|
        }
 | 
						|
 | 
						|
        if (includeFlags.HasFlag(AppUserIncludes.Progress))
 | 
						|
        {
 | 
						|
            query = query.Include(u => u.Progresses);
 | 
						|
        }
 | 
						|
 | 
						|
        if (includeFlags.HasFlag(AppUserIncludes.ReadingLists))
 | 
						|
        {
 | 
						|
            query = query.Include(u => u.ReadingLists);
 | 
						|
        }
 | 
						|
 | 
						|
        if (includeFlags.HasFlag(AppUserIncludes.Ratings))
 | 
						|
        {
 | 
						|
            query = query.Include(u => u.Ratings);
 | 
						|
        }
 | 
						|
 | 
						|
        return query;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// This fetches the Id for a user. Use whenever you just need an ID.
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="username"></param>
 | 
						|
    /// <returns></returns>
 | 
						|
    public async Task<int> GetUserIdByUsernameAsync(string username)
 | 
						|
    {
 | 
						|
        return await _context.Users
 | 
						|
            .Where(x => x.UserName == username)
 | 
						|
            .Select(u => u.Id)
 | 
						|
            .SingleOrDefaultAsync();
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Gets an AppUser by username. Returns back Reading List and their Items.
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="username"></param>
 | 
						|
    /// <returns></returns>
 | 
						|
    public async Task<AppUser> GetUserWithReadingListsByUsernameAsync(string username)
 | 
						|
    {
 | 
						|
        return await _context.Users
 | 
						|
            .Include(u => u.ReadingLists)
 | 
						|
            .ThenInclude(l => l.Items)
 | 
						|
            .SingleOrDefaultAsync(x => x.UserName == username);
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<IEnumerable<AppUser>> GetAdminUsersAsync()
 | 
						|
    {
 | 
						|
        return await _userManager.GetUsersInRoleAsync(PolicyConstants.AdminRole);
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<IEnumerable<AppUser>> GetNonAdminUsersAsync()
 | 
						|
    {
 | 
						|
        return await _userManager.GetUsersInRoleAsync(PolicyConstants.PlebRole);
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<bool> IsUserAdmin(AppUser user)
 | 
						|
    {
 | 
						|
        return await _userManager.IsInRoleAsync(user, PolicyConstants.AdminRole);
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<AppUserRating> GetUserRating(int seriesId, int userId)
 | 
						|
    {
 | 
						|
        return await _context.AppUserRating.Where(r => r.SeriesId == seriesId && r.AppUserId == userId)
 | 
						|
            .SingleOrDefaultAsync();
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<AppUserPreferences> GetPreferencesAsync(string username)
 | 
						|
    {
 | 
						|
        return await _context.AppUserPreferences
 | 
						|
            .Include(p => p.AppUser)
 | 
						|
            .SingleOrDefaultAsync(p => p.AppUser.UserName == username);
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForSeries(int userId, int seriesId)
 | 
						|
    {
 | 
						|
        return await _context.AppUserBookmark
 | 
						|
            .Where(x => x.AppUserId == userId && x.SeriesId == seriesId)
 | 
						|
            .OrderBy(x => x.Page)
 | 
						|
            .AsNoTracking()
 | 
						|
            .ProjectTo<BookmarkDto>(_mapper.ConfigurationProvider)
 | 
						|
            .ToListAsync();
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForVolume(int userId, int volumeId)
 | 
						|
    {
 | 
						|
        return await _context.AppUserBookmark
 | 
						|
            .Where(x => x.AppUserId == userId && x.VolumeId == volumeId)
 | 
						|
            .OrderBy(x => x.Page)
 | 
						|
            .AsNoTracking()
 | 
						|
            .ProjectTo<BookmarkDto>(_mapper.ConfigurationProvider)
 | 
						|
            .ToListAsync();
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForChapter(int userId, int chapterId)
 | 
						|
    {
 | 
						|
        return await _context.AppUserBookmark
 | 
						|
            .Where(x => x.AppUserId == userId && x.ChapterId == chapterId)
 | 
						|
            .OrderBy(x => x.Page)
 | 
						|
            .AsNoTracking()
 | 
						|
            .ProjectTo<BookmarkDto>(_mapper.ConfigurationProvider)
 | 
						|
            .ToListAsync();
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<IEnumerable<BookmarkDto>> GetAllBookmarkDtos(int userId)
 | 
						|
    {
 | 
						|
        return await _context.AppUserBookmark
 | 
						|
            .Where(x => x.AppUserId == userId)
 | 
						|
            .OrderBy(x => x.Page)
 | 
						|
            .AsNoTracking()
 | 
						|
            .ProjectTo<BookmarkDto>(_mapper.ConfigurationProvider)
 | 
						|
            .ToListAsync();
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Fetches the UserId by API Key. This does not include any extra information
 | 
						|
    /// </summary>
 | 
						|
    /// <param name="apiKey"></param>
 | 
						|
    /// <returns></returns>
 | 
						|
    public async Task<int> GetUserIdByApiKeyAsync(string apiKey)
 | 
						|
    {
 | 
						|
        return await _context.AppUser
 | 
						|
            .Where(u => u.ApiKey.Equals(apiKey))
 | 
						|
            .Select(u => u.Id)
 | 
						|
            .SingleOrDefaultAsync();
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    public async Task<IEnumerable<MemberDto>> GetMembersAsync()
 | 
						|
    {
 | 
						|
        return await _context.Users
 | 
						|
            .Include(x => x.Libraries)
 | 
						|
            .Include(r => r.UserRoles)
 | 
						|
            .ThenInclude(r => r.Role)
 | 
						|
            .OrderBy(u => u.UserName)
 | 
						|
            .Select(u => new MemberDto
 | 
						|
            {
 | 
						|
                Id = u.Id,
 | 
						|
                Username = u.UserName,
 | 
						|
                Created = u.Created,
 | 
						|
                LastActive = u.LastActive,
 | 
						|
                Roles = u.UserRoles.Select(r => r.Role.Name).ToList(),
 | 
						|
                Libraries =  u.Libraries.Select(l => new LibraryDto
 | 
						|
                {
 | 
						|
                    Name = l.Name,
 | 
						|
                    Type = l.Type,
 | 
						|
                    LastScanned = l.LastScanned,
 | 
						|
                    Folders = l.Folders.Select(x => x.Path).ToList()
 | 
						|
                }).ToList()
 | 
						|
            })
 | 
						|
            .AsNoTracking()
 | 
						|
            .ToListAsync();
 | 
						|
    }
 | 
						|
}
 |